diff --git a/.coveragerc b/.coveragerc index 85e08d51f71..afdf2b3acb9 100644 --- a/.coveragerc +++ b/.coveragerc @@ -46,6 +46,8 @@ omit = homeassistant/components/airtouch4/const.py homeassistant/components/airvisual/__init__.py homeassistant/components/airvisual/sensor.py + homeassistant/components/airvisual_pro/__init__.py + homeassistant/components/airvisual_pro/sensor.py homeassistant/components/alarmdecoder/__init__.py homeassistant/components/alarmdecoder/alarm_control_panel.py homeassistant/components/alarmdecoder/binary_sensor.py @@ -224,8 +226,6 @@ omit = homeassistant/components/denonavr/__init__.py homeassistant/components/denonavr/media_player.py homeassistant/components/denonavr/receiver.py - homeassistant/components/deutsche_bahn/sensor.py - homeassistant/components/devolo_home_control/sensor.py homeassistant/components/devolo_home_control/switch.py homeassistant/components/digital_ocean/* homeassistant/components/discogs/sensor.py @@ -294,9 +294,11 @@ omit = homeassistant/components/elkm1/sensor.py homeassistant/components/elkm1/switch.py homeassistant/components/elmax/__init__.py + homeassistant/components/elmax/alarm_control_panel.py homeassistant/components/elmax/binary_sensor.py homeassistant/components/elmax/common.py homeassistant/components/elmax/const.py + homeassistant/components/elmax/binary_sensor.py homeassistant/components/elmax/switch.py homeassistant/components/elv/* homeassistant/components/emby/media_player.py @@ -727,8 +729,6 @@ omit = homeassistant/components/mastodon/notify.py homeassistant/components/matrix/* homeassistant/components/matter/__init__.py - homeassistant/components/matter/adapter.py - homeassistant/components/matter/entity.py homeassistant/components/meater/__init__.py homeassistant/components/meater/const.py homeassistant/components/meater/sensor.py @@ -797,12 +797,9 @@ omit = homeassistant/components/myq/cover.py homeassistant/components/myq/light.py homeassistant/components/mysensors/__init__.py - homeassistant/components/mysensors/binary_sensor.py homeassistant/components/mysensors/climate.py - homeassistant/components/mysensors/const.py homeassistant/components/mysensors/cover.py homeassistant/components/mysensors/device.py - homeassistant/components/mysensors/device_tracker.py homeassistant/components/mysensors/gateway.py homeassistant/components/mysensors/handler.py homeassistant/components/mysensors/helpers.py @@ -846,7 +843,9 @@ omit = homeassistant/components/nfandroidtv/__init__.py homeassistant/components/nfandroidtv/notify.py homeassistant/components/nibe_heatpump/__init__.py + homeassistant/components/nibe_heatpump/climate.py homeassistant/components/nibe_heatpump/binary_sensor.py + homeassistant/components/nibe_heatpump/button.py homeassistant/components/nibe_heatpump/number.py homeassistant/components/nibe_heatpump/select.py homeassistant/components/nibe_heatpump/sensor.py @@ -921,6 +920,7 @@ omit = homeassistant/components/opentherm_gw/sensor.py homeassistant/components/openuv/__init__.py homeassistant/components/openuv/binary_sensor.py + homeassistant/components/openuv/coordinator.py homeassistant/components/openuv/sensor.py homeassistant/components/openweathermap/sensor.py homeassistant/components/openweathermap/weather.py @@ -1002,6 +1002,9 @@ omit = homeassistant/components/proxmoxve/* homeassistant/components/proxy/camera.py homeassistant/components/pulseaudio_loopback/switch.py + homeassistant/components/purpleair/__init__.py + homeassistant/components/purpleair/coordinator.py + homeassistant/components/purpleair/sensor.py homeassistant/components/pushbullet/api.py homeassistant/components/pushbullet/notify.py homeassistant/components/pushbullet/sensor.py @@ -1028,7 +1031,6 @@ omit = homeassistant/components/radiotherm/entity.py homeassistant/components/radiotherm/switch.py homeassistant/components/radiotherm/util.py - homeassistant/components/rainbird/* homeassistant/components/raincloud/* homeassistant/components/rainmachine/__init__.py homeassistant/components/rainmachine/binary_sensor.py @@ -1048,6 +1050,11 @@ omit = homeassistant/components/rejseplanen/sensor.py homeassistant/components/remember_the_milk/__init__.py homeassistant/components/remote_rpi_gpio/* + homeassistant/components/reolink/__init__.py + homeassistant/components/reolink/camera.py + homeassistant/components/reolink/const.py + homeassistant/components/reolink/entity.py + homeassistant/components/reolink/host.py homeassistant/components/repetier/__init__.py homeassistant/components/repetier/sensor.py homeassistant/components/rest/notify.py @@ -1246,9 +1253,11 @@ omit = homeassistant/components/switchbot/coordinator.py homeassistant/components/switchbot/cover.py homeassistant/components/switchbot/entity.py + homeassistant/components/switchbot/humidifier.py homeassistant/components/switchbot/light.py homeassistant/components/switchbot/sensor.py homeassistant/components/switchbot/switch.py + homeassistant/components/switchbot/lock.py homeassistant/components/switchmate/switch.py homeassistant/components/syncthing/__init__.py homeassistant/components/syncthing/sensor.py @@ -1416,9 +1425,6 @@ omit = homeassistant/components/upnp/__init__.py homeassistant/components/upnp/device.py homeassistant/components/upnp/sensor.py - homeassistant/components/vallox/__init__.py - homeassistant/components/vallox/fan.py - homeassistant/components/vallox/sensor.py homeassistant/components/vasttrafik/sensor.py homeassistant/components/velbus/__init__.py homeassistant/components/velbus/binary_sensor.py @@ -1581,8 +1587,6 @@ omit = homeassistant/components/youless/const.py homeassistant/components/youless/sensor.py homeassistant/components/zabbix/* - homeassistant/components/zamg/__init__.py - homeassistant/components/zamg/const.py homeassistant/components/zamg/coordinator.py homeassistant/components/zamg/sensor.py homeassistant/components/zamg/weather.py diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 0390910cc58..5bb755750e1 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -80,8 +80,7 @@ body: label: Diagnostics information placeholder: "drag-and-drop the diagnostics data file here (do not copy-and-paste the content)" description: >- - Many integrations provide the ability to download diagnostic data - on the device page (and on the integration dashboard). + Many integrations provide the ability to [download diagnostic data](https://www.home-assistant.io/docs/configuration/troubleshooting/#debug-logs-and-diagnostics). **It would really help if you could download the diagnostics data for the device you are having issues with, and drag-and-drop that file into the textbox below.** diff --git a/.github/workflows/builder.yml b/.github/workflows/builder.yml index e4fbd33cd09..9aea4badcc7 100644 --- a/.github/workflows/builder.yml +++ b/.github/workflows/builder.yml @@ -24,12 +24,12 @@ jobs: publish: ${{ steps.version.outputs.publish }} steps: - name: Checkout the repository - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v3.2.0 with: fetch-depth: 0 - name: Set up Python ${{ env.DEFAULT_PYTHON }} - uses: actions/setup-python@v4.3.0 + uses: actions/setup-python@v4.4.0 with: python-version: ${{ env.DEFAULT_PYTHON }} @@ -67,10 +67,10 @@ jobs: if: github.repository_owner == 'home-assistant' && needs.init.outputs.publish == 'true' steps: - name: Checkout the repository - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v3.2.0 - name: Set up Python ${{ env.DEFAULT_PYTHON }} - uses: actions/setup-python@v4.3.0 + uses: actions/setup-python@v4.4.0 with: python-version: ${{ env.DEFAULT_PYTHON }} @@ -100,7 +100,7 @@ jobs: arch: ${{ fromJson(needs.init.outputs.architectures) }} steps: - name: Checkout the repository - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v3.2.0 - name: Download nightly wheels of frontend if: needs.init.outputs.channel == 'dev' @@ -115,7 +115,7 @@ jobs: - name: Set up Python ${{ env.DEFAULT_PYTHON }} if: needs.init.outputs.channel == 'dev' - uses: actions/setup-python@v4.3.0 + uses: actions/setup-python@v4.4.0 with: python-version: ${{ env.DEFAULT_PYTHON }} @@ -198,7 +198,7 @@ jobs: - yellow steps: - name: Checkout the repository - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v3.2.0 - name: Set build additional args run: | @@ -241,7 +241,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout the repository - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v3.2.0 - name: Initialize git uses: home-assistant/actions/helpers/git-init@master @@ -280,7 +280,7 @@ jobs: - "homeassistant" steps: - name: Checkout the repository - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v3.2.0 - name: Login to DockerHub if: matrix.registry == 'homeassistant' diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 13ff8ea12e3..b6a4ba5a793 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -22,7 +22,7 @@ on: env: CACHE_VERSION: 3 PIP_CACHE_VERSION: 3 - HA_SHORT_VERSION: 2022.12 + HA_SHORT_VERSION: 2023.1 DEFAULT_PYTHON: 3.9 ALL_PYTHON_VERSIONS: "['3.9', '3.10']" PRE_COMMIT_CACHE: ~/.cache/pre-commit @@ -56,7 +56,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Check out code from GitHub - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v3.2.0 - name: Generate partial Python venv restore key id: generate_python_cache_key run: >- @@ -167,16 +167,16 @@ jobs: - info steps: - name: Check out code from GitHub - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v3.2.0 - name: Set up Python ${{ env.DEFAULT_PYTHON }} id: python - uses: actions/setup-python@v4.3.0 + uses: actions/setup-python@v4.4.0 with: python-version: ${{ env.DEFAULT_PYTHON }} check-latest: true - name: Restore base Python virtual environment id: cache-venv - uses: actions/cache@v3.0.11 + uses: actions/cache@v3.2.2 with: path: venv key: >- @@ -191,7 +191,7 @@ jobs: pip install "$(cat requirements_test.txt | grep pre-commit)" - name: Restore pre-commit environment from cache id: cache-precommit - uses: actions/cache@v3.0.11 + uses: actions/cache@v3.2.2 with: path: ${{ env.PRE_COMMIT_CACHE }} key: >- @@ -211,16 +211,16 @@ jobs: - pre-commit steps: - name: Check out code from GitHub - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v3.2.0 - name: Set up Python ${{ env.DEFAULT_PYTHON }} - uses: actions/setup-python@v4.3.0 + uses: actions/setup-python@v4.4.0 id: python with: python-version: ${{ env.DEFAULT_PYTHON }} check-latest: true - name: Restore base Python virtual environment id: cache-venv - uses: actions/cache@v3.0.11 + uses: actions/cache/restore@v3.2.2 with: path: venv key: >- @@ -233,7 +233,7 @@ jobs: exit 1 - name: Restore pre-commit environment from cache id: cache-precommit - uses: actions/cache@v3.0.11 + uses: actions/cache/restore@v3.2.2 with: path: ${{ env.PRE_COMMIT_CACHE }} key: >- @@ -255,7 +255,7 @@ jobs: run: | . venv/bin/activate shopt -s globstar - pre-commit run --hook-stage manual black --files {homeassistant,tests}/components/${{ needs.info.outputs.integrations_glob }}/**/* --show-diff-on-failure + pre-commit run --hook-stage manual black --files {homeassistant,tests}/components/${{ needs.info.outputs.integrations_glob }}/{*,**/*} --show-diff-on-failure lint-flake8: name: Check flake8 @@ -265,16 +265,16 @@ jobs: - pre-commit steps: - name: Check out code from GitHub - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v3.2.0 - name: Set up Python ${{ env.DEFAULT_PYTHON }} - uses: actions/setup-python@v4.3.0 + uses: actions/setup-python@v4.4.0 id: python with: python-version: ${{ env.DEFAULT_PYTHON }} check-latest: true - name: Restore base Python virtual environment id: cache-venv - uses: actions/cache@v3.0.11 + uses: actions/cache/restore@v3.2.2 with: path: venv key: >- @@ -287,7 +287,7 @@ jobs: exit 1 - name: Restore pre-commit environment from cache id: cache-precommit - uses: actions/cache@v3.0.11 + uses: actions/cache/restore@v3.2.2 with: path: ${{ env.PRE_COMMIT_CACHE }} key: >- @@ -312,7 +312,7 @@ jobs: run: | . venv/bin/activate shopt -s globstar - pre-commit run --hook-stage manual flake8 --files {homeassistant,tests}/components/${{ needs.info.outputs.integrations_glob }}/**/* + pre-commit run --hook-stage manual flake8 --files {homeassistant,tests}/components/${{ needs.info.outputs.integrations_glob }}/{*,**/*} lint-isort: name: Check isort @@ -322,16 +322,16 @@ jobs: - pre-commit steps: - name: Check out code from GitHub - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v3.2.0 - name: Set up Python ${{ env.DEFAULT_PYTHON }} - uses: actions/setup-python@v4.3.0 + uses: actions/setup-python@v4.4.0 id: python with: python-version: ${{ env.DEFAULT_PYTHON }} check-latest: true - name: Restore base Python virtual environment id: cache-venv - uses: actions/cache@v3.0.11 + uses: actions/cache/restore@v3.2.2 with: path: venv key: >- @@ -344,7 +344,7 @@ jobs: exit 1 - name: Restore pre-commit environment from cache id: cache-precommit - uses: actions/cache@v3.0.11 + uses: actions/cache/restore@v3.2.2 with: path: ${{ env.PRE_COMMIT_CACHE }} key: >- @@ -368,16 +368,16 @@ jobs: - pre-commit steps: - name: Check out code from GitHub - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v3.2.0 - name: Set up Python ${{ env.DEFAULT_PYTHON }} - uses: actions/setup-python@v4.3.0 + uses: actions/setup-python@v4.4.0 id: python with: python-version: ${{ env.DEFAULT_PYTHON }} check-latest: true - name: Restore base Python virtual environment id: cache-venv - uses: actions/cache@v3.0.11 + uses: actions/cache/restore@v3.2.2 with: path: venv key: >- @@ -390,7 +390,7 @@ jobs: exit 1 - name: Restore pre-commit environment from cache id: cache-precommit - uses: actions/cache@v3.0.11 + uses: actions/cache/restore@v3.2.2 with: path: ${{ env.PRE_COMMIT_CACHE }} key: >- @@ -413,7 +413,7 @@ jobs: run: | . venv/bin/activate shopt -s globstar - pre-commit run --hook-stage manual pyupgrade --files {homeassistant,tests}/components/${{ needs.info.outputs.integrations_glob }}/**/* --show-diff-on-failure + pre-commit run --hook-stage manual pyupgrade --files {homeassistant,tests}/components/${{ needs.info.outputs.integrations_glob }}/{*,**/*} --show-diff-on-failure - name: Register yamllint problem matcher run: | @@ -442,7 +442,7 @@ jobs: shell: bash run: | . venv/bin/activate - pre-commit run --hook-stage manual prettier --files {homeassistant,tests}/components/${{ needs.info.outputs.integrations_glob }}/**/* + pre-commit run --hook-stage manual prettier --files {homeassistant,tests}/components/${{ needs.info.outputs.integrations_glob }}/{*,**/*} - name: Register check executables problem matcher run: | @@ -483,7 +483,7 @@ jobs: run: | . venv/bin/activate shopt -s globstar - pre-commit run --hook-stage manual bandit --files {homeassistant,tests}/components/${{ needs.info.outputs.integrations_glob }}/**/* --show-diff-on-failure + pre-commit run --hook-stage manual bandit --files {homeassistant,tests}/components/${{ needs.info.outputs.integrations_glob }}/{*,**/*} --show-diff-on-failure base: name: Prepare dependencies @@ -495,10 +495,10 @@ jobs: python-version: ${{ fromJSON(needs.info.outputs.python_versions) }} steps: - name: Check out code from GitHub - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v3.2.0 - name: Set up Python ${{ matrix.python-version }} id: python - uses: actions/setup-python@v4.3.0 + uses: actions/setup-python@v4.4.0 with: python-version: ${{ matrix.python-version }} check-latest: true @@ -509,7 +509,7 @@ jobs: env.HA_SHORT_VERSION }}-$(date -u '+%Y-%m-%dT%H:%M:%s')" >> $GITHUB_OUTPUT - name: Restore base Python virtual environment id: cache-venv - uses: actions/cache@v3.0.11 + uses: actions/cache@v3.2.2 with: path: venv key: >- @@ -517,7 +517,7 @@ jobs: needs.info.outputs.python_cache_key }} - name: Restore pip wheel cache if: steps.cache-venv.outputs.cache-hit != 'true' - uses: actions/cache@v3.0.11 + uses: actions/cache@v3.2.2 with: path: ${{ env.PIP_CACHE }} key: >- @@ -559,16 +559,16 @@ jobs: - base steps: - name: Check out code from GitHub - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v3.2.0 - name: Set up Python ${{ env.DEFAULT_PYTHON }} id: python - uses: actions/setup-python@v4.3.0 + uses: actions/setup-python@v4.4.0 with: python-version: ${{ env.DEFAULT_PYTHON }} check-latest: true - name: Restore full Python ${{ env.DEFAULT_PYTHON }} virtual environment id: cache-venv - uses: actions/cache@v3.0.11 + uses: actions/cache/restore@v3.2.2 with: path: venv key: >- @@ -592,16 +592,16 @@ jobs: - base steps: - name: Check out code from GitHub - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v3.2.0 - name: Set up Python ${{ env.DEFAULT_PYTHON }} id: python - uses: actions/setup-python@v4.3.0 + uses: actions/setup-python@v4.4.0 with: python-version: ${{ env.DEFAULT_PYTHON }} check-latest: true - name: Restore base Python virtual environment id: cache-venv - uses: actions/cache@v3.0.11 + uses: actions/cache/restore@v3.2.2 with: path: venv key: >- @@ -626,16 +626,16 @@ jobs: - base steps: - name: Check out code from GitHub - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v3.2.0 - name: Set up Python ${{ env.DEFAULT_PYTHON }} id: python - uses: actions/setup-python@v4.3.0 + uses: actions/setup-python@v4.4.0 with: python-version: ${{ env.DEFAULT_PYTHON }} check-latest: true - name: Restore full Python ${{ env.DEFAULT_PYTHON }} virtual environment id: cache-venv - uses: actions/cache@v3.0.11 + uses: actions/cache/restore@v3.2.2 with: path: venv key: >- @@ -671,16 +671,16 @@ jobs: - base steps: - name: Check out code from GitHub - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v3.2.0 - name: Set up Python ${{ env.DEFAULT_PYTHON }} id: python - uses: actions/setup-python@v4.3.0 + uses: actions/setup-python@v4.4.0 with: python-version: ${{ env.DEFAULT_PYTHON }} check-latest: true - name: Restore full Python ${{ env.DEFAULT_PYTHON }} virtual environment id: cache-venv - uses: actions/cache@v3.0.11 + uses: actions/cache/restore@v3.2.2 with: path: venv key: >- @@ -720,16 +720,16 @@ jobs: name: Run pip check ${{ matrix.python-version }} steps: - name: Check out code from GitHub - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v3.2.0 - name: Set up Python ${{ matrix.python-version }} id: python - uses: actions/setup-python@v4.3.0 + uses: actions/setup-python@v4.4.0 with: python-version: ${{ matrix.python-version }} check-latest: true - name: Restore full Python ${{ matrix.python-version }} virtual environment id: cache-venv - uses: actions/cache@v3.0.11 + uses: actions/cache/restore@v3.2.2 with: path: venv key: >- @@ -775,16 +775,16 @@ jobs: bluez \ ffmpeg - name: Check out code from GitHub - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v3.2.0 - name: Set up Python ${{ matrix.python-version }} id: python - uses: actions/setup-python@v4.3.0 + uses: actions/setup-python@v4.4.0 with: python-version: ${{ matrix.python-version }} check-latest: true - name: Restore full Python ${{ matrix.python-version }} virtual environment id: cache-venv - uses: actions/cache@v3.0.11 + uses: actions/cache/restore@v3.2.2 with: path: venv key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{ @@ -898,16 +898,16 @@ jobs: ffmpeg \ libmariadb-dev-compat - name: Check out code from GitHub - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v3.2.0 - name: Set up Python ${{ matrix.python-version }} id: python - uses: actions/setup-python@v4.3.0 + uses: actions/setup-python@v4.4.0 with: python-version: ${{ matrix.python-version }} check-latest: true - name: Restore full Python ${{ matrix.python-version }} virtual environment id: cache-venv - uses: actions/cache@v3.0.11 + uses: actions/cache/restore@v3.2.2 with: path: venv key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{ @@ -970,7 +970,7 @@ jobs: - pytest steps: - name: Check out code from GitHub - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v3.2.0 - name: Download all coverage artifacts uses: actions/download-artifact@v3 - name: Upload coverage to Codecov (full coverage) diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml index 64b7a2cd8e3..c0593fa3a9a 100644 --- a/.github/workflows/lock.yml +++ b/.github/workflows/lock.yml @@ -10,7 +10,7 @@ jobs: if: github.repository_owner == 'home-assistant' runs-on: ubuntu-latest steps: - - uses: dessant/lock-threads@v3 + - uses: dessant/lock-threads@v4.0.0 with: github-token: ${{ github.token }} issue-inactive-days: "30" diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 914fa415051..7ae6a3477d7 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -17,7 +17,7 @@ jobs: # - No PRs marked as no-stale # - No issues marked as no-stale or help-wanted - name: 90 days stale issues & PRs policy - uses: actions/stale@v6.0.1 + uses: actions/stale@v7.0.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} days-before-stale: 90 @@ -54,7 +54,7 @@ jobs: # - No PRs marked as no-stale or new-integrations # - No issues (-1) - name: 30 days stale PRs policy - uses: actions/stale@v6.0.1 + uses: actions/stale@v7.0.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} days-before-stale: 30 @@ -79,7 +79,7 @@ jobs: # - No Issues marked as no-stale or help-wanted # - No PRs (-1) - name: Needs more information stale issues policy - uses: actions/stale@v6.0.1 + uses: actions/stale@v7.0.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} only-labels: "needs-more-information" diff --git a/.github/workflows/translations.yaml b/.github/workflows/translations.yaml index cbf8e26c9ec..1dcbcfabd3a 100644 --- a/.github/workflows/translations.yaml +++ b/.github/workflows/translations.yaml @@ -21,10 +21,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout the repository - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v3.2.0 - name: Set up Python ${{ env.DEFAULT_PYTHON }} - uses: actions/setup-python@v4.3.0 + uses: actions/setup-python@v4.4.0 with: python-version: ${{ env.DEFAULT_PYTHON }} @@ -40,10 +40,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout the repository - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v3.2.0 - name: Set up Python ${{ env.DEFAULT_PYTHON }} - uses: actions/setup-python@v4.3.0 + uses: actions/setup-python@v4.4.0 with: python-version: ${{ env.DEFAULT_PYTHON }} diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 672cf4fe4cb..20b758d032f 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -22,7 +22,7 @@ jobs: architectures: ${{ steps.info.outputs.architectures }} steps: - name: Checkout the repository - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v3.2.0 - name: Get information id: info @@ -79,7 +79,7 @@ jobs: arch: ${{ fromJson(needs.init.outputs.architectures) }} steps: - name: Checkout the repository - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v3.2.0 - name: Download env_file uses: actions/download-artifact@v3 @@ -116,7 +116,7 @@ jobs: arch: ${{ fromJson(needs.init.outputs.architectures) }} steps: - name: Checkout the repository - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v3.2.0 - name: Download env_file uses: actions/download-artifact@v3 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f7b1e54d0a4..d68fde0ec25 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/asottile/pyupgrade - rev: v3.2.2 + rev: v3.3.1 hooks: - id: pyupgrade args: [--py39-plus] @@ -12,11 +12,10 @@ repos: - --in-place - --remove-all-unused-imports - repo: https://github.com/psf/black - rev: 22.10.0 + rev: 22.12.0 hooks: - id: black args: - - --safe - --quiet files: ^((homeassistant|pylint|script|tests)/.+)?[^/]+\.py$ - repo: https://github.com/codespell-project/codespell @@ -24,7 +23,7 @@ repos: hooks: - id: codespell args: - - --ignore-words-list=additionals,alot,ba,bre,bund,datas,dof,dur,ether,farenheit,falsy,fo,haa,hass,hist,iam,iff,iif,incomfort,ines,ist,lightsensor,mut,nam,nd,pres,pullrequests,referer,resset,rime,ser,serie,sur,te,technik,ue,uint,unsecure,visability,wan,wanna,withing,zar + - --ignore-words-list=additionals,alle,alot,ba,bre,bund,datas,dof,dur,ether,farenheit,falsy,fo,haa,hass,hist,iam,iff,iif,incomfort,ines,ist,lightsensor,mut,nam,nd,pres,pullrequests,referer,resset,rime,ser,serie,sur,te,technik,ue,uint,unsecure,visability,wan,wanna,withing,zar - --skip="./.*,*.csv,*.json" - --quiet-level=2 exclude_types: [csv, json] @@ -52,7 +51,7 @@ repos: - --configfile=tests/bandit.yaml files: ^(homeassistant|script|tests)/.+\.py$ - repo: https://github.com/PyCQA/isort - rev: 5.10.1 + rev: 5.11.4 hooks: - id: isort - repo: https://github.com/pre-commit/pre-commit-hooks diff --git a/.strict-typing b/.strict-typing index e47533d6ca9..598f2bc1f6d 100644 --- a/.strict-typing +++ b/.strict-typing @@ -56,6 +56,7 @@ homeassistant.components.amazon_polly.* homeassistant.components.ambient_station.* homeassistant.components.amcrest.* homeassistant.components.ampio.* +homeassistant.components.analytics.* homeassistant.components.anthemav.* homeassistant.components.aqualogic.* homeassistant.components.aseko_pool_live.* @@ -92,6 +93,7 @@ homeassistant.components.device_tracker.* homeassistant.components.devolo_home_control.* homeassistant.components.devolo_home_network.* homeassistant.components.dhcp.* +homeassistant.components.diagnostics.* homeassistant.components.dlna_dmr.* homeassistant.components.dnsip.* homeassistant.components.dsmr.* @@ -126,9 +128,15 @@ homeassistant.components.google_sheets.* homeassistant.components.greeneye_monitor.* homeassistant.components.group.* homeassistant.components.guardian.* +homeassistant.components.hardkernel.* +homeassistant.components.hardware.* +homeassistant.components.here_travel_time.* homeassistant.components.history.* homeassistant.components.homeassistant.triggers.event homeassistant.components.homeassistant_alerts.* +homeassistant.components.homeassistant_hardware.* +homeassistant.components.homeassistant_sky_connect.* +homeassistant.components.homeassistant_yellow.* homeassistant.components.homekit homeassistant.components.homekit.accessories homeassistant.components.homekit.aidmanager @@ -152,8 +160,8 @@ homeassistant.components.http.* homeassistant.components.huawei_lte.* homeassistant.components.hyperion.* homeassistant.components.ibeacon.* -homeassistant.components.image.* homeassistant.components.image_processing.* +homeassistant.components.image_upload.* homeassistant.components.input_button.* homeassistant.components.input_select.* homeassistant.components.integration.* @@ -179,6 +187,7 @@ homeassistant.components.logger.* homeassistant.components.lookin.* homeassistant.components.luftdaten.* homeassistant.components.mailbox.* +homeassistant.components.mastodon.* homeassistant.components.matter.* homeassistant.components.media_player.* homeassistant.components.media_source.* @@ -218,10 +227,12 @@ homeassistant.components.powerwall.* homeassistant.components.proximity.* homeassistant.components.prusalink.* homeassistant.components.pure_energie.* +homeassistant.components.purpleair.* homeassistant.components.pvoutput.* homeassistant.components.qnap_qsw.* homeassistant.components.radarr.* homeassistant.components.rainmachine.* +homeassistant.components.raspberry_pi.* homeassistant.components.rdw.* homeassistant.components.recollect_waste.* homeassistant.components.recorder.* @@ -246,6 +257,7 @@ homeassistant.components.sensirion_ble.* homeassistant.components.sensor.* homeassistant.components.senz.* homeassistant.components.shelly.* +homeassistant.components.simplepush.* homeassistant.components.simplisafe.* homeassistant.components.skybell.* homeassistant.components.slack.* @@ -253,6 +265,7 @@ homeassistant.components.sleepiq.* homeassistant.components.smhi.* homeassistant.components.snooz.* homeassistant.components.sonarr.* +homeassistant.components.speedtestdotnet.* homeassistant.components.ssdp.* homeassistant.components.statistics.* homeassistant.components.steamist.* diff --git a/CODEOWNERS b/CODEOWNERS index 2966d69b032..464bb252a2e 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -55,6 +55,8 @@ build.json @home-assistant/supervisor /tests/components/airtouch4/ @LonePurpleWolf /homeassistant/components/airvisual/ @bachya /tests/components/airvisual/ @bachya +/homeassistant/components/airvisual_pro/ @bachya +/tests/components/airvisual_pro/ @bachya /homeassistant/components/airzone/ @Noltari /tests/components/airzone/ @Noltari /homeassistant/components/aladdin_connect/ @mkmer @@ -428,13 +430,15 @@ build.json @home-assistant/supervisor /tests/components/google/ @allenporter /homeassistant/components/google_assistant/ @home-assistant/cloud /tests/components/google_assistant/ @home-assistant/cloud +/homeassistant/components/google_assistant_sdk/ @tronikos +/tests/components/google_assistant_sdk/ @tronikos /homeassistant/components/google_cloud/ @lufton /homeassistant/components/google_sheets/ @tkdrob /tests/components/google_sheets/ @tkdrob /homeassistant/components/google_travel_time/ @eifinger /tests/components/google_travel_time/ @eifinger -/homeassistant/components/govee_ble/ @bdraco -/tests/components/govee_ble/ @bdraco +/homeassistant/components/govee_ble/ @bdraco @PierreAronnax +/tests/components/govee_ble/ @bdraco @PierreAronnax /homeassistant/components/gpsd/ @fabaff /homeassistant/components/gree/ @cmroche /tests/components/gree/ @cmroche @@ -525,10 +529,10 @@ build.json @home-assistant/supervisor /tests/components/icloud/ @Quentame @nzapponi /homeassistant/components/ign_sismologia/ @exxamalte /tests/components/ign_sismologia/ @exxamalte -/homeassistant/components/image/ @home-assistant/core -/tests/components/image/ @home-assistant/core /homeassistant/components/image_processing/ @home-assistant/core /tests/components/image_processing/ @home-assistant/core +/homeassistant/components/image_upload/ @home-assistant/core +/tests/components/image_upload/ @home-assistant/core /homeassistant/components/incomfort/ @zxdavb /homeassistant/components/influxdb/ @mdegat01 /tests/components/influxdb/ @mdegat01 @@ -666,8 +670,8 @@ build.json @home-assistant/supervisor /tests/components/lyric/ @timmo001 /homeassistant/components/mastodon/ @fabaff /homeassistant/components/matrix/ @tinloaf -/homeassistant/components/matter/ @MartinHjelmare @marcelveldt -/tests/components/matter/ @MartinHjelmare @marcelveldt +/homeassistant/components/matter/ @home-assistant/matter +/tests/components/matter/ @home-assistant/matter /homeassistant/components/mazda/ @bdr99 /tests/components/mazda/ @bdr99 /homeassistant/components/meater/ @Sotolotl @emontnemery @@ -798,8 +802,8 @@ build.json @home-assistant/supervisor /tests/components/number/ @home-assistant/core @Shulyaka /homeassistant/components/nut/ @bdraco @ollo69 /tests/components/nut/ @bdraco @ollo69 -/homeassistant/components/nws/ @MatthewFlamm -/tests/components/nws/ @MatthewFlamm +/homeassistant/components/nws/ @MatthewFlamm @kamiyo +/tests/components/nws/ @MatthewFlamm @kamiyo /homeassistant/components/nzbget/ @chriscla /tests/components/nzbget/ @chriscla /homeassistant/components/obihai/ @dshokouhi @@ -839,8 +843,8 @@ build.json @home-assistant/supervisor /homeassistant/components/oralb/ @bdraco /tests/components/oralb/ @bdraco /homeassistant/components/oru/ @bvlaicu -/homeassistant/components/overkiz/ @imicknl @vlebourl @tetienne -/tests/components/overkiz/ @imicknl @vlebourl @tetienne +/homeassistant/components/overkiz/ @imicknl @vlebourl @tetienne @nyroDev +/tests/components/overkiz/ @imicknl @vlebourl @tetienne @nyroDev /homeassistant/components/ovo_energy/ @timmo001 /tests/components/ovo_energy/ @timmo001 /homeassistant/components/p1_monitor/ @klaasnicolaas @@ -890,6 +894,8 @@ build.json @home-assistant/supervisor /tests/components/ps4/ @ktnrg45 /homeassistant/components/pure_energie/ @klaasnicolaas /tests/components/pure_energie/ @klaasnicolaas +/homeassistant/components/purpleair/ @bachya +/tests/components/purpleair/ @bachya /homeassistant/components/push/ @dgomes /tests/components/push/ @dgomes /homeassistant/components/pushbullet/ @engrbm87 @@ -919,7 +925,8 @@ build.json @home-assistant/supervisor /tests/components/radio_browser/ @frenck /homeassistant/components/radiotherm/ @bdraco @vinnyfuria /tests/components/radiotherm/ @bdraco @vinnyfuria -/homeassistant/components/rainbird/ @konikvranik +/homeassistant/components/rainbird/ @konikvranik @allenporter +/tests/components/rainbird/ @konikvranik @allenporter /homeassistant/components/raincloud/ @vanstinator /homeassistant/components/rainforest_eagle/ @gtdiehl @jcalbert @hastarin /tests/components/rainforest_eagle/ @gtdiehl @jcalbert @hastarin @@ -940,6 +947,8 @@ build.json @home-assistant/supervisor /tests/components/remote/ @home-assistant/core /homeassistant/components/renault/ @epenet /tests/components/renault/ @epenet +/homeassistant/components/reolink/ @starkillerOG +/tests/components/reolink/ @starkillerOG /homeassistant/components/repairs/ @home-assistant/core /tests/components/repairs/ @home-assistant/core /homeassistant/components/repetier/ @MTrab @ShadowBr0ther @@ -1127,8 +1136,8 @@ build.json @home-assistant/supervisor /tests/components/switch_as_x/ @home-assistant/core /homeassistant/components/switchbee/ @jafar-atili /tests/components/switchbee/ @jafar-atili -/homeassistant/components/switchbot/ @bdraco @danielhiversen @RenierM26 @murtas @Eloston -/tests/components/switchbot/ @bdraco @danielhiversen @RenierM26 @murtas @Eloston +/homeassistant/components/switchbot/ @bdraco @danielhiversen @RenierM26 @murtas @Eloston @dsypniewski +/tests/components/switchbot/ @bdraco @danielhiversen @RenierM26 @murtas @Eloston @dsypniewski /homeassistant/components/switcher_kis/ @tomerfi @thecode /tests/components/switcher_kis/ @tomerfi @thecode /homeassistant/components/switchmate/ @danielhiversen @qiz-li diff --git a/homeassistant/auth/__init__.py b/homeassistant/auth/__init__.py index bbd23983e2b..966536f446c 100644 --- a/homeassistant/auth/__init__.py +++ b/homeassistant/auth/__init__.py @@ -87,7 +87,7 @@ class AuthManagerFlowManager(data_entry_flow.FlowManager): async def async_create_flow( self, - handler_key: Any, + handler_key: str, *, context: dict[str, Any] | None = None, data: dict[str, Any] | None = None, @@ -534,7 +534,8 @@ class AuthManager: ) if provider is None: raise InvalidProvider( - f"Auth provider {refresh_token.credential.auth_provider_type}, {refresh_token.credential.auth_provider_id} not available" + f"Auth provider {refresh_token.credential.auth_provider_type}," + f" {refresh_token.credential.auth_provider_id} not available" ) return provider diff --git a/homeassistant/auth/auth_store.py b/homeassistant/auth/auth_store.py index 2597781dc60..5a9fb469e0d 100644 --- a/homeassistant/auth/auth_store.py +++ b/homeassistant/auth/auth_store.py @@ -449,8 +449,10 @@ class AuthStore: created_at = dt_util.parse_datetime(rt_dict["created_at"]) if created_at is None: getLogger(__name__).error( - "Ignoring refresh token %(id)s with invalid created_at " - "%(created_at)s for user_id %(user_id)s", + ( + "Ignoring refresh token %(id)s with invalid created_at " + "%(created_at)s for user_id %(user_id)s" + ), rt_dict, ) continue diff --git a/homeassistant/auth/mfa_modules/__init__.py b/homeassistant/auth/mfa_modules/__init__.py index ebfe1332cd2..b89982127a0 100644 --- a/homeassistant/auth/mfa_modules/__init__.py +++ b/homeassistant/auth/mfa_modules/__init__.py @@ -116,9 +116,7 @@ class SetupFlow(data_entry_flow.FlowHandler): if user_input: result = await self._auth_module.async_setup_user(self._user_id, user_input) - return self.async_create_entry( - title=self._auth_module.name, data={"result": result} - ) + return self.async_create_entry(data={"result": result}) return self.async_show_form( step_id="init", data_schema=self._setup_schema, errors=errors diff --git a/homeassistant/auth/mfa_modules/notify.py b/homeassistant/auth/mfa_modules/notify.py index 0a65c42b520..57989849367 100644 --- a/homeassistant/auth/mfa_modules/notify.py +++ b/homeassistant/auth/mfa_modules/notify.py @@ -26,7 +26,7 @@ from . import ( SetupFlow, ) -REQUIREMENTS = ["pyotp==2.7.0"] +REQUIREMENTS = ["pyotp==2.8.0"] CONF_MESSAGE = "message" @@ -330,7 +330,7 @@ class NotifySetupFlow(SetupFlow): self._user_id, {"notify_service": self._notify_service, "target": self._target}, ) - return self.async_create_entry(title=self._auth_module.name, data={}) + return self.async_create_entry(data={}) errors["base"] = "invalid_code" diff --git a/homeassistant/auth/mfa_modules/totp.py b/homeassistant/auth/mfa_modules/totp.py index 7db4919ba6f..0c02ef4bd8d 100644 --- a/homeassistant/auth/mfa_modules/totp.py +++ b/homeassistant/auth/mfa_modules/totp.py @@ -19,7 +19,7 @@ from . import ( SetupFlow, ) -REQUIREMENTS = ["pyotp==2.7.0", "PyQRCode==1.2.1"] +REQUIREMENTS = ["pyotp==2.8.0", "PyQRCode==1.2.1"] CONFIG_SCHEMA = MULTI_FACTOR_AUTH_MODULE_SCHEMA.extend({}, extra=vol.PREVENT_EXTRA) @@ -47,8 +47,10 @@ def _generate_qr_code(data: str) -> str: .decode("ascii") .replace("\n", "") .replace( - '' - '' + ' FlowResult: """Handle the pass of login flow.""" - return self.async_create_entry(title=self._auth_provider.name, data=flow_result) + return self.async_create_entry(data=flow_result) diff --git a/homeassistant/auth/providers/homeassistant.py b/homeassistant/auth/providers/homeassistant.py index d190a618596..050a0660a6b 100644 --- a/homeassistant/auth/providers/homeassistant.py +++ b/homeassistant/auth/providers/homeassistant.py @@ -93,9 +93,11 @@ class Data: self.is_legacy = True logging.getLogger(__name__).warning( - "Home Assistant auth provider is running in legacy mode " - "because we detected usernames that are case-insensitive" - "equivalent. Please change the username: '%s'.", + ( + "Home Assistant auth provider is running in legacy mode " + "because we detected usernames that are case-insensitive" + "equivalent. Please change the username: '%s'." + ), username, ) @@ -108,9 +110,11 @@ class Data: self.is_legacy = True logging.getLogger(__name__).warning( - "Home Assistant auth provider is running in legacy mode " - "because we detected usernames that start or end in a " - "space. Please change the username: '%s'.", + ( + "Home Assistant auth provider is running in legacy mode " + "because we detected usernames that start or end in a " + "space. Please change the username: '%s'." + ), username, ) diff --git a/homeassistant/bootstrap.py b/homeassistant/bootstrap.py index 6b91557a476..c8858293706 100644 --- a/homeassistant/bootstrap.py +++ b/homeassistant/bootstrap.py @@ -284,8 +284,7 @@ async def async_from_config_dict( return None except HomeAssistantError: _LOGGER.error( - "Home Assistant core failed to initialize. " - "Further initialization aborted" + "Home Assistant core failed to initialize. Further initialization aborted" ) return None diff --git a/homeassistant/brands/airvisual.json b/homeassistant/brands/airvisual.json new file mode 100644 index 00000000000..2f9e7588a77 --- /dev/null +++ b/homeassistant/brands/airvisual.json @@ -0,0 +1,5 @@ +{ + "domain": "airvisual", + "name": "AirVisual", + "integrations": ["airvisual", "airvisual_pro"] +} diff --git a/homeassistant/brands/google.json b/homeassistant/brands/google.json index de27fa7c515..cceda7505c6 100644 --- a/homeassistant/brands/google.json +++ b/homeassistant/brands/google.json @@ -3,6 +3,7 @@ "name": "Google", "integrations": [ "google_assistant", + "google_assistant_sdk", "google_cloud", "google_domains", "google_maps", diff --git a/homeassistant/components/abode/translations/de.json b/homeassistant/components/abode/translations/de.json index 695ecba621c..b3bd4945b9f 100644 --- a/homeassistant/components/abode/translations/de.json +++ b/homeassistant/components/abode/translations/de.json @@ -28,7 +28,7 @@ "password": "Passwort", "username": "E-Mail" }, - "title": "Gib deine Abode-Anmeldeinformationen ein" + "title": "Gib deine Abode Anmeldeinformationen ein" } } } diff --git a/homeassistant/components/abode/translations/pt.json b/homeassistant/components/abode/translations/pt.json index 7dc8448b288..dead07a005a 100644 --- a/homeassistant/components/abode/translations/pt.json +++ b/homeassistant/components/abode/translations/pt.json @@ -5,7 +5,7 @@ "single_instance_allowed": "J\u00e1 configurado. Apenas uma \u00fanica configura\u00e7\u00e3o \u00e9 poss\u00edvel." }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "invalid_mfa_code": "C\u00f3digo MFA inv\u00e1lido" }, @@ -19,14 +19,14 @@ "reauth_confirm": { "data": { "password": "Palavra-passe", - "username": "Email" + "username": "" }, "title": "Preencha as informa\u00e7\u00f5es de login de Abode" }, "user": { "data": { "password": "Palavra-passe", - "username": "Email" + "username": "" }, "title": "Preencha as informa\u00e7\u00f5es de login de Abode" } diff --git a/homeassistant/components/accuweather/sensor.py b/homeassistant/components/accuweather/sensor.py index 2dbefe19965..9fd80362320 100644 --- a/homeassistant/components/accuweather/sensor.py +++ b/homeassistant/components/accuweather/sensor.py @@ -14,17 +14,13 @@ from homeassistant.components.sensor import ( from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONCENTRATION_PARTS_PER_CUBIC_METER, - LENGTH_FEET, - LENGTH_INCHES, - LENGTH_METERS, - LENGTH_MILLIMETERS, PERCENTAGE, - SPEED_KILOMETERS_PER_HOUR, - SPEED_MILES_PER_HOUR, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, - TIME_HOURS, UV_INDEX, + UnitOfLength, + UnitOfPrecipitationDepth, + UnitOfSpeed, + UnitOfTemperature, + UnitOfTime, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -65,7 +61,8 @@ class AccuWeatherSensorDescription( """Class describing AccuWeather sensor entities.""" attr_fn: Callable[[dict[str, Any]], dict[str, StateType]] = lambda _: {} - unit_fn: Callable[[bool], str | None] = lambda _: None + metric_unit: str | None = None + us_customary_unit: str | None = None FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = ( @@ -74,7 +71,7 @@ FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = ( icon="mdi:weather-cloudy", name="Cloud cover day", entity_registry_enabled_default=False, - unit_fn=lambda _: PERCENTAGE, + native_unit_of_measurement=PERCENTAGE, value_fn=lambda data, _: cast(int, data), ), AccuWeatherSensorDescription( @@ -82,7 +79,7 @@ FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = ( icon="mdi:weather-cloudy", name="Cloud cover night", entity_registry_enabled_default=False, - unit_fn=lambda _: PERCENTAGE, + native_unit_of_measurement=PERCENTAGE, value_fn=lambda data, _: cast(int, data), ), AccuWeatherSensorDescription( @@ -90,7 +87,7 @@ FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = ( icon="mdi:grass", name="Grass pollen", entity_registry_enabled_default=False, - unit_fn=lambda _: CONCENTRATION_PARTS_PER_CUBIC_METER, + native_unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER, value_fn=lambda data, _: cast(int, data[ATTR_VALUE]), attr_fn=lambda data: {ATTR_LEVEL: data[ATTR_CATEGORY]}, ), @@ -98,7 +95,7 @@ FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = ( key="HoursOfSun", icon="mdi:weather-partly-cloudy", name="Hours of sun", - unit_fn=lambda _: TIME_HOURS, + native_unit_of_measurement=UnitOfTime.HOURS, value_fn=lambda data, _: cast(float, data), ), AccuWeatherSensorDescription( @@ -106,7 +103,7 @@ FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = ( icon="mdi:blur", name="Mold pollen", entity_registry_enabled_default=False, - unit_fn=lambda _: CONCENTRATION_PARTS_PER_CUBIC_METER, + native_unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER, value_fn=lambda data, _: cast(int, data[ATTR_VALUE]), attr_fn=lambda data: {ATTR_LEVEL: data[ATTR_CATEGORY]}, ), @@ -122,7 +119,7 @@ FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = ( key="Ragweed", icon="mdi:sprout", name="Ragweed pollen", - unit_fn=lambda _: CONCENTRATION_PARTS_PER_CUBIC_METER, + native_unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER, entity_registry_enabled_default=False, value_fn=lambda data, _: cast(int, data[ATTR_VALUE]), attr_fn=lambda data: {ATTR_LEVEL: data[ATTR_CATEGORY]}, @@ -131,14 +128,16 @@ FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = ( key="RealFeelTemperatureMax", device_class=SensorDeviceClass.TEMPERATURE, name="RealFeel temperature max", - unit_fn=lambda metric: TEMP_CELSIUS if metric else TEMP_FAHRENHEIT, + metric_unit=UnitOfTemperature.CELSIUS, + us_customary_unit=UnitOfTemperature.FAHRENHEIT, value_fn=lambda data, _: cast(float, data[ATTR_VALUE]), ), AccuWeatherSensorDescription( key="RealFeelTemperatureMin", device_class=SensorDeviceClass.TEMPERATURE, name="RealFeel temperature min", - unit_fn=lambda metric: TEMP_CELSIUS if metric else TEMP_FAHRENHEIT, + metric_unit=UnitOfTemperature.CELSIUS, + us_customary_unit=UnitOfTemperature.FAHRENHEIT, value_fn=lambda data, _: cast(float, data[ATTR_VALUE]), ), AccuWeatherSensorDescription( @@ -146,7 +145,8 @@ FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = ( device_class=SensorDeviceClass.TEMPERATURE, name="RealFeel temperature shade max", entity_registry_enabled_default=False, - unit_fn=lambda metric: TEMP_CELSIUS if metric else TEMP_FAHRENHEIT, + metric_unit=UnitOfTemperature.CELSIUS, + us_customary_unit=UnitOfTemperature.FAHRENHEIT, value_fn=lambda data, _: cast(float, data[ATTR_VALUE]), ), AccuWeatherSensorDescription( @@ -154,28 +154,29 @@ FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = ( device_class=SensorDeviceClass.TEMPERATURE, name="RealFeel temperature shade min", entity_registry_enabled_default=False, - unit_fn=lambda metric: TEMP_CELSIUS if metric else TEMP_FAHRENHEIT, + metric_unit=UnitOfTemperature.CELSIUS, + us_customary_unit=UnitOfTemperature.FAHRENHEIT, value_fn=lambda data, _: cast(float, data[ATTR_VALUE]), ), AccuWeatherSensorDescription( key="ThunderstormProbabilityDay", icon="mdi:weather-lightning", name="Thunderstorm probability day", - unit_fn=lambda _: PERCENTAGE, + native_unit_of_measurement=PERCENTAGE, value_fn=lambda data, _: cast(int, data), ), AccuWeatherSensorDescription( key="ThunderstormProbabilityNight", icon="mdi:weather-lightning", name="Thunderstorm probability night", - unit_fn=lambda _: PERCENTAGE, + native_unit_of_measurement=PERCENTAGE, value_fn=lambda data, _: cast(int, data), ), AccuWeatherSensorDescription( key="Tree", icon="mdi:tree-outline", name="Tree pollen", - unit_fn=lambda _: CONCENTRATION_PARTS_PER_CUBIC_METER, + native_unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER, entity_registry_enabled_default=False, value_fn=lambda data, _: cast(int, data[ATTR_VALUE]), attr_fn=lambda data: {ATTR_LEVEL: data[ATTR_CATEGORY]}, @@ -184,53 +185,45 @@ FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = ( key="UVIndex", icon="mdi:weather-sunny", name="UV index", - unit_fn=lambda _: UV_INDEX, + native_unit_of_measurement=UV_INDEX, value_fn=lambda data, _: cast(int, data[ATTR_VALUE]), attr_fn=lambda data: {ATTR_LEVEL: data[ATTR_CATEGORY]}, ), AccuWeatherSensorDescription( key="WindGustDay", device_class=SensorDeviceClass.WIND_SPEED, - icon="mdi:weather-windy", name="Wind gust day", entity_registry_enabled_default=False, - unit_fn=lambda metric: SPEED_KILOMETERS_PER_HOUR - if metric - else SPEED_MILES_PER_HOUR, + metric_unit=UnitOfSpeed.KILOMETERS_PER_HOUR, + us_customary_unit=UnitOfSpeed.MILES_PER_HOUR, value_fn=lambda data, _: cast(float, data[ATTR_SPEED][ATTR_VALUE]), attr_fn=lambda data: {"direction": data[ATTR_DIRECTION][ATTR_ENGLISH]}, ), AccuWeatherSensorDescription( key="WindGustNight", device_class=SensorDeviceClass.WIND_SPEED, - icon="mdi:weather-windy", name="Wind gust night", entity_registry_enabled_default=False, - unit_fn=lambda metric: SPEED_KILOMETERS_PER_HOUR - if metric - else SPEED_MILES_PER_HOUR, + metric_unit=UnitOfSpeed.KILOMETERS_PER_HOUR, + us_customary_unit=UnitOfSpeed.MILES_PER_HOUR, value_fn=lambda data, _: cast(float, data[ATTR_SPEED][ATTR_VALUE]), attr_fn=lambda data: {"direction": data[ATTR_DIRECTION][ATTR_ENGLISH]}, ), AccuWeatherSensorDescription( key="WindDay", device_class=SensorDeviceClass.WIND_SPEED, - icon="mdi:weather-windy", name="Wind day", - unit_fn=lambda metric: SPEED_KILOMETERS_PER_HOUR - if metric - else SPEED_MILES_PER_HOUR, + metric_unit=UnitOfSpeed.KILOMETERS_PER_HOUR, + us_customary_unit=UnitOfSpeed.MILES_PER_HOUR, value_fn=lambda data, _: cast(float, data[ATTR_SPEED][ATTR_VALUE]), attr_fn=lambda data: {"direction": data[ATTR_DIRECTION][ATTR_ENGLISH]}, ), AccuWeatherSensorDescription( key="WindNight", device_class=SensorDeviceClass.WIND_SPEED, - icon="mdi:weather-windy", name="Wind night", - unit_fn=lambda metric: SPEED_KILOMETERS_PER_HOUR - if metric - else SPEED_MILES_PER_HOUR, + metric_unit=UnitOfSpeed.KILOMETERS_PER_HOUR, + us_customary_unit=UnitOfSpeed.MILES_PER_HOUR, value_fn=lambda data, _: cast(float, data[ATTR_SPEED][ATTR_VALUE]), attr_fn=lambda data: {"direction": data[ATTR_DIRECTION][ATTR_ENGLISH]}, ), @@ -243,7 +236,8 @@ SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = ( name="Apparent temperature", entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, - unit_fn=lambda metric: TEMP_CELSIUS if metric else TEMP_FAHRENHEIT, + metric_unit=UnitOfTemperature.CELSIUS, + us_customary_unit=UnitOfTemperature.FAHRENHEIT, value_fn=lambda data, unit: cast(float, data[unit][ATTR_VALUE]), ), AccuWeatherSensorDescription( @@ -252,7 +246,8 @@ SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = ( icon="mdi:weather-fog", name="Cloud ceiling", state_class=SensorStateClass.MEASUREMENT, - unit_fn=lambda metric: LENGTH_METERS if metric else LENGTH_FEET, + metric_unit=UnitOfLength.METERS, + us_customary_unit=UnitOfLength.FEET, value_fn=lambda data, unit: round(cast(float, data[unit][ATTR_VALUE])), ), AccuWeatherSensorDescription( @@ -261,7 +256,7 @@ SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = ( name="Cloud cover", entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, - unit_fn=lambda _: PERCENTAGE, + native_unit_of_measurement=PERCENTAGE, value_fn=lambda data, _: cast(int, data), ), AccuWeatherSensorDescription( @@ -270,7 +265,8 @@ SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = ( name="Dew point", entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, - unit_fn=lambda metric: TEMP_CELSIUS if metric else TEMP_FAHRENHEIT, + metric_unit=UnitOfTemperature.CELSIUS, + us_customary_unit=UnitOfTemperature.FAHRENHEIT, value_fn=lambda data, unit: cast(float, data[unit][ATTR_VALUE]), ), AccuWeatherSensorDescription( @@ -278,7 +274,8 @@ SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = ( device_class=SensorDeviceClass.TEMPERATURE, name="RealFeel temperature", state_class=SensorStateClass.MEASUREMENT, - unit_fn=lambda metric: TEMP_CELSIUS if metric else TEMP_FAHRENHEIT, + metric_unit=UnitOfTemperature.CELSIUS, + us_customary_unit=UnitOfTemperature.FAHRENHEIT, value_fn=lambda data, unit: cast(float, data[unit][ATTR_VALUE]), ), AccuWeatherSensorDescription( @@ -287,23 +284,25 @@ SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = ( name="RealFeel temperature shade", entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, - unit_fn=lambda metric: TEMP_CELSIUS if metric else TEMP_FAHRENHEIT, + metric_unit=UnitOfTemperature.CELSIUS, + us_customary_unit=UnitOfTemperature.FAHRENHEIT, value_fn=lambda data, unit: cast(float, data[unit][ATTR_VALUE]), ), AccuWeatherSensorDescription( key="Precipitation", - icon="mdi:weather-rainy", + device_class=SensorDeviceClass.PRECIPITATION, name="Precipitation", state_class=SensorStateClass.MEASUREMENT, - unit_fn=lambda metric: LENGTH_MILLIMETERS if metric else LENGTH_INCHES, + metric_unit=UnitOfPrecipitationDepth.MILLIMETERS, + us_customary_unit=UnitOfPrecipitationDepth.INCHES, value_fn=lambda data, unit: cast(float, data[unit][ATTR_VALUE]), attr_fn=lambda data: {"type": data["PrecipitationType"]}, ), AccuWeatherSensorDescription( key="PressureTendency", - device_class="accuweather__pressure_tendency", icon="mdi:gauge", name="Pressure tendency", + translation_key="pressure_tendency", value_fn=lambda data, _: cast(str, data["LocalizedText"]).lower(), ), AccuWeatherSensorDescription( @@ -311,7 +310,7 @@ SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = ( icon="mdi:weather-sunny", name="UV index", state_class=SensorStateClass.MEASUREMENT, - unit_fn=lambda _: UV_INDEX, + native_unit_of_measurement=UV_INDEX, value_fn=lambda data, _: cast(int, data), attr_fn=lambda data: {ATTR_LEVEL: data["UVIndexText"]}, ), @@ -321,7 +320,8 @@ SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = ( name="Wet bulb temperature", entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, - unit_fn=lambda metric: TEMP_CELSIUS if metric else TEMP_FAHRENHEIT, + metric_unit=UnitOfTemperature.CELSIUS, + us_customary_unit=UnitOfTemperature.FAHRENHEIT, value_fn=lambda data, unit: cast(float, data[unit][ATTR_VALUE]), ), AccuWeatherSensorDescription( @@ -330,30 +330,27 @@ SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = ( name="Wind chill temperature", entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, - unit_fn=lambda metric: TEMP_CELSIUS if metric else TEMP_FAHRENHEIT, + metric_unit=UnitOfTemperature.CELSIUS, + us_customary_unit=UnitOfTemperature.FAHRENHEIT, value_fn=lambda data, unit: cast(float, data[unit][ATTR_VALUE]), ), AccuWeatherSensorDescription( key="Wind", device_class=SensorDeviceClass.WIND_SPEED, - icon="mdi:weather-windy", name="Wind", state_class=SensorStateClass.MEASUREMENT, - unit_fn=lambda metric: SPEED_KILOMETERS_PER_HOUR - if metric - else SPEED_MILES_PER_HOUR, + metric_unit=UnitOfSpeed.KILOMETERS_PER_HOUR, + us_customary_unit=UnitOfSpeed.MILES_PER_HOUR, value_fn=lambda data, unit: cast(float, data[ATTR_SPEED][unit][ATTR_VALUE]), ), AccuWeatherSensorDescription( key="WindGust", device_class=SensorDeviceClass.WIND_SPEED, - icon="mdi:weather-windy", name="Wind gust", entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, - unit_fn=lambda metric: SPEED_KILOMETERS_PER_HOUR - if metric - else SPEED_MILES_PER_HOUR, + metric_unit=UnitOfSpeed.KILOMETERS_PER_HOUR, + us_customary_unit=UnitOfSpeed.MILES_PER_HOUR, value_fn=lambda data, unit: cast(float, data[ATTR_SPEED][unit][ATTR_VALUE]), ), ) @@ -413,13 +410,15 @@ class AccuWeatherSensor( self._attr_unique_id = ( f"{coordinator.location_key}-{description.key}".lower() ) + self._attr_native_unit_of_measurement = description.native_unit_of_measurement if self.coordinator.hass.config.units is METRIC_SYSTEM: self._unit_system = API_METRIC + if metric_unit := description.metric_unit: + self._attr_native_unit_of_measurement = metric_unit else: self._unit_system = API_IMPERIAL - self._attr_native_unit_of_measurement = self.entity_description.unit_fn( - self.coordinator.hass.config.units is METRIC_SYSTEM - ) + if us_customary_unit := description.us_customary_unit: + self._attr_native_unit_of_measurement = us_customary_unit self._attr_device_info = coordinator.device_info if forecast_day is not None: self.forecast_day = forecast_day diff --git a/homeassistant/components/accuweather/strings.json b/homeassistant/components/accuweather/strings.json index ba1bba21d9e..d37b5a10776 100644 --- a/homeassistant/components/accuweather/strings.json +++ b/homeassistant/components/accuweather/strings.json @@ -22,6 +22,17 @@ "single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]" } }, + "entity": { + "sensor": { + "pressure_tendency": { + "state": { + "steady": "Steady", + "rising": "Rising", + "falling": "Falling" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/accuweather/strings.sensor.json b/homeassistant/components/accuweather/strings.sensor.json deleted file mode 100644 index 57cb89bcecf..00000000000 --- a/homeassistant/components/accuweather/strings.sensor.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "state": { - "accuweather__pressure_tendency": { - "steady": "Steady", - "rising": "Rising", - "falling": "Falling" - } - } -} diff --git a/homeassistant/components/accuweather/translations/ar.json b/homeassistant/components/accuweather/translations/ar.json index 5aadd35df92..b5806b42f2d 100644 --- a/homeassistant/components/accuweather/translations/ar.json +++ b/homeassistant/components/accuweather/translations/ar.json @@ -4,16 +4,6 @@ "requests_exceeded": "\u062a\u0645 \u062a\u062c\u0627\u0648\u0632 \u0627\u0644\u0639\u062f\u062f \u0627\u0644\u0645\u0633\u0645\u0648\u062d \u0628\u0647 \u0645\u0646 \u0627\u0644\u0637\u0644\u0628\u0627\u062a \u0625\u0644\u0649 Accuweather API. \u0639\u0644\u064a\u0643 \u0627\u0644\u0627\u0646\u062a\u0638\u0627\u0631 \u0623\u0648 \u062a\u063a\u064a\u064a\u0631 \u0645\u0641\u062a\u0627\u062d API." } }, - "options": { - "step": { - "user": { - "data": { - "forecast": "\u0627\u0644\u0646\u0634\u0631\u0629 \u0627\u0644\u062c\u0648\u064a\u0629" - }, - "description": "\u0646\u0638\u0631\u064b\u0627 \u0644\u0642\u064a\u0648\u062f \u0627\u0644\u0625\u0635\u062f\u0627\u0631 \u0627\u0644\u0645\u062c\u0627\u0646\u064a \u0645\u0646 \u0645\u0641\u062a\u0627\u062d AccuWeather API \u060c \u0639\u0646\u062f \u062a\u0645\u0643\u064a\u0646 \u0627\u0644\u062a\u0646\u0628\u0624 \u0628\u0627\u0644\u0637\u0642\u0633 \u060c \u0633\u064a\u062a\u0645 \u0625\u062c\u0631\u0627\u0621 \u062a\u062d\u062f\u064a\u062b\u0627\u062a \u0627\u0644\u0628\u064a\u0627\u0646\u0627\u062a \u0643\u0644 80 \u062f\u0642\u064a\u0642\u0629 \u0628\u062f\u0644\u0627\u064b \u0645\u0646 \u0643\u0644 40 \u062f\u0642\u064a\u0642\u0629." - } - } - }, "system_health": { "info": { "can_reach_server": "\u0627\u0644\u0648\u0635\u0648\u0644 \u0625\u0644\u0649 \u062e\u0627\u062f\u0645 AccuWeather", diff --git a/homeassistant/components/accuweather/translations/bg.json b/homeassistant/components/accuweather/translations/bg.json index 9c5e3075643..85f9cb24499 100644 --- a/homeassistant/components/accuweather/translations/bg.json +++ b/homeassistant/components/accuweather/translations/bg.json @@ -18,6 +18,17 @@ } } }, + "entity": { + "sensor": { + "pressure_tendency": { + "state": { + "falling": "\u041f\u043e\u043d\u0438\u0436\u0435\u043d\u0438\u0435", + "rising": "\u041f\u043e\u0432\u0438\u0448\u0435\u043d\u0438\u0435", + "steady": "\u0421\u0442\u0430\u0431\u0438\u043b\u043d\u043e" + } + } + } + }, "options": { "step": { "init": { @@ -25,11 +36,6 @@ "forecast": "\u041f\u0440\u043e\u0433\u043d\u043e\u0437\u0430 \u0437\u0430 \u0432\u0440\u0435\u043c\u0435\u0442\u043e" }, "description": "\u041f\u043e\u0440\u0430\u0434\u0438 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f\u0442\u0430 \u043d\u0430 \u0431\u0435\u0437\u043f\u043b\u0430\u0442\u043d\u0430\u0442\u0430 \u0432\u0435\u0440\u0441\u0438\u044f \u043d\u0430 API \u043a\u043b\u044e\u0447\u0430 \u043d\u0430 AccuWeather, \u043a\u043e\u0433\u0430\u0442\u043e \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u0430\u0442\u0435 \u043f\u0440\u043e\u0433\u043d\u043e\u0437\u0430\u0442\u0430 \u0437\u0430 \u0432\u0440\u0435\u043c\u0435\u0442\u043e, \u0430\u043a\u0442\u0443\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438\u0442\u0435 \u043d\u0430 \u0434\u0430\u043d\u043d\u0438 \u0449\u0435 \u0441\u0435 \u0438\u0437\u0432\u044a\u0440\u0448\u0432\u0430\u0442 \u043d\u0430 \u0432\u0441\u0435\u043a\u0438 80 \u043c\u0438\u043d\u0443\u0442\u0438 \u0432\u043c\u0435\u0441\u0442\u043e \u043d\u0430 \u0432\u0441\u0435\u043a\u0438 40 \u043c\u0438\u043d\u0443\u0442\u0438." - }, - "user": { - "data": { - "forecast": "\u041f\u0440\u043e\u0433\u043d\u043e\u0437\u0430 \u0437\u0430 \u0432\u0440\u0435\u043c\u0435\u0442\u043e" - } } } } diff --git a/homeassistant/components/accuweather/translations/ca.json b/homeassistant/components/accuweather/translations/ca.json index 54b93643e8e..0c04fd1e23f 100644 --- a/homeassistant/components/accuweather/translations/ca.json +++ b/homeassistant/components/accuweather/translations/ca.json @@ -22,18 +22,24 @@ } } }, + "entity": { + "sensor": { + "pressure_tendency": { + "state": { + "falling": "Disminuint", + "rising": "Augmentant", + "steady": "Estable" + } + } + } + }, "options": { "step": { "init": { - "data": { - "forecast": "Previsi\u00f3 meteorol\u00f2gica" - } - }, - "user": { "data": { "forecast": "Previsi\u00f3 meteorol\u00f2gica" }, - "description": "Per culpa de les limitacions de la versi\u00f3 gratu\u00efta l'API d'AccuWeather, quan habilitis la previsi\u00f3 meteorol\u00f2gica, les actualitzacions de dades es faran cada 80 minuts en comptes de cada 40." + "description": "Per culpa de les limitacions de la versi\u00f3 gratu\u00efta de l'API d'AccuWeather, quan activis la previsi\u00f3 meteorol\u00f2gica, les actualitzacions de dades es faran cada 80 minuts en comptes de cada 40." } } }, diff --git a/homeassistant/components/accuweather/translations/cs.json b/homeassistant/components/accuweather/translations/cs.json index f0796ff4d1e..189c6bfe4fa 100644 --- a/homeassistant/components/accuweather/translations/cs.json +++ b/homeassistant/components/accuweather/translations/cs.json @@ -19,6 +19,15 @@ } } }, + "entity": { + "sensor": { + "pressure_tendency": { + "state": { + "steady": "Stabiln\u00ed" + } + } + } + }, "options": { "step": { "init": { @@ -26,12 +35,6 @@ "forecast": "P\u0159edpov\u011b\u010f po\u010das\u00ed" }, "description": "Vzhledem k omezen\u00edm bezplatn\u00e9 verze kl\u00ed\u010de AccuWeather API, kdy\u017e povol\u00edte p\u0159edpov\u011b\u010f po\u010das\u00ed, aktualizace dat se budou prov\u00e1d\u011bt ka\u017ed\u00fdch 80 minut m\u00edsto ka\u017ed\u00fdch 40 minut." - }, - "user": { - "data": { - "forecast": "P\u0159edpov\u011b\u010f po\u010das\u00ed" - }, - "description": "Kdy\u017e povol\u00edte p\u0159edpov\u011b\u010f po\u010das\u00ed, budou aktualizace dat prov\u00e1d\u011bny ka\u017ed\u00fdch 80 minut nam\u00edsto 40 minut z d\u016fvodu omezen\u00ed bezplatn\u00e9 verze AccuWeather." } } }, diff --git a/homeassistant/components/accuweather/translations/de.json b/homeassistant/components/accuweather/translations/de.json index 6a3c887d881..ebea1a968db 100644 --- a/homeassistant/components/accuweather/translations/de.json +++ b/homeassistant/components/accuweather/translations/de.json @@ -22,6 +22,17 @@ } } }, + "entity": { + "sensor": { + "pressure_tendency": { + "state": { + "falling": "Fallend", + "rising": "Steigend", + "steady": "Stetig" + } + } + } + }, "options": { "step": { "init": { @@ -29,12 +40,6 @@ "forecast": "Wettervorhersage" }, "description": "Aufgrund der Einschr\u00e4nkungen der kostenlosen Version des AccuWeather API-Schl\u00fcssels werden bei aktivierter Wettervorhersage Datenaktualisierungen alle 80 Minuten statt alle 40 Minuten durchgef\u00fchrt." - }, - "user": { - "data": { - "forecast": "Wettervorhersage" - }, - "description": "Aufgrund der Einschr\u00e4nkungen der kostenlosen Version des AccuWeather-API-Schl\u00fcssels werden bei aktivierter Wettervorhersage Datenaktualisierungen alle 80 Minuten statt alle 40 Minuten durchgef\u00fchrt." } } }, diff --git a/homeassistant/components/accuweather/translations/el.json b/homeassistant/components/accuweather/translations/el.json index a0eca3e75fc..4cb30216625 100644 --- a/homeassistant/components/accuweather/translations/el.json +++ b/homeassistant/components/accuweather/translations/el.json @@ -22,6 +22,17 @@ } } }, + "entity": { + "sensor": { + "pressure_tendency": { + "state": { + "falling": "\u03a0\u03c4\u03ce\u03c3\u03b7", + "rising": "\u0391\u03c5\u03be\u03b1\u03bd\u03cc\u03bc\u03b5\u03bd\u03b7", + "steady": "\u03a3\u03c4\u03b1\u03b8\u03b5\u03c1\u03ae" + } + } + } + }, "options": { "step": { "init": { @@ -29,12 +40,6 @@ "forecast": "\u03a0\u03c1\u03cc\u03b3\u03bd\u03c9\u03c3\u03b7 \u03ba\u03b1\u03b9\u03c1\u03bf\u03cd" }, "description": "\u039b\u03cc\u03b3\u03c9 \u03c4\u03c9\u03bd \u03c0\u03b5\u03c1\u03b9\u03bf\u03c1\u03b9\u03c3\u03bc\u03ce\u03bd \u03c4\u03b7\u03c2 \u03b4\u03c9\u03c1\u03b5\u03ac\u03bd \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 \u03ba\u03bb\u03b5\u03b9\u03b4\u03b9\u03bf\u03cd AccuWeather API, \u03cc\u03c4\u03b1\u03bd \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b5 \u03c4\u03b7\u03bd \u03c0\u03c1\u03cc\u03b3\u03bd\u03c9\u03c3\u03b7 \u03ba\u03b1\u03b9\u03c1\u03bf\u03cd, \u03bf\u03b9 \u03b5\u03bd\u03b7\u03bc\u03b5\u03c1\u03ce\u03c3\u03b5\u03b9\u03c2 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03c9\u03bd \u03b8\u03b1 \u03b5\u03ba\u03c4\u03b5\u03bb\u03bf\u03cd\u03bd\u03c4\u03b1\u03b9 \u03ba\u03ac\u03b8\u03b5 80 \u03bb\u03b5\u03c0\u03c4\u03ac \u03b1\u03bd\u03c4\u03af \u03b3\u03b9\u03b1 \u03ba\u03ac\u03b8\u03b5 40 \u03bb\u03b5\u03c0\u03c4\u03ac." - }, - "user": { - "data": { - "forecast": "\u03a0\u03c1\u03cc\u03b3\u03bd\u03c9\u03c3\u03b7 \u03ba\u03b1\u03b9\u03c1\u03bf\u03cd" - }, - "description": "\u039b\u03cc\u03b3\u03c9 \u03c4\u03c9\u03bd \u03c0\u03b5\u03c1\u03b9\u03bf\u03c1\u03b9\u03c3\u03bc\u03ce\u03bd \u03c4\u03b7\u03c2 \u03b4\u03c9\u03c1\u03b5\u03ac\u03bd \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 \u03ba\u03bb\u03b5\u03b9\u03b4\u03b9\u03bf\u03cd API \u03c4\u03bf\u03c5 AccuWeather, \u03cc\u03c4\u03b1\u03bd \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b5 \u03c4\u03b7\u03bd \u03c0\u03c1\u03cc\u03b3\u03bd\u03c9\u03c3\u03b7 \u03ba\u03b1\u03b9\u03c1\u03bf\u03cd, \u03bf\u03b9 \u03b5\u03bd\u03b7\u03bc\u03b5\u03c1\u03ce\u03c3\u03b5\u03b9\u03c2 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03c9\u03bd \u03b8\u03b1 \u03c0\u03c1\u03b1\u03b3\u03bc\u03b1\u03c4\u03bf\u03c0\u03bf\u03b9\u03bf\u03cd\u03bd\u03c4\u03b1\u03b9 \u03ba\u03ac\u03b8\u03b5 80 \u03bb\u03b5\u03c0\u03c4\u03ac \u03b1\u03bd\u03c4\u03af \u03b3\u03b9\u03b1 \u03ba\u03ac\u03b8\u03b5 40 \u03bb\u03b5\u03c0\u03c4\u03ac." } } }, diff --git a/homeassistant/components/accuweather/translations/en.json b/homeassistant/components/accuweather/translations/en.json index 844970f0d2b..8bbbd770388 100644 --- a/homeassistant/components/accuweather/translations/en.json +++ b/homeassistant/components/accuweather/translations/en.json @@ -22,6 +22,17 @@ } } }, + "entity": { + "sensor": { + "pressure_tendency": { + "state": { + "falling": "Falling", + "rising": "Rising", + "steady": "Steady" + } + } + } + }, "options": { "step": { "init": { @@ -29,12 +40,6 @@ "forecast": "Weather forecast" }, "description": "Due to the limitations of the free version of the AccuWeather API key, when you enable weather forecast, data updates will be performed every 80 minutes instead of every 40 minutes." - }, - "user": { - "data": { - "forecast": "Weather forecast" - }, - "description": "Due to the limitations of the free version of the AccuWeather API key, when you enable weather forecast, data updates will be performed every 80 minutes instead of every 40 minutes." } } }, diff --git a/homeassistant/components/accuweather/translations/es-419.json b/homeassistant/components/accuweather/translations/es-419.json index 35413ba7fe7..7d34d63d36f 100644 --- a/homeassistant/components/accuweather/translations/es-419.json +++ b/homeassistant/components/accuweather/translations/es-419.json @@ -12,16 +12,6 @@ "requests_exceeded": "Se super\u00f3 el n\u00famero permitido de solicitudes a la API de Accuweather. Tiene que esperar o cambiar la clave de API." } }, - "options": { - "step": { - "user": { - "data": { - "forecast": "Pron\u00f3stico del tiempo" - }, - "description": "Debido a las limitaciones de la versi\u00f3n gratuita de la clave API de AccuWeather, cuando habilita el pron\u00f3stico del tiempo, las actualizaciones de datos se realizar\u00e1n cada 64 minutos en lugar de cada 32 minutos." - } - } - }, "system_health": { "info": { "can_reach_server": "Llegar al servidor de AccuWeather", diff --git a/homeassistant/components/accuweather/translations/es.json b/homeassistant/components/accuweather/translations/es.json index 094d725c2f4..e9361f7abba 100644 --- a/homeassistant/components/accuweather/translations/es.json +++ b/homeassistant/components/accuweather/translations/es.json @@ -22,6 +22,17 @@ } } }, + "entity": { + "sensor": { + "pressure_tendency": { + "state": { + "falling": "Descendente", + "rising": "Creciente", + "steady": "Estable" + } + } + } + }, "options": { "step": { "init": { @@ -29,12 +40,6 @@ "forecast": "Previsi\u00f3n meteorol\u00f3gica" }, "description": "Debido a las limitaciones de la versi\u00f3n gratuita de la clave API de AccuWeather, cuando habilitas la previsi\u00f3n meteorol\u00f3gica, las actualizaciones de datos se realizar\u00e1n cada 80 minutos en lugar de cada 40 minutos." - }, - "user": { - "data": { - "forecast": "Previsi\u00f3n meteorol\u00f3gica" - }, - "description": "Debido a las limitaciones de la versi\u00f3n gratuita de la clave API de AccuWeather, cuando habilitas la previsi\u00f3n meteorol\u00f3gica, las actualizaciones de datos se realizar\u00e1n cada 80 minutos en lugar de cada 40 minutos." } } }, diff --git a/homeassistant/components/accuweather/translations/et.json b/homeassistant/components/accuweather/translations/et.json index 9ce64fdd91c..45b942028f6 100644 --- a/homeassistant/components/accuweather/translations/et.json +++ b/homeassistant/components/accuweather/translations/et.json @@ -22,6 +22,17 @@ } } }, + "entity": { + "sensor": { + "pressure_tendency": { + "state": { + "falling": "Langev", + "rising": "T\u00f5usev", + "steady": "\u00dchtlane" + } + } + } + }, "options": { "step": { "init": { @@ -29,12 +40,6 @@ "forecast": "Ilmateade" }, "description": "AccuWeather API tasuta versioonis toimub ilmaennustuse lubamisel andmete v\u00e4rskendamine iga 80 minuti j\u00e4rel (muidu 40 minutit)." - }, - "user": { - "data": { - "forecast": "Ilmateade" - }, - "description": "AccuWeather API tasuta versioonis toimub ilmaennustuse lubamisel andmete v\u00e4rskendamine iga 80 minuti j\u00e4rel (muidu 40 minutit)." } } }, diff --git a/homeassistant/components/accuweather/translations/fr.json b/homeassistant/components/accuweather/translations/fr.json index 187b1fb3e0b..9c6c25e54ed 100644 --- a/homeassistant/components/accuweather/translations/fr.json +++ b/homeassistant/components/accuweather/translations/fr.json @@ -28,12 +28,6 @@ "data": { "forecast": "Pr\u00e9visions m\u00e9t\u00e9orologiques" } - }, - "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." } } }, diff --git a/homeassistant/components/accuweather/translations/he.json b/homeassistant/components/accuweather/translations/he.json index f4b95268a25..f8b156ca055 100644 --- a/homeassistant/components/accuweather/translations/he.json +++ b/homeassistant/components/accuweather/translations/he.json @@ -22,9 +22,20 @@ } } }, + "entity": { + "sensor": { + "pressure_tendency": { + "state": { + "falling": "\u05e9\u05d5\u05e7\u05e2\u05ea", + "rising": "\u05d6\u05d5\u05e8\u05d7\u05ea", + "steady": "\u05d9\u05e6\u05d9\u05d1" + } + } + } + }, "options": { "step": { - "user": { + "init": { "data": { "forecast": "\u05ea\u05d7\u05d6\u05d9\u05ea \u05de\u05d6\u05d2 \u05d4\u05d0\u05d5\u05d5\u05d9\u05e8" }, diff --git a/homeassistant/components/accuweather/translations/hu.json b/homeassistant/components/accuweather/translations/hu.json index 0e207c5fde8..f7723caf85d 100644 --- a/homeassistant/components/accuweather/translations/hu.json +++ b/homeassistant/components/accuweather/translations/hu.json @@ -22,13 +22,24 @@ } } }, + "entity": { + "sensor": { + "pressure_tendency": { + "state": { + "falling": "Cs\u00f6kken\u0151", + "rising": "Emelked\u0151", + "steady": "\u00c1lland\u00f3" + } + } + } + }, "options": { "step": { - "user": { + "init": { "data": { "forecast": "Id\u0151j\u00e1r\u00e1s el\u0151rejelz\u00e9s" }, - "description": "Az AccuWeather API kulcs ingyenes verzi\u00f3j\u00e1nak korl\u00e1tai miatt, amikor enged\u00e9lyezi az id\u0151j\u00e1r\u00e1s -el\u0151rejelz\u00e9st, az adatfriss\u00edt\u00e9seket 40 percenk\u00e9nt 80 percenk\u00e9nt hajtj\u00e1k v\u00e9gre." + "description": "Az AccuWeather API-kulcs ingyenes verzi\u00f3j\u00e1nak korl\u00e1tai miatt az id\u0151j\u00e1r\u00e1s-el\u0151rejelz\u00e9s enged\u00e9lyez\u00e9sekor az adatok friss\u00edt\u00e9se 40 perc helyett 80 percenk\u00e9nt t\u00f6rt\u00e9nik." } } }, diff --git a/homeassistant/components/accuweather/translations/id.json b/homeassistant/components/accuweather/translations/id.json index 72a4fab86f8..9b8f9d50c6a 100644 --- a/homeassistant/components/accuweather/translations/id.json +++ b/homeassistant/components/accuweather/translations/id.json @@ -22,6 +22,17 @@ } } }, + "entity": { + "sensor": { + "pressure_tendency": { + "state": { + "falling": "Turun", + "rising": "Naik", + "steady": "Tetap" + } + } + } + }, "options": { "step": { "init": { @@ -29,12 +40,6 @@ "forecast": "Prakiraan cuaca" }, "description": "Karena keterbatasan versi gratis kunci API AccuWeather, ketika Anda mengaktifkan prakiraan cuaca, pembaruan data akan dilakukan setiap 80 menit, bukan setiap 40 menit." - }, - "user": { - "data": { - "forecast": "Prakiraan cuaca" - }, - "description": "Karena keterbatasan versi gratis kunci API AccuWeather, ketika Anda mengaktifkan prakiraan cuaca, pembaruan data akan dilakukan setiap 80 menit, bukan setiap 40 menit." } } }, diff --git a/homeassistant/components/accuweather/translations/it.json b/homeassistant/components/accuweather/translations/it.json index d07a900adc9..74c4ff5eb44 100644 --- a/homeassistant/components/accuweather/translations/it.json +++ b/homeassistant/components/accuweather/translations/it.json @@ -22,13 +22,24 @@ } } }, + "entity": { + "sensor": { + "pressure_tendency": { + "state": { + "falling": "In diminuzione", + "rising": "In aumento", + "steady": "Stabile" + } + } + } + }, "options": { "step": { - "user": { + "init": { "data": { - "forecast": "Previsioni meteo" + "forecast": "Previsioni del tempo" }, - "description": "A causa delle limitazioni della versione gratuita della chiave API AccuWeather, quando si abilitano le previsioni del tempo, gli aggiornamenti dei dati verranno eseguiti ogni 80 minuti invece che ogni 40." + "description": "A causa delle limitazioni della versione gratuita della chiave API AccuWeather, quando abiliti le previsioni del tempo, gli aggiornamenti dei dati verranno eseguiti ogni 80 minuti anzich\u00e9 ogni 40 minuti." } } }, diff --git a/homeassistant/components/accuweather/translations/ja.json b/homeassistant/components/accuweather/translations/ja.json index 3583f9760b9..ea750c17b33 100644 --- a/homeassistant/components/accuweather/translations/ja.json +++ b/homeassistant/components/accuweather/translations/ja.json @@ -22,16 +22,6 @@ } } }, - "options": { - "step": { - "user": { - "data": { - "forecast": "\u5929\u6c17\u4e88\u5831" - }, - "description": "\u7121\u6599\u7248\u306eAccuWeather API\u30ad\u30fc\u306e\u5236\u9650\u306b\u3088\u308a\u3001\u5929\u6c17\u4e88\u5831\u3092\u6709\u52b9\u306b\u3057\u305f\u5834\u5408\u3001\u30c7\u30fc\u30bf\u306e\u66f4\u65b0\u306f40\u5206\u6bce\u3067\u306f\u306a\u304f80\u5206\u6bce\u306b\u5b9f\u884c\u3055\u308c\u307e\u3059\u3002" - } - } - }, "system_health": { "info": { "can_reach_server": "AccuWeather\u30b5\u30fc\u30d0\u30fc\u3078\u306e\u30a2\u30af\u30bb\u30b9", diff --git a/homeassistant/components/accuweather/translations/ko.json b/homeassistant/components/accuweather/translations/ko.json index ad33fae591c..a8b03945887 100644 --- a/homeassistant/components/accuweather/translations/ko.json +++ b/homeassistant/components/accuweather/translations/ko.json @@ -19,13 +19,24 @@ } } }, + "entity": { + "sensor": { + "pressure_tendency": { + "state": { + "falling": "\ud558\uac15", + "rising": "\uc0c1\uc2b9", + "steady": "\uc548\uc815\ub428" + } + } + } + }, "options": { "step": { - "user": { + "init": { "data": { "forecast": "\ub0a0\uc528 \uc608\ubcf4" }, - "description": "\ubb34\ub8cc \ubc84\uc804\uc758 AccuWeather API \ud0a4\ub85c \uc77c\uae30\uc608\ubcf4\ub97c \ud65c\uc131\ud654\ud55c \uacbd\uc6b0 \uc81c\ud55c\uc0ac\ud56d\uc73c\ub85c \uc778\ud574 \uc5c5\ub370\uc774\ud2b8\ub294 40 \ubd84\uc774 \uc544\ub2cc 80 \ubd84\ub9c8\ub2e4 \uc218\ud589\ub429\ub2c8\ub2e4." + "description": "\ubb34\ub8cc \ubc84\uc804 AccuWeather API \ud0a4\uc758 \uc81c\ud55c\uc73c\ub85c \uc778\ud574 \ub0a0\uc528 \uc608\ubcf4\ub97c \ud65c\uc131\ud654\ud558\uba74 \ub370\uc774\ud130 \uc5c5\ub370\uc774\ud2b8\uac00 40\ubd84\uc774 \uc544\ub2cc 80\ubd84\ub9c8\ub2e4 \uc218\ud589\ub429\ub2c8\ub2e4." } } }, diff --git a/homeassistant/components/accuweather/translations/lb.json b/homeassistant/components/accuweather/translations/lb.json index 618b9ef984b..3d13e34297d 100644 --- a/homeassistant/components/accuweather/translations/lb.json +++ b/homeassistant/components/accuweather/translations/lb.json @@ -19,16 +19,6 @@ } } }, - "options": { - "step": { - "user": { - "data": { - "forecast": "Wieder Pr\u00e9visioun" - }, - "description": "Duerch d'Limite vun der Gratis Versioun vun der AccuWeather API, wann d'Wieder Pr\u00e9visoune aktiv\u00e9iert sinn, ginn d'Aktualis\u00e9ierungen all 64 Minutten gemaach, am plaatz vun all 32 Minutten." - } - } - }, "system_health": { "info": { "can_reach_server": "AccuWeather Server ereechbar", diff --git a/homeassistant/components/accuweather/translations/nl.json b/homeassistant/components/accuweather/translations/nl.json index 9b18af3b6da..c98a968452d 100644 --- a/homeassistant/components/accuweather/translations/nl.json +++ b/homeassistant/components/accuweather/translations/nl.json @@ -22,6 +22,17 @@ } } }, + "entity": { + "sensor": { + "pressure_tendency": { + "state": { + "falling": "Dalende", + "rising": "Stijgende", + "steady": "Stabiel" + } + } + } + }, "options": { "step": { "init": { @@ -29,12 +40,6 @@ "forecast": "Weersverwachting" }, "description": "Wanneer je de weersverwachting ingeschakeld zullen updates elke 80 minuten plaatsvinden i.p.v. elke 40 minuten, dit komt door de beperkingen van de gratis versie van de AccuWeather API sleutel." - }, - "user": { - "data": { - "forecast": "Weervoorspelling" - }, - "description": "Vanwege de beperkingen van de gratis versie van de AccuWeather API-sleutel, worden gegevensupdates elke 64 minuten in plaats van elke 32 minuten uitgevoerd wanneer u weersvoorspelling inschakelt." } } }, diff --git a/homeassistant/components/accuweather/translations/no.json b/homeassistant/components/accuweather/translations/no.json index c6af3b8320c..dfda89558cd 100644 --- a/homeassistant/components/accuweather/translations/no.json +++ b/homeassistant/components/accuweather/translations/no.json @@ -22,6 +22,17 @@ } } }, + "entity": { + "sensor": { + "pressure_tendency": { + "state": { + "falling": "Fallende", + "rising": "Stiger", + "steady": "Jevn" + } + } + } + }, "options": { "step": { "init": { @@ -29,12 +40,6 @@ "forecast": "V\u00e6rmelding" }, "description": "P\u00e5 grunn av begrensningene til gratisversjonen av AccuWeather API-n\u00f8kkelen, n\u00e5r du aktiverer v\u00e6rmelding, vil dataoppdateringer utf\u00f8res hvert 80. minutt i stedet for hvert 40. minutt." - }, - "user": { - "data": { - "forecast": "V\u00e6rmelding" - }, - "description": "P\u00e5 grunn av begrensningene i den gratis versjonen av AccuWeather API-n\u00f8kkelen, vil dataoppdateringer utf\u00f8res hvert 80. minutt i stedet for hvert 40. minutt n\u00e5r du aktiverer v\u00e6rmelding." } } }, diff --git a/homeassistant/components/accuweather/translations/pl.json b/homeassistant/components/accuweather/translations/pl.json index 4ec4d05a932..3ea0a88b306 100644 --- a/homeassistant/components/accuweather/translations/pl.json +++ b/homeassistant/components/accuweather/translations/pl.json @@ -22,9 +22,20 @@ } } }, + "entity": { + "sensor": { + "pressure_tendency": { + "state": { + "falling": "malej\u0105ce", + "rising": "rosn\u0105ce", + "steady": "sta\u0142e" + } + } + } + }, "options": { "step": { - "user": { + "init": { "data": { "forecast": "Prognoza pogody" }, diff --git a/homeassistant/components/accuweather/translations/pt-BR.json b/homeassistant/components/accuweather/translations/pt-BR.json index 726de96c37d..7c1f8a49613 100644 --- a/homeassistant/components/accuweather/translations/pt-BR.json +++ b/homeassistant/components/accuweather/translations/pt-BR.json @@ -22,6 +22,17 @@ } } }, + "entity": { + "sensor": { + "pressure_tendency": { + "state": { + "falling": "Caindo", + "rising": "Ascendente", + "steady": "Est\u00e1vel" + } + } + } + }, "options": { "step": { "init": { @@ -29,12 +40,6 @@ "forecast": "Previs\u00e3o do tempo" }, "description": "Devido \u00e0s limita\u00e7\u00f5es da vers\u00e3o gratuita da chave API AccuWeather, quando voc\u00ea ativa a previs\u00e3o do tempo, as atualiza\u00e7\u00f5es de dados s\u00e3o realizadas a cada 80 minutos em vez de 40 minutos." - }, - "user": { - "data": { - "forecast": "Previs\u00e3o do Tempo" - }, - "description": "Devido \u00e0s limita\u00e7\u00f5es da vers\u00e3o gratuita da chave da API AccuWeather, quando voc\u00ea habilita a previs\u00e3o do tempo, as atualiza\u00e7\u00f5es de dados ser\u00e3o realizadas a cada 64 minutos em vez de a cada 32 minutos." } } }, diff --git a/homeassistant/components/accuweather/translations/pt.json b/homeassistant/components/accuweather/translations/pt.json index 1346ee8367f..3a87e6144bf 100644 --- a/homeassistant/components/accuweather/translations/pt.json +++ b/homeassistant/components/accuweather/translations/pt.json @@ -7,7 +7,7 @@ "default": "Alguns sensores n\u00e3o s\u00e3o ativados por defeito. Podem ser ativados no registo da entidade ap\u00f3s a configura\u00e7\u00e3o da integra\u00e7\u00e3o.\nA previs\u00e3o do tempo n\u00e3o est\u00e1 ativada por defeito. Pode ativ\u00e1-la nas op\u00e7\u00f5es de integra\u00e7\u00e3o." }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_api_key": "Chave de API inv\u00e1lida", "requests_exceeded": "O n\u00famero permitido de pedidos \u00e0 API do Accuweather foi excedido. \u00c9 necess\u00e1rio aguardar ou alterar a chave API." }, @@ -22,16 +22,6 @@ } } }, - "options": { - "step": { - "user": { - "data": { - "forecast": "Previs\u00e3o meteorol\u00f3gica" - }, - "description": "Devido \u00e0s limita\u00e7\u00f5es da vers\u00e3o gratuita da chave AccuWeather API, quando se activa a previs\u00e3o do tempo, as actualiza\u00e7\u00f5es de dados ser\u00e3o realizadas a cada 80 minutos em vez de a cada 40 minutos." - } - } - }, "system_health": { "info": { "can_reach_server": "Alcance o servidor AccuWeather", diff --git a/homeassistant/components/accuweather/translations/ru.json b/homeassistant/components/accuweather/translations/ru.json index 8f137f71c74..1a42d7ba657 100644 --- a/homeassistant/components/accuweather/translations/ru.json +++ b/homeassistant/components/accuweather/translations/ru.json @@ -22,6 +22,17 @@ } } }, + "entity": { + "sensor": { + "pressure_tendency": { + "state": { + "falling": "\u041f\u043e\u043d\u0438\u0436\u0430\u0435\u0442\u0441\u044f", + "rising": "\u041f\u043e\u0432\u044b\u0448\u0430\u0435\u0442\u0441\u044f", + "steady": "\u041f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e\u0435" + } + } + } + }, "options": { "step": { "init": { @@ -29,12 +40,6 @@ "forecast": "\u041f\u0440\u043e\u0433\u043d\u043e\u0437 \u043f\u043e\u0433\u043e\u0434\u044b" }, "description": "\u0412 \u0441\u0432\u044f\u0437\u0438 \u0441 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f\u043c\u0438 \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 \u043a\u043b\u044e\u0447\u0430 API AccuWeather, \u043f\u0440\u0438 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u043f\u0440\u043e\u0433\u043d\u043e\u0437\u0430 \u043f\u043e\u0433\u043e\u0434\u044b \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442\u044c \u043a\u0430\u0436\u0434\u044b\u0435 80 \u043c\u0438\u043d\u0443\u0442, \u0430 \u043d\u0435 \u043a\u0430\u0436\u0434\u044b\u0435 40 \u043c\u0438\u043d\u0443\u0442." - }, - "user": { - "data": { - "forecast": "\u041f\u0440\u043e\u0433\u043d\u043e\u0437 \u043f\u043e\u0433\u043e\u0434\u044b" - }, - "description": "\u0412 \u0441\u0432\u044f\u0437\u0438 \u0441 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f\u043c\u0438 \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 \u043a\u043b\u044e\u0447\u0430 API AccuWeather, \u043f\u0440\u0438 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u043f\u0440\u043e\u0433\u043d\u043e\u0437\u0430 \u043f\u043e\u0433\u043e\u0434\u044b \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442\u044c \u043a\u0430\u0436\u0434\u044b\u0435 80 \u043c\u0438\u043d\u0443\u0442, \u0430 \u043d\u0435 \u043a\u0430\u0436\u0434\u044b\u0435 40 \u043c\u0438\u043d\u0443\u0442." } } }, diff --git a/homeassistant/components/accuweather/translations/sensor.sk.json b/homeassistant/components/accuweather/translations/sensor.sk.json index 4152daff605..580e3cb77a7 100644 --- a/homeassistant/components/accuweather/translations/sensor.sk.json +++ b/homeassistant/components/accuweather/translations/sensor.sk.json @@ -2,7 +2,8 @@ "state": { "accuweather__pressure_tendency": { "falling": "Klesaj\u00faci", - "rising": "Padaj\u00faci" + "rising": "Padaj\u00faci", + "steady": "Stabiln\u00fd" } } } \ No newline at end of file diff --git a/homeassistant/components/accuweather/translations/sk.json b/homeassistant/components/accuweather/translations/sk.json index d39416fdab7..3f1af36b970 100644 --- a/homeassistant/components/accuweather/translations/sk.json +++ b/homeassistant/components/accuweather/translations/sk.json @@ -3,6 +3,9 @@ "abort": { "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." }, + "create_entry": { + "default": "Niektor\u00e9 sn\u00edma\u010de nie s\u00fa predvolene povolen\u00e9. M\u00f4\u017eete ich povoli\u0165 v registri ent\u00edt po konfigur\u00e1cii integr\u00e1cie.\n Predpove\u010f po\u010dasia nie je predvolene zapnut\u00e1. M\u00f4\u017eete to povoli\u0165 v mo\u017enostiach integr\u00e1cie." + }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d", @@ -19,17 +22,24 @@ } } }, + "entity": { + "sensor": { + "pressure_tendency": { + "state": { + "falling": "Klesaj\u00faci", + "rising": "St\u00fapaj\u00faci", + "steady": "Stabiln\u00fd" + } + } + } + }, "options": { "step": { "init": { "data": { "forecast": "Predpove\u010f po\u010dasia" - } - }, - "user": { - "data": { - "forecast": "Predpove\u010f po\u010dasia" - } + }, + "description": "Z d\u00f4vodu obmedzen\u00ed bezplatnej verzie k\u013e\u00fa\u010da AccuWeather API, ke\u010f povol\u00edte predpove\u010f po\u010dasia, aktualiz\u00e1cie \u00fadajov sa bud\u00fa vykon\u00e1va\u0165 ka\u017ed\u00fdch 80 min\u00fat namiesto ka\u017ed\u00fdch 40 min\u00fat." } } }, diff --git a/homeassistant/components/accuweather/translations/sv.json b/homeassistant/components/accuweather/translations/sv.json index 4dea8a74f47..2c0a9595eb2 100644 --- a/homeassistant/components/accuweather/translations/sv.json +++ b/homeassistant/components/accuweather/translations/sv.json @@ -22,16 +22,6 @@ } } }, - "options": { - "step": { - "user": { - "data": { - "forecast": "V\u00e4derprognos" - }, - "description": "P\u00e5 grund av begr\u00e4nsningarna f\u00f6r den kostnadsfria versionen av AccuWeather API-nyckeln, n\u00e4r du aktiverar v\u00e4derprognos, kommer datauppdateringar att utf\u00f6ras var 80:e minut ist\u00e4llet f\u00f6r var 40:e minut." - } - } - }, "system_health": { "info": { "can_reach_server": "N\u00e5 AccuWeather-servern", diff --git a/homeassistant/components/accuweather/translations/tr.json b/homeassistant/components/accuweather/translations/tr.json index e0907fe2fa8..c7049160868 100644 --- a/homeassistant/components/accuweather/translations/tr.json +++ b/homeassistant/components/accuweather/translations/tr.json @@ -22,16 +22,6 @@ } } }, - "options": { - "step": { - "user": { - "data": { - "forecast": "Hava Durumu tahmini" - }, - "description": "AccuWeather API anahtar\u0131n\u0131n \u00fccretsiz s\u00fcr\u00fcm\u00fcn\u00fcn s\u0131n\u0131rlamalar\u0131 nedeniyle, hava tahminini etkinle\u015ftirdi\u011finizde, veri g\u00fcncellemeleri her 40 dakikada bir yerine 80 dakikada bir ger\u00e7ekle\u015ftirilir." - } - } - }, "system_health": { "info": { "can_reach_server": "AccuWeather sunucusuna ula\u015f\u0131n", diff --git a/homeassistant/components/accuweather/translations/uk.json b/homeassistant/components/accuweather/translations/uk.json index 99818d354f9..b7f48369804 100644 --- a/homeassistant/components/accuweather/translations/uk.json +++ b/homeassistant/components/accuweather/translations/uk.json @@ -19,16 +19,6 @@ } } }, - "options": { - "step": { - "user": { - "data": { - "forecast": "\u041f\u0440\u043e\u0433\u043d\u043e\u0437 \u043f\u043e\u0433\u043e\u0434\u0438" - }, - "description": "\u0423 \u0437\u0432'\u044f\u0437\u043a\u0443 \u0437 \u043e\u0431\u043c\u0435\u0436\u0435\u043d\u043d\u044f\u043c\u0438 \u0431\u0435\u0437\u043a\u043e\u0448\u0442\u043e\u0432\u043d\u043e\u0457 \u0432\u0435\u0440\u0441\u0456\u0457 \u043a\u043b\u044e\u0447\u0430 API AccuWeather, \u043f\u0440\u0438 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u0456 \u043f\u0440\u043e\u0433\u043d\u043e\u0437\u0443 \u043f\u043e\u0433\u043e\u0434\u0438 \u043e\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044f \u0434\u0430\u043d\u0438\u0445 \u0431\u0443\u0434\u0435 \u0432\u0456\u0434\u0431\u0443\u0432\u0430\u0442\u0438\u0441\u044f \u043a\u043e\u0436\u043d\u0456 64 \u0445\u0432\u0438\u043b\u0438\u043d\u0438, \u0430 \u043d\u0435 \u043a\u043e\u0436\u043d\u0456 32 \u0445\u0432\u0438\u043b\u0438\u043d\u0438." - } - } - }, "system_health": { "info": { "can_reach_server": "\u0414\u043e\u0441\u0442\u0443\u043f \u0434\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430 AccuWeather", diff --git a/homeassistant/components/accuweather/translations/zh-Hant.json b/homeassistant/components/accuweather/translations/zh-Hant.json index 03bfb362e30..542a795ff64 100644 --- a/homeassistant/components/accuweather/translations/zh-Hant.json +++ b/homeassistant/components/accuweather/translations/zh-Hant.json @@ -22,6 +22,17 @@ } } }, + "entity": { + "sensor": { + "pressure_tendency": { + "state": { + "falling": "\u4e0b\u964d", + "rising": "\u4e0a\u5347", + "steady": "\u7a69\u5b9a" + } + } + } + }, "options": { "step": { "init": { @@ -29,12 +40,6 @@ "forecast": "\u5929\u6c23\u9810\u5831" }, "description": "\u7531\u65bc AccuWeather API \u91d1\u9470\u514d\u8cbb\u7248\u672c\u9650\u5236\uff0c\u7576\u958b\u555f\u5929\u6c23\u9810\u5831\u6642\u3001\u6578\u64da\u6703\u6bcf 80 \u5206\u9418\u66f4\u65b0\u4e00\u6b21\uff0c\u800c\u975e 40 \u5206\u9418\u3002" - }, - "user": { - "data": { - "forecast": "\u5929\u6c23\u9810\u5831" - }, - "description": "\u7531\u65bc AccuWeather API \u91d1\u9470\u514d\u8cbb\u7248\u672c\u9650\u5236\uff0c\u7576\u958b\u555f\u5929\u6c23\u9810\u5831\u6642\u3001\u6578\u64da\u6703\u6bcf 80 \u5206\u9418\u66f4\u65b0\u4e00\u6b21\uff0c\u800c\u975e 40 \u5206\u9418\u3002" } } }, diff --git a/homeassistant/components/acmeda/translations/sk.json b/homeassistant/components/acmeda/translations/sk.json index f1ebfe35ab4..1fd50d405ac 100644 --- a/homeassistant/components/acmeda/translations/sk.json +++ b/homeassistant/components/acmeda/translations/sk.json @@ -7,7 +7,8 @@ "user": { "data": { "id": "ID hostite\u013ea" - } + }, + "title": "Vyberte hub, ktor\u00fd chcete prida\u0165" } } } diff --git a/homeassistant/components/adax/climate.py b/homeassistant/components/adax/climate.py index 8703619ca92..cc15872dafa 100644 --- a/homeassistant/components/adax/climate.py +++ b/homeassistant/components/adax/climate.py @@ -19,7 +19,7 @@ from homeassistant.const import ( CONF_TOKEN, CONF_UNIQUE_ID, PRECISION_WHOLE, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.aiohttp_client import async_get_clientsession @@ -69,7 +69,7 @@ class AdaxDevice(ClimateEntity): _attr_min_temp = 5 _attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE _attr_target_temperature_step = PRECISION_WHOLE - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS def __init__(self, heater_data: dict[str, Any], adax_data_handler: Adax) -> None: """Initialize the heater.""" @@ -132,7 +132,7 @@ class LocalAdaxDevice(ClimateEntity): _attr_min_temp = 5 _attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE _attr_target_temperature_step = PRECISION_WHOLE - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS def __init__(self, adax_data_handler, unique_id): """Initialize the heater.""" diff --git a/homeassistant/components/adax/translations/ko.json b/homeassistant/components/adax/translations/ko.json new file mode 100644 index 00000000000..bdf92393e61 --- /dev/null +++ b/homeassistant/components/adax/translations/ko.json @@ -0,0 +1,18 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "cloud": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/adax/translations/pt.json b/homeassistant/components/adax/translations/pt.json index b8256278f36..bb06e1ce955 100644 --- a/homeassistant/components/adax/translations/pt.json +++ b/homeassistant/components/adax/translations/pt.json @@ -7,7 +7,7 @@ "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "cloud": { diff --git a/homeassistant/components/adax/translations/sk.json b/homeassistant/components/adax/translations/sk.json index 5232969e1a8..e728ef09d36 100644 --- a/homeassistant/components/adax/translations/sk.json +++ b/homeassistant/components/adax/translations/sk.json @@ -20,7 +20,8 @@ "data": { "wifi_pswd": "Heslo Wi-Fi", "wifi_ssid": "Wi-Fi SSID" - } + }, + "description": "Resetujte ohrieva\u010d stla\u010den\u00edm + a OK, dokia\u013e se nezobraz\u00ed \"Reset\". Potom stla\u010dte a podr\u017ete tla\u010didlo OK na ohrieva\u010di, dokia\u013e modr\u00e1 led di\u00f3da neza\u010dne blika\u0165, k\u00fdm stla\u010d\u00edte tla\u010didlo Odosla\u0165. Konfigur\u00e1cia ohrieva\u010da m\u00f4\u017ee trva\u0165 nieko\u013eko min\u00fat." }, "user": { "data": { diff --git a/homeassistant/components/adguard/manifest.json b/homeassistant/components/adguard/manifest.json index 91e1393c734..32d801fa6a6 100644 --- a/homeassistant/components/adguard/manifest.json +++ b/homeassistant/components/adguard/manifest.json @@ -3,7 +3,7 @@ "name": "AdGuard Home", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/adguard", - "requirements": ["adguardhome==0.5.1"], + "requirements": ["adguardhome==0.6.1"], "codeowners": ["@frenck"], "iot_class": "local_polling", "integration_type": "service", diff --git a/homeassistant/components/adguard/sensor.py b/homeassistant/components/adguard/sensor.py index 86104d15ef2..f24aa20d28d 100644 --- a/homeassistant/components/adguard/sensor.py +++ b/homeassistant/components/adguard/sensor.py @@ -10,7 +10,7 @@ from adguardhome import AdGuardHome, AdGuardHomeConnectionError from homeassistant.components.sensor import SensorEntity, SensorEntityDescription from homeassistant.config_entries import ConfigEntry -from homeassistant.const import PERCENTAGE, TIME_MILLISECONDS +from homeassistant.const import PERCENTAGE, UnitOfTime from homeassistant.core import HomeAssistant from homeassistant.exceptions import PlatformNotReady from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -83,7 +83,7 @@ SENSORS: tuple[AdGuardHomeEntityDescription, ...] = ( key="average_speed", name="Average processing speed", icon="mdi:speedometer", - native_unit_of_measurement=TIME_MILLISECONDS, + native_unit_of_measurement=UnitOfTime.MILLISECONDS, value_fn=lambda adguard: adguard.stats.avg_processing_time(), ), AdGuardHomeEntityDescription( diff --git a/homeassistant/components/adguard/translations/pt.json b/homeassistant/components/adguard/translations/pt.json index 28a63ea54e7..e43dfcfaec8 100644 --- a/homeassistant/components/adguard/translations/pt.json +++ b/homeassistant/components/adguard/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "O servi\u00e7o j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "hassio_confirm": { @@ -13,7 +13,7 @@ }, "user": { "data": { - "host": "Servidor", + "host": "Endere\u00e7o", "password": "Palavra-passe", "port": "Porta", "ssl": "Utiliza um certificado SSL", diff --git a/homeassistant/components/adguard/translations/sk.json b/homeassistant/components/adguard/translations/sk.json index 10e7c5b3860..b6422e8afec 100644 --- a/homeassistant/components/adguard/translations/sk.json +++ b/homeassistant/components/adguard/translations/sk.json @@ -20,7 +20,8 @@ "ssl": "Pou\u017e\u00edva SSL certifik\u00e1t", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno", "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" - } + }, + "description": "Nastavte svoju in\u0161tanciu AdGuard Home pre monitorovanie a riadenie." } } } diff --git a/homeassistant/components/advantage_air/climate.py b/homeassistant/components/advantage_air/climate.py index fdba46cde76..362701f3b9f 100644 --- a/homeassistant/components/advantage_air/climate.py +++ b/homeassistant/components/advantage_air/climate.py @@ -14,7 +14,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, TEMP_CELSIUS +from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -83,7 +83,7 @@ async def async_setup_entry( class AdvantageAirAC(AdvantageAirAcEntity, ClimateEntity): """AdvantageAir AC unit.""" - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS _attr_target_temperature_step = PRECISION_WHOLE _attr_max_temp = 32 _attr_min_temp = 16 @@ -149,7 +149,7 @@ class AdvantageAirAC(AdvantageAirAcEntity, ClimateEntity): class AdvantageAirZone(AdvantageAirZoneEntity, ClimateEntity): """AdvantageAir Zone control.""" - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS _attr_target_temperature_step = PRECISION_WHOLE _attr_max_temp = 32 _attr_min_temp = 16 diff --git a/homeassistant/components/advantage_air/sensor.py b/homeassistant/components/advantage_air/sensor.py index 60e640d36e9..270f2efbc6e 100644 --- a/homeassistant/components/advantage_air/sensor.py +++ b/homeassistant/components/advantage_air/sensor.py @@ -12,7 +12,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import PERCENTAGE, TEMP_CELSIUS +from homeassistant.const import PERCENTAGE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers import config_validation as cv, entity_platform from homeassistant.helpers.entity import EntityCategory @@ -155,7 +155,7 @@ class AdvantageAirZoneSignal(AdvantageAirZoneEntity, SensorEntity): class AdvantageAirZoneTemp(AdvantageAirZoneEntity, SensorEntity): """Representation of Advantage Air Zone temperature sensor.""" - _attr_native_unit_of_measurement = TEMP_CELSIUS + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS _attr_device_class = SensorDeviceClass.TEMPERATURE _attr_state_class = SensorStateClass.MEASUREMENT _attr_entity_registry_enabled_default = False diff --git a/homeassistant/components/advantage_air/translations/pt.json b/homeassistant/components/advantage_air/translations/pt.json index 37e27fd8394..dc8502436ba 100644 --- a/homeassistant/components/advantage_air/translations/pt.json +++ b/homeassistant/components/advantage_air/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "user": { diff --git a/homeassistant/components/advantage_air/translations/sk.json b/homeassistant/components/advantage_air/translations/sk.json index 7abc08b46d9..b5287284e9e 100644 --- a/homeassistant/components/advantage_air/translations/sk.json +++ b/homeassistant/components/advantage_air/translations/sk.json @@ -12,6 +12,7 @@ "ip_address": "IP adresa", "port": "Port" }, + "description": "Pripojte sa k API v\u00e1\u0161ho n\u00e1stenn\u00e9ho tabletu Advantage Air.", "title": "Pripoji\u0165" } } diff --git a/homeassistant/components/aemet/const.py b/homeassistant/components/aemet/const.py index 7058257d808..bd10d09bea0 100644 --- a/homeassistant/components/aemet/const.py +++ b/homeassistant/components/aemet/const.py @@ -1,11 +1,6 @@ """Constant values for the AEMET OpenData component.""" from __future__ import annotations -from homeassistant.components.sensor import ( - SensorDeviceClass, - SensorEntityDescription, - SensorStateClass, -) from homeassistant.components.weather import ( ATTR_CONDITION_CLEAR_NIGHT, ATTR_CONDITION_CLOUDY, @@ -18,15 +13,7 @@ from homeassistant.components.weather import ( ATTR_CONDITION_SNOWY, ATTR_CONDITION_SUNNY, ) -from homeassistant.const import ( - DEGREE, - PERCENTAGE, - PRESSURE_HPA, - SPEED_KILOMETERS_PER_HOUR, - TEMP_CELSIUS, - Platform, - UnitOfVolumetricFlux, -) +from homeassistant.const import Platform ATTRIBUTION = "Powered by AEMET OpenData" CONF_STATION_UPDATES = "station_updates" @@ -200,157 +187,6 @@ FORECAST_MODE_ATTR_API = { FORECAST_MODE_HOURLY: ATTR_API_FORECAST_HOURLY, } -FORECAST_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( - SensorEntityDescription( - key=ATTR_API_FORECAST_CONDITION, - name="Condition", - ), - SensorEntityDescription( - key=ATTR_API_FORECAST_PRECIPITATION, - name="Precipitation", - native_unit_of_measurement=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR, - device_class=SensorDeviceClass.PRECIPITATION_INTENSITY, - ), - SensorEntityDescription( - key=ATTR_API_FORECAST_PRECIPITATION_PROBABILITY, - name="Precipitation probability", - native_unit_of_measurement=PERCENTAGE, - ), - SensorEntityDescription( - key=ATTR_API_FORECAST_TEMP, - name="Temperature", - native_unit_of_measurement=TEMP_CELSIUS, - device_class=SensorDeviceClass.TEMPERATURE, - ), - SensorEntityDescription( - key=ATTR_API_FORECAST_TEMP_LOW, - name="Temperature Low", - native_unit_of_measurement=TEMP_CELSIUS, - device_class=SensorDeviceClass.TEMPERATURE, - ), - SensorEntityDescription( - key=ATTR_API_FORECAST_TIME, - name="Time", - device_class=SensorDeviceClass.TIMESTAMP, - ), - SensorEntityDescription( - key=ATTR_API_FORECAST_WIND_BEARING, - name="Wind bearing", - native_unit_of_measurement=DEGREE, - ), - SensorEntityDescription( - key=ATTR_API_FORECAST_WIND_SPEED, - name="Wind speed", - native_unit_of_measurement=SPEED_KILOMETERS_PER_HOUR, - ), -) -WEATHER_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( - SensorEntityDescription( - key=ATTR_API_CONDITION, - name="Condition", - ), - SensorEntityDescription( - key=ATTR_API_HUMIDITY, - name="Humidity", - native_unit_of_measurement=PERCENTAGE, - device_class=SensorDeviceClass.HUMIDITY, - state_class=SensorStateClass.MEASUREMENT, - ), - SensorEntityDescription( - key=ATTR_API_PRESSURE, - name="Pressure", - native_unit_of_measurement=PRESSURE_HPA, - device_class=SensorDeviceClass.PRESSURE, - state_class=SensorStateClass.MEASUREMENT, - ), - SensorEntityDescription( - key=ATTR_API_RAIN, - name="Rain", - native_unit_of_measurement=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR, - device_class=SensorDeviceClass.PRECIPITATION_INTENSITY, - ), - SensorEntityDescription( - key=ATTR_API_RAIN_PROB, - name="Rain probability", - native_unit_of_measurement=PERCENTAGE, - state_class=SensorStateClass.MEASUREMENT, - ), - SensorEntityDescription( - key=ATTR_API_SNOW, - name="Snow", - native_unit_of_measurement=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR, - device_class=SensorDeviceClass.PRECIPITATION_INTENSITY, - ), - SensorEntityDescription( - key=ATTR_API_SNOW_PROB, - name="Snow probability", - native_unit_of_measurement=PERCENTAGE, - state_class=SensorStateClass.MEASUREMENT, - ), - SensorEntityDescription( - key=ATTR_API_STATION_ID, - name="Station ID", - ), - SensorEntityDescription( - key=ATTR_API_STATION_NAME, - name="Station name", - ), - SensorEntityDescription( - key=ATTR_API_STATION_TIMESTAMP, - name="Station timestamp", - device_class=SensorDeviceClass.TIMESTAMP, - ), - SensorEntityDescription( - key=ATTR_API_STORM_PROB, - name="Storm probability", - native_unit_of_measurement=PERCENTAGE, - state_class=SensorStateClass.MEASUREMENT, - ), - SensorEntityDescription( - key=ATTR_API_TEMPERATURE, - name="Temperature", - native_unit_of_measurement=TEMP_CELSIUS, - device_class=SensorDeviceClass.TEMPERATURE, - state_class=SensorStateClass.MEASUREMENT, - ), - SensorEntityDescription( - key=ATTR_API_TEMPERATURE_FEELING, - name="Temperature feeling", - native_unit_of_measurement=TEMP_CELSIUS, - device_class=SensorDeviceClass.TEMPERATURE, - state_class=SensorStateClass.MEASUREMENT, - ), - SensorEntityDescription( - key=ATTR_API_TOWN_ID, - name="Town ID", - ), - SensorEntityDescription( - key=ATTR_API_TOWN_NAME, - name="Town name", - ), - SensorEntityDescription( - key=ATTR_API_TOWN_TIMESTAMP, - name="Town timestamp", - device_class=SensorDeviceClass.TIMESTAMP, - ), - SensorEntityDescription( - key=ATTR_API_WIND_BEARING, - name="Wind bearing", - native_unit_of_measurement=DEGREE, - state_class=SensorStateClass.MEASUREMENT, - ), - SensorEntityDescription( - key=ATTR_API_WIND_MAX_SPEED, - name="Wind max speed", - native_unit_of_measurement=SPEED_KILOMETERS_PER_HOUR, - ), - SensorEntityDescription( - key=ATTR_API_WIND_SPEED, - name="Wind speed", - native_unit_of_measurement=SPEED_KILOMETERS_PER_HOUR, - state_class=SensorStateClass.MEASUREMENT, - ), -) WIND_BEARING_MAP = { "C": None, diff --git a/homeassistant/components/aemet/sensor.py b/homeassistant/components/aemet/sensor.py index 42cc005dcdd..de8e9aa4828 100644 --- a/homeassistant/components/aemet/sensor.py +++ b/homeassistant/components/aemet/sensor.py @@ -1,15 +1,54 @@ """Support for the AEMET OpenData service.""" from __future__ import annotations -from homeassistant.components.sensor import SensorEntity, SensorEntityDescription +from homeassistant.components.sensor import ( + SensorDeviceClass, + SensorEntity, + SensorEntityDescription, + SensorStateClass, +) from homeassistant.config_entries import ConfigEntry +from homeassistant.const import ( + DEGREE, + PERCENTAGE, + UnitOfPressure, + UnitOfSpeed, + UnitOfTemperature, + UnitOfVolumetricFlux, +) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.util import dt as dt_util from .const import ( + ATTR_API_CONDITION, + ATTR_API_FORECAST_CONDITION, + ATTR_API_FORECAST_PRECIPITATION, + ATTR_API_FORECAST_PRECIPITATION_PROBABILITY, + ATTR_API_FORECAST_TEMP, + ATTR_API_FORECAST_TEMP_LOW, ATTR_API_FORECAST_TIME, + ATTR_API_FORECAST_WIND_BEARING, + ATTR_API_FORECAST_WIND_SPEED, + ATTR_API_HUMIDITY, + ATTR_API_PRESSURE, + ATTR_API_RAIN, + ATTR_API_RAIN_PROB, + ATTR_API_SNOW, + ATTR_API_SNOW_PROB, + ATTR_API_STATION_ID, + ATTR_API_STATION_NAME, + ATTR_API_STATION_TIMESTAMP, + ATTR_API_STORM_PROB, + ATTR_API_TEMPERATURE, + ATTR_API_TEMPERATURE_FEELING, + ATTR_API_TOWN_ID, + ATTR_API_TOWN_NAME, + ATTR_API_TOWN_TIMESTAMP, + ATTR_API_WIND_BEARING, + ATTR_API_WIND_MAX_SPEED, + ATTR_API_WIND_SPEED, ATTRIBUTION, DOMAIN, ENTRY_NAME, @@ -18,12 +57,165 @@ from .const import ( FORECAST_MODE_DAILY, FORECAST_MODES, FORECAST_MONITORED_CONDITIONS, - FORECAST_SENSOR_TYPES, MONITORED_CONDITIONS, - WEATHER_SENSOR_TYPES, ) from .weather_update_coordinator import WeatherUpdateCoordinator +FORECAST_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( + SensorEntityDescription( + key=ATTR_API_FORECAST_CONDITION, + name="Condition", + ), + SensorEntityDescription( + key=ATTR_API_FORECAST_PRECIPITATION, + name="Precipitation", + native_unit_of_measurement=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR, + device_class=SensorDeviceClass.PRECIPITATION_INTENSITY, + ), + SensorEntityDescription( + key=ATTR_API_FORECAST_PRECIPITATION_PROBABILITY, + name="Precipitation probability", + native_unit_of_measurement=PERCENTAGE, + ), + SensorEntityDescription( + key=ATTR_API_FORECAST_TEMP, + name="Temperature", + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + device_class=SensorDeviceClass.TEMPERATURE, + ), + SensorEntityDescription( + key=ATTR_API_FORECAST_TEMP_LOW, + name="Temperature Low", + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + device_class=SensorDeviceClass.TEMPERATURE, + ), + SensorEntityDescription( + key=ATTR_API_FORECAST_TIME, + name="Time", + device_class=SensorDeviceClass.TIMESTAMP, + ), + SensorEntityDescription( + key=ATTR_API_FORECAST_WIND_BEARING, + name="Wind bearing", + native_unit_of_measurement=DEGREE, + ), + SensorEntityDescription( + key=ATTR_API_FORECAST_WIND_SPEED, + name="Wind speed", + native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, + device_class=SensorDeviceClass.WIND_SPEED, + ), +) +WEATHER_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( + SensorEntityDescription( + key=ATTR_API_CONDITION, + name="Condition", + ), + SensorEntityDescription( + key=ATTR_API_HUMIDITY, + name="Humidity", + native_unit_of_measurement=PERCENTAGE, + device_class=SensorDeviceClass.HUMIDITY, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + key=ATTR_API_PRESSURE, + name="Pressure", + native_unit_of_measurement=UnitOfPressure.HPA, + device_class=SensorDeviceClass.PRESSURE, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + key=ATTR_API_RAIN, + name="Rain", + native_unit_of_measurement=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR, + device_class=SensorDeviceClass.PRECIPITATION_INTENSITY, + ), + SensorEntityDescription( + key=ATTR_API_RAIN_PROB, + name="Rain probability", + native_unit_of_measurement=PERCENTAGE, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + key=ATTR_API_SNOW, + name="Snow", + native_unit_of_measurement=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR, + device_class=SensorDeviceClass.PRECIPITATION_INTENSITY, + ), + SensorEntityDescription( + key=ATTR_API_SNOW_PROB, + name="Snow probability", + native_unit_of_measurement=PERCENTAGE, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + key=ATTR_API_STATION_ID, + name="Station ID", + ), + SensorEntityDescription( + key=ATTR_API_STATION_NAME, + name="Station name", + ), + SensorEntityDescription( + key=ATTR_API_STATION_TIMESTAMP, + name="Station timestamp", + device_class=SensorDeviceClass.TIMESTAMP, + ), + SensorEntityDescription( + key=ATTR_API_STORM_PROB, + name="Storm probability", + native_unit_of_measurement=PERCENTAGE, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + key=ATTR_API_TEMPERATURE, + name="Temperature", + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + key=ATTR_API_TEMPERATURE_FEELING, + name="Temperature feeling", + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + key=ATTR_API_TOWN_ID, + name="Town ID", + ), + SensorEntityDescription( + key=ATTR_API_TOWN_NAME, + name="Town name", + ), + SensorEntityDescription( + key=ATTR_API_TOWN_TIMESTAMP, + name="Town timestamp", + device_class=SensorDeviceClass.TIMESTAMP, + ), + SensorEntityDescription( + key=ATTR_API_WIND_BEARING, + name="Wind bearing", + native_unit_of_measurement=DEGREE, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + key=ATTR_API_WIND_MAX_SPEED, + name="Wind max speed", + native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, + device_class=SensorDeviceClass.WIND_SPEED, + ), + SensorEntityDescription( + key=ATTR_API_WIND_SPEED, + name="Wind speed", + native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, + state_class=SensorStateClass.MEASUREMENT, + device_class=SensorDeviceClass.WIND_SPEED, + ), +) + async def async_setup_entry( hass: HomeAssistant, diff --git a/homeassistant/components/aemet/translations/de.json b/homeassistant/components/aemet/translations/de.json index cc8b8ead259..f91665e77ba 100644 --- a/homeassistant/components/aemet/translations/de.json +++ b/homeassistant/components/aemet/translations/de.json @@ -22,7 +22,7 @@ "step": { "init": { "data": { - "station_updates": "Sammeln von Daten von AEMET-Wetterstationen" + "station_updates": "Sammeln von Daten von AEMET Wetterstationen" } } } diff --git a/homeassistant/components/agent_dvr/translations/pt.json b/homeassistant/components/agent_dvr/translations/pt.json index f1ef5ef665f..3cc2362d1b8 100644 --- a/homeassistant/components/agent_dvr/translations/pt.json +++ b/homeassistant/components/agent_dvr/translations/pt.json @@ -5,12 +5,12 @@ }, "error": { "already_in_progress": "O processo de configura\u00e7\u00e3o j\u00e1 est\u00e1 a decorrer", - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "user": { "data": { - "host": "Servidor", + "host": "Endere\u00e7o", "port": "Porta" } } diff --git a/homeassistant/components/agent_dvr/translations/sk.json b/homeassistant/components/agent_dvr/translations/sk.json index b6699663a03..c12c58f389f 100644 --- a/homeassistant/components/agent_dvr/translations/sk.json +++ b/homeassistant/components/agent_dvr/translations/sk.json @@ -12,7 +12,8 @@ "data": { "host": "Hostite\u013e", "port": "Port" - } + }, + "title": "Nastavenie agenta DVR" } } } diff --git a/homeassistant/components/airly/sensor.py b/homeassistant/components/airly/sensor.py index 122990adecc..c85db155a55 100644 --- a/homeassistant/components/airly/sensor.py +++ b/homeassistant/components/airly/sensor.py @@ -16,8 +16,8 @@ from homeassistant.const import ( CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, CONF_NAME, PERCENTAGE, - PRESSURE_HPA, - TEMP_CELSIUS, + UnitOfPressure, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceEntryType @@ -105,14 +105,14 @@ SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = ( key=ATTR_API_PRESSURE, device_class=SensorDeviceClass.PRESSURE, name=ATTR_API_PRESSURE.capitalize(), - native_unit_of_measurement=PRESSURE_HPA, + native_unit_of_measurement=UnitOfPressure.HPA, state_class=SensorStateClass.MEASUREMENT, ), AirlySensorEntityDescription( key=ATTR_API_TEMPERATURE, device_class=SensorDeviceClass.TEMPERATURE, name=ATTR_API_TEMPERATURE.capitalize(), - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, value=lambda value: round(value, 1), ), diff --git a/homeassistant/components/airly/translations/de.json b/homeassistant/components/airly/translations/de.json index bc37bbc139c..fb76155e76a 100644 --- a/homeassistant/components/airly/translations/de.json +++ b/homeassistant/components/airly/translations/de.json @@ -21,7 +21,7 @@ }, "system_health": { "info": { - "can_reach_server": "Airly-Server erreichen", + "can_reach_server": "Airly Server erreichen", "requests_per_day": "Erlaubte Anfragen pro Tag", "requests_remaining": "Verbleibende erlaubte Anfragen" } diff --git a/homeassistant/components/airnow/config_flow.py b/homeassistant/components/airnow/config_flow.py index d4e39c63c27..e1828117d5f 100644 --- a/homeassistant/components/airnow/config_flow.py +++ b/homeassistant/components/airnow/config_flow.py @@ -75,7 +75,10 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): else: # Create Entry return self.async_create_entry( - title=f"AirNow Sensor at {user_input[CONF_LATITUDE]}, {user_input[CONF_LONGITUDE]}", + title=( + f"AirNow Sensor at {user_input[CONF_LATITUDE]}," + f" {user_input[CONF_LONGITUDE]}" + ), data=user_input, ) diff --git a/homeassistant/components/airnow/translations/pt.json b/homeassistant/components/airnow/translations/pt.json index dd714aa6dad..4cc90ffe588 100644 --- a/homeassistant/components/airnow/translations/pt.json +++ b/homeassistant/components/airnow/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, diff --git a/homeassistant/components/airq/config_flow.py b/homeassistant/components/airq/config_flow.py index 05af6825233..90a6b9e0555 100644 --- a/homeassistant/components/airq/config_flow.py +++ b/homeassistant/components/airq/config_flow.py @@ -55,8 +55,11 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): await airq.validate() except ClientConnectionError: _LOGGER.debug( - "Failed to connect to device %s. Check the IP address / device ID " - "as well as whether the device is connected to power and the WiFi", + ( + "Failed to connect to device %s. Check the IP address / device" + " ID as well as whether the device is connected to power and" + " the WiFi" + ), user_input[CONF_IP_ADDRESS], ) errors["base"] = "cannot_connect" diff --git a/homeassistant/components/airq/sensor.py b/homeassistant/components/airq/sensor.py index c524050ea66..e45639b15e9 100644 --- a/homeassistant/components/airq/sensor.py +++ b/homeassistant/components/airq/sensor.py @@ -19,9 +19,9 @@ from homeassistant.const import ( CONCENTRATION_PARTS_PER_BILLION, CONCENTRATION_PARTS_PER_MILLION, PERCENTAGE, - PRESSURE_HPA, - SOUND_PRESSURE_WEIGHTED_DBA, - TEMP_CELSIUS, + UnitOfPressure, + UnitOfSoundPressure, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -84,10 +84,10 @@ SENSOR_TYPES: list[AirQEntityDescription] = [ AirQEntityDescription( key="dewpt", name="Dew point", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, value=lambda data: data.get("dewpt"), - icon="mdi:water-thermometer", + device_class=SensorDeviceClass.TEMPERATURE, ), AirQEntityDescription( key="ethanol", @@ -227,17 +227,17 @@ SENSOR_TYPES: list[AirQEntityDescription] = [ key="pressure", name="Pressure", device_class=SensorDeviceClass.PRESSURE, - native_unit_of_measurement=PRESSURE_HPA, + native_unit_of_measurement=UnitOfPressure.HPA, state_class=SensorStateClass.MEASUREMENT, value=lambda data: data.get("pressure"), ), AirQEntityDescription( key="pressure_rel", name="Relative pressure", - native_unit_of_measurement=PRESSURE_HPA, + native_unit_of_measurement=UnitOfPressure.HPA, state_class=SensorStateClass.MEASUREMENT, value=lambda data: data.get("pressure_rel"), - icon="mdi:gauge", + device_class=SensorDeviceClass.PRESSURE, ), AirQEntityDescription( key="c3h8_MIPEX", @@ -257,18 +257,18 @@ SENSOR_TYPES: list[AirQEntityDescription] = [ AirQEntityDescription( key="sound", name="Noise", - native_unit_of_measurement=SOUND_PRESSURE_WEIGHTED_DBA, + native_unit_of_measurement=UnitOfSoundPressure.WEIGHTED_DECIBEL_A, state_class=SensorStateClass.MEASUREMENT, value=lambda data: data.get("sound"), - icon="mdi:ear-hearing", + device_class=SensorDeviceClass.SOUND_PRESSURE, ), AirQEntityDescription( key="sound_max", name="Noise (Maximum)", - native_unit_of_measurement=SOUND_PRESSURE_WEIGHTED_DBA, + native_unit_of_measurement=UnitOfSoundPressure.WEIGHTED_DECIBEL_A, state_class=SensorStateClass.MEASUREMENT, value=lambda data: data.get("sound_max"), - icon="mdi:ear-hearing", + device_class=SensorDeviceClass.SOUND_PRESSURE, ), AirQEntityDescription( key="radon", @@ -282,7 +282,7 @@ SENSOR_TYPES: list[AirQEntityDescription] = [ key="temperature", name="Temperature", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, value=lambda data: data.get("temperature"), ), diff --git a/homeassistant/components/airq/translations/ko.json b/homeassistant/components/airq/translations/ko.json new file mode 100644 index 00000000000..b249fb1ed80 --- /dev/null +++ b/homeassistant/components/airq/translations/ko.json @@ -0,0 +1,21 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "invalid_input": "\ud638\uc2a4\ud2b8 \uc774\ub984 \ub610\ub294 IP \uc8fc\uc18c\uac00 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "ip_address": "IP \uc8fc\uc18c", + "password": "\ube44\ubc00\ubc88\ud638" + }, + "description": "\uae30\uae30\uc758 IP \uc8fc\uc18c \ub610\ub294 mDNS\uc640 \uc554\ud638\ub97c \uc785\ub825\ud558\uc2ed\uc2dc\uc624." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airq/translations/pt.json b/homeassistant/components/airq/translations/pt.json new file mode 100644 index 00000000000..e4036bf22be --- /dev/null +++ b/homeassistant/components/airq/translations/pt.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" + }, + "error": { + "cannot_connect": "A liga\u00e7\u00e3o falhou", + "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", + "invalid_input": "Endere\u00e7o IP ou hostname inv\u00e1lido." + }, + "step": { + "user": { + "data": { + "ip_address": "Endere\u00e7o IP", + "password": "Palavra-passe" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airthings/config_flow.py b/homeassistant/components/airthings/config_flow.py index 842f05d76db..f07f7164f2b 100644 --- a/homeassistant/components/airthings/config_flow.py +++ b/homeassistant/components/airthings/config_flow.py @@ -37,7 +37,9 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): step_id="user", data_schema=STEP_USER_DATA_SCHEMA, description_placeholders={ - "url": "https://dashboard.airthings.com/integrations/api-integration", + "url": ( + "https://dashboard.airthings.com/integrations/api-integration" + ), }, ) diff --git a/homeassistant/components/airthings/sensor.py b/homeassistant/components/airthings/sensor.py index 028ea67916a..481150f77dd 100644 --- a/homeassistant/components/airthings/sensor.py +++ b/homeassistant/components/airthings/sensor.py @@ -15,9 +15,9 @@ from homeassistant.const import ( CONCENTRATION_PARTS_PER_BILLION, CONCENTRATION_PARTS_PER_MILLION, PERCENTAGE, - PRESSURE_MBAR, SIGNAL_STRENGTH_DECIBELS, - TEMP_CELSIUS, + UnitOfPressure, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo, EntityCategory @@ -39,7 +39,7 @@ SENSORS: dict[str, SensorEntityDescription] = { "temp": SensorEntityDescription( key="temp", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, name="Temperature", ), "humidity": SensorEntityDescription( @@ -51,7 +51,7 @@ SENSORS: dict[str, SensorEntityDescription] = { "pressure": SensorEntityDescription( key="pressure", device_class=SensorDeviceClass.PRESSURE, - native_unit_of_measurement=PRESSURE_MBAR, + native_unit_of_measurement=UnitOfPressure.MBAR, name="Pressure", ), "battery": SensorEntityDescription( diff --git a/homeassistant/components/airthings/translations/ko.json b/homeassistant/components/airthings/translations/ko.json index 27863f2e0e4..2324d63d00d 100644 --- a/homeassistant/components/airthings/translations/ko.json +++ b/homeassistant/components/airthings/translations/ko.json @@ -1,5 +1,13 @@ { "config": { + "abort": { + "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/airthings/translations/pt.json b/homeassistant/components/airthings/translations/pt.json index ef1f838f139..710ad9cbfa9 100644 --- a/homeassistant/components/airthings/translations/pt.json +++ b/homeassistant/components/airthings/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "Conta j\u00e1 configurada" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, diff --git a/homeassistant/components/airthings_ble/sensor.py b/homeassistant/components/airthings_ble/sensor.py index 37b5ce6160e..57a8c3827d2 100644 --- a/homeassistant/components/airthings_ble/sensor.py +++ b/homeassistant/components/airthings_ble/sensor.py @@ -17,8 +17,8 @@ from homeassistant.const import ( CONCENTRATION_PARTS_PER_MILLION, LIGHT_LUX, PERCENTAGE, - PRESSURE_MBAR, - TEMP_CELSIUS, + UnitOfPressure, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import CONNECTION_BLUETOOTH @@ -63,7 +63,7 @@ SENSORS_MAPPING_TEMPLATE: dict[str, SensorEntityDescription] = { "temperature": SensorEntityDescription( key="temperature", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, name="Temperature", ), "humidity": SensorEntityDescription( @@ -75,7 +75,7 @@ SENSORS_MAPPING_TEMPLATE: dict[str, SensorEntityDescription] = { "pressure": SensorEntityDescription( key="pressure", device_class=SensorDeviceClass.PRESSURE, - native_unit_of_measurement=PRESSURE_MBAR, + native_unit_of_measurement=UnitOfPressure.MBAR, name="Pressure", ), "battery": SensorEntityDescription( diff --git a/homeassistant/components/airthings_ble/translations/en.json b/homeassistant/components/airthings_ble/translations/en.json index 245f0fecd2c..541b88aa440 100644 --- a/homeassistant/components/airthings_ble/translations/en.json +++ b/homeassistant/components/airthings_ble/translations/en.json @@ -10,13 +10,13 @@ "flow_title": "{name}", "step": { "bluetooth_confirm": { - "description": "Do you want to setup {name}?" + "description": "Do you want to set up {name}?" }, "user": { "data": { "address": "Device" }, - "description": "Choose a device to setup" + "description": "Choose a device to set up" } } } diff --git a/homeassistant/components/airthings_ble/translations/it.json b/homeassistant/components/airthings_ble/translations/it.json index 90e1ecdeee8..672bcce08eb 100644 --- a/homeassistant/components/airthings_ble/translations/it.json +++ b/homeassistant/components/airthings_ble/translations/it.json @@ -16,7 +16,7 @@ "data": { "address": "Dispositivo" }, - "description": "Seleziona un dispositivo da configurare" + "description": "Scegli un dispositivo da configurare" } } } diff --git a/homeassistant/components/airthings_ble/translations/ko.json b/homeassistant/components/airthings_ble/translations/ko.json new file mode 100644 index 00000000000..ab917ff9237 --- /dev/null +++ b/homeassistant/components/airthings_ble/translations/ko.json @@ -0,0 +1,11 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airthings_ble/translations/no.json b/homeassistant/components/airthings_ble/translations/no.json index d23d4703ac3..570c5e9ef84 100644 --- a/homeassistant/components/airthings_ble/translations/no.json +++ b/homeassistant/components/airthings_ble/translations/no.json @@ -10,7 +10,7 @@ "flow_title": "{name}", "step": { "bluetooth_confirm": { - "description": "Vil du konfigurere {name}?" + "description": "Vil du sette opp {name} ?" }, "user": { "data": { diff --git a/homeassistant/components/airthings_ble/translations/pt.json b/homeassistant/components/airthings_ble/translations/pt.json new file mode 100644 index 00000000000..593a8bcd046 --- /dev/null +++ b/homeassistant/components/airthings_ble/translations/pt.json @@ -0,0 +1,11 @@ +{ + "config": { + "abort": { + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", + "already_in_progress": "O processo de configura\u00e7\u00e3o j\u00e1 est\u00e1 a decorrer", + "cannot_connect": "A liga\u00e7\u00e3o falhou", + "no_devices_found": "Nenhum dispositivo encontrado na rede", + "unknown": "Erro inesperado" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airtouch4/climate.py b/homeassistant/components/airtouch4/climate.py index 598b6ecd6e3..e7d73ec0f1c 100644 --- a/homeassistant/components/airtouch4/climate.py +++ b/homeassistant/components/airtouch4/climate.py @@ -16,7 +16,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -87,7 +87,7 @@ class AirtouchAC(CoordinatorEntity, ClimateEntity): _attr_supported_features = ( ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.FAN_MODE ) - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS def __init__(self, coordinator, ac_number, info): """Initialize the climate device.""" @@ -201,7 +201,7 @@ class AirtouchGroup(CoordinatorEntity, ClimateEntity): """Representation of an AirTouch 4 group.""" _attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS _attr_hvac_modes = AT_GROUP_MODES def __init__(self, coordinator, group_number, info): diff --git a/homeassistant/components/airtouch4/strings.json b/homeassistant/components/airtouch4/strings.json index 5259b20fb73..240b3e0007c 100644 --- a/homeassistant/components/airtouch4/strings.json +++ b/homeassistant/components/airtouch4/strings.json @@ -9,7 +9,7 @@ }, "step": { "user": { - "title": "Setup your AirTouch 4 connection details.", + "title": "Set up your AirTouch 4 connection details.", "data": { "host": "[%key:common::config_flow::data::host%]" } diff --git a/homeassistant/components/airtouch4/translations/ca.json b/homeassistant/components/airtouch4/translations/ca.json index 083c4a0ba87..412337bf1c3 100644 --- a/homeassistant/components/airtouch4/translations/ca.json +++ b/homeassistant/components/airtouch4/translations/ca.json @@ -12,7 +12,7 @@ "data": { "host": "Amfitri\u00f3" }, - "title": "Configura els detalls de connexi\u00f3 d'AirTouch 4." + "title": "Configuraci\u00f3 dels detalls de connexi\u00f3 d'AirTouch 4." } } } diff --git a/homeassistant/components/airtouch4/translations/en.json b/homeassistant/components/airtouch4/translations/en.json index 0f86b787249..54872872339 100644 --- a/homeassistant/components/airtouch4/translations/en.json +++ b/homeassistant/components/airtouch4/translations/en.json @@ -12,7 +12,7 @@ "data": { "host": "Host" }, - "title": "Setup your AirTouch 4 connection details." + "title": "Set up your AirTouch 4 connection details." } } } diff --git a/homeassistant/components/airtouch4/translations/it.json b/homeassistant/components/airtouch4/translations/it.json index f9a72a50e33..534d391a961 100644 --- a/homeassistant/components/airtouch4/translations/it.json +++ b/homeassistant/components/airtouch4/translations/it.json @@ -12,7 +12,7 @@ "data": { "host": "Host" }, - "title": "Imposta i dettagli della connessione AirTouch 4." + "title": "Configura i dettagli della connessione di AirTouch 4." } } } diff --git a/homeassistant/components/airtouch4/translations/ko.json b/homeassistant/components/airtouch4/translations/ko.json new file mode 100644 index 00000000000..54eae29c72e --- /dev/null +++ b/homeassistant/components/airtouch4/translations/ko.json @@ -0,0 +1,17 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airtouch4/translations/no.json b/homeassistant/components/airtouch4/translations/no.json index 66bf4e3b915..3b92d663bd4 100644 --- a/homeassistant/components/airtouch4/translations/no.json +++ b/homeassistant/components/airtouch4/translations/no.json @@ -12,7 +12,7 @@ "data": { "host": "Vert" }, - "title": "Konfigurer AirTouch 4 -tilkoblingsdetaljer." + "title": "Konfigurer AirTouch 4-tilkoblingsdetaljer." } } } diff --git a/homeassistant/components/airtouch4/translations/pl.json b/homeassistant/components/airtouch4/translations/pl.json index 55f0b72b1a7..c8df85524d8 100644 --- a/homeassistant/components/airtouch4/translations/pl.json +++ b/homeassistant/components/airtouch4/translations/pl.json @@ -12,7 +12,7 @@ "data": { "host": "Nazwa hosta lub adres IP" }, - "title": "Konfiguracja po\u0142\u0105czenia AirTouch 4." + "title": "Skonfiguruj szczeg\u00f3\u0142y po\u0142\u0105czenia AirTouch 4." } } } diff --git a/homeassistant/components/airvisual/__init__.py b/homeassistant/components/airvisual/__init__.py index 2a544edb20a..16579be9a72 100644 --- a/homeassistant/components/airvisual/__init__.py +++ b/homeassistant/components/airvisual/__init__.py @@ -1,23 +1,27 @@ -"""The airvisual component.""" +"""The AirVisual component.""" from __future__ import annotations +import asyncio from collections.abc import Mapping from datetime import timedelta from math import ceil from typing import Any -from pyairvisual import CloudAPI, NodeSamba -from pyairvisual.cloud_api import InvalidKeyError, KeyExpiredError, UnauthorizedError +from pyairvisual.cloud_api import ( + CloudAPI, + InvalidKeyError, + KeyExpiredError, + UnauthorizedError, +) from pyairvisual.errors import AirVisualError -from pyairvisual.node import NodeProError -from homeassistant.config_entries import ConfigEntry +from homeassistant.components import automation +from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry from homeassistant.const import ( CONF_API_KEY, CONF_IP_ADDRESS, CONF_LATITUDE, CONF_LONGITUDE, - CONF_PASSWORD, CONF_SHOW_ON_MAP, CONF_STATE, Platform, @@ -27,9 +31,11 @@ from homeassistant.exceptions import ConfigEntryAuthFailed from homeassistant.helpers import ( aiohttp_client, config_validation as cv, - entity_registry, + device_registry as dr, + entity_registry as er, ) from homeassistant.helpers.entity import EntityDescription +from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue from homeassistant.helpers.update_coordinator import ( CoordinatorEntity, DataUpdateCoordinator, @@ -48,6 +54,10 @@ from .const import ( LOGGER, ) +# We use a raw string for the airvisual_pro domain (instead of importing the actual +# constant) so that we can avoid listing it as a dependency: +DOMAIN_AIRVISUAL_PRO = "airvisual_pro" + PLATFORMS = [Platform.SENSOR] DEFAULT_ATTRIBUTION = "Data provided by AirVisual" @@ -56,22 +66,6 @@ DEFAULT_NODE_PRO_UPDATE_INTERVAL = timedelta(minutes=1) CONFIG_SCHEMA = cv.removed(DOMAIN, raise_if_present=False) -@callback -def async_get_geography_id(geography_dict: Mapping[str, Any]) -> str: - """Generate a unique ID from a geography dict.""" - if CONF_CITY in geography_dict: - return ", ".join( - ( - geography_dict[CONF_CITY], - geography_dict[CONF_STATE], - geography_dict[CONF_COUNTRY], - ) - ) - return ", ".join( - (str(geography_dict[CONF_LATITUDE]), str(geography_dict[CONF_LONGITUDE])) - ) - - @callback def async_get_cloud_api_update_interval( hass: HomeAssistant, api_key: str, num_consumers: int @@ -108,6 +102,22 @@ def async_get_cloud_coordinators_by_api_key( ] +@callback +def async_get_geography_id(geography_dict: Mapping[str, Any]) -> str: + """Generate a unique ID from a geography dict.""" + if CONF_CITY in geography_dict: + return ", ".join( + ( + geography_dict[CONF_CITY], + geography_dict[CONF_STATE], + geography_dict[CONF_COUNTRY], + ) + ) + return ", ".join( + (str(geography_dict[CONF_LATITUDE]), str(geography_dict[CONF_LONGITUDE])) + ) + + @callback def async_sync_geo_coordinator_update_intervals( hass: HomeAssistant, api_key: str @@ -166,108 +176,59 @@ def _standardize_geography_config_entry( hass.config_entries.async_update_entry(entry, **entry_updates) -@callback -def _standardize_node_pro_config_entry(hass: HomeAssistant, entry: ConfigEntry) -> None: - """Ensure that Node/Pro config entries have appropriate properties.""" - entry_updates: dict[str, Any] = {} - - if CONF_INTEGRATION_TYPE not in entry.data: - # If the config entry data doesn't contain the integration type, add it: - entry_updates["data"] = { - **entry.data, - CONF_INTEGRATION_TYPE: INTEGRATION_TYPE_NODE_PRO, - } - - if not entry_updates: - return - - hass.config_entries.async_update_entry(entry, **entry_updates) - - async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up AirVisual as config entry.""" - if CONF_API_KEY in entry.data: - _standardize_geography_config_entry(hass, entry) + if CONF_API_KEY not in entry.data: + # If this is a migrated AirVisual Pro entry, there's no actual setup to do; + # that will be handled by the `airvisual_pro` domain: + return False - websession = aiohttp_client.async_get_clientsession(hass) - cloud_api = CloudAPI(entry.data[CONF_API_KEY], session=websession) + _standardize_geography_config_entry(hass, entry) - async def async_update_data() -> dict[str, Any]: - """Get new data from the API.""" - if CONF_CITY in entry.data: - api_coro = cloud_api.air_quality.city( - entry.data[CONF_CITY], - entry.data[CONF_STATE], - entry.data[CONF_COUNTRY], - ) - else: - api_coro = cloud_api.air_quality.nearest_city( - entry.data[CONF_LATITUDE], - entry.data[CONF_LONGITUDE], - ) + websession = aiohttp_client.async_get_clientsession(hass) + cloud_api = CloudAPI(entry.data[CONF_API_KEY], session=websession) - try: - return await api_coro - except (InvalidKeyError, KeyExpiredError, UnauthorizedError) as ex: - raise ConfigEntryAuthFailed from ex - except AirVisualError as err: - raise UpdateFailed(f"Error while retrieving data: {err}") from err - - coordinator = DataUpdateCoordinator( - hass, - LOGGER, - name=async_get_geography_id(entry.data), - # We give a placeholder update interval in order to create the coordinator; - # then, below, we use the coordinator's presence (along with any other - # coordinators using the same API key) to calculate an actual, leveled - # update interval: - update_interval=timedelta(minutes=5), - update_method=async_update_data, - ) - - # Only geography-based entries have options: - entry.async_on_unload(entry.add_update_listener(async_reload_entry)) - else: - # Remove outdated air_quality entities from the entity registry if they exist: - ent_reg = entity_registry.async_get(hass) - for entity_entry in [ - e - for e in ent_reg.entities.values() - if e.config_entry_id == entry.entry_id - and e.entity_id.startswith("air_quality") - ]: - LOGGER.debug( - 'Removing deprecated air_quality entity: "%s"', entity_entry.entity_id + async def async_update_data() -> dict[str, Any]: + """Get new data from the API.""" + if CONF_CITY in entry.data: + api_coro = cloud_api.air_quality.city( + entry.data[CONF_CITY], + entry.data[CONF_STATE], + entry.data[CONF_COUNTRY], + ) + else: + api_coro = cloud_api.air_quality.nearest_city( + entry.data[CONF_LATITUDE], + entry.data[CONF_LONGITUDE], ) - ent_reg.async_remove(entity_entry.entity_id) - _standardize_node_pro_config_entry(hass, entry) + try: + return await api_coro + except (InvalidKeyError, KeyExpiredError, UnauthorizedError) as ex: + raise ConfigEntryAuthFailed from ex + except AirVisualError as err: + raise UpdateFailed(f"Error while retrieving data: {err}") from err - async def async_update_data() -> dict[str, Any]: - """Get new data from the API.""" - try: - async with NodeSamba( - entry.data[CONF_IP_ADDRESS], 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 + coordinator = DataUpdateCoordinator( + hass, + LOGGER, + name=async_get_geography_id(entry.data), + # We give a placeholder update interval in order to create the coordinator; + # then, below, we use the coordinator's presence (along with any other + # coordinators using the same API key) to calculate an actual, leveled + # update interval: + update_interval=timedelta(minutes=5), + update_method=async_update_data, + ) - coordinator = DataUpdateCoordinator( - hass, - LOGGER, - name="Node/Pro data", - update_interval=DEFAULT_NODE_PRO_UPDATE_INTERVAL, - update_method=async_update_data, - ) + entry.async_on_unload(entry.add_update_listener(async_reload_entry)) await coordinator.async_config_entry_first_refresh() hass.data.setdefault(DOMAIN, {}) hass.data[DOMAIN][entry.entry_id] = coordinator # Reassess the interval between 2 server requests - if CONF_API_KEY in entry.data: - async_sync_geo_coordinator_update_intervals(hass, entry.data[CONF_API_KEY]) + async_sync_geo_coordinator_update_intervals(hass, entry.data[CONF_API_KEY]) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) @@ -311,6 +272,113 @@ async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: ) ) + # 2 -> 3: Moving AirVisual Pro to its own domain + elif version == 2: + version = 3 + + if entry.data[CONF_INTEGRATION_TYPE] == INTEGRATION_TYPE_NODE_PRO: + device_registry = dr.async_get(hass) + entity_registry = er.async_get(hass) + ip_address = entry.data[CONF_IP_ADDRESS] + + # Store the existing Pro device before the migration removes it: + old_device_entry = next( + entry + for entry in dr.async_entries_for_config_entry( + device_registry, entry.entry_id + ) + ) + + # Store the existing Pro entity entries (mapped by unique ID) before the + # migration removes it: + old_entity_entries: dict[str, er.RegistryEntry] = { + entry.unique_id: entry + for entry in er.async_entries_for_device( + entity_registry, old_device_entry.id, include_disabled_entities=True + ) + } + + # Remove this config entry and create a new one under the `airvisual_pro` + # domain: + new_entry_data = {**entry.data} + new_entry_data.pop(CONF_INTEGRATION_TYPE) + tasks = [ + hass.config_entries.async_remove(entry.entry_id), + hass.config_entries.flow.async_init( + DOMAIN_AIRVISUAL_PRO, + context={"source": SOURCE_IMPORT}, + data=new_entry_data, + ), + ] + await asyncio.gather(*tasks) + + # After the migration has occurred, grab the new config and device entries + # (now under the `airvisual_pro` domain): + new_config_entry = next( + entry + for entry in hass.config_entries.async_entries(DOMAIN_AIRVISUAL_PRO) + if entry.data[CONF_IP_ADDRESS] == ip_address + ) + new_device_entry = next( + entry + for entry in dr.async_entries_for_config_entry( + device_registry, new_config_entry.entry_id + ) + ) + + # Update the new device entry with any customizations from the old one: + device_registry.async_update_device( + new_device_entry.id, + area_id=old_device_entry.area_id, + disabled_by=old_device_entry.disabled_by, + name_by_user=old_device_entry.name_by_user, + ) + + # Update the new entity entries with any customizations from the old ones: + for new_entity_entry in er.async_entries_for_device( + entity_registry, new_device_entry.id, include_disabled_entities=True + ): + if old_entity_entry := old_entity_entries.get( + new_entity_entry.unique_id + ): + entity_registry.async_update_entity( + new_entity_entry.entity_id, + area_id=old_entity_entry.area_id, + device_class=old_entity_entry.device_class, + disabled_by=old_entity_entry.disabled_by, + hidden_by=old_entity_entry.hidden_by, + icon=old_entity_entry.icon, + name=old_entity_entry.name, + new_entity_id=old_entity_entry.entity_id, + unit_of_measurement=old_entity_entry.unit_of_measurement, + ) + + # If any automations are using the old device ID, create a Repairs issues + # with instructions on how to update it: + if device_automations := automation.automations_with_device( + hass, old_device_entry.id + ): + async_create_issue( + hass, + DOMAIN, + f"airvisual_pro_migration_{entry.entry_id}", + is_fixable=False, + is_persistent=True, + severity=IssueSeverity.WARNING, + translation_key="airvisual_pro_migration", + translation_placeholders={ + "ip_address": ip_address, + "old_device_id": old_device_entry.id, + "new_device_id": new_device_entry.id, + "device_automations_string": ", ".join( + f"`{automation}`" for automation in device_automations + ), + }, + ) + else: + entry.version = version + hass.config_entries.async_update_entry(entry) + LOGGER.info("Migration to version %s successful", version) return True diff --git a/homeassistant/components/airvisual/config_flow.py b/homeassistant/components/airvisual/config_flow.py index 8ba75c43bdb..57d28ab0e87 100644 --- a/homeassistant/components/airvisual/config_flow.py +++ b/homeassistant/components/airvisual/config_flow.py @@ -5,25 +5,22 @@ import asyncio from collections.abc import Mapping from typing import Any -from pyairvisual import CloudAPI, NodeSamba from pyairvisual.cloud_api import ( + CloudAPI, InvalidKeyError, KeyExpiredError, NotFoundError, UnauthorizedError, ) from pyairvisual.errors import AirVisualError -from pyairvisual.node import NodeProError import voluptuous as vol from homeassistant import config_entries from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONF_API_KEY, - CONF_IP_ADDRESS, CONF_LATITUDE, CONF_LONGITUDE, - CONF_PASSWORD, CONF_SHOW_ON_MAP, CONF_STATE, ) @@ -43,7 +40,6 @@ from .const import ( DOMAIN, INTEGRATION_TYPE_GEOGRAPHY_COORDS, INTEGRATION_TYPE_GEOGRAPHY_NAME, - INTEGRATION_TYPE_NODE_PRO, LOGGER, ) @@ -55,16 +51,12 @@ GEOGRAPHY_NAME_SCHEMA = API_KEY_DATA_SCHEMA.extend( vol.Required(CONF_COUNTRY): cv.string, } ) -NODE_PRO_SCHEMA = vol.Schema( - {vol.Required(CONF_IP_ADDRESS): str, vol.Required(CONF_PASSWORD): cv.string} -) PICK_INTEGRATION_TYPE_SCHEMA = vol.Schema( { vol.Required("type"): vol.In( [ INTEGRATION_TYPE_GEOGRAPHY_COORDS, INTEGRATION_TYPE_GEOGRAPHY_NAME, - INTEGRATION_TYPE_NODE_PRO, ] ) } @@ -81,7 +73,7 @@ OPTIONS_FLOW = { class AirVisualFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): """Handle an AirVisual config flow.""" - VERSION = 2 + VERSION = 3 def __init__(self) -> None: """Initialize the config flow.""" @@ -206,34 +198,6 @@ class AirVisualFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): user_input, INTEGRATION_TYPE_GEOGRAPHY_NAME ) - async def async_step_node_pro( - self, user_input: dict[str, str] | None = None - ) -> FlowResult: - """Handle the initialization of the integration with a Node/Pro.""" - if not user_input: - return self.async_show_form(step_id="node_pro", data_schema=NODE_PRO_SCHEMA) - - await self._async_set_unique_id(user_input[CONF_IP_ADDRESS]) - - node = NodeSamba(user_input[CONF_IP_ADDRESS], user_input[CONF_PASSWORD]) - - try: - await node.async_connect() - except NodeProError as err: - LOGGER.error("Error connecting to Node/Pro unit: %s", err) - return self.async_show_form( - step_id="node_pro", - data_schema=NODE_PRO_SCHEMA, - errors={CONF_IP_ADDRESS: "cannot_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, entry_data: Mapping[str, Any]) -> FlowResult: """Handle configuration by re-auth.""" self._entry_data_for_reauth = entry_data @@ -266,6 +230,4 @@ class AirVisualFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): if user_input["type"] == INTEGRATION_TYPE_GEOGRAPHY_COORDS: return await self.async_step_geography_by_coords() - if user_input["type"] == INTEGRATION_TYPE_GEOGRAPHY_NAME: - return await self.async_step_geography_by_name() - return await self.async_step_node_pro() + return await self.async_step_geography_by_name() diff --git a/homeassistant/components/airvisual/manifest.json b/homeassistant/components/airvisual/manifest.json index ae9eeb270a8..f8502784ee0 100644 --- a/homeassistant/components/airvisual/manifest.json +++ b/homeassistant/components/airvisual/manifest.json @@ -1,11 +1,12 @@ { "domain": "airvisual", - "name": "AirVisual", + "name": "AirVisual Cloud", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/airvisual", - "requirements": ["pyairvisual==2022.11.1"], + "requirements": ["pyairvisual==2022.12.1"], + "dependencies": ["airvisual_pro"], "codeowners": ["@bachya"], "iot_class": "cloud_polling", "loggers": ["pyairvisual", "pysmb"], - "integration_type": "device" + "integration_type": "service" } diff --git a/homeassistant/components/airvisual/sensor.py b/homeassistant/components/airvisual/sensor.py index e28a11666da..cfb9b67ff38 100644 --- a/homeassistant/components/airvisual/sensor.py +++ b/homeassistant/components/airvisual/sensor.py @@ -20,22 +20,15 @@ from homeassistant.const import ( CONF_SHOW_ON_MAP, CONF_STATE, PERCENTAGE, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers.entity import DeviceInfo, EntityCategory +from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from . import AirVisualEntity -from .const import ( - CONF_CITY, - CONF_COUNTRY, - CONF_INTEGRATION_TYPE, - DOMAIN, - INTEGRATION_TYPE_GEOGRAPHY_COORDS, - INTEGRATION_TYPE_GEOGRAPHY_NAME, -) +from .const import CONF_CITY, CONF_COUNTRY, DOMAIN ATTR_CITY = "city" ATTR_COUNTRY = "country" @@ -43,9 +36,6 @@ ATTR_POLLUTANT_SYMBOL = "pollutant_symbol" ATTR_POLLUTANT_UNIT = "pollutant_unit" ATTR_REGION = "region" -DEVICE_CLASS_POLLUTANT_LABEL = "airvisual__pollutant_label" -DEVICE_CLASS_POLLUTANT_LEVEL = "airvisual__pollutant_level" - SENSOR_KIND_AQI = "air_quality_index" SENSOR_KIND_BATTERY_LEVEL = "battery_level" SENSOR_KIND_CO2 = "carbon_dioxide" @@ -63,21 +53,31 @@ GEOGRAPHY_SENSOR_DESCRIPTIONS = ( SensorEntityDescription( key=SENSOR_KIND_LEVEL, name="Air pollution level", - device_class=DEVICE_CLASS_POLLUTANT_LEVEL, icon="mdi:gauge", + device_class=SensorDeviceClass.ENUM, + options=[ + "good", + "moderate", + "unhealthy", + "unhealthy_sensitive", + "very_unhealthy", + "hazardous", + ], + translation_key="pollutant_level", ), SensorEntityDescription( key=SENSOR_KIND_AQI, name="Air quality index", device_class=SensorDeviceClass.AQI, - native_unit_of_measurement="AQI", state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=SENSOR_KIND_POLLUTANT, name="Main pollutant", - device_class=DEVICE_CLASS_POLLUTANT_LABEL, icon="mdi:chemical-weapon", + device_class=SensorDeviceClass.ENUM, + options=["co", "n2", "o3", "p1", "p2", "s2"], + translation_key="pollutant_label", ), ) GEOGRAPHY_SENSOR_LOCALES = {"cn": "Chinese", "us": "U.S."} @@ -135,7 +135,7 @@ NODE_PRO_SENSOR_DESCRIPTIONS = ( key=SENSOR_KIND_TEMPERATURE, name="Temperature", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( @@ -185,24 +185,11 @@ async def async_setup_entry( ) -> None: """Set up AirVisual sensors based on a config entry.""" coordinator = hass.data[DOMAIN][entry.entry_id] - - sensors: list[AirVisualGeographySensor | AirVisualNodeProSensor] - if entry.data[CONF_INTEGRATION_TYPE] in ( - INTEGRATION_TYPE_GEOGRAPHY_COORDS, - INTEGRATION_TYPE_GEOGRAPHY_NAME, - ): - sensors = [ - AirVisualGeographySensor(coordinator, entry, description, locale) - for locale in GEOGRAPHY_SENSOR_LOCALES - for description in GEOGRAPHY_SENSOR_DESCRIPTIONS - ] - else: - sensors = [ - AirVisualNodeProSensor(coordinator, entry, description) - for description in NODE_PRO_SENSOR_DESCRIPTIONS - ] - - async_add_entities(sensors, True) + async_add_entities( + AirVisualGeographySensor(coordinator, entry, description, locale) + for locale in GEOGRAPHY_SENSOR_LOCALES + for description in GEOGRAPHY_SENSOR_DESCRIPTIONS + ) class AirVisualGeographySensor(AirVisualEntity, SensorEntity): @@ -287,67 +274,3 @@ class AirVisualGeographySensor(AirVisualEntity, SensorEntity): self._attr_extra_state_attributes["long"] = longitude self._attr_extra_state_attributes.pop(ATTR_LATITUDE, None) self._attr_extra_state_attributes.pop(ATTR_LONGITUDE, None) - - -class AirVisualNodeProSensor(AirVisualEntity, SensorEntity): - """Define an AirVisual sensor related to a Node/Pro unit.""" - - _attr_has_entity_name = True - - def __init__( - self, - coordinator: DataUpdateCoordinator, - entry: ConfigEntry, - description: SensorEntityDescription, - ) -> None: - """Initialize.""" - super().__init__(coordinator, entry, description) - - self._attr_unique_id = f"{coordinator.data['serial_number']}_{description.key}" - - @property - def device_info(self) -> DeviceInfo: - """Return device registry information for this entity.""" - return DeviceInfo( - identifiers={(DOMAIN, self.coordinator.data["serial_number"])}, - manufacturer="AirVisual", - model=f'{self.coordinator.data["status"]["model"]}', - name=self.coordinator.data["settings"]["node_name"], - sw_version=( - f'Version {self.coordinator.data["status"]["system_version"]}' - f'{self.coordinator.data["status"]["app_version"]}' - ), - ) - - @callback - def update_from_latest_data(self) -> None: - """Update the entity from the latest data.""" - if self.entity_description.key == SENSOR_KIND_AQI: - if self.coordinator.data["settings"]["is_aqi_usa"]: - self._attr_native_value = self.coordinator.data["measurements"][ - "aqi_us" - ] - else: - self._attr_native_value = self.coordinator.data["measurements"][ - "aqi_cn" - ] - elif self.entity_description.key == SENSOR_KIND_BATTERY_LEVEL: - self._attr_native_value = self.coordinator.data["status"]["battery"] - elif self.entity_description.key == SENSOR_KIND_CO2: - self._attr_native_value = self.coordinator.data["measurements"].get("co2") - elif self.entity_description.key == SENSOR_KIND_HUMIDITY: - self._attr_native_value = self.coordinator.data["measurements"].get( - "humidity" - ) - elif self.entity_description.key == SENSOR_KIND_PM_0_1: - self._attr_native_value = self.coordinator.data["measurements"].get("pm0_1") - elif self.entity_description.key == SENSOR_KIND_PM_1_0: - self._attr_native_value = self.coordinator.data["measurements"].get("pm1_0") - elif self.entity_description.key == SENSOR_KIND_PM_2_5: - self._attr_native_value = self.coordinator.data["measurements"].get("pm2_5") - elif self.entity_description.key == SENSOR_KIND_TEMPERATURE: - self._attr_native_value = self.coordinator.data["measurements"].get( - "temperature_C" - ) - elif self.entity_description.key == SENSOR_KIND_VOC: - self._attr_native_value = self.coordinator.data["measurements"].get("voc") diff --git a/homeassistant/components/airvisual/strings.json b/homeassistant/components/airvisual/strings.json index 8d2dce85a17..18183eee197 100644 --- a/homeassistant/components/airvisual/strings.json +++ b/homeassistant/components/airvisual/strings.json @@ -20,14 +20,6 @@ "state": "state" } }, - "node_pro": { - "title": "Configure an AirVisual Node/Pro", - "description": "Monitor a personal AirVisual unit. The password can be retrieved from the unit's UI.", - "data": { - "ip_address": "[%key:common::config_flow::data::host%]", - "password": "[%key:common::config_flow::data::password%]" - } - }, "reauth_confirm": { "title": "Re-authenticate AirVisual", "data": { @@ -46,7 +38,7 @@ "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]" }, "abort": { - "already_configured": "[%key:common::config_flow::abort::already_configured_location%] or Node/Pro ID is already registered.", + "already_configured": "[%key:common::config_flow::abort::already_configured_location%]", "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]" } }, @@ -59,5 +51,35 @@ } } } + }, + "entity": { + "sensor": { + "pollutant_label": { + "state": { + "co": "Carbon Monoxide", + "n2": "Nitrogen Dioxide", + "o3": "Ozone", + "p1": "PM10", + "p2": "PM2.5", + "s2": "Sulfur Dioxide" + } + }, + "pollutant_level": { + "state": { + "good": "Good", + "moderate": "Moderate", + "unhealthy": "Unhealthy", + "unhealthy_sensitive": "Unhealthy for sensitive groups", + "very_unhealthy": "Very unhealthy", + "hazardous": "Hazardous" + } + } + } + }, + "issues": { + "airvisual_pro_migration": { + "title": "{ip_address} is now part of the AirVisual Pro integration", + "description": "AirVisual Pro units are now their own Home Assistant integration (as opposed to be included with the original AirVisual integration that uses the AirVisual cloud API). The Pro device located at `{ip_address}` has automatically been migrated.\n\nAs part of that migration, the Pro's device ID has changed from `{old_device_id}` to `{new_device_id}`. Please update these automations to use the new device ID: {device_automations_string}." + } } } diff --git a/homeassistant/components/airvisual/strings.sensor.json b/homeassistant/components/airvisual/strings.sensor.json deleted file mode 100644 index 583ddaf4f3b..00000000000 --- a/homeassistant/components/airvisual/strings.sensor.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "state": { - "airvisual__pollutant_label": { - "co": "Carbon Monoxide", - "n2": "Nitrogen Dioxide", - "o3": "Ozone", - "p1": "PM10", - "p2": "PM2.5", - "s2": "Sulfur Dioxide" - }, - "airvisual__pollutant_level": { - "good": "Good", - "moderate": "Moderate", - "unhealthy": "Unhealthy", - "unhealthy_sensitive": "Unhealthy for sensitive groups", - "very_unhealthy": "Very unhealthy", - "hazardous": "Hazardous" - } - } -} diff --git a/homeassistant/components/airvisual/translations/bg.json b/homeassistant/components/airvisual/translations/bg.json index d97a78ab191..b01f61640bd 100644 --- a/homeassistant/components/airvisual/translations/bg.json +++ b/homeassistant/components/airvisual/translations/bg.json @@ -34,5 +34,19 @@ } } } + }, + "entity": { + "sensor": { + "pollutant_label": { + "state": { + "co": "\u0412\u044a\u0433\u043b\u0435\u0440\u043e\u0434\u0435\u043d \u043e\u043a\u0441\u0438\u0434", + "n2": "\u0410\u0437\u043e\u0442\u0435\u043d \u0434\u0438\u043e\u043a\u0441\u0438\u0434", + "o3": "\u041e\u0437\u043e\u043d", + "p1": "PM10", + "p2": "PM2.5", + "s2": "\u0421\u0435\u0440\u0435\u043d \u0434\u0438\u043e\u043a\u0441\u0438\u0434" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/airvisual/translations/ca.json b/homeassistant/components/airvisual/translations/ca.json index 0440189cdb9..bd2fabdebe4 100644 --- a/homeassistant/components/airvisual/translations/ca.json +++ b/homeassistant/components/airvisual/translations/ca.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "La ubicaci\u00f3 ja est\u00e0 configurada o el Node/Pro ID ja est\u00e0 registrat.", + "already_configured": "La ubicaci\u00f3 ja est\u00e0 configurada", "reauth_successful": "Re-autenticaci\u00f3 realitzada correctament" }, "error": { @@ -50,6 +50,36 @@ } } }, + "entity": { + "sensor": { + "pollutant_label": { + "state": { + "co": "Mon\u00f2xid de carboni", + "n2": "Di\u00f2xid de nitrogen", + "o3": "Oz\u00f3", + "p1": "PM10", + "p2": "PM2.5", + "s2": "Di\u00f2xid de sofre" + } + }, + "pollutant_level": { + "state": { + "good": "Bo", + "hazardous": "Perill\u00f3s", + "moderate": "Moderat", + "unhealthy": "No saludable", + "unhealthy_sensitive": "No saludable per a grups sensibles", + "very_unhealthy": "Gens saludable" + } + } + } + }, + "issues": { + "airvisual_pro_migration": { + "description": "Les unitats d'AirVisual Pro s\u00f3n ara la seva pr\u00f2pia integraci\u00f3 de Home Assistant (en lloc de ser incloses a trav\u00e9s de la integraci\u00f3 AirVisual original que utilitza l'API del n\u00favol d'AirVisual). El dispositiu Pro que es troba a `{ip_address}` s'ha migrat autom\u00e0ticament. \n\nCom a part de la migraci\u00f3, l'identificador (ID) del dispositiu Pro ha canviat de `{old_device_id}` a `{new_device_id}`. Actualitza les seg\u00fcents automatitzacions amb el nou ID de dispositiu: {device_automations_string}.", + "title": "{ip_address} ara forma part de la integraci\u00f3 AirVisual Pro" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/airvisual/translations/cs.json b/homeassistant/components/airvisual/translations/cs.json index 4fd193e6ddc..ba9a28bfc87 100644 --- a/homeassistant/components/airvisual/translations/cs.json +++ b/homeassistant/components/airvisual/translations/cs.json @@ -43,6 +43,16 @@ } } }, + "entity": { + "sensor": { + "pollutant_level": { + "state": { + "good": "Dobr\u00e9", + "hazardous": "Nebezpe\u010dn\u00e9" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/airvisual/translations/de.json b/homeassistant/components/airvisual/translations/de.json index c6d00ea1375..8512e797cfd 100644 --- a/homeassistant/components/airvisual/translations/de.json +++ b/homeassistant/components/airvisual/translations/de.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Diese Node/Pro ID oder Standort ist bereits konfiguriert.", + "already_configured": "Standort ist bereits konfiguriert", "reauth_successful": "Die erneute Authentifizierung war erfolgreich" }, "error": { @@ -50,6 +50,36 @@ } } }, + "entity": { + "sensor": { + "pollutant_label": { + "state": { + "co": "Kohlenmonoxid", + "n2": "Stickstoffdioxid", + "o3": "Ozon", + "p1": "PM10", + "p2": "PM2,5", + "s2": "Schwefeldioxid" + } + }, + "pollutant_level": { + "state": { + "good": "Gut", + "hazardous": "Gef\u00e4hrlich", + "moderate": "M\u00e4\u00dfig", + "unhealthy": "Ungesund", + "unhealthy_sensitive": "Ungesund f\u00fcr sensible Gruppen", + "very_unhealthy": "Sehr ungesund" + } + } + } + }, + "issues": { + "airvisual_pro_migration": { + "description": "AirVisual Pro-Ger\u00e4te sind jetzt eine eigene Home Assistant-Integration (im Gegensatz zur urspr\u00fcnglichen AirVisual-Integration, die die AirVisual Cloud API verwendet). Das Pro-Ger\u00e4t, das sich unter `{ip_address}` befindet, wurde automatisch migriert.\n\nAls Teil dieser Migration hat sich die Ger\u00e4te-ID des Pro-Ger\u00e4ts von `{old_device_id}` zu `{new_device_id}` ge\u00e4ndert. Bitte aktualisiere diese Automatisierungen, um die neue Ger\u00e4te-ID zu verwenden: {device_automations_string}.", + "title": "{ip_address} ist jetzt Teil der AirVisual Pro-Integration" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/airvisual/translations/el.json b/homeassistant/components/airvisual/translations/el.json index bb4268a3ffd..8b3e8a4ef9a 100644 --- a/homeassistant/components/airvisual/translations/el.json +++ b/homeassistant/components/airvisual/translations/el.json @@ -50,6 +50,36 @@ } } }, + "entity": { + "sensor": { + "pollutant_label": { + "state": { + "co": "\u039c\u03bf\u03bd\u03bf\u03be\u03b5\u03af\u03b4\u03b9\u03bf \u03c4\u03bf\u03c5 \u03ac\u03bd\u03b8\u03c1\u03b1\u03ba\u03b1", + "n2": "\u0394\u03b9\u03bf\u03be\u03b5\u03af\u03b4\u03b9\u03bf \u03c4\u03bf\u03c5 \u03b1\u03b6\u03ce\u03c4\u03bf\u03c5", + "o3": "\u038c\u03b6\u03bf\u03bd", + "p1": "PM10", + "p2": "PM2.5", + "s2": "\u0394\u03b9\u03bf\u03be\u03b5\u03af\u03b4\u03b9\u03bf \u03c4\u03bf\u03c5 \u03b8\u03b5\u03af\u03bf\u03c5" + } + }, + "pollutant_level": { + "state": { + "good": "\u039a\u03b1\u03bb\u03cc", + "hazardous": "\u0395\u03c0\u03b9\u03ba\u03af\u03bd\u03b4\u03c5\u03bd\u03bf", + "moderate": "\u039c\u03ad\u03c4\u03c1\u03b9\u03bf", + "unhealthy": "\u0391\u03bd\u03b8\u03c5\u03b3\u03b9\u03b5\u03b9\u03bd\u03cc", + "unhealthy_sensitive": "\u0391\u03bd\u03b8\u03c5\u03b3\u03b9\u03b5\u03b9\u03bd\u03cc \u03b3\u03b9\u03b1 \u03b5\u03c5\u03b1\u03af\u03c3\u03b8\u03b7\u03c4\u03b5\u03c2 \u03bf\u03bc\u03ac\u03b4\u03b5\u03c2", + "very_unhealthy": "\u03a0\u03bf\u03bb\u03cd \u03b1\u03bd\u03b8\u03c5\u03b3\u03b9\u03b5\u03b9\u03bd\u03cc" + } + } + } + }, + "issues": { + "airvisual_pro_migration": { + "description": "\u039f\u03b9 \u03bc\u03bf\u03bd\u03ac\u03b4\u03b5\u03c2 AirVisual Pro \u03b1\u03c0\u03bf\u03c4\u03b5\u03bb\u03bf\u03cd\u03bd \u03c0\u03bb\u03ad\u03bf\u03bd \u03c4\u03b7 \u03b4\u03b9\u03ba\u03ae \u03c4\u03bf\u03c5\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 Home Assistant (\u03c3\u03b5 \u03b1\u03bd\u03c4\u03af\u03b8\u03b5\u03c3\u03b7 \u03bc\u03b5 \u03c4\u03b7\u03bd \u03b1\u03c1\u03c7\u03b9\u03ba\u03ae \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 AirVisual \u03c0\u03bf\u03c5 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af \u03c4\u03bf AirVisual cloud API). \u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae Pro \u03c0\u03bf\u03c5 \u03b2\u03c1\u03af\u03c3\u03ba\u03b5\u03c4\u03b1\u03b9 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 ` {ip_address} ` \u03ad\u03c7\u03b5\u03b9 \u03bc\u03b5\u03c4\u03b5\u03b3\u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03b1\u03b8\u03b5\u03af \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b1. \n\n \u03a9\u03c2 \u03bc\u03ad\u03c1\u03bf\u03c2 \u03b1\u03c5\u03c4\u03ae\u03c2 \u03c4\u03b7\u03c2 \u03bc\u03b5\u03c4\u03b5\u03b3\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2, \u03c4\u03bf \u03b1\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 \u03c4\u03bf\u03c5 \u03b5\u03c0\u03b1\u03b3\u03b3\u03b5\u03bb\u03bc\u03b1\u03c4\u03af\u03b1 \u03ad\u03c7\u03b5\u03b9 \u03b1\u03bb\u03bb\u03ac\u03be\u03b5\u03b9 \u03b1\u03c0\u03cc \u00ab {old_device_id} \u00bb \u03c3\u03b5 \u00ab {new_device_id} \u00bb. \u0395\u03bd\u03b7\u03bc\u03b5\u03c1\u03ce\u03c3\u03c4\u03b5 \u03b1\u03c5\u03c4\u03bf\u03cd\u03c2 \u03c4\u03bf\u03c5\u03c2 \u03b1\u03c5\u03c4\u03bf\u03bc\u03b1\u03c4\u03b9\u03c3\u03bc\u03bf\u03cd\u03c2 \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf \u03bd\u03ad\u03bf \u03b1\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2: {device_automations_string} .", + "title": "\u03a4\u03bf {ip_address} \u03b1\u03c0\u03bf\u03c4\u03b5\u03bb\u03b5\u03af \u03c0\u03bb\u03ad\u03bf\u03bd \u03bc\u03ad\u03c1\u03bf\u03c2 \u03c4\u03b7\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 AirVisual Pro" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/airvisual/translations/en.json b/homeassistant/components/airvisual/translations/en.json index 1e3cb59a520..78ed599babb 100644 --- a/homeassistant/components/airvisual/translations/en.json +++ b/homeassistant/components/airvisual/translations/en.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Location is already configured or Node/Pro ID is already registered.", + "already_configured": "Location is already configured", "reauth_successful": "Re-authentication was successful" }, "error": { @@ -50,6 +50,36 @@ } } }, + "entity": { + "sensor": { + "pollutant_label": { + "state": { + "co": "Carbon Monoxide", + "n2": "Nitrogen Dioxide", + "o3": "Ozone", + "p1": "PM10", + "p2": "PM2.5", + "s2": "Sulfur Dioxide" + } + }, + "pollutant_level": { + "state": { + "good": "Good", + "hazardous": "Hazardous", + "moderate": "Moderate", + "unhealthy": "Unhealthy", + "unhealthy_sensitive": "Unhealthy for sensitive groups", + "very_unhealthy": "Very unhealthy" + } + } + } + }, + "issues": { + "airvisual_pro_migration": { + "description": "AirVisual Pro units are now their own Home Assistant integration (as opposed to be included with the original AirVisual integration that uses the AirVisual cloud API). The Pro device located at `{ip_address}` has automatically been migrated.\n\nAs part of that migration, the Pro's device ID has changed from `{old_device_id}` to `{new_device_id}`. Please update these automations to use the new device ID: {device_automations_string}.", + "title": "{ip_address} is now part of the AirVisual Pro integration" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/airvisual/translations/es.json b/homeassistant/components/airvisual/translations/es.json index 33802db6415..25c76c32565 100644 --- a/homeassistant/components/airvisual/translations/es.json +++ b/homeassistant/components/airvisual/translations/es.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "La ubicaci\u00f3n ya est\u00e1 configurada o el Nodo/Pro ID ya est\u00e1 registrado.", + "already_configured": "La ubicaci\u00f3n ya est\u00e1 configurada", "reauth_successful": "La autenticaci\u00f3n se volvi\u00f3 a realizar correctamente" }, "error": { @@ -50,6 +50,36 @@ } } }, + "entity": { + "sensor": { + "pollutant_label": { + "state": { + "co": "Mon\u00f3xido de carbono", + "n2": "Di\u00f3xido de nitr\u00f3geno", + "o3": "Ozono", + "p1": "PM10", + "p2": "PM2.5", + "s2": "Di\u00f3xido de azufre" + } + }, + "pollutant_level": { + "state": { + "good": "Bueno", + "hazardous": "Peligroso", + "moderate": "Moderado", + "unhealthy": "Poco saludable", + "unhealthy_sensitive": "Poco saludable para grupos sensibles", + "very_unhealthy": "Muy poco saludable" + } + } + } + }, + "issues": { + "airvisual_pro_migration": { + "description": "Las unidades AirVisual Pro son ahora su propia integraci\u00f3n de Home Assistant (en lugar de estar incluidas con la integraci\u00f3n original de AirVisual que usa la API en la nube de AirVisual). El dispositivo Pro ubicado en `{ip_address}` se migr\u00f3 autom\u00e1ticamente. \n\nComo parte de esa migraci\u00f3n, la identificaci\u00f3n del dispositivo Pro ha cambiado de `{old_device_id}` a `{new_device_id}`. Actualiza estas automatizaciones para usar el nuevo ID de dispositivo: {device_automations_string}.", + "title": "{ip_address} es ahora parte de la integraci\u00f3n de AirVisual Pro" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/airvisual/translations/et.json b/homeassistant/components/airvisual/translations/et.json index 45490bb63fe..c685eaf77ab 100644 --- a/homeassistant/components/airvisual/translations/et.json +++ b/homeassistant/components/airvisual/translations/et.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Asukoht on juba m\u00e4\u00e4ratud v\u00f5i Node/Pro ID on juba registreeritud.", + "already_configured": "Asukoht on juba m\u00e4\u00e4ratud", "reauth_successful": "Taastuvastamine \u00f5nnestus" }, "error": { @@ -50,6 +50,36 @@ } } }, + "entity": { + "sensor": { + "pollutant_label": { + "state": { + "co": "Vingugaas", + "n2": "L\u00e4mmastikdioksiid", + "o3": "Osoon", + "p1": "PM10 osakesed", + "p2": "PM2.5 osakesed", + "s2": "V\u00e4\u00e4veldioksiid" + } + }, + "pollutant_level": { + "state": { + "good": "Hea", + "hazardous": "Ohtlik", + "moderate": "M\u00f5\u00f5dukas", + "unhealthy": "Ebatervislik", + "unhealthy_sensitive": "Ebatervislik riskir\u00fchmale", + "very_unhealthy": "V\u00e4ga ebatervislik" + } + } + } + }, + "issues": { + "airvisual_pro_migration": { + "description": "AirVisual Pro seadmed on n\u00fc\u00fcd nende enda Home Assistanti sidumise (erinevalt sellest, et need on kaasatud algsesse AirVisuali sidumisse mis kasutab AirVisuali pilve API-d). Pro-seade, mis asub aadressil ` {ip_address} `, on automaatselt \u00fcle viidud. \n\n Osana sellest \u00fcleviimisest on pro seadme ID {old_device_id} muutunud v\u00e4\u00e4rtuseks {new_device_id} . V\u00e4rskendage neid automatiseerimisi, et kasutada uut seadme ID-d: {device_automations_string} .", + "title": "{ip_address} on n\u00fc\u00fcd osa AirVisual Pro sidumisest" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/airvisual/translations/hu.json b/homeassistant/components/airvisual/translations/hu.json index 681f32ca3bc..e8e80d1d9de 100644 --- a/homeassistant/components/airvisual/translations/hu.json +++ b/homeassistant/components/airvisual/translations/hu.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "A hely m\u00e1r konfigur\u00e1lva van vagy a Node/Pro azonos\u00edt\u00f3 m\u00e1r regisztr\u00e1lva van.", + "already_configured": "A hely m\u00e1r konfigur\u00e1lva van", "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt." }, "error": { @@ -50,6 +50,36 @@ } } }, + "entity": { + "sensor": { + "pollutant_label": { + "state": { + "co": "Sz\u00e9n-monoxid", + "n2": "Nitrog\u00e9n-dioxid", + "o3": "\u00d3zon", + "p1": "PM10", + "p2": "PM2.5", + "s2": "K\u00e9n-dioxid" + } + }, + "pollutant_level": { + "state": { + "good": "J\u00f3", + "hazardous": "Vesz\u00e9lyes", + "moderate": "M\u00e9rs\u00e9kelt", + "unhealthy": "Eg\u00e9szs\u00e9gtelen", + "unhealthy_sensitive": "Eg\u00e9szs\u00e9gtelen az \u00e9rz\u00e9keny csoportok sz\u00e1m\u00e1ra", + "very_unhealthy": "Nagyon eg\u00e9szs\u00e9gtelen" + } + } + } + }, + "issues": { + "airvisual_pro_migration": { + "description": "Az AirVisual Pro egys\u00e9gek imm\u00e1r saj\u00e1t Home Assistant integr\u00e1ci\u00f3t jelentenek (ellent\u00e9tben az AirVisual felh\u0151 API-t haszn\u00e1l\u00f3 eredeti AirVisual integr\u00e1ci\u00f3val). A(z) `{ip_address}` c\u00edmen tal\u00e1lhat\u00f3 Pro eszk\u00f6zt automatikusan \u00e1ttelep\u00edtett\u00fck. \n\n Az \u00e1ttelep\u00edt\u00e9s r\u00e9szek\u00e9nt a Pro eszk\u00f6zazonos\u00edt\u00f3ja `{old_device_id}` \u00e9rt\u00e9kr\u0151l `{new_device_id}` \u00e9rt\u00e9kre v\u00e1ltozott. K\u00e9rem, friss\u00edtse ezeket az automatiz\u00e1l\u00e1sokat az \u00faj eszk\u00f6zazonos\u00edt\u00f3 haszn\u00e1lat\u00e1hoz: {device_automations_string} .", + "title": "{ip_address} mostant\u00f3l az AirVisual Pro integr\u00e1ci\u00f3 r\u00e9sze" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/airvisual/translations/id.json b/homeassistant/components/airvisual/translations/id.json index 6fcd6eb5410..bfd1d7eea05 100644 --- a/homeassistant/components/airvisual/translations/id.json +++ b/homeassistant/components/airvisual/translations/id.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Lokasi sudah dikonfigurasi atau ID Node/Pro sudah terdaftar.", + "already_configured": "Lokasi sudah dikonfigurasi", "reauth_successful": "Autentikasi ulang berhasil" }, "error": { @@ -50,6 +50,36 @@ } } }, + "entity": { + "sensor": { + "pollutant_label": { + "state": { + "co": "Karbon monoksida", + "n2": "Nitrogen dioksida", + "o3": "Ozon", + "p1": "PM10", + "p2": "PM2.5", + "s2": "Sulfur dioksida" + } + }, + "pollutant_level": { + "state": { + "good": "Bagus", + "hazardous": "Berbahaya", + "moderate": "Sedang", + "unhealthy": "Tidak Sehat", + "unhealthy_sensitive": "Tidak sehat untuk kelompok sensitif", + "very_unhealthy": "Sangat tidak sehat" + } + } + } + }, + "issues": { + "airvisual_pro_migration": { + "description": "Unit AirVisual Pro sekarang memiliki integrasi Home Assistant sendiri (bukan lagi disertakan dengan integrasi AirVisual asli yang menggunakan API cloud AirVisual). Perangkat Pro yang terletak di `{ip_address}` secara otomatis telah dimigrasikan.\n\nSebagai bagian dari migrasi tersebut, ID perangkat Pro telah berubah dari `{old_device_id}` ke `{new_device_id}`. Perbarui otomasi berikut untuk menggunakan ID perangkat baru: {device_automations_string}.", + "title": "{ip_address} sekarang menjadi bagian dari integrasi AirVisual Pro" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/airvisual/translations/it.json b/homeassistant/components/airvisual/translations/it.json index 34605fdcaf7..4fd98e3fdbf 100644 --- a/homeassistant/components/airvisual/translations/it.json +++ b/homeassistant/components/airvisual/translations/it.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "La posizione \u00e8 gi\u00e0 configurata o Node/Pro ID sono gi\u00e0 registrati.", + "already_configured": "La posizione \u00e8 gi\u00e0 configurata", "reauth_successful": "La nuova autenticazione \u00e8 stata eseguita correttamente" }, "error": { @@ -50,6 +50,36 @@ } } }, + "entity": { + "sensor": { + "pollutant_label": { + "state": { + "co": "Monossido di carbonio", + "n2": "Biossido di azoto", + "o3": "Ozono", + "p1": "PM10", + "p2": "PM2.5", + "s2": "Biossido di zolfo" + } + }, + "pollutant_level": { + "state": { + "good": "Buono", + "hazardous": "Pericoloso", + "moderate": "Moderato", + "unhealthy": "Malsano", + "unhealthy_sensitive": "Malsano per i gruppi sensibili", + "very_unhealthy": "Molto malsano" + } + } + } + }, + "issues": { + "airvisual_pro_migration": { + "description": "Le unit\u00e0 AirVisual Pro sono ora un'integrazione Home Assistant a s\u00e9 stante (invece di essere incluse nell'integrazione AirVisual originale che utilizza l'API cloud di AirVisual). Il dispositivo Pro situato all'indirizzo `{ip_address}` \u00e8 stato migrato automaticamente.\n\nCome parte della migrazione, l'ID del dispositivo Pro \u00e8 cambiato da `{old_device_id}` a `{new_device_id}`. Aggiorna le automazioni per utilizzare il nuovo ID dispositivo: {device_automations_string}.", + "title": "{ip_address} fa ora parte dell'integrazione AirVisual Pro" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/airvisual/translations/no.json b/homeassistant/components/airvisual/translations/no.json index 89ff3fca958..92f0861a8d1 100644 --- a/homeassistant/components/airvisual/translations/no.json +++ b/homeassistant/components/airvisual/translations/no.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Plasseringen er allerede konfigurert eller Node / Pro ID er allerede registrert.", + "already_configured": "Plasseringen er allerede konfigurert", "reauth_successful": "Re-autentisering var vellykket" }, "error": { @@ -50,6 +50,36 @@ } } }, + "entity": { + "sensor": { + "pollutant_label": { + "state": { + "co": "Karbonmonoksid", + "n2": "Nitrogendioksid", + "o3": "Ozon", + "p1": "PM10", + "p2": "PM2.5", + "s2": "Svoveldioksid" + } + }, + "pollutant_level": { + "state": { + "good": "Bra", + "hazardous": "Farlig", + "moderate": "Moderat", + "unhealthy": "Usunt", + "unhealthy_sensitive": "Usunt for sensitive grupper", + "very_unhealthy": "Veldig usunt" + } + } + } + }, + "issues": { + "airvisual_pro_migration": { + "description": "AirVisual Pro-enheter er n\u00e5 deres egen Home Assistant-integrasjon (i motsetning til \u00e5 v\u00e6re inkludert i den originale AirVisual-integrasjonen som bruker AirVisual cloud API). Pro-enheten som ligger p\u00e5 ` {ip_address} ` er automatisk blitt migrert. \n\n Som en del av den migreringen har Pro-ens enhets-ID endret fra ` {old_device_id} ` til ` {new_device_id} `. Oppdater disse automatiseringene for \u00e5 bruke den nye enhets-ID-en: {device_automations_string} .", + "title": "{ip_address} er n\u00e5 en del av AirVisual Pro-integrasjonen" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/airvisual/translations/pl.json b/homeassistant/components/airvisual/translations/pl.json index 26883f514cd..6d69bc38981 100644 --- a/homeassistant/components/airvisual/translations/pl.json +++ b/homeassistant/components/airvisual/translations/pl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Lokalizacja jest ju\u017c skonfigurowana lub ID Node/Pro jest ju\u017c zarejestrowane", + "already_configured": "Lokalizacja jest ju\u017c skonfigurowana", "reauth_successful": "Ponowne uwierzytelnienie powiod\u0142o si\u0119" }, "error": { @@ -50,6 +50,36 @@ } } }, + "entity": { + "sensor": { + "pollutant_label": { + "state": { + "co": "tlenek w\u0119gla", + "n2": "dwutlenek azotu", + "o3": "ozon", + "p1": "PM10", + "p2": "PM2.5", + "s2": "dwutlenek siarki" + } + }, + "pollutant_level": { + "state": { + "good": "dobry", + "hazardous": "niebezpieczny", + "moderate": "umiarkowany", + "unhealthy": "niezdrowy", + "unhealthy_sensitive": "niezdrowy dla grup wra\u017cliwych", + "very_unhealthy": "bardzo niezdrowy" + } + } + } + }, + "issues": { + "airvisual_pro_migration": { + "description": "Urz\u0105dzenia AirVisual Pro maj\u0105 teraz w\u0142asn\u0105 integracj\u0119 z Home Assistant (w przeciwie\u0144stwie do oryginalnej integracji AirVisual, kt\u00f3ra wykorzystuje interfejs API chmury AirVisual). Urz\u0105dzenie Pro znajduj\u0105ce si\u0119 pod adresem `{ip_address}` zosta\u0142o automatycznie przeniesione.\n\nW ramach tej migracji identyfikator urz\u0105dzenia Pro zmieni\u0142 si\u0119 z `{old_device_id}` na `{new_device_id}`. Zaktualizuj te automatyzacje, aby u\u017cywa\u0142y nowego identyfikatora urz\u0105dzenia: {device_automations_string}.", + "title": "{ip_address} jest teraz cz\u0119\u015bci\u0105 integracji AirVisual Pro" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/airvisual/translations/pt-BR.json b/homeassistant/components/airvisual/translations/pt-BR.json index b1d8b655029..b1ed880bf71 100644 --- a/homeassistant/components/airvisual/translations/pt-BR.json +++ b/homeassistant/components/airvisual/translations/pt-BR.json @@ -50,6 +50,36 @@ } } }, + "entity": { + "sensor": { + "pollutant_label": { + "state": { + "co": "Mon\u00f3xido de carbono", + "n2": "Di\u00f3xido de nitrog\u00eanio", + "o3": "Oz\u00f4nio", + "p1": "PM10", + "p2": "PM2,5", + "s2": "Di\u00f3xido de enxofre" + } + }, + "pollutant_level": { + "state": { + "good": "Bom", + "hazardous": "Perigoso", + "moderate": "Moderado", + "unhealthy": "Insalubre", + "unhealthy_sensitive": "Insalubres para grupos sens\u00edveis", + "very_unhealthy": "Muito insalubre" + } + } + } + }, + "issues": { + "airvisual_pro_migration": { + "description": "As unidades AirVisual Pro agora s\u00e3o sua pr\u00f3pria integra\u00e7\u00e3o com o Home Assistant (em vez de serem inclu\u00eddas na integra\u00e7\u00e3o AirVisual original que usa a API de nuvem AirVisual). O dispositivo Pro localizado em ` {ip_address} ` foi migrado automaticamente. \n\n Como parte dessa migra\u00e7\u00e3o, o ID do dispositivo do Pro mudou de ` {old_device_id} ` para ` {new_device_id} `. Atualize essas automa\u00e7\u00f5es para usar o novo ID do dispositivo: {device_automations_string} .", + "title": "{ip_address} agora faz parte da integra\u00e7\u00e3o do AirVisual Pro" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/airvisual/translations/pt.json b/homeassistant/components/airvisual/translations/pt.json index 75869241f0d..ab54ba867ea 100644 --- a/homeassistant/components/airvisual/translations/pt.json +++ b/homeassistant/components/airvisual/translations/pt.json @@ -5,7 +5,7 @@ "reauth_successful": "Reautentica\u00e7\u00e3o bem sucedida" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "general_error": "Erro inesperado", "invalid_api_key": "Chave de API inv\u00e1lida" }, @@ -17,7 +17,7 @@ }, "node_pro": { "data": { - "ip_address": "Servidor", + "ip_address": "Endere\u00e7o", "password": "Palavra-passe" } }, diff --git a/homeassistant/components/airvisual/translations/ru.json b/homeassistant/components/airvisual/translations/ru.json index f774ec76aaf..776f15301f9 100644 --- a/homeassistant/components/airvisual/translations/ru.json +++ b/homeassistant/components/airvisual/translations/ru.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430. \u041b\u0438\u0431\u043e \u044d\u0442\u043e\u0442 Node / Pro ID \u0443\u0436\u0435 \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d.", + "already_configured": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430.", "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e." }, "error": { @@ -50,6 +50,36 @@ } } }, + "entity": { + "sensor": { + "pollutant_label": { + "state": { + "co": "\u0423\u0433\u0430\u0440\u043d\u044b\u0439 \u0433\u0430\u0437", + "n2": "\u0414\u0438\u043e\u043a\u0441\u0438\u0434 \u0430\u0437\u043e\u0442\u0430", + "o3": "\u041e\u0437\u043e\u043d", + "p1": "PM10", + "p2": "PM2.5", + "s2": "\u0414\u0438\u043e\u043a\u0441\u0438\u0434 \u0441\u0435\u0440\u044b" + } + }, + "pollutant_level": { + "state": { + "good": "\u0425\u043e\u0440\u043e\u0448\u043e", + "hazardous": "\u041e\u043f\u0430\u0441\u043d\u043e", + "moderate": "\u0421\u0440\u0435\u0434\u043d\u0435", + "unhealthy": "\u0412\u0440\u0435\u0434\u043d\u043e", + "unhealthy_sensitive": "\u0412\u0440\u0435\u0434\u043d\u043e \u0434\u043b\u044f \u0443\u044f\u0437\u0432\u0438\u043c\u044b\u0445 \u0433\u0440\u0443\u043f\u043f", + "very_unhealthy": "\u041e\u0447\u0435\u043d\u044c \u0432\u0440\u0435\u0434\u043d\u043e" + } + } + } + }, + "issues": { + "airvisual_pro_migration": { + "description": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 AirVisual Pro \u0442\u0435\u043f\u0435\u0440\u044c \u0438\u043c\u0435\u044e\u0442 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u0443\u044e \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044e \u0441 Home Assistant (\u0432 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u043e\u0442 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0439 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 AirVisual, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u043e\u0431\u043b\u0430\u0447\u043d\u044b\u0439 API AirVisual). \u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0441 \u0430\u0434\u0440\u0435\u0441\u043e\u043c `{ip_address}` \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0435\u043d\u043e \u0432 \u043d\u043e\u0432\u0443\u044e \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044e. \n\n\u0412 \u0441\u0432\u044f\u0437\u0438 \u0441 \u044d\u0442\u0438\u043c, \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0438\u0437\u043c\u0435\u043d\u0438\u043b\u0441\u044f \u0441 `{old_device_id}` \u043d\u0430 `{new_device_id}`. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u0435 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0432 \u044d\u0442\u0438\u0445 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044f\u0445: {device_automations_string}.", + "title": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e {ip_address} \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0435\u043d\u043e \u0432 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044e AirVisual Pro" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/airvisual/translations/sensor.ca.json b/homeassistant/components/airvisual/translations/sensor.ca.json index e836674af2b..bbe31f2fe72 100644 --- a/homeassistant/components/airvisual/translations/sensor.ca.json +++ b/homeassistant/components/airvisual/translations/sensor.ca.json @@ -12,7 +12,7 @@ "good": "Bo", "hazardous": "Perill\u00f3s", "moderate": "Moderat", - "unhealthy": "Poc saludable", + "unhealthy": "No saludable", "unhealthy_sensitive": "No saludable per a grups sensibles", "very_unhealthy": "Gens saludable" } diff --git a/homeassistant/components/airvisual/translations/sk.json b/homeassistant/components/airvisual/translations/sk.json index 23301cf1742..b93c5fad8f1 100644 --- a/homeassistant/components/airvisual/translations/sk.json +++ b/homeassistant/components/airvisual/translations/sk.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Umiestnenie u\u017e je nakonfigurovan\u00e9 alebo ID uzla/Pro je u\u017e zaregistrovan\u00e9.", + "already_configured": "Umiestnenie u\u017e je nakonfigurovan\u00e9", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { @@ -17,6 +17,7 @@ "latitude": "Zemepisn\u00e1 \u0161\u00edrka", "longitude": "Zemepisn\u00e1 d\u013a\u017eka" }, + "description": "Na monitorovanie zemepisnej \u0161\u00edrky/d\u013a\u017eky pou\u017eite cloudov\u00e9 API AirVisual.", "title": "Konfigur\u00e1cia geografie" }, "geography_by_name": { @@ -26,13 +27,16 @@ "country": "Krajina", "state": "stav" }, + "description": "Pou\u017eite cloudov\u00e9 API AirVisual na monitorovanie mesta/\u0161t\u00e1tu/krajiny.", "title": "Konfigur\u00e1cia geografie" }, "node_pro": { "data": { "ip_address": "Hostite\u013e", "password": "Heslo" - } + }, + "description": "Monitorujte osobn\u00fa jednotku AirVisual. Heslo je mo\u017en\u00e9 z\u00edska\u0165 z pou\u017e\u00edvate\u013esk\u00e9ho rozhrania jednotky.", + "title": "Nastavenie AirVisual Node/Pro" }, "reauth_confirm": { "data": { @@ -41,10 +45,41 @@ "title": "Op\u00e4tovn\u00e9 overenie AirVisual" }, "user": { + "description": "Vyberte, ak\u00fd typ d\u00e1t AirVisual chcete sledova\u0165.", "title": "Nakonfigurujte AirVisual" } } }, + "entity": { + "sensor": { + "pollutant_label": { + "state": { + "co": "Oxid uho\u013enat\u00fd", + "n2": "Oxid dusi\u010dit\u00fd", + "o3": "Oz\u00f3n", + "p1": "PM10", + "p2": "PM2,5", + "s2": "Oxid siri\u010dit\u00fd" + } + }, + "pollutant_level": { + "state": { + "good": "Dobr\u00e9", + "hazardous": "Nebezpe\u010dn\u00e9", + "moderate": "Mierne", + "unhealthy": "Nezdrav\u00e9", + "unhealthy_sensitive": "Nezdrav\u00e9 pre citliv\u00e9 skupiny", + "very_unhealthy": "Ve\u013emi nezdrav\u00e9" + } + } + } + }, + "issues": { + "airvisual_pro_migration": { + "description": "Jednotky AirVisual Pro s\u00fa teraz vlastnou integr\u00e1ciou dom\u00e1ceho asistenta (na rozdiel od p\u00f4vodnej integr\u00e1cie AirVisual, ktor\u00e1 vyu\u017e\u00edva cloudov\u00e9 API AirVisual). Zariadenie Pro umiestnen\u00e9 na adrese `{ip_address}` bolo automaticky migrovan\u00e9. \n\n V r\u00e1mci tejto migr\u00e1cie sa ID zariadenia profesion\u00e1la zmenilo z `{old_device_id}` na `{new_device_id}`. Aktualizujte tieto automatiz\u00e1cie, aby pou\u017e\u00edvali nov\u00e9 ID zariadenia: {device_automations_string}.", + "title": "{ip_address} je teraz s\u00fa\u010das\u0165ou integr\u00e1cie AirVisual Pro" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/airvisual/translations/zh-Hant.json b/homeassistant/components/airvisual/translations/zh-Hant.json index e8779af7f52..b3c521cf2e4 100644 --- a/homeassistant/components/airvisual/translations/zh-Hant.json +++ b/homeassistant/components/airvisual/translations/zh-Hant.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Node/Pro ID \u5df2\u8a3b\u518a\u6216\u5ea7\u6a19\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210\u3002", + "already_configured": "\u5ea7\u6a19\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", "reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f" }, "error": { @@ -50,6 +50,36 @@ } } }, + "entity": { + "sensor": { + "pollutant_label": { + "state": { + "co": "\u4e00\u6c27\u5316\u78b3", + "n2": "\u4e8c\u6c27\u5316\u6c2e", + "o3": "\u81ed\u6c27", + "p1": "PM10", + "p2": "PM2.5", + "s2": "\u4e8c\u6c27\u5316\u786b" + } + }, + "pollutant_level": { + "state": { + "good": "\u826f\u597d", + "hazardous": "\u5371\u96aa", + "moderate": "\u4e2d\u7b49", + "unhealthy": "\u4e0d\u5065\u5eb7", + "unhealthy_sensitive": "\u5c0d\u654f\u611f\u65cf\u7fa4\u4e0d\u5065\u5eb7", + "very_unhealthy": "\u975e\u5e38\u4e0d\u5065\u5eb7" + } + } + } + }, + "issues": { + "airvisual_pro_migration": { + "description": "AirVisual Pro \u8a2d\u5099\u73fe\u5728\u4f7f\u7528\u5176\u539f\u751f Home Assistant \u6574\u5408\uff08\u800c\u975e\u539f\u5148\u4f7f\u7528 AirVisual \u96f2\u7aef API \u7684 AirVisual \u6574\u5408\uff09\u3002\u4f4d\u65bc `{ip_address}` \u7684 Pro \u88dd\u7f6e\u5df2\u7d93\u81ea\u52d5\u9077\u79fb\u3002\n\n\u65bc\u9077\u79fb\u7684\u904e\u7a0b\u4e2d\u3001Pro \u7684\u88dd\u7f6e ID \u5df2\u7d93\u7531 `{old_device_id}` \u8b8a\u66f4\u70ba `{new_device_id}`\u3002\u8acb\u4f7f\u7528\u65b0\u88dd\u7f6e ID \u66f4\u65b0\u76f8\u95dc\u81ea\u52d5\u5316\uff1a{device_automations_string}\u3002", + "title": "{ip_address} \u73fe\u5728\u70ba AirVisual Pro \u6574\u5408\u90e8\u5206" + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/airvisual_pro/__init__.py b/homeassistant/components/airvisual_pro/__init__.py new file mode 100644 index 00000000000..b745dea1d94 --- /dev/null +++ b/homeassistant/components/airvisual_pro/__init__.py @@ -0,0 +1,154 @@ +"""The AirVisual Pro integration.""" +from __future__ import annotations + +import asyncio +from contextlib import suppress +from dataclasses import dataclass +from datetime import timedelta +from typing import Any + +from pyairvisual.node import ( + InvalidAuthenticationError, + NodeConnectionError, + NodeProError, + NodeSamba, +) + +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import ( + CONF_IP_ADDRESS, + CONF_PASSWORD, + EVENT_HOMEASSISTANT_STOP, + Platform, +) +from homeassistant.core import Event, HomeAssistant, callback +from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady +from homeassistant.helpers.entity import DeviceInfo, EntityDescription +from homeassistant.helpers.update_coordinator import ( + CoordinatorEntity, + DataUpdateCoordinator, + UpdateFailed, +) + +from .const import DOMAIN, LOGGER + +PLATFORMS = [Platform.SENSOR] + +UPDATE_INTERVAL = timedelta(minutes=1) + + +@dataclass +class AirVisualProData: + """Define a data class.""" + + coordinator: DataUpdateCoordinator + node: NodeSamba + + +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Set up AirVisual Pro from a config entry.""" + node = NodeSamba(entry.data[CONF_IP_ADDRESS], entry.data[CONF_PASSWORD]) + + try: + await node.async_connect() + except NodeProError as err: + raise ConfigEntryNotReady() from err + + reload_task: asyncio.Task | None = None + + async def async_get_data() -> dict[str, Any]: + """Get data from the device.""" + try: + data = await node.async_get_latest_measurements() + except InvalidAuthenticationError as err: + raise ConfigEntryAuthFailed("Invalid Samba password") from err + except NodeConnectionError as err: + nonlocal reload_task + if not reload_task: + reload_task = hass.async_create_task( + hass.config_entries.async_reload(entry.entry_id) + ) + raise UpdateFailed(f"Connection to Pro unit lost: {err}") from err + except NodeProError as err: + raise UpdateFailed(f"Error while retrieving data: {err}") from err + + return data + + coordinator = DataUpdateCoordinator( + hass, + LOGGER, + name="Node/Pro data", + update_interval=UPDATE_INTERVAL, + update_method=async_get_data, + ) + + await coordinator.async_config_entry_first_refresh() + hass.data.setdefault(DOMAIN, {})[entry.entry_id] = AirVisualProData( + coordinator=coordinator, node=node + ) + + async def async_shutdown(_: Event) -> None: + """Define an event handler to disconnect from the websocket.""" + nonlocal reload_task + if reload_task: + with suppress(asyncio.CancelledError): + reload_task.cancel() + await node.async_disconnect() + + entry.async_on_unload( + hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, async_shutdown) + ) + + await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) + + return True + + +async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Unload a config entry.""" + if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): + data = hass.data[DOMAIN].pop(entry.entry_id) + await data.node.async_disconnect() + + return unload_ok + + +class AirVisualProEntity(CoordinatorEntity): + """Define a generic AirVisual Pro entity.""" + + def __init__( + self, coordinator: DataUpdateCoordinator, description: EntityDescription + ) -> None: + """Initialize.""" + super().__init__(coordinator) + + self._attr_unique_id = f"{coordinator.data['serial_number']}_{description.key}" + self.entity_description = description + + @property + def device_info(self) -> DeviceInfo: + """Return device registry information for this entity.""" + return DeviceInfo( + identifiers={(DOMAIN, self.coordinator.data["serial_number"])}, + manufacturer="AirVisual", + model=self.coordinator.data["status"]["model"], + name=self.coordinator.data["settings"]["node_name"], + hw_version=self.coordinator.data["status"]["system_version"], + sw_version=self.coordinator.data["status"]["app_version"], + ) + + @callback + def _async_update_from_latest_data(self) -> None: + """Update the entity's underlying data.""" + raise NotImplementedError + + @callback + def _handle_coordinator_update(self) -> None: + """Respond to a DataUpdateCoordinator update.""" + self._async_update_from_latest_data() + self.async_write_ha_state() + + async def async_added_to_hass(self) -> None: + """Handle entity which will be added.""" + await super().async_added_to_hass() + self._async_update_from_latest_data() diff --git a/homeassistant/components/airvisual_pro/config_flow.py b/homeassistant/components/airvisual_pro/config_flow.py new file mode 100644 index 00000000000..23da39150c5 --- /dev/null +++ b/homeassistant/components/airvisual_pro/config_flow.py @@ -0,0 +1,148 @@ +"""Define a config flow manager for AirVisual Pro.""" +from __future__ import annotations + +from collections.abc import Mapping +from dataclasses import dataclass, field +from typing import Any + +from pyairvisual.node import ( + InvalidAuthenticationError, + NodeConnectionError, + NodeProError, + NodeSamba, +) +import voluptuous as vol + +from homeassistant import config_entries +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_IP_ADDRESS, CONF_PASSWORD +from homeassistant.data_entry_flow import FlowResult + +from .const import DOMAIN, LOGGER + +STEP_REAUTH_SCHEMA = vol.Schema( + { + vol.Required(CONF_PASSWORD): str, + } +) + +STEP_USER_SCHEMA = vol.Schema( + { + vol.Required(CONF_IP_ADDRESS): str, + vol.Required(CONF_PASSWORD): str, + } +) + + +@dataclass +class ValidationResult: + """Define a validation result.""" + + serial_number: str | None = None + errors: dict[str, Any] = field(default_factory=dict) + + +async def async_validate_credentials( + ip_address: str, password: str +) -> ValidationResult: + """Validate an IP address/password combo.""" + node = NodeSamba(ip_address, password) + errors = {} + + try: + await node.async_connect() + measurements = await node.async_get_latest_measurements() + except InvalidAuthenticationError as err: + LOGGER.error("Invalid password for Pro at IP address %s: %s", ip_address, err) + errors["base"] = "invalid_auth" + except NodeConnectionError as err: + LOGGER.error("Cannot connect to Pro at IP address %s: %s", ip_address, err) + errors["base"] = "cannot_connect" + except NodeProError as err: + LOGGER.error("Unknown Pro error while connecting to %s: %s", ip_address, err) + errors["base"] = "unknown" + except Exception as err: # pylint: disable=broad-except + LOGGER.exception("Unknown error while connecting to %s: %s", ip_address, err) + errors["base"] = "unknown" + else: + return ValidationResult(serial_number=measurements["serial_number"]) + finally: + await node.async_disconnect() + + return ValidationResult(errors=errors) + + +class AirVisualProFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): + """Handle an AirVisual Pro config flow.""" + + VERSION = 1 + + def __init__(self) -> None: + """Initialize.""" + self._reauth_entry: ConfigEntry | None = None + + async def async_step_import(self, import_config: dict[str, Any]) -> FlowResult: + """Import a config entry from configuration.yaml.""" + return await self.async_step_user(import_config) + + async def async_step_reauth(self, entry_data: Mapping[str, Any]) -> FlowResult: + """Handle configuration by re-auth.""" + self._reauth_entry = self.hass.config_entries.async_get_entry( + self.context["entry_id"] + ) + return await self.async_step_reauth_confirm() + + async def async_step_reauth_confirm( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle the re-auth step.""" + if user_input is None: + return self.async_show_form( + step_id="reauth_confirm", data_schema=STEP_REAUTH_SCHEMA + ) + + assert self._reauth_entry + + validation_result = await async_validate_credentials( + self._reauth_entry.data[CONF_IP_ADDRESS], user_input[CONF_PASSWORD] + ) + + if validation_result.errors: + return self.async_show_form( + step_id="reauth_confirm", + data_schema=STEP_REAUTH_SCHEMA, + errors=validation_result.errors, + ) + + self.hass.config_entries.async_update_entry( + self._reauth_entry, data=self._reauth_entry.data | user_input + ) + self.hass.async_create_task( + self.hass.config_entries.async_reload(self._reauth_entry.entry_id) + ) + return self.async_abort(reason="reauth_successful") + + async def async_step_user( + self, user_input: dict[str, str] | None = None + ) -> FlowResult: + """Handle the initial step.""" + if not user_input: + return self.async_show_form(step_id="user", data_schema=STEP_USER_SCHEMA) + + ip_address = user_input[CONF_IP_ADDRESS] + + validation_result = await async_validate_credentials( + ip_address, user_input[CONF_PASSWORD] + ) + + if validation_result.errors: + return self.async_show_form( + step_id="user", + data_schema=STEP_USER_SCHEMA, + errors=validation_result.errors, + ) + + await self.async_set_unique_id(validation_result.serial_number) + self._abort_if_unique_id_configured() + + return self.async_create_entry(title=ip_address, data=user_input) diff --git a/homeassistant/components/airvisual_pro/const.py b/homeassistant/components/airvisual_pro/const.py new file mode 100644 index 00000000000..83a6cc5739c --- /dev/null +++ b/homeassistant/components/airvisual_pro/const.py @@ -0,0 +1,6 @@ +"""Constants for the AirVisual Pro integration.""" +import logging + +DOMAIN = "airvisual_pro" + +LOGGER = logging.getLogger(__package__) diff --git a/homeassistant/components/airvisual_pro/diagnostics.py b/homeassistant/components/airvisual_pro/diagnostics.py new file mode 100644 index 00000000000..d6e60207214 --- /dev/null +++ b/homeassistant/components/airvisual_pro/diagnostics.py @@ -0,0 +1,36 @@ +"""Support for AirVisual Pro diagnostics.""" +from __future__ import annotations + +from typing import Any + +from homeassistant.components.diagnostics import async_redact_data +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_PASSWORD +from homeassistant.core import HomeAssistant + +from . import AirVisualProData +from .const import DOMAIN + +CONF_MAC_ADDRESS = "mac_address" +CONF_SERIAL_NUMBER = "serial_number" + +TO_REDACT = { + CONF_MAC_ADDRESS, + CONF_PASSWORD, + CONF_SERIAL_NUMBER, +} + + +async def async_get_config_entry_diagnostics( + hass: HomeAssistant, entry: ConfigEntry +) -> dict[str, Any]: + """Return diagnostics for a config entry.""" + data: AirVisualProData = hass.data[DOMAIN][entry.entry_id] + + return async_redact_data( + { + "entry": entry.as_dict(), + "data": data.coordinator.data, + }, + TO_REDACT, + ) diff --git a/homeassistant/components/airvisual_pro/manifest.json b/homeassistant/components/airvisual_pro/manifest.json new file mode 100644 index 00000000000..d90b5c6e339 --- /dev/null +++ b/homeassistant/components/airvisual_pro/manifest.json @@ -0,0 +1,11 @@ +{ + "domain": "airvisual_pro", + "name": "AirVisual Pro", + "config_flow": true, + "documentation": "https://www.home-assistant.io/integrations/airvisual_pro", + "requirements": ["pyairvisual==2022.12.1"], + "codeowners": ["@bachya"], + "iot_class": "local_polling", + "loggers": ["pyairvisual", "pysmb"], + "integration_type": "device" +} diff --git a/homeassistant/components/airvisual_pro/sensor.py b/homeassistant/components/airvisual_pro/sensor.py new file mode 100644 index 00000000000..9fb15c7ac74 --- /dev/null +++ b/homeassistant/components/airvisual_pro/sensor.py @@ -0,0 +1,163 @@ +"""Support for AirVisual Pro sensors.""" +from __future__ import annotations + +from typing import Any + +from homeassistant.components.sensor import ( + SensorDeviceClass, + SensorEntity, + SensorEntityDescription, + SensorStateClass, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import ( + CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + CONCENTRATION_PARTS_PER_MILLION, + PERCENTAGE, + UnitOfTemperature, +) +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.entity import EntityCategory +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from . import AirVisualProData, AirVisualProEntity +from .const import DOMAIN + +SENSOR_KIND_AQI = "air_quality_index" +SENSOR_KIND_BATTERY_LEVEL = "battery_level" +SENSOR_KIND_CO2 = "carbon_dioxide" +SENSOR_KIND_HUMIDITY = "humidity" +SENSOR_KIND_PM_0_1 = "particulate_matter_0_1" +SENSOR_KIND_PM_1_0 = "particulate_matter_1_0" +SENSOR_KIND_PM_2_5 = "particulate_matter_2_5" +SENSOR_KIND_SENSOR_LIFE = "sensor_life" +SENSOR_KIND_TEMPERATURE = "temperature" +SENSOR_KIND_VOC = "voc" + +SENSOR_DESCRIPTIONS = ( + SensorEntityDescription( + key=SENSOR_KIND_AQI, + name="Air quality index", + device_class=SensorDeviceClass.AQI, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + key=SENSOR_KIND_BATTERY_LEVEL, + name="Battery", + device_class=SensorDeviceClass.BATTERY, + entity_category=EntityCategory.DIAGNOSTIC, + native_unit_of_measurement=PERCENTAGE, + ), + SensorEntityDescription( + key=SENSOR_KIND_CO2, + name="C02", + device_class=SensorDeviceClass.CO2, + native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + key=SENSOR_KIND_HUMIDITY, + name="Humidity", + device_class=SensorDeviceClass.HUMIDITY, + native_unit_of_measurement=PERCENTAGE, + ), + SensorEntityDescription( + key=SENSOR_KIND_PM_0_1, + name="PM 0.1", + device_class=SensorDeviceClass.PM1, + native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + key=SENSOR_KIND_PM_1_0, + name="PM 1.0", + device_class=SensorDeviceClass.PM10, + native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + key=SENSOR_KIND_PM_2_5, + name="PM 2.5", + device_class=SensorDeviceClass.PM25, + native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + key=SENSOR_KIND_TEMPERATURE, + name="Temperature", + device_class=SensorDeviceClass.TEMPERATURE, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + key=SENSOR_KIND_VOC, + name="VOC", + device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS, + native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + state_class=SensorStateClass.MEASUREMENT, + ), +) + + +@callback +def async_get_aqi_locale(settings: dict[str, Any]) -> str: + """Return the correct AQI locale based on settings data.""" + if settings["is_aqi_usa"]: + return "aqi_us" + return "aqi_cn" + + +async def async_setup_entry( + hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback +) -> None: + """Set up AirVisual sensors based on a config entry.""" + data: AirVisualProData = hass.data[DOMAIN][entry.entry_id] + + async_add_entities( + AirVisualProSensor(data.coordinator, description) + for description in SENSOR_DESCRIPTIONS + ) + + +class AirVisualProSensor(AirVisualProEntity, SensorEntity): + """Define an AirVisual Pro sensor.""" + + _attr_has_entity_name = True + + MEASUREMENTS_KEY_TO_VALUE = { + SENSOR_KIND_CO2: "co2", + SENSOR_KIND_HUMIDITY: "humidity", + SENSOR_KIND_PM_0_1: "pm0_1", + SENSOR_KIND_PM_1_0: "pm1_0", + SENSOR_KIND_PM_2_5: "pm2_5", + SENSOR_KIND_TEMPERATURE: "temperature_C", + SENSOR_KIND_VOC: "voc", + } + + @property + def measurements(self) -> dict[str, Any]: + """Define measurements data.""" + return self.coordinator.data["measurements"] + + @property + def settings(self) -> dict[str, Any]: + """Define settings data.""" + return self.coordinator.data["settings"] + + @property + def status(self) -> dict[str, Any]: + """Define status data.""" + return self.coordinator.data["status"] + + @callback + def _async_update_from_latest_data(self) -> None: + """Update the entity from the latest data.""" + if self.entity_description.key == SENSOR_KIND_AQI: + locale = async_get_aqi_locale(self.settings) + self._attr_native_value = self.measurements[locale] + elif self.entity_description.key == SENSOR_KIND_BATTERY_LEVEL: + self._attr_native_value = self.status["battery"] + else: + self._attr_native_value = self.measurements[ + self.MEASUREMENTS_KEY_TO_VALUE[self.entity_description.key] + ] diff --git a/homeassistant/components/airvisual_pro/strings.json b/homeassistant/components/airvisual_pro/strings.json new file mode 100644 index 00000000000..f06f120885e --- /dev/null +++ b/homeassistant/components/airvisual_pro/strings.json @@ -0,0 +1,28 @@ +{ + "config": { + "step": { + "reauth_confirm": { + "description": "[%key:component::airvisual_pro::config::step::user::description%]", + "data": { + "password": "[%key:common::config_flow::data::password%]" + } + }, + "user": { + "description": "The password can be retrieved from the AirVisual Pro's UI.", + "data": { + "ip_address": "[%key:common::config_flow::data::host%]", + "password": "[%key:common::config_flow::data::password%]" + } + } + }, + "error": { + "invalid_auth": "[%key:common::config_flow::error::invalid_auth%]", + "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", + "unknown": "[%key:common::config_flow::error::unknown%]" + }, + "abort": { + "already_configured": "[%key:common::config_flow::abort::already_configured_device%]", + "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]" + } + } +} diff --git a/homeassistant/components/airvisual_pro/translations/bg.json b/homeassistant/components/airvisual_pro/translations/bg.json new file mode 100644 index 00000000000..2d3e4c717a2 --- /dev/null +++ b/homeassistant/components/airvisual_pro/translations/bg.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" + }, + "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", + "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f", + "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "\u041f\u0430\u0440\u043e\u043b\u0430" + } + }, + "user": { + "data": { + "ip_address": "\u0425\u043e\u0441\u0442", + "password": "\u041f\u0430\u0440\u043e\u043b\u0430" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airvisual_pro/translations/ca.json b/homeassistant/components/airvisual_pro/translations/ca.json new file mode 100644 index 00000000000..cdaa8c8d6d7 --- /dev/null +++ b/homeassistant/components/airvisual_pro/translations/ca.json @@ -0,0 +1,28 @@ +{ + "config": { + "abort": { + "already_configured": "El dispositiu ja est\u00e0 configurat", + "reauth_successful": "Re-autenticaci\u00f3 realitzada correctament" + }, + "error": { + "cannot_connect": "Ha fallat la connexi\u00f3", + "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida", + "unknown": "Error inesperat" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "Contrasenya" + }, + "description": "La contrasenya es pot obtenir des de la interf\u00edcie d'usuari d'AirVisual Pro." + }, + "user": { + "data": { + "ip_address": "Amfitri\u00f3", + "password": "Contrasenya" + }, + "description": "La contrasenya es pot obtenir des de la interf\u00edcie d'usuari d'AirVisual Pro." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airvisual_pro/translations/de.json b/homeassistant/components/airvisual_pro/translations/de.json new file mode 100644 index 00000000000..3164ced9078 --- /dev/null +++ b/homeassistant/components/airvisual_pro/translations/de.json @@ -0,0 +1,28 @@ +{ + "config": { + "abort": { + "already_configured": "Ger\u00e4t ist bereits konfiguriert", + "reauth_successful": "Die erneute Authentifizierung war erfolgreich" + }, + "error": { + "cannot_connect": "Verbindung fehlgeschlagen", + "invalid_auth": "Ung\u00fcltige Authentifizierung", + "unknown": "Unerwarteter Fehler" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "Passwort" + }, + "description": "Das Passwort kann \u00fcber die Benutzeroberfl\u00e4che von AirVisual Pro abgefragt werden." + }, + "user": { + "data": { + "ip_address": "Host", + "password": "Passwort" + }, + "description": "Das Passwort kann \u00fcber die Benutzeroberfl\u00e4che von AirVisual Pro abgefragt werden." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airvisual_pro/translations/el.json b/homeassistant/components/airvisual_pro/translations/el.json new file mode 100644 index 00000000000..3fccd251a33 --- /dev/null +++ b/homeassistant/components/airvisual_pro/translations/el.json @@ -0,0 +1,28 @@ +{ + "config": { + "abort": { + "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", + "reauth_successful": "\u039f \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ae\u03c4\u03b1\u03bd \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2" + }, + "error": { + "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", + "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", + "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2" + }, + "description": "\u039f \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2 \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03b1\u03bd\u03b1\u03ba\u03c4\u03b7\u03b8\u03b5\u03af \u03b1\u03c0\u03cc \u03c4\u03b7 \u03b4\u03b9\u03b5\u03c0\u03b1\u03c6\u03ae \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7 \u03c4\u03bf\u03c5 AirVisual Pro." + }, + "user": { + "data": { + "ip_address": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2", + "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2" + }, + "description": "\u039f \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2 \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03b1\u03bd\u03b1\u03ba\u03c4\u03b7\u03b8\u03b5\u03af \u03b1\u03c0\u03cc \u03c4\u03b7 \u03b4\u03b9\u03b5\u03c0\u03b1\u03c6\u03ae \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7 \u03c4\u03bf\u03c5 AirVisual Pro." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airvisual_pro/translations/en.json b/homeassistant/components/airvisual_pro/translations/en.json new file mode 100644 index 00000000000..767fbf40752 --- /dev/null +++ b/homeassistant/components/airvisual_pro/translations/en.json @@ -0,0 +1,28 @@ +{ + "config": { + "abort": { + "already_configured": "Device is already configured", + "reauth_successful": "Re-authentication was successful" + }, + "error": { + "cannot_connect": "Failed to connect", + "invalid_auth": "Invalid authentication", + "unknown": "Unexpected error" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "Password" + }, + "description": "The password can be retrieved from the AirVisual Pro's UI." + }, + "user": { + "data": { + "ip_address": "Host", + "password": "Password" + }, + "description": "The password can be retrieved from the AirVisual Pro's UI." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airvisual_pro/translations/es.json b/homeassistant/components/airvisual_pro/translations/es.json new file mode 100644 index 00000000000..a2562dbd51a --- /dev/null +++ b/homeassistant/components/airvisual_pro/translations/es.json @@ -0,0 +1,28 @@ +{ + "config": { + "abort": { + "already_configured": "El dispositivo ya est\u00e1 configurado", + "reauth_successful": "La autenticaci\u00f3n se volvi\u00f3 a realizar correctamente" + }, + "error": { + "cannot_connect": "No se pudo conectar", + "invalid_auth": "Autenticaci\u00f3n no v\u00e1lida", + "unknown": "Error inesperado" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "Contrase\u00f1a" + }, + "description": "La contrase\u00f1a se puede recuperar desde la IU de AirVisual Pro." + }, + "user": { + "data": { + "ip_address": "Host", + "password": "Contrase\u00f1a" + }, + "description": "La contrase\u00f1a se puede recuperar desde la IU de AirVisual Pro." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airvisual_pro/translations/et.json b/homeassistant/components/airvisual_pro/translations/et.json new file mode 100644 index 00000000000..61a7f6d80b9 --- /dev/null +++ b/homeassistant/components/airvisual_pro/translations/et.json @@ -0,0 +1,28 @@ +{ + "config": { + "abort": { + "already_configured": "Seade on juba h\u00e4\u00e4lestatud", + "reauth_successful": "Taastuvastamine \u00f5nnestus" + }, + "error": { + "cannot_connect": "\u00dchendamine nurjus", + "invalid_auth": "Tuvastamine nurjus", + "unknown": "Ootamatu t\u00f5rge" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "Salas\u00f5na" + }, + "description": "Parooli saab hankida AirVisual Pro kasutajaliidest." + }, + "user": { + "data": { + "ip_address": "Host", + "password": "Salas\u00f5na" + }, + "description": "Parooli saab hankida AirVisual Pro kasutajaliidest." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airvisual_pro/translations/he.json b/homeassistant/components/airvisual_pro/translations/he.json new file mode 100644 index 00000000000..e1dc5cbe779 --- /dev/null +++ b/homeassistant/components/airvisual_pro/translations/he.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4" + }, + "error": { + "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", + "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" + }, + "step": { + "user": { + "data": { + "ip_address": "\u05de\u05d0\u05e8\u05d7", + "password": "\u05e1\u05d9\u05e1\u05de\u05d4" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airvisual_pro/translations/hu.json b/homeassistant/components/airvisual_pro/translations/hu.json new file mode 100644 index 00000000000..ee0bf57b9a0 --- /dev/null +++ b/homeassistant/components/airvisual_pro/translations/hu.json @@ -0,0 +1,28 @@ +{ + "config": { + "abort": { + "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", + "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt." + }, + "error": { + "cannot_connect": "Sikertelen csatlakoz\u00e1s", + "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s", + "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "Jelsz\u00f3" + }, + "description": "A jelsz\u00f3 az AirVisual Pro felhaszn\u00e1l\u00f3i fel\u00fclet\u00e9n tal\u00e1lhat\u00f3 meg." + }, + "user": { + "data": { + "ip_address": "C\u00edm", + "password": "Jelsz\u00f3" + }, + "description": "A jelsz\u00f3 az AirVisual Pro felhaszn\u00e1l\u00f3i fel\u00fclet\u00e9n tal\u00e1lhat\u00f3 meg." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airvisual_pro/translations/id.json b/homeassistant/components/airvisual_pro/translations/id.json new file mode 100644 index 00000000000..d1545206477 --- /dev/null +++ b/homeassistant/components/airvisual_pro/translations/id.json @@ -0,0 +1,28 @@ +{ + "config": { + "abort": { + "already_configured": "Perangkat sudah dikonfigurasi", + "reauth_successful": "Autentikasi ulang berhasil" + }, + "error": { + "cannot_connect": "Gagal terhubung", + "invalid_auth": "Autentikasi tidak valid", + "unknown": "Kesalahan yang tidak diharapkan" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "Kata Sandi" + }, + "description": "Kata sandi dapat diambil dari antarmuka AirVisual Pro." + }, + "user": { + "data": { + "ip_address": "Host", + "password": "Kata Sandi" + }, + "description": "Kata sandi dapat diambil dari antarmuka AirVisual Pro." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airvisual_pro/translations/it.json b/homeassistant/components/airvisual_pro/translations/it.json new file mode 100644 index 00000000000..6d72328cffd --- /dev/null +++ b/homeassistant/components/airvisual_pro/translations/it.json @@ -0,0 +1,28 @@ +{ + "config": { + "abort": { + "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato", + "reauth_successful": "La nuova autenticazione \u00e8 stata eseguita correttamente" + }, + "error": { + "cannot_connect": "Impossibile connettersi", + "invalid_auth": "Autenticazione non valida", + "unknown": "Errore imprevisto" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "Password" + }, + "description": "La password pu\u00f2 essere recuperata dall'interfaccia utente di AirVisual Pro." + }, + "user": { + "data": { + "ip_address": "Host", + "password": "Password" + }, + "description": "La password pu\u00f2 essere recuperata dall'interfaccia utente di AirVisual Pro." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airvisual_pro/translations/no.json b/homeassistant/components/airvisual_pro/translations/no.json new file mode 100644 index 00000000000..a1adee35b4e --- /dev/null +++ b/homeassistant/components/airvisual_pro/translations/no.json @@ -0,0 +1,28 @@ +{ + "config": { + "abort": { + "already_configured": "Enheten er allerede konfigurert", + "reauth_successful": "Re-autentisering var vellykket" + }, + "error": { + "cannot_connect": "Tilkobling mislyktes", + "invalid_auth": "Ugyldig godkjenning", + "unknown": "Uventet feil" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "Passord" + }, + "description": "Passordet kan hentes fra AirVisual Pros brukergrensesnitt." + }, + "user": { + "data": { + "ip_address": "Vert", + "password": "Passord" + }, + "description": "Passordet kan hentes fra AirVisual Pros brukergrensesnitt." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airvisual_pro/translations/pl.json b/homeassistant/components/airvisual_pro/translations/pl.json new file mode 100644 index 00000000000..ff0bb4e0a2d --- /dev/null +++ b/homeassistant/components/airvisual_pro/translations/pl.json @@ -0,0 +1,28 @@ +{ + "config": { + "abort": { + "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane", + "reauth_successful": "Ponowne uwierzytelnienie powiod\u0142o si\u0119" + }, + "error": { + "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", + "invalid_auth": "Niepoprawne uwierzytelnienie", + "unknown": "Nieoczekiwany b\u0142\u0105d" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "Has\u0142o" + }, + "description": "Has\u0142o mo\u017cna odzyska\u0107 z interfejsu u\u017cytkownika AirVisual Pro." + }, + "user": { + "data": { + "ip_address": "Nazwa hosta lub adres IP", + "password": "Has\u0142o" + }, + "description": "Has\u0142o mo\u017cna odzyska\u0107 z interfejsu u\u017cytkownika AirVisual Pro." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airvisual_pro/translations/pt-BR.json b/homeassistant/components/airvisual_pro/translations/pt-BR.json new file mode 100644 index 00000000000..9e5b04ace73 --- /dev/null +++ b/homeassistant/components/airvisual_pro/translations/pt-BR.json @@ -0,0 +1,28 @@ +{ + "config": { + "abort": { + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", + "reauth_successful": "A reautentica\u00e7\u00e3o foi bem-sucedida" + }, + "error": { + "cannot_connect": "Falhou ao conectar", + "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", + "unknown": "Erro inesperado" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "Senha" + }, + "description": "A senha pode ser recuperada da IU do AirVisual Pro." + }, + "user": { + "data": { + "ip_address": "Host", + "password": "Senha" + }, + "description": "A senha pode ser recuperada da IU do AirVisual Pro." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airvisual_pro/translations/ru.json b/homeassistant/components/airvisual_pro/translations/ru.json new file mode 100644 index 00000000000..65d7834fdbe --- /dev/null +++ b/homeassistant/components/airvisual_pro/translations/ru.json @@ -0,0 +1,28 @@ +{ + "config": { + "abort": { + "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant.", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e." + }, + "error": { + "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", + "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", + "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." + }, + "step": { + "reauth_confirm": { + "data": { + "password": "\u041f\u0430\u0440\u043e\u043b\u044c" + }, + "description": "\u041f\u0430\u0440\u043e\u043b\u044c \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0438\u0437 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 AirVisual Pro." + }, + "user": { + "data": { + "ip_address": "\u0425\u043e\u0441\u0442", + "password": "\u041f\u0430\u0440\u043e\u043b\u044c" + }, + "description": "\u041f\u0430\u0440\u043e\u043b\u044c \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0438\u0437 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 AirVisual Pro." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airvisual_pro/translations/sk.json b/homeassistant/components/airvisual_pro/translations/sk.json new file mode 100644 index 00000000000..9816d44800d --- /dev/null +++ b/homeassistant/components/airvisual_pro/translations/sk.json @@ -0,0 +1,28 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "Heslo" + }, + "description": "Heslo je mo\u017en\u00e9 z\u00edska\u0165 z pou\u017e\u00edvate\u013esk\u00e9ho rozhrania AirVisual Pro." + }, + "user": { + "data": { + "ip_address": "Hostite\u013e", + "password": "Heslo" + }, + "description": "Heslo je mo\u017en\u00e9 z\u00edska\u0165 z pou\u017e\u00edvate\u013esk\u00e9ho rozhrania AirVisual Pro." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airvisual_pro/translations/sv.json b/homeassistant/components/airvisual_pro/translations/sv.json new file mode 100644 index 00000000000..3693d7bff1e --- /dev/null +++ b/homeassistant/components/airvisual_pro/translations/sv.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "reauth_confirm": { + "data": { + "password": "L\u00f6senord" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airvisual_pro/translations/zh-Hant.json b/homeassistant/components/airvisual_pro/translations/zh-Hant.json new file mode 100644 index 00000000000..ae17c15dbe2 --- /dev/null +++ b/homeassistant/components/airvisual_pro/translations/zh-Hant.json @@ -0,0 +1,28 @@ +{ + "config": { + "abort": { + "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", + "reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f" + }, + "error": { + "cannot_connect": "\u9023\u7dda\u5931\u6557", + "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548", + "unknown": "\u672a\u9810\u671f\u932f\u8aa4" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "\u5bc6\u78bc" + }, + "description": "\u5bc6\u78bc\u53ef\u4ee5\u900f\u904e AirVisual Pro UI \u7372\u5f97\u3002" + }, + "user": { + "data": { + "ip_address": "\u4e3b\u6a5f\u7aef", + "password": "\u5bc6\u78bc" + }, + "description": "\u5bc6\u78bc\u53ef\u4ee5\u900f\u904e AirVisual Pro UI \u7372\u5f97\u3002" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airzone/const.py b/homeassistant/components/airzone/const.py index 345a07692c5..2263d5ab4a3 100644 --- a/homeassistant/components/airzone/const.py +++ b/homeassistant/components/airzone/const.py @@ -4,7 +4,7 @@ from typing import Final from aioairzone.common import TemperatureUnit -from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT +from homeassistant.const import UnitOfTemperature DOMAIN: Final = "airzone" MANUFACTURER: Final = "Airzone" @@ -13,6 +13,6 @@ AIOAIRZONE_DEVICE_TIMEOUT_SEC: Final = 10 API_TEMPERATURE_STEP: Final = 0.5 TEMP_UNIT_LIB_TO_HASS: Final[dict[TemperatureUnit, str]] = { - TemperatureUnit.CELSIUS: TEMP_CELSIUS, - TemperatureUnit.FAHRENHEIT: TEMP_FAHRENHEIT, + TemperatureUnit.CELSIUS: UnitOfTemperature.CELSIUS, + TemperatureUnit.FAHRENHEIT: UnitOfTemperature.FAHRENHEIT, } diff --git a/homeassistant/components/airzone/manifest.json b/homeassistant/components/airzone/manifest.json index 142ace5e70b..5cf3ab0689d 100644 --- a/homeassistant/components/airzone/manifest.json +++ b/homeassistant/components/airzone/manifest.json @@ -3,7 +3,7 @@ "name": "Airzone", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/airzone", - "requirements": ["aioairzone==0.5.1"], + "requirements": ["aioairzone==0.5.2"], "codeowners": ["@Noltari"], "iot_class": "local_polling", "loggers": ["aioairzone"], diff --git a/homeassistant/components/airzone/sensor.py b/homeassistant/components/airzone/sensor.py index 86d88aa5a55..ed07b5a0764 100644 --- a/homeassistant/components/airzone/sensor.py +++ b/homeassistant/components/airzone/sensor.py @@ -23,7 +23,7 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( PERCENTAGE, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import EntityCategory @@ -50,7 +50,7 @@ ZONE_SENSOR_TYPES: Final[tuple[SensorEntityDescription, ...]] = ( device_class=SensorDeviceClass.TEMPERATURE, key=AZD_TEMP, name="Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( diff --git a/homeassistant/components/airzone/translations/ca.json b/homeassistant/components/airzone/translations/ca.json index 4f37cf005f3..98e2d0ba800 100644 --- a/homeassistant/components/airzone/translations/ca.json +++ b/homeassistant/components/airzone/translations/ca.json @@ -20,8 +20,7 @@ "host": "Amfitri\u00f3", "id": "ID de sistema", "port": "Port" - }, - "description": "Configura la integraci\u00f3 Airzone." + } } } } diff --git a/homeassistant/components/airzone/translations/de.json b/homeassistant/components/airzone/translations/de.json index 38b56e70308..0c0d80667d8 100644 --- a/homeassistant/components/airzone/translations/de.json +++ b/homeassistant/components/airzone/translations/de.json @@ -20,8 +20,7 @@ "host": "Host", "id": "System-ID", "port": "Port" - }, - "description": "Richte die Airzone Integration ein." + } } } } diff --git a/homeassistant/components/airzone/translations/el.json b/homeassistant/components/airzone/translations/el.json index 2da19364c28..1ff55657efa 100644 --- a/homeassistant/components/airzone/translations/el.json +++ b/homeassistant/components/airzone/translations/el.json @@ -20,8 +20,7 @@ "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2", "id": "\u0391\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c3\u03c5\u03c3\u03c4\u03ae\u03bc\u03b1\u03c4\u03bf\u03c2", "port": "\u0398\u03cd\u03c1\u03b1" - }, - "description": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 Airzone." + } } } } diff --git a/homeassistant/components/airzone/translations/en.json b/homeassistant/components/airzone/translations/en.json index 638f542e1ca..691cfe40445 100644 --- a/homeassistant/components/airzone/translations/en.json +++ b/homeassistant/components/airzone/translations/en.json @@ -20,8 +20,7 @@ "host": "Host", "id": "System ID", "port": "Port" - }, - "description": "Set up Airzone integration." + } } } } diff --git a/homeassistant/components/airzone/translations/es-419.json b/homeassistant/components/airzone/translations/es-419.json index 194005f53ce..272b0f59eba 100644 --- a/homeassistant/components/airzone/translations/es-419.json +++ b/homeassistant/components/airzone/translations/es-419.json @@ -2,11 +2,6 @@ "config": { "error": { "invalid_system_id": "ID del sistema Airzone no v\u00e1lido" - }, - "step": { - "user": { - "description": "Configurar la integraci\u00f3n de Airzone." - } } } } \ No newline at end of file diff --git a/homeassistant/components/airzone/translations/es.json b/homeassistant/components/airzone/translations/es.json index 858cb488344..778ae632c18 100644 --- a/homeassistant/components/airzone/translations/es.json +++ b/homeassistant/components/airzone/translations/es.json @@ -20,8 +20,7 @@ "host": "Host", "id": "ID del sistema", "port": "Puerto" - }, - "description": "Configura la integraci\u00f3n Airzone." + } } } } diff --git a/homeassistant/components/airzone/translations/et.json b/homeassistant/components/airzone/translations/et.json index c209a688c2d..5f50acce85e 100644 --- a/homeassistant/components/airzone/translations/et.json +++ b/homeassistant/components/airzone/translations/et.json @@ -20,8 +20,7 @@ "host": "Host", "id": "S\u00fcsteemi ID", "port": "Port" - }, - "description": "Seadista Airzone'i sidumine" + } } } } diff --git a/homeassistant/components/airzone/translations/fr.json b/homeassistant/components/airzone/translations/fr.json index f525617b2ef..87a55e7b836 100644 --- a/homeassistant/components/airzone/translations/fr.json +++ b/homeassistant/components/airzone/translations/fr.json @@ -20,8 +20,7 @@ "host": "H\u00f4te", "id": "ID syst\u00e8me", "port": "Port" - }, - "description": "Configurer l'int\u00e9gration Airzone." + } } } } diff --git a/homeassistant/components/airzone/translations/hu.json b/homeassistant/components/airzone/translations/hu.json index 9a835f88dfc..cabcde2412c 100644 --- a/homeassistant/components/airzone/translations/hu.json +++ b/homeassistant/components/airzone/translations/hu.json @@ -8,12 +8,19 @@ "invalid_system_id": "\u00c9rv\u00e9nytelen Airzone rendszerazonos\u00edt\u00f3" }, "step": { + "discovered_connection": { + "data": { + "host": "C\u00edm", + "id": "Rendszerazonos\u00edt\u00f3", + "port": "Port" + } + }, "user": { "data": { "host": "C\u00edm", + "id": "Rendszerazonos\u00edt\u00f3", "port": "Port" - }, - "description": "Airzone integr\u00e1ci\u00f3 be\u00e1ll\u00edt\u00e1sa." + } } } } diff --git a/homeassistant/components/airzone/translations/id.json b/homeassistant/components/airzone/translations/id.json index f80012a3ad4..09d640210cf 100644 --- a/homeassistant/components/airzone/translations/id.json +++ b/homeassistant/components/airzone/translations/id.json @@ -20,8 +20,7 @@ "host": "Host", "id": "ID Sistem", "port": "Port" - }, - "description": "Siapkan integrasi Airzone" + } } } } diff --git a/homeassistant/components/airzone/translations/it.json b/homeassistant/components/airzone/translations/it.json index 10297d1e288..2df2e2aabeb 100644 --- a/homeassistant/components/airzone/translations/it.json +++ b/homeassistant/components/airzone/translations/it.json @@ -20,8 +20,7 @@ "host": "Host", "id": "ID di sistema", "port": "Porta" - }, - "description": "Imposta l'integrazione Airzone." + } } } } diff --git a/homeassistant/components/airzone/translations/ja.json b/homeassistant/components/airzone/translations/ja.json index 68bee99ba29..27a1ff12a7b 100644 --- a/homeassistant/components/airzone/translations/ja.json +++ b/homeassistant/components/airzone/translations/ja.json @@ -12,8 +12,7 @@ "data": { "host": "\u30db\u30b9\u30c8", "port": "\u30dd\u30fc\u30c8" - }, - "description": "Airzone\u7d71\u5408\u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7" + } } } } diff --git a/homeassistant/components/airzone/translations/ko.json b/homeassistant/components/airzone/translations/ko.json new file mode 100644 index 00000000000..3296e907b9b --- /dev/null +++ b/homeassistant/components/airzone/translations/ko.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "discovered_connection": { + "data": { + "host": "\ud638\uc2a4\ud2b8", + "id": "\uc2dc\uc2a4\ud15c ID", + "port": "\ud3ec\ud2b8" + } + }, + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8", + "id": "\uc2dc\uc2a4\ud15c ID", + "port": "\ud3ec\ud2b8" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airzone/translations/nl.json b/homeassistant/components/airzone/translations/nl.json index b87f0f3cab5..1c8c936c2c9 100644 --- a/homeassistant/components/airzone/translations/nl.json +++ b/homeassistant/components/airzone/translations/nl.json @@ -19,8 +19,7 @@ "host": "Host", "id": "Systeem ID", "port": "Poort" - }, - "description": "Airzone integratie instellen." + } } } } diff --git a/homeassistant/components/airzone/translations/no.json b/homeassistant/components/airzone/translations/no.json index 2ab24253236..77dc7b28b07 100644 --- a/homeassistant/components/airzone/translations/no.json +++ b/homeassistant/components/airzone/translations/no.json @@ -20,8 +20,7 @@ "host": "Vert", "id": "System-ID", "port": "Port" - }, - "description": "Sett opp Airzone-integrasjon." + } } } } diff --git a/homeassistant/components/airzone/translations/pl.json b/homeassistant/components/airzone/translations/pl.json index 42efddb4310..c685c67e5f9 100644 --- a/homeassistant/components/airzone/translations/pl.json +++ b/homeassistant/components/airzone/translations/pl.json @@ -20,8 +20,7 @@ "host": "Nazwa hosta lub adres IP", "id": "Identyfikator systemu", "port": "Port" - }, - "description": "Skonfiguruj integracj\u0119 Airzone." + } } } } diff --git a/homeassistant/components/airzone/translations/pt-BR.json b/homeassistant/components/airzone/translations/pt-BR.json index 10a0e4555b3..39891891dde 100644 --- a/homeassistant/components/airzone/translations/pt-BR.json +++ b/homeassistant/components/airzone/translations/pt-BR.json @@ -10,7 +10,7 @@ "step": { "discovered_connection": { "data": { - "host": "Host", + "host": "Nome do host", "id": "ID do sistema", "port": "Porta" } @@ -20,8 +20,7 @@ "host": "Nome do host", "id": "ID do sistema", "port": "Porta" - }, - "description": "Configure a integra\u00e7\u00e3o Airzone." + } } } } diff --git a/homeassistant/components/airzone/translations/pt.json b/homeassistant/components/airzone/translations/pt.json index fa5aa3de317..7970a84f967 100644 --- a/homeassistant/components/airzone/translations/pt.json +++ b/homeassistant/components/airzone/translations/pt.json @@ -4,12 +4,18 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { + "discovered_connection": { + "data": { + "host": "Endere\u00e7o", + "port": "Porta" + } + }, "user": { "data": { - "host": "Servidor", + "host": "Endere\u00e7o", "port": "Porta" } } diff --git a/homeassistant/components/airzone/translations/ru.json b/homeassistant/components/airzone/translations/ru.json index 6ee7cb98950..af0099ba035 100644 --- a/homeassistant/components/airzone/translations/ru.json +++ b/homeassistant/components/airzone/translations/ru.json @@ -20,8 +20,7 @@ "host": "\u0425\u043e\u0441\u0442", "id": "ID \u0441\u0438\u0441\u0442\u0435\u043c\u044b", "port": "\u041f\u043e\u0440\u0442" - }, - "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 Home Assistant \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 Airzone." + } } } } diff --git a/homeassistant/components/airzone/translations/sk.json b/homeassistant/components/airzone/translations/sk.json index 2566a9e353e..917ef19a157 100644 --- a/homeassistant/components/airzone/translations/sk.json +++ b/homeassistant/components/airzone/translations/sk.json @@ -20,8 +20,7 @@ "host": "Hostite\u013e", "id": "ID syst\u00e9mu", "port": "Port" - }, - "description": "Nastavte integr\u00e1ciu Airzone." + } } } } diff --git a/homeassistant/components/airzone/translations/sv.json b/homeassistant/components/airzone/translations/sv.json index 1fe6a415693..f82da6f72f8 100644 --- a/homeassistant/components/airzone/translations/sv.json +++ b/homeassistant/components/airzone/translations/sv.json @@ -12,8 +12,7 @@ "data": { "host": "V\u00e4rd", "port": "Port" - }, - "description": "St\u00e4ll in Airzone-integration." + } } } } diff --git a/homeassistant/components/airzone/translations/tr.json b/homeassistant/components/airzone/translations/tr.json index c911478ec32..a5798334a23 100644 --- a/homeassistant/components/airzone/translations/tr.json +++ b/homeassistant/components/airzone/translations/tr.json @@ -12,8 +12,7 @@ "data": { "host": "Sunucu", "port": "Port" - }, - "description": "Airzone entegrasyonunu ayarlay\u0131n." + } } } } diff --git a/homeassistant/components/airzone/translations/zh-Hant.json b/homeassistant/components/airzone/translations/zh-Hant.json index b53c19ad364..a62387de6d8 100644 --- a/homeassistant/components/airzone/translations/zh-Hant.json +++ b/homeassistant/components/airzone/translations/zh-Hant.json @@ -20,8 +20,7 @@ "host": "\u4e3b\u6a5f\u7aef", "id": "\u7cfb\u7d71 ID", "port": "\u901a\u8a0a\u57e0" - }, - "description": "\u8a2d\u5b9a Airzone \u6574\u5408\u3002" + } } } } diff --git a/homeassistant/components/aladdin_connect/cover.py b/homeassistant/components/aladdin_connect/cover.py index 18dcafeb71c..532261339b0 100644 --- a/homeassistant/components/aladdin_connect/cover.py +++ b/homeassistant/components/aladdin_connect/cover.py @@ -47,8 +47,8 @@ async def async_setup_platform( ) -> None: """Set up Aladdin Connect devices yaml depreciated.""" _LOGGER.warning( - "Configuring Aladdin Connect through yaml is deprecated" - "Please remove it from your configuration as it has already been imported to a config entry" + "Configuring Aladdin Connect through yaml is deprecated. Please remove it from" + " your configuration as it has already been imported to a config entry" ) await hass.async_create_task( hass.config_entries.flow.async_init( diff --git a/homeassistant/components/alarm_control_panel/translations/de.json b/homeassistant/components/alarm_control_panel/translations/de.json index 379ddfc041d..6f8f8d7a835 100644 --- a/homeassistant/components/alarm_control_panel/translations/de.json +++ b/homeassistant/components/alarm_control_panel/translations/de.json @@ -5,7 +5,7 @@ "arm_home": "Aktiviere {entity_name} Zuhause", "arm_night": "Aktiviere {entity_name} Nacht-Modus", "arm_vacation": "Aktiviere {entity_name} Urlaub", - "disarm": "Deaktivere {entity_name}", + "disarm": "Deaktiviere {entity_name}", "trigger": "Ausl\u00f6ser {entity_name}" }, "condition_type": { diff --git a/homeassistant/components/alarm_control_panel/translations/it.json b/homeassistant/components/alarm_control_panel/translations/it.json index ac07c28da62..e16fd250961 100644 --- a/homeassistant/components/alarm_control_panel/translations/it.json +++ b/homeassistant/components/alarm_control_panel/translations/it.json @@ -29,7 +29,7 @@ "_": { "armed": "Attivo", "armed_away": "Attivo fuori casa", - "armed_custom_bypass": "Attivo con bypass personalizzato", + "armed_custom_bypass": "Attivo con esclusione personalizzata", "armed_home": "Attivo in casa", "armed_night": "Attivo Notte", "armed_vacation": "Attivo in vacanza", diff --git a/homeassistant/components/alarm_control_panel/translations/sk.json b/homeassistant/components/alarm_control_panel/translations/sk.json index 844fd5c2ec1..533f7069cad 100644 --- a/homeassistant/components/alarm_control_panel/translations/sk.json +++ b/homeassistant/components/alarm_control_panel/translations/sk.json @@ -1,9 +1,27 @@ { "device_automation": { "action_type": { + "arm_away": "Aktivova\u0165 {entity_name} v re\u017eime nepr\u00edtomnos\u0165", + "arm_home": "Aktivova\u0165 {entity_name} v re\u017eime domov", + "arm_night": "Aktivova\u0165 {entity_name} v no\u010dnom re\u017eime", + "arm_vacation": "Aktivova\u0165 {entity_name} v re\u017eime dovolenka", + "disarm": "Deaktivova\u0165 {entity_name}", "trigger": "Sp\u00fa\u0161\u0165a\u010d {entity_name}" }, + "condition_type": { + "is_armed_away": "{entity_name} je v re\u017eime nepr\u00edtomnosti", + "is_armed_home": "{entity_name} je v re\u017eime domov", + "is_armed_night": "{entity_name} je v no\u010dnom re\u017eime", + "is_armed_vacation": "{entity_name} je v re\u017eime dovolenka", + "is_disarmed": "{entity_name} nie je zabezpe\u010den\u00e9", + "is_triggered": "{entity_name} je spusten\u00e9" + }, "trigger_type": { + "armed_away": "{entity_name} v re\u017eime nepr\u00edtomnos\u0165", + "armed_home": "{entity_name} v re\u017eime domov", + "armed_night": "{entity_name} v no\u010dnom re\u017eime", + "armed_vacation": "{entity_name} v re\u017eime dovolenka", + "disarmed": "{entity_name} nezabezpe\u010den\u00e9", "triggered": "{entity_name} spusten\u00fd" } }, @@ -14,6 +32,7 @@ "armed_custom_bypass": "Zak\u00f3dovan\u00e9 prisp\u00f4soben\u00e9 vyl\u00fa\u010denie", "armed_home": "Akt\u00edvny doma", "armed_night": "Akt\u00edvny v noci", + "armed_vacation": "V re\u017eime dovolenka", "arming": "Aktivuje sa", "disarmed": "Neakt\u00edvny", "disarming": "Deaktivuje sa", diff --git a/homeassistant/components/alarmdecoder/translations/de.json b/homeassistant/components/alarmdecoder/translations/de.json index 73c1c82b6c1..aded846fd3b 100644 --- a/homeassistant/components/alarmdecoder/translations/de.json +++ b/homeassistant/components/alarmdecoder/translations/de.json @@ -31,7 +31,7 @@ "error": { "int": "Das Feld unten muss eine ganze Zahl sein.", "loop_range": "RF Loop muss eine ganze Zahl zwischen 1 und 4 sein.", - "loop_rfid": "RF Loop kann nicht ohne RF Serial verwendet werden.", + "loop_rfid": "RF Loop kann nicht ohne RF Seriell verwendet werden.", "relay_inclusive": "Relaisadresse und Relaiskanal sind abh\u00e4ngig voneinander und m\u00fcssen zusammen aufgenommen werden." }, "step": { @@ -56,7 +56,7 @@ "zone_name": "Zonenname", "zone_relayaddr": "Relais-Adresse", "zone_relaychan": "Relaiskanal", - "zone_rfid": "RF Serial", + "zone_rfid": "RF Seriell", "zone_type": "Zonentyp" }, "description": "Gib Details f\u00fcr Zone {zone_number} ein. Um Zone {zone_number} zu l\u00f6schen, lass den Zonennamen leer.", diff --git a/homeassistant/components/alarmdecoder/translations/it.json b/homeassistant/components/alarmdecoder/translations/it.json index 83de29ca190..60aa47406ff 100644 --- a/homeassistant/components/alarmdecoder/translations/it.json +++ b/homeassistant/components/alarmdecoder/translations/it.json @@ -38,7 +38,7 @@ "arm_settings": { "data": { "alt_night_mode": "Modalit\u00e0 notturna alternativa", - "auto_bypass": "Bypass automatico all'attivazione", + "auto_bypass": "Esclusione automatica all'attivazione", "code_arm_required": "Codice richiesto per l'attivazione" }, "title": "Configura AlarmDecoder" diff --git a/homeassistant/components/alarmdecoder/translations/pt.json b/homeassistant/components/alarmdecoder/translations/pt.json index 8d6cb9a2ebf..19184aba556 100644 --- a/homeassistant/components/alarmdecoder/translations/pt.json +++ b/homeassistant/components/alarmdecoder/translations/pt.json @@ -4,12 +4,12 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "protocol": { "data": { - "host": "Servidor", + "host": "Endere\u00e7o", "port": "Porta" } } diff --git a/homeassistant/components/alarmdecoder/translations/sk.json b/homeassistant/components/alarmdecoder/translations/sk.json index 07bceeca884..0b9fb5f8984 100644 --- a/homeassistant/components/alarmdecoder/translations/sk.json +++ b/homeassistant/components/alarmdecoder/translations/sk.json @@ -22,26 +22,33 @@ "user": { "data": { "protocol": "Protokol" - } + }, + "title": "Vyberte AlarmDecoder protokol" } } }, "options": { "error": { "int": "Pole ni\u017e\u0161ie mus\u00ed by\u0165 cel\u00e9 \u010d\u00edslo.", - "loop_range": "RF Loop mus\u00ed by\u0165 cel\u00e9 \u010d\u00edslo od 1 do 4." + "loop_range": "RF Loop mus\u00ed by\u0165 cel\u00e9 \u010d\u00edslo od 1 do 4.", + "loop_rfid": "RF Loop nemo\u017eno pou\u017ei\u0165 bez RF Serial.", + "relay_inclusive": "Rel\u00e9ov\u00e1 adresa a rel\u00e9ov\u00fd kan\u00e1l s\u00fa vz\u00e1jomne z\u00e1visl\u00e9 a musia by\u0165 uveden\u00e9 spolo\u010dne." }, "step": { "arm_settings": { "data": { - "alt_night_mode": "Alternat\u00edvny no\u010dn\u00fd re\u017eim" - } + "alt_night_mode": "Alternat\u00edvny no\u010dn\u00fd re\u017eim", + "auto_bypass": "Automatick\u00fd bypass pri str\u00e1\u017een\u00ed", + "code_arm_required": "K\u00f3d potrebn\u00fd pre zabezpe\u010denie" + }, + "title": "Konfigur\u00e1cia AlarmDecoder" }, "init": { "data": { "edit_select": "Upravi\u0165" }, - "description": "\u010co by ste chceli upravi\u0165?" + "description": "\u010co by ste chceli upravi\u0165?", + "title": "Konfigur\u00e1cia AlarmDecoder" }, "zone_details": { "data": { @@ -52,13 +59,15 @@ "zone_rfid": "RF Serial", "zone_type": "Typ z\u00f3ny" }, - "description": "Zadajte podrobnosti pre z\u00f3nu {zone_number}. Ak chcete odstr\u00e1ni\u0165 z\u00f3nu {zone_number}, ponechajte n\u00e1zov z\u00f3ny pr\u00e1zdny." + "description": "Zadajte podrobnosti pre z\u00f3nu {zone_number}. Ak chcete odstr\u00e1ni\u0165 z\u00f3nu {zone_number}, ponechajte n\u00e1zov z\u00f3ny pr\u00e1zdny.", + "title": "Konfigur\u00e1cia AlarmDecoder" }, "zone_select": { "data": { "zone_number": "\u010c\u00edslo z\u00f3ny" }, - "description": "Zadajte \u010d\u00edslo z\u00f3ny, ktor\u00fa chcete prida\u0165, upravi\u0165 alebo odstr\u00e1ni\u0165." + "description": "Zadajte \u010d\u00edslo z\u00f3ny, ktor\u00fa chcete prida\u0165, upravi\u0165 alebo odstr\u00e1ni\u0165.", + "title": "Konfigur\u00e1cia AlarmDecoder" } } } diff --git a/homeassistant/components/alert/strings.json b/homeassistant/components/alert/strings.json new file mode 100644 index 00000000000..fb31ecd0577 --- /dev/null +++ b/homeassistant/components/alert/strings.json @@ -0,0 +1,10 @@ +{ + "title": "Alert", + "state": { + "_": { + "idle": "[%key:common::state::idle%]", + "off": "Acknowledged", + "on": "[%key:common::state::active%]" + } + } +} diff --git a/homeassistant/components/alert/translations/bg.json b/homeassistant/components/alert/translations/bg.json new file mode 100644 index 00000000000..73b1797b507 --- /dev/null +++ b/homeassistant/components/alert/translations/bg.json @@ -0,0 +1,3 @@ +{ + "title": "\u0421\u0438\u0433\u043d\u0430\u043b" +} \ No newline at end of file diff --git a/homeassistant/components/alert/translations/ca.json b/homeassistant/components/alert/translations/ca.json new file mode 100644 index 00000000000..29825023331 --- /dev/null +++ b/homeassistant/components/alert/translations/ca.json @@ -0,0 +1,10 @@ +{ + "state": { + "_": { + "idle": "Inactiu", + "off": "Ent\u00e8s", + "on": "Actiu" + } + }, + "title": "Alerta" +} \ No newline at end of file diff --git a/homeassistant/components/alert/translations/de.json b/homeassistant/components/alert/translations/de.json new file mode 100644 index 00000000000..02ce3698260 --- /dev/null +++ b/homeassistant/components/alert/translations/de.json @@ -0,0 +1,10 @@ +{ + "state": { + "_": { + "idle": "Inaktiv", + "off": "Anerkannt", + "on": "Aktiv" + } + }, + "title": "Alarm" +} \ No newline at end of file diff --git a/homeassistant/components/alert/translations/el.json b/homeassistant/components/alert/translations/el.json new file mode 100644 index 00000000000..fd444fc1042 --- /dev/null +++ b/homeassistant/components/alert/translations/el.json @@ -0,0 +1,10 @@ +{ + "state": { + "_": { + "idle": "\u0391\u03b4\u03c1\u03b1\u03bd\u03ae\u03c2", + "off": "\u0391\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03af\u03c3\u03c4\u03b7\u03ba\u03b5", + "on": "\u0395\u03bd\u03b5\u03c1\u03b3\u03cc\u03c2" + } + }, + "title": "\u03a3\u03c5\u03bd\u03b1\u03b3\u03b5\u03c1\u03bc\u03cc\u03c2" +} \ No newline at end of file diff --git a/homeassistant/components/alert/translations/en.json b/homeassistant/components/alert/translations/en.json new file mode 100644 index 00000000000..e3b95175064 --- /dev/null +++ b/homeassistant/components/alert/translations/en.json @@ -0,0 +1,10 @@ +{ + "state": { + "_": { + "idle": "Idle", + "off": "Acknowledged", + "on": "Active" + } + }, + "title": "Alert" +} \ No newline at end of file diff --git a/homeassistant/components/alert/translations/es.json b/homeassistant/components/alert/translations/es.json new file mode 100644 index 00000000000..791f6dfe9bb --- /dev/null +++ b/homeassistant/components/alert/translations/es.json @@ -0,0 +1,10 @@ +{ + "state": { + "_": { + "idle": "Inactivo", + "off": "Reconocida", + "on": "Activo" + } + }, + "title": "Alerta" +} \ No newline at end of file diff --git a/homeassistant/components/alert/translations/et.json b/homeassistant/components/alert/translations/et.json new file mode 100644 index 00000000000..e2c2d745d33 --- /dev/null +++ b/homeassistant/components/alert/translations/et.json @@ -0,0 +1,10 @@ +{ + "state": { + "_": { + "idle": "Ootel", + "off": "Kinnitatud", + "on": "Aktiivne" + } + }, + "title": "Hoiatus" +} \ No newline at end of file diff --git a/homeassistant/components/alert/translations/he.json b/homeassistant/components/alert/translations/he.json new file mode 100644 index 00000000000..c561f31dc7d --- /dev/null +++ b/homeassistant/components/alert/translations/he.json @@ -0,0 +1,10 @@ +{ + "state": { + "_": { + "idle": "\u05de\u05de\u05ea\u05d9\u05df", + "off": "\u05de\u05d5\u05db\u05e8", + "on": "\u05e4\u05e2\u05d9\u05dc" + } + }, + "title": "\u05d4\u05ea\u05e8\u05d0\u05d4" +} \ No newline at end of file diff --git a/homeassistant/components/alert/translations/hu.json b/homeassistant/components/alert/translations/hu.json new file mode 100644 index 00000000000..40b47b7d2d5 --- /dev/null +++ b/homeassistant/components/alert/translations/hu.json @@ -0,0 +1,10 @@ +{ + "state": { + "_": { + "idle": "T\u00e9tlen", + "off": "Tudom\u00e1sul v\u00e9ve", + "on": "Akt\u00edv" + } + }, + "title": "Riaszt\u00e1s" +} \ No newline at end of file diff --git a/homeassistant/components/alert/translations/id.json b/homeassistant/components/alert/translations/id.json new file mode 100644 index 00000000000..c8b5ee317b4 --- /dev/null +++ b/homeassistant/components/alert/translations/id.json @@ -0,0 +1,10 @@ +{ + "state": { + "_": { + "idle": "Siaga", + "off": "Diakui", + "on": "Aktif" + } + }, + "title": "Siaga" +} \ No newline at end of file diff --git a/homeassistant/components/alert/translations/it.json b/homeassistant/components/alert/translations/it.json new file mode 100644 index 00000000000..753fafb8ed6 --- /dev/null +++ b/homeassistant/components/alert/translations/it.json @@ -0,0 +1,10 @@ +{ + "state": { + "_": { + "idle": "Inattivo", + "off": "Riconosciuto", + "on": "Attivo" + } + }, + "title": "Allarme" +} \ No newline at end of file diff --git a/homeassistant/components/alert/translations/nl.json b/homeassistant/components/alert/translations/nl.json new file mode 100644 index 00000000000..07718138abb --- /dev/null +++ b/homeassistant/components/alert/translations/nl.json @@ -0,0 +1,10 @@ +{ + "state": { + "_": { + "idle": "Niet actief", + "off": "Bevestigd", + "on": "Actief" + } + }, + "title": "Alarmering" +} \ No newline at end of file diff --git a/homeassistant/components/alert/translations/no.json b/homeassistant/components/alert/translations/no.json new file mode 100644 index 00000000000..4411f85ef12 --- /dev/null +++ b/homeassistant/components/alert/translations/no.json @@ -0,0 +1,10 @@ +{ + "state": { + "_": { + "idle": "Inaktiv", + "off": "Anerkjent", + "on": "Aktiv" + } + }, + "title": "Varsling" +} \ No newline at end of file diff --git a/homeassistant/components/alert/translations/pl.json b/homeassistant/components/alert/translations/pl.json new file mode 100644 index 00000000000..5c5751848da --- /dev/null +++ b/homeassistant/components/alert/translations/pl.json @@ -0,0 +1,10 @@ +{ + "state": { + "_": { + "idle": "nieaktywny", + "off": "potwierdzony", + "on": "aktywny" + } + }, + "title": "Alert" +} \ No newline at end of file diff --git a/homeassistant/components/alert/translations/pt-BR.json b/homeassistant/components/alert/translations/pt-BR.json new file mode 100644 index 00000000000..cec1745b7ce --- /dev/null +++ b/homeassistant/components/alert/translations/pt-BR.json @@ -0,0 +1,10 @@ +{ + "state": { + "_": { + "idle": "Ocioso", + "off": "Reconhecido", + "on": "Ativo" + } + }, + "title": "Alerta" +} \ No newline at end of file diff --git a/homeassistant/components/alert/translations/ru.json b/homeassistant/components/alert/translations/ru.json new file mode 100644 index 00000000000..948fc6034ea --- /dev/null +++ b/homeassistant/components/alert/translations/ru.json @@ -0,0 +1,10 @@ +{ + "state": { + "_": { + "idle": "\u0411\u0435\u0437\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435", + "off": "\u041f\u0440\u0438\u043d\u044f\u0442\u043e", + "on": "\u0410\u043a\u0442\u0438\u0432\u043d\u043e" + } + }, + "title": "\u041e\u043f\u043e\u0432\u0435\u0449\u0435\u043d\u0438\u0435" +} \ No newline at end of file diff --git a/homeassistant/components/alert/translations/sk.json b/homeassistant/components/alert/translations/sk.json new file mode 100644 index 00000000000..37da3db1035 --- /dev/null +++ b/homeassistant/components/alert/translations/sk.json @@ -0,0 +1,10 @@ +{ + "state": { + "_": { + "idle": "Ne\u010dinn\u00e1", + "off": "Potvrden\u00e9", + "on": "akt\u00edvny" + } + }, + "title": "Poplach" +} \ No newline at end of file diff --git a/homeassistant/components/alert/translations/zh-Hant.json b/homeassistant/components/alert/translations/zh-Hant.json new file mode 100644 index 00000000000..e2362b4c08e --- /dev/null +++ b/homeassistant/components/alert/translations/zh-Hant.json @@ -0,0 +1,10 @@ +{ + "state": { + "_": { + "idle": "\u5f85\u547d", + "off": "\u5df2\u4e86\u89e3", + "on": "\u555f\u7528" + } + }, + "title": "\u8b66\u5831" +} \ No newline at end of file diff --git a/homeassistant/components/alexa/capabilities.py b/homeassistant/components/alexa/capabilities.py index 56b9e88e27e..efa2ee3a48a 100644 --- a/homeassistant/components/alexa/capabilities.py +++ b/homeassistant/components/alexa/capabilities.py @@ -169,60 +169,47 @@ class AlexaCapability: def serialize_discovery(self): """Serialize according to the Discovery API.""" - # pylint: disable=assignment-from-none - # Methods may be overridden and return a value. result = {"type": "AlexaInterface", "interface": self.name(), "version": "3"} if (instance := self.instance) is not None: result["instance"] = instance - properties_supported = self.properties_supported() - if properties_supported: + if properties_supported := self.properties_supported(): result["properties"] = { - "supported": self.properties_supported(), + "supported": properties_supported, "proactivelyReported": self.properties_proactively_reported(), "retrievable": self.properties_retrievable(), } - proactively_reported = self.capability_proactively_reported() - if proactively_reported is not None: + if (proactively_reported := self.capability_proactively_reported()) is not None: result["proactivelyReported"] = proactively_reported - non_controllable = self.properties_non_controllable() - if non_controllable is not None: + if (non_controllable := self.properties_non_controllable()) is not None: result["properties"]["nonControllable"] = non_controllable - supports_deactivation = self.supports_deactivation() - if supports_deactivation is not None: + if (supports_deactivation := self.supports_deactivation()) is not None: result["supportsDeactivation"] = supports_deactivation - capability_resources = self.capability_resources() - if capability_resources: + if capability_resources := self.capability_resources(): result["capabilityResources"] = capability_resources - configuration = self.configuration() - if configuration: + if configuration := self.configuration(): result["configuration"] = configuration # The plural configurations object is different than the singular configuration object above. - configurations = self.configurations() - if configurations: + if configurations := self.configurations(): result["configurations"] = configurations - semantics = self.semantics() - if semantics: + if semantics := self.semantics(): result["semantics"] = semantics - supported_operations = self.supported_operations() - if supported_operations: + if supported_operations := self.supported_operations(): result["supportedOperations"] = supported_operations - inputs = self.inputs() - if inputs: + if inputs := self.inputs(): result["inputs"] = inputs - camera_stream_configurations = self.camera_stream_configurations() - if camera_stream_configurations: + if camera_stream_configurations := self.camera_stream_configurations(): result["cameraStreamConfigurations"] = camera_stream_configurations return result diff --git a/homeassistant/components/alexa/const.py b/homeassistant/components/alexa/const.py index a34355b7ddb..9e1c9e589c1 100644 --- a/homeassistant/components/alexa/const.py +++ b/homeassistant/components/alexa/const.py @@ -2,7 +2,7 @@ from collections import OrderedDict from homeassistant.components import climate -from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT +from homeassistant.const import UnitOfTemperature DOMAIN = "alexa" EVENT_ALEXA_SMART_HOME = "alexa_smart_home" @@ -61,7 +61,10 @@ CONF_SUPPORTED_LOCALES = ( "pt-BR", ) -API_TEMP_UNITS = {TEMP_FAHRENHEIT: "FAHRENHEIT", TEMP_CELSIUS: "CELSIUS"} +API_TEMP_UNITS = { + UnitOfTemperature.FAHRENHEIT: "FAHRENHEIT", + UnitOfTemperature.CELSIUS: "CELSIUS", +} # Needs to be ordered dict for `async_api_set_thermostat_mode` which does a # reverse mapping of this dict and we want to map the first occurrence of OFF diff --git a/homeassistant/components/alexa/entities.py b/homeassistant/components/alexa/entities.py index 35313573b19..77d35a1582c 100644 --- a/homeassistant/components/alexa/entities.py +++ b/homeassistant/components/alexa/entities.py @@ -37,8 +37,7 @@ from homeassistant.const import ( CLOUD_NEVER_EXPOSED_ENTITIES, CONF_DESCRIPTION, CONF_NAME, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, + UnitOfTemperature, __version__, ) from homeassistant.core import HomeAssistant, State, callback @@ -745,7 +744,10 @@ class SensorCapabilities(AlexaEntity): def interfaces(self): """Yield the supported interfaces.""" attrs = self.entity.attributes - if attrs.get(ATTR_UNIT_OF_MEASUREMENT) in (TEMP_FAHRENHEIT, TEMP_CELSIUS): + if attrs.get(ATTR_UNIT_OF_MEASUREMENT) in { + UnitOfTemperature.FAHRENHEIT, + UnitOfTemperature.CELSIUS, + }: yield AlexaTemperatureSensor(self.hass, self.entity) yield AlexaEndpointHealth(self.hass, self.entity) yield Alexa(self.hass) diff --git a/homeassistant/components/alexa/handlers.py b/homeassistant/components/alexa/handlers.py index d9a2e7016e9..24ab3ec10e3 100644 --- a/homeassistant/components/alexa/handlers.py +++ b/homeassistant/components/alexa/handlers.py @@ -47,8 +47,7 @@ from homeassistant.const import ( SERVICE_VOLUME_SET, SERVICE_VOLUME_UP, STATE_ALARM_DISARMED, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, + UnitOfTemperature, ) from homeassistant.helpers import network from homeassistant.util import color as color_util, dt as dt_util @@ -476,7 +475,10 @@ async def async_api_unlock( ) -> AlexaResponse: """Process an unlock request.""" if config.locale not in {"de-DE", "en-US", "ja-JP"}: - msg = f"The unlock directive is not supported for the following locales: {config.locale}" + msg = ( + "The unlock directive is not supported for the following locales:" + f" {config.locale}" + ) raise AlexaInvalidDirectiveError(msg) entity = directive.entity @@ -758,11 +760,11 @@ async def async_api_previous( def temperature_from_object(hass, temp_obj, interval=False): """Get temperature from Temperature object in requested unit.""" to_unit = hass.config.units.temperature_unit - from_unit = TEMP_CELSIUS + from_unit = UnitOfTemperature.CELSIUS temp = float(temp_obj["value"]) if temp_obj["scale"] == "FAHRENHEIT": - from_unit = TEMP_FAHRENHEIT + from_unit = UnitOfTemperature.FAHRENHEIT elif temp_obj["scale"] == "KELVIN" and not interval: # convert to Celsius if absolute temperature temp -= 273.15 diff --git a/homeassistant/components/alexa/logbook.py b/homeassistant/components/alexa/logbook.py index 079fea99fdf..496989c57de 100644 --- a/homeassistant/components/alexa/logbook.py +++ b/homeassistant/components/alexa/logbook.py @@ -21,7 +21,10 @@ def async_describe_events(hass, async_describe_event): if entity_id := data["request"].get("entity_id"): state = hass.states.get(entity_id) name = state.name if state else entity_id - message = f"sent command {data['request']['namespace']}/{data['request']['name']} for {name}" + message = ( + "sent command" + f" {data['request']['namespace']}/{data['request']['name']} for {name}" + ) else: message = ( f"sent command {data['request']['namespace']}/{data['request']['name']}" diff --git a/homeassistant/components/almond/__init__.py b/homeassistant/components/almond/__init__.py index 09ff85491ba..07aea4f792e 100644 --- a/homeassistant/components/almond/__init__.py +++ b/homeassistant/components/almond/__init__.py @@ -273,7 +273,10 @@ class AlmondAgent(conversation.AbstractConversationAgent): if self.entry.data.get("is_hassio"): host = "/core_almond" return { - "text": "Would you like to opt-in to share your anonymized commands with Stanford to improve Almond's responses?", + "text": ( + "Would you like to opt-in to share your anonymized commands with" + " Stanford to improve Almond's responses?" + ), "url": f"{host}/conversation", } @@ -286,10 +289,15 @@ class AlmondAgent(conversation.AbstractConversationAgent): return True async def async_process( - self, text: str, context: Context, conversation_id: str | None = None - ) -> intent.IntentResponse: + self, + text: str, + context: Context, + conversation_id: str | None = None, + language: str | None = None, + ) -> conversation.ConversationResult | None: """Process a sentence.""" response = await self.api.async_converse_text(text, conversation_id) + language = language or self.hass.config.language first_choice = True buffer = "" @@ -310,6 +318,8 @@ class AlmondAgent(conversation.AbstractConversationAgent): buffer += "," buffer += f" {message['title']}" - intent_result = intent.IntentResponse() - intent_result.async_set_speech(buffer.strip()) - return intent_result + intent_response = intent.IntentResponse(language=language) + intent_response.async_set_speech(buffer.strip()) + return conversation.ConversationResult( + response=intent_response, conversation_id=conversation_id + ) diff --git a/homeassistant/components/almond/translations/pt.json b/homeassistant/components/almond/translations/pt.json index 44f49239642..86d4823a272 100644 --- a/homeassistant/components/almond/translations/pt.json +++ b/homeassistant/components/almond/translations/pt.json @@ -1,9 +1,9 @@ { "config": { "abort": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "missing_configuration": "O componente n\u00e3o est\u00e1 configurado. Por favor, siga a documenta\u00e7\u00e3o.", - "no_url_available": "Nenhum URL dispon\u00edvel. Para obter informa\u00e7\u00f5es sobre esse erro, [verifique a sec\u00e7\u00e3o de ajuda]({docs_url})", + "no_url_available": "Nenhum URL est\u00e1 dispon\u00edvel. Para obter informa\u00e7\u00f5es sobre esse erro, [consulte a sec\u00e7\u00e3o de ajuda]({docs_url})", "single_instance_allowed": "J\u00e1 configurado. Apenas uma \u00fanica configura\u00e7\u00e3o \u00e9 poss\u00edvel." }, "step": { diff --git a/homeassistant/components/almond/translations/sk.json b/homeassistant/components/almond/translations/sk.json index db82c552cb2..0189ab5be44 100644 --- a/homeassistant/components/almond/translations/sk.json +++ b/homeassistant/components/almond/translations/sk.json @@ -8,7 +8,8 @@ }, "step": { "hassio_confirm": { - "description": "Chcete nakonfigurova\u0165 dom\u00e1ceho asistenta na pripojenie k Almond poskytovan\u00e9mu doplnkom: {addon}?" + "description": "Chcete nakonfigurova\u0165 dom\u00e1ceho asistenta na pripojenie k Almond poskytovan\u00e9mu doplnkom: {addon}?", + "title": "Doplnok Almond cez Home Assistant" }, "pick_implementation": { "title": "Vyberte met\u00f3du overenia" diff --git a/homeassistant/components/amberelectric/sensor.py b/homeassistant/components/amberelectric/sensor.py index 98aed91a941..4a6d1a6ea18 100644 --- a/homeassistant/components/amberelectric/sensor.py +++ b/homeassistant/components/amberelectric/sensor.py @@ -20,7 +20,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CURRENCY_DOLLAR, ENERGY_KILO_WATT_HOUR, PERCENTAGE +from homeassistant.const import CURRENCY_DOLLAR, PERCENTAGE, UnitOfEnergy from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import CoordinatorEntity @@ -34,7 +34,7 @@ ICONS = { "feed_in": "mdi:solar-power", } -UNIT = f"{CURRENCY_DOLLAR}/{ENERGY_KILO_WATT_HOUR}" +UNIT = f"{CURRENCY_DOLLAR}/{UnitOfEnergy.KILO_WATT_HOUR}" def format_cents_to_dollars(cents: float) -> float: @@ -227,7 +227,10 @@ async def async_setup_entry( for channel_type in current: description = SensorEntityDescription( key="descriptors", - name=f"{entry.title} - {friendly_channel_type(channel_type)} Price Descriptor", + name=( + f"{entry.title} - {friendly_channel_type(channel_type)} Price" + " Descriptor" + ), icon=ICONS[channel_type], ) entities.append( diff --git a/homeassistant/components/amberelectric/translations/ko.json b/homeassistant/components/amberelectric/translations/ko.json new file mode 100644 index 00000000000..d3f88e8263e --- /dev/null +++ b/homeassistant/components/amberelectric/translations/ko.json @@ -0,0 +1,8 @@ +{ + "config": { + "error": { + "invalid_api_token": "API \ud0a4\uac00 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown_error": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/amberelectric/translations/sk.json b/homeassistant/components/amberelectric/translations/sk.json index 9935c530ebc..a0a3f98b79e 100644 --- a/homeassistant/components/amberelectric/translations/sk.json +++ b/homeassistant/components/amberelectric/translations/sk.json @@ -6,6 +6,13 @@ "unknown_error": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { + "site": { + "data": { + "site_name": "N\u00e1zov lokality", + "site_nmi": "Miesto NMI" + }, + "description": "Vyberte NMI lokality, ktor\u00fa chcete prida\u0165" + }, "user": { "data": { "api_token": "API token", diff --git a/homeassistant/components/ambiclimate/climate.py b/homeassistant/components/ambiclimate/climate.py index 5a5fea6c230..2bb2b441430 100644 --- a/homeassistant/components/ambiclimate/climate.py +++ b/homeassistant/components/ambiclimate/climate.py @@ -19,7 +19,7 @@ from homeassistant.const import ( ATTR_TEMPERATURE, CONF_CLIENT_ID, CONF_CLIENT_SECRET, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant, ServiceCall from homeassistant.helpers import config_validation as cv @@ -150,7 +150,7 @@ async def async_setup_entry( class AmbiclimateEntity(ClimateEntity): """Representation of a Ambiclimate Thermostat device.""" - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS _attr_target_temperature_step = 1 _attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE _attr_hvac_modes = [HVACMode.HEAT, HVACMode.OFF] diff --git a/homeassistant/components/ambiclimate/translations/sk.json b/homeassistant/components/ambiclimate/translations/sk.json index d13178679a4..e877cd7e1b8 100644 --- a/homeassistant/components/ambiclimate/translations/sk.json +++ b/homeassistant/components/ambiclimate/translations/sk.json @@ -9,7 +9,14 @@ "default": "\u00daspe\u0161ne overen\u00e9" }, "error": { + "follow_link": "Pred stla\u010den\u00edm Odosla\u0165 kliknite na odkaz a overte sa", "no_token": "Neoveren\u00e9 pomocou Ambiclimate" + }, + "step": { + "auth": { + "description": "Nasledujte tento [odkaz]({authorization_url}) a **Povoli\u0165** pr\u00edstup k v\u00e1\u0161mu \u00fa\u010dtu Ambiclimate, potom se vr\u00e1\u0165te a stla\u010dte **Odesla\u0165** n\u00ed\u017e\u0161ie. \n (Uistite sa, \u017ee zadan\u00e1 adresa URL sp\u00e4tn\u00e9ho volan\u00eda je {cb_url})", + "title": "Overi\u0165 Ambiclimate" + } } } } \ No newline at end of file diff --git a/homeassistant/components/ambient_station/__init__.py b/homeassistant/components/ambient_station/__init__.py index 7242c0ba53b..9aced7a2a45 100644 --- a/homeassistant/components/ambient_station/__init__.py +++ b/homeassistant/components/ambient_station/__init__.py @@ -213,7 +213,9 @@ class AmbientWeatherEntity(Entity): public_device_id = get_public_device_id(mac_address) self._attr_device_info = DeviceInfo( - configuration_url=f"https://ambientweather.net/dashboard/{public_device_id}", + configuration_url=( + f"https://ambientweather.net/dashboard/{public_device_id}" + ), identifiers={(DOMAIN, mac_address)}, manufacturer="Ambient Weather", name=station_name.capitalize(), diff --git a/homeassistant/components/ambient_station/binary_sensor.py b/homeassistant/components/ambient_station/binary_sensor.py index bcc2ae60404..67a54906e92 100644 --- a/homeassistant/components/ambient_station/binary_sensor.py +++ b/homeassistant/components/ambient_station/binary_sensor.py @@ -31,6 +31,10 @@ TYPE_BATT9 = "batt9" TYPE_BATTIN = "battin" TYPE_BATTOUT = "battout" TYPE_BATT_CO2 = "batt_co2" +TYPE_BATT_LEAK1 = "batleak1" +TYPE_BATT_LEAK2 = "batleak2" +TYPE_BATT_LEAK3 = "batleak3" +TYPE_BATT_LEAK4 = "batleak4" TYPE_BATT_LIGHTNING = "batt_lightning" TYPE_BATT_SM1 = "battsm1" TYPE_BATT_SM10 = "battsm10" @@ -42,6 +46,10 @@ TYPE_BATT_SM6 = "battsm6" TYPE_BATT_SM7 = "battsm7" TYPE_BATT_SM8 = "battsm8" TYPE_BATT_SM9 = "battsm9" +TYPE_LEAK1 = "leak1" +TYPE_LEAK2 = "leak2" +TYPE_LEAK3 = "leak3" +TYPE_LEAK4 = "leak4" TYPE_PM25IN_BATT = "batt_25in" TYPE_PM25_BATT = "batt_25" TYPE_RELAY1 = "relay1" @@ -155,6 +163,34 @@ BINARY_SENSOR_DESCRIPTIONS = ( entity_category=EntityCategory.DIAGNOSTIC, on_state=0, ), + AmbientBinarySensorDescription( + key=TYPE_BATT_LEAK1, + name="Leak detector battery 1", + device_class=BinarySensorDeviceClass.BATTERY, + entity_category=EntityCategory.DIAGNOSTIC, + on_state=0, + ), + AmbientBinarySensorDescription( + key=TYPE_BATT_LEAK2, + name="Leak detector battery 2", + device_class=BinarySensorDeviceClass.BATTERY, + entity_category=EntityCategory.DIAGNOSTIC, + on_state=0, + ), + AmbientBinarySensorDescription( + key=TYPE_BATT_LEAK3, + name="Leak detector battery 3", + device_class=BinarySensorDeviceClass.BATTERY, + entity_category=EntityCategory.DIAGNOSTIC, + on_state=0, + ), + AmbientBinarySensorDescription( + key=TYPE_BATT_LEAK4, + name="Leak detector battery 4", + device_class=BinarySensorDeviceClass.BATTERY, + entity_category=EntityCategory.DIAGNOSTIC, + on_state=0, + ), AmbientBinarySensorDescription( key=TYPE_BATT_SM1, name="Soil monitor battery 1", @@ -239,6 +275,30 @@ BINARY_SENSOR_DESCRIPTIONS = ( entity_category=EntityCategory.DIAGNOSTIC, on_state=0, ), + AmbientBinarySensorDescription( + key=TYPE_LEAK1, + name="Leak detector 1", + device_class=BinarySensorDeviceClass.MOISTURE, + on_state=1, + ), + AmbientBinarySensorDescription( + key=TYPE_LEAK2, + name="Leak detector 2", + device_class=BinarySensorDeviceClass.MOISTURE, + on_state=1, + ), + AmbientBinarySensorDescription( + key=TYPE_LEAK3, + name="Leak detector 3", + device_class=BinarySensorDeviceClass.MOISTURE, + on_state=1, + ), + AmbientBinarySensorDescription( + key=TYPE_LEAK4, + name="Leak detector 4", + device_class=BinarySensorDeviceClass.MOISTURE, + on_state=1, + ), AmbientBinarySensorDescription( key=TYPE_PM25IN_BATT, name="PM25 indoor battery", diff --git a/homeassistant/components/ambient_station/sensor.py b/homeassistant/components/ambient_station/sensor.py index b1e98261c6e..eb01fd379b2 100644 --- a/homeassistant/components/ambient_station/sensor.py +++ b/homeassistant/components/ambient_station/sensor.py @@ -15,13 +15,13 @@ from homeassistant.const import ( CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, CONCENTRATION_PARTS_PER_MILLION, DEGREE, - IRRADIATION_WATTS_PER_SQUARE_METER, LIGHT_LUX, PERCENTAGE, - PRECIPITATION_INCHES, - PRESSURE_INHG, - SPEED_MILES_PER_HOUR, - TEMP_FAHRENHEIT, + UnitOfIrradiance, + UnitOfPrecipitationDepth, + UnitOfPressure, + UnitOfSpeed, + UnitOfTemperature, UnitOfVolumetricFlux, ) from homeassistant.core import HomeAssistant, callback @@ -114,8 +114,8 @@ SENSOR_DESCRIPTIONS = ( SensorEntityDescription( key=TYPE_24HOURRAININ, name="24 hr rain", - icon="mdi:water", - native_unit_of_measurement=PRECIPITATION_INCHES, + native_unit_of_measurement=UnitOfPrecipitationDepth.INCHES, + device_class=SensorDeviceClass.PRECIPITATION, state_class=SensorStateClass.TOTAL_INCREASING, ), SensorEntityDescription( @@ -145,14 +145,14 @@ SENSOR_DESCRIPTIONS = ( SensorEntityDescription( key=TYPE_BAROMABSIN, name="Abs pressure", - native_unit_of_measurement=PRESSURE_INHG, + native_unit_of_measurement=UnitOfPressure.INHG, device_class=SensorDeviceClass.PRESSURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=TYPE_BAROMRELIN, name="Rel pressure", - native_unit_of_measurement=PRESSURE_INHG, + native_unit_of_measurement=UnitOfPressure.INHG, device_class=SensorDeviceClass.PRESSURE, state_class=SensorStateClass.MEASUREMENT, ), @@ -166,28 +166,28 @@ SENSOR_DESCRIPTIONS = ( SensorEntityDescription( key=TYPE_DAILYRAININ, name="Daily rain", - icon="mdi:water", - native_unit_of_measurement=PRECIPITATION_INCHES, + native_unit_of_measurement=UnitOfPrecipitationDepth.INCHES, + device_class=SensorDeviceClass.PRECIPITATION, state_class=SensorStateClass.TOTAL_INCREASING, ), SensorEntityDescription( key=TYPE_DEWPOINT, name="Dew point", - native_unit_of_measurement=TEMP_FAHRENHEIT, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=TYPE_EVENTRAININ, name="Event rain", - icon="mdi:water", - native_unit_of_measurement=PRECIPITATION_INCHES, + native_unit_of_measurement=UnitOfPrecipitationDepth.INCHES, + device_class=SensorDeviceClass.PRECIPITATION, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=TYPE_FEELSLIKE, name="Feels like", - native_unit_of_measurement=TEMP_FAHRENHEIT, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), @@ -306,16 +306,15 @@ SENSOR_DESCRIPTIONS = ( SensorEntityDescription( key=TYPE_MAXDAILYGUST, name="Max gust", - icon="mdi:weather-windy", - native_unit_of_measurement=SPEED_MILES_PER_HOUR, - device_class=SensorDeviceClass.SPEED, + native_unit_of_measurement=UnitOfSpeed.MILES_PER_HOUR, + device_class=SensorDeviceClass.WIND_SPEED, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=TYPE_MONTHLYRAININ, name="Monthly rain", - icon="mdi:water", - native_unit_of_measurement=PRECIPITATION_INCHES, + native_unit_of_measurement=UnitOfPrecipitationDepth.INCHES, + device_class=SensorDeviceClass.PRECIPITATION, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( @@ -417,78 +416,78 @@ SENSOR_DESCRIPTIONS = ( SensorEntityDescription( key=TYPE_SOILTEMP10F, name="Soil temp 10", - native_unit_of_measurement=TEMP_FAHRENHEIT, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=TYPE_SOILTEMP1F, name="Soil temp 1", - native_unit_of_measurement=TEMP_FAHRENHEIT, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=TYPE_SOILTEMP2F, name="Soil temp 2", - native_unit_of_measurement=TEMP_FAHRENHEIT, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=TYPE_SOILTEMP3F, name="Soil temp 3", - native_unit_of_measurement=TEMP_FAHRENHEIT, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=TYPE_SOILTEMP4F, name="Soil temp 4", - native_unit_of_measurement=TEMP_FAHRENHEIT, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=TYPE_SOILTEMP5F, name="Soil temp 5", - native_unit_of_measurement=TEMP_FAHRENHEIT, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=TYPE_SOILTEMP6F, name="Soil temp 6", - native_unit_of_measurement=TEMP_FAHRENHEIT, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=TYPE_SOILTEMP7F, name="Soil temp 7", - native_unit_of_measurement=TEMP_FAHRENHEIT, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=TYPE_SOILTEMP8F, name="Soil temp 8", - native_unit_of_measurement=TEMP_FAHRENHEIT, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=TYPE_SOILTEMP9F, name="Soil temp 9", - native_unit_of_measurement=TEMP_FAHRENHEIT, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=TYPE_SOLARRADIATION, name="Solar rad", - native_unit_of_measurement=IRRADIATION_WATTS_PER_SQUARE_METER, - device_class=SensorDeviceClass.ILLUMINANCE, + native_unit_of_measurement=UnitOfIrradiance.WATTS_PER_SQUARE_METER, + device_class=SensorDeviceClass.IRRADIANCE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( @@ -501,106 +500,105 @@ SENSOR_DESCRIPTIONS = ( SensorEntityDescription( key=TYPE_TEMP10F, name="Temp 10", - native_unit_of_measurement=TEMP_FAHRENHEIT, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=TYPE_TEMP1F, name="Temp 1", - native_unit_of_measurement=TEMP_FAHRENHEIT, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=TYPE_TEMP2F, name="Temp 2", - native_unit_of_measurement=TEMP_FAHRENHEIT, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=TYPE_TEMP3F, name="Temp 3", - native_unit_of_measurement=TEMP_FAHRENHEIT, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=TYPE_TEMP4F, name="Temp 4", - native_unit_of_measurement=TEMP_FAHRENHEIT, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=TYPE_TEMP5F, name="Temp 5", - native_unit_of_measurement=TEMP_FAHRENHEIT, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=TYPE_TEMP6F, name="Temp 6", - native_unit_of_measurement=TEMP_FAHRENHEIT, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=TYPE_TEMP7F, name="Temp 7", - native_unit_of_measurement=TEMP_FAHRENHEIT, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=TYPE_TEMP8F, name="Temp 8", - native_unit_of_measurement=TEMP_FAHRENHEIT, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=TYPE_TEMP9F, name="Temp 9", - native_unit_of_measurement=TEMP_FAHRENHEIT, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=TYPE_TEMPF, name="Temp", - native_unit_of_measurement=TEMP_FAHRENHEIT, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=TYPE_TEMPINF, name="Inside temp", - native_unit_of_measurement=TEMP_FAHRENHEIT, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=TYPE_TOTALRAININ, name="Lifetime rain", - icon="mdi:water", - native_unit_of_measurement=PRECIPITATION_INCHES, + native_unit_of_measurement=UnitOfPrecipitationDepth.INCHES, + device_class=SensorDeviceClass.PRECIPITATION, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=TYPE_UV, name="UV index", native_unit_of_measurement="Index", - device_class=SensorDeviceClass.ILLUMINANCE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=TYPE_WEEKLYRAININ, name="Weekly rain", - icon="mdi:water", - native_unit_of_measurement=PRECIPITATION_INCHES, + native_unit_of_measurement=UnitOfPrecipitationDepth.INCHES, + device_class=SensorDeviceClass.PRECIPITATION, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( @@ -630,38 +628,34 @@ SENSOR_DESCRIPTIONS = ( SensorEntityDescription( key=TYPE_WINDGUSTMPH, name="Wind gust", - icon="mdi:weather-windy", - native_unit_of_measurement=SPEED_MILES_PER_HOUR, - device_class=SensorDeviceClass.SPEED, + native_unit_of_measurement=UnitOfSpeed.MILES_PER_HOUR, + device_class=SensorDeviceClass.WIND_SPEED, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=TYPE_WINDSPDMPH_AVG10M, name="Wind avg 10m", - icon="mdi:weather-windy", - native_unit_of_measurement=SPEED_MILES_PER_HOUR, - device_class=SensorDeviceClass.SPEED, + native_unit_of_measurement=UnitOfSpeed.MILES_PER_HOUR, + device_class=SensorDeviceClass.WIND_SPEED, ), SensorEntityDescription( key=TYPE_WINDSPDMPH_AVG2M, name="Wind avg 2m", - icon="mdi:weather-windy", - native_unit_of_measurement=SPEED_MILES_PER_HOUR, - device_class=SensorDeviceClass.SPEED, + native_unit_of_measurement=UnitOfSpeed.MILES_PER_HOUR, + device_class=SensorDeviceClass.WIND_SPEED, ), SensorEntityDescription( key=TYPE_WINDSPEEDMPH, name="Wind speed", - icon="mdi:weather-windy", - native_unit_of_measurement=SPEED_MILES_PER_HOUR, - device_class=SensorDeviceClass.SPEED, + native_unit_of_measurement=UnitOfSpeed.MILES_PER_HOUR, + device_class=SensorDeviceClass.WIND_SPEED, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=TYPE_YEARLYRAININ, name="Yearly rain", - icon="mdi:water", - native_unit_of_measurement=PRECIPITATION_INCHES, + native_unit_of_measurement=UnitOfPrecipitationDepth.INCHES, + device_class=SensorDeviceClass.PRECIPITATION, state_class=SensorStateClass.TOTAL_INCREASING, ), ) diff --git a/homeassistant/components/analytics/__init__.py b/homeassistant/components/analytics/__init__.py index bdc7806e456..ad53fb03113 100644 --- a/homeassistant/components/analytics/__init__.py +++ b/homeassistant/components/analytics/__init__.py @@ -5,7 +5,7 @@ import voluptuous as vol from homeassistant.components import websocket_api from homeassistant.const import EVENT_HOMEASSISTANT_STARTED -from homeassistant.core import HomeAssistant +from homeassistant.core import Event, HomeAssistant, callback from homeassistant.helpers.event import async_call_later, async_track_time_interval from homeassistant.helpers.typing import ConfigType @@ -20,7 +20,8 @@ async def async_setup(hass: HomeAssistant, _: ConfigType) -> bool: # Load stored data await analytics.load() - async def start_schedule(_event): + @callback + def start_schedule(_event: Event) -> None: """Start the send schedule after the started event.""" # Wait 15 min after started async_call_later(hass, 900, analytics.send_analytics) @@ -37,10 +38,10 @@ async def async_setup(hass: HomeAssistant, _: ConfigType) -> bool: return True +@callback @websocket_api.require_admin @websocket_api.websocket_command({vol.Required("type"): "analytics"}) -@websocket_api.async_response -async def websocket_analytics( +def websocket_analytics( hass: HomeAssistant, connection: websocket_api.connection.ActiveConnection, msg: dict[str, Any], diff --git a/homeassistant/components/analytics/analytics.py b/homeassistant/components/analytics/analytics.py index 2e53d9c03d5..178faf7ccca 100644 --- a/homeassistant/components/analytics/analytics.py +++ b/homeassistant/components/analytics/analytics.py @@ -1,5 +1,9 @@ """Analytics helper class for the analytics integration.""" +from __future__ import annotations + import asyncio +from dataclasses import asdict as dataclass_asdict, dataclass +from datetime import datetime from typing import Any import uuid @@ -39,9 +43,7 @@ from .const import ( ATTR_HEALTHY, ATTR_INTEGRATION_COUNT, ATTR_INTEGRATIONS, - ATTR_ONBOARDED, ATTR_OPERATING_SYSTEM, - ATTR_PREFERENCES, ATTR_PROTECTED, ATTR_SLUG, ATTR_STATE_COUNT, @@ -59,6 +61,24 @@ from .const import ( ) +@dataclass +class AnalyticsData: + """Analytics data.""" + + onboarded: bool + preferences: dict[str, bool] + uuid: str | None + + @classmethod + def from_dict(cls, data: dict[str, Any]) -> AnalyticsData: + """Initialize analytics data from a dict.""" + return cls( + data["onboarded"], + data["preferences"], + data["uuid"], + ) + + class Analytics: """Analytics helper class for the analytics integration.""" @@ -66,17 +86,13 @@ class Analytics: """Initialize the Analytics class.""" self.hass: HomeAssistant = hass self.session = async_get_clientsession(hass) - self._data: dict[str, Any] = { - ATTR_PREFERENCES: {}, - ATTR_ONBOARDED: False, - ATTR_UUID: None, - } + self._data = AnalyticsData(False, {}, None) self._store = Store[dict[str, Any]](hass, STORAGE_VERSION, STORAGE_KEY) @property def preferences(self) -> dict: """Return the current active preferences.""" - preferences = self._data[ATTR_PREFERENCES] + preferences = self._data.preferences return { ATTR_BASE: preferences.get(ATTR_BASE, False), ATTR_DIAGNOSTICS: preferences.get(ATTR_DIAGNOSTICS, False), @@ -87,12 +103,12 @@ class Analytics: @property def onboarded(self) -> bool: """Return bool if the user has made a choice.""" - return self._data[ATTR_ONBOARDED] + return self._data.onboarded @property - def uuid(self) -> bool: + def uuid(self) -> str | None: """Return the uuid for the analytics integration.""" - return self._data[ATTR_UUID] + return self._data.uuid @property def endpoint(self) -> str: @@ -111,7 +127,7 @@ class Analytics: """Load preferences.""" stored = await self._store.async_load() if stored: - self._data = stored + self._data = AnalyticsData.from_dict(stored) if ( self.supervisor @@ -122,41 +138,41 @@ class Analytics: if supervisor_info[ATTR_DIAGNOSTICS] and not self.preferences.get( ATTR_DIAGNOSTICS, False ): - self._data[ATTR_PREFERENCES][ATTR_DIAGNOSTICS] = True + self._data.preferences[ATTR_DIAGNOSTICS] = True elif not supervisor_info[ATTR_DIAGNOSTICS] and self.preferences.get( ATTR_DIAGNOSTICS, False ): - self._data[ATTR_PREFERENCES][ATTR_DIAGNOSTICS] = False + self._data.preferences[ATTR_DIAGNOSTICS] = False async def save_preferences(self, preferences: dict) -> None: """Save preferences.""" preferences = PREFERENCE_SCHEMA(preferences) - self._data[ATTR_PREFERENCES].update(preferences) - self._data[ATTR_ONBOARDED] = True + self._data.preferences.update(preferences) + self._data.onboarded = True - await self._store.async_save(self._data) + await self._store.async_save(dataclass_asdict(self._data)) if self.supervisor: await hassio.async_update_diagnostics( self.hass, self.preferences.get(ATTR_DIAGNOSTICS, False) ) - async def send_analytics(self, _=None) -> None: + async def send_analytics(self, _: datetime | None = None) -> None: """Send analytics.""" supervisor_info = None - operating_system_info = {} + operating_system_info: dict[str, Any] = {} if not self.onboarded or not self.preferences.get(ATTR_BASE, False): LOGGER.debug("Nothing to submit") return - if self._data.get(ATTR_UUID) is None: - self._data[ATTR_UUID] = uuid.uuid4().hex - await self._store.async_save(self._data) + if self._data.uuid is None: + self._data.uuid = uuid.uuid4().hex + await self._store.async_save(dataclass_asdict(self._data)) if self.supervisor: supervisor_info = hassio.get_supervisor_info(self.hass) - operating_system_info = hassio.get_os_info(self.hass) + operating_system_info = hassio.get_os_info(self.hass) or {} system_info = await async_get_system_info(self.hass) integrations = [] diff --git a/homeassistant/components/android_ip_webcam/translations/ko.json b/homeassistant/components/android_ip_webcam/translations/ko.json new file mode 100644 index 00000000000..1f8dc9118f9 --- /dev/null +++ b/homeassistant/components/android_ip_webcam/translations/ko.json @@ -0,0 +1,21 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8", + "password": "\ube44\ubc00\ubc88\ud638", + "port": "\ud3ec\ud2b8", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/android_ip_webcam/translations/pt.json b/homeassistant/components/android_ip_webcam/translations/pt.json index 795ba71964f..426167bfc4f 100644 --- a/homeassistant/components/android_ip_webcam/translations/pt.json +++ b/homeassistant/components/android_ip_webcam/translations/pt.json @@ -4,13 +4,13 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida" }, "step": { "user": { "data": { - "host": "Servidor", + "host": "Endere\u00e7o", "password": "Palavra-passe", "port": "Porta", "username": "Nome de Utilizador" diff --git a/homeassistant/components/android_ip_webcam/translations/sk.json b/homeassistant/components/android_ip_webcam/translations/sk.json index 820da08a7b6..8a4e88a8b82 100644 --- a/homeassistant/components/android_ip_webcam/translations/sk.json +++ b/homeassistant/components/android_ip_webcam/translations/sk.json @@ -17,5 +17,11 @@ } } } + }, + "issues": { + "deprecated_yaml": { + "description": "Konfigur\u00e1cia webovej kamery Android IP pomocou YAML sa odstra\u0148uje. \n\n Va\u0161a existuj\u00faca konfigur\u00e1cia YAML bola importovan\u00e1 do pou\u017e\u00edvate\u013esk\u00e9ho rozhrania automaticky. \n\n Ak chcete tento probl\u00e9m vyrie\u0161i\u0165, odstr\u00e1\u0148te konfigur\u00e1ciu YAML webovej kamery Android IP zo s\u00faboru configuration.yaml a re\u0161tartujte aplik\u00e1ciu Home Assistant.", + "title": "Konfigur\u00e1cia webovej kamery Android IP Webcam YAML sa odstra\u0148uje" + } } } \ No newline at end of file diff --git a/homeassistant/components/androidtv/__init__.py b/homeassistant/components/androidtv/__init__.py index 6942ff7ffd0..c2d83ab05e8 100644 --- a/homeassistant/components/androidtv/__init__.py +++ b/homeassistant/components/androidtv/__init__.py @@ -79,7 +79,10 @@ def _setup_androidtv( else: # Use "pure-python-adb" (communicate with ADB server) signer = None - adb_log = f"using ADB server at {config[CONF_ADB_SERVER_IP]}:{config[CONF_ADB_SERVER_PORT]}" + adb_log = ( + "using ADB server at" + f" {config[CONF_ADB_SERVER_IP]}:{config[CONF_ADB_SERVER_PORT]}" + ) return adbkey, signer, adb_log diff --git a/homeassistant/components/androidtv/config_flow.py b/homeassistant/components/androidtv/config_flow.py index ea51ddedfdb..bac5a9aec6c 100644 --- a/homeassistant/components/androidtv/config_flow.py +++ b/homeassistant/components/androidtv/config_flow.py @@ -1,7 +1,6 @@ """Config flow to configure the Android TV integration.""" from __future__ import annotations -import json import logging import os from typing import Any @@ -18,6 +17,13 @@ from homeassistant.const import CONF_DEVICE_CLASS, CONF_HOST, CONF_PORT from homeassistant.core import callback from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers import config_validation as cv +from homeassistant.helpers.selector import ( + ObjectSelector, + SelectOptionDict, + SelectSelector, + SelectSelectorConfig, + SelectSelectorMode, +) from . import async_connect_androidtv, get_androidtv_mac from .const import ( @@ -224,13 +230,17 @@ class OptionsFlowHandler(OptionsFlowWithConfigEntry): """Return initial configuration form.""" apps_list = {k: f"{v} ({k})" if v else k for k, v in self._apps.items()} - apps = {APPS_NEW_ID: "Add new", **apps_list} + apps = [SelectOptionDict(value=APPS_NEW_ID, label="Add new")] + [ + SelectOptionDict(value=k, label=v) for k, v in apps_list.items() + ] rules = [RULES_NEW_ID] + list(self._state_det_rules) options = self.options data_schema = vol.Schema( { - vol.Optional(CONF_APPS): vol.In(apps), + vol.Optional(CONF_APPS): SelectSelector( + SelectSelectorConfig(options=apps, mode=SelectSelectorMode.DROPDOWN) + ), vol.Optional( CONF_GET_SOURCES, default=options.get(CONF_GET_SOURCES, DEFAULT_GET_SOURCES), @@ -257,7 +267,11 @@ class OptionsFlowHandler(OptionsFlowWithConfigEntry): "suggested_value": options.get(CONF_TURN_ON_COMMAND, "") }, ): str, - vol.Optional(CONF_STATE_DETECTION_RULES): vol.In(rules), + vol.Optional(CONF_STATE_DETECTION_RULES): SelectSelector( + SelectSelectorConfig( + options=rules, mode=SelectSelectorMode.DROPDOWN + ) + ), } ) @@ -318,8 +332,8 @@ class OptionsFlowHandler(OptionsFlowWithConfigEntry): if rule_id: if user_input.get(CONF_RULE_DELETE, False): self._state_det_rules.pop(rule_id) - elif str_det_rule := user_input.get(CONF_RULE_VALUES): - state_det_rule = _validate_state_det_rules(str_det_rule) + elif det_rule := user_input.get(CONF_RULE_VALUES): + state_det_rule = _validate_state_det_rules(det_rule) if state_det_rule is None: return self._async_rules_form( rule_id=self._conf_rule_id or RULES_NEW_ID, @@ -335,10 +349,11 @@ class OptionsFlowHandler(OptionsFlowWithConfigEntry): self, rule_id: str, default_id: str = "", errors: dict[str, str] | None = None ) -> FlowResult: """Return configuration form for detection rules.""" - state_det_rule = self._state_det_rules.get(rule_id) - str_det_rule = json.dumps(state_det_rule) if state_det_rule else "" - - rule_schema = {vol.Optional(CONF_RULE_VALUES, default=str_det_rule): str} + rule_schema = { + vol.Optional( + CONF_RULE_VALUES, default=self._state_det_rules.get(rule_id) + ): ObjectSelector() + } if rule_id == RULES_NEW_ID: data_schema = vol.Schema( {vol.Optional(CONF_RULE_ID, default=default_id): str, **rule_schema} @@ -358,14 +373,9 @@ class OptionsFlowHandler(OptionsFlowWithConfigEntry): ) -def _validate_state_det_rules(state_det_rules: str) -> list[Any] | None: +def _validate_state_det_rules(state_det_rules: Any) -> list[Any] | None: """Validate a string that contain state detection rules and return a dict.""" - try: - json_rules = json.loads(state_det_rules) - except ValueError: - _LOGGER.warning("Error loading state detection rules") - return None - + json_rules = state_det_rules if not isinstance(json_rules, list): json_rules = [json_rules] diff --git a/homeassistant/components/androidtv/manifest.json b/homeassistant/components/androidtv/manifest.json index a2883653869..c421db8b5e3 100644 --- a/homeassistant/components/androidtv/manifest.json +++ b/homeassistant/components/androidtv/manifest.json @@ -1,6 +1,7 @@ { "domain": "androidtv", "name": "Android TV", + "integration_type": "device", "documentation": "https://www.home-assistant.io/integrations/androidtv", "requirements": [ "adb-shell[async]==0.4.3", diff --git a/homeassistant/components/androidtv/media_player.py b/homeassistant/components/androidtv/media_player.py index 241ac12e780..572aa426105 100644 --- a/homeassistant/components/androidtv/media_player.py +++ b/homeassistant/components/androidtv/media_player.py @@ -179,13 +179,16 @@ def adb_decorator( except LockNotAcquiredException: # If the ADB lock could not be acquired, skip this command _LOGGER.info( - "ADB command not executed because the connection is currently in use" + "ADB command not executed because the connection is currently" + " in use" ) return None except self.exceptions as err: _LOGGER.error( - "Failed to execute an ADB command. ADB connection re-" - "establishing attempt in the next update. Error: %s", + ( + "Failed to execute an ADB command. ADB connection re-" + "establishing attempt in the next update. Error: %s" + ), err, ) await self.aftv.adb_close() @@ -427,7 +430,10 @@ class ADBDevice(MediaPlayerEntity): self._attr_extra_state_attributes[ATTR_ADB_RESPONSE] = output self.async_write_ha_state() - msg = f"Output from service '{SERVICE_LEARN_SENDEVENT}' from {self.entity_id}: '{output}'" + msg = ( + f"Output from service '{SERVICE_LEARN_SENDEVENT}' from" + f" {self.entity_id}: '{output}'" + ) persistent_notification.async_create( self.hass, msg, diff --git a/homeassistant/components/androidtv/translations/de.json b/homeassistant/components/androidtv/translations/de.json index 55a3b75d76c..701ca8136e9 100644 --- a/homeassistant/components/androidtv/translations/de.json +++ b/homeassistant/components/androidtv/translations/de.json @@ -45,8 +45,8 @@ "get_sources": "Abrufen der laufenden Anwendungen als Liste der Quellen", "screencap": "Bildschirmaufnahme als Albumcover verwenden", "state_detection_rules": "Regeln zur Statuserkennung konfigurieren", - "turn_off_command": "ADB-Shell-Abschaltbefehl (f\u00fcr Standard leer lassen)", - "turn_on_command": "ADB-Shell-Einschaltbefehl (f\u00fcr Standard leer lassen)" + "turn_off_command": "ADB-Shell Abschaltbefehl (f\u00fcr Standard leer lassen)", + "turn_on_command": "ADB-Shell Einschaltbefehl (f\u00fcr Standard leer lassen)" } }, "rules": { diff --git a/homeassistant/components/androidtv/translations/ko.json b/homeassistant/components/androidtv/translations/ko.json new file mode 100644 index 00000000000..f3e2dd078e8 --- /dev/null +++ b/homeassistant/components/androidtv/translations/ko.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_host": "\ud638\uc2a4\ud2b8 \uc774\ub984 \ub610\ub294 IP \uc8fc\uc18c\uac00 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8", + "port": "\ud3ec\ud2b8" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/androidtv/translations/pt.json b/homeassistant/components/androidtv/translations/pt.json index adc29b49b38..657148511c0 100644 --- a/homeassistant/components/androidtv/translations/pt.json +++ b/homeassistant/components/androidtv/translations/pt.json @@ -4,14 +4,14 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", - "invalid_host": "Nome de servidor ou endere\u00e7o IP inv\u00e1lido.", + "cannot_connect": "A liga\u00e7\u00e3o falhou", + "invalid_host": "Endere\u00e7o IP ou hostname inv\u00e1lido.", "unknown": "Erro inesperado" }, "step": { "user": { "data": { - "host": "Servidor", + "host": "Endere\u00e7o", "port": "Porta" } } diff --git a/homeassistant/components/androidtv/translations/sk.json b/homeassistant/components/androidtv/translations/sk.json index 509a1b4d0e6..a567405fcd1 100644 --- a/homeassistant/components/androidtv/translations/sk.json +++ b/homeassistant/components/androidtv/translations/sk.json @@ -25,6 +25,9 @@ } }, "options": { + "error": { + "invalid_det_rules": "Neplatn\u00e9 pravidl\u00e1 detekcie stavu" + }, "step": { "apps": { "data": { @@ -37,14 +40,23 @@ }, "init": { "data": { - "apps": "Konfigur\u00e1cia zoznamu aplik\u00e1ci\u00ed" + "apps": "Konfigur\u00e1cia zoznamu aplik\u00e1ci\u00ed", + "exclude_unnamed_apps": "Vyl\u00fa\u010dte aplik\u00e1cie s nezn\u00e1mym n\u00e1zvom zo zoznamu zdrojov", + "get_sources": "Z\u00edskanie spusten\u00fdch aplik\u00e1ci\u00ed ako zoznamu zdrojov", + "screencap": "Pou\u017eitie sn\u00edmania obrazovky pre obal albumu", + "state_detection_rules": "Konfigur\u00e1cia pravidiel detekcie stavu", + "turn_off_command": "Pr\u00edkaz na vypnutie prostredia ADB (pre predvolen\u00e9 nastavenie ponechajte pr\u00e1zdne)", + "turn_on_command": "Pr\u00edkaz na zapnutie prostredia ADB (pre predvolen\u00e9 nastavenie ponechajte pr\u00e1zdne)" } }, "rules": { "data": { + "rule_delete": "Za\u010diarknut\u00edm tohto pravidla odstr\u00e1nite", "rule_id": "ID aplik\u00e1cie", "rule_values": "Zoznam pravidiel zis\u0165ovania stavu (pozri dokument\u00e1ciu)" - } + }, + "description": "Konfigur\u00e1cia pravidla detekcie pre ID aplik\u00e1cie {rule_id}", + "title": "Konfigur\u00e1cia pravidiel detekcie stavu Android TV" } } } diff --git a/homeassistant/components/anthemav/translations/ko.json b/homeassistant/components/anthemav/translations/ko.json new file mode 100644 index 00000000000..3b82e6b0855 --- /dev/null +++ b/homeassistant/components/anthemav/translations/ko.json @@ -0,0 +1,18 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8", + "port": "\ud3ec\ud2b8" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/anthemav/translations/pt.json b/homeassistant/components/anthemav/translations/pt.json index fa5aa3de317..cfa13737af4 100644 --- a/homeassistant/components/anthemav/translations/pt.json +++ b/homeassistant/components/anthemav/translations/pt.json @@ -4,12 +4,12 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "user": { "data": { - "host": "Servidor", + "host": "Endere\u00e7o", "port": "Porta" } } diff --git a/homeassistant/components/apcupsd/__init__.py b/homeassistant/components/apcupsd/__init__.py index 1e76d070a48..e7088a09101 100644 --- a/homeassistant/components/apcupsd/__init__.py +++ b/homeassistant/components/apcupsd/__init__.py @@ -6,13 +6,11 @@ import logging from typing import Any, Final from apcaccess import status -import voluptuous as vol -from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry +from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_HOST, CONF_PORT, Platform from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.typing import ConfigType from homeassistant.util import Throttle _LOGGER = logging.getLogger(__name__) @@ -22,51 +20,7 @@ VALUE_ONLINE: Final = 8 PLATFORMS: Final = (Platform.BINARY_SENSOR, Platform.SENSOR) MIN_TIME_BETWEEN_UPDATES: Final = timedelta(seconds=60) - -CONFIG_SCHEMA = vol.Schema( - { - DOMAIN: vol.Schema( - { - vol.Optional(CONF_HOST, default="localhost"): cv.string, - vol.Optional(CONF_PORT, default=3551): cv.port, - } - ) - }, - extra=vol.ALLOW_EXTRA, -) - - -async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: - """Set up integration from legacy YAML configurations.""" - conf = config.get(DOMAIN) - if conf is None: - return True - - # We only import configs from YAML if it hasn't been imported. If there is a config - # entry marked with SOURCE_IMPORT, it means the YAML config has been imported. - for entry in hass.config_entries.async_entries(DOMAIN): - if entry.source == SOURCE_IMPORT: - return True - - # Since the YAML configuration for apcupsd consists of two parts: - # apcupsd: - # host: xxx - # port: xxx - # sensor: - # - platform: apcupsd - # resource: - # - resource_1 - # - resource_2 - # - ... - # Here at the integration set up we do not have the entire information to be - # imported to config flow yet. So we temporarily store the configuration to - # hass.data[DOMAIN] under a special entry_id SOURCE_IMPORT (which shouldn't - # conflict with other entry ids). Later when the sensor platform setup is - # called we gather the resources information and from there we start the - # actual config entry imports. - hass.data.setdefault(DOMAIN, {}) - hass.data[DOMAIN][SOURCE_IMPORT] = conf - return True +CONFIG_SCHEMA = cv.removed(DOMAIN, raise_if_present=False) async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: diff --git a/homeassistant/components/apcupsd/config_flow.py b/homeassistant/components/apcupsd/config_flow.py index a191cf77117..f1ce20694c7 100644 --- a/homeassistant/components/apcupsd/config_flow.py +++ b/homeassistant/components/apcupsd/config_flow.py @@ -78,9 +78,3 @@ class ConfigFlowHandler(ConfigFlow, domain=DOMAIN): title=title, data=user_input, ) - - async def async_step_import(self, conf: dict[str, Any]) -> FlowResult: - """Import a configuration from yaml configuration.""" - # If we are importing from YAML configuration, user_input could contain a - # CONF_RESOURCES with a list of resources (sensors) to be enabled. - return await self.async_step_user(user_input=conf) diff --git a/homeassistant/components/apcupsd/sensor.py b/homeassistant/components/apcupsd/sensor.py index b55f672264d..08464285853 100644 --- a/homeassistant/components/apcupsd/sensor.py +++ b/homeassistant/components/apcupsd/sensor.py @@ -4,35 +4,26 @@ from __future__ import annotations import logging from apcaccess.status import ALL_UNITS -import voluptuous as vol from homeassistant.components.sensor import ( - PLATFORM_SCHEMA, SensorDeviceClass, SensorEntity, SensorEntityDescription, ) -from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry +from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - CONF_HOST, - CONF_PORT, - CONF_RESOURCES, - ELECTRIC_CURRENT_AMPERE, - ELECTRIC_POTENTIAL_VOLT, - FREQUENCY_HERTZ, PERCENTAGE, - POWER_VOLT_AMPERE, - POWER_WATT, - TEMP_CELSIUS, - TIME_MINUTES, - TIME_SECONDS, + UnitOfApparentPower, + UnitOfElectricCurrent, + UnitOfElectricPotential, + UnitOfFrequency, + UnitOfPower, + UnitOfTemperature, + UnitOfTime, ) from homeassistant.core import HomeAssistant -import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue -from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from . import DOMAIN, APCUPSdData @@ -79,8 +70,8 @@ SENSORS: dict[str, SensorEntityDescription] = { "battv": SensorEntityDescription( key="battv", name="UPS Battery Voltage", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, - icon="mdi:flash", + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + device_class=SensorDeviceClass.VOLTAGE, ), "bcharge": SensorEntityDescription( key="bcharge", @@ -151,8 +142,8 @@ SENSORS: dict[str, SensorEntityDescription] = { "hitrans": SensorEntityDescription( key="hitrans", name="UPS Transfer High", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, - icon="mdi:flash", + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + device_class=SensorDeviceClass.VOLTAGE, ), "hostname": SensorEntityDescription( key="hostname", @@ -169,7 +160,7 @@ SENSORS: dict[str, SensorEntityDescription] = { "itemp": SensorEntityDescription( key="itemp", name="UPS Internal Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, ), "laststest": SensorEntityDescription( @@ -191,14 +182,14 @@ SENSORS: dict[str, SensorEntityDescription] = { "linefreq": SensorEntityDescription( key="linefreq", name="UPS Line Frequency", - native_unit_of_measurement=FREQUENCY_HERTZ, - icon="mdi:information-outline", + native_unit_of_measurement=UnitOfFrequency.HERTZ, + device_class=SensorDeviceClass.FREQUENCY, ), "linev": SensorEntityDescription( key="linev", name="UPS Input Voltage", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, - icon="mdi:flash", + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + device_class=SensorDeviceClass.VOLTAGE, ), "loadpct": SensorEntityDescription( key="loadpct", @@ -215,8 +206,8 @@ SENSORS: dict[str, SensorEntityDescription] = { "lotrans": SensorEntityDescription( key="lotrans", name="UPS Transfer Low", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, - icon="mdi:flash", + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + device_class=SensorDeviceClass.VOLTAGE, ), "mandate": SensorEntityDescription( key="mandate", @@ -232,8 +223,8 @@ SENSORS: dict[str, SensorEntityDescription] = { "maxlinev": SensorEntityDescription( key="maxlinev", name="UPS Input Voltage High", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, - icon="mdi:flash", + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + device_class=SensorDeviceClass.VOLTAGE, ), "maxtime": SensorEntityDescription( key="maxtime", @@ -249,8 +240,8 @@ SENSORS: dict[str, SensorEntityDescription] = { "minlinev": SensorEntityDescription( key="minlinev", name="UPS Input Voltage Low", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, - icon="mdi:flash", + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + device_class=SensorDeviceClass.VOLTAGE, ), "mintimel": SensorEntityDescription( key="mintimel", @@ -266,32 +257,32 @@ SENSORS: dict[str, SensorEntityDescription] = { "nombattv": SensorEntityDescription( key="nombattv", name="UPS Battery Nominal Voltage", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, - icon="mdi:flash", + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + device_class=SensorDeviceClass.VOLTAGE, ), "nominv": SensorEntityDescription( key="nominv", name="UPS Nominal Input Voltage", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, - icon="mdi:flash", + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + device_class=SensorDeviceClass.VOLTAGE, ), "nomoutv": SensorEntityDescription( key="nomoutv", name="UPS Nominal Output Voltage", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, - icon="mdi:flash", + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + device_class=SensorDeviceClass.VOLTAGE, ), "nompower": SensorEntityDescription( key="nompower", name="UPS Nominal Output Power", - native_unit_of_measurement=POWER_WATT, - icon="mdi:flash", + native_unit_of_measurement=UnitOfPower.WATT, + device_class=SensorDeviceClass.POWER, ), "nomapnt": SensorEntityDescription( key="nomapnt", name="UPS Nominal Apparent Power", - native_unit_of_measurement=POWER_VOLT_AMPERE, - icon="mdi:flash", + native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE, + device_class=SensorDeviceClass.APPARENT_POWER, ), "numxfers": SensorEntityDescription( key="numxfers", @@ -301,14 +292,14 @@ SENSORS: dict[str, SensorEntityDescription] = { "outcurnt": SensorEntityDescription( key="outcurnt", name="UPS Output Current", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, - icon="mdi:flash", + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, + device_class=SensorDeviceClass.CURRENT, ), "outputv": SensorEntityDescription( key="outputv", name="UPS Output Voltage", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, - icon="mdi:flash", + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + device_class=SensorDeviceClass.VOLTAGE, ), "reg1": SensorEntityDescription( key="reg1", @@ -416,89 +407,20 @@ SENSORS: dict[str, SensorEntityDescription] = { ), } -SPECIFIC_UNITS = {"ITEMP": TEMP_CELSIUS} +SPECIFIC_UNITS = {"ITEMP": UnitOfTemperature.CELSIUS} INFERRED_UNITS = { - " Minutes": TIME_MINUTES, - " Seconds": TIME_SECONDS, + " Minutes": UnitOfTime.MINUTES, + " Seconds": UnitOfTime.SECONDS, " Percent": PERCENTAGE, - " Volts": ELECTRIC_POTENTIAL_VOLT, - " Ampere": ELECTRIC_CURRENT_AMPERE, - " Volt-Ampere": POWER_VOLT_AMPERE, - " Watts": POWER_WATT, - " Hz": FREQUENCY_HERTZ, - " C": TEMP_CELSIUS, + " Volts": UnitOfElectricPotential.VOLT, + " Ampere": UnitOfElectricCurrent.AMPERE, + " Volt-Ampere": UnitOfApparentPower.VOLT_AMPERE, + " Watts": UnitOfPower.WATT, + " Hz": UnitOfFrequency.HERTZ, + " C": UnitOfTemperature.CELSIUS, " Percent Load Capacity": PERCENTAGE, } -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( - { - vol.Required(CONF_RESOURCES, default=[]): vol.All( - cv.ensure_list, [vol.In([desc.key for desc in SENSORS.values()])] - ) - } -) - - -async def async_setup_platform( - hass: HomeAssistant, - config: ConfigType, - add_entities: AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> None: - """Import the configurations from YAML to config flows.""" - # We only import configs from YAML if it hasn't been imported. If there is a config - # entry marked with SOURCE_IMPORT, it means the YAML config has been imported. - for entry in hass.config_entries.async_entries(DOMAIN): - if entry.source == SOURCE_IMPORT: - return - - # This is the second step of YAML config imports, first see the comments in - # async_setup() of __init__.py to get an idea of how we import the YAML configs. - # Here we retrieve the partial YAML configs from the special entry id. - conf = hass.data[DOMAIN].get(SOURCE_IMPORT) - if conf is None: - return - - _LOGGER.warning( - "Configuration of apcupsd in YAML is deprecated and will be " - "removed in Home Assistant 2022.12; Your existing configuration " - "has been imported into the UI automatically and can be safely removed " - "from your configuration.yaml file" - ) - - async_create_issue( - hass, - DOMAIN, - "deprecated_yaml", - breaks_in_ha_version="2022.12.0", - is_fixable=False, - severity=IssueSeverity.WARNING, - translation_key="deprecated_yaml", - ) - - # Remove the artificial entry since it's no longer needed. - hass.data[DOMAIN].pop(SOURCE_IMPORT) - - # Our config flow supports CONF_RESOURCES and will properly import it to disable - # entities not listed in CONF_RESOURCES by default. Note that this designed to - # support YAML config import only (i.e., not shown in UI during setup). - conf[CONF_RESOURCES] = config[CONF_RESOURCES] - - _LOGGER.debug( - "YAML configurations loaded with host %s, port %s and resources %s", - conf[CONF_HOST], - conf[CONF_PORT], - conf[CONF_RESOURCES], - ) - - hass.async_create_task( - hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_IMPORT}, data=conf - ) - ) - - return - async def async_setup_entry( hass: HomeAssistant, @@ -512,28 +434,13 @@ async def async_setup_entry( # lower cases throughout this integration. available_resources: set[str] = {k.lower() for k, _ in data_service.status.items()} - # We use user-specified resources from imported YAML config (if available) to - # determine whether to enable the entity by default. Here, we first collect the - # specified resources - specified_resources = None - if (resources := config_entry.data.get(CONF_RESOURCES)) is not None: - assert isinstance(resources, list) - specified_resources = set(resources) - entities = [] for resource in available_resources: if resource not in SENSORS: _LOGGER.warning("Invalid resource from APCUPSd: %s", resource.upper()) continue - # To avoid breaking changes, we disable sensors not specified in resources. - description = SENSORS[resource] - enabled_by_default = description.entity_registry_enabled_default - if specified_resources is not None: - enabled_by_default = resource in specified_resources - - entity = APCUPSdSensor(data_service, description, enabled_by_default) - entities.append(entity) + entities.append(APCUPSdSensor(data_service, SENSORS[resource])) async_add_entities(entities, update_before_add=True) @@ -558,7 +465,6 @@ class APCUPSdSensor(SensorEntity): self, data_service: APCUPSdData, description: SensorEntityDescription, - enabled_by_default: bool, ) -> None: """Initialize the sensor.""" # Set up unique id and device info if serial number is available. @@ -573,7 +479,6 @@ class APCUPSdSensor(SensorEntity): ) self.entity_description = description - self._attr_entity_registry_enabled_default = enabled_by_default self._data_service = data_service def update(self) -> None: diff --git a/homeassistant/components/apcupsd/translations/de.json b/homeassistant/components/apcupsd/translations/de.json index cc410a8c84c..c72ee52a1fe 100644 --- a/homeassistant/components/apcupsd/translations/de.json +++ b/homeassistant/components/apcupsd/translations/de.json @@ -13,7 +13,7 @@ "host": "Host", "port": "Port" }, - "description": "Gib den Host und den Port ein, auf dem das apcupsd-NIS bereitgestellt wird." + "description": "Gib den Host und den Port ein, auf dem das apcupsd NIS bereitgestellt wird." } } }, diff --git a/homeassistant/components/apcupsd/translations/ko.json b/homeassistant/components/apcupsd/translations/ko.json new file mode 100644 index 00000000000..3c0105552a7 --- /dev/null +++ b/homeassistant/components/apcupsd/translations/ko.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "no_status": "\ud638\uc2a4\ud2b8 \uc5d0\uc11c \ubcf4\uace0\ub41c \uc0c1\ud0dc\uac00 \uc5c6\uc2b5\ub2c8\ub2e4." + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8", + "port": "\ud3ec\ud2b8" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/apcupsd/translations/pt.json b/homeassistant/components/apcupsd/translations/pt.json new file mode 100644 index 00000000000..ca5349a28e1 --- /dev/null +++ b/homeassistant/components/apcupsd/translations/pt.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", + "no_status": "O dispositivo n\u00e3o reportou o seu estado" + }, + "error": { + "cannot_connect": "A liga\u00e7\u00e3o falhou" + }, + "step": { + "user": { + "data": { + "host": "Endere\u00e7o", + "port": "Porta" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/apcupsd/translations/sk.json b/homeassistant/components/apcupsd/translations/sk.json index c441c50a3a9..c502d9fefa7 100644 --- a/homeassistant/components/apcupsd/translations/sk.json +++ b/homeassistant/components/apcupsd/translations/sk.json @@ -12,8 +12,15 @@ "data": { "host": "Hostite\u013e", "port": "Port" - } + }, + "description": "Zadajte hostite\u013ea a port, na ktorom sa obsluhuje apcupsd NIS." } } + }, + "issues": { + "deprecated_yaml": { + "description": "Konfigur\u00e1cia APC UPS Daemon pomocou YAML sa odstra\u0148uje. \n\n Va\u0161a existuj\u00faca konfigur\u00e1cia YAML bola importovan\u00e1 do pou\u017e\u00edvate\u013esk\u00e9ho rozhrania automaticky. \n\n Odstr\u00e1\u0148te konfigur\u00e1ciu APC UPS Daemon YAML zo s\u00faboru configuration.yaml a re\u0161tartujte Home Assistant, aby ste tento probl\u00e9m vyrie\u0161ili.", + "title": "Konfigur\u00e1cia APC UPS Daemon YAML sa odstra\u0148uje" + } } } \ No newline at end of file diff --git a/homeassistant/components/apple_tv/__init__.py b/homeassistant/components/apple_tv/__init__.py index 5d9c1cde785..2c9ad84ac42 100644 --- a/homeassistant/components/apple_tv/__init__.py +++ b/homeassistant/components/apple_tv/__init__.py @@ -309,7 +309,8 @@ class AppleTVManager: missing_protocols_str = ", ".join(missing_protocols) if raise_missing_credentials: raise ConfigEntryNotReady( - f"Protocol(s) {missing_protocols_str} not yet found for {name}, waiting for discovery." + f"Protocol(s) {missing_protocols_str} not yet found for {name}," + " waiting for discovery." ) _LOGGER.info( "Protocol(s) %s not yet found for %s, trying later", diff --git a/homeassistant/components/apple_tv/strings.json b/homeassistant/components/apple_tv/strings.json index e25c596f786..1420c0ffefc 100644 --- a/homeassistant/components/apple_tv/strings.json +++ b/homeassistant/components/apple_tv/strings.json @@ -3,7 +3,7 @@ "flow_title": "{name} ({type})", "step": { "user": { - "title": "Setup a new Apple TV", + "title": "Set up a new Apple TV", "description": "Start by entering the device name (e.g. Kitchen or Bedroom) or IP address of the Apple TV you want to add.\n\nIf you cannot see your device or experience any issues, try specifying the device IP address.", "data": { "device_input": "Device" diff --git a/homeassistant/components/apple_tv/translations/ca.json b/homeassistant/components/apple_tv/translations/ca.json index ca0ca27bcfe..b76ee5fbaec 100644 --- a/homeassistant/components/apple_tv/translations/ca.json +++ b/homeassistant/components/apple_tv/translations/ca.json @@ -57,7 +57,7 @@ "device_input": "Dispositiu" }, "description": "Comen\u00e7a introduint el nom del dispositiu (per exemple, cuina o dormitori) o l'adre\u00e7a IP de l'Apple TV que vulguis afegir.\n\n Si no veus el teu dispositiu o tens problemes, prova d'especificar l'adre\u00e7a IP del dispositiu.", - "title": "Configuraci\u00f3 d'una nova Apple TV" + "title": "Configuraci\u00f3 de nova Apple TV" } } }, diff --git a/homeassistant/components/apple_tv/translations/de.json b/homeassistant/components/apple_tv/translations/de.json index 1a4155977ad..bc5eb647b3d 100644 --- a/homeassistant/components/apple_tv/translations/de.json +++ b/homeassistant/components/apple_tv/translations/de.json @@ -56,7 +56,7 @@ "data": { "device_input": "Ger\u00e4t" }, - "description": "Gib zun\u00e4chst den Ger\u00e4tenamen (z. B. K\u00fcche oder Schlafzimmer) oder die IP-Adresse des Apple TV ein, den du hinzuf\u00fcgen m\u00f6chtest.\n\nWenn du dein Ger\u00e4t nicht sehen kannst oder Probleme auftreten, versuche die IP-Adresse des Ger\u00e4ts einzugeben.", + "description": "Gib zun\u00e4chst den Ger\u00e4tenamen (z.B. K\u00fcche oder Schlafzimmer) oder die IP-Adresse des Apple TV ein, den du hinzuf\u00fcgen m\u00f6chtest.\n\nWenn du dein Ger\u00e4t nicht sehen kannst oder Probleme auftreten, versuche die IP-Adresse des Ger\u00e4ts einzugeben.", "title": "Neuen Apple TV einrichten" } } diff --git a/homeassistant/components/apple_tv/translations/en.json b/homeassistant/components/apple_tv/translations/en.json index f455d590d79..b13313700f7 100644 --- a/homeassistant/components/apple_tv/translations/en.json +++ b/homeassistant/components/apple_tv/translations/en.json @@ -57,7 +57,7 @@ "device_input": "Device" }, "description": "Start by entering the device name (e.g. Kitchen or Bedroom) or IP address of the Apple TV you want to add.\n\nIf you cannot see your device or experience any issues, try specifying the device IP address.", - "title": "Setup a new Apple TV" + "title": "Set up a new Apple TV" } } }, diff --git a/homeassistant/components/apple_tv/translations/ko.json b/homeassistant/components/apple_tv/translations/ko.json index 9bddc31c56f..cf2592252d9 100644 --- a/homeassistant/components/apple_tv/translations/ko.json +++ b/homeassistant/components/apple_tv/translations/ko.json @@ -1,11 +1,13 @@ { "config": { "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", "backoff": "\uae30\uae30\uac00 \ud604\uc7ac \ud398\uc5b4\ub9c1 \uc694\uccad\uc744 \uc218\ub77d\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4(\uc798\ubabb\ub41c PIN \ucf54\ub4dc\ub97c \ub108\ubb34 \ub9ce\uc774 \uc785\ub825\ud588\uc744 \uc218 \uc788\uc74c). \ub098\uc911\uc5d0 \ub2e4\uc2dc \uc2dc\ub3c4\ud574\uc8fc\uc138\uc694.", "device_did_not_pair": "\uae30\uae30\uc5d0\uc11c \ud398\uc5b4\ub9c1 \ud504\ub85c\uc138\uc2a4\ub97c \uc644\ub8cc\ud558\ub824\uace0 \uc2dc\ub3c4\ud558\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4.", "inconsistent_device": "\uc7a5\uce58\uac80\uc0c9 \uc911\uc5d0 \ud574\ub2f9 \ud504\ub85c\ud1a0\ucf5c\uc744 \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4. \uc774\ub294 \uc77c\ubc18\uc801\uc73c\ub85c \uba40\ud2f0\uce90\uc2a4\ud2b8 DNS(Zeroconf)\uc5d0 \ubb38\uc81c\uac00 \uc788\uc74c\uc744 \ub098\ud0c0\ub0c5\ub2c8\ub2e4. \uc7a5\uce58\ub97c \ub2e4\uc2dc \ucd94\uac00\ud574 \ubcf4\uc2ed\uc2dc\uc624.", "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4", "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" }, "error": { diff --git a/homeassistant/components/apple_tv/translations/lb.json b/homeassistant/components/apple_tv/translations/lb.json index 0950ff8235a..eb2fdd7a350 100644 --- a/homeassistant/components/apple_tv/translations/lb.json +++ b/homeassistant/components/apple_tv/translations/lb.json @@ -23,9 +23,9 @@ }, "pair_with_pin": { "data": { - "pin": "PIN Code" + "pin": "PIN-Code" }, - "description": "Kopplung ass n\u00e9ideg fir de `{protocol}` Protokoll. G\u00ebff de PIN code un deen um Ecran ugewise g\u00ebtt. Nullen op der 1ter Plaatz ginn ewechgelooss, dh g\u00ebff 123 wann de gewise Code 0123 ass.", + "description": "Fir de Protokoll `{protocol}` ass eng Kopplung n\u00e9ideg. G\u00ebff de PIN-Code, deen um Ecran gewise gett. Nullen um Ufank musse wech gelooss ginn, d.h. g\u00ebff '123' an, wann den ugewisene Code '0123' ass.", "title": "Kopplung" }, "reconfigure": { diff --git a/homeassistant/components/apple_tv/translations/no.json b/homeassistant/components/apple_tv/translations/no.json index d0250d31dfa..45b8feaa070 100644 --- a/homeassistant/components/apple_tv/translations/no.json +++ b/homeassistant/components/apple_tv/translations/no.json @@ -57,7 +57,7 @@ "device_input": "Enhet" }, "description": "Start med \u00e5 skrive inn enhetsnavnet (f.eks. Kj\u00f8kken eller soverom) eller IP-adressen til Apple TV-en du vil legge til. \n\n Hvis du ikke kan se enheten eller opplever problemer, pr\u00f8v \u00e5 spesifisere enhetens IP-adresse.", - "title": "Konfigurere en ny Apple TV" + "title": "Sett opp en ny Apple TV" } } }, diff --git a/homeassistant/components/apple_tv/translations/sk.json b/homeassistant/components/apple_tv/translations/sk.json index 18961885448..8eb5d21c120 100644 --- a/homeassistant/components/apple_tv/translations/sk.json +++ b/homeassistant/components/apple_tv/translations/sk.json @@ -3,6 +3,10 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "backoff": "Zariadenie moment\u00e1lne neprij\u00edma po\u017eiadavky na sp\u00e1rovanie (mo\u017eno ste pr\u00edli\u0161 ve\u013eakr\u00e1t zadali neplatn\u00fd PIN k\u00f3d), sk\u00faste to znova nesk\u00f4r.", + "device_did_not_pair": "Zo zariadenia nebol vykonan\u00fd \u017eiadny pokus o dokon\u010denie procesu p\u00e1rovania.", + "device_not_found": "Zariadenie sa po\u010das zis\u0165ovania nena\u0161lo, sk\u00faste ho prida\u0165 znova.", + "inconsistent_device": "Po\u010das objavovania sa nena\u0161li o\u010dak\u00e1van\u00e9 protokoly. Zvy\u010dajne to znamen\u00e1 probl\u00e9m s multicast DNS (Zeroconf). Sk\u00faste prida\u0165 zariadenie znova.", "ipv6_not_supported": "IPv6 nie je podporovan\u00e9", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", @@ -18,6 +22,7 @@ "flow_title": "{name} ({type})", "step": { "confirm": { + "description": "Chyst\u00e1te sa prida\u0165 `{name}` typu `{type}` do Home Assistant. \n\n **Na dokon\u010denie procesu mo\u017eno budete musie\u0165 zada\u0165 viacero k\u00f3dov PIN.** \n\nUpozor\u0148ujeme, \u017ee pomocou tejto integr\u00e1cie *nebudete* m\u00f4c\u0165 vypn\u00fa\u0165 v\u00e1\u0161 Apple TV. Vypne sa iba prehr\u00e1va\u010d m\u00e9di\u00ed v aplik\u00e1cii Home Assistant!", "title": "Potvr\u010fte pridanie Apple TV" }, "pair_no_pin": { @@ -28,6 +33,7 @@ "data": { "pin": "PIN k\u00f3d" }, + "description": "U protokolu `{protocol}` je vy\u017eadovan\u00e9 p\u00e1rovanie. Zadajte pros\u00edm PIN k\u00f3d zobrazen\u00fd na obrazovke. \u00davodn\u00e9 nuly musia b\u00fd\u0165 vynechan\u00e9, tj. zadajte 123, pokia\u013e je zobrazen\u00fd k\u00f3d 0123.", "title": "P\u00e1rovanie" }, "password": { @@ -35,6 +41,7 @@ "title": "Vy\u017eaduje sa heslo" }, "protocol_disabled": { + "description": "P\u00e1rovanie sa vy\u017eaduje pre `{protocol}`, ale na zariaden\u00ed je zak\u00e1zan\u00e9. Skontrolujte potenci\u00e1lne obmedzenia pr\u00edstupu (napr. povo\u013ete pripojenie v\u0161etk\u00fdch zariaden\u00ed v lok\u00e1lnej sieti) na zariaden\u00ed. \n\nM\u00f4\u017eete pokra\u010dova\u0165 bez sp\u00e1rovania tohto protokolu, ale niektor\u00e9 funkcie bud\u00fa obmedzen\u00e9.", "title": "P\u00e1rovanie nie je mo\u017en\u00e9" }, "reconfigure": { @@ -49,6 +56,7 @@ "data": { "device_input": "Zariadenie" }, + "description": "Za\u010dnite zadan\u00edm n\u00e1zvu zariadenia (napr. Kuchy\u0148a alebo sp\u00e1l\u0148a) alebo IP adresy Apple TV, ktor\u00fa chcete prida\u0165. Pokia\u013e bola vo va\u0161ej sieti automaticky n\u00e1jden\u00e9 niektor\u00e9 zariadenia, s\u00fa uveden\u00e9 ni\u017e\u0161ie. \n\n Pokia\u013e nevid\u00edte svoje zariadenie alebo nastali nejak\u00e9 probl\u00e9my, sk\u00faste zada\u0165 IP adresu zariadenia. \n\n {devices}", "title": "Nastavte nov\u00fa Apple TV" } } diff --git a/homeassistant/components/application_credentials/__init__.py b/homeassistant/components/application_credentials/__init__.py index 811a637b4ef..33521b3d066 100644 --- a/homeassistant/components/application_credentials/__init__.py +++ b/homeassistant/components/application_credentials/__init__.py @@ -50,8 +50,8 @@ DEFAULT_IMPORT_NAME = "Import from configuration.yaml" CREATE_FIELDS = { vol.Required(CONF_DOMAIN): cv.string, - vol.Required(CONF_CLIENT_ID): cv.string, - vol.Required(CONF_CLIENT_SECRET): cv.string, + vol.Required(CONF_CLIENT_ID): vol.All(cv.string, vol.Strip), + vol.Required(CONF_CLIENT_SECRET): vol.All(cv.string, vol.Strip), vol.Optional(CONF_AUTH_DOMAIN): cv.string, vol.Optional(CONF_NAME): cv.string, } @@ -302,8 +302,8 @@ async def _get_platform( platform, "async_get_auth_implementation" ): raise ValueError( - f"Integration '{integration_domain}' platform {DOMAIN} did not " - f"implement 'async_get_authorization_server' or 'async_get_auth_implementation'" + f"Integration '{integration_domain}' platform {DOMAIN} did not implement" + " 'async_get_authorization_server' or 'async_get_auth_implementation'" ) return platform diff --git a/homeassistant/components/application_credentials/translations/sk.json b/homeassistant/components/application_credentials/translations/sk.json new file mode 100644 index 00000000000..1dac3d7d15b --- /dev/null +++ b/homeassistant/components/application_credentials/translations/sk.json @@ -0,0 +1,3 @@ +{ + "title": "Prihla\u0161ovacoe \u00fadaje aplik\u00e1cie" +} \ No newline at end of file diff --git a/homeassistant/components/apprise/manifest.json b/homeassistant/components/apprise/manifest.json index 984ecea50d7..4475f68cd3b 100644 --- a/homeassistant/components/apprise/manifest.json +++ b/homeassistant/components/apprise/manifest.json @@ -2,7 +2,7 @@ "domain": "apprise", "name": "Apprise", "documentation": "https://www.home-assistant.io/integrations/apprise", - "requirements": ["apprise==1.2.0"], + "requirements": ["apprise==1.2.1"], "codeowners": ["@caronc"], "iot_class": "cloud_push", "loggers": ["apprise"] diff --git a/homeassistant/components/aqualogic/sensor.py b/homeassistant/components/aqualogic/sensor.py index e8abc3bae62..955293a938e 100644 --- a/homeassistant/components/aqualogic/sensor.py +++ b/homeassistant/components/aqualogic/sensor.py @@ -14,9 +14,8 @@ from homeassistant.components.sensor import ( from homeassistant.const import ( CONF_MONITORED_CONDITIONS, PERCENTAGE, - POWER_WATT, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, + UnitOfPower, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant, callback import homeassistant.helpers.config_validation as cv @@ -40,23 +39,23 @@ SENSOR_TYPES: tuple[AquaLogicSensorEntityDescription, ...] = ( AquaLogicSensorEntityDescription( key="air_temp", name="Air Temperature", - unit_metric=TEMP_CELSIUS, - unit_imperial=TEMP_FAHRENHEIT, + unit_metric=UnitOfTemperature.CELSIUS, + unit_imperial=UnitOfTemperature.FAHRENHEIT, device_class=SensorDeviceClass.TEMPERATURE, ), AquaLogicSensorEntityDescription( key="pool_temp", name="Pool Temperature", - unit_metric=TEMP_CELSIUS, - unit_imperial=TEMP_FAHRENHEIT, + unit_metric=UnitOfTemperature.CELSIUS, + unit_imperial=UnitOfTemperature.FAHRENHEIT, icon="mdi:oil-temperature", device_class=SensorDeviceClass.TEMPERATURE, ), AquaLogicSensorEntityDescription( key="spa_temp", name="Spa Temperature", - unit_metric=TEMP_CELSIUS, - unit_imperial=TEMP_FAHRENHEIT, + unit_metric=UnitOfTemperature.CELSIUS, + unit_imperial=UnitOfTemperature.FAHRENHEIT, icon="mdi:oil-temperature", device_class=SensorDeviceClass.TEMPERATURE, ), @@ -91,9 +90,9 @@ SENSOR_TYPES: tuple[AquaLogicSensorEntityDescription, ...] = ( AquaLogicSensorEntityDescription( key="pump_power", name="Pump Power", - unit_metric=POWER_WATT, - unit_imperial=POWER_WATT, - icon="mdi:gauge", + unit_metric=UnitOfPower.WATT, + unit_imperial=UnitOfPower.WATT, + device_class=SensorDeviceClass.POWER, ), AquaLogicSensorEntityDescription( key="status", diff --git a/homeassistant/components/aranet/sensor.py b/homeassistant/components/aranet/sensor.py index 6d8c7feb0ac..512748fef8d 100644 --- a/homeassistant/components/aranet/sensor.py +++ b/homeassistant/components/aranet/sensor.py @@ -25,9 +25,9 @@ from homeassistant.const import ( ATTR_SW_VERSION, CONCENTRATION_PARTS_PER_MILLION, PERCENTAGE, - PRESSURE_HPA, - TEMP_CELSIUS, - TIME_SECONDS, + UnitOfPressure, + UnitOfTemperature, + UnitOfTime, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo @@ -40,7 +40,7 @@ SENSOR_DESCRIPTIONS = { key="temperature", name="Temperature", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, ), "humidity": SensorEntityDescription( @@ -54,7 +54,7 @@ SENSOR_DESCRIPTIONS = { key="pressure", name="Pressure", device_class=SensorDeviceClass.PRESSURE, - native_unit_of_measurement=PRESSURE_HPA, + native_unit_of_measurement=UnitOfPressure.HPA, state_class=SensorStateClass.MEASUREMENT, ), "co2": SensorEntityDescription( @@ -75,8 +75,10 @@ SENSOR_DESCRIPTIONS = { key="update_interval", name="Update Interval", device_class=SensorDeviceClass.DURATION, - native_unit_of_measurement=TIME_SECONDS, + native_unit_of_measurement=UnitOfTime.SECONDS, state_class=SensorStateClass.MEASUREMENT, + # The interval setting is not a generally useful entity for most users. + entity_registry_enabled_default=False, ), } diff --git a/homeassistant/components/aranet/translations/cs.json b/homeassistant/components/aranet/translations/cs.json new file mode 100644 index 00000000000..e1bf8e7f45f --- /dev/null +++ b/homeassistant/components/aranet/translations/cs.json @@ -0,0 +1,7 @@ +{ + "config": { + "error": { + "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aranet/translations/en.json b/homeassistant/components/aranet/translations/en.json index 00d5aacf11c..06b08cfe1e1 100644 --- a/homeassistant/components/aranet/translations/en.json +++ b/homeassistant/components/aranet/translations/en.json @@ -12,13 +12,13 @@ "flow_title": "{name}", "step": { "bluetooth_confirm": { - "description": "Do you want to setup {name}?" + "description": "Do you want to set up {name}?" }, "user": { "data": { "address": "Device" }, - "description": "Choose a device to setup" + "description": "Choose a device to set up" } } } diff --git a/homeassistant/components/aranet/translations/it.json b/homeassistant/components/aranet/translations/it.json index 372e6266b5b..82cd53e5aa6 100644 --- a/homeassistant/components/aranet/translations/it.json +++ b/homeassistant/components/aranet/translations/it.json @@ -18,7 +18,7 @@ "data": { "address": "Dispositivo" }, - "description": "Seleziona un dispositivo da configurare" + "description": "Scegli un dispositivo da configurare" } } } diff --git a/homeassistant/components/aranet/translations/ko.json b/homeassistant/components/aranet/translations/ko.json new file mode 100644 index 00000000000..057a689342a --- /dev/null +++ b/homeassistant/components/aranet/translations/ko.json @@ -0,0 +1,10 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aranet/translations/no.json b/homeassistant/components/aranet/translations/no.json index 8e4a732972a..d5bd5229395 100644 --- a/homeassistant/components/aranet/translations/no.json +++ b/homeassistant/components/aranet/translations/no.json @@ -12,7 +12,7 @@ "flow_title": "{name}", "step": { "bluetooth_confirm": { - "description": "Vil du konfigurere {name}?" + "description": "Vil du sette opp {name} ?" }, "user": { "data": { diff --git a/homeassistant/components/aranet/translations/pt.json b/homeassistant/components/aranet/translations/pt.json new file mode 100644 index 00000000000..ae100e45845 --- /dev/null +++ b/homeassistant/components/aranet/translations/pt.json @@ -0,0 +1,10 @@ +{ + "config": { + "abort": { + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" + }, + "error": { + "unknown": "Erro inesperado" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aranet/translations/sk.json b/homeassistant/components/aranet/translations/sk.json index 83276dd71fd..449db2120c1 100644 --- a/homeassistant/components/aranet/translations/sk.json +++ b/homeassistant/components/aranet/translations/sk.json @@ -2,6 +2,8 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "integrations_diabled": "Toto zariadenie nem\u00e1 povolen\u00e9 integr\u00e1cie. Povo\u013ete integr\u00e1ciu inteligentnej dom\u00e1cnosti pomocou aplik\u00e1cie a sk\u00faste to znova.", + "no_devices_found": "Nena\u0161li sa \u017eiadne nenakonfigurovan\u00e9 zariadenia Aranet.", "outdated_version": "Toto zariadenie pou\u017e\u00edva zastaran\u00fd firmv\u00e9r. Aktualizujte ho aspo\u0148 na verziu 1.2.0 a sk\u00faste to znova." }, "error": { diff --git a/homeassistant/components/arcam_fmj/translations/pt.json b/homeassistant/components/arcam_fmj/translations/pt.json index af72dfe96e2..2755b086acf 100644 --- a/homeassistant/components/arcam_fmj/translations/pt.json +++ b/homeassistant/components/arcam_fmj/translations/pt.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", "already_in_progress": "O processo de configura\u00e7\u00e3o j\u00e1 est\u00e1 a decorrer", - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "error": { "one": "uma", diff --git a/homeassistant/components/arcam_fmj/translations/sk.json b/homeassistant/components/arcam_fmj/translations/sk.json index ad75e5f9df3..4f0f211ada9 100644 --- a/homeassistant/components/arcam_fmj/translations/sk.json +++ b/homeassistant/components/arcam_fmj/translations/sk.json @@ -5,8 +5,17 @@ "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "cannot_connect": "Nepodarilo sa pripoji\u0165" }, + "error": { + "few": "Pr\u00e1zdnych", + "many": "Pr\u00e1zdnych", + "one": "Pr\u00e1zdny", + "other": "Pr\u00e1zdny" + }, "flow_title": "{host}", "step": { + "confirm": { + "description": "Chcete prida\u0165 Arcam FMJ na `{host}` do Home Assistant?" + }, "user": { "data": { "host": "Hostite\u013e", diff --git a/homeassistant/components/arwn/sensor.py b/homeassistant/components/arwn/sensor.py index 45db805d1de..420ffb2d8a8 100644 --- a/homeassistant/components/arwn/sensor.py +++ b/homeassistant/components/arwn/sensor.py @@ -6,12 +6,7 @@ import logging from homeassistant.components import mqtt from homeassistant.components.sensor import SensorDeviceClass, SensorEntity -from homeassistant.const import ( - DEGREE, - PRECIPITATION_INCHES, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, -) +from homeassistant.const import DEGREE, UnitOfPrecipitationDepth, UnitOfTemperature from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType @@ -36,9 +31,9 @@ def discover_sensors(topic, payload): if domain == "temperature": name = parts[2] if unit == "F": - unit = TEMP_FAHRENHEIT + unit = UnitOfTemperature.FAHRENHEIT else: - unit = TEMP_CELSIUS + unit = UnitOfTemperature.CELSIUS return ArwnSensor( topic, name, "temp", unit, device_class=SensorDeviceClass.TEMPERATURE ) @@ -51,21 +46,43 @@ def discover_sensors(topic, payload): topic, "Rain Since Midnight", "since_midnight", - PRECIPITATION_INCHES, - "mdi:water", + UnitOfPrecipitationDepth.INCHES, + device_class=SensorDeviceClass.PRECIPITATION, ) return ( - ArwnSensor(topic + "/total", "Total Rainfall", "total", unit, "mdi:water"), - ArwnSensor(topic + "/rate", "Rainfall Rate", "rate", unit, "mdi:water"), + ArwnSensor( + topic + "/total", + "Total Rainfall", + "total", + unit, + device_class=SensorDeviceClass.PRECIPITATION, + ), + ArwnSensor( + topic + "/rate", + "Rainfall Rate", + "rate", + unit, + device_class=SensorDeviceClass.PRECIPITATION, + ), ) if domain == "barometer": return ArwnSensor(topic, "Barometer", "pressure", unit, "mdi:thermometer-lines") if domain == "wind": return ( ArwnSensor( - topic + "/speed", "Wind Speed", "speed", unit, "mdi:speedometer" + topic + "/speed", + "Wind Speed", + "speed", + unit, + device_class=SensorDeviceClass.WIND_SPEED, + ), + ArwnSensor( + topic + "/gust", + "Wind Gust", + "gust", + unit, + device_class=SensorDeviceClass.WIND_SPEED, ), - ArwnSensor(topic + "/gust", "Wind Gust", "gust", unit, "mdi:speedometer"), ArwnSensor( topic + "/dir", "Wind Direction", "direction", DEGREE, "mdi:compass" ), diff --git a/homeassistant/components/aseko_pool_live/translations/ko.json b/homeassistant/components/aseko_pool_live/translations/ko.json new file mode 100644 index 00000000000..c6ea8bb51ba --- /dev/null +++ b/homeassistant/components/aseko_pool_live/translations/ko.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "email": "\uc774\uba54\uc77c", + "password": "\ube44\ubc00\ubc88\ud638" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aseko_pool_live/translations/pt.json b/homeassistant/components/aseko_pool_live/translations/pt.json index cdb482efaa8..adcd0beadb6 100644 --- a/homeassistant/components/aseko_pool_live/translations/pt.json +++ b/homeassistant/components/aseko_pool_live/translations/pt.json @@ -4,14 +4,14 @@ "already_configured": "Conta j\u00e1 configurada" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, "step": { "user": { "data": { - "email": "Email", + "email": "", "password": "Palavra-passe" } } diff --git a/homeassistant/components/asuswrt/manifest.json b/homeassistant/components/asuswrt/manifest.json index c1d67fa9e57..d76b8c94439 100644 --- a/homeassistant/components/asuswrt/manifest.json +++ b/homeassistant/components/asuswrt/manifest.json @@ -1,6 +1,7 @@ { "domain": "asuswrt", "name": "ASUSWRT", + "integration_type": "hub", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/asuswrt", "requirements": ["aioasuswrt==1.4.0"], diff --git a/homeassistant/components/asuswrt/router.py b/homeassistant/components/asuswrt/router.py index a48e1374b6d..ffdec02bd3e 100644 --- a/homeassistant/components/asuswrt/router.py +++ b/homeassistant/components/asuswrt/router.py @@ -402,7 +402,10 @@ class AsusWrtRouter: ] except Exception as exc: # pylint: disable=broad-except _LOGGER.debug( - "Failed checking temperature sensor availability for ASUS router %s. Exception: %s", + ( + "Failed checking temperature sensor availability for ASUS router" + " %s. Exception: %s" + ), self._host, exc, ) diff --git a/homeassistant/components/asuswrt/sensor.py b/homeassistant/components/asuswrt/sensor.py index e66874e4137..a959b174c0d 100644 --- a/homeassistant/components/asuswrt/sensor.py +++ b/homeassistant/components/asuswrt/sensor.py @@ -10,11 +10,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - DATA_GIGABYTES, - DATA_RATE_MEGABITS_PER_SECOND, - TEMP_CELSIUS, -) +from homeassistant.const import UnitOfDataRate, UnitOfInformation, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -57,8 +53,9 @@ CONNECTION_SENSORS: tuple[AsusWrtSensorEntityDescription, ...] = ( key=SENSORS_RATES[0], name="Download Speed", icon="mdi:download-network", + device_class=SensorDeviceClass.DATA_RATE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=DATA_RATE_MEGABITS_PER_SECOND, + native_unit_of_measurement=UnitOfDataRate.MEGABITS_PER_SECOND, entity_registry_enabled_default=False, factor=125000, ), @@ -66,8 +63,9 @@ CONNECTION_SENSORS: tuple[AsusWrtSensorEntityDescription, ...] = ( key=SENSORS_RATES[1], name="Upload Speed", icon="mdi:upload-network", + device_class=SensorDeviceClass.DATA_RATE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=DATA_RATE_MEGABITS_PER_SECOND, + native_unit_of_measurement=UnitOfDataRate.MEGABITS_PER_SECOND, entity_registry_enabled_default=False, factor=125000, ), @@ -76,7 +74,8 @@ CONNECTION_SENSORS: tuple[AsusWrtSensorEntityDescription, ...] = ( name="Download", icon="mdi:download", state_class=SensorStateClass.TOTAL_INCREASING, - native_unit_of_measurement=DATA_GIGABYTES, + native_unit_of_measurement=UnitOfInformation.GIGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, entity_registry_enabled_default=False, factor=1000000000, ), @@ -85,7 +84,8 @@ CONNECTION_SENSORS: tuple[AsusWrtSensorEntityDescription, ...] = ( name="Upload", icon="mdi:upload", state_class=SensorStateClass.TOTAL_INCREASING, - native_unit_of_measurement=DATA_GIGABYTES, + native_unit_of_measurement=UnitOfInformation.GIGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, entity_registry_enabled_default=False, factor=1000000000, ), @@ -124,7 +124,7 @@ CONNECTION_SENSORS: tuple[AsusWrtSensorEntityDescription, ...] = ( name="2.4GHz Temperature", state_class=SensorStateClass.MEASUREMENT, device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, entity_category=EntityCategory.DIAGNOSTIC, entity_registry_enabled_default=False, factor=1, @@ -135,7 +135,7 @@ CONNECTION_SENSORS: tuple[AsusWrtSensorEntityDescription, ...] = ( name="5GHz Temperature", state_class=SensorStateClass.MEASUREMENT, device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, entity_category=EntityCategory.DIAGNOSTIC, entity_registry_enabled_default=False, factor=1, @@ -146,7 +146,7 @@ CONNECTION_SENSORS: tuple[AsusWrtSensorEntityDescription, ...] = ( name="CPU Temperature", state_class=SensorStateClass.MEASUREMENT, device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, entity_category=EntityCategory.DIAGNOSTIC, entity_registry_enabled_default=False, factor=1, diff --git a/homeassistant/components/asuswrt/translations/de.json b/homeassistant/components/asuswrt/translations/de.json index be17c242d58..f8d2e489e8d 100644 --- a/homeassistant/components/asuswrt/translations/de.json +++ b/homeassistant/components/asuswrt/translations/de.json @@ -34,7 +34,7 @@ "init": { "data": { "consider_home": "Sekunden, um ein Ger\u00e4t als 'abwesend' zu betrachten", - "dnsmasq": "Der Speicherort der dnsmasq.leases-Dateien im Router", + "dnsmasq": "Der Speicherort der dnsmasq.leases Dateien im Router", "interface": "Schnittstelle, von der du Statistiken haben m\u00f6chtest (z.B. eth0, eth1 usw.)", "require_ip": "Ger\u00e4te m\u00fcssen IP haben (f\u00fcr Zugangspunkt-Modus)", "track_unknown": "Unbekannte / unbenannte Ger\u00e4te verfolgen" diff --git a/homeassistant/components/asuswrt/translations/pt.json b/homeassistant/components/asuswrt/translations/pt.json index 54c86ef332a..e114532a76c 100644 --- a/homeassistant/components/asuswrt/translations/pt.json +++ b/homeassistant/components/asuswrt/translations/pt.json @@ -3,7 +3,7 @@ "step": { "user": { "data": { - "host": "Servidor", + "host": "Endere\u00e7o", "port": "Porta (leave empty for protocol default)" } } diff --git a/homeassistant/components/asuswrt/translations/sk.json b/homeassistant/components/asuswrt/translations/sk.json index 5e58d4bc638..b8503b0140d 100644 --- a/homeassistant/components/asuswrt/translations/sk.json +++ b/homeassistant/components/asuswrt/translations/sk.json @@ -1,8 +1,14 @@ { "config": { + "abort": { + "invalid_unique_id": "Nie je mo\u017en\u00e9 ur\u010di\u0165 platn\u00e9 jedine\u010dn\u00e9 ID zariadenia", + "no_unique_id": "Zariadenie bez platn\u00e9ho jedine\u010dn\u00e9ho ID je u\u017e nakonfigurovan\u00e9. Konfigur\u00e1cia viacer\u00fdch in\u0161tanci\u00ed nie je mo\u017en\u00e1" + }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa", + "pwd_and_ssh": "Poskytnite iba heslo alebo s\u00fabor s k\u013e\u00fa\u010dom SSH", + "pwd_or_ssh": "Zadajte heslo alebo s\u00fabor s k\u013e\u00fa\u010dom SSH", "ssh_not_file": "S\u00fabor s k\u013e\u00fa\u010dom SSH nebol n\u00e1jden\u00fd", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, @@ -15,9 +21,11 @@ "password": "Heslo", "port": "Port", "protocol": "Komunika\u010dn\u00fd protokol, ktor\u00fd sa m\u00e1 pou\u017ei\u0165", + "ssh_key": "Cesta k s\u00faboru s k\u013e\u00fa\u010dom SSH (namiesto hesla)", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" }, - "description": "Nastavte po\u017eadovan\u00fd parameter na pripojenie k smerova\u010du" + "description": "Nastavte po\u017eadovan\u00fd parameter na pripojenie k smerova\u010du", + "title": "AsusWRT" } } }, @@ -25,8 +33,13 @@ "step": { "init": { "data": { - "require_ip": "Zariadenia musia ma\u0165 IP (pre re\u017eim pr\u00edstupov\u00e9ho bodu)" - } + "consider_home": "Sekundy na \u010dakanie, k\u00fdm sa zariadenie neodstr\u00e1ni", + "dnsmasq": "Umiestnenie s\u00faborov dnsmasq.leases v routri", + "interface": "Rozhranie, z ktor\u00e9ho chcete \u0161tatistiky (napr. eth0, eth1 at\u010f.)", + "require_ip": "Zariadenia musia ma\u0165 IP (pre re\u017eim pr\u00edstupov\u00e9ho bodu)", + "track_unknown": "Sledovanie nezn\u00e1mych / nepomenovan\u00fdch zariaden\u00ed" + }, + "title": "Mo\u017enosti AsusWRT" } } } diff --git a/homeassistant/components/atag/__init__.py b/homeassistant/components/atag/__init__.py index 56b2c1e969e..e8deca6f04d 100644 --- a/homeassistant/components/atag/__init__.py +++ b/homeassistant/components/atag/__init__.py @@ -37,7 +37,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: atag = AtagOne( session=async_get_clientsession(hass), **entry.data, device=entry.unique_id ) - coordinator = DataUpdateCoordinator( + coordinator = DataUpdateCoordinator[AtagOne]( hass, _LOGGER, name=DOMAIN.title(), @@ -65,10 +65,12 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return unload_ok -class AtagEntity(CoordinatorEntity): +class AtagEntity(CoordinatorEntity[DataUpdateCoordinator[AtagOne]]): """Defines a base Atag entity.""" - def __init__(self, coordinator: DataUpdateCoordinator, atag_id: str) -> None: + def __init__( + self, coordinator: DataUpdateCoordinator[AtagOne], atag_id: str + ) -> None: """Initialize the Atag entity.""" super().__init__(coordinator) diff --git a/homeassistant/components/atag/sensor.py b/homeassistant/components/atag/sensor.py index b009d3a0a37..a006d1dfe05 100644 --- a/homeassistant/components/atag/sensor.py +++ b/homeassistant/components/atag/sensor.py @@ -3,10 +3,9 @@ from homeassistant.components.sensor import SensorDeviceClass, SensorEntity from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( PERCENTAGE, - PRESSURE_BAR, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, - TIME_HOURS, + UnitOfPressure, + UnitOfTemperature, + UnitOfTime, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -48,11 +47,11 @@ class AtagSensor(AtagEntity, SensorEntity): ): self._attr_device_class = coordinator.data.report[self._id].sensorclass if coordinator.data.report[self._id].measure in ( - PRESSURE_BAR, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, + UnitOfPressure.BAR, + UnitOfTemperature.CELSIUS, + UnitOfTemperature.FAHRENHEIT, PERCENTAGE, - TIME_HOURS, + UnitOfTime.HOURS, ): self._attr_native_unit_of_measurement = coordinator.data.report[ self._id diff --git a/homeassistant/components/atag/translations/pt.json b/homeassistant/components/atag/translations/pt.json index fa5aa3de317..cfa13737af4 100644 --- a/homeassistant/components/atag/translations/pt.json +++ b/homeassistant/components/atag/translations/pt.json @@ -4,12 +4,12 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "user": { "data": { - "host": "Servidor", + "host": "Endere\u00e7o", "port": "Porta" } } diff --git a/homeassistant/components/atag/translations/sk.json b/homeassistant/components/atag/translations/sk.json index 14d096336a4..e0ca614650f 100644 --- a/homeassistant/components/atag/translations/sk.json +++ b/homeassistant/components/atag/translations/sk.json @@ -4,7 +4,8 @@ "already_configured": "Zariadenie je u\u017e nakonfigurovan\u00e9" }, "error": { - "cannot_connect": "Nepodarilo sa pripoji\u0165" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unauthorized": "P\u00e1rovanie bolo odmietnut\u00e9, skontrolujte po\u017eiadavku na autoriz\u00e1ciu na zariadenia" }, "step": { "user": { diff --git a/homeassistant/components/atag/water_heater.py b/homeassistant/components/atag/water_heater.py index 0b41373356d..8976a3f78ec 100644 --- a/homeassistant/components/atag/water_heater.py +++ b/homeassistant/components/atag/water_heater.py @@ -7,7 +7,7 @@ from homeassistant.components.water_heater import ( WaterHeaterEntity, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, STATE_OFF, TEMP_CELSIUS, Platform +from homeassistant.const import ATTR_TEMPERATURE, STATE_OFF, Platform, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -30,7 +30,7 @@ class AtagWaterHeater(AtagEntity, WaterHeaterEntity): """Representation of an ATAG water heater.""" _attr_operation_list = OPERATION_LIST - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS @property def current_temperature(self): diff --git a/homeassistant/components/atome/sensor.py b/homeassistant/components/atome/sensor.py index d3df8b4e684..37a8fd4460f 100644 --- a/homeassistant/components/atome/sensor.py +++ b/homeassistant/components/atome/sensor.py @@ -17,8 +17,8 @@ from homeassistant.const import ( CONF_NAME, CONF_PASSWORD, CONF_USERNAME, - ENERGY_KILO_WATT_HOUR, - POWER_WATT, + UnitOfEnergy, + UnitOfPower, ) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv @@ -262,11 +262,11 @@ class AtomeSensor(SensorEntity): if sensor_type == LIVE_TYPE: self._attr_device_class = SensorDeviceClass.POWER - self._attr_native_unit_of_measurement = POWER_WATT + self._attr_native_unit_of_measurement = UnitOfPower.WATT self._attr_state_class = SensorStateClass.MEASUREMENT else: self._attr_device_class = SensorDeviceClass.ENERGY - self._attr_native_unit_of_measurement = ENERGY_KILO_WATT_HOUR + self._attr_native_unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR self._attr_state_class = SensorStateClass.TOTAL_INCREASING def update(self) -> None: diff --git a/homeassistant/components/august/__init__.py b/homeassistant/components/august/__init__.py index 249d9e51a85..20007ed8304 100644 --- a/homeassistant/components/august/__init__.py +++ b/homeassistant/components/august/__init__.py @@ -341,7 +341,7 @@ class AugustData(AugustSubscriberMixin): ) async def async_status_async(self, device_id, hyper_bridge): - """Request status of the the device but do not wait for a response since it will come via pubnub.""" + """Request status of the device but do not wait for a response since it will come via pubnub.""" return await self._async_call_api_op_requires_bridge( device_id, self._api.async_status_async, @@ -400,7 +400,10 @@ class AugustData(AugustSubscriberMixin): if self._device_detail_by_id.get(device_id): continue _LOGGER.info( - "The doorbell %s could not be setup because the system could not fetch details about the doorbell", + ( + "The doorbell %s could not be setup because the system could not" + " fetch details about the doorbell" + ), doorbell.device_name, ) del self._doorbells_by_id[device_id] @@ -414,12 +417,18 @@ class AugustData(AugustSubscriberMixin): lock_detail = self._device_detail_by_id.get(device_id) if lock_detail is None: _LOGGER.info( - "The lock %s could not be setup because the system could not fetch details about the lock", + ( + "The lock %s could not be setup because the system could not" + " fetch details about the lock" + ), lock.device_name, ) elif lock_detail.bridge is None: _LOGGER.info( - "The lock %s could not be setup because it does not have a bridge (Connect)", + ( + "The lock %s could not be setup because it does not have a" + " bridge (Connect)" + ), lock.device_name, ) del self._device_detail_by_id[device_id] diff --git a/homeassistant/components/august/binary_sensor.py b/homeassistant/components/august/binary_sensor.py index 6f45f626180..89e05c5057e 100644 --- a/homeassistant/components/august/binary_sensor.py +++ b/homeassistant/components/august/binary_sensor.py @@ -167,7 +167,10 @@ async def async_setup_entry( detail = data.get_device_detail(door.device_id) if not detail.doorsense: _LOGGER.debug( - "Not adding sensor class door for lock %s because it does not have doorsense", + ( + "Not adding sensor class door for lock %s because it does not have" + " doorsense" + ), door.device_name, ) continue diff --git a/homeassistant/components/august/gateway.py b/homeassistant/components/august/gateway.py index 6c9f9113d98..ac7b81a7117 100644 --- a/homeassistant/components/august/gateway.py +++ b/homeassistant/components/august/gateway.py @@ -131,7 +131,10 @@ class AugustGateway: await self.authenticator.async_refresh_access_token(force=False) ) _LOGGER.info( - "Refreshed august access token. The old token expired at %s, and the new token expires at %s", + ( + "Refreshed august access token. The old token expired at %s, and" + " the new token expires at %s" + ), self.authentication.access_token_expires, refreshed_authentication.access_token_expires, ) diff --git a/homeassistant/components/august/manifest.json b/homeassistant/components/august/manifest.json index e8f8736b522..773a341954c 100644 --- a/homeassistant/components/august/manifest.json +++ b/homeassistant/components/august/manifest.json @@ -2,9 +2,13 @@ "domain": "august", "name": "August", "documentation": "https://www.home-assistant.io/integrations/august", - "requirements": ["yalexs==1.2.6", "yalexs_ble==1.10.2"], + "requirements": ["yalexs==1.2.6", "yalexs_ble==1.12.5"], "codeowners": ["@bdraco"], "dhcp": [ + { + "hostname": "yale-connect-plus", + "macaddress": "00177A*" + }, { "hostname": "connect", "macaddress": "D86162*" diff --git a/homeassistant/components/august/strings.json b/homeassistant/components/august/strings.json index 7939fb1d25f..50db556c13a 100644 --- a/homeassistant/components/august/strings.json +++ b/homeassistant/components/august/strings.json @@ -24,7 +24,7 @@ "username": "[%key:common::config_flow::data::username%]", "login_method": "Login Method" }, - "title": "Setup an August account" + "title": "Set up an August account" }, "reauth_validate": { "description": "Enter the password for {username}.", diff --git a/homeassistant/components/august/translations/en.json b/homeassistant/components/august/translations/en.json index f2ceef78d48..4b8e0717c48 100644 --- a/homeassistant/components/august/translations/en.json +++ b/homeassistant/components/august/translations/en.json @@ -24,7 +24,7 @@ "username": "Username" }, "description": "If the Login Method is 'email', Username is the email address. If the Login Method is 'phone', Username is the phone number in the format '+NNNNNNNNN'.", - "title": "Setup an August account" + "title": "Set up an August account" }, "validation": { "data": { diff --git a/homeassistant/components/august/translations/no.json b/homeassistant/components/august/translations/no.json index 11e30ed8bf6..2f9e372f874 100644 --- a/homeassistant/components/august/translations/no.json +++ b/homeassistant/components/august/translations/no.json @@ -24,7 +24,7 @@ "username": "Brukernavn" }, "description": "Hvis p\u00e5loggingsmetoden er 'e-post', er brukernavnet e-postadressen. Hvis p\u00e5loggingsmetoden er 'telefon', er brukernavn telefonnummeret i formatet '+ NNNNNNNNN'.", - "title": "Sett opp en August konto" + "title": "Sett opp en augustkonto" }, "validation": { "data": { diff --git a/homeassistant/components/august/translations/pt.json b/homeassistant/components/august/translations/pt.json index 6c6765da70e..7fb872e5f76 100644 --- a/homeassistant/components/august/translations/pt.json +++ b/homeassistant/components/august/translations/pt.json @@ -5,7 +5,7 @@ "reauth_successful": "Reautentica\u00e7\u00e3o bem sucedida" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, diff --git a/homeassistant/components/august/translations/sk.json b/homeassistant/components/august/translations/sk.json index ef420973986..e293efb90a9 100644 --- a/homeassistant/components/august/translations/sk.json +++ b/homeassistant/components/august/translations/sk.json @@ -14,19 +14,23 @@ "data": { "password": "Heslo" }, - "description": "Zadajte heslo pre {username}." + "description": "Zadajte heslo pre {username}.", + "title": "Op\u00e4tovn\u00e9 overenie konta August" }, "user_validate": { "data": { "login_method": "Sp\u00f4sob prihl\u00e1senia", "password": "Heslo", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" - } + }, + "description": "Ak je sp\u00f4sob prihl\u00e1senia \u201ee-mail\u201c, pou\u017e\u00edvate\u013esk\u00e9 meno je e-mailov\u00e1 adresa. Ak je met\u00f3da prihl\u00e1senia \u201etelef\u00f3n\u201c, pou\u017e\u00edvate\u013esk\u00e9 meno je telef\u00f3nne \u010d\u00edslo vo form\u00e1te \u201e+NNNNNNNNNN\u201c.", + "title": "Nastavenie \u00fa\u010dtu August" }, "validation": { "data": { "code": "Overovac\u00ed k\u00f3d" }, + "description": "Skontrolujte pros\u00edm {login_method} ({username}) a ni\u017e\u0161ie zadajte overovac\u00ed k\u00f3d", "title": "Dvojfaktorov\u00e1 autentifik\u00e1cia" } } diff --git a/homeassistant/components/aurora/translations/pt.json b/homeassistant/components/aurora/translations/pt.json index 336f6ac5f68..6e51fd5b403 100644 --- a/homeassistant/components/aurora/translations/pt.json +++ b/homeassistant/components/aurora/translations/pt.json @@ -1,7 +1,7 @@ { "config": { "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "user": { diff --git a/homeassistant/components/aurora/translations/sk.json b/homeassistant/components/aurora/translations/sk.json index ad49a4a2551..6026eb9be8f 100644 --- a/homeassistant/components/aurora/translations/sk.json +++ b/homeassistant/components/aurora/translations/sk.json @@ -12,5 +12,15 @@ } } } - } + }, + "options": { + "step": { + "init": { + "data": { + "threshold": "Prahov\u00e1 hodnota (%)" + } + } + } + }, + "title": "Sn\u00edma\u010d NOAA Aurora" } \ No newline at end of file diff --git a/homeassistant/components/aurora_abb_powerone/sensor.py b/homeassistant/components/aurora_abb_powerone/sensor.py index eeb72e4d485..2502f808e51 100644 --- a/homeassistant/components/aurora_abb_powerone/sensor.py +++ b/homeassistant/components/aurora_abb_powerone/sensor.py @@ -14,7 +14,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ENERGY_KILO_WATT_HOUR, POWER_WATT, TEMP_CELSIUS +from homeassistant.const import UnitOfEnergy, UnitOfPower, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -28,7 +28,7 @@ SENSOR_TYPES = [ SensorEntityDescription( key="instantaneouspower", device_class=SensorDeviceClass.POWER, - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, state_class=SensorStateClass.MEASUREMENT, name="Power Output", ), @@ -36,14 +36,14 @@ SENSOR_TYPES = [ key="temp", device_class=SensorDeviceClass.TEMPERATURE, entity_category=EntityCategory.DIAGNOSTIC, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, name="Temperature", ), SensorEntityDescription( key="totalenergy", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, state_class=SensorStateClass.TOTAL_INCREASING, name="Total Energy", ), diff --git a/homeassistant/components/aurora_abb_powerone/translations/en_GB.json b/homeassistant/components/aurora_abb_powerone/translations/en-GB.json similarity index 100% rename from homeassistant/components/aurora_abb_powerone/translations/en_GB.json rename to homeassistant/components/aurora_abb_powerone/translations/en-GB.json diff --git a/homeassistant/components/aurora_abb_powerone/translations/ko.json b/homeassistant/components/aurora_abb_powerone/translations/ko.json new file mode 100644 index 00000000000..17dee71d640 --- /dev/null +++ b/homeassistant/components/aurora_abb_powerone/translations/ko.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aurora_abb_powerone/translations/sk.json b/homeassistant/components/aurora_abb_powerone/translations/sk.json index 1163236043d..afdfd9056be 100644 --- a/homeassistant/components/aurora_abb_powerone/translations/sk.json +++ b/homeassistant/components/aurora_abb_powerone/translations/sk.json @@ -14,7 +14,8 @@ "data": { "address": "Adresa meni\u010da", "port": "Port adapt\u00e9ra RS485 alebo USB-RS485" - } + }, + "description": "Meni\u010d mus\u00ed by\u0165 pripojen\u00fd cez adapt\u00e9r RS485, vyberte pros\u00edm s\u00e9riov\u00fd port a adresu meni\u010da pod\u013ea konfigur\u00e1cie na LCD paneli" } } } diff --git a/homeassistant/components/aussie_broadband/__init__.py b/homeassistant/components/aussie_broadband/__init__.py index b32e11f27c0..ae4bc78580c 100644 --- a/homeassistant/components/aussie_broadband/__init__.py +++ b/homeassistant/components/aussie_broadband/__init__.py @@ -45,7 +45,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return await client.get_usage(service_id) except UnrecognisedServiceType as err: raise UpdateFailed( - f"Service {service_id} of type '{services[service_id]['type']}' was unrecognised" + f"Service {service_id} of type '{services[service_id]['type']}' was" + " unrecognised" ) from err return async_update_data diff --git a/homeassistant/components/aussie_broadband/sensor.py b/homeassistant/components/aussie_broadband/sensor.py index 648abaccb0c..896930649eb 100644 --- a/homeassistant/components/aussie_broadband/sensor.py +++ b/homeassistant/components/aussie_broadband/sensor.py @@ -7,12 +7,13 @@ import re from typing import Any, cast from homeassistant.components.sensor import ( + SensorDeviceClass, SensorEntity, SensorEntityDescription, SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import DATA_KILOBYTES, DATA_MEGABYTES, TIME_DAYS +from homeassistant.const import UnitOfInformation, UnitOfTime from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceEntryType from homeassistant.helpers.entity import DeviceInfo @@ -36,21 +37,24 @@ SENSOR_DESCRIPTIONS: tuple[SensorValueEntityDescription, ...] = ( key="usedMb", name="Data used", state_class=SensorStateClass.TOTAL_INCREASING, - native_unit_of_measurement=DATA_MEGABYTES, + native_unit_of_measurement=UnitOfInformation.MEGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:network", ), SensorValueEntityDescription( key="downloadedMb", name="Downloaded", state_class=SensorStateClass.TOTAL_INCREASING, - native_unit_of_measurement=DATA_MEGABYTES, + native_unit_of_measurement=UnitOfInformation.MEGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:download-network", ), SensorValueEntityDescription( key="uploadedMb", name="Uploaded", state_class=SensorStateClass.TOTAL_INCREASING, - native_unit_of_measurement=DATA_MEGABYTES, + native_unit_of_measurement=UnitOfInformation.MEGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:upload-network", ), # Mobile Phone Services sensors @@ -86,7 +90,8 @@ SENSOR_DESCRIPTIONS: tuple[SensorValueEntityDescription, ...] = ( key="internet", name="Data used", state_class=SensorStateClass.TOTAL_INCREASING, - native_unit_of_measurement=DATA_KILOBYTES, + native_unit_of_measurement=UnitOfInformation.KILOBYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:network", value=lambda x: x.get("kbytes"), ), @@ -108,13 +113,13 @@ SENSOR_DESCRIPTIONS: tuple[SensorValueEntityDescription, ...] = ( SensorValueEntityDescription( key="daysTotal", name="Billing cycle length", - native_unit_of_measurement=TIME_DAYS, + native_unit_of_measurement=UnitOfTime.DAYS, icon="mdi:calendar-range", ), SensorValueEntityDescription( key="daysRemaining", name="Billing cycle remaining", - native_unit_of_measurement=TIME_DAYS, + native_unit_of_measurement=UnitOfTime.DAYS, icon="mdi:calendar-clock", ), ) diff --git a/homeassistant/components/aussie_broadband/translations/ko.json b/homeassistant/components/aussie_broadband/translations/ko.json new file mode 100644 index 00000000000..bf55c8939ad --- /dev/null +++ b/homeassistant/components/aussie_broadband/translations/ko.json @@ -0,0 +1,34 @@ +{ + "config": { + "abort": { + "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638" + }, + "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" + }, + "user": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + } + } + } + }, + "options": { + "abort": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aussie_broadband/translations/pt.json b/homeassistant/components/aussie_broadband/translations/pt.json index 535612a4dbd..5c94ffb98cf 100644 --- a/homeassistant/components/aussie_broadband/translations/pt.json +++ b/homeassistant/components/aussie_broadband/translations/pt.json @@ -4,7 +4,7 @@ "reauth_successful": "Reautentica\u00e7\u00e3o bem sucedida" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, @@ -25,7 +25,7 @@ }, "options": { "abort": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" } diff --git a/homeassistant/components/auth/login_flow.py b/homeassistant/components/auth/login_flow.py index b907598fe5a..364f5242377 100644 --- a/homeassistant/components/auth/login_flow.py +++ b/homeassistant/components/auth/login_flow.py @@ -122,7 +122,9 @@ class WellKnownOAuthInfoView(HomeAssistantView): "token_endpoint": "/auth/token", "revocation_endpoint": "/auth/revoke", "response_types_supported": ["code"], - "service_documentation": "https://developers.home-assistant.io/docs/auth_api", + "service_documentation": ( + "https://developers.home-assistant.io/docs/auth_api" + ), } ) diff --git a/homeassistant/components/auth/mfa_setup_flow.py b/homeassistant/components/auth/mfa_setup_flow.py index d6a9282e089..a7999af666a 100644 --- a/homeassistant/components/auth/mfa_setup_flow.py +++ b/homeassistant/components/auth/mfa_setup_flow.py @@ -40,7 +40,7 @@ class MfaFlowManager(data_entry_flow.FlowManager): async def async_create_flow( # type: ignore[override] self, - handler_key: Any, + handler_key: str, *, context: dict[str, Any], data: dict[str, Any], diff --git a/homeassistant/components/auth/translations/de.json b/homeassistant/components/auth/translations/de.json index ec97c449ac4..fcecb886ff1 100644 --- a/homeassistant/components/auth/translations/de.json +++ b/homeassistant/components/auth/translations/de.json @@ -10,14 +10,14 @@ "step": { "init": { "description": "Bitte w\u00e4hle einen der Benachrichtigungsdienste:", - "title": "Einmal Passwort f\u00fcr Notify einrichten" + "title": "Einmal Passwort f\u00fcr Benachrichtigung einrichten" }, "setup": { "description": "Ein Einmal-Passwort wurde per **notify.{notify_service}** gesendet. Bitte gib es unten ein:", "title": "\u00dcberpr\u00fcfe das Setup" } }, - "title": "Benachrichtigen f\u00fcr One-Time Password" + "title": "Benachrichtigen f\u00fcr Einmalpasswort" }, "totp": { "error": { @@ -25,7 +25,7 @@ }, "step": { "init": { - "description": "Um die Zwei-Faktor-Authentifizierung mit zeitbasierten Einmalpassw\u00f6rtern zu aktivieren, scanne den QR-Code mit deiner Authentifizierungs-App. Wenn du keine hast, empfehlen wir entweder [Google Authenticator] (https://support.google.com/accounts/answer/1066447) oder [Authy] (https://authy.com/). \n\n {qr_code} \n \nNachdem du den Code gescannt hast, gibst du den sechsstelligen Code aus der App ein, um das Setup zu \u00fcberpr\u00fcfen. Wenn es Probleme beim Scannen des QR-Codes gibt, f\u00fchre ein manuelles Setup mit dem Code ** ` {code} ` ** durch.", + "description": "Um die Zwei-Faktor-Authentifizierung mit zeitbasierten Einmalpassw\u00f6rtern zu aktivieren, scanne den QR-Code mit deiner Authentifizierungs-App. Wenn du keine hast, empfehlen wir entweder [Google Authenticator] (https://support.google.com/accounts/answer/1066447) oder [Authy] (https://authy.com/). \n\n{qr_code} \n \nNachdem du den Code gescannt hast, gibst du den sechsstelligen Code aus der App ein, um das Setup zu \u00fcberpr\u00fcfen. Wenn es Probleme beim Scannen des QR-Codes gibt, f\u00fchre ein manuelles Setup mit dem Code ** ` {code} ` ** durch.", "title": "Richte die Zwei-Faktor-Authentifizierung mit TOTP ein" } }, diff --git a/homeassistant/components/auth/translations/sk.json b/homeassistant/components/auth/translations/sk.json index 5d510baa4be..7943f914952 100644 --- a/homeassistant/components/auth/translations/sk.json +++ b/homeassistant/components/auth/translations/sk.json @@ -20,6 +20,15 @@ "title": "Ozn\u00e1menie jednorazov\u00e9ho hesla" }, "totp": { + "error": { + "invalid_code": "Neplatn\u00fd k\u00f3d, sk\u00faste to znovu. Pokia\u013e sa t\u00e1to chyba opakuje, uistite sa, \u017ee hodiny syst\u00e9mu Home Assistant s\u00fa spr\u00e1vne nastaven\u00e9." + }, + "step": { + "init": { + "description": "Chcete aktivova\u0165 dvojfaktorov\u00fa autentiz\u00e1ciu pomocou jednor\u00e1zov\u00fdch hesiel zalo\u017een\u00fdch na \u010dase, na\u010d\u00edtajte k\u00f3d QR pomocou va\u0161ej autentiza\u010dnej aplik\u00e1cie. Pokia\u013e ju nem\u00e1te, doporu\u010dujeme alebo [Google Authenticator](https://support.google.com/accounts/answer/1066447) alebo [Authy](https://authy.com/). \n\n{qr_code} \n \nPo skenovan\u00ed k\u00f3du zadajte \u0161es\u0165cifern\u00fd k\u00f3d z aplik\u00e1cie a overte nastavenie. Pokia\u013e m\u00e1te probl\u00e9my so skenovan\u00edm k\u00f3du QR, preve\u010fte ru\u010dn\u00e9 nastavenie s k\u00f3dom **`{code}`**.", + "title": "Nastavte dvjfaktorov\u00e9 overovanie pomocou TOTP" + } + }, "title": "TOTP" } } diff --git a/homeassistant/components/automation/__init__.py b/homeassistant/components/automation/__init__.py index 9581a6b1c40..c0bbf51138a 100644 --- a/homeassistant/components/automation/__init__.py +++ b/homeassistant/components/automation/__init__.py @@ -8,9 +8,8 @@ import logging from typing import Any, Protocol, cast import voluptuous as vol -from voluptuous.humanize import humanize_error -from homeassistant.components import blueprint, websocket_api +from homeassistant.components import websocket_api from homeassistant.components.blueprint import CONF_USE_BLUEPRINT from homeassistant.const import ( ATTR_ENTITY_ID, @@ -92,7 +91,7 @@ from homeassistant.helpers.typing import ConfigType from homeassistant.loader import bind_hass from homeassistant.util.dt import parse_datetime -from .config import AutomationConfig, async_validate_config_item +from .config import AutomationConfig from .const import ( CONF_ACTION, CONF_INITIAL_STATE, @@ -121,8 +120,6 @@ ATTR_SOURCE = "source" ATTR_VARIABLES = "variables" SERVICE_TRIGGER = "trigger" -_LOGGER = logging.getLogger(__name__) - class IfAction(Protocol): """Define the format of if_action.""" @@ -421,8 +418,7 @@ class AutomationEntity(ToggleEntity, RestoreEntity): if last_triggered is not None: self.action_script.last_triggered = parse_datetime(last_triggered) self._logger.debug( - "Loaded automation %s with state %s from state " - " storage last state %s", + "Loaded automation %s with state %s from state storage last state %s", self.entity_id, enable_automation, state, @@ -438,8 +434,7 @@ class AutomationEntity(ToggleEntity, RestoreEntity): if self._initial_state is not None: enable_automation = self._initial_state self._logger.debug( - "Automation %s initial state %s overridden from " - "config initial_state", + "Automation %s initial state %s overridden from config initial_state", self.entity_id, enable_automation, ) @@ -682,32 +677,11 @@ async def _prepare_automation_config( """Parse configuration and prepare automation entity configuration.""" automation_configs: list[AutomationEntityConfig] = [] - conf: list[ConfigType | blueprint.BlueprintInputs] = config[DOMAIN] + conf: list[ConfigType] = config[DOMAIN] for list_no, config_block in enumerate(conf): - raw_blueprint_inputs = None - raw_config = None - if isinstance(config_block, blueprint.BlueprintInputs): - blueprint_inputs = config_block - raw_blueprint_inputs = blueprint_inputs.config_with_inputs - - try: - raw_config = blueprint_inputs.async_substitute() - config_block = cast( - dict[str, Any], - await async_validate_config_item(hass, raw_config), - ) - except vol.Invalid as err: - LOGGER.error( - "Blueprint %s generated invalid automation with inputs %s: %s", - blueprint_inputs.blueprint.name, - blueprint_inputs.inputs, - humanize_error(config_block, err), - ) - continue - else: - raw_config = cast(AutomationConfig, config_block).raw_config - + raw_config = cast(AutomationConfig, config_block).raw_config + raw_blueprint_inputs = cast(AutomationConfig, config_block).raw_blueprint_inputs automation_configs.append( AutomationEntityConfig( config_block, list_no, raw_blueprint_inputs, raw_config diff --git a/homeassistant/components/automation/config.py b/homeassistant/components/automation/config.py index ec35e617b07..c127208377f 100644 --- a/homeassistant/components/automation/config.py +++ b/homeassistant/components/automation/config.py @@ -2,17 +2,16 @@ from __future__ import annotations import asyncio +from collections.abc import Mapping from contextlib import suppress from typing import Any import voluptuous as vol +from voluptuous.humanize import humanize_error from homeassistant.components import blueprint -from homeassistant.components.device_automation.exceptions import ( - InvalidDeviceAutomationConfig, -) from homeassistant.components.trace import TRACE_CONFIG_SCHEMA -from homeassistant.config import async_log_exception, config_without_domain +from homeassistant.config import config_without_domain from homeassistant.const import ( CONF_ALIAS, CONF_CONDITION, @@ -26,7 +25,7 @@ from homeassistant.helpers import config_per_platform, config_validation as cv, from homeassistant.helpers.condition import async_validate_conditions_config from homeassistant.helpers.trigger import async_validate_trigger_config from homeassistant.helpers.typing import ConfigType -from homeassistant.loader import IntegrationNotFound +from homeassistant.util.yaml.input import UndefinedSubstitution from .const import ( CONF_ACTION, @@ -36,6 +35,7 @@ from .const import ( CONF_TRIGGER, CONF_TRIGGER_VARIABLES, DOMAIN, + LOGGER, ) from .helpers import async_get_blueprints @@ -65,67 +65,157 @@ PLATFORM_SCHEMA = vol.All( ) -async def async_validate_config_item( +async def _async_validate_config_item( hass: HomeAssistant, config: ConfigType, - full_config: ConfigType | None = None, -) -> blueprint.BlueprintInputs | dict[str, Any]: + warn_on_errors: bool, +) -> AutomationConfig: """Validate config item.""" - if blueprint.is_blueprint_instance_config(config): - blueprints = async_get_blueprints(hass) - return await blueprints.async_inputs_from_config(config) + raw_config = None + raw_blueprint_inputs = None + uses_blueprint = False + with suppress(ValueError): + raw_config = dict(config) - config = PLATFORM_SCHEMA(config) + def _log_invalid_automation( + err: Exception, + automation_name: str, + problem: str, + config: ConfigType, + ) -> None: + """Log an error about invalid automation.""" + if not warn_on_errors: + return - config[CONF_TRIGGER] = await async_validate_trigger_config( - hass, config[CONF_TRIGGER] - ) + if uses_blueprint: + LOGGER.error( + "Blueprint '%s' generated invalid automation with inputs %s: %s", + blueprint_inputs.blueprint.name, + blueprint_inputs.inputs, + humanize_error(config, err) if isinstance(err, vol.Invalid) else err, + ) + return - if CONF_CONDITION in config: - config[CONF_CONDITION] = await async_validate_conditions_config( - hass, config[CONF_CONDITION] + LOGGER.error( + "%s %s and has been disabled: %s", + automation_name, + problem, + humanize_error(config, err) if isinstance(err, vol.Invalid) else err, ) + return - config[CONF_ACTION] = await script.async_validate_actions_config( - hass, config[CONF_ACTION] - ) + if blueprint.is_blueprint_instance_config(config): + uses_blueprint = True + blueprints = async_get_blueprints(hass) + try: + blueprint_inputs = await blueprints.async_inputs_from_config(config) + except blueprint.BlueprintException as err: + if warn_on_errors: + LOGGER.error( + "Failed to generate automation from blueprint: %s", + err, + ) + raise - return config + raw_blueprint_inputs = blueprint_inputs.config_with_inputs + + try: + config = blueprint_inputs.async_substitute() + raw_config = dict(config) + except UndefinedSubstitution as err: + if warn_on_errors: + LOGGER.error( + "Blueprint '%s' failed to generate automation with inputs %s: %s", + blueprint_inputs.blueprint.name, + blueprint_inputs.inputs, + err, + ) + raise HomeAssistantError from err + + automation_name = "Unnamed automation" + if isinstance(config, Mapping): + if CONF_ALIAS in config: + automation_name = f"Automation with alias '{config[CONF_ALIAS]}'" + elif CONF_ID in config: + automation_name = f"Automation with ID '{config[CONF_ID]}'" + + try: + validated_config = PLATFORM_SCHEMA(config) + except vol.Invalid as err: + _log_invalid_automation(err, automation_name, "could not be validated", config) + raise + + try: + validated_config[CONF_TRIGGER] = await async_validate_trigger_config( + hass, validated_config[CONF_TRIGGER] + ) + except ( + vol.Invalid, + HomeAssistantError, + ) as err: + _log_invalid_automation( + err, automation_name, "failed to setup triggers", validated_config + ) + raise + + if CONF_CONDITION in validated_config: + try: + validated_config[CONF_CONDITION] = await async_validate_conditions_config( + hass, validated_config[CONF_CONDITION] + ) + except ( + vol.Invalid, + HomeAssistantError, + ) as err: + _log_invalid_automation( + err, automation_name, "failed to setup conditions", validated_config + ) + raise + + try: + validated_config[CONF_ACTION] = await script.async_validate_actions_config( + hass, validated_config[CONF_ACTION] + ) + except ( + vol.Invalid, + HomeAssistantError, + ) as err: + _log_invalid_automation( + err, automation_name, "failed to setup actions", validated_config + ) + raise + + automation_config = AutomationConfig(validated_config) + automation_config.raw_blueprint_inputs = raw_blueprint_inputs + automation_config.raw_config = raw_config + return automation_config class AutomationConfig(dict): """Dummy class to allow adding attributes.""" raw_config: dict[str, Any] | None = None + raw_blueprint_inputs: dict[str, Any] | None = None async def _try_async_validate_config_item( hass: HomeAssistant, config: dict[str, Any], - full_config: dict[str, Any] | None = None, -) -> AutomationConfig | blueprint.BlueprintInputs | None: +) -> AutomationConfig | None: """Validate config item.""" - raw_config = None - with suppress(ValueError): - raw_config = dict(config) - try: - validated_config = await async_validate_config_item(hass, config, full_config) - except ( - vol.Invalid, - HomeAssistantError, - IntegrationNotFound, - InvalidDeviceAutomationConfig, - ) as ex: - async_log_exception(ex, DOMAIN, full_config or config, hass) + return await _async_validate_config_item(hass, config, True) + except (vol.Invalid, HomeAssistantError): return None - if isinstance(validated_config, blueprint.BlueprintInputs): - return validated_config - automation_config = AutomationConfig(validated_config) - automation_config.raw_config = raw_config - return automation_config +async def async_validate_config_item( + hass: HomeAssistant, + config_key: str, + config: dict[str, Any], +) -> AutomationConfig | None: + """Validate config item, called by EditAutomationConfigView.""" + return await _async_validate_config_item(hass, config, False) async def async_validate_config(hass: HomeAssistant, config: ConfigType) -> ConfigType: @@ -135,7 +225,7 @@ async def async_validate_config(hass: HomeAssistant, config: ConfigType) -> Conf lambda x: x is not None, await asyncio.gather( *( - _try_async_validate_config_item(hass, p_config, config) + _try_async_validate_config_item(hass, p_config) for _, p_config in config_per_platform(config, DOMAIN) ) ), diff --git a/homeassistant/components/automation/translations/he.json b/homeassistant/components/automation/translations/he.json index 0b94cedbebd..1cd985d8350 100644 --- a/homeassistant/components/automation/translations/he.json +++ b/homeassistant/components/automation/translations/he.json @@ -1,4 +1,17 @@ { + "issues": { + "service_not_found": { + "fix_flow": { + "step": { + "confirm": { + "description": "\u05d4\u05d0\u05d5\u05d8\u05d5\u05de\u05e6\u05d9\u05d4 \"{name}\" (`{entity_id}`) \u05db\u05d5\u05dc\u05dc\u05ea \u05e4\u05e2\u05d5\u05dc\u05d4 \u05d4\u05e7\u05d5\u05e8\u05d0\u05ea \u05dc\u05e9\u05d9\u05e8\u05d5\u05ea \u05dc\u05d0 \u05d9\u05d3\u05d5\u05e2: `{service}`.\n\n\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d6\u05d5 \u05de\u05d5\u05e0\u05e2\u05ea \u05de\u05d4\u05d0\u05d5\u05d8\u05d5\u05de\u05e6\u05d9\u05d4 \u05dc\u05e4\u05e2\u05d5\u05dc \u05db\u05e8\u05d0\u05d5\u05d9. \u05d0\u05d5\u05dc\u05d9 \u05e9\u05d9\u05e8\u05d5\u05ea \u05d6\u05d4 \u05d0\u05d9\u05e0\u05d5 \u05d6\u05de\u05d9\u05df \u05e2\u05d5\u05d3, \u05d0\u05d5 \u05d0\u05d5\u05dc\u05d9 \u05e9\u05d2\u05d9\u05d0\u05ea \u05d4\u05e7\u05dc\u05d3\u05d4 \u05d2\u05e8\u05de\u05d4 \u05dc\u05d5.\n\n\u05db\u05d3\u05d9 \u05dc\u05ea\u05e7\u05df \u05e9\u05d2\u05d9\u05d0\u05d4 \u05d6\u05d5, [\u05e2\u05e8\u05d9\u05db\u05ea \u05d4\u05d0\u05d5\u05d8\u05d5\u05de\u05e6\u05d9\u05d4]({edit}) \u05d5\u05d4\u05e1\u05e8\u05ea \u05d4\u05e4\u05e2\u05d5\u05dc\u05d4 \u05d4\u05e7\u05d5\u05e8\u05d0\u05ea \u05dc\u05e9\u05d9\u05e8\u05d5\u05ea \u05d6\u05d4.\n\n\u05dc\u05d7\u05d9\u05e6\u05d4 \u05e2\u05dc \u05e9\u05dc\u05d9\u05d7\u05d4 \u05dc\u05de\u05d8\u05d4 \u05db\u05d3\u05d9 \u05dc\u05d0\u05e9\u05e8 \u05e9\u05ea\u05d9\u05e7\u05e0\u05ea \u05d0\u05d5\u05d8\u05d5\u05de\u05e6\u05d9\u05d4 \u05d6\u05d5.", + "title": "{name} \u05de\u05e9\u05ea\u05de\u05e9 \u05d1\u05e9\u05d9\u05e8\u05d5\u05ea \u05dc\u05d0 \u05d9\u05d3\u05d5\u05e2" + } + } + }, + "title": "{name} \u05de\u05e9\u05ea\u05de\u05e9 \u05d1\u05e9\u05d9\u05e8\u05d5\u05ea \u05dc\u05d0 \u05d9\u05d3\u05d5\u05e2" + } + }, "state": { "_": { "off": "\u05db\u05d1\u05d5\u05d9", diff --git a/homeassistant/components/automation/translations/sk.json b/homeassistant/components/automation/translations/sk.json index d75fc6b3157..5d641dec5e4 100644 --- a/homeassistant/components/automation/translations/sk.json +++ b/homeassistant/components/automation/translations/sk.json @@ -4,6 +4,7 @@ "fix_flow": { "step": { "confirm": { + "description": "Automatiz\u00e1cia \"{name}\" (`{entity_id}`) m\u00e1 akciu, ktor\u00e1 vol\u00e1 nezn\u00e1mu slu\u017ebu: `{service}`.\n\nT\u00e1to chyba br\u00e1ni spr\u00e1vnemu spusteniu automatiz\u00e1cie. Mo\u017eno t\u00e1to slu\u017eba u\u017e nie je k dispoz\u00edci, alebo ju sp\u00f4sobil preklep.\n\nChcete t\u00fato chybu opravi\u0165, [upravte automatiz\u00e1ciu]({edit}) a odstr\u00e1\u0148te akciu, ktor\u00e1 t\u00fato slu\u017ebu vol\u00e1.\n\nKliknut\u00edm na tla\u010didlo ULO\u017dI\u0164 potvr\u010fte, \u017ee ste t\u00fato automatiz\u00e1ciu opravili.", "title": "{name} pou\u017e\u00edva nezn\u00e1mu slu\u017ebu" } } diff --git a/homeassistant/components/awair/__init__.py b/homeassistant/components/awair/__init__.py index fd964328c4d..cef2c7d1fd4 100644 --- a/homeassistant/components/awair/__init__.py +++ b/homeassistant/components/awair/__init__.py @@ -72,7 +72,7 @@ async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> return unload_ok -class AwairDataUpdateCoordinator(DataUpdateCoordinator): +class AwairDataUpdateCoordinator(DataUpdateCoordinator[dict[str, AwairResult]]): """Define a wrapper class to update Awair data.""" def __init__( @@ -107,7 +107,7 @@ class AwairCloudDataUpdateCoordinator(AwairDataUpdateCoordinator): super().__init__(hass, config_entry, UPDATE_INTERVAL_CLOUD) - async def _async_update_data(self) -> dict[str, AwairResult] | None: + async def _async_update_data(self) -> dict[str, AwairResult]: """Update data via Awair client library.""" async with timeout(API_TIMEOUT): try: @@ -139,7 +139,7 @@ class AwairLocalDataUpdateCoordinator(AwairDataUpdateCoordinator): super().__init__(hass, config_entry, UPDATE_INTERVAL_LOCAL) - async def _async_update_data(self) -> dict[str, AwairResult] | None: + async def _async_update_data(self) -> dict[str, AwairResult]: """Update data via Awair client library.""" async with timeout(API_TIMEOUT): try: diff --git a/homeassistant/components/awair/const.py b/homeassistant/components/awair/const.py index c117129dd2a..d483df64298 100644 --- a/homeassistant/components/awair/const.py +++ b/homeassistant/components/awair/const.py @@ -19,8 +19,8 @@ from homeassistant.const import ( CONCENTRATION_PARTS_PER_MILLION, LIGHT_LUX, PERCENTAGE, - SOUND_PRESSURE_WEIGHTED_DBA, - TEMP_CELSIUS, + UnitOfSoundPressure, + UnitOfTemperature, ) API_CO2 = "carbon_dioxide" @@ -87,8 +87,8 @@ SENSOR_TYPES: tuple[AwairSensorEntityDescription, ...] = ( ), AwairSensorEntityDescription( key=API_SPL_A, - icon="mdi:ear-hearing", - native_unit_of_measurement=SOUND_PRESSURE_WEIGHTED_DBA, + device_class=SensorDeviceClass.SOUND_PRESSURE, + native_unit_of_measurement=UnitOfSoundPressure.WEIGHTED_DECIBEL_A, name="Sound level", unique_id_tag="sound_level", state_class=SensorStateClass.MEASUREMENT, @@ -104,7 +104,7 @@ SENSOR_TYPES: tuple[AwairSensorEntityDescription, ...] = ( AwairSensorEntityDescription( key=API_TEMP, device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, name="Temperature", unique_id_tag="TEMP", # matches legacy format state_class=SensorStateClass.MEASUREMENT, diff --git a/homeassistant/components/awair/sensor.py b/homeassistant/components/awair/sensor.py index 18805154283..dc48e0f92c3 100644 --- a/homeassistant/components/awair/sensor.py +++ b/homeassistant/components/awair/sensor.py @@ -215,8 +215,7 @@ class AwairSensor(CoordinatorEntity[AwairDataUpdateCoordinator], SensorEntity): @property def _air_data(self) -> AirData | None: """Return the latest data for our device, or None.""" - result: AwairResult | None = self.coordinator.data.get(self._device.uuid) - if result: + if result := self.coordinator.data.get(self._device.uuid): return result.air_data return None diff --git a/homeassistant/components/awair/strings.json b/homeassistant/components/awair/strings.json index 5b16f359403..6040bc1d7b0 100644 --- a/homeassistant/components/awair/strings.json +++ b/homeassistant/components/awair/strings.json @@ -25,7 +25,7 @@ } }, "discovery_confirm": { - "description": "Do you want to setup {model} ({device_id})?" + "description": "Do you want to set up {model} ({device_id})?" }, "user": { "menu_options": { diff --git a/homeassistant/components/awair/translations/en.json b/homeassistant/components/awair/translations/en.json index 9746eac4ca1..20a797768a0 100644 --- a/homeassistant/components/awair/translations/en.json +++ b/homeassistant/components/awair/translations/en.json @@ -22,7 +22,7 @@ "description": "You must register for an Awair developer access token at: {url}" }, "discovery_confirm": { - "description": "Do you want to setup {model} ({device_id})?" + "description": "Do you want to set up {model} ({device_id})?" }, "local": { "description": "Follow [these instructions]({url}) on how to enable the Awair Local API.\n\nClick submit when done." diff --git a/homeassistant/components/awair/translations/ko.json b/homeassistant/components/awair/translations/ko.json index 0f9f06d9e46..dde556c020d 100644 --- a/homeassistant/components/awair/translations/ko.json +++ b/homeassistant/components/awair/translations/ko.json @@ -1,16 +1,51 @@ { "config": { "abort": { + "already_configured_account": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "already_configured_device": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4", - "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4", + "unreachable": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" }, "error": { "invalid_access_token": "\uc561\uc138\uc2a4 \ud1a0\ud070\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", - "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4", + "unreachable": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" }, + "flow_title": "{model} ({device_id})", "step": { + "cloud": { + "data": { + "access_token": "\uc561\uc138\uc2a4 \ud1a0\ud070", + "email": "\uc774\uba54\uc77c" + }, + "description": "{url} \uc5d0\uc11c \uc5b4\uc6e8\uc5b4 \uac1c\ubc1c\uc790 \uc561\uc138\uc2a4 \ud1a0\ud070\uc744 \ub4f1\ub85d\ud574\uc57c \ud569\ub2c8\ub2e4." + }, + "discovery_confirm": { + "description": "{model} ( {device_id} )\ub97c \uc124\uc815\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" + }, + "local": { + "description": "\uc5b4\uc6e8\uc5b4 \ub85c\uceec API\ub97c \ud65c\uc131\ud654\ud558\ub294 \ubc29\ubc95\uc740 [\uc774 \uc9c0\uce68]( {url} )\uc744 \ub530\ub974\uc138\uc694. \n\n \uc644\ub8cc\ub418\uba74 \ud655\uc778\uc744 \ud074\ub9ad\ud569\ub2c8\ub2e4." + }, + "local_pick": { + "data": { + "device": "\uae30\uae30", + "host": "IP \uc8fc\uc18c" + } + }, + "reauth_confirm": { + "data": { + "access_token": "\uc561\uc138\uc2a4 \ud1a0\ud070", + "email": "\uc774\uba54\uc77c" + }, + "description": "\uc5b4\uc6e8\uc5b4 \uac1c\ubc1c\uc790 \uc561\uc138\uc2a4 \ud1a0\ud070\uc744 \ub2e4\uc2dc \uc785\ub825\ud558\uc138\uc694." + }, "user": { - "description": "https://developer.getawair.com/onboard/login \uc5d0 Awair \uac1c\ubc1c\uc790 \uc561\uc138\uc2a4 \ud1a0\ud070\uc744 \ub4f1\ub85d\ud574\uc57c\ud569\ub2c8\ub2e4" + "description": "https://developer.getawair.com/onboard/login \uc5d0 Awair \uac1c\ubc1c\uc790 \uc561\uc138\uc2a4 \ud1a0\ud070\uc744 \ub4f1\ub85d\ud574\uc57c\ud569\ub2c8\ub2e4", + "menu_options": { + "cloud": "\ud074\ub77c\uc6b0\ub4dc\ub97c \ud1b5\ud574 \uc5f0\uacb0", + "local": "\ub85c\uceec\ub85c \uc5f0\uacb0(\uad8c\uc7a5)" + } } } } diff --git a/homeassistant/components/awair/translations/nl.json b/homeassistant/components/awair/translations/nl.json index c3d1e1d9583..6b7591ec79b 100644 --- a/homeassistant/components/awair/translations/nl.json +++ b/homeassistant/components/awair/translations/nl.json @@ -35,7 +35,8 @@ "user": { "description": "U moet zich registreren voor een Awair-toegangstoken voor ontwikkelaars op: https://developer.getawair.com/onboard/login", "menu_options": { - "cloud": "Verbinden via de cloud" + "cloud": "Verbinden via de cloud", + "local": "Lokaal verbinden (aanbevolen)" } } } diff --git a/homeassistant/components/awair/translations/pt.json b/homeassistant/components/awair/translations/pt.json index 085b6dd056d..09757c78559 100644 --- a/homeassistant/components/awair/translations/pt.json +++ b/homeassistant/components/awair/translations/pt.json @@ -5,19 +5,19 @@ "already_configured_device": "O dispositivo j\u00e1 est\u00e1 configurado", "no_devices_found": "Nenhum dispositivo encontrado na rede", "reauth_successful": "Reautentica\u00e7\u00e3o bem sucedida", - "unreachable": "Falha na liga\u00e7\u00e3o" + "unreachable": "A liga\u00e7\u00e3o falhou" }, "error": { "invalid_access_token": "Token de acesso inv\u00e1lido", "unknown": "Erro inesperado", - "unreachable": "Falha na liga\u00e7\u00e3o" + "unreachable": "A liga\u00e7\u00e3o falhou" }, "flow_title": "{model} ( {device_id} )", "step": { "cloud": { "data": { "access_token": "Token de Acesso", - "email": "Email" + "email": "" }, "description": "Voc\u00ea deve se registrar para um token de acesso de desenvolvedor Awair em: {url}" }, @@ -36,7 +36,7 @@ "reauth_confirm": { "data": { "access_token": "Token de Acesso", - "email": "Email" + "email": "" } }, "user": { diff --git a/homeassistant/components/awair/translations/sk.json b/homeassistant/components/awair/translations/sk.json index b8b771b2463..2b8daa3c050 100644 --- a/homeassistant/components/awair/translations/sk.json +++ b/homeassistant/components/awair/translations/sk.json @@ -18,11 +18,15 @@ "data": { "access_token": "Pr\u00edstupov\u00fd token", "email": "Email" - } + }, + "description": "Mus\u00edte sa zaregistrova\u0165 na z\u00edskanie pr\u00edstupov\u00e9ho tokenu pre v\u00fdvoj\u00e1rov Awair na: {url}" }, "discovery_confirm": { "description": "Chcete nastavi\u0165 {model} ({device_id})?" }, + "local": { + "description": "Postupujte pod\u013ea [t\u00fdchto pokynov]({url}), ako povoli\u0165 Awair Local API. \n\n Po dokon\u010den\u00ed kliknite na odosla\u0165." + }, "local_pick": { "data": { "device": "Zaradenie", @@ -33,9 +37,11 @@ "data": { "access_token": "Pr\u00edstupov\u00fd token", "email": "Email" - } + }, + "description": "Znova zadajte svoj pr\u00edstupov\u00fd token pre v\u00fdvoj\u00e1rov Awair." }, "user": { + "description": "Pro pr\u00edstupov\u00fd token v\u00fdvoj\u00e1ra Awair se mus\u00edte zaregistrova\u0165 na: https://developer.getawair.com/onboard/login", "menu_options": { "cloud": "Pripojenie cez cloud", "local": "Lok\u00e1lne pripojenie (preferovan\u00e9)" diff --git a/homeassistant/components/axis/translations/de.json b/homeassistant/components/axis/translations/de.json index 607000b6eaa..785e47700e8 100644 --- a/homeassistant/components/axis/translations/de.json +++ b/homeassistant/components/axis/translations/de.json @@ -2,8 +2,8 @@ "config": { "abort": { "already_configured": "Ger\u00e4t ist bereits konfiguriert", - "link_local_address": "Link-local Adressen werden nicht unterst\u00fctzt", - "not_axis_device": "Erkanntes Ger\u00e4t ist kein Axis-Ger\u00e4t" + "link_local_address": "Link-lokale Adressen werden nicht unterst\u00fctzt", + "not_axis_device": "Erkanntes Ger\u00e4t ist kein Axis Ger\u00e4t" }, "error": { "already_configured": "Ger\u00e4t ist bereits konfiguriert", diff --git a/homeassistant/components/axis/translations/pt.json b/homeassistant/components/axis/translations/pt.json index b74ecb2dc44..006d8e004cb 100644 --- a/homeassistant/components/axis/translations/pt.json +++ b/homeassistant/components/axis/translations/pt.json @@ -7,7 +7,7 @@ "error": { "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", "already_in_progress": "O processo de configura\u00e7\u00e3o j\u00e1 est\u00e1 a decorrer", - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida" }, "flow_title": "{name} ({host})", diff --git a/homeassistant/components/axis/translations/sk.json b/homeassistant/components/axis/translations/sk.json index b4415304f35..335ae5e1ff0 100644 --- a/homeassistant/components/axis/translations/sk.json +++ b/homeassistant/components/axis/translations/sk.json @@ -2,7 +2,8 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", - "link_local_address": "Lok\u00e1lne adresy odkazov nie s\u00fa podporovan\u00e9" + "link_local_address": "Lok\u00e1lne adresy odkazov nie s\u00fa podporovan\u00e9", + "not_axis_device": "Objaven\u00e9 zariadenie nie je zariadenie Axis" }, "error": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", @@ -18,7 +19,18 @@ "password": "Heslo", "port": "Port", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" - } + }, + "title": "Nastavenie zariadenia Axis" + } + } + }, + "options": { + "step": { + "configure_stream": { + "data": { + "stream_profile": "Vyberte profil streamu, ktor\u00fd chcete pou\u017ei\u0165" + }, + "title": "Mo\u017enosti video streamu zariadenia Axis" } } } diff --git a/homeassistant/components/azure_devops/__init__.py b/homeassistant/components/azure_devops/__init__.py index 645932f8b73..01c0cd123e3 100644 --- a/homeassistant/components/azure_devops/__init__.py +++ b/homeassistant/components/azure_devops/__init__.py @@ -48,7 +48,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: await client.authorize(entry.data[CONF_PAT], entry.data[CONF_ORG]) if not client.authorized: raise ConfigEntryAuthFailed( - "Could not authorize with Azure DevOps. You will need to update your token" + "Could not authorize with Azure DevOps. You will need to update your" + " token" ) project = await client.get_project( diff --git a/homeassistant/components/azure_devops/translations/de.json b/homeassistant/components/azure_devops/translations/de.json index eabc88625fb..10310905a1c 100644 --- a/homeassistant/components/azure_devops/translations/de.json +++ b/homeassistant/components/azure_devops/translations/de.json @@ -7,7 +7,7 @@ "error": { "cannot_connect": "Verbindung fehlgeschlagen", "invalid_auth": "Ung\u00fcltige Authentifizierung", - "project_error": "Konnte keine Projektinformationen erhalten." + "project_error": "Es konnten keine Projektinformationen erhalten werden" }, "flow_title": "{project_url}", "step": { diff --git a/homeassistant/components/azure_devops/translations/pt.json b/homeassistant/components/azure_devops/translations/pt.json index 66fad0c2b08..5ddb93cd08d 100644 --- a/homeassistant/components/azure_devops/translations/pt.json +++ b/homeassistant/components/azure_devops/translations/pt.json @@ -5,7 +5,7 @@ "reauth_successful": "Reautentica\u00e7\u00e3o bem sucedida" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida" }, "flow_title": "{project_url}" diff --git a/homeassistant/components/azure_devops/translations/sk.json b/homeassistant/components/azure_devops/translations/sk.json index bbfab69b01c..3aad2ddd2c4 100644 --- a/homeassistant/components/azure_devops/translations/sk.json +++ b/homeassistant/components/azure_devops/translations/sk.json @@ -23,7 +23,9 @@ "organization": "Organiz\u00e1cia", "personal_access_token": "Osobn\u00fd pr\u00edstupov\u00fd token (PAT)", "project": "Projekt" - } + }, + "description": "Nastavte in\u0161tanciu Azure DevOps na pr\u00edstup k v\u00e1\u0161mu projektu. Osobn\u00fd pr\u00edstupov\u00fd token sa vy\u017eaduje iba pre s\u00fakromn\u00fd projekt.", + "title": "Pridajte projekt Azure DevOps" } } } diff --git a/homeassistant/components/azure_event_hub/__init__.py b/homeassistant/components/azure_event_hub/__init__.py index 743463abda5..30417a7f94a 100644 --- a/homeassistant/components/azure_event_hub/__init__.py +++ b/homeassistant/components/azure_event_hub/__init__.py @@ -78,9 +78,10 @@ async def async_setup(hass: HomeAssistant, yaml_config: ConfigType) -> bool: if not yaml_config[DOMAIN]: return True _LOGGER.warning( - "Loading Azure Event Hub completely via yaml config is deprecated; Only the \ - Filter can be set in yaml, the rest is done through a config flow and has \ - been imported, all other keys but filter can be deleted from configuration.yaml" + "Loading Azure Event Hub completely via yaml config is deprecated; Only the" + " Filter can be set in yaml, the rest is done through a config flow and has" + " been imported, all other keys but filter can be deleted from" + " configuration.yaml" ) hass.async_create_task( hass.config_entries.flow.async_init( diff --git a/homeassistant/components/azure_event_hub/client.py b/homeassistant/components/azure_event_hub/client.py index 90880a92b64..dc1bfe37971 100644 --- a/homeassistant/components/azure_event_hub/client.py +++ b/homeassistant/components/azure_event_hub/client.py @@ -64,7 +64,9 @@ class AzureEventHubClientSAS(AzureEventHubClient): def client(self) -> EventHubProducerClient: """Get a Event Producer Client.""" return EventHubProducerClient( - fully_qualified_namespace=f"{self.event_hub_namespace}.servicebus.windows.net", + fully_qualified_namespace=( + f"{self.event_hub_namespace}.servicebus.windows.net" + ), eventhub_name=self.event_hub_instance_name, credential=EventHubSharedKeyCredential( # type: ignore[arg-type] policy=self.event_hub_sas_policy, key=self.event_hub_sas_key diff --git a/homeassistant/components/azure_event_hub/strings.json b/homeassistant/components/azure_event_hub/strings.json index 716b691959e..3f05e4b8e35 100644 --- a/homeassistant/components/azure_event_hub/strings.json +++ b/homeassistant/components/azure_event_hub/strings.json @@ -2,7 +2,7 @@ "config": { "step": { "user": { - "title": "Setup your Azure Event Hub integration", + "title": "Set up your Azure Event Hub integration", "data": { "event_hub_instance_name": "Event Hub Instance Name", "use_connection_string": "Use Connection String" diff --git a/homeassistant/components/azure_event_hub/translations/de.json b/homeassistant/components/azure_event_hub/translations/de.json index b72ef47ab34..a3629586095 100644 --- a/homeassistant/components/azure_event_hub/translations/de.json +++ b/homeassistant/components/azure_event_hub/translations/de.json @@ -16,11 +16,11 @@ "event_hub_connection_string": "Event Hub-Verbindungszeichenfolge" }, "description": "Bitte gib die Verbindungszeichenfolge ein f\u00fcr: {event_hub_instance_name}", - "title": "Verbindungszeichenfolgenmethode" + "title": "Verbindungszeichenfolge Methode" }, "sas": { "data": { - "event_hub_namespace": "Event Hub-Namespace", + "event_hub_namespace": "Event Hub-Namensraum", "event_hub_sas_key": "Event Hub SAS-Schl\u00fcssel", "event_hub_sas_policy": "Event Hub SAS-Richtlinie" }, @@ -40,7 +40,7 @@ "step": { "options": { "data": { - "send_interval": "Intervall zwischen dem Senden von Batches an den Hub." + "send_interval": "Intervall zwischen dem Senden von Stapeln an den Hub." }, "title": "Optionen f\u00fcr den Azure Event Hub." } diff --git a/homeassistant/components/azure_event_hub/translations/en.json b/homeassistant/components/azure_event_hub/translations/en.json index 4a4d8117e56..27db9f6fdd1 100644 --- a/homeassistant/components/azure_event_hub/translations/en.json +++ b/homeassistant/components/azure_event_hub/translations/en.json @@ -32,7 +32,7 @@ "event_hub_instance_name": "Event Hub Instance Name", "use_connection_string": "Use Connection String" }, - "title": "Setup your Azure Event Hub integration" + "title": "Set up your Azure Event Hub integration" } } }, diff --git a/homeassistant/components/azure_event_hub/translations/ko.json b/homeassistant/components/azure_event_hub/translations/ko.json index 814b5cba06f..3a730a5f0df 100644 --- a/homeassistant/components/azure_event_hub/translations/ko.json +++ b/homeassistant/components/azure_event_hub/translations/ko.json @@ -1,8 +1,14 @@ { "config": { "abort": { + "already_configured": "\uc11c\ube44\uc2a4\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "cannot_connect": "configuration.yaml\uc758 \uc815\ubcf4\ub85c \uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4. yaml\uc5d0\uc11c \uc81c\uac70\ud558\uace0 \uad6c\uc131 \ud750\ub984\uc744 \uc0ac\uc6a9\ud558\uc138\uc694.", + "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.", "unknown": "\uc54c \uc218 \uc5c6\ub294 \uc624\ub958\ub85c \uc778\ud574 configuration.yaml\uc758 \uc815\ubcf4\ub85c \uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4. yaml\uc5d0\uc11c \uc81c\uac70\ud558\uace0 \uad6c\uc131 \ud750\ub984\uc744 \uc0ac\uc6a9\ud558\uc138\uc694." + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" } } } \ No newline at end of file diff --git a/homeassistant/components/azure_event_hub/translations/pt.json b/homeassistant/components/azure_event_hub/translations/pt.json index cf10963fbd4..3f3384ed130 100644 --- a/homeassistant/components/azure_event_hub/translations/pt.json +++ b/homeassistant/components/azure_event_hub/translations/pt.json @@ -5,7 +5,7 @@ "single_instance_allowed": "J\u00e1 configurado. Apenas uma \u00fanica configura\u00e7\u00e3o \u00e9 poss\u00edvel." }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "unknown": "Erro inesperado" } } diff --git a/homeassistant/components/azure_event_hub/translations/sk.json b/homeassistant/components/azure_event_hub/translations/sk.json index cf133735e3b..293f8cd0da5 100644 --- a/homeassistant/components/azure_event_hub/translations/sk.json +++ b/homeassistant/components/azure_event_hub/translations/sk.json @@ -2,7 +2,9 @@ "config": { "abort": { "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1", - "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + "cannot_connect": "Pripojenie k povereniam zo s\u00faboru configuration.yaml zlyhalo, odstr\u00e1\u0148te ho z yaml a pou\u017eite konfigura\u010dn\u00fd postup.", + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia.", + "unknown": "Pripojenie k povereniam zo s\u00faboru configuration.yaml zlyhalo s nezn\u00e1mou chybou, odstr\u00e1\u0148te ho z yaml a pou\u017eite postup konfigur\u00e1cie." }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", @@ -10,13 +12,37 @@ }, "step": { "conn_string": { + "data": { + "event_hub_connection_string": "Pripojovac\u00ed re\u0165azec hubu udalost\u00ed" + }, "description": "Zadajte re\u0165azec pripojenia pre: {event_hub_instance_name}", "title": "Met\u00f3da re\u0165azca pripojenia" }, + "sas": { + "data": { + "event_hub_namespace": "Oblas\u0165 n\u00e1zvov centra udalost\u00ed", + "event_hub_sas_key": "K\u013e\u00fa\u010d SAS hub udalost\u00ed", + "event_hub_sas_policy": "Pravidl\u00e1 SAS hubu udalost\u00ed" + }, + "description": "Zadajte prihlasovacie \u00fadaje SAS (zdie\u013ean\u00fd pr\u00edstupov\u00fd podpis) pre: {event_hub_instance_name}", + "title": "Met\u00f3da poveren\u00ed SAS" + }, "user": { "data": { + "event_hub_instance_name": "N\u00e1zov in\u0161tancie hubu udalost\u00ed", "use_connection_string": "Pou\u017eitie re\u0165azca pripojenia" - } + }, + "title": "Nastavte integr\u00e1ciu Azure Event Hub" + } + } + }, + "options": { + "step": { + "options": { + "data": { + "send_interval": "Interval medzi odoslan\u00edm d\u00e1vok do hubu." + }, + "title": "Mo\u017enosti pre Azure Event Hub." } } } diff --git a/homeassistant/components/backup/manager.py b/homeassistant/components/backup/manager.py index 15fc4a7f648..cf42f49fa1b 100644 --- a/homeassistant/components/backup/manager.py +++ b/homeassistant/components/backup/manager.py @@ -129,7 +129,10 @@ class BackupManager: if not backup.path.exists(): LOGGER.debug( - "Removing tracked backup (%s) that does not exists on the expected path %s", + ( + "Removing tracked backup (%s) that does not exists on the expected" + " path %s" + ), backup.slug, backup.path, ) diff --git a/homeassistant/components/baf/climate.py b/homeassistant/components/baf/climate.py index d4ed4ac4337..6798639e7a8 100644 --- a/homeassistant/components/baf/climate.py +++ b/homeassistant/components/baf/climate.py @@ -10,7 +10,7 @@ from homeassistant.components.climate import ( HVACAction, HVACMode, ) -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -36,7 +36,7 @@ class BAFAutoComfort(BAFEntity, ClimateEntity): """BAF climate auto comfort.""" _attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS _attr_hvac_modes = [HVACMode.OFF, HVACMode.FAN_ONLY] @callback diff --git a/homeassistant/components/baf/manifest.json b/homeassistant/components/baf/manifest.json index 7462a64e770..bf9be6365b9 100644 --- a/homeassistant/components/baf/manifest.json +++ b/homeassistant/components/baf/manifest.json @@ -3,7 +3,7 @@ "name": "Big Ass Fans", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/baf", - "requirements": ["aiobafi6==0.7.2"], + "requirements": ["aiobafi6==0.7.3"], "codeowners": ["@bdraco", "@jfroy"], "iot_class": "local_push", "zeroconf": [ diff --git a/homeassistant/components/baf/number.py b/homeassistant/components/baf/number.py index 73d60fa6c03..0e014de6750 100644 --- a/homeassistant/components/baf/number.py +++ b/homeassistant/components/baf/number.py @@ -13,7 +13,7 @@ from homeassistant.components.number import ( NumberEntityDescription, NumberMode, ) -from homeassistant.const import TIME_SECONDS +from homeassistant.const import UnitOfTime from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -73,7 +73,7 @@ FAN_NUMBER_DESCRIPTIONS = ( native_min_value=ONE_MIN_SECS, native_max_value=HALF_DAY_SECS, entity_category=EntityCategory.CONFIG, - native_unit_of_measurement=TIME_SECONDS, + native_unit_of_measurement=UnitOfTime.SECONDS, value_fn=lambda device: cast(Optional[int], device.return_to_auto_timeout), mode=NumberMode.SLIDER, ), @@ -83,7 +83,7 @@ FAN_NUMBER_DESCRIPTIONS = ( native_min_value=ONE_MIN_SECS, native_max_value=ONE_DAY_SECS, entity_category=EntityCategory.CONFIG, - native_unit_of_measurement=TIME_SECONDS, + native_unit_of_measurement=UnitOfTime.SECONDS, value_fn=lambda device: cast(Optional[int], device.motion_sense_timeout), mode=NumberMode.SLIDER, ), @@ -96,7 +96,7 @@ LIGHT_NUMBER_DESCRIPTIONS = ( native_min_value=ONE_MIN_SECS, native_max_value=HALF_DAY_SECS, entity_category=EntityCategory.CONFIG, - native_unit_of_measurement=TIME_SECONDS, + native_unit_of_measurement=UnitOfTime.SECONDS, value_fn=lambda device: cast( Optional[int], device.light_return_to_auto_timeout ), @@ -108,7 +108,7 @@ LIGHT_NUMBER_DESCRIPTIONS = ( native_min_value=ONE_MIN_SECS, native_max_value=ONE_DAY_SECS, entity_category=EntityCategory.CONFIG, - native_unit_of_measurement=TIME_SECONDS, + native_unit_of_measurement=UnitOfTime.SECONDS, value_fn=lambda device: cast(Optional[int], device.light_auto_motion_timeout), mode=NumberMode.SLIDER, ), diff --git a/homeassistant/components/baf/sensor.py b/homeassistant/components/baf/sensor.py index 79ae320969b..81b48ae59d7 100644 --- a/homeassistant/components/baf/sensor.py +++ b/homeassistant/components/baf/sensor.py @@ -14,7 +14,7 @@ from homeassistant.components.sensor import ( SensorEntityDescription, SensorStateClass, ) -from homeassistant.const import PERCENTAGE, REVOLUTIONS_PER_MINUTE, TEMP_CELSIUS +from homeassistant.const import PERCENTAGE, REVOLUTIONS_PER_MINUTE, UnitOfTemperature from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -43,7 +43,7 @@ AUTO_COMFORT_SENSORS = ( BAFSensorDescription( key="temperature", name="Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, value_fn=lambda device: cast(Optional[float], device.temperature), diff --git a/homeassistant/components/baf/strings.json b/homeassistant/components/baf/strings.json index a26e3152326..59a20ea400c 100644 --- a/homeassistant/components/baf/strings.json +++ b/homeassistant/components/baf/strings.json @@ -8,7 +8,7 @@ } }, "discovery_confirm": { - "description": "Do you want to setup {name} - {model} ({ip_address})?" + "description": "Do you want to set up {name} - {model} ({ip_address})?" } }, "abort": { diff --git a/homeassistant/components/baf/translations/en.json b/homeassistant/components/baf/translations/en.json index 4bb7256a692..7072746a8db 100644 --- a/homeassistant/components/baf/translations/en.json +++ b/homeassistant/components/baf/translations/en.json @@ -11,7 +11,7 @@ "flow_title": "{name} - {model} ({ip_address})", "step": { "discovery_confirm": { - "description": "Do you want to setup {name} - {model} ({ip_address})?" + "description": "Do you want to set up {name} - {model} ({ip_address})?" }, "user": { "data": { diff --git a/homeassistant/components/baf/translations/no.json b/homeassistant/components/baf/translations/no.json index fa65c5ca21f..fa19ea46398 100644 --- a/homeassistant/components/baf/translations/no.json +++ b/homeassistant/components/baf/translations/no.json @@ -11,7 +11,7 @@ "flow_title": "{name} \u2013 {model} ( {ip_address} )", "step": { "discovery_confirm": { - "description": "Vil du konfigurere {name} - {model} ( {ip_address} )?" + "description": "Vil du sette opp {name} - {model} ( {ip_address} )?" }, "user": { "data": { diff --git a/homeassistant/components/balboa/climate.py b/homeassistant/components/balboa/climate.py index 1cd93b4fddb..c04f6367cfd 100644 --- a/homeassistant/components/balboa/climate.py +++ b/homeassistant/components/balboa/climate.py @@ -19,8 +19,7 @@ from homeassistant.const import ( ATTR_TEMPERATURE, PRECISION_HALVES, PRECISION_WHOLE, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -82,10 +81,10 @@ class BalboaSpaClimate(BalboaEntity, ClimateEntity): self._attr_supported_features |= ClimateEntityFeature.FAN_MODE self._attr_min_temp = self._client.tmin[self._client.TEMPRANGE_LOW][scale] self._attr_max_temp = self._client.tmax[self._client.TEMPRANGE_HIGH][scale] - self._attr_temperature_unit = TEMP_FAHRENHEIT + self._attr_temperature_unit = UnitOfTemperature.FAHRENHEIT self._attr_precision = PRECISION_WHOLE if self._client.get_tempscale() == self._client.TSCALE_C: - self._attr_temperature_unit = TEMP_CELSIUS + self._attr_temperature_unit = UnitOfTemperature.CELSIUS self._attr_precision = PRECISION_HALVES @property diff --git a/homeassistant/components/balboa/translations/ko.json b/homeassistant/components/balboa/translations/ko.json new file mode 100644 index 00000000000..85281856809 --- /dev/null +++ b/homeassistant/components/balboa/translations/ko.json @@ -0,0 +1,18 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/balboa/translations/pt.json b/homeassistant/components/balboa/translations/pt.json index 04374af8e82..632cb64e485 100644 --- a/homeassistant/components/balboa/translations/pt.json +++ b/homeassistant/components/balboa/translations/pt.json @@ -4,13 +4,13 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "unknown": "Erro inesperado" }, "step": { "user": { "data": { - "host": "Servidor" + "host": "Endere\u00e7o" } } } diff --git a/homeassistant/components/balboa/translations/sk.json b/homeassistant/components/balboa/translations/sk.json index be996d9dacf..fd3e7e123ea 100644 --- a/homeassistant/components/balboa/translations/sk.json +++ b/homeassistant/components/balboa/translations/sk.json @@ -15,5 +15,14 @@ "title": "Pripojte sa k zariadeniu Balboa Wi-Fi" } } + }, + "options": { + "step": { + "init": { + "data": { + "sync_time": "Udr\u017eujte \u010das svojho klienta Balboa Spa synchronizovan\u00fd s Home Assistant" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/bayesian/binary_sensor.py b/homeassistant/components/bayesian/binary_sensor.py index 28af050e85e..4cd68c7a9b4 100644 --- a/homeassistant/components/bayesian/binary_sensor.py +++ b/homeassistant/components/bayesian/binary_sensor.py @@ -266,9 +266,7 @@ class BayesianBinarySensor(BinarySensorEntity): ) if isinstance(result, TemplateError): _LOGGER.error( - "TemplateError('%s') " - "while processing template '%s' " - "in entity '%s'", + "TemplateError('%s') while processing template '%s' in entity '%s'", result, template, self.entity_id, @@ -369,12 +367,18 @@ class BayesianBinarySensor(BinarySensorEntity): # observation.observed is None if observation.entity_id is not None: _LOGGER.debug( - "Observation for entity '%s' returned None, it will not be used for Bayesian updating", + ( + "Observation for entity '%s' returned None, it will not be used" + " for Bayesian updating" + ), observation.entity_id, ) continue _LOGGER.debug( - "Observation for template entity returned None rather than a valid boolean, it will not be used for Bayesian updating", + ( + "Observation for template entity returned None rather than a valid" + " boolean, it will not be used for Bayesian updating" + ), ) # the prior has been updated and is now the posterior return prior diff --git a/homeassistant/components/bayesian/translations/de.json b/homeassistant/components/bayesian/translations/de.json index 2c3cfa28f5a..156f838536f 100644 --- a/homeassistant/components/bayesian/translations/de.json +++ b/homeassistant/components/bayesian/translations/de.json @@ -1,11 +1,11 @@ { "issues": { "manual_migration": { - "description": "Die Bayes'sche Integration aktualisiert nun auch die Wahrscheinlichkeit, wenn das beobachtete \u201eto_state\u201c, \u201eabove\u201c, \u201ebelow\u201c oder \u201evalue_template\u201c zu \u201eFalse\u201c und nicht nur zu \u201eTrue\u201c ausgewertet wird. Es ist also nicht l\u00e4nger erforderlich, doppelte, komplement\u00e4re Eintr\u00e4ge f\u00fcr jeden bin\u00e4ren Zustand zu haben. Bitte entferne den gespiegelten Eintrag f\u00fcr ` {entity} `.", + "description": "Die Bayes'sche Integration aktualisiert nun auch die Wahrscheinlichkeit, wenn das beobachtete \u201eto_state\u201c, \u201eabove\u201c, \u201ebelow\u201c oder \u201evalue_template\u201c zu \u201eFalse\u201c und nicht nur zu \u201eTrue\u201c ausgewertet wird. Es ist also nicht l\u00e4nger erforderlich, doppelte, komplement\u00e4re Eintr\u00e4ge f\u00fcr jeden bin\u00e4ren Zustand zu haben. Bitte entferne den gespiegelten Eintrag f\u00fcr `{entity}`.", "title": "Manuelle YAML-Korrektur f\u00fcr Bayes erforderlich" }, "no_prob_given_false": { - "description": "In der Bayes'schen Integration ist `prob_given_false` jetzt eine erforderliche Konfigurationsvariable, da es keine mathematische Begr\u00fcndung f\u00fcr den vorherigen Standardwert gab. Bitte f\u00fcge dies deiner `configuration.yml` f\u00fcr `bayesian/ {entity} ` hinzu. Diese Beobachtungen werden ignoriert, bis du dies tust.", + "description": "In der Bayes'schen Integration ist `prob_given_false` jetzt eine erforderliche Konfigurationsvariable, da es keine mathematische Begr\u00fcndung f\u00fcr den vorherigen Standardwert gab. Bitte f\u00fcge dies deiner `configuration.yml` f\u00fcr `bayesian/ {entity}` hinzu. Diese Beobachtungen werden ignoriert, bis du dies tust.", "title": "Manuelle YAML-Erg\u00e4nzung f\u00fcr Bayes erforderlich" } } diff --git a/homeassistant/components/bayesian/translations/sk.json b/homeassistant/components/bayesian/translations/sk.json new file mode 100644 index 00000000000..a2733b5bb37 --- /dev/null +++ b/homeassistant/components/bayesian/translations/sk.json @@ -0,0 +1,12 @@ +{ + "issues": { + "manual_migration": { + "description": "Bayesovsk\u00e1 integr\u00e1cia teraz aktualizuje pravdepodobnos\u0165 aj vtedy, ke\u010f sa pozorovan\u00e1 hodnota \"to_state\", \"above\", \"below\" alebo \"value_template\" vyhodnot\u00ed ako \"False\", a nie len ako \"True\". Tak\u017ee u\u017e nie je potrebn\u00e9 ma\u0165 duplicitn\u00e9 doplnkov\u00e9 z\u00e1znamy pre ka\u017ed\u00fd bin\u00e1rny stav. Odstr\u00e1\u0148te zrkadlen\u00fd z\u00e1znam pre `{entity}`.", + "title": "Pre Bayesian je potrebn\u00e1 manu\u00e1lna oprava YAML" + }, + "no_prob_given_false": { + "description": "V bayesovskej integr\u00e1cii je `prob_given_false` teraz povinnou konfigura\u010dnou premennou, preto\u017ee pre predch\u00e1dzaj\u00facu predvolen\u00fa hodnotu neexistovalo \u017eiadne matematick\u00e9 zd\u00f4vodnenie. Pridajte to do s\u00faboru `configuration.yml` pre `bayesian/{entity}`. Tieto pozorovania bud\u00fa ignorovan\u00e9, k\u00fdm to neurob\u00edte.", + "title": "Pre Bayesian je potrebn\u00e9 manu\u00e1lne pridanie YAML" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/bbox/sensor.py b/homeassistant/components/bbox/sensor.py index 43ba6956507..4cc77a4b780 100644 --- a/homeassistant/components/bbox/sensor.py +++ b/homeassistant/components/bbox/sensor.py @@ -15,11 +15,7 @@ from homeassistant.components.sensor import ( SensorEntityDescription, SensorStateClass, ) -from homeassistant.const import ( - CONF_MONITORED_VARIABLES, - CONF_NAME, - DATA_RATE_MEGABITS_PER_SECOND, -) +from homeassistant.const import CONF_MONITORED_VARIABLES, CONF_NAME, UnitOfDataRate from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -39,26 +35,30 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="down_max_bandwidth", name="Maximum Download Bandwidth", - native_unit_of_measurement=DATA_RATE_MEGABITS_PER_SECOND, + device_class=SensorDeviceClass.DATA_RATE, + native_unit_of_measurement=UnitOfDataRate.MEGABITS_PER_SECOND, icon="mdi:download", ), SensorEntityDescription( key="up_max_bandwidth", name="Maximum Upload Bandwidth", - native_unit_of_measurement=DATA_RATE_MEGABITS_PER_SECOND, + device_class=SensorDeviceClass.DATA_RATE, + native_unit_of_measurement=UnitOfDataRate.MEGABITS_PER_SECOND, icon="mdi:upload", ), SensorEntityDescription( key="current_down_bandwidth", name="Currently Used Download Bandwidth", - native_unit_of_measurement=DATA_RATE_MEGABITS_PER_SECOND, + device_class=SensorDeviceClass.DATA_RATE, + native_unit_of_measurement=UnitOfDataRate.MEGABITS_PER_SECOND, state_class=SensorStateClass.MEASUREMENT, icon="mdi:download", ), SensorEntityDescription( key="current_up_bandwidth", name="Currently Used Upload Bandwidth", - native_unit_of_measurement=DATA_RATE_MEGABITS_PER_SECOND, + device_class=SensorDeviceClass.DATA_RATE, + native_unit_of_measurement=UnitOfDataRate.MEGABITS_PER_SECOND, state_class=SensorStateClass.MEASUREMENT, icon="mdi:upload", ), diff --git a/homeassistant/components/beewi_smartclim/sensor.py b/homeassistant/components/beewi_smartclim/sensor.py index 4d8936859f5..0bb3a5bbb69 100644 --- a/homeassistant/components/beewi_smartclim/sensor.py +++ b/homeassistant/components/beewi_smartclim/sensor.py @@ -9,7 +9,7 @@ from homeassistant.components.sensor import ( SensorDeviceClass, SensorEntity, ) -from homeassistant.const import CONF_MAC, CONF_NAME, PERCENTAGE, TEMP_CELSIUS +from homeassistant.const import CONF_MAC, CONF_NAME, PERCENTAGE, UnitOfTemperature from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -20,7 +20,7 @@ DEFAULT_NAME = "BeeWi SmartClim" # Sensor config SENSOR_TYPES = [ - [SensorDeviceClass.TEMPERATURE, "Temperature", TEMP_CELSIUS], + [SensorDeviceClass.TEMPERATURE, "Temperature", UnitOfTemperature.CELSIUS], [SensorDeviceClass.HUMIDITY, "Humidity", PERCENTAGE], [SensorDeviceClass.BATTERY, "Battery", PERCENTAGE], ] diff --git a/homeassistant/components/binary_sensor/__init__.py b/homeassistant/components/binary_sensor/__init__.py index 46107938ddf..d99f569ed59 100644 --- a/homeassistant/components/binary_sensor/__init__.py +++ b/homeassistant/components/binary_sensor/__init__.py @@ -179,19 +179,19 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: class BinarySensorEntityDescription(EntityDescription): """A class that describes binary sensor entities.""" - device_class: BinarySensorDeviceClass | str | None = None + device_class: BinarySensorDeviceClass | None = None class BinarySensorEntity(Entity): """Represent a binary sensor.""" entity_description: BinarySensorEntityDescription - _attr_device_class: BinarySensorDeviceClass | str | None + _attr_device_class: BinarySensorDeviceClass | None _attr_is_on: bool | None = None _attr_state: None = None @property - def device_class(self) -> BinarySensorDeviceClass | str | None: + def device_class(self) -> BinarySensorDeviceClass | None: """Return the class of this entity.""" if hasattr(self, "_attr_device_class"): return self._attr_device_class diff --git a/homeassistant/components/binary_sensor/translations/cs.json b/homeassistant/components/binary_sensor/translations/cs.json index 9e5c5b9d7c2..efe4ac9582f 100644 --- a/homeassistant/components/binary_sensor/translations/cs.json +++ b/homeassistant/components/binary_sensor/translations/cs.json @@ -31,6 +31,7 @@ "is_not_plugged_in": "{entity_name} je odpojeno", "is_not_powered": "{entity_name} nen\u00ed nap\u00e1jeno", "is_not_present": "{entity_name} nen\u00ed p\u0159\u00edtomno", + "is_not_running": "{entity_name} nen\u00ed spu\u0161t\u011bno", "is_not_unsafe": "{entity_name} je bezpe\u010dno", "is_occupied": "{entity_name} je obsazeno", "is_off": "{entity_name} je vypnuto", @@ -40,6 +41,7 @@ "is_powered": "{entity_name} nap\u00e1jeno", "is_present": "{entity_name} p\u0159\u00edtomno", "is_problem": "{entity_name} detekuje probl\u00e9m", + "is_running": "{entity_name} je spu\u0161t\u011bno", "is_smoke": "{entity_name} detekuje kou\u0159", "is_sound": "{entity_name} detekuje zvuk", "is_unsafe": "{entity_name} nen\u00ed bezpe\u010dno", @@ -79,6 +81,7 @@ "not_plugged_in": "{entity_name} odpojeno", "not_powered": "{entity_name} nen\u00ed nap\u00e1jeno", "not_present": "{entity_name} nep\u0159\u00edtomno", + "not_running": "{entity_name} u\u017e neb\u011b\u017e\u00ed", "not_tampered": "{entity_name} p\u0159estalo detekovat neopr\u00e1vn\u011bnou manipulaci", "not_unsafe": "{entity_name} bezpe\u010dno", "occupied": "{entity_name} obsazeno", @@ -87,6 +90,7 @@ "powered": "{entity_name} nap\u00e1jeno", "present": "{entity_name} p\u0159\u00edtomno", "problem": "{entity_name} za\u010dalo detekovat probl\u00e9m", + "running": "{entity_name} spu\u0161t\u011bno", "smoke": "{entity_name} za\u010dalo detekovat kou\u0159", "sound": "{entity_name} za\u010dalo detekovat zvuk", "tampered": "{entity_name} za\u010dalo detekovat neopr\u00e1vn\u011bnou manipulaci", @@ -182,6 +186,10 @@ "off": "V po\u0159\u00e1dku", "on": "Probl\u00e9m" }, + "running": { + "off": "Nen\u00ed spu\u0161t\u011bno", + "on": "Spu\u0161t\u011bno" + }, "safety": { "off": "Zaji\u0161t\u011bno", "on": "Nezaji\u0161t\u011bno" diff --git a/homeassistant/components/binary_sensor/translations/sk.json b/homeassistant/components/binary_sensor/translations/sk.json index c6d0666cffb..252122699e2 100644 --- a/homeassistant/components/binary_sensor/translations/sk.json +++ b/homeassistant/components/binary_sensor/translations/sk.json @@ -9,6 +9,7 @@ "is_hot": "{entity_name} je hor\u00face", "is_light": "{entity_name} zaznamen\u00e1va svetlo", "is_locked": "{entity_name} je uzamknut\u00e9", + "is_moist": "{entity_name} je vlhk\u00fd", "is_motion": "{entity_name} zaznamen\u00e1va pohyb", "is_moving": "{entity_name} sa h\u00fdbe", "is_no_co": "{entity_name} nezaznamen\u00e1va oxid uho\u013enat\u00fd", @@ -34,6 +35,7 @@ "is_not_present": "{entity_name} nie je pr\u00edtomn\u00e9", "is_not_running": "{entity_name} nie je spusten\u00e9", "is_not_tampered": "{entity_name} nedetekuje manipul\u00e1ciu", + "is_not_unsafe": "{entity_name} je bezpe\u010dn\u00e9", "is_occupied": "{entity_name} je obsaden\u00e9", "is_off": "{entity_name} je vypnut\u00e9", "is_on": "{entity_name} je zapnut\u00e9", @@ -46,6 +48,7 @@ "is_smoke": "{entity_name} zis\u0165uje dym", "is_sound": "{entity_name} rozpozn\u00e1va zvuk", "is_tampered": "{entity_name} detekuje manipul\u00e1ciu", + "is_unsafe": "{entity_name} nie je bezpe\u010dn\u00e9", "is_update": "{entity_name} m\u00e1 k dispoz\u00edcii aktualiz\u00e1ciu", "is_vibration": "{entity_name} zaznamen\u00e1va vibr\u00e1cie" }, @@ -57,25 +60,49 @@ "gas": "{entity_name} detekuje plyn", "hot": "{n\u00e1zov_objektu} sa stal hor\u00facim", "light": "{entity_name} za\u010dal detekova\u0165 svetlo", + "locked": "{entity_name} zam\u010den\u00e9", "moist": "{entity_name} sa stal vlhk\u00fdm", "motion": "{entity_name} za\u010dal zis\u0165ova\u0165 pohyb", + "moving": "{entity_name} se za\u010dal pohybova\u0165", + "no_co": "{entity_name} prestalo detekova\u0165 oxid uho\u013enat\u00fd", + "no_gas": "{entity_name} prestalo detekova\u0165 plyn", + "no_light": "{entity_name} prestalo detekova\u0165 svetlo", + "no_motion": "{entity_name} prestalo detekova\u0165 pohyb", + "no_problem": "{entity_name} prestalo detekova\u0165 probl\u00e9m", + "no_smoke": "{entity_name} prestalo detekova\u0165 dym", + "no_sound": "{entity_name} prestalo detekova\u0165 zvuk", + "no_update": "{entity_name} se stalo aktu\u00e1lne", + "no_vibration": "{entity_name} prestalo detekova\u0165 vibr\u00e1cie", "not_bat_low": "{entity_name} bat\u00e9ria norm\u00e1lna", + "not_cold": "{entity_name} prestal by\u0165 studen\u00fd", "not_connected": "{entity_name} odpojen\u00fd", "not_hot": "{entity_name} prestal by\u0165 hor\u00facim", "not_locked": "{entity_name} odomknut\u00e9", "not_moist": "{n\u00e1zov_objektu} sa stal such\u00fdm", + "not_moving": "{entity_name} sa prestalo pohybova\u0165", + "not_occupied": "{entity_name} vo\u013en\u00e9", "not_opened": "{entity_name} zatvoren\u00e9", "not_plugged_in": "{entity_name} odpojen\u00fd", "not_powered": "{entity_name} nie je nap\u00e1jan\u00e9", "not_present": "{entity_name} nie je pr\u00edtomn\u00e9", "not_running": "{entity_name} u\u017e nie je v prev\u00e1dzke", + "not_tampered": "{entity_name} prestalo detekova\u0165 neopr\u00e1vnen\u00fa manipul\u00e1ciu", + "not_unsafe": "{entity_name} bezpe\u010dn\u00e9", + "occupied": "{entity_name} obsaden\u00e9", "opened": "{entity_name} otvoren\u00e9", "plugged_in": "{entity_name} zapojen\u00e9", "powered": "{entity_name} nap\u00e1jan\u00e9", + "present": "{entity_name} p\u0159\u00edtomn\u00e9", "problem": "{entity_name} za\u010dal zis\u0165ova\u0165 probl\u00e9m", + "running": "{entity_name} spusten\u00e9", "smoke": "{entity_name} za\u010dala zis\u0165ova\u0165 dym", "sound": "{entity_name} za\u010dala rozpozn\u00e1va\u0165 zvuk", - "update": "{entity_name} m\u00e1 k dispoz\u00edcii aktualiz\u00e1ciu" + "tampered": "{entity_name} za\u010dalo detekova\u0165 neopr\u00e1vnen\u00fa manipul\u00e1ciu", + "turned_off": "{entity_name} vypnut\u00e9", + "turned_on": "{entity_name} zapnut\u00e9", + "unsafe": "{entity_name} hl\u00e1si ohrozenie", + "update": "{entity_name} m\u00e1 k dispoz\u00edcii aktualiz\u00e1ciu", + "vibration": "{entity_name} za\u010dalo detekova\u0165 vibr\u00e1cie" } }, "device_class": { @@ -149,6 +176,10 @@ "off": "K\u013eud", "on": "Pohyb" }, + "moving": { + "off": "Neh\u00fdbe sa", + "on": "V pohybe" + }, "occupancy": { "off": "Vo\u013en\u00e9", "on": "Obsaden\u00e9" diff --git a/homeassistant/components/bitcoin/sensor.py b/homeassistant/components/bitcoin/sensor.py index 49691089f35..f8a38bbe470 100644 --- a/homeassistant/components/bitcoin/sensor.py +++ b/homeassistant/components/bitcoin/sensor.py @@ -12,12 +12,7 @@ from homeassistant.components.sensor import ( SensorEntity, SensorEntityDescription, ) -from homeassistant.const import ( - CONF_CURRENCY, - CONF_DISPLAY_OPTIONS, - TIME_MINUTES, - TIME_SECONDS, -) +from homeassistant.const import CONF_CURRENCY, CONF_DISPLAY_OPTIONS, UnitOfTime from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -65,7 +60,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="minutes_between_blocks", name="Time between Blocks", - native_unit_of_measurement=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, ), SensorEntityDescription( key="number_of_transactions", @@ -74,7 +69,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="hash_rate", name="Hash rate", - native_unit_of_measurement=f"PH/{TIME_SECONDS}", + native_unit_of_measurement=f"PH/{UnitOfTime.SECONDS}", ), SensorEntityDescription( key="timestamp", diff --git a/homeassistant/components/bizkaibus/sensor.py b/homeassistant/components/bizkaibus/sensor.py index 2c2d1b9db29..7078de158e7 100644 --- a/homeassistant/components/bizkaibus/sensor.py +++ b/homeassistant/components/bizkaibus/sensor.py @@ -7,7 +7,7 @@ from bizkaibus.bizkaibus import BizkaibusData import voluptuous as vol from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity -from homeassistant.const import CONF_NAME, TIME_MINUTES +from homeassistant.const import CONF_NAME, UnitOfTime from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -47,7 +47,7 @@ def setup_platform( class BizkaibusSensor(SensorEntity): """The class for handling the data.""" - _attr_native_unit_of_measurement = TIME_MINUTES + _attr_native_unit_of_measurement = UnitOfTime.MINUTES def __init__(self, data, name): """Initialize the sensor.""" diff --git a/homeassistant/components/blebox/__init__.py b/homeassistant/components/blebox/__init__.py index 1a7c8104652..a1e646c0b32 100644 --- a/homeassistant/components/blebox/__init__.py +++ b/homeassistant/components/blebox/__init__.py @@ -83,6 +83,7 @@ class BleBoxEntity(Entity, Generic[_FeatureT]): model=product.model, name=product.name, sw_version=product.firmware_version, + configuration_url=f"http://{product.address}", ) async def async_update(self) -> None: diff --git a/homeassistant/components/blebox/climate.py b/homeassistant/components/blebox/climate.py index 9b632c9aceb..94e79009853 100644 --- a/homeassistant/components/blebox/climate.py +++ b/homeassistant/components/blebox/climate.py @@ -12,7 +12,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -21,6 +21,19 @@ from .const import DOMAIN, PRODUCT SCAN_INTERVAL = timedelta(seconds=5) +BLEBOX_TO_HVACMODE = { + 0: HVACMode.OFF, + 1: HVACMode.HEAT, + 2: HVACMode.COOL, +} + +BLEBOX_TO_HVACACTION = { + 0: HVACAction.IDLE, + 1: HVACAction.HEATING, + 2: HVACAction.COOLING, + 3: HVACAction.IDLE, +} + async def async_setup_entry( hass: HomeAssistant, @@ -40,20 +53,29 @@ class BleBoxClimateEntity(BleBoxEntity[blebox_uniapi.climate.Climate], ClimateEn """Representation of a BleBox climate feature (saunaBox).""" _attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE - _attr_hvac_modes = [HVACMode.OFF, HVACMode.HEAT] - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS + + @property + def hvac_modes(self): + """Return list of supported HVAC modes.""" + return [HVACMode.OFF, self.hvac_mode] @property def hvac_mode(self): """Return the desired HVAC mode.""" if self._feature.is_on is None: return None - + if self._feature.mode is not None: + return BLEBOX_TO_HVACMODE[self._feature.mode] return HVACMode.HEAT if self._feature.is_on else HVACMode.OFF @property def hvac_action(self): """Return the actual current HVAC action.""" + if self._feature.hvac_action is not None: + if not self._feature.is_on: + return HVACAction.OFF + return BLEBOX_TO_HVACACTION[self._feature.hvac_action] if not (is_on := self._feature.is_on): return None if is_on is None else HVACAction.OFF @@ -82,7 +104,7 @@ class BleBoxClimateEntity(BleBoxEntity[blebox_uniapi.climate.Climate], ClimateEn async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None: """Set the climate entity mode.""" - if hvac_mode == HVACMode.HEAT: + if hvac_mode in [HVACMode.HEAT, HVACMode.COOL]: await self._feature.async_on() return diff --git a/homeassistant/components/blebox/config_flow.py b/homeassistant/components/blebox/config_flow.py index 5ae975f83d9..cf9a943b3df 100644 --- a/homeassistant/components/blebox/config_flow.py +++ b/homeassistant/components/blebox/config_flow.py @@ -1,13 +1,18 @@ """Config flow for BleBox devices integration.""" +from __future__ import annotations + import logging +from typing import Any from blebox_uniapi.box import Box -from blebox_uniapi.error import Error, UnsupportedBoxVersion +from blebox_uniapi.error import Error, UnsupportedBoxResponse, UnsupportedBoxVersion from blebox_uniapi.session import ApiHost import voluptuous as vol from homeassistant import config_entries +from homeassistant.components import zeroconf from homeassistant.const import CONF_HOST, CONF_PORT +from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers.aiohttp_client import async_get_clientsession from .const import ( @@ -74,9 +79,67 @@ class BleBoxConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): description_placeholders={"address": f"{host}:{port}"}, ) + async def async_step_zeroconf( + self, discovery_info: zeroconf.ZeroconfServiceInfo + ) -> FlowResult: + """Handle zeroconf discovery.""" + hass = self.hass + ipaddress = host_port(discovery_info.__dict__) + self.device_config["host"] = discovery_info.host + self.device_config["port"] = discovery_info.port + + websession = async_get_clientsession(hass) + + api_host = ApiHost( + *ipaddress, DEFAULT_SETUP_TIMEOUT, websession, hass.loop, _LOGGER + ) + + try: + product = await Box.async_from_host(api_host) + except UnsupportedBoxVersion: + return self.async_abort(reason="unsupported_device_version") + except UnsupportedBoxResponse: + return self.async_abort(reason="unsupported_device_response") + + self.device_config["name"] = product.name + # Check if configured but IP changed since + await self.async_set_unique_id(product.unique_id) + self._abort_if_unique_id_configured() + self.context.update( + { + "title_placeholders": { + "name": self.device_config["name"], + "host": self.device_config["host"], + }, + "configuration_url": f"http://{discovery_info.host}", + } + ) + return await self.async_step_confirm_discovery() + + async def async_step_confirm_discovery( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle discovery confirmation.""" + if user_input is not None: + return self.async_create_entry( + title=self.device_config["name"], + data={ + "host": self.device_config["host"], + "port": self.device_config["port"], + }, + ) + + return self.async_show_form( + step_id="confirm_discovery", + description_placeholders={ + "name": self.device_config["name"], + "host": self.device_config["host"], + "port": self.device_config["port"], + }, + ) + async def async_step_user(self, user_input=None): """Handle initial user-triggered config step.""" - hass = self.hass schema = create_schema(user_input) @@ -97,7 +160,6 @@ class BleBoxConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): reason=ADDRESS_ALREADY_CONFIGURED, description_placeholders={"address": f"{host}:{port}"}, ) - websession = async_get_clientsession(hass) api_host = ApiHost(*addr, DEFAULT_SETUP_TIMEOUT, websession, hass.loop, _LOGGER) try: @@ -119,7 +181,7 @@ class BleBoxConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): ) # Check if configured but IP changed since - await self.async_set_unique_id(product.unique_id) + await self.async_set_unique_id(product.unique_id, raise_on_progress=False) self._abort_if_unique_id_configured() return self.async_create_entry(title=product.name, data=user_input) diff --git a/homeassistant/components/blebox/light.py b/homeassistant/components/blebox/light.py index b138aae15b7..c4f13503abf 100644 --- a/homeassistant/components/blebox/light.py +++ b/homeassistant/components/blebox/light.py @@ -177,7 +177,8 @@ class BleBoxLightEntity(BleBoxEntity[blebox_uniapi.light.Light], LightEntity): await self._feature.async_api_command("effect", effect_value) except ValueError as exc: raise ValueError( - f"Turning on with effect '{self.name}' failed: {effect} not in effect list." + f"Turning on with effect '{self.name}' failed: {effect} not in" + " effect list." ) from exc async def async_turn_off(self, **kwargs: Any) -> None: diff --git a/homeassistant/components/blebox/manifest.json b/homeassistant/components/blebox/manifest.json index 78c7186eb31..39a0d57558f 100644 --- a/homeassistant/components/blebox/manifest.json +++ b/homeassistant/components/blebox/manifest.json @@ -6,5 +6,6 @@ "requirements": ["blebox_uniapi==2.1.3"], "codeowners": ["@bbx-a", "@riokuu"], "iot_class": "local_polling", - "loggers": ["blebox_uniapi"] + "loggers": ["blebox_uniapi"], + "zeroconf": ["_bbxsrv._tcp.local."] } diff --git a/homeassistant/components/blebox/sensor.py b/homeassistant/components/blebox/sensor.py index 471f8c6eb86..82c9bb876d7 100644 --- a/homeassistant/components/blebox/sensor.py +++ b/homeassistant/components/blebox/sensor.py @@ -9,7 +9,10 @@ from homeassistant.components.sensor import ( SensorEntityDescription, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, TEMP_CELSIUS +from homeassistant.const import ( + CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + UnitOfTemperature, +) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -35,7 +38,7 @@ SENSOR_TYPES = ( SensorEntityDescription( key="temperature", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, ), ) diff --git a/homeassistant/components/blebox/translations/de.json b/homeassistant/components/blebox/translations/de.json index 2b13adf2d69..814287222fb 100644 --- a/homeassistant/components/blebox/translations/de.json +++ b/homeassistant/components/blebox/translations/de.json @@ -1,13 +1,13 @@ { "config": { "abort": { - "address_already_configured": "Ein BleBox-Ger\u00e4t ist bereits unter {address} konfiguriert.", + "address_already_configured": "Ein BleBox Ger\u00e4t ist bereits unter {address} konfiguriert.", "already_configured": "Ger\u00e4t ist bereits konfiguriert" }, "error": { "cannot_connect": "Verbindung fehlgeschlagen", "unknown": "Unerwarteter Fehler", - "unsupported_version": "Das BleBox-Ger\u00e4t hat eine veraltete Firmware. Bitte aktualisiere es zuerst." + "unsupported_version": "Das BleBox Ger\u00e4t hat eine veraltete Firmware. Bitte aktualisiere es zuerst." }, "flow_title": "{name} ({host})", "step": { @@ -17,7 +17,7 @@ "port": "Port" }, "description": "Richte deine BleBox f\u00fcr die Integration mit dem Home Assistant ein.", - "title": "Richte dein BleBox-Ger\u00e4t ein" + "title": "Richte dein BleBox Ger\u00e4t ein" } } } diff --git a/homeassistant/components/blebox/translations/pt.json b/homeassistant/components/blebox/translations/pt.json index 8b581a984e7..0339fcf0529 100644 --- a/homeassistant/components/blebox/translations/pt.json +++ b/homeassistant/components/blebox/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "unknown": "Erro inesperado", "unsupported_version": "O dispositivo BleBox possui firmware desatualizado. Atualize-o primeiro." }, diff --git a/homeassistant/components/blebox/translations/sk.json b/homeassistant/components/blebox/translations/sk.json index fa1e3a90bbd..3ef311e7696 100644 --- a/homeassistant/components/blebox/translations/sk.json +++ b/homeassistant/components/blebox/translations/sk.json @@ -15,7 +15,9 @@ "data": { "host": "IP adresa", "port": "Port" - } + }, + "description": "Nastavte BleBox k integr\u00e1cii s Home Assistant.", + "title": "Nastavenie zariadenia BleBox" } } } diff --git a/homeassistant/components/blink/__init__.py b/homeassistant/components/blink/__init__.py index ef5a99356bc..668a7f99c02 100644 --- a/homeassistant/components/blink/__init__.py +++ b/homeassistant/components/blink/__init__.py @@ -55,7 +55,10 @@ def _reauth_flow_wrapper(hass, data): ) persistent_notification.async_create( hass, - "Blink configuration migrated to a new version. Please go to the integrations page to re-configure (such as sending a new 2FA key).", + ( + "Blink configuration migrated to a new version. Please go to the" + " integrations page to re-configure (such as sending a new 2FA key)." + ), "Blink Migration", ) diff --git a/homeassistant/components/blink/sensor.py b/homeassistant/components/blink/sensor.py index 3940522074b..bc52f484243 100644 --- a/homeassistant/components/blink/sensor.py +++ b/homeassistant/components/blink/sensor.py @@ -9,7 +9,7 @@ from homeassistant.components.sensor import ( SensorEntityDescription, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import SIGNAL_STRENGTH_DECIBELS_MILLIWATT, TEMP_FAHRENHEIT +from homeassistant.const import SIGNAL_STRENGTH_DECIBELS_MILLIWATT, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo, EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -22,7 +22,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key=TYPE_TEMPERATURE, name="Temperature", - native_unit_of_measurement=TEMP_FAHRENHEIT, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, device_class=SensorDeviceClass.TEMPERATURE, entity_category=EntityCategory.DIAGNOSTIC, ), diff --git a/homeassistant/components/blink/strings.json b/homeassistant/components/blink/strings.json index c0428703762..ae04f37714b 100644 --- a/homeassistant/components/blink/strings.json +++ b/homeassistant/components/blink/strings.json @@ -11,7 +11,7 @@ "2fa": { "title": "Two-factor authentication", "data": { "2fa": "Two-factor code" }, - "description": "Enter the PIN sent to your email" + "description": "Enter the PIN sent via email or SMS" } }, "error": { diff --git a/homeassistant/components/blink/translations/ca.json b/homeassistant/components/blink/translations/ca.json index 695db588b0d..ab7a2dd0dd9 100644 --- a/homeassistant/components/blink/translations/ca.json +++ b/homeassistant/components/blink/translations/ca.json @@ -14,7 +14,7 @@ "data": { "2fa": "Codi de dos factors" }, - "description": "Introdueix el PIN que s'ha enviat al teu correu electr\u00f2nic", + "description": "Introdueix el PIN enviat per correu electr\u00f2nic o SMS", "title": "Autenticaci\u00f3 de dos factors" }, "user": { diff --git a/homeassistant/components/blink/translations/de.json b/homeassistant/components/blink/translations/de.json index 8d3911d5f80..3cb64c47153 100644 --- a/homeassistant/components/blink/translations/de.json +++ b/homeassistant/components/blink/translations/de.json @@ -14,7 +14,7 @@ "data": { "2fa": "Zwei-Faktor Authentifizierungscode" }, - "description": "Gib die an deine E-Mail gesendete Pin ein", + "description": "Gib die per E-Mail oder SMS zugesandte PIN ein.", "title": "Zwei-Faktor-Authentifizierung" }, "user": { diff --git a/homeassistant/components/blink/translations/en.json b/homeassistant/components/blink/translations/en.json index c8c154418df..6f3c11af494 100644 --- a/homeassistant/components/blink/translations/en.json +++ b/homeassistant/components/blink/translations/en.json @@ -14,7 +14,7 @@ "data": { "2fa": "Two-factor code" }, - "description": "Enter the PIN sent to your email", + "description": "Enter the PIN sent via email or SMS", "title": "Two-factor authentication" }, "user": { diff --git a/homeassistant/components/blink/translations/es.json b/homeassistant/components/blink/translations/es.json index 17c724102eb..b2f5d5382ab 100644 --- a/homeassistant/components/blink/translations/es.json +++ b/homeassistant/components/blink/translations/es.json @@ -14,7 +14,7 @@ "data": { "2fa": "C\u00f3digo de dos factores" }, - "description": "Introduce el PIN enviado a tu correo electr\u00f3nico", + "description": "Introduce el PIN enviado por correo electr\u00f3nico o SMS", "title": "Autenticaci\u00f3n de dos factores" }, "user": { diff --git a/homeassistant/components/blink/translations/et.json b/homeassistant/components/blink/translations/et.json index a5cae0eaae2..ce3cd307cc2 100644 --- a/homeassistant/components/blink/translations/et.json +++ b/homeassistant/components/blink/translations/et.json @@ -14,7 +14,7 @@ "data": { "2fa": "2FA kood" }, - "description": "Sisesta e-posti aadressile saadetud PIN kood", + "description": "Sisesta e-kirja v\u00f5i SMS-iga saadetud PIN kood", "title": "Kaheastmeline tuvastamine (2FA)" }, "user": { diff --git a/homeassistant/components/blink/translations/hu.json b/homeassistant/components/blink/translations/hu.json index 1822dfbcf50..09822c8b6f5 100644 --- a/homeassistant/components/blink/translations/hu.json +++ b/homeassistant/components/blink/translations/hu.json @@ -14,7 +14,7 @@ "data": { "2fa": "K\u00e9tfaktoros k\u00f3d" }, - "description": "Adja meg az e-mail c\u00edm\u00e9re k\u00fcld\u00f6tt PIN-t", + "description": "Adja meg az e-mail-ben, vagy SMS-ben kapott PIN-t", "title": "K\u00e9tfaktoros hiteles\u00edt\u00e9s" }, "user": { diff --git a/homeassistant/components/blink/translations/id.json b/homeassistant/components/blink/translations/id.json index bdbc406bda7..381d050f158 100644 --- a/homeassistant/components/blink/translations/id.json +++ b/homeassistant/components/blink/translations/id.json @@ -14,7 +14,7 @@ "data": { "2fa": "Kode autentikasi dua faktor" }, - "description": "Masukkan PIN yang dikirimkan ke email Anda", + "description": "Masukkan PIN yang dikirimkan lewat SMS atau email", "title": "Autentikasi dua faktor" }, "user": { diff --git a/homeassistant/components/blink/translations/it.json b/homeassistant/components/blink/translations/it.json index 5e052c2d95e..3c168a8f11f 100644 --- a/homeassistant/components/blink/translations/it.json +++ b/homeassistant/components/blink/translations/it.json @@ -14,7 +14,7 @@ "data": { "2fa": "Codice a due fattori" }, - "description": "Inserisci il PIN inviato alla tua email", + "description": "Inserisci il PIN inviato via email o SMS", "title": "Autenticazione a due fattori" }, "user": { diff --git a/homeassistant/components/blink/translations/no.json b/homeassistant/components/blink/translations/no.json index 90f8fcaa06b..55c8d515e19 100644 --- a/homeassistant/components/blink/translations/no.json +++ b/homeassistant/components/blink/translations/no.json @@ -14,7 +14,7 @@ "data": { "2fa": "Totrinnsbekreftelse kode" }, - "description": "Skriv inn PIN-koden som er sendt til e-posten din", + "description": "Skriv inn PIN-koden sendt via e-post eller SMS", "title": "Totrinnsbekreftelse" }, "user": { diff --git a/homeassistant/components/blink/translations/pt-BR.json b/homeassistant/components/blink/translations/pt-BR.json index 440558cddd7..1842eaaca28 100644 --- a/homeassistant/components/blink/translations/pt-BR.json +++ b/homeassistant/components/blink/translations/pt-BR.json @@ -14,7 +14,7 @@ "data": { "2fa": "C\u00f3digo de dois fatores" }, - "description": "Digite o PIN enviado para o seu e-mail", + "description": "Digite o PIN enviado por e-mail ou SMS", "title": "Autentica\u00e7\u00e3o de dois fatores" }, "user": { diff --git a/homeassistant/components/blink/translations/pt.json b/homeassistant/components/blink/translations/pt.json index 1f71ecf3f22..0e5236220e0 100644 --- a/homeassistant/components/blink/translations/pt.json +++ b/homeassistant/components/blink/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_access_token": "Token de acesso inv\u00e1lido", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" diff --git a/homeassistant/components/blink/translations/sk.json b/homeassistant/components/blink/translations/sk.json index 6057b8ea9d4..f16c0765de3 100644 --- a/homeassistant/components/blink/translations/sk.json +++ b/homeassistant/components/blink/translations/sk.json @@ -14,7 +14,7 @@ "data": { "2fa": "Dvojfaktorov\u00fd k\u00f3d" }, - "description": "Zadajte PIN zaslan\u00fd na v\u00e1\u0161 e-mail", + "description": "Zadajte PIN zaslan\u00fd na e-mail alebo SMS", "title": "Dvojfaktorov\u00e1 autentifik\u00e1cia" }, "user": { @@ -31,7 +31,9 @@ "simple_options": { "data": { "scan_interval": "Interval skenovania (sekundy)" - } + }, + "description": "Nastavenie integr\u00e1cie Blink", + "title": "Mo\u017enosti Blink" } } } diff --git a/homeassistant/components/blink/translations/zh-Hant.json b/homeassistant/components/blink/translations/zh-Hant.json index 4596b55df9d..7fbc724c0dc 100644 --- a/homeassistant/components/blink/translations/zh-Hant.json +++ b/homeassistant/components/blink/translations/zh-Hant.json @@ -14,7 +14,7 @@ "data": { "2fa": "\u96d9\u91cd\u8a8d\u8b49\u78bc" }, - "description": "\u8f38\u5165\u90f5\u4ef6\u6240\u6536\u5230 PIN \u78bc", + "description": "\u8f38\u5165\u90f5\u4ef6\u6216\u7c21\u8a0a\u6240\u6536\u5230 PIN \u78bc", "title": "\u96d9\u91cd\u8a8d\u8b49" }, "user": { diff --git a/homeassistant/components/bloomsky/sensor.py b/homeassistant/components/bloomsky/sensor.py index 77978e6324d..6cefcdb3346 100644 --- a/homeassistant/components/bloomsky/sensor.py +++ b/homeassistant/components/bloomsky/sensor.py @@ -11,12 +11,10 @@ from homeassistant.components.sensor import ( from homeassistant.const import ( AREA_SQUARE_METERS, CONF_MONITORED_CONDITIONS, - ELECTRIC_POTENTIAL_MILLIVOLT, PERCENTAGE, - PRESSURE_INHG, - PRESSURE_MBAR, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, + UnitOfElectricPotential, + UnitOfPressure, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv @@ -37,25 +35,28 @@ SENSOR_TYPES = [ # Sensor units - these do not currently align with the API documentation SENSOR_UNITS_IMPERIAL = { - "Temperature": TEMP_FAHRENHEIT, + "Temperature": UnitOfTemperature.FAHRENHEIT, "Humidity": PERCENTAGE, - "Pressure": PRESSURE_INHG, + "Pressure": UnitOfPressure.INHG, "Luminance": f"cd/{AREA_SQUARE_METERS}", - "Voltage": ELECTRIC_POTENTIAL_MILLIVOLT, + "Voltage": UnitOfElectricPotential.MILLIVOLT, } # Metric units SENSOR_UNITS_METRIC = { - "Temperature": TEMP_CELSIUS, + "Temperature": UnitOfTemperature.CELSIUS, "Humidity": PERCENTAGE, - "Pressure": PRESSURE_MBAR, + "Pressure": UnitOfPressure.MBAR, "Luminance": f"cd/{AREA_SQUARE_METERS}", - "Voltage": ELECTRIC_POTENTIAL_MILLIVOLT, + "Voltage": UnitOfElectricPotential.MILLIVOLT, } # Device class SENSOR_DEVICE_CLASS = { "Temperature": SensorDeviceClass.TEMPERATURE, + "Humidity": SensorDeviceClass.HUMIDITY, + "Pressure": SensorDeviceClass.PRESSURE, + "Voltage": SensorDeviceClass.VOLTAGE, } # Which sensors to format numerically @@ -108,7 +109,7 @@ class BloomSkySensor(SensorEntity): ) @property - def device_class(self): + def device_class(self) -> SensorDeviceClass | None: """Return the class of this device, from component DEVICE_CLASSES.""" return SENSOR_DEVICE_CLASS.get(self._sensor_name) diff --git a/homeassistant/components/bluemaestro/sensor.py b/homeassistant/components/bluemaestro/sensor.py index 7fff348d587..d3776d418e5 100644 --- a/homeassistant/components/bluemaestro/sensor.py +++ b/homeassistant/components/bluemaestro/sensor.py @@ -24,9 +24,9 @@ from homeassistant.components.sensor import ( ) from homeassistant.const import ( PERCENTAGE, - PRESSURE_MBAR, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, - TEMP_CELSIUS, + UnitOfPressure, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory @@ -67,7 +67,7 @@ SENSOR_DESCRIPTIONS = { ): SensorEntityDescription( key=f"{BlueMaestroSensorDeviceClass.TEMPERATURE}_{Units.TEMP_CELSIUS}", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, ), ( @@ -76,7 +76,7 @@ SENSOR_DESCRIPTIONS = { ): SensorEntityDescription( key=f"{BlueMaestroSensorDeviceClass.DEW_POINT}_{Units.TEMP_CELSIUS}", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, ), ( @@ -85,7 +85,7 @@ SENSOR_DESCRIPTIONS = { ): SensorEntityDescription( key=f"{BlueMaestroSensorDeviceClass.PRESSURE}_{Units.PRESSURE_MBAR}", device_class=SensorDeviceClass.PRESSURE, - native_unit_of_measurement=PRESSURE_MBAR, + native_unit_of_measurement=UnitOfPressure.MBAR, state_class=SensorStateClass.MEASUREMENT, ), } diff --git a/homeassistant/components/bluemaestro/translations/en.json b/homeassistant/components/bluemaestro/translations/en.json index ebd9760c161..afe859ca766 100644 --- a/homeassistant/components/bluemaestro/translations/en.json +++ b/homeassistant/components/bluemaestro/translations/en.json @@ -9,13 +9,13 @@ "flow_title": "{name}", "step": { "bluetooth_confirm": { - "description": "Do you want to setup {name}?" + "description": "Do you want to set up {name}?" }, "user": { "data": { "address": "Device" }, - "description": "Choose a device to setup" + "description": "Choose a device to set up" } } } diff --git a/homeassistant/components/bluemaestro/translations/it.json b/homeassistant/components/bluemaestro/translations/it.json index 7784ed3a240..97113c57103 100644 --- a/homeassistant/components/bluemaestro/translations/it.json +++ b/homeassistant/components/bluemaestro/translations/it.json @@ -15,7 +15,7 @@ "data": { "address": "Dispositivo" }, - "description": "Seleziona un dispositivo da configurare" + "description": "Scegli un dispositivo da configurare" } } } diff --git a/homeassistant/components/bluemaestro/translations/ko.json b/homeassistant/components/bluemaestro/translations/ko.json new file mode 100644 index 00000000000..1a59c05b30c --- /dev/null +++ b/homeassistant/components/bluemaestro/translations/ko.json @@ -0,0 +1,9 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", + "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/bluemaestro/translations/no.json b/homeassistant/components/bluemaestro/translations/no.json index 0bf8b1695ec..38ab3d096f2 100644 --- a/homeassistant/components/bluemaestro/translations/no.json +++ b/homeassistant/components/bluemaestro/translations/no.json @@ -9,7 +9,7 @@ "flow_title": "{name}", "step": { "bluetooth_confirm": { - "description": "Vil du konfigurere {name}?" + "description": "Vil du sette opp {name} ?" }, "user": { "data": { diff --git a/homeassistant/components/bluemaestro/translations/pt.json b/homeassistant/components/bluemaestro/translations/pt.json index 5a10362e52b..a91d8b08e67 100644 --- a/homeassistant/components/bluemaestro/translations/pt.json +++ b/homeassistant/components/bluemaestro/translations/pt.json @@ -1,6 +1,9 @@ { "config": { "abort": { + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", + "already_in_progress": "O processo de configura\u00e7\u00e3o j\u00e1 est\u00e1 a decorrer", + "no_devices_found": "Nenhum dispositivo encontrado na rede", "not_supported": "Dispositivo n\u00e3o suportado" } } diff --git a/homeassistant/components/blueprint/importer.py b/homeassistant/components/blueprint/importer.py index f8b37a97c31..d857992a13c 100644 --- a/homeassistant/components/blueprint/importer.py +++ b/homeassistant/components/blueprint/importer.py @@ -125,7 +125,8 @@ def _extract_blueprint_from_community_topic( if blueprint is None: raise HomeAssistantError( - "No valid blueprint found in the topic. Blueprint syntax blocks need to be marked as YAML or no syntax." + "No valid blueprint found in the topic. Blueprint syntax blocks need to be" + " marked as YAML or no syntax." ) return ImportedBlueprint( @@ -209,7 +210,8 @@ async def fetch_blueprint_from_github_gist_url( if blueprint is None: raise HomeAssistantError( - "No valid blueprint found in the gist. The blueprint file needs to end with '.yaml'" + "No valid blueprint found in the gist. The blueprint file needs to end with" + " '.yaml'" ) return ImportedBlueprint( diff --git a/homeassistant/components/blueprint/models.py b/homeassistant/components/blueprint/models.py index ee81a583391..6f48080a451 100644 --- a/homeassistant/components/blueprint/models.py +++ b/homeassistant/components/blueprint/models.py @@ -69,7 +69,10 @@ class Blueprint: expected_domain, path or self.name, data, - f"Found incorrect blueprint type {data_domain}, expected {expected_domain}", + ( + f"Found incorrect blueprint type {data_domain}, expected" + f" {expected_domain}" + ), ) self.domain = data_domain diff --git a/homeassistant/components/bluetooth/__init__.py b/homeassistant/components/bluetooth/__init__.py index 8386178f459..d0a69bfe379 100644 --- a/homeassistant/components/bluetooth/__init__.py +++ b/homeassistant/components/bluetooth/__init__.py @@ -7,12 +7,15 @@ import platform from typing import TYPE_CHECKING from awesomeversion import AwesomeVersion +from bleak_retry_connector import BleakSlotManager from bluetooth_adapters import ( ADAPTER_ADDRESS, + ADAPTER_CONNECTION_SLOTS, ADAPTER_HW_VERSION, ADAPTER_MANUFACTURER, ADAPTER_SW_VERSION, DEFAULT_ADDRESS, + DEFAULT_CONNECTION_SLOTS, AdapterDetails, adapter_human_name, adapter_model, @@ -71,9 +74,14 @@ from .const import ( ) from .manager import BluetoothManager from .match import BluetoothCallbackMatcher, IntegrationMatcher -from .models import BluetoothCallback, BluetoothChange, BluetoothScanningMode +from .models import ( + BluetoothCallback, + BluetoothChange, + BluetoothScanningMode, + HaBluetoothConnector, +) from .scanner import HaScanner, ScannerStartError -from .wrappers import HaBluetoothConnector +from .storage import BluetoothStorage if TYPE_CHECKING: from homeassistant.helpers.typing import ConfigType @@ -158,7 +166,13 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: integration_matcher = IntegrationMatcher(await async_get_bluetooth(hass)) integration_matcher.async_setup() bluetooth_adapters = get_adapters() - manager = BluetoothManager(hass, integration_matcher, bluetooth_adapters) + bluetooth_storage = BluetoothStorage(hass) + await bluetooth_storage.async_setup() + slot_manager = BleakSlotManager() + await slot_manager.async_setup() + manager = BluetoothManager( + hass, integration_matcher, bluetooth_adapters, bluetooth_storage, slot_manager + ) await manager.async_setup() hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, manager.async_stop) hass.data[DATA_MANAGER] = models.MANAGER = manager @@ -261,7 +275,7 @@ async def async_discover_adapters( async def async_update_device( - hass: HomeAssistant, entry: ConfigEntry, adapter: str + hass: HomeAssistant, entry: ConfigEntry, adapter: str, details: AdapterDetails ) -> None: """Update device registry entry. @@ -270,11 +284,7 @@ async def async_update_device( update the device with the new location so they can figure out where the adapter is. """ - manager: BluetoothManager = hass.data[DATA_MANAGER] - adapters = await manager.async_get_bluetooth_adapters() - details = adapters[adapter] - registry = dr.async_get(manager.hass) - registry.async_get_or_create( + dr.async_get(hass).async_get_or_create( config_entry_id=entry.entry_id, name=adapter_human_name(adapter, details[ADAPTER_ADDRESS]), connections={(dr.CONNECTION_BLUETOOTH, details[ADAPTER_ADDRESS])}, @@ -298,6 +308,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: passive = entry.options.get(CONF_PASSIVE) mode = BluetoothScanningMode.PASSIVE if passive else BluetoothScanningMode.ACTIVE new_info_callback = async_get_advertisement_callback(hass) + manager: BluetoothManager = hass.data[DATA_MANAGER] scanner = HaScanner(hass, mode, adapter, address, new_info_callback) try: scanner.async_setup() @@ -309,8 +320,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: await scanner.async_start() except ScannerStartError as err: raise ConfigEntryNotReady from err - entry.async_on_unload(async_register_scanner(hass, scanner, True)) - await async_update_device(hass, entry, adapter) + adapters = await manager.async_get_bluetooth_adapters() + details = adapters[adapter] + slots: int = details.get(ADAPTER_CONNECTION_SLOTS) or DEFAULT_CONNECTION_SLOTS + entry.async_on_unload(async_register_scanner(hass, scanner, True, slots)) + await async_update_device(hass, entry, adapter, details) hass.data.setdefault(DOMAIN, {})[entry.entry_id] = scanner entry.async_on_unload(entry.add_update_listener(async_update_listener)) return True diff --git a/homeassistant/components/bluetooth/active_update_coordinator.py b/homeassistant/components/bluetooth/active_update_coordinator.py index ab26a0260f3..c4d40d5eaeb 100644 --- a/homeassistant/components/bluetooth/active_update_coordinator.py +++ b/homeassistant/components/bluetooth/active_update_coordinator.py @@ -1,4 +1,4 @@ -"""A Bluetooth passive coordinator that collects data from advertisements but can also poll.""" +"""A Bluetooth passive coordinator that receives data from advertisements but can also poll.""" from __future__ import annotations from collections.abc import Callable, Coroutine @@ -12,7 +12,7 @@ from homeassistant.helpers.debounce import Debouncer from homeassistant.util.dt import monotonic_time_coarse from . import BluetoothChange, BluetoothScanningMode, BluetoothServiceInfoBleak -from .passive_update_processor import PassiveBluetoothProcessorCoordinator +from .passive_update_coordinator import PassiveBluetoothDataUpdateCoordinator POLL_DEFAULT_COOLDOWN = 10 POLL_DEFAULT_IMMEDIATE = True @@ -20,11 +20,14 @@ POLL_DEFAULT_IMMEDIATE = True _T = TypeVar("_T") -class ActiveBluetoothProcessorCoordinator( - Generic[_T], PassiveBluetoothProcessorCoordinator[_T] +class ActiveBluetoothDataUpdateCoordinator( + Generic[_T], PassiveBluetoothDataUpdateCoordinator ): """ - A coordinator that parses passive data from advertisements but can also poll. + A coordinator that receives passive data from advertisements but can also poll. + + Unlike the passive processor coordinator, this coordinator does call a parser + method to parse the data from the advertisement. Every time an advertisement is received, needs_poll_method is called to work out if a poll is needed. This should return True if it is and False if it is @@ -46,6 +49,9 @@ class ActiveBluetoothProcessorCoordinator( BluetoothServiceInfoBleak.device contains a BLEDevice. You should use this in your poll function, as it is the most efficient way to get a BleakClient. + + Once the poll is complete, the coordinator will call _async_handle_bluetooth_poll + which needs to be implemented in the subclass. """ def __init__( @@ -55,7 +61,6 @@ class ActiveBluetoothProcessorCoordinator( *, address: str, mode: BluetoothScanningMode, - update_method: Callable[[BluetoothServiceInfoBleak], _T], needs_poll_method: Callable[[BluetoothServiceInfoBleak, float | None], bool], poll_method: Callable[ [BluetoothServiceInfoBleak], @@ -65,8 +70,12 @@ class ActiveBluetoothProcessorCoordinator( poll_debouncer: Debouncer[Coroutine[Any, Any, None]] | None = None, connectable: bool = True, ) -> None: - """Initialize the processor.""" - super().__init__(hass, logger, address, mode, update_method, connectable) + """Initialize the coordinator.""" + super().__init__(hass, logger, address, mode, connectable) + # It's None before the first successful update. + # Set type to just T to remove annoying checks that data is not None + # when it was already checked during setup. + self.data: _T = None # type: ignore[assignment] self._needs_poll_method = needs_poll_method self._poll_method = poll_method @@ -110,7 +119,7 @@ class ActiveBluetoothProcessorCoordinator( assert self._last_service_info try: - update = await self._async_poll_data(self._last_service_info) + self.data = await self._async_poll_data(self._last_service_info) except BleakError as exc: if self.last_poll_successful: self.logger.error( @@ -130,8 +139,12 @@ class ActiveBluetoothProcessorCoordinator( self.logger.debug("%s: Polling recovered") self.last_poll_successful = True - for processor in self._processors: - processor.async_handle_update(update) + self._async_handle_bluetooth_poll() + + @callback + def _async_handle_bluetooth_poll(self) -> None: + """Handle a poll event.""" + self.async_update_listeners() @callback def _async_handle_bluetooth_event( diff --git a/homeassistant/components/bluetooth/active_update_processor.py b/homeassistant/components/bluetooth/active_update_processor.py new file mode 100644 index 00000000000..e175fc665f4 --- /dev/null +++ b/homeassistant/components/bluetooth/active_update_processor.py @@ -0,0 +1,151 @@ +"""A Bluetooth passive processor coordinator that collects data from advertisements but can also poll.""" +from __future__ import annotations + +from collections.abc import Callable, Coroutine +import logging +from typing import Any, Generic, TypeVar + +from bleak import BleakError + +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.debounce import Debouncer +from homeassistant.util.dt import monotonic_time_coarse + +from . import BluetoothChange, BluetoothScanningMode, BluetoothServiceInfoBleak +from .passive_update_processor import PassiveBluetoothProcessorCoordinator + +POLL_DEFAULT_COOLDOWN = 10 +POLL_DEFAULT_IMMEDIATE = True + +_T = TypeVar("_T") + + +class ActiveBluetoothProcessorCoordinator( + Generic[_T], PassiveBluetoothProcessorCoordinator[_T] +): + """ + A processor coordinator that parses passive data from advertisements but can also poll. + + Every time an advertisement is received, needs_poll_method is called to work + out if a poll is needed. This should return True if it is and False if it is + not needed. + + def needs_poll_method(svc_info: BluetoothServiceInfoBleak, last_poll: float | None) -> bool: + return True + + If there has been no poll since HA started, `last_poll` will be None. Otherwise it is + the number of seconds since one was last attempted. + + If a poll is needed, the coordinator will call poll_method. This is a coroutine. + It should return the same type of data as your update_method. The expectation is that + data from advertisements and from polling are being parsed and fed into a shared + object that represents the current state of the device. + + async def poll_method(svc_info: BluetoothServiceInfoBleak) -> YourDataType: + return YourDataType(....) + + BluetoothServiceInfoBleak.device contains a BLEDevice. You should use this in + your poll function, as it is the most efficient way to get a BleakClient. + """ + + def __init__( + self, + hass: HomeAssistant, + logger: logging.Logger, + *, + address: str, + mode: BluetoothScanningMode, + update_method: Callable[[BluetoothServiceInfoBleak], _T], + needs_poll_method: Callable[[BluetoothServiceInfoBleak, float | None], bool], + poll_method: Callable[ + [BluetoothServiceInfoBleak], + Coroutine[Any, Any, _T], + ] + | None = None, + poll_debouncer: Debouncer[Coroutine[Any, Any, None]] | None = None, + connectable: bool = True, + ) -> None: + """Initialize the processor.""" + super().__init__(hass, logger, address, mode, update_method, connectable) + + self._needs_poll_method = needs_poll_method + self._poll_method = poll_method + self._last_poll: float | None = None + self.last_poll_successful = True + + # We keep the last service info in case the poller needs to refer to + # e.g. its BLEDevice + self._last_service_info: BluetoothServiceInfoBleak | None = None + + if poll_debouncer is None: + poll_debouncer = Debouncer( + hass, + logger, + cooldown=POLL_DEFAULT_COOLDOWN, + immediate=POLL_DEFAULT_IMMEDIATE, + function=self._async_poll, + ) + else: + poll_debouncer.function = self._async_poll + + self._debounced_poll = poll_debouncer + + def needs_poll(self, service_info: BluetoothServiceInfoBleak) -> bool: + """Return true if time to try and poll.""" + poll_age: float | None = None + if self._last_poll: + poll_age = monotonic_time_coarse() - self._last_poll + return self._needs_poll_method(service_info, poll_age) + + async def _async_poll_data( + self, last_service_info: BluetoothServiceInfoBleak + ) -> _T: + """Fetch the latest data from the source.""" + if self._poll_method is None: + raise NotImplementedError("Poll method not implemented") + return await self._poll_method(last_service_info) + + async def _async_poll(self) -> None: + """Poll the device to retrieve any extra data.""" + assert self._last_service_info + + try: + update = await self._async_poll_data(self._last_service_info) + except BleakError as exc: + if self.last_poll_successful: + self.logger.error( + "%s: Bluetooth error whilst polling: %s", self.address, str(exc) + ) + self.last_poll_successful = False + return + except Exception: # pylint: disable=broad-except + if self.last_poll_successful: + self.logger.exception("%s: Failure while polling", self.address) + self.last_poll_successful = False + return + finally: + self._last_poll = monotonic_time_coarse() + + if not self.last_poll_successful: + self.logger.debug("%s: Polling recovered") + self.last_poll_successful = True + + for processor in self._processors: + processor.async_handle_update(update) + + @callback + def _async_handle_bluetooth_event( + self, + service_info: BluetoothServiceInfoBleak, + change: BluetoothChange, + ) -> None: + """Handle a Bluetooth event.""" + super()._async_handle_bluetooth_event(service_info, change) + + self._last_service_info = service_info + + # See if its time to poll + # We use bluetooth events to trigger the poll so that we scan as soon as + # possible after a device comes online or back in range, if a poll is due + if self.needs_poll(service_info): + self.hass.async_create_task(self._debounced_poll.async_call()) diff --git a/homeassistant/components/bluetooth/api.py b/homeassistant/components/bluetooth/api.py index 582370ffbda..cd6b4ac959b 100644 --- a/homeassistant/components/bluetooth/api.py +++ b/homeassistant/components/bluetooth/api.py @@ -172,10 +172,15 @@ def async_rediscover_address(hass: HomeAssistant, address: str) -> None: @hass_callback def async_register_scanner( - hass: HomeAssistant, scanner: BaseHaScanner, connectable: bool + hass: HomeAssistant, + scanner: BaseHaScanner, + connectable: bool, + connection_slots: int | None = None, ) -> CALLBACK_TYPE: """Register a BleakScanner.""" - return _get_manager(hass).async_register_scanner(scanner, connectable) + return _get_manager(hass).async_register_scanner( + scanner, connectable, connection_slots + ) @hass_callback diff --git a/homeassistant/components/bluetooth/base_scanner.py b/homeassistant/components/bluetooth/base_scanner.py index 6204897c35c..d522d69cdbe 100644 --- a/homeassistant/components/bluetooth/base_scanner.py +++ b/homeassistant/components/bluetooth/base_scanner.py @@ -1,44 +1,123 @@ """Base classes for HA Bluetooth scanners for bluetooth.""" from __future__ import annotations -from abc import abstractmethod +from abc import ABC, abstractmethod from collections.abc import Callable, Generator from contextlib import contextmanager import datetime from datetime import timedelta +import logging from typing import Any, Final from bleak.backends.device import BLEDevice from bleak.backends.scanner import AdvertisementData from bleak_retry_connector import NO_RSSI_VALUE -from bluetooth_adapters import adapter_human_name +from bluetooth_adapters import DiscoveredDeviceAdvertisementData, adapter_human_name from home_assistant_bluetooth import BluetoothServiceInfoBleak -from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback as hass_callback +from homeassistant.const import EVENT_HOMEASSISTANT_STOP +from homeassistant.core import ( + CALLBACK_TYPE, + Event, + HomeAssistant, + callback as hass_callback, +) from homeassistant.helpers.event import async_track_time_interval +import homeassistant.util.dt as dt_util from homeassistant.util.dt import monotonic_time_coarse +from . import models from .const import ( CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS, FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS, + SCANNER_WATCHDOG_INTERVAL, + SCANNER_WATCHDOG_TIMEOUT, ) from .models import HaBluetoothConnector MONOTONIC_TIME: Final = monotonic_time_coarse +_LOGGER = logging.getLogger(__name__) -class BaseHaScanner: +class BaseHaScanner(ABC): """Base class for Ha Scanners.""" - __slots__ = ("hass", "source", "_connecting", "name", "scanning") + __slots__ = ( + "hass", + "adapter", + "connectable", + "source", + "connector", + "_connecting", + "name", + "scanning", + "_last_detection", + "_start_time", + "_cancel_watchdog", + ) - def __init__(self, hass: HomeAssistant, source: str, adapter: str) -> None: + def __init__( + self, + hass: HomeAssistant, + source: str, + adapter: str, + connector: HaBluetoothConnector | None = None, + ) -> None: """Initialize the scanner.""" self.hass = hass + self.connectable = False self.source = source + self.connector = connector self._connecting = 0 + self.adapter = adapter self.name = adapter_human_name(adapter, source) if adapter != source else source self.scanning = True + self._last_detection = 0.0 + self._start_time = 0.0 + self._cancel_watchdog: CALLBACK_TYPE | None = None + + @hass_callback + def _async_stop_scanner_watchdog(self) -> None: + """Stop the scanner watchdog.""" + if self._cancel_watchdog: + self._cancel_watchdog() + self._cancel_watchdog = None + + @hass_callback + def _async_setup_scanner_watchdog(self) -> None: + """If something has restarted or updated, we need to restart the scanner.""" + self._start_time = self._last_detection = MONOTONIC_TIME() + if not self._cancel_watchdog: + self._cancel_watchdog = async_track_time_interval( + self.hass, self._async_scanner_watchdog, SCANNER_WATCHDOG_INTERVAL + ) + + @hass_callback + def _async_watchdog_triggered(self) -> bool: + """Check if the watchdog has been triggered.""" + time_since_last_detection = MONOTONIC_TIME() - self._last_detection + _LOGGER.debug( + "%s: Scanner watchdog time_since_last_detection: %s", + self.name, + time_since_last_detection, + ) + return time_since_last_detection > SCANNER_WATCHDOG_TIMEOUT + + @hass_callback + def _async_scanner_watchdog(self, now: datetime.datetime) -> None: + """Check if the scanner is running. + + Override this method if you need to do something else when the watchdog is triggered. + """ + if self._async_watchdog_triggered(): + _LOGGER.info( + ( + "%s: Bluetooth scanner has gone quiet for %ss, check logs on the" + " scanner device for more information" + ), + self.name, + SCANNER_WATCHDOG_TIMEOUT, + ) @contextmanager def connecting(self) -> Generator[None, None, None]: @@ -66,7 +145,13 @@ class BaseHaScanner: async def async_diagnostics(self) -> dict[str, Any]: """Return diagnostic information about the scanner.""" return { + "name": self.name, + "start_time": self._start_time, + "source": self.source, + "scanning": self.scanning, "type": self.__class__.__name__, + "last_detection": self._last_detection, + "monotonic_time": MONOTONIC_TIME(), "discovered_devices_and_advertisement_data": [ { "name": device_adv[0].name, @@ -87,10 +172,9 @@ class BaseHaRemoteScanner(BaseHaScanner): "_new_info_callback", "_discovered_device_advertisement_datas", "_discovered_device_timestamps", - "_connector", - "_connectable", "_details", "_expire_seconds", + "_storage", ) def __init__( @@ -103,18 +187,18 @@ class BaseHaRemoteScanner(BaseHaScanner): connectable: bool, ) -> None: """Initialize the scanner.""" - super().__init__(hass, scanner_id, name) + super().__init__(hass, scanner_id, name, connector) self._new_info_callback = new_info_callback self._discovered_device_advertisement_datas: dict[ str, tuple[BLEDevice, AdvertisementData] ] = {} self._discovered_device_timestamps: dict[str, float] = {} - self._connector = connector - self._connectable = connectable + self.connectable = connectable self._details: dict[str, str | HaBluetoothConnector] = {"source": scanner_id} self._expire_seconds = FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + assert models.MANAGER is not None + self._storage = models.MANAGER.storage if connectable: - self._details["connector"] = connector self._expire_seconds = ( CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS ) @@ -122,9 +206,42 @@ class BaseHaRemoteScanner(BaseHaScanner): @hass_callback def async_setup(self) -> CALLBACK_TYPE: """Set up the scanner.""" - return async_track_time_interval( + if history := self._storage.async_get_advertisement_history(self.source): + self._discovered_device_advertisement_datas = ( + history.discovered_device_advertisement_datas + ) + self._discovered_device_timestamps = history.discovered_device_timestamps + # Expire anything that is too old + self._async_expire_devices(dt_util.utcnow()) + + cancel_track = async_track_time_interval( self.hass, self._async_expire_devices, timedelta(seconds=30) ) + cancel_stop = self.hass.bus.async_listen( + EVENT_HOMEASSISTANT_STOP, self._save_history + ) + self._async_setup_scanner_watchdog() + + @hass_callback + def _cancel() -> None: + self._save_history() + self._async_stop_scanner_watchdog() + cancel_track() + cancel_stop() + + return _cancel + + def _save_history(self, event: Event | None = None) -> None: + """Save the history.""" + self._storage.async_set_advertisement_history( + self.source, + DiscoveredDeviceAdvertisementData( + self.connectable, + self._expire_seconds, + self._discovered_device_advertisement_datas, + self._discovered_device_timestamps, + ), + ) def _async_expire_devices(self, _datetime: datetime.datetime) -> None: """Expire old devices.""" @@ -167,6 +284,7 @@ class BaseHaRemoteScanner(BaseHaScanner): ) -> None: """Call the registered callback.""" now = MONOTONIC_TIME() + self._last_detection = now if prev_discovery := self._discovered_device_advertisement_datas.get(address): # Merge the new data with the old data # to function the same as BlueZ which @@ -222,7 +340,22 @@ class BaseHaRemoteScanner(BaseHaScanner): source=self.source, device=device, advertisement=advertisement_data, - connectable=self._connectable, + connectable=self.connectable, time=now, ) ) + + async def async_diagnostics(self) -> dict[str, Any]: + """Return diagnostic information about the scanner.""" + now = MONOTONIC_TIME() + return await super().async_diagnostics() | { + "storage": self._storage.async_get_advertisement_history_as_dict( + self.source + ), + "connectable": self.connectable, + "discovered_device_timestamps": self._discovered_device_timestamps, + "time_since_last_device_detection": { + address: now - timestamp + for address, timestamp in self._discovered_device_timestamps.items() + }, + } diff --git a/homeassistant/components/bluetooth/manager.py b/homeassistant/components/bluetooth/manager.py index 513dc9dda14..748b685d866 100644 --- a/homeassistant/components/bluetooth/manager.py +++ b/homeassistant/components/bluetooth/manager.py @@ -9,7 +9,7 @@ import logging from typing import TYPE_CHECKING, Any, Final from bleak.backends.scanner import AdvertisementDataCallback -from bleak_retry_connector import NO_RSSI_VALUE, RSSI_SWITCH_THRESHOLD +from bleak_retry_connector import NO_RSSI_VALUE, RSSI_SWITCH_THRESHOLD, BleakSlotManager from bluetooth_adapters import ( ADAPTER_ADDRESS, ADAPTER_PASSIVE_SCAN, @@ -45,6 +45,7 @@ from .match import ( ble_device_matches, ) from .models import BluetoothCallback, BluetoothChange, BluetoothServiceInfoBleak +from .storage import BluetoothStorage from .usage import install_multiple_bleak_catcher, uninstall_multiple_bleak_catcher from .util import async_load_history_from_system @@ -102,6 +103,8 @@ class BluetoothManager: hass: HomeAssistant, integration_matcher: IntegrationMatcher, bluetooth_adapters: BluetoothAdapters, + storage: BluetoothStorage, + slot_manager: BleakSlotManager, ) -> None: """Init bluetooth manager.""" self.hass = hass @@ -128,6 +131,8 @@ class BluetoothManager: self._adapters: dict[str, AdapterDetails] = {} self._sources: dict[str, BaseHaScanner] = {} self._bluetooth_adapters = bluetooth_adapters + self.storage = storage + self.slot_manager = slot_manager @property def supports_passive_scan(self) -> bool: @@ -152,6 +157,7 @@ class BluetoothManager: ) return { "adapters": self._adapters, + "slot_manager": self.slot_manager.diagnostics(), "scanners": scanner_diagnostics, "connectable_history": [ service_info.as_dict() @@ -196,12 +202,9 @@ class BluetoothManager: """Set up the bluetooth manager.""" await self._bluetooth_adapters.refresh() install_multiple_bleak_catcher() - history = async_load_history_from_system(self._bluetooth_adapters) - # Everything is connectable so it fall into both - # buckets since the host system can only provide - # connectable devices - self._all_history = history.copy() - self._connectable_history = history.copy() + self._all_history, self._connectable_history = async_load_history_from_system( + self._bluetooth_adapters, self.storage + ) self.async_setup_unavailable_tracking() @hass_callback @@ -214,20 +217,19 @@ class BluetoothManager: uninstall_multiple_bleak_catcher() @hass_callback - def async_get_discovered_devices_and_advertisement_data_by_address( + def async_get_scanner_discovered_devices_and_advertisement_data_by_address( self, address: str, connectable: bool - ) -> list[tuple[BLEDevice, AdvertisementData]]: - """Get devices and advertisement_data by address.""" + ) -> list[tuple[BaseHaScanner, BLEDevice, AdvertisementData]]: + """Get scanner, devices, and advertisement_data by address.""" types_ = (True,) if connectable else (True, False) - return [ - device_advertisement_data - for device_advertisement_data in ( - scanner.discovered_devices_and_advertisement_data.get(address) - for type_ in types_ - for scanner in self._get_scanners_by_type(type_) - ) - if device_advertisement_data is not None - ] + results: list[tuple[BaseHaScanner, BLEDevice, AdvertisementData]] = [] + for type_ in types_: + for scanner in self._get_scanners_by_type(type_): + if device_advertisement_data := scanner.discovered_devices_and_advertisement_data.get( + address + ): + results.append((scanner, *device_advertisement_data)) + return results @hass_callback def _async_all_discovered_addresses(self, connectable: bool) -> Iterable[str]: @@ -318,7 +320,10 @@ class BluetoothManager: # If the old advertisement is stale, any new advertisement is preferred if debug: _LOGGER.debug( - "%s (%s): Switching from %s to %s (time elapsed:%s > stale seconds:%s)", + ( + "%s (%s): Switching from %s to %s (time elapsed:%s > stale" + " seconds:%s)" + ), new.name, new.address, self._async_describe_source(old), @@ -333,7 +338,10 @@ class BluetoothManager: # If new advertisement is RSSI_SWITCH_THRESHOLD more, the new one is preferred if debug: _LOGGER.debug( - "%s (%s): Switching from %s to %s (new rssi:%s - threshold:%s > old rssi:%s)", + ( + "%s (%s): Switching from %s to %s (new rssi:%s - threshold:%s >" + " old rssi:%s)" + ), new.name, new.address, self._async_describe_source(old), @@ -636,7 +644,10 @@ class BluetoothManager: return self._connectable_history if connectable else self._all_history def async_register_scanner( - self, scanner: BaseHaScanner, connectable: bool + self, + scanner: BaseHaScanner, + connectable: bool, + connection_slots: int | None = None, ) -> CALLBACK_TYPE: """Register a new scanner.""" _LOGGER.debug("Registering scanner %s", scanner.name) @@ -647,9 +658,13 @@ class BluetoothManager: self._advertisement_tracker.async_remove_source(scanner.source) scanners.remove(scanner) del self._sources[scanner.source] + if connection_slots: + self.slot_manager.remove_adapter(scanner.adapter) scanners.append(scanner) self._sources[scanner.source] = scanner + if connection_slots: + self.slot_manager.register_adapter(scanner.adapter, connection_slots) return _unregister_scanner @hass_callback @@ -673,3 +688,13 @@ class BluetoothManager: ) return _remove_callback + + @hass_callback + def async_release_connection_slot(self, device: BLEDevice) -> None: + """Release a connection slot.""" + self.slot_manager.release_slot(device) + + @hass_callback + def async_allocate_connection_slot(self, device: BLEDevice) -> bool: + """Allocate a connection slot.""" + return self.slot_manager.allocate_slot(device) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index 35659a0b1db..b20a3f17b50 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -7,11 +7,11 @@ "quality_scale": "internal", "requirements": [ "bleak==0.19.2", - "bleak-retry-connector==2.10.2", - "bluetooth-adapters==0.12.0", + "bleak-retry-connector==2.13.0", + "bluetooth-adapters==0.15.2", "bluetooth-auto-recovery==1.0.3", "bluetooth-data-tools==0.3.1", - "dbus-fast==1.75.0" + "dbus-fast==1.82.0" ], "codeowners": ["@bdraco"], "config_flow": true, diff --git a/homeassistant/components/bluetooth/passive_update_processor.py b/homeassistant/components/bluetooth/passive_update_processor.py index b04447cc4ee..04034293491 100644 --- a/homeassistant/components/bluetooth/passive_update_processor.py +++ b/homeassistant/components/bluetooth/passive_update_processor.py @@ -286,7 +286,8 @@ class PassiveBluetoothDataProcessor(Generic[_T]): if not isinstance(new_data, PassiveBluetoothDataUpdate): self.last_update_success = False # type: ignore[unreachable] raise ValueError( - f"The update_method for {self.coordinator.name} returned {new_data} instead of a PassiveBluetoothDataUpdate" + f"The update_method for {self.coordinator.name} returned" + f" {new_data} instead of a PassiveBluetoothDataUpdate" ) if not self.last_update_success: diff --git a/homeassistant/components/bluetooth/scanner.py b/homeassistant/components/bluetooth/scanner.py index d56c3f76c4c..a80386c25ef 100644 --- a/homeassistant/components/bluetooth/scanner.py +++ b/homeassistant/components/bluetooth/scanner.py @@ -20,13 +20,11 @@ from bleak_retry_connector import restore_discoveries from bluetooth_adapters import DEFAULT_ADDRESS from dbus_fast import InvalidMessageError -from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback as hass_callback +from homeassistant.core import HomeAssistant, callback as hass_callback from homeassistant.exceptions import HomeAssistantError -from homeassistant.helpers.event import async_track_time_interval -from homeassistant.util.dt import monotonic_time_coarse from homeassistant.util.package import is_docker_env -from .base_scanner import BaseHaScanner +from .base_scanner import MONOTONIC_TIME, BaseHaScanner from .const import ( SCANNER_WATCHDOG_INTERVAL, SCANNER_WATCHDOG_TIMEOUT, @@ -37,7 +35,6 @@ from .models import BluetoothScanningMode, BluetoothServiceInfoBleak from .util import async_reset_adapter OriginalBleakScanner = bleak.BleakScanner -MONOTONIC_TIME = monotonic_time_coarse # or_patterns is a workaround for the fact that passive scanning # needs at least one matcher to be set. The below matcher @@ -133,12 +130,9 @@ class HaScanner(BaseHaScanner): self.mac_address = address source = address if address != DEFAULT_ADDRESS else adapter or SOURCE_LOCAL super().__init__(hass, source, adapter) + self.connectable = True self.mode = mode - self.adapter = adapter self._start_stop_lock = asyncio.Lock() - self._cancel_watchdog: CALLBACK_TYPE | None = None - self._last_detection = 0.0 - self._start_time = 0.0 self._new_info_callback = new_info_callback self.scanning = False @@ -166,10 +160,6 @@ class HaScanner(BaseHaScanner): base_diag = await super().async_diagnostics() return base_diag | { "adapter": self.adapter, - "source": self.source, - "name": self.name, - "last_detection": self._last_detection, - "start_time": self._start_time, } @hass_callback @@ -272,7 +262,8 @@ class HaScanner(BaseHaScanner): await self._async_reset_adapter() continue raise ScannerStartError( - f"{self.name}: Timed out starting Bluetooth after {START_TIMEOUT} seconds" + f"{self.name}: Timed out starting Bluetooth after" + f" {START_TIMEOUT} seconds" ) from ex except BleakError as ex: error_str = str(ex) @@ -319,23 +310,9 @@ class HaScanner(BaseHaScanner): await restore_discoveries(self.scanner, self.adapter) @hass_callback - def _async_setup_scanner_watchdog(self) -> None: - """If Dbus gets restarted or updated, we need to restart the scanner.""" - self._start_time = self._last_detection = MONOTONIC_TIME() - if not self._cancel_watchdog: - self._cancel_watchdog = async_track_time_interval( - self.hass, self._async_scanner_watchdog, SCANNER_WATCHDOG_INTERVAL - ) - - async def _async_scanner_watchdog(self, now: datetime) -> None: + def _async_scanner_watchdog(self, now: datetime) -> None: """Check if the scanner is running.""" - time_since_last_detection = MONOTONIC_TIME() - self._last_detection - _LOGGER.debug( - "%s: Scanner watchdog time_since_last_detection: %s", - self.name, - time_since_last_detection, - ) - if time_since_last_detection < SCANNER_WATCHDOG_TIMEOUT: + if not self._async_watchdog_triggered(): return if self._start_stop_lock.locked(): _LOGGER.debug( @@ -348,7 +325,12 @@ class HaScanner(BaseHaScanner): self.name, SCANNER_WATCHDOG_TIMEOUT, ) + self.hass.async_create_task(self._async_restart_scanner()) + + async def _async_restart_scanner(self) -> None: + """Restart the scanner.""" async with self._start_stop_lock: + time_since_last_detection = MONOTONIC_TIME() - self._last_detection # Stop the scanner but not the watchdog # since we want to try again later if it's still quiet await self._async_stop_scanner() @@ -381,10 +363,8 @@ class HaScanner(BaseHaScanner): async def async_stop(self) -> None: """Stop bluetooth scanner.""" - if self._cancel_watchdog: - self._cancel_watchdog() - self._cancel_watchdog = None async with self._start_stop_lock: + self._async_stop_scanner_watchdog() await self._async_stop_scanner() async def _async_stop_scanner(self) -> None: diff --git a/homeassistant/components/bluetooth/storage.py b/homeassistant/components/bluetooth/storage.py new file mode 100644 index 00000000000..41354e95b2e --- /dev/null +++ b/homeassistant/components/bluetooth/storage.py @@ -0,0 +1,67 @@ +"""Storage for remote scanners.""" +from __future__ import annotations + +from bluetooth_adapters import ( + DiscoveredDeviceAdvertisementData, + DiscoveredDeviceAdvertisementDataDict, + DiscoveryStorageType, + discovered_device_advertisement_data_from_dict, + discovered_device_advertisement_data_to_dict, + expire_stale_scanner_discovered_device_advertisement_data, +) + +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.storage import Store + +REMOTE_SCANNER_STORAGE_VERSION = 1 +REMOTE_SCANNER_STORAGE_KEY = "bluetooth.remote_scanners" +SCANNER_SAVE_DELAY = 5 + + +class BluetoothStorage: + """Storage for remote scanners.""" + + def __init__(self, hass: HomeAssistant) -> None: + """Initialize the storage.""" + self._store: Store[DiscoveryStorageType] = Store( + hass, REMOTE_SCANNER_STORAGE_VERSION, REMOTE_SCANNER_STORAGE_KEY + ) + self._data: DiscoveryStorageType = {} + + async def async_setup(self) -> None: + """Set up the storage.""" + self._data = await self._store.async_load() or {} + expire_stale_scanner_discovered_device_advertisement_data(self._data) + + def scanners(self) -> list[str]: + """Get all scanners.""" + return list(self._data.keys()) + + @callback + def async_get_advertisement_history( + self, scanner: str + ) -> DiscoveredDeviceAdvertisementData | None: + """Get discovered devices by scanner.""" + if not (scanner_data := self._data.get(scanner)): + return None + return discovered_device_advertisement_data_from_dict(scanner_data) + + @callback + def async_get_advertisement_history_as_dict( + self, scanner: str + ) -> DiscoveredDeviceAdvertisementDataDict | None: + """Get discovered devices by scanner as a dict.""" + return self._data.get(scanner) + + @callback + def _async_get_data(self) -> DiscoveryStorageType: + """Get data to save to disk.""" + return self._data + + @callback + def async_set_advertisement_history( + self, scanner: str, data: DiscoveredDeviceAdvertisementData + ) -> None: + """Set discovered devices by scanner.""" + self._data[scanner] = discovered_device_advertisement_data_to_dict(data) + self._store.async_delay_save(self._async_get_data, SCANNER_SAVE_DELAY) diff --git a/homeassistant/components/bluetooth/strings.json b/homeassistant/components/bluetooth/strings.json index cfde1b90cd8..97662dbb9c7 100644 --- a/homeassistant/components/bluetooth/strings.json +++ b/homeassistant/components/bluetooth/strings.json @@ -9,22 +9,22 @@ "flow_title": "{name}", "step": { "user": { - "description": "Choose a device to setup", + "description": "Choose a device to set up", "data": { "address": "Device" } }, "bluetooth_confirm": { - "description": "Do you want to setup {name}?" + "description": "Do you want to set up {name}?" }, "multiple_adapters": { - "description": "Select a Bluetooth adapter to setup", + "description": "Select a Bluetooth adapter to set up", "data": { "adapter": "Adapter" } }, "single_adapter": { - "description": "Do you want to setup the Bluetooth adapter {name}?" + "description": "Do you want to set up the Bluetooth adapter {name}?" } }, "abort": { diff --git a/homeassistant/components/bluetooth/translations/en.json b/homeassistant/components/bluetooth/translations/en.json index beefb842204..8be4b41b7cb 100644 --- a/homeassistant/components/bluetooth/translations/en.json +++ b/homeassistant/components/bluetooth/translations/en.json @@ -7,22 +7,22 @@ "flow_title": "{name}", "step": { "bluetooth_confirm": { - "description": "Do you want to setup {name}?" + "description": "Do you want to set up {name}?" }, "multiple_adapters": { "data": { "adapter": "Adapter" }, - "description": "Select a Bluetooth adapter to setup" + "description": "Select a Bluetooth adapter to set up" }, "single_adapter": { - "description": "Do you want to setup the Bluetooth adapter {name}?" + "description": "Do you want to set up the Bluetooth adapter {name}?" }, "user": { "data": { "address": "Device" }, - "description": "Choose a device to setup" + "description": "Choose a device to set up" } } }, diff --git a/homeassistant/components/bluetooth/translations/et.json b/homeassistant/components/bluetooth/translations/et.json index d6413e23648..24bc0e7b8f9 100644 --- a/homeassistant/components/bluetooth/translations/et.json +++ b/homeassistant/components/bluetooth/translations/et.json @@ -16,7 +16,7 @@ "description": "Vali seadistamiseks Bluetooth-adapter" }, "single_adapter": { - "description": "Kas seadistada Bluetooth-adapterit {nimi}?" + "description": "Kas seadistada Bluetooth-adapterit {name}?" }, "user": { "data": { diff --git a/homeassistant/components/bluetooth/translations/it.json b/homeassistant/components/bluetooth/translations/it.json index 4252a84c889..1a21fc51890 100644 --- a/homeassistant/components/bluetooth/translations/it.json +++ b/homeassistant/components/bluetooth/translations/it.json @@ -22,7 +22,7 @@ "data": { "address": "Dispositivo" }, - "description": "Seleziona un dispositivo da configurare" + "description": "Scegli un dispositivo da configurare" } } }, diff --git a/homeassistant/components/bluetooth/translations/ko.json b/homeassistant/components/bluetooth/translations/ko.json new file mode 100644 index 00000000000..e1300423811 --- /dev/null +++ b/homeassistant/components/bluetooth/translations/ko.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "\uc11c\ube44\uc2a4\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/bluetooth/translations/no.json b/homeassistant/components/bluetooth/translations/no.json index 2246f72a748..52c6f7a88ef 100644 --- a/homeassistant/components/bluetooth/translations/no.json +++ b/homeassistant/components/bluetooth/translations/no.json @@ -7,7 +7,7 @@ "flow_title": "{name}", "step": { "bluetooth_confirm": { - "description": "Vil du konfigurere {name}?" + "description": "Vil du sette opp {name} ?" }, "multiple_adapters": { "data": { @@ -16,7 +16,7 @@ "description": "Velg en Bluetooth-adapter for \u00e5 konfigurere" }, "single_adapter": { - "description": "Vil du konfigurere Bluetooth-adapteren {name} ?" + "description": "Vil du sette opp Bluetooth-adapteren {name} ?" }, "user": { "data": { diff --git a/homeassistant/components/bluetooth/translations/pt.json b/homeassistant/components/bluetooth/translations/pt.json new file mode 100644 index 00000000000..d252c078a2c --- /dev/null +++ b/homeassistant/components/bluetooth/translations/pt.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "O servi\u00e7o j\u00e1 est\u00e1 configurado" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/bluetooth/translations/sk.json b/homeassistant/components/bluetooth/translations/sk.json index bbb745622ac..10fa3236069 100644 --- a/homeassistant/components/bluetooth/translations/sk.json +++ b/homeassistant/components/bluetooth/translations/sk.json @@ -28,7 +28,17 @@ }, "issues": { "haos_outdated": { + "description": "Ak chcete zlep\u0161i\u0165 spo\u013eahlivos\u0165 a v\u00fdkon Bluetooth, d\u00f4razne v\u00e1m odpor\u00fa\u010dame aktualizova\u0165 opera\u010dn\u00fd syst\u00e9m Home Assistant na verziu 9.0 alebo nov\u0161iu.", "title": "Aktualizujte na opera\u010dn\u00fd syst\u00e9m Home Assistant 9.0 alebo nov\u0161\u00ed" } + }, + "options": { + "step": { + "init": { + "data": { + "passive": "Pas\u00edvne skenovanie" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/bluetooth/update_coordinator.py b/homeassistant/components/bluetooth/update_coordinator.py index a02e601a878..ed2bfb5ffac 100644 --- a/homeassistant/components/bluetooth/update_coordinator.py +++ b/homeassistant/components/bluetooth/update_coordinator.py @@ -1,7 +1,7 @@ """Update coordinator for the Bluetooth integration.""" from __future__ import annotations -from abc import abstractmethod +from abc import ABC, abstractmethod import logging from typing import cast @@ -19,7 +19,7 @@ from . import ( ) -class BasePassiveBluetoothCoordinator: +class BasePassiveBluetoothCoordinator(ABC): """Base class for passive bluetooth coordinator for bluetooth advertisements. The coordinator is responsible for tracking devices. diff --git a/homeassistant/components/bluetooth/util.py b/homeassistant/components/bluetooth/util.py index e3f44daff2b..5419fa79e1c 100644 --- a/homeassistant/components/bluetooth/util.py +++ b/homeassistant/components/bluetooth/util.py @@ -8,32 +8,64 @@ from homeassistant.core import callback from homeassistant.util.dt import monotonic_time_coarse from .models import BluetoothServiceInfoBleak +from .storage import BluetoothStorage @callback def async_load_history_from_system( - adapters: BluetoothAdapters, -) -> dict[str, BluetoothServiceInfoBleak]: + adapters: BluetoothAdapters, storage: BluetoothStorage +) -> tuple[dict[str, BluetoothServiceInfoBleak], dict[str, BluetoothServiceInfoBleak]]: """Load the device and advertisement_data history if available on the current system.""" - now = monotonic_time_coarse() - return { - address: BluetoothServiceInfoBleak( - name=history.advertisement_data.local_name - or history.device.name - or history.device.address, - address=history.device.address, - rssi=history.advertisement_data.rssi, - manufacturer_data=history.advertisement_data.manufacturer_data, - service_data=history.advertisement_data.service_data, - service_uuids=history.advertisement_data.service_uuids, - source=history.source, - device=history.device, - advertisement=history.advertisement_data, - connectable=False, - time=now, - ) - for address, history in adapters.history.items() - } + now_monotonic = monotonic_time_coarse() + connectable_loaded_history: dict[str, BluetoothServiceInfoBleak] = {} + all_loaded_history: dict[str, BluetoothServiceInfoBleak] = {} + + # Restore local adapters + for address, history in adapters.history.items(): + if ( + not (existing_all := connectable_loaded_history.get(address)) + or history.advertisement_data.rssi > existing_all.rssi + ): + connectable_loaded_history[address] = all_loaded_history[ + address + ] = BluetoothServiceInfoBleak.from_device_and_advertisement_data( + history.device, + history.advertisement_data, + history.source, + now_monotonic, + True, + ) + + # Restore remote adapters + for scanner in storage.scanners(): + if not (adv_history := storage.async_get_advertisement_history(scanner)): + continue + + connectable = adv_history.connectable + discovered_device_timestamps = adv_history.discovered_device_timestamps + for ( + address, + (device, advertisement_data), + ) in adv_history.discovered_device_advertisement_datas.items(): + service_info = BluetoothServiceInfoBleak.from_device_and_advertisement_data( + device, + advertisement_data, + scanner, + discovered_device_timestamps[address], + connectable, + ) + if ( + not (existing_all := all_loaded_history.get(address)) + or service_info.rssi > existing_all.rssi + ): + all_loaded_history[address] = service_info + if connectable and ( + not (existing_connectable := connectable_loaded_history.get(address)) + or service_info.rssi > existing_connectable.rssi + ): + connectable_loaded_history[address] = service_info + + return all_loaded_history, connectable_loaded_history async def async_reset_adapter(adapter: str | None, mac_address: str) -> bool | None: diff --git a/homeassistant/components/bluetooth/wrappers.py b/homeassistant/components/bluetooth/wrappers.py index b1b06d43e31..a2c417ca382 100644 --- a/homeassistant/components/bluetooth/wrappers.py +++ b/homeassistant/components/bluetooth/wrappers.py @@ -5,31 +5,47 @@ import asyncio from collections.abc import Callable import contextlib from dataclasses import dataclass +from functools import partial import logging -from typing import Any, Final +from typing import TYPE_CHECKING, Any, Final from bleak import BleakClient, BleakError from bleak.backends.client import BaseBleakClient, get_platform_client_backend_type from bleak.backends.device import BLEDevice -from bleak.backends.scanner import AdvertisementDataCallback, BaseBleakScanner -from bleak_retry_connector import NO_RSSI_VALUE, ble_device_description, clear_cache +from bleak.backends.scanner import ( + AdvertisementData, + AdvertisementDataCallback, + BaseBleakScanner, +) +from bleak_retry_connector import ( + NO_RSSI_VALUE, + ble_device_description, + clear_cache, + device_source, +) from homeassistant.core import CALLBACK_TYPE, callback as hass_callback from homeassistant.helpers.frame import report from . import models -from .models import HaBluetoothConnector +from .base_scanner import BaseHaScanner FILTER_UUIDS: Final = "UUIDs" _LOGGER = logging.getLogger(__name__) +if TYPE_CHECKING: + from .manager import BluetoothManager + + @dataclass class _HaWrappedBleakBackend: """Wrap bleak backend to make it usable by Home Assistant.""" device: BLEDevice + scanner: BaseHaScanner client: type[BaseBleakClient] + source: str | None class HaBleakScannerWrapper(BaseBleakScanner): @@ -131,6 +147,33 @@ class HaBleakScannerWrapper(BaseBleakScanner): asyncio.get_running_loop().call_soon_threadsafe(self._detection_cancel) +def _rssi_sorter_with_connection_failure_penalty( + scanner_device_advertisement_data: tuple[ + BaseHaScanner, BLEDevice, AdvertisementData + ], + connection_failure_count: dict[BaseHaScanner, int], + rssi_diff: int, +) -> float: + """Get a sorted list of scanner, device, advertisement data adjusting for previous connection failures. + + When a connection fails, we want to try the next best adapter so we + apply a penalty to the RSSI value to make it less likely to be chosen + for every previous connection failure. + + We use the 51% of the RSSI difference between the first and second + best adapter as the penalty. This ensures we will always try the + best adapter twice before moving on to the next best adapter since + the first failure may be a transient service resolution issue. + """ + scanner, _, advertisement_data = scanner_device_advertisement_data + base_rssi = advertisement_data.rssi or NO_RSSI_VALUE + if connect_failures := connection_failure_count.get(scanner): + if connect_failures > 1 and not rssi_diff: + rssi_diff = 1 + return base_rssi - (rssi_diff * connect_failures * 0.51) + return base_rssi + + class HaBleakClientWrapper(BleakClient): """Wrap the BleakClient to ensure it does not shutdown our scanner. @@ -162,6 +205,7 @@ class HaBleakClientWrapper(BleakClient): self.__address = address_or_ble_device self.__disconnected_callback = disconnected_callback self.__timeout = timeout + self.__connect_failures: dict[BaseHaScanner, int] = {} self._backend: BaseBleakClient | None = None # type: ignore[assignment] @property @@ -188,70 +232,115 @@ class HaBleakClientWrapper(BleakClient): async def connect(self, **kwargs: Any) -> bool: """Connect to the specified GATT server.""" assert models.MANAGER is not None - wrapped_backend = self._async_get_best_available_backend_and_device() + manager = models.MANAGER + wrapped_backend = self._async_get_best_available_backend_and_device(manager) self._backend = wrapped_backend.client( wrapped_backend.device, disconnected_callback=self.__disconnected_callback, timeout=self.__timeout, - hass=models.MANAGER.hass, + hass=manager.hass, ) if debug_logging := _LOGGER.isEnabledFor(logging.DEBUG): # Only lookup the description if we are going to log it description = ble_device_description(wrapped_backend.device) rssi = wrapped_backend.device.rssi _LOGGER.debug("%s: Connecting (last rssi: %s)", description, rssi) - connected = await super().connect(**kwargs) + connected = None + try: + connected = await super().connect(**kwargs) + finally: + # If we failed to connect and its a local adapter (no source) + # we release the connection slot + if not connected: + self.__connect_failures[wrapped_backend.scanner] = ( + self.__connect_failures.get(wrapped_backend.scanner, 0) + 1 + ) + if not wrapped_backend.source: + manager.async_release_connection_slot(wrapped_backend.device) + if debug_logging: _LOGGER.debug("%s: Connected (last rssi: %s)", description, rssi) return connected @hass_callback def _async_get_backend_for_ble_device( - self, ble_device: BLEDevice + self, manager: BluetoothManager, scanner: BaseHaScanner, ble_device: BLEDevice ) -> _HaWrappedBleakBackend | None: """Get the backend for a BLEDevice.""" - details = ble_device.details - if not isinstance(details, dict) or "connector" not in details: + if not (source := device_source(ble_device)): # If client is not defined in details # its the client for this platform + if not manager.async_allocate_connection_slot(ble_device): + return None cls = get_platform_client_backend_type() - return _HaWrappedBleakBackend(ble_device, cls) + return _HaWrappedBleakBackend(ble_device, scanner, cls, source) - connector: HaBluetoothConnector = details["connector"] # Make sure the backend can connect to the device # as some backends have connection limits - if not connector.can_connect(): + if not scanner.connector or not scanner.connector.can_connect(): return None - return _HaWrappedBleakBackend(ble_device, connector.client) + return _HaWrappedBleakBackend( + ble_device, scanner, scanner.connector.client, source + ) @hass_callback def _async_get_best_available_backend_and_device( - self, + self, manager: BluetoothManager ) -> _HaWrappedBleakBackend: """Get a best available backend and device for the given address. This method will return the backend with the best rssi that has a free connection slot. """ - assert models.MANAGER is not None address = self.__address - device_advertisement_datas = models.MANAGER.async_get_discovered_devices_and_advertisement_data_by_address( + scanner_device_advertisement_datas = manager.async_get_scanner_discovered_devices_and_advertisement_data_by_address( address, True ) - for device_advertisement_data in sorted( - device_advertisement_datas, - key=lambda device_advertisement_data: device_advertisement_data[1].rssi + sorted_scanner_device_advertisement_datas = sorted( + scanner_device_advertisement_datas, + key=lambda scanner_device_advertisement_data: scanner_device_advertisement_data[ + 2 + ].rssi or NO_RSSI_VALUE, reverse=True, + ) + + # If we have connection failures we adjust the rssi sorting + # to prefer the adapter/scanner with the less failures so + # we don't keep trying to connect with an adapter + # that is failing + if ( + self.__connect_failures + and len(sorted_scanner_device_advertisement_datas) > 1 ): + # We use the rssi diff between to the top two + # to adjust the rssi sorter so that each failure + # will reduce the rssi sorter by the diff amount + rssi_diff = ( + sorted_scanner_device_advertisement_datas[0][2].rssi + - sorted_scanner_device_advertisement_datas[1][2].rssi + ) + adjusted_rssi_sorter = partial( + _rssi_sorter_with_connection_failure_penalty, + connection_failure_count=self.__connect_failures, + rssi_diff=rssi_diff, + ) + sorted_scanner_device_advertisement_datas = sorted( + scanner_device_advertisement_datas, + key=adjusted_rssi_sorter, + reverse=True, + ) + + for (scanner, ble_device, _) in sorted_scanner_device_advertisement_datas: if backend := self._async_get_backend_for_ble_device( - device_advertisement_data[0] + manager, scanner, ble_device ): return backend raise BleakError( - f"No backend with an available connection slot that can reach address {address} was found" + "No backend with an available connection slot that can reach address" + f" {address} was found" ) async def disconnect(self) -> bool: diff --git a/homeassistant/components/bluetooth_tracker/device_tracker.py b/homeassistant/components/bluetooth_tracker/device_tracker.py index ce8f6ca8006..c277985782c 100644 --- a/homeassistant/components/bluetooth_tracker/device_tracker.py +++ b/homeassistant/components/bluetooth_tracker/device_tracker.py @@ -187,7 +187,10 @@ async def async_setup_scanner( # If an update is in progress, we don't do anything if update_bluetooth_lock.locked(): _LOGGER.debug( - "Previous execution of update_bluetooth is taking longer than the scheduled update of interval %s", + ( + "Previous execution of update_bluetooth is taking longer than the" + " scheduled update of interval %s" + ), interval, ) return diff --git a/homeassistant/components/bmw_connected_drive/button.py b/homeassistant/components/bmw_connected_drive/button.py index 810edaf9617..873a72762ab 100644 --- a/homeassistant/components/bmw_connected_drive/button.py +++ b/homeassistant/components/bmw_connected_drive/button.py @@ -120,9 +120,11 @@ class BMWButton(BMWBaseEntity, ButtonEntity): await self.entity_description.remote_function(self.vehicle) elif self.entity_description.account_function: _LOGGER.warning( - "The 'Refresh from cloud' button is deprecated. Use the 'homeassistant.update_entity' " - "service with any BMW entity for a full reload. See https://www.home-assistant.io/" - "integrations/bmw_connected_drive/#update-the-state--refresh-from-api for details" + "The 'Refresh from cloud' button is deprecated. Use the" + " 'homeassistant.update_entity' service with any BMW entity for a full" + " reload. See" + " https://www.home-assistant.io/integrations/bmw_connected_drive/#update-the-state--refresh-from-api" + " for details" ) await self.entity_description.account_function(self.coordinator) diff --git a/homeassistant/components/bmw_connected_drive/const.py b/homeassistant/components/bmw_connected_drive/const.py index 6a8f82ae22d..50634ebdb96 100644 --- a/homeassistant/components/bmw_connected_drive/const.py +++ b/homeassistant/components/bmw_connected_drive/const.py @@ -1,10 +1,5 @@ """Const file for the MyBMW integration.""" -from homeassistant.const import ( - LENGTH_KILOMETERS, - LENGTH_MILES, - VOLUME_GALLONS, - VOLUME_LITERS, -) +from homeassistant.const import UnitOfLength, UnitOfVolume DOMAIN = "bmw_connected_drive" ATTRIBUTION = "Data provided by MyBMW" @@ -20,8 +15,8 @@ CONF_REFRESH_TOKEN = "refresh_token" DATA_HASS_CONFIG = "hass_config" UNIT_MAP = { - "KILOMETERS": LENGTH_KILOMETERS, - "MILES": LENGTH_MILES, - "LITERS": VOLUME_LITERS, - "GALLONS": VOLUME_GALLONS, + "KILOMETERS": UnitOfLength.KILOMETERS, + "MILES": UnitOfLength.MILES, + "LITERS": UnitOfVolume.LITERS, + "GALLONS": UnitOfVolume.GALLONS, } diff --git a/homeassistant/components/bmw_connected_drive/coordinator.py b/homeassistant/components/bmw_connected_drive/coordinator.py index 08e90d3c4e0..0f03505ff29 100644 --- a/homeassistant/components/bmw_connected_drive/coordinator.py +++ b/homeassistant/components/bmw_connected_drive/coordinator.py @@ -21,7 +21,7 @@ SCAN_INTERVAL = timedelta(seconds=DEFAULT_SCAN_INTERVAL_SECONDS) _LOGGER = logging.getLogger(__name__) -class BMWDataUpdateCoordinator(DataUpdateCoordinator): +class BMWDataUpdateCoordinator(DataUpdateCoordinator[None]): """Class to manage fetching BMW data.""" account: MyBMWAccount diff --git a/homeassistant/components/bmw_connected_drive/device_tracker.py b/homeassistant/components/bmw_connected_drive/device_tracker.py index fc33335fe24..c94d9b5b678 100644 --- a/homeassistant/components/bmw_connected_drive/device_tracker.py +++ b/homeassistant/components/bmw_connected_drive/device_tracker.py @@ -31,7 +31,10 @@ async def async_setup_entry( entities.append(BMWDeviceTracker(coordinator, vehicle)) if not vehicle.is_vehicle_tracking_enabled: _LOGGER.info( - "Tracking is (currently) disabled for vehicle %s (%s), defaulting to unknown", + ( + "Tracking is (currently) disabled for vehicle %s (%s), defaulting" + " to unknown" + ), vehicle.name, vehicle.vin, ) diff --git a/homeassistant/components/bmw_connected_drive/translations/de.json b/homeassistant/components/bmw_connected_drive/translations/de.json index 0ee49c105f4..5eb400d9e22 100644 --- a/homeassistant/components/bmw_connected_drive/translations/de.json +++ b/homeassistant/components/bmw_connected_drive/translations/de.json @@ -21,7 +21,7 @@ "step": { "account_options": { "data": { - "read_only": "Schreibgesch\u00fctzt (nur Sensoren und Notify, keine Ausf\u00fchrung von Diensten, kein Abschlie\u00dfen)" + "read_only": "Schreibgesch\u00fctzt (nur Sensoren und Benachrichtigungen, keine Ausf\u00fchrung von Diensten, kein Abschlie\u00dfen)" } } } diff --git a/homeassistant/components/bmw_connected_drive/translations/pt.json b/homeassistant/components/bmw_connected_drive/translations/pt.json index 3814c892bd1..941380d00fd 100644 --- a/homeassistant/components/bmw_connected_drive/translations/pt.json +++ b/homeassistant/components/bmw_connected_drive/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "Conta j\u00e1 configurada" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida" }, "step": { diff --git a/homeassistant/components/bmw_connected_drive/translations/sk.json b/homeassistant/components/bmw_connected_drive/translations/sk.json index bf40df63e99..bed4d73f135 100644 --- a/homeassistant/components/bmw_connected_drive/translations/sk.json +++ b/homeassistant/components/bmw_connected_drive/translations/sk.json @@ -11,6 +11,7 @@ "user": { "data": { "password": "Heslo", + "region": "Regi\u00f3n ConnectedDrive", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } diff --git a/homeassistant/components/bond/fan.py b/homeassistant/components/bond/fan.py index bd4f01bce52..a856af83bb8 100644 --- a/homeassistant/components/bond/fan.py +++ b/homeassistant/components/bond/fan.py @@ -151,7 +151,8 @@ class BondFan(BondEntity, FanEntity): ) except ClientResponseError as ex: raise HomeAssistantError( - f"The bond API returned an error calling set_power_state_belief for {self.entity_id}. Code: {ex.code} Message: {ex.message}" + "The bond API returned an error calling set_power_state_belief for" + f" {self.entity_id}. Code: {ex.code} Message: {ex.message}" ) from ex async def async_set_speed_belief(self, speed: int) -> None: @@ -175,7 +176,8 @@ class BondFan(BondEntity, FanEntity): ) except ClientResponseError as ex: raise HomeAssistantError( - f"The bond API returned an error calling set_speed_belief for {self.entity_id}. Code: {ex.code} Message: {ex.message}" + "The bond API returned an error calling set_speed_belief for" + f" {self.entity_id}. Code: {ex.code} Message: {ex.message}" ) from ex async def async_turn_on( diff --git a/homeassistant/components/bond/light.py b/homeassistant/components/bond/light.py index 2fcff44ddc1..2380321cc4c 100644 --- a/homeassistant/components/bond/light.py +++ b/homeassistant/components/bond/light.py @@ -137,7 +137,8 @@ class BondBaseLight(BondEntity, LightEntity): ) except ClientResponseError as ex: raise HomeAssistantError( - f"The bond API returned an error calling set_brightness_belief for {self.entity_id}. Code: {ex.code} Message: {ex.message}" + "The bond API returned an error calling set_brightness_belief for" + f" {self.entity_id}. Code: {ex.code} Message: {ex.message}" ) from ex async def async_set_power_belief(self, power_state: bool) -> None: @@ -148,7 +149,8 @@ class BondBaseLight(BondEntity, LightEntity): ) except ClientResponseError as ex: raise HomeAssistantError( - f"The bond API returned an error calling set_light_state_belief for {self.entity_id}. Code: {ex.code} Message: {ex.message}" + "The bond API returned an error calling set_light_state_belief for" + f" {self.entity_id}. Code: {ex.code} Message: {ex.message}" ) from ex @@ -197,7 +199,8 @@ class BondLight(BondBaseLight, BondEntity, LightEntity): async def async_start_increasing_brightness(self) -> None: """Start increasing the light brightness.""" _LOGGER.warning( - "The bond.start_increasing_brightness service is deprecated and has been replaced with a button; Call the button.press service instead" + "The bond.start_increasing_brightness service is deprecated and has been" + " replaced with a button; Call the button.press service instead" ) self._async_has_action_or_raise(Action.START_INCREASING_BRIGHTNESS) await self._hub.bond.action( @@ -207,7 +210,8 @@ class BondLight(BondBaseLight, BondEntity, LightEntity): async def async_start_decreasing_brightness(self) -> None: """Start decreasing the light brightness.""" _LOGGER.warning( - "The bond.start_decreasing_brightness service is deprecated and has been replaced with a button; Call the button.press service instead" + "The bond.start_decreasing_brightness service is deprecated and has been" + " replaced with a button; Call the button.press service instead" ) self._async_has_action_or_raise(Action.START_DECREASING_BRIGHTNESS) await self._hub.bond.action( @@ -217,7 +221,8 @@ class BondLight(BondBaseLight, BondEntity, LightEntity): async def async_stop(self) -> None: """Stop all actions and clear the queue.""" _LOGGER.warning( - "The bond.stop service is deprecated and has been replaced with a button; Call the button.press service instead" + "The bond.stop service is deprecated and has been replaced with a button;" + " Call the button.press service instead" ) self._async_has_action_or_raise(Action.STOP) await self._hub.bond.action(self._device.device_id, Action(Action.STOP)) @@ -307,7 +312,8 @@ class BondFireplace(BondEntity, LightEntity): ) except ClientResponseError as ex: raise HomeAssistantError( - f"The bond API returned an error calling set_brightness_belief for {self.entity_id}. Code: {ex.code} Message: {ex.message}" + "The bond API returned an error calling set_brightness_belief for" + f" {self.entity_id}. Code: {ex.code} Message: {ex.message}" ) from ex async def async_set_power_belief(self, power_state: bool) -> None: @@ -318,5 +324,6 @@ class BondFireplace(BondEntity, LightEntity): ) except ClientResponseError as ex: raise HomeAssistantError( - f"The bond API returned an error calling set_power_state_belief for {self.entity_id}. Code: {ex.code} Message: {ex.message}" + "The bond API returned an error calling set_power_state_belief for" + f" {self.entity_id}. Code: {ex.code} Message: {ex.message}" ) from ex diff --git a/homeassistant/components/bond/switch.py b/homeassistant/components/bond/switch.py index afa5e1cee10..c0ff6368e5a 100644 --- a/homeassistant/components/bond/switch.py +++ b/homeassistant/components/bond/switch.py @@ -64,5 +64,6 @@ class BondSwitch(BondEntity, SwitchEntity): ) except ClientResponseError as ex: raise HomeAssistantError( - f"The bond API returned an error calling set_power_state_belief for {self.entity_id}. Code: {ex.code} Message: {ex.message}" + "The bond API returned an error calling set_power_state_belief for" + f" {self.entity_id}. Code: {ex.code} Message: {ex.message}" ) from ex diff --git a/homeassistant/components/bond/translations/pt.json b/homeassistant/components/bond/translations/pt.json index 828e7c55baf..b5f24d1a5e2 100644 --- a/homeassistant/components/bond/translations/pt.json +++ b/homeassistant/components/bond/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, @@ -18,7 +18,7 @@ "user": { "data": { "access_token": "Token de Acesso", - "host": "Servidor" + "host": "Endere\u00e7o" } } } diff --git a/homeassistant/components/bosch_shc/sensor.py b/homeassistant/components/bosch_shc/sensor.py index 90fc44710a0..dad816a0908 100644 --- a/homeassistant/components/bosch_shc/sensor.py +++ b/homeassistant/components/bosch_shc/sensor.py @@ -12,10 +12,10 @@ from homeassistant.components.sensor import ( from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONCENTRATION_PARTS_PER_MILLION, - ENERGY_KILO_WATT_HOUR, PERCENTAGE, - POWER_WATT, - TEMP_CELSIUS, + UnitOfEnergy, + UnitOfPower, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -164,7 +164,7 @@ class TemperatureSensor(SHCEntity, SensorEntity): """Representation of an SHC temperature reporting sensor.""" _attr_device_class = SensorDeviceClass.TEMPERATURE - _attr_native_unit_of_measurement = TEMP_CELSIUS + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS def __init__(self, device: SHCDevice, parent_id: str, entry_id: str) -> None: """Initialize an SHC temperature reporting sensor.""" @@ -302,7 +302,7 @@ class PowerSensor(SHCEntity, SensorEntity): """Representation of an SHC power reporting sensor.""" _attr_device_class = SensorDeviceClass.POWER - _attr_native_unit_of_measurement = POWER_WATT + _attr_native_unit_of_measurement = UnitOfPower.WATT def __init__(self, device: SHCDevice, parent_id: str, entry_id: str) -> None: """Initialize an SHC power reporting sensor.""" @@ -321,7 +321,7 @@ class EnergySensor(SHCEntity, SensorEntity): _attr_device_class = SensorDeviceClass.ENERGY _attr_state_class = SensorStateClass.TOTAL_INCREASING - _attr_native_unit_of_measurement = ENERGY_KILO_WATT_HOUR + _attr_native_unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR def __init__(self, device: SHCDevice, parent_id: str, entry_id: str) -> None: """Initialize an SHC energy reporting sensor.""" diff --git a/homeassistant/components/bosch_shc/translations/de.json b/homeassistant/components/bosch_shc/translations/de.json index b99c00d6a6f..6ace3cedc95 100644 --- a/homeassistant/components/bosch_shc/translations/de.json +++ b/homeassistant/components/bosch_shc/translations/de.json @@ -8,7 +8,7 @@ "cannot_connect": "Verbindung fehlgeschlagen", "invalid_auth": "Ung\u00fcltige Authentifizierung", "pairing_failed": "Pairing fehlgeschlagen; bitte pr\u00fcfe, ob sich der Bosch Smart Home Controller im Pairing-Modus befindet (LED blinkt) und ob dein Passwort korrekt ist.", - "session_error": "Sitzungsfehler: API gab Non-OK-Ergebnis zur\u00fcck.", + "session_error": "Sitzungsfehler: API gab Non-OK Ergebnis zur\u00fcck.", "unknown": "Unerwarteter Fehler" }, "flow_title": "Bosch SHC: {name}", @@ -22,7 +22,7 @@ } }, "reauth_confirm": { - "description": "Die bosch_shc-Integration muss dein Konto neu authentifizieren", + "description": "Die bosch_shc Integration muss dein Konto neu authentifizieren", "title": "Integration erneut authentifizieren" }, "user": { diff --git a/homeassistant/components/bosch_shc/translations/pt.json b/homeassistant/components/bosch_shc/translations/pt.json index 1462f7a14a0..079ca08c504 100644 --- a/homeassistant/components/bosch_shc/translations/pt.json +++ b/homeassistant/components/bosch_shc/translations/pt.json @@ -5,7 +5,7 @@ "reauth_successful": "Reautentica\u00e7\u00e3o bem sucedida" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, @@ -15,7 +15,7 @@ }, "user": { "data": { - "host": "Servidor" + "host": "Endere\u00e7o" } } } diff --git a/homeassistant/components/bosch_shc/translations/sk.json b/homeassistant/components/bosch_shc/translations/sk.json index aa59cecc5fb..1a735edc5c2 100644 --- a/homeassistant/components/bosch_shc/translations/sk.json +++ b/homeassistant/components/bosch_shc/translations/sk.json @@ -7,6 +7,8 @@ "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie", + "pairing_failed": "P\u00e1rovanie zlyhalo; skontrolujte, \u010di je ovl\u00e1da\u010d Bosch Smart Home Controller v re\u017eime p\u00e1rovania (LED blik\u00e1) a \u010di je va\u0161e heslo spr\u00e1vne.", + "session_error": "Chyba rel\u00e1cie: API vr\u00e1tilo v\u00fdsledok Non-OK.", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "flow_title": "Bosch SHC: {name}", diff --git a/homeassistant/components/braviatv/config_flow.py b/homeassistant/components/braviatv/config_flow.py index 369aae374cf..43d2059c547 100644 --- a/homeassistant/components/braviatv/config_flow.py +++ b/homeassistant/components/braviatv/config_flow.py @@ -44,8 +44,6 @@ class BraviaTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): self.client: BraviaTV | None = None self.device_config: dict[str, Any] = {} self.entry: ConfigEntry | None = None - self.client_id: str = "" - self.nickname: str = "" @staticmethod @callback @@ -62,8 +60,13 @@ class BraviaTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): ) self.client = BraviaTV(host=host, session=session) - async def async_create_device(self) -> FlowResult: - """Initialize and create Bravia TV device from config.""" + async def gen_instance_ids(self) -> tuple[str, str]: + """Generate client_id and nickname.""" + uuid = await instance_id.async_get(self.hass) + return uuid, f"{NICKNAME_PREFIX} {uuid[:6]}" + + async def async_connect_device(self) -> None: + """Connect to Bravia TV device from config.""" assert self.client pin = self.device_config[CONF_PIN] @@ -72,13 +75,16 @@ class BraviaTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): if use_psk: await self.client.connect(psk=pin) else: - self.device_config[CONF_CLIENT_ID] = self.client_id - self.device_config[CONF_NICKNAME] = self.nickname - await self.client.connect( - pin=pin, clientid=self.client_id, nickname=self.nickname - ) + client_id = self.device_config[CONF_CLIENT_ID] + nickname = self.device_config[CONF_NICKNAME] + await self.client.connect(pin=pin, clientid=client_id, nickname=nickname) await self.client.set_wol_mode(True) + async def async_create_device(self) -> FlowResult: + """Create Bravia TV device from config.""" + assert self.client + await self.async_connect_device() + system_info = await self.client.get_system_info() cid = system_info[ATTR_CID].lower() title = system_info[ATTR_MODEL] @@ -90,6 +96,16 @@ class BraviaTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): return self.async_create_entry(title=title, data=self.device_config) + async def async_reauth_device(self) -> FlowResult: + """Reauthorize Bravia TV device from config.""" + assert self.entry + assert self.client + await self.async_connect_device() + + self.hass.config_entries.async_update_entry(self.entry, data=self.device_config) + await self.hass.config_entries.async_reload(self.entry.entry_id) + return self.async_abort(reason="reauth_successful") + async def async_step_user( self, user_input: dict[str, Any] | None = None ) -> FlowResult: @@ -100,28 +116,51 @@ class BraviaTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): host = user_input[CONF_HOST] if is_host_valid(host): self.device_config[CONF_HOST] = host - self.create_client() return await self.async_step_authorize() errors[CONF_HOST] = "invalid_host" return self.async_show_form( step_id="user", - data_schema=vol.Schema({vol.Required(CONF_HOST, default=""): str}), + data_schema=vol.Schema({vol.Required(CONF_HOST): str}), errors=errors, ) async def async_step_authorize( self, user_input: dict[str, Any] | None = None ) -> FlowResult: - """Authorize Bravia TV device.""" + """Handle authorize step.""" + self.create_client() + + if user_input is not None: + self.device_config[CONF_USE_PSK] = user_input[CONF_USE_PSK] + if user_input[CONF_USE_PSK]: + return await self.async_step_psk() + return await self.async_step_pin() + + return self.async_show_form( + step_id="authorize", + data_schema=vol.Schema( + { + vol.Required(CONF_USE_PSK, default=False): bool, + } + ), + ) + + async def async_step_pin( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle PIN authorize step.""" errors: dict[str, str] = {} - self.client_id, self.nickname = await self.gen_instance_ids() + client_id, nickname = await self.gen_instance_ids() if user_input is not None: self.device_config[CONF_PIN] = user_input[CONF_PIN] - self.device_config[CONF_USE_PSK] = user_input[CONF_USE_PSK] + self.device_config[CONF_CLIENT_ID] = client_id + self.device_config[CONF_NICKNAME] = nickname try: + if self.entry: + return await self.async_reauth_device() return await self.async_create_device() except BraviaTVAuthError: errors["base"] = "invalid_auth" @@ -133,16 +172,44 @@ class BraviaTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): assert self.client try: - await self.client.pair(self.client_id, self.nickname) + await self.client.pair(client_id, nickname) except BraviaTVError: return self.async_abort(reason="no_ip_control") return self.async_show_form( - step_id="authorize", + step_id="pin", data_schema=vol.Schema( { - vol.Required(CONF_PIN, default=""): str, - vol.Required(CONF_USE_PSK, default=False): bool, + vol.Required(CONF_PIN): str, + } + ), + errors=errors, + ) + + async def async_step_psk( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle PSK authorize step.""" + errors: dict[str, str] = {} + + if user_input is not None: + self.device_config[CONF_PIN] = user_input[CONF_PIN] + try: + if self.entry: + return await self.async_reauth_device() + return await self.async_create_device() + except BraviaTVAuthError: + errors["base"] = "invalid_auth" + except BraviaTVNotSupported: + errors["base"] = "unsupported_model" + except BraviaTVError: + errors["base"] = "cannot_connect" + + return self.async_show_form( + step_id="psk", + data_schema=vol.Schema( + { + vol.Required(CONF_PIN): str, } ), errors=errors, @@ -181,7 +248,6 @@ class BraviaTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): ) -> FlowResult: """Allow the user to confirm adding the device.""" if user_input is not None: - self.create_client() return await self.async_step_authorize() return self.async_show_form(step_id="confirm") @@ -190,59 +256,7 @@ class BraviaTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): """Handle configuration by re-auth.""" self.entry = self.hass.config_entries.async_get_entry(self.context["entry_id"]) self.device_config = {**entry_data} - return await self.async_step_reauth_confirm() - - async def async_step_reauth_confirm( - self, user_input: dict[str, Any] | None = None - ) -> FlowResult: - """Dialog that informs the user that reauth is required.""" - self.create_client() - client_id, nickname = await self.gen_instance_ids() - - assert self.client is not None - assert self.entry is not None - - if user_input is not None: - pin = user_input[CONF_PIN] - use_psk = user_input[CONF_USE_PSK] - try: - if use_psk: - await self.client.connect(psk=pin) - else: - self.device_config[CONF_CLIENT_ID] = client_id - self.device_config[CONF_NICKNAME] = nickname - await self.client.connect( - pin=pin, clientid=client_id, nickname=nickname - ) - await self.client.set_wol_mode(True) - except BraviaTVError: - return self.async_abort(reason="reauth_unsuccessful") - else: - self.hass.config_entries.async_update_entry( - self.entry, data={**self.device_config, **user_input} - ) - await self.hass.config_entries.async_reload(self.entry.entry_id) - return self.async_abort(reason="reauth_successful") - - try: - await self.client.pair(client_id, nickname) - except BraviaTVError: - return self.async_abort(reason="reauth_unsuccessful") - - return self.async_show_form( - step_id="reauth_confirm", - data_schema=vol.Schema( - { - vol.Required(CONF_PIN, default=""): str, - vol.Required(CONF_USE_PSK, default=False): bool, - } - ), - ) - - async def gen_instance_ids(self) -> tuple[str, str]: - """Generate client_id and nickname.""" - uuid = await instance_id.async_get(self.hass) - return uuid, f"{NICKNAME_PREFIX} {uuid[:6]}" + return await self.async_step_authorize() class BraviaTVOptionsFlowHandler(config_entries.OptionsFlowWithConfigEntry): diff --git a/homeassistant/components/braviatv/manifest.json b/homeassistant/components/braviatv/manifest.json index fa009bf05ef..83fe34fed28 100644 --- a/homeassistant/components/braviatv/manifest.json +++ b/homeassistant/components/braviatv/manifest.json @@ -2,7 +2,7 @@ "domain": "braviatv", "name": "Sony Bravia TV", "documentation": "https://www.home-assistant.io/integrations/braviatv", - "requirements": ["pybravia==0.2.3"], + "requirements": ["pybravia==0.2.5"], "codeowners": ["@bieniu", "@Drafteed"], "ssdp": [ { diff --git a/homeassistant/components/braviatv/strings.json b/homeassistant/components/braviatv/strings.json index ac651955166..f40494f2251 100644 --- a/homeassistant/components/braviatv/strings.json +++ b/homeassistant/components/braviatv/strings.json @@ -9,21 +9,27 @@ }, "authorize": { "title": "Authorize Sony Bravia TV", - "description": "Enter the PIN code shown on the Sony Bravia TV. \n\nIf the PIN code is not shown, you have to unregister Home Assistant on your TV, go to: Settings -> Network -> Remote device settings -> Deregister remote device. \n\nYou can use PSK (Pre-Shared-Key) instead of PIN. PSK is a user-defined secret key used for access control. This authentication method is recommended as more stable. To enable PSK on your TV, go to: Settings -> Network -> Home Network Setup -> IP Control. Then check «Use PSK authentication» box and enter your PSK instead of PIN.", + "description": "Make sure that «Control remotely» is enabled on your TV, go to: \nSettings -> Network -> Remote device settings -> Control remotely. \n\nThere are two authorization methods: PIN code or PSK (Pre-Shared Key). \nAuthorization via PSK is recommended as more stable.", "data": { - "pin": "[%key:common::config_flow::data::pin%]", "use_psk": "Use PSK authentication" } }, + "pin": { + "title": "Authorize Sony Bravia TV", + "description": "Enter the PIN code shown on the Sony Bravia TV. \n\nIf the PIN code is not shown, you have to unregister Home Assistant on your TV, go to: Settings -> Network -> Remote device settings -> Deregister remote device.", + "data": { + "pin": "[%key:common::config_flow::data::pin%]" + } + }, + "psk": { + "title": "Authorize Sony Bravia TV", + "description": "To set up PSK on your TV, go to: Settings -> Network -> Home Network Setup -> IP Control. Set «Authentication» to «Normal and Pre-Shared Key» or «Pre-Shared Key» and define your Pre-Shared-Key string (e.g. sony). \n\nThen enter your PSK here.", + "data": { + "pin": "PSK" + } + }, "confirm": { "description": "[%key:common::config_flow::description::confirm_setup%]" - }, - "reauth_confirm": { - "description": "Enter the PIN code shown on the Sony Bravia TV. \n\nIf the PIN code is not shown, you have to unregister Home Assistant on your TV, go to: Settings -> Network -> Remote device settings -> Deregister remote device. \n\nYou can use PSK (Pre-Shared-Key) instead of PIN. PSK is a user-defined secret key used for access control. This authentication method is recommended as more stable. To enable PSK on your TV, go to: Settings -> Network -> Home Network Setup -> IP Control. Then check «Use PSK authentication» box and enter your PSK instead of PIN.", - "data": { - "pin": "[%key:common::config_flow::data::pin%]", - "use_psk": "Use PSK authentication" - } } }, "error": { @@ -36,8 +42,7 @@ "already_configured": "[%key:common::config_flow::abort::already_configured_device%]", "no_ip_control": "IP Control is disabled on your TV or the TV is not supported.", "not_bravia_device": "The device is not a Bravia TV.", - "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]", - "reauth_unsuccessful": "Re-authentication was unsuccessful, please remove the integration and set it up again." + "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]" } }, "options": { diff --git a/homeassistant/components/braviatv/translations/en_GB.json b/homeassistant/components/braviatv/translations/en-GB.json similarity index 100% rename from homeassistant/components/braviatv/translations/en_GB.json rename to homeassistant/components/braviatv/translations/en-GB.json diff --git a/homeassistant/components/braviatv/translations/en.json b/homeassistant/components/braviatv/translations/en.json index 39c95c706de..6cfa94de1bd 100644 --- a/homeassistant/components/braviatv/translations/en.json +++ b/homeassistant/components/braviatv/translations/en.json @@ -4,8 +4,7 @@ "already_configured": "Device is already configured", "no_ip_control": "IP Control is disabled on your TV or the TV is not supported.", "not_bravia_device": "The device is not a Bravia TV.", - "reauth_successful": "Re-authentication was successful", - "reauth_unsuccessful": "Re-authentication was unsuccessful, please remove the integration and set it up again." + "reauth_successful": "Re-authentication was successful" }, "error": { "cannot_connect": "Failed to connect", @@ -16,21 +15,27 @@ "step": { "authorize": { "data": { - "pin": "PIN Code", "use_psk": "Use PSK authentication" }, - "description": "Enter the PIN code shown on the Sony Bravia TV. \n\nIf the PIN code is not shown, you have to unregister Home Assistant on your TV, go to: Settings -> Network -> Remote device settings -> Deregister remote device. \n\nYou can use PSK (Pre-Shared-Key) instead of PIN. PSK is a user-defined secret key used for access control. This authentication method is recommended as more stable. To enable PSK on your TV, go to: Settings -> Network -> Home Network Setup -> IP Control. Then check \u00abUse PSK authentication\u00bb box and enter your PSK instead of PIN.", + "description": "Make sure that \u00abControl remotely\u00bb is enabled on your TV, go to: \nSettings -> Network -> Remote device settings -> Control remotely. \n\nThere are two authorization methods: PIN code or PSK (Pre-Shared Key). \nAuthorization via PSK is recommended as more stable.", "title": "Authorize Sony Bravia TV" }, "confirm": { - "description": "Do you want to start set up?" + "description": "Do you want to start setup?" }, - "reauth_confirm": { + "pin": { "data": { - "pin": "PIN Code", - "use_psk": "Use PSK authentication" + "pin": "PIN Code" }, - "description": "Enter the PIN code shown on the Sony Bravia TV. \n\nIf the PIN code is not shown, you have to unregister Home Assistant on your TV, go to: Settings -> Network -> Remote device settings -> Deregister remote device. \n\nYou can use PSK (Pre-Shared-Key) instead of PIN. PSK is a user-defined secret key used for access control. This authentication method is recommended as more stable. To enable PSK on your TV, go to: Settings -> Network -> Home Network Setup -> IP Control. Then check \u00abUse PSK authentication\u00bb box and enter your PSK instead of PIN." + "description": "Enter the PIN code shown on the Sony Bravia TV. \n\nIf the PIN code is not shown, you have to unregister Home Assistant on your TV, go to: Settings -> Network -> Remote device settings -> Deregister remote device.", + "title": "Authorize Sony Bravia TV" + }, + "psk": { + "data": { + "pin": "PSK" + }, + "description": "To set up PSK on your TV, go to: Settings -> Network -> Home Network Setup -> IP Control. Set \u00abAuthentication\u00bb to \u00abNormal and Pre-Shared Key\u00bb or \u00abPre-Shared Key\u00bb and define your Pre-Shared-Key string (e.g. sony). \n\nThen enter your PSK here.", + "title": "Authorize Sony Bravia TV" }, "user": { "data": { diff --git a/homeassistant/components/braviatv/translations/hu.json b/homeassistant/components/braviatv/translations/hu.json index 0912003f74f..97ee61c4b53 100644 --- a/homeassistant/components/braviatv/translations/hu.json +++ b/homeassistant/components/braviatv/translations/hu.json @@ -41,6 +41,9 @@ } }, "options": { + "abort": { + "failed_update": "Hiba t\u00f6rt\u00e9nt a forr\u00e1slista friss\u00edt\u00e9se k\u00f6zben. \n\n Gy\u0151z\u0151dj\u00f6n meg arr\u00f3l, hogy a TV be van kapcsolva, miel\u0151tt megpr\u00f3b\u00e1ln\u00e1 be\u00e1ll\u00edtani." + }, "step": { "user": { "data": { diff --git a/homeassistant/components/braviatv/translations/it.json b/homeassistant/components/braviatv/translations/it.json index e17c961fa45..23d61c324b7 100644 --- a/homeassistant/components/braviatv/translations/it.json +++ b/homeassistant/components/braviatv/translations/it.json @@ -23,7 +23,7 @@ "title": "Autorizza Sony Bravia TV" }, "confirm": { - "description": "Vuoi iniziare la configurazione?" + "description": "Vuoi avviare la configurazione?" }, "reauth_confirm": { "data": { diff --git a/homeassistant/components/braviatv/translations/ko.json b/homeassistant/components/braviatv/translations/ko.json index 78c02bceb44..00382c06ca0 100644 --- a/homeassistant/components/braviatv/translations/ko.json +++ b/homeassistant/components/braviatv/translations/ko.json @@ -2,10 +2,12 @@ "config": { "abort": { "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", - "no_ip_control": "TV\uc5d0\uc11c IP \uc81c\uc5b4\uac00 \ube44\ud65c\uc131\ud654\ub418\uc5c8\uac70\ub098 TV\uac00 \uc9c0\uc6d0\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4." + "no_ip_control": "TV\uc5d0\uc11c IP \uc81c\uc5b4\uac00 \ube44\ud65c\uc131\ud654\ub418\uc5c8\uac70\ub098 TV\uac00 \uc9c0\uc6d0\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" }, "error": { "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "invalid_host": "\ud638\uc2a4\ud2b8 \uc774\ub984 \ub610\ub294 IP \uc8fc\uc18c\uac00 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "unsupported_model": "\uc774 TV \ubaa8\ub378\uc740 \uc9c0\uc6d0\ub418\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4." }, @@ -17,6 +19,14 @@ "description": "Sony Bravia TV\uc5d0 \ud45c\uc2dc\ub41c PIN \ucf54\ub4dc\ub97c \uc785\ub825\ud574\uc8fc\uc138\uc694.\n\nPIN \ucf54\ub4dc\uac00 \ud45c\uc2dc\ub418\uc9c0 \uc54a\uc73c\uba74 TV\uc5d0\uc11c Home Assistant\ub97c \ub4f1\ub85d \ud574\uc81c\ud558\uc5ec\uc57c \ud569\ub2c8\ub2e4. Settings -> Network -> Remote device settings -> Unregister remote device\ub85c \uc774\ub3d9\ud558\uc5ec \ub4f1\ub85d\uc744 \ud574\uc81c\ud574\uc8fc\uc138\uc694.", "title": "Sony Bravia TV \uc2b9\uc778\ud558\uae30" }, + "confirm": { + "description": "\uc124\uc815\uc744 \uc2dc\uc791\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" + }, + "reauth_confirm": { + "data": { + "pin": "PIN \ucf54\ub4dc" + } + }, "user": { "data": { "host": "\ud638\uc2a4\ud2b8" diff --git a/homeassistant/components/braviatv/translations/lb.json b/homeassistant/components/braviatv/translations/lb.json index 6510666ad55..109ce1c7e20 100644 --- a/homeassistant/components/braviatv/translations/lb.json +++ b/homeassistant/components/braviatv/translations/lb.json @@ -12,7 +12,7 @@ "step": { "authorize": { "data": { - "pin": "PIN Code" + "pin": "PIN-Code" }, "description": "G\u00ebff de PIN code an deen op der Sony Bravia TV ugewise g\u00ebtt.\n\nFalls kee PIN code ugewise g\u00ebtt muss den Home Assistant um Fernseh ofgemellt ginn, um TV: Settings -> Network -> Remote device settings -> Unregister remote device.", "title": "Sony Bravia TV erlaaben" diff --git a/homeassistant/components/braviatv/translations/pt.json b/homeassistant/components/braviatv/translations/pt.json index 5ee36fdbb85..f41faab1272 100644 --- a/homeassistant/components/braviatv/translations/pt.json +++ b/homeassistant/components/braviatv/translations/pt.json @@ -2,12 +2,13 @@ "config": { "abort": { "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", - "not_bravia_device": "O dispositivo n\u00e3o \u00e9 uma TV Bravia." + "not_bravia_device": "O dispositivo n\u00e3o \u00e9 uma TV Bravia.", + "reauth_successful": "Reautentica\u00e7\u00e3o bem sucedida" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", - "invalid_host": "Nome de servidor ou endere\u00e7o IP inv\u00e1lido.", + "invalid_host": "Endere\u00e7o IP ou hostname inv\u00e1lido.", "unsupported_model": "O seu modelo de TV n\u00e3o \u00e9 suportado." }, "step": { @@ -18,9 +19,17 @@ "description": "Digite o c\u00f3digo PIN mostrado na TV Sony Bravia. \n\nSe o c\u00f3digo PIN n\u00e3o for exibido, \u00e9 necess\u00e1rio cancelar o registro do Home Assistant na TV, v\u00e1 para: Configura\u00e7\u00f5es -> Rede -> Configura\u00e7\u00f5es do dispositivo remoto -> Cancelar registro do dispositivo remoto.", "title": "Autorizar TV Sony Bravia" }, + "confirm": { + "description": "Quer dar in\u00edcio \u00e0 configura\u00e7\u00e3o?" + }, + "reauth_confirm": { + "data": { + "pin": "C\u00f3digo PIN" + } + }, "user": { "data": { - "host": "Servidor" + "host": "Endere\u00e7o" } } } diff --git a/homeassistant/components/braviatv/translations/sk.json b/homeassistant/components/braviatv/translations/sk.json index 6447484aac8..133429ed2c2 100644 --- a/homeassistant/components/braviatv/translations/sk.json +++ b/homeassistant/components/braviatv/translations/sk.json @@ -4,7 +4,8 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "no_ip_control": "Ovl\u00e1danie IP je na va\u0161om telev\u00edzore vypnut\u00e9 alebo telev\u00edzor nie je podporovan\u00fd.", "not_bravia_device": "Zariadenie nie je telev\u00edzor Bravia.", - "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", + "reauth_unsuccessful": "Op\u00e4tovn\u00e1 autentifik\u00e1cia bola ne\u00faspe\u0161n\u00e1, odstr\u00e1\u0148te integr\u00e1ciu a znova ju nastavte." }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", @@ -15,8 +16,10 @@ "step": { "authorize": { "data": { - "pin": "PIN k\u00f3d" + "pin": "PIN k\u00f3d", + "use_psk": "Pou\u017eite autentifik\u00e1ciu PSK" }, + "description": "Zadajte PIN k\u00f3d zobrazen\u00fd na telev\u00edzii Sony Bravia.\n\nPokia\u013e sa PIN k\u00f3d nezobraz\u00ed, je potrebn\u00e9 zru\u0161i\u0165 registr\u00e1ciu Home Assistant na telev\u00edzii, prejdite na: Nastavenia -> Sie\u0165 -> Nastavenie vzdialen\u00e9ho zariadenia -> Zru\u0161i\u0165 registr\u00e1ciu vzdialen\u00e9ho zariadenia.", "title": "Autorizujte telev\u00edzor Sony Bravia" }, "confirm": { @@ -26,21 +29,27 @@ "data": { "pin": "PIN k\u00f3d", "use_psk": "Pou\u017eite autentifik\u00e1ciu PSK" - } + }, + "description": "Zadajte PIN k\u00f3d zobrazen\u00fd na telev\u00edzii Sony Bravia.\n\nPokia\u013e sa PIN k\u00f3d nezobraz\u00ed, je potrebn\u00e9 zru\u0161i\u0165 registr\u00e1ciu Home Assistant na telev\u00edzii, prejdite na: Nastavenia -> Sie\u0165 -> Nastavenie vzdialen\u00e9ho zariadenia -> Zru\u0161i\u0165 registr\u00e1ciu vzdialen\u00e9ho zariadenia." }, "user": { "data": { "host": "Hostite\u013e" - } + }, + "description": "Pred pokusom o nastavenie sa uistite, \u017ee je v\u00e1\u0161 telev\u00edzor zapnut\u00fd." } } }, "options": { + "abort": { + "failed_update": "Pri aktualiz\u00e1cii zoznamu zdrojov sa vyskytla chyba. \n\n Pred pokusom o nastavenie sa uistite, \u017ee je v\u00e1\u0161 telev\u00edzor zapnut\u00fd." + }, "step": { "user": { "data": { "ignored_sources": "Zoznam ignorovan\u00fdch zdrojov" - } + }, + "title": "Mo\u017enosti pre telev\u00edzor Sony Bravia" } } } diff --git a/homeassistant/components/broadlink/config_flow.py b/homeassistant/components/broadlink/config_flow.py index 5a0ed45b2ba..39c470ad77b 100644 --- a/homeassistant/components/broadlink/config_flow.py +++ b/homeassistant/components/broadlink/config_flow.py @@ -39,8 +39,10 @@ class BroadlinkFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): """Define a device for the config flow.""" if device.type not in DEVICE_TYPES: _LOGGER.error( - "Unsupported device: %s. If it worked before, please open " - "an issue at https://github.com/home-assistant/core/issues", + ( + "Unsupported device: %s. If it worked before, please open " + "an issue at https://github.com/home-assistant/core/issues" + ), hex(device.devtype), ) raise AbortFlow("not_supported") @@ -175,9 +177,11 @@ class BroadlinkFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): await self.async_set_unique_id(device.mac.hex()) if self.source == config_entries.SOURCE_IMPORT: _LOGGER.warning( - "%s (%s at %s) is ready to be configured. Click " - "Configuration in the sidebar, click Integrations and " - "click Configure on the device to complete the setup", + ( + "%s (%s at %s) is ready to be configured. Click " + "Configuration in the sidebar, click Integrations and " + "click Configure on the device to complete the setup" + ), device.name, device.model, device.host[0], diff --git a/homeassistant/components/broadlink/device.py b/homeassistant/components/broadlink/device.py index c279e7860b6..99239e3030a 100644 --- a/homeassistant/components/broadlink/device.py +++ b/homeassistant/components/broadlink/device.py @@ -175,9 +175,11 @@ class BroadlinkDevice: self.authorized = False _LOGGER.error( - "%s (%s at %s) is locked. Click Configuration in the sidebar, " - "click Integrations, click Configure on the device and follow " - "the instructions to unlock it", + ( + "%s (%s at %s) is locked. Click Configuration in the sidebar, " + "click Integrations, click Configure on the device and follow " + "the instructions to unlock it" + ), self.name, self.api.model, self.api.host[0], diff --git a/homeassistant/components/broadlink/sensor.py b/homeassistant/components/broadlink/sensor.py index 4d362482e64..2527b48e29c 100644 --- a/homeassistant/components/broadlink/sensor.py +++ b/homeassistant/components/broadlink/sensor.py @@ -9,12 +9,12 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - ELECTRIC_CURRENT_AMPERE, - ELECTRIC_POTENTIAL_VOLT, - ENERGY_KILO_WATT_HOUR, PERCENTAGE, - POWER_WATT, - TEMP_CELSIUS, + UnitOfElectricCurrent, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfPower, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -26,7 +26,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="temperature", name="Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), @@ -44,7 +44,6 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="light", name="Light", - device_class=SensorDeviceClass.ILLUMINANCE, ), SensorEntityDescription( key="noise", @@ -53,35 +52,35 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="power", name="Current power", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="volt", name="Voltage", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="current", name="Current", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="overload", name="Overload", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="totalconsum", name="Total consumption", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), diff --git a/homeassistant/components/broadlink/translations/de.json b/homeassistant/components/broadlink/translations/de.json index 98a5fe4bd2a..a7dd533ce59 100644 --- a/homeassistant/components/broadlink/translations/de.json +++ b/homeassistant/components/broadlink/translations/de.json @@ -25,7 +25,7 @@ "title": "W\u00e4hle einen Namen f\u00fcr das Ger\u00e4t" }, "reset": { - "description": "{name} ({model} unter {host}) ist gesperrt. Du musst das Ger\u00e4t entsperren, um dich zu authentifizieren und die Konfiguration abzuschlie\u00dfen. Anweisungen:\n1. \u00d6ffne die Broadlink-App.\n2. Dr\u00fccke auf auf das Ger\u00e4t.\n3. Dr\u00fccke oben rechts auf `...`.\n4. Scrolle zum unteren Ende der Seite.\n5. Deaktiviere die Sperre.", + "description": "{name} ({model} unter {host}) ist gesperrt. Du musst das Ger\u00e4t entsperren, um dich zu authentifizieren und die Konfiguration abzuschlie\u00dfen. Anweisungen:\n1. \u00d6ffne die Broadlink App.\n2. Dr\u00fccke auf das Ger\u00e4t.\n3. Dr\u00fccke oben rechts auf `\u2026`.\n4. Scrolle zum unteren Ende der Seite.\n5. Deaktiviere die Sperre.", "title": "Entsperren des Ger\u00e4ts" }, "unlock": { diff --git a/homeassistant/components/broadlink/translations/pt.json b/homeassistant/components/broadlink/translations/pt.json index 45fb03ad040..7dbf8451a8f 100644 --- a/homeassistant/components/broadlink/translations/pt.json +++ b/homeassistant/components/broadlink/translations/pt.json @@ -3,13 +3,13 @@ "abort": { "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", "already_in_progress": "O processo de configura\u00e7\u00e3o j\u00e1 est\u00e1 a decorrer", - "cannot_connect": "Falha na liga\u00e7\u00e3o", - "invalid_host": "Nome de servidor ou endere\u00e7o IP inv\u00e1lido.", + "cannot_connect": "A liga\u00e7\u00e3o falhou", + "invalid_host": "Endere\u00e7o IP ou hostname inv\u00e1lido.", "unknown": "Erro inesperado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", - "invalid_host": "Nome de servidor ou endere\u00e7o IP inv\u00e1lido.", + "cannot_connect": "A liga\u00e7\u00e3o falhou", + "invalid_host": "Endere\u00e7o IP ou hostname inv\u00e1lido.", "unknown": "Erro inesperado" }, "flow_title": "{name} ({model} em {host})", @@ -31,7 +31,7 @@ }, "user": { "data": { - "host": "Servidor" + "host": "Endere\u00e7o" } } } diff --git a/homeassistant/components/broadlink/translations/sk.json b/homeassistant/components/broadlink/translations/sk.json index 88c402f5a9a..c35daa5f3e0 100644 --- a/homeassistant/components/broadlink/translations/sk.json +++ b/homeassistant/components/broadlink/translations/sk.json @@ -15,23 +15,30 @@ }, "flow_title": "{name} ({model} na {host})", "step": { + "auth": { + "title": "Overenie zariadenia" + }, "finish": { "data": { "name": "N\u00e1zov" - } + }, + "title": "Vyberte meno zariadenia" }, "reset": { + "description": "{name} ({model} na {host}) je uzamknut\u00e9. Chcete zariadenie overi\u0165 a dokon\u010di\u0165 nastavenie, mus\u00edte ho odomkn\u00fa\u0165. In\u0161trukcie:\n1. Otvorte aplik\u00e1ciu Broadlink.\n2. Kliknite na zariadenie.\n3. Vpravo hore kliknite na `...`.\n4. Prejdite do dolnej \u010dasti str\u00e1nky.\n5. Vypnite z\u00e1mok.", "title": "Odomknite zariadenie" }, "unlock": { "data": { "unlock": "\u00c1no, urobte to." }, + "description": "{name} ({model} na {host}) je uzamknut\u00e9. To m\u00f4\u017ee vies\u0165 k probl\u00e9mom s overovan\u00edm v Home Assistant. Chcete ho odomkn\u00fa\u0165?", "title": "Odomknite zariadenie (volite\u013en\u00e9)" }, "user": { "data": { - "host": "Hostite\u013e" + "host": "Hostite\u013e", + "timeout": "\u010casov\u00fd limit" }, "title": "Pripojte sa k zariadeniu" } diff --git a/homeassistant/components/brother/__init__.py b/homeassistant/components/brother/__init__.py index c1dbfd5bf0a..e0d0a0ca44c 100644 --- a/homeassistant/components/brother/__init__.py +++ b/homeassistant/components/brother/__init__.py @@ -62,7 +62,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return unload_ok -class BrotherDataUpdateCoordinator(DataUpdateCoordinator): +class BrotherDataUpdateCoordinator(DataUpdateCoordinator[BrotherSensors]): """Class to manage fetching Brother data from the printer.""" def __init__(self, hass: HomeAssistant, brother: Brother) -> None: diff --git a/homeassistant/components/brother/manifest.json b/homeassistant/components/brother/manifest.json index 68922ecaeb3..acaa185bd4d 100644 --- a/homeassistant/components/brother/manifest.json +++ b/homeassistant/components/brother/manifest.json @@ -3,7 +3,7 @@ "name": "Brother Printer", "documentation": "https://www.home-assistant.io/integrations/brother", "codeowners": ["@bieniu"], - "requirements": ["brother==2.0.0"], + "requirements": ["brother==2.1.1"], "zeroconf": [ { "type": "_printer._tcp.local.", diff --git a/homeassistant/components/brother/translations/bs.json b/homeassistant/components/brother/translations/bs.json new file mode 100644 index 00000000000..27cf6d54670 --- /dev/null +++ b/homeassistant/components/brother/translations/bs.json @@ -0,0 +1,7 @@ +{ + "config": { + "error": { + "wrong_host": "Neplatn\u00fd n\u00e1zev hostitele nebo IP adresa" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/brother/translations/pt.json b/homeassistant/components/brother/translations/pt.json index f9c19c6be38..3033646ffda 100644 --- a/homeassistant/components/brother/translations/pt.json +++ b/homeassistant/components/brother/translations/pt.json @@ -4,13 +4,13 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "wrong_host": "Nome de servidor ou endere\u00e7o IP inv\u00e1lido." }, "step": { "user": { "data": { - "host": "Servidor", + "host": "Endere\u00e7o", "type": "Tipo de impressora" } }, diff --git a/homeassistant/components/brunt/strings.json b/homeassistant/components/brunt/strings.json index 2dd4a441d60..c1f80f66bc7 100644 --- a/homeassistant/components/brunt/strings.json +++ b/homeassistant/components/brunt/strings.json @@ -2,7 +2,7 @@ "config": { "step": { "user": { - "title": "Setup your Brunt integration", + "title": "Set up your Brunt integration", "data": { "username": "[%key:common::config_flow::data::username%]", "password": "[%key:common::config_flow::data::password%]" diff --git a/homeassistant/components/brunt/translations/de.json b/homeassistant/components/brunt/translations/de.json index efbc9b437e1..8dba1aacc85 100644 --- a/homeassistant/components/brunt/translations/de.json +++ b/homeassistant/components/brunt/translations/de.json @@ -22,7 +22,7 @@ "password": "Passwort", "username": "Benutzername" }, - "title": "Richte deine Brunt-Integration ein" + "title": "Richte deine Brunt Integration ein" } } } diff --git a/homeassistant/components/brunt/translations/en.json b/homeassistant/components/brunt/translations/en.json index 82fdda36822..b763128fb03 100644 --- a/homeassistant/components/brunt/translations/en.json +++ b/homeassistant/components/brunt/translations/en.json @@ -22,7 +22,7 @@ "password": "Password", "username": "Username" }, - "title": "Setup your Brunt integration" + "title": "Set up your Brunt integration" } } } diff --git a/homeassistant/components/brunt/translations/ko.json b/homeassistant/components/brunt/translations/ko.json new file mode 100644 index 00000000000..e4ea775f9c2 --- /dev/null +++ b/homeassistant/components/brunt/translations/ko.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638" + }, + "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" + }, + "user": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/brunt/translations/no.json b/homeassistant/components/brunt/translations/no.json index 78dde9c6234..8cb1b2bfaf8 100644 --- a/homeassistant/components/brunt/translations/no.json +++ b/homeassistant/components/brunt/translations/no.json @@ -22,7 +22,7 @@ "password": "Passord", "username": "Brukernavn" }, - "title": "Konfigurer Brunt-integrasjonen" + "title": "Sett opp Brunt-integrasjonen" } } } diff --git a/homeassistant/components/brunt/translations/sk.json b/homeassistant/components/brunt/translations/sk.json index a654a071ab9..805509d42a2 100644 --- a/homeassistant/components/brunt/translations/sk.json +++ b/homeassistant/components/brunt/translations/sk.json @@ -21,7 +21,8 @@ "data": { "password": "Heslo", "username": "U\u017e\u00edvate\u013esk\u00e9 meno" - } + }, + "title": "Nastavte integr\u00e1ciu Brunt" } } } diff --git a/homeassistant/components/bsblan/translations/ko.json b/homeassistant/components/bsblan/translations/ko.json index 65843a25833..a1a235f9b31 100644 --- a/homeassistant/components/bsblan/translations/ko.json +++ b/homeassistant/components/bsblan/translations/ko.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" }, "error": { "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" diff --git a/homeassistant/components/bsblan/translations/pt.json b/homeassistant/components/bsblan/translations/pt.json index 0b09e208858..17bd1b74f1c 100644 --- a/homeassistant/components/bsblan/translations/pt.json +++ b/homeassistant/components/bsblan/translations/pt.json @@ -2,10 +2,10 @@ "config": { "abort": { "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/bsblan/translations/sk.json b/homeassistant/components/bsblan/translations/sk.json index 40a6e13da9a..636a7bbd311 100644 --- a/homeassistant/components/bsblan/translations/sk.json +++ b/homeassistant/components/bsblan/translations/sk.json @@ -12,10 +12,13 @@ "user": { "data": { "host": "Hostite\u013e", + "passkey": "Passkey String", "password": "Heslo", "port": "Port", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" - } + }, + "description": "Nastavte svoje zariadenie BSB-Lan na integr\u00e1ciu s Home Assistant.", + "title": "Pripojenie k zariadeniu BSB-Lan" } } } diff --git a/homeassistant/components/bthome/manifest.json b/homeassistant/components/bthome/manifest.json index bf447e6a485..7a879608fc4 100644 --- a/homeassistant/components/bthome/manifest.json +++ b/homeassistant/components/bthome/manifest.json @@ -17,7 +17,7 @@ "service_data_uuid": "0000fcd2-0000-1000-8000-00805f9b34fb" } ], - "requirements": ["bthome-ble==2.3.1"], + "requirements": ["bthome-ble==2.4.0"], "dependencies": ["bluetooth"], "codeowners": ["@Ernst79"], "iot_class": "local_push" diff --git a/homeassistant/components/bthome/sensor.py b/homeassistant/components/bthome/sensor.py index 6493b291085..219ce17c081 100644 --- a/homeassistant/components/bthome/sensor.py +++ b/homeassistant/components/bthome/sensor.py @@ -22,12 +22,11 @@ from homeassistant.const import ( CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, CONCENTRATION_PARTS_PER_MILLION, DEGREE, - ELECTRIC_CURRENT_AMPERE, - ELECTRIC_POTENTIAL_VOLT, LIGHT_LUX, PERCENTAGE, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, - TIME_SECONDS, + UnitOfElectricCurrent, + UnitOfElectricPotential, UnitOfEnergy, UnitOfLength, UnitOfMass, @@ -35,6 +34,9 @@ from homeassistant.const import ( UnitOfPressure, UnitOfSpeed, UnitOfTemperature, + UnitOfTime, + UnitOfVolume, + UnitOfVolumeFlowRate, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory @@ -45,30 +47,7 @@ from .const import DOMAIN from .device import device_key_to_bluetooth_entity_key SENSOR_DESCRIPTIONS = { - (BTHomeSensorDeviceClass.TEMPERATURE, Units.TEMP_CELSIUS): SensorEntityDescription( - key=f"{BTHomeSensorDeviceClass.TEMPERATURE}_{Units.TEMP_CELSIUS}", - device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=UnitOfTemperature.CELSIUS, - state_class=SensorStateClass.MEASUREMENT, - ), - (BTHomeSensorDeviceClass.HUMIDITY, Units.PERCENTAGE): SensorEntityDescription( - key=f"{BTHomeSensorDeviceClass.HUMIDITY}_{Units.PERCENTAGE}", - device_class=SensorDeviceClass.HUMIDITY, - native_unit_of_measurement=PERCENTAGE, - state_class=SensorStateClass.MEASUREMENT, - ), - (BTHomeSensorDeviceClass.ILLUMINANCE, Units.LIGHT_LUX): SensorEntityDescription( - key=f"{BTHomeSensorDeviceClass.ILLUMINANCE}_{Units.LIGHT_LUX}", - device_class=SensorDeviceClass.ILLUMINANCE, - native_unit_of_measurement=LIGHT_LUX, - state_class=SensorStateClass.MEASUREMENT, - ), - (BTHomeSensorDeviceClass.PRESSURE, Units.PRESSURE_MBAR): SensorEntityDescription( - key=f"{BTHomeSensorDeviceClass.PRESSURE}_{Units.PRESSURE_MBAR}", - device_class=SensorDeviceClass.PRESSURE, - native_unit_of_measurement=UnitOfPressure.MBAR, - state_class=SensorStateClass.MEASUREMENT, - ), + # Battery (percent) (BTHomeSensorDeviceClass.BATTERY, Units.PERCENTAGE): SensorEntityDescription( key=f"{BTHomeSensorDeviceClass.BATTERY}_{Units.PERCENTAGE}", device_class=SensorDeviceClass.BATTERY, @@ -76,48 +55,12 @@ SENSOR_DESCRIPTIONS = { state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, ), - ( - BTHomeSensorDeviceClass.VOLTAGE, - Units.ELECTRIC_POTENTIAL_VOLT, - ): SensorEntityDescription( - key=f"{BTHomeSensorDeviceClass.VOLTAGE}_{Units.ELECTRIC_POTENTIAL_VOLT}", - device_class=SensorDeviceClass.VOLTAGE, - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, - state_class=SensorStateClass.MEASUREMENT, - ), - ( - BTHomeSensorDeviceClass.ENERGY, - Units.ENERGY_KILO_WATT_HOUR, - ): SensorEntityDescription( - key=f"{BTHomeSensorDeviceClass.ENERGY}_{Units.ENERGY_KILO_WATT_HOUR}", - device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, - state_class=SensorStateClass.TOTAL_INCREASING, - ), - (BTHomeSensorDeviceClass.POWER, Units.POWER_WATT): SensorEntityDescription( - key=f"{BTHomeSensorDeviceClass.POWER}_{Units.POWER_WATT}", - device_class=SensorDeviceClass.POWER, - native_unit_of_measurement=UnitOfPower.WATT, - state_class=SensorStateClass.MEASUREMENT, - ), - ( - BTHomeSensorDeviceClass.PM10, - Units.CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, - ): SensorEntityDescription( - key=f"{BTHomeSensorDeviceClass.PM10}_{Units.CONCENTRATION_MICROGRAMS_PER_CUBIC_METER}", - device_class=SensorDeviceClass.PM10, - native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, - state_class=SensorStateClass.MEASUREMENT, - ), - ( - BTHomeSensorDeviceClass.PM25, - Units.CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, - ): SensorEntityDescription( - key=f"{BTHomeSensorDeviceClass.PM25}_{Units.CONCENTRATION_MICROGRAMS_PER_CUBIC_METER}", - device_class=SensorDeviceClass.PM25, - native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + # Count (-) + (BTHomeSensorDeviceClass.COUNT, None): SensorEntityDescription( + key=f"{BTHomeSensorDeviceClass.COUNT}", state_class=SensorStateClass.MEASUREMENT, ), + # CO2 (parts per million) ( BTHomeSensorDeviceClass.CO2, Units.CONCENTRATION_PARTS_PER_MILLION, @@ -127,15 +70,140 @@ SENSOR_DESCRIPTIONS = { native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION, state_class=SensorStateClass.MEASUREMENT, ), + # Current (Ampere) ( - BTHomeSensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS, + BTHomeSensorDeviceClass.CURRENT, + Units.ELECTRIC_CURRENT_AMPERE, + ): SensorEntityDescription( + key=f"{BTHomeSensorDeviceClass.CURRENT}_{Units.ELECTRIC_CURRENT_AMPERE}", + device_class=SensorDeviceClass.CURRENT, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, + state_class=SensorStateClass.MEASUREMENT, + ), + # Dew Point (°C) + (BTHomeSensorDeviceClass.DEW_POINT, Units.TEMP_CELSIUS): SensorEntityDescription( + key=f"{BTHomeSensorDeviceClass.DEW_POINT}_{Units.TEMP_CELSIUS}", + device_class=SensorDeviceClass.TEMPERATURE, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + state_class=SensorStateClass.MEASUREMENT, + ), + # Distance (mm) + ( + BTHomeSensorDeviceClass.DISTANCE, + Units.LENGTH_MILLIMETERS, + ): SensorEntityDescription( + key=f"{BTHomeSensorDeviceClass.DISTANCE}_{Units.LENGTH_MILLIMETERS}", + device_class=SensorDeviceClass.DISTANCE, + native_unit_of_measurement=UnitOfLength.MILLIMETERS, + state_class=SensorStateClass.MEASUREMENT, + ), + # Distance (m) + (BTHomeSensorDeviceClass.DISTANCE, Units.LENGTH_METERS): SensorEntityDescription( + key=f"{BTHomeSensorDeviceClass.DISTANCE}_{Units.LENGTH_METERS}", + device_class=SensorDeviceClass.DISTANCE, + native_unit_of_measurement=UnitOfLength.METERS, + state_class=SensorStateClass.MEASUREMENT, + ), + # Duration (seconds) + (BTHomeSensorDeviceClass.DURATION, Units.TIME_SECONDS): SensorEntityDescription( + key=f"{BTHomeSensorDeviceClass.DURATION}_{Units.TIME_SECONDS}", + device_class=SensorDeviceClass.DURATION, + native_unit_of_measurement=UnitOfTime.SECONDS, + state_class=SensorStateClass.MEASUREMENT, + ), + # Energy (kWh) + ( + BTHomeSensorDeviceClass.ENERGY, + Units.ENERGY_KILO_WATT_HOUR, + ): SensorEntityDescription( + key=f"{BTHomeSensorDeviceClass.ENERGY}_{Units.ENERGY_KILO_WATT_HOUR}", + device_class=SensorDeviceClass.ENERGY, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, + state_class=SensorStateClass.TOTAL_INCREASING, + ), + # Humidity in (percent) + (BTHomeSensorDeviceClass.HUMIDITY, Units.PERCENTAGE): SensorEntityDescription( + key=f"{BTHomeSensorDeviceClass.HUMIDITY}_{Units.PERCENTAGE}", + device_class=SensorDeviceClass.HUMIDITY, + native_unit_of_measurement=PERCENTAGE, + state_class=SensorStateClass.MEASUREMENT, + ), + # Illuminance (lux) + (BTHomeSensorDeviceClass.ILLUMINANCE, Units.LIGHT_LUX): SensorEntityDescription( + key=f"{BTHomeSensorDeviceClass.ILLUMINANCE}_{Units.LIGHT_LUX}", + device_class=SensorDeviceClass.ILLUMINANCE, + native_unit_of_measurement=LIGHT_LUX, + state_class=SensorStateClass.MEASUREMENT, + ), + # Mass sensor (kg) + (BTHomeSensorDeviceClass.MASS, Units.MASS_KILOGRAMS): SensorEntityDescription( + key=f"{BTHomeSensorDeviceClass.MASS}_{Units.MASS_KILOGRAMS}", + device_class=SensorDeviceClass.WEIGHT, + native_unit_of_measurement=UnitOfMass.KILOGRAMS, + state_class=SensorStateClass.MEASUREMENT, + ), + # Mass sensor (lb) + (BTHomeSensorDeviceClass.MASS, Units.MASS_POUNDS): SensorEntityDescription( + key=f"{BTHomeSensorDeviceClass.MASS}_{Units.MASS_POUNDS}", + device_class=SensorDeviceClass.WEIGHT, + native_unit_of_measurement=UnitOfMass.POUNDS, + state_class=SensorStateClass.MEASUREMENT, + ), + # Moisture (percent) + (BTHomeSensorDeviceClass.MOISTURE, Units.PERCENTAGE): SensorEntityDescription( + key=f"{BTHomeSensorDeviceClass.MOISTURE}_{Units.PERCENTAGE}", + device_class=SensorDeviceClass.MOISTURE, + native_unit_of_measurement=PERCENTAGE, + state_class=SensorStateClass.MEASUREMENT, + ), + # Packet Id (-) + (BTHomeSensorDeviceClass.PACKET_ID, None): SensorEntityDescription( + key=f"{BTHomeSensorDeviceClass.PACKET_ID}", + state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + # PM10 (µg/m3) + ( + BTHomeSensorDeviceClass.PM10, Units.CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, ): SensorEntityDescription( - key=f"{BTHomeSensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS}_{Units.CONCENTRATION_MICROGRAMS_PER_CUBIC_METER}", - device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS, + key=f"{BTHomeSensorDeviceClass.PM10}_{Units.CONCENTRATION_MICROGRAMS_PER_CUBIC_METER}", + device_class=SensorDeviceClass.PM10, native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, state_class=SensorStateClass.MEASUREMENT, ), + # PM2.5 (µg/m3) + ( + BTHomeSensorDeviceClass.PM25, + Units.CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + ): SensorEntityDescription( + key=f"{BTHomeSensorDeviceClass.PM25}_{Units.CONCENTRATION_MICROGRAMS_PER_CUBIC_METER}", + device_class=SensorDeviceClass.PM25, + native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + state_class=SensorStateClass.MEASUREMENT, + ), + # Power (Watt) + (BTHomeSensorDeviceClass.POWER, Units.POWER_WATT): SensorEntityDescription( + key=f"{BTHomeSensorDeviceClass.POWER}_{Units.POWER_WATT}", + device_class=SensorDeviceClass.POWER, + native_unit_of_measurement=UnitOfPower.WATT, + state_class=SensorStateClass.MEASUREMENT, + ), + # Pressure (mbar) + (BTHomeSensorDeviceClass.PRESSURE, Units.PRESSURE_MBAR): SensorEntityDescription( + key=f"{BTHomeSensorDeviceClass.PRESSURE}_{Units.PRESSURE_MBAR}", + device_class=SensorDeviceClass.PRESSURE, + native_unit_of_measurement=UnitOfPressure.MBAR, + state_class=SensorStateClass.MEASUREMENT, + ), + # Rotation (°) + (BTHomeSensorDeviceClass.ROTATION, Units.DEGREE): SensorEntityDescription( + key=f"{BTHomeSensorDeviceClass.ROTATION}_{Units.DEGREE}", + native_unit_of_measurement=DEGREE, + state_class=SensorStateClass.MEASUREMENT, + ), + # Signal Strength (RSSI) (dB) ( BTHomeSensorDeviceClass.SIGNAL_STRENGTH, Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT, @@ -147,80 +215,7 @@ SENSOR_DESCRIPTIONS = { entity_category=EntityCategory.DIAGNOSTIC, entity_registry_enabled_default=False, ), - # Used for mass sensor with kg unit - (BTHomeSensorDeviceClass.MASS, Units.MASS_KILOGRAMS): SensorEntityDescription( - key=f"{BTHomeSensorDeviceClass.MASS}_{Units.MASS_KILOGRAMS}", - device_class=SensorDeviceClass.WEIGHT, - native_unit_of_measurement=UnitOfMass.KILOGRAMS, - state_class=SensorStateClass.MEASUREMENT, - ), - # Used for mass sensor with lb unit - (BTHomeSensorDeviceClass.MASS, Units.MASS_POUNDS): SensorEntityDescription( - key=f"{BTHomeSensorDeviceClass.MASS}_{Units.MASS_POUNDS}", - device_class=SensorDeviceClass.WEIGHT, - native_unit_of_measurement=UnitOfMass.POUNDS, - state_class=SensorStateClass.MEASUREMENT, - ), - # Used for moisture sensor - (BTHomeSensorDeviceClass.MOISTURE, Units.PERCENTAGE): SensorEntityDescription( - key=f"{BTHomeSensorDeviceClass.MOISTURE}_{Units.PERCENTAGE}", - device_class=SensorDeviceClass.MOISTURE, - native_unit_of_measurement=PERCENTAGE, - state_class=SensorStateClass.MEASUREMENT, - ), - # Used for dew point sensor - (BTHomeSensorDeviceClass.DEW_POINT, Units.TEMP_CELSIUS): SensorEntityDescription( - key=f"{BTHomeSensorDeviceClass.DEW_POINT}_{Units.TEMP_CELSIUS}", - device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=UnitOfTemperature.CELSIUS, - state_class=SensorStateClass.MEASUREMENT, - ), - # Used for count sensor - (BTHomeSensorDeviceClass.COUNT, None): SensorEntityDescription( - key=f"{BTHomeSensorDeviceClass.COUNT}", - state_class=SensorStateClass.MEASUREMENT, - ), - # Used for rotation sensor - (BTHomeSensorDeviceClass.ROTATION, Units.DEGREE): SensorEntityDescription( - key=f"{BTHomeSensorDeviceClass.ROTATION}_{Units.DEGREE}", - native_unit_of_measurement=DEGREE, - state_class=SensorStateClass.MEASUREMENT, - ), - # Used for distance sensor in mm - ( - BTHomeSensorDeviceClass.DISTANCE, - Units.LENGTH_MILLIMETERS, - ): SensorEntityDescription( - key=f"{BTHomeSensorDeviceClass.DISTANCE}_{Units.LENGTH_MILLIMETERS}", - device_class=SensorDeviceClass.DISTANCE, - native_unit_of_measurement=UnitOfLength.MILLIMETERS, - state_class=SensorStateClass.MEASUREMENT, - ), - # Used for distance sensor in m - (BTHomeSensorDeviceClass.DISTANCE, Units.LENGTH_METERS): SensorEntityDescription( - key=f"{BTHomeSensorDeviceClass.DISTANCE}_{Units.LENGTH_METERS}", - device_class=SensorDeviceClass.DISTANCE, - native_unit_of_measurement=UnitOfLength.METERS, - state_class=SensorStateClass.MEASUREMENT, - ), - # Used for duration sensor - (BTHomeSensorDeviceClass.DURATION, Units.TIME_SECONDS): SensorEntityDescription( - key=f"{BTHomeSensorDeviceClass.DURATION}_{Units.TIME_SECONDS}", - device_class=SensorDeviceClass.DURATION, - native_unit_of_measurement=TIME_SECONDS, - state_class=SensorStateClass.MEASUREMENT, - ), - # Used for current sensor - ( - BTHomeSensorDeviceClass.CURRENT, - Units.ELECTRIC_CURRENT_AMPERE, - ): SensorEntityDescription( - key=f"{BTHomeSensorDeviceClass.CURRENT}_{Units.ELECTRIC_CURRENT_AMPERE}", - device_class=SensorDeviceClass.CURRENT, - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, - state_class=SensorStateClass.MEASUREMENT, - ), - # Used for speed sensor + # Speed (m/s) ( BTHomeSensorDeviceClass.SPEED, Units.SPEED_METERS_PER_SECOND, @@ -230,11 +225,64 @@ SENSOR_DESCRIPTIONS = { native_unit_of_measurement=UnitOfSpeed.METERS_PER_SECOND, state_class=SensorStateClass.MEASUREMENT, ), - # Used for UV index sensor + # Temperature (°C) + (BTHomeSensorDeviceClass.TEMPERATURE, Units.TEMP_CELSIUS): SensorEntityDescription( + key=f"{BTHomeSensorDeviceClass.TEMPERATURE}_{Units.TEMP_CELSIUS}", + device_class=SensorDeviceClass.TEMPERATURE, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + state_class=SensorStateClass.MEASUREMENT, + ), + # UV index (-) (BTHomeSensorDeviceClass.UV_INDEX, None,): SensorEntityDescription( key=f"{BTHomeSensorDeviceClass.UV_INDEX}", state_class=SensorStateClass.MEASUREMENT, ), + # Volatile organic Compounds (VOC) (µg/m3) + ( + BTHomeSensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS, + Units.CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + ): SensorEntityDescription( + key=f"{BTHomeSensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS}_{Units.CONCENTRATION_MICROGRAMS_PER_CUBIC_METER}", + device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS, + native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + state_class=SensorStateClass.MEASUREMENT, + ), + # Voltage (volt) + ( + BTHomeSensorDeviceClass.VOLTAGE, + Units.ELECTRIC_POTENTIAL_VOLT, + ): SensorEntityDescription( + key=f"{BTHomeSensorDeviceClass.VOLTAGE}_{Units.ELECTRIC_POTENTIAL_VOLT}", + device_class=SensorDeviceClass.VOLTAGE, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + state_class=SensorStateClass.MEASUREMENT, + ), + # Volume (L) + (BTHomeSensorDeviceClass.VOLUME, Units.VOLUME_LITERS,): SensorEntityDescription( + key=f"{BTHomeSensorDeviceClass.VOLUME}_{Units.VOLUME_LITERS}", + device_class=SensorDeviceClass.VOLUME, + native_unit_of_measurement=UnitOfVolume.LITERS, + state_class=SensorStateClass.MEASUREMENT, + ), + # Volume (mL) + ( + BTHomeSensorDeviceClass.VOLUME, + Units.VOLUME_MILLILITERS, + ): SensorEntityDescription( + key=f"{BTHomeSensorDeviceClass.VOLUME}_{Units.VOLUME_MILLILITERS}", + device_class=SensorDeviceClass.VOLUME, + native_unit_of_measurement=UnitOfVolume.MILLILITERS, + state_class=SensorStateClass.MEASUREMENT, + ), + # Volume Flow Rate (m3/hour) + ( + BTHomeSensorDeviceClass.VOLUME_FLOW_RATE, + Units.VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR, + ): SensorEntityDescription( + key=f"{BTHomeSensorDeviceClass.VOLUME_FLOW_RATE}_{Units.VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR}", + native_unit_of_measurement=UnitOfVolumeFlowRate.CUBIC_METERS_PER_HOUR, + state_class=SensorStateClass.MEASUREMENT, + ), } diff --git a/homeassistant/components/bthome/translations/en.json b/homeassistant/components/bthome/translations/en.json index 29115e12781..f9a0d971d98 100644 --- a/homeassistant/components/bthome/translations/en.json +++ b/homeassistant/components/bthome/translations/en.json @@ -13,7 +13,7 @@ "flow_title": "{name}", "step": { "bluetooth_confirm": { - "description": "Do you want to setup {name}?" + "description": "Do you want to set up {name}?" }, "get_encryption_key": { "data": { @@ -25,7 +25,7 @@ "data": { "address": "Device" }, - "description": "Choose a device to setup" + "description": "Choose a device to set up" } } } diff --git a/homeassistant/components/bthome/translations/it.json b/homeassistant/components/bthome/translations/it.json index 9ec4af86278..3e24891c7d6 100644 --- a/homeassistant/components/bthome/translations/it.json +++ b/homeassistant/components/bthome/translations/it.json @@ -25,7 +25,7 @@ "data": { "address": "Dispositivo" }, - "description": "Seleziona un dispositivo da configurare" + "description": "Scegli un dispositivo da configurare" } } } diff --git a/homeassistant/components/bthome/translations/ko.json b/homeassistant/components/bthome/translations/ko.json new file mode 100644 index 00000000000..0cfe3268874 --- /dev/null +++ b/homeassistant/components/bthome/translations/ko.json @@ -0,0 +1,10 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", + "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/bthome/translations/no.json b/homeassistant/components/bthome/translations/no.json index 84f7e7853df..0d7b8914214 100644 --- a/homeassistant/components/bthome/translations/no.json +++ b/homeassistant/components/bthome/translations/no.json @@ -13,7 +13,7 @@ "flow_title": "{name}", "step": { "bluetooth_confirm": { - "description": "Vil du konfigurere {name}?" + "description": "Vil du sette opp {name} ?" }, "get_encryption_key": { "data": { diff --git a/homeassistant/components/bthome/translations/sk.json b/homeassistant/components/bthome/translations/sk.json index 3767640fbf2..b5ec05f3ad7 100644 --- a/homeassistant/components/bthome/translations/sk.json +++ b/homeassistant/components/bthome/translations/sk.json @@ -7,6 +7,7 @@ "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { + "decryption_failed": "Poskytnut\u00fd bindkey k\u013e\u00fa\u010d nefungoval, \u00fadaje sn\u00edma\u010da sa nepodarilo de\u0161ifrova\u0165. Skontrolujte to a sk\u00faste to znova.", "expected_32_characters": "O\u010dak\u00e1van\u00fd 32-znakov\u00fd hexadecim\u00e1lny bindkey." }, "flow_title": "{name}", @@ -14,6 +15,12 @@ "bluetooth_confirm": { "description": "Chcete nastavi\u0165 {name}?" }, + "get_encryption_key": { + "data": { + "bindkey": "Bindkey" + }, + "description": "D\u00e1ta sn\u00edma\u010da vysielan\u00e9 sn\u00edma\u010dom s\u00fa \u0161ifrovan\u00e9. Aby sme ho de\u0161ifrovali, potrebujeme 32-znakov\u00fd hexadecim\u00e1lny bindkey k\u013e\u00fa\u010d." + }, "user": { "data": { "address": "Zaradenie" diff --git a/homeassistant/components/bthome/translations/zh-Hant.json b/homeassistant/components/bthome/translations/zh-Hant.json index 9e723abf1af..a640651f654 100644 --- a/homeassistant/components/bthome/translations/zh-Hant.json +++ b/homeassistant/components/bthome/translations/zh-Hant.json @@ -7,7 +7,7 @@ "reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f" }, "error": { - "decryption_failed": "\u6240\u63d0\u4f9b\u7684\u7d81\u5b9a\u78bc\u7121\u6cd5\u4f7f\u7528\u3001\u50b3\u611f\u5668\u8cc7\u6599\u7121\u6cd5\u89e3\u5bc6\u3002\u8acb\u4fee\u6b63\u5f8c\u3001\u518d\u8a66\u4e00\u6b21\u3002", + "decryption_failed": "\u6240\u63d0\u4f9b\u7684\u7d81\u5b9a\u78bc\u7121\u6cd5\u4f7f\u7528\u3001\u611f\u6e2c\u5668\u8cc7\u6599\u7121\u6cd5\u89e3\u5bc6\u3002\u8acb\u4fee\u6b63\u5f8c\u3001\u518d\u8a66\u4e00\u6b21\u3002", "expected_32_characters": "\u9700\u8981 32 \u500b\u5b57\u5143\u4e4b\u5341\u516d\u9032\u4f4d\u7d81\u5b9a\u78bc\u3002" }, "flow_title": "{name}", @@ -19,7 +19,7 @@ "data": { "bindkey": "\u7d81\u5b9a\u78bc" }, - "description": "\u7531\u50b3\u611f\u5668\u6240\u5ee3\u64ad\u4e4b\u8cc7\u6599\u70ba\u52a0\u5bc6\u8cc7\u6599\u3002\u82e5\u8981\u89e3\u78bc\u3001\u9700\u8981 32 \u500b\u5b57\u5143\u4e4b\u7d81\u5b9a\u78bc\u3002" + "description": "\u7531\u611f\u6e2c\u5668\u6240\u5ee3\u64ad\u4e4b\u8cc7\u6599\u70ba\u52a0\u5bc6\u8cc7\u6599\u3002\u82e5\u8981\u89e3\u78bc\u3001\u9700\u8981 32 \u500b\u5b57\u5143\u4e4b\u7d81\u5b9a\u78bc\u3002" }, "user": { "data": { diff --git a/homeassistant/components/buienradar/sensor.py b/homeassistant/components/buienradar/sensor.py index bf44c884147..1ba51e476d8 100644 --- a/homeassistant/components/buienradar/sensor.py +++ b/homeassistant/components/buienradar/sensor.py @@ -34,13 +34,13 @@ from homeassistant.const import ( CONF_LONGITUDE, CONF_NAME, DEGREE, - IRRADIATION_WATTS_PER_SQUARE_METER, - LENGTH_KILOMETERS, - LENGTH_MILLIMETERS, PERCENTAGE, - PRESSURE_HPA, - SPEED_KILOMETERS_PER_HOUR, - TEMP_CELSIUS, + UnitOfIrradiance, + UnitOfLength, + UnitOfPrecipitationDepth, + UnitOfPressure, + UnitOfSpeed, + UnitOfTemperature, UnitOfVolumetricFlux, ) from homeassistant.core import HomeAssistant, callback @@ -110,7 +110,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="feeltemperature", name="Feel temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, ), SensorEntityDescription( @@ -123,23 +123,22 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="temperature", name="Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="groundtemperature", name="Ground temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="windspeed", name="Wind speed", - native_unit_of_measurement=SPEED_KILOMETERS_PER_HOUR, - device_class=SensorDeviceClass.SPEED, - icon="mdi:weather-windy", + native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, + device_class=SensorDeviceClass.WIND_SPEED, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( @@ -162,23 +161,22 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="pressure", name="Pressure", - native_unit_of_measurement=PRESSURE_HPA, + native_unit_of_measurement=UnitOfPressure.HPA, icon="mdi:gauge", state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="visibility", name="Visibility", - native_unit_of_measurement=LENGTH_KILOMETERS, + native_unit_of_measurement=UnitOfLength.KILOMETERS, device_class=SensorDeviceClass.DISTANCE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="windgust", name="Wind gust", - native_unit_of_measurement=SPEED_KILOMETERS_PER_HOUR, - device_class=SensorDeviceClass.SPEED, - icon="mdi:weather-windy", + native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, + device_class=SensorDeviceClass.WIND_SPEED, ), SensorEntityDescription( key="precipitation", @@ -190,8 +188,8 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="irradiance", name="Irradiance", - native_unit_of_measurement=IRRADIATION_WATTS_PER_SQUARE_METER, - icon="mdi:sunglasses", + device_class=SensorDeviceClass.IRRADIANCE, + native_unit_of_measurement=UnitOfIrradiance.WATTS_PER_SQUARE_METER, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( @@ -203,174 +201,174 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="precipitation_forecast_total", name="Precipitation forecast total", - native_unit_of_measurement=LENGTH_MILLIMETERS, - icon="mdi:weather-pouring", + native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + device_class=SensorDeviceClass.PRECIPITATION, ), # new in json api (>1.0.0): SensorEntityDescription( key="rainlast24hour", name="Rain last 24h", - native_unit_of_measurement=LENGTH_MILLIMETERS, - icon="mdi:weather-pouring", + native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + device_class=SensorDeviceClass.PRECIPITATION, ), # new in json api (>1.0.0): SensorEntityDescription( key="rainlasthour", name="Rain last hour", - native_unit_of_measurement=LENGTH_MILLIMETERS, - icon="mdi:weather-pouring", + native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + device_class=SensorDeviceClass.PRECIPITATION, ), SensorEntityDescription( key="temperature_1d", name="Temperature 1d", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, ), SensorEntityDescription( key="temperature_2d", name="Temperature 2d", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, ), SensorEntityDescription( key="temperature_3d", name="Temperature 3d", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, ), SensorEntityDescription( key="temperature_4d", name="Temperature 4d", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, ), SensorEntityDescription( key="temperature_5d", name="Temperature 5d", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, ), SensorEntityDescription( key="mintemp_1d", name="Minimum temperature 1d", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, ), SensorEntityDescription( key="mintemp_2d", name="Minimum temperature 2d", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, ), SensorEntityDescription( key="mintemp_3d", name="Minimum temperature 3d", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, ), SensorEntityDescription( key="mintemp_4d", name="Minimum temperature 4d", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, ), SensorEntityDescription( key="mintemp_5d", name="Minimum temperature 5d", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, ), SensorEntityDescription( key="rain_1d", name="Rain 1d", - native_unit_of_measurement=LENGTH_MILLIMETERS, - icon="mdi:weather-pouring", + native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + device_class=SensorDeviceClass.PRECIPITATION, ), SensorEntityDescription( key="rain_2d", name="Rain 2d", - native_unit_of_measurement=LENGTH_MILLIMETERS, - icon="mdi:weather-pouring", + native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + device_class=SensorDeviceClass.PRECIPITATION, ), SensorEntityDescription( key="rain_3d", name="Rain 3d", - native_unit_of_measurement=LENGTH_MILLIMETERS, - icon="mdi:weather-pouring", + native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + device_class=SensorDeviceClass.PRECIPITATION, ), SensorEntityDescription( key="rain_4d", name="Rain 4d", - native_unit_of_measurement=LENGTH_MILLIMETERS, - icon="mdi:weather-pouring", + native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + device_class=SensorDeviceClass.PRECIPITATION, ), SensorEntityDescription( key="rain_5d", name="Rain 5d", - native_unit_of_measurement=LENGTH_MILLIMETERS, - icon="mdi:weather-pouring", + native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + device_class=SensorDeviceClass.PRECIPITATION, ), # new in json api (>1.0.0): SensorEntityDescription( key="minrain_1d", name="Minimum rain 1d", - native_unit_of_measurement=LENGTH_MILLIMETERS, - icon="mdi:weather-pouring", + native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + device_class=SensorDeviceClass.PRECIPITATION, ), SensorEntityDescription( key="minrain_2d", name="Minimum rain 2d", - native_unit_of_measurement=LENGTH_MILLIMETERS, - icon="mdi:weather-pouring", + native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + device_class=SensorDeviceClass.PRECIPITATION, ), SensorEntityDescription( key="minrain_3d", name="Minimum rain 3d", - native_unit_of_measurement=LENGTH_MILLIMETERS, - icon="mdi:weather-pouring", + native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + device_class=SensorDeviceClass.PRECIPITATION, ), SensorEntityDescription( key="minrain_4d", name="Minimum rain 4d", - native_unit_of_measurement=LENGTH_MILLIMETERS, - icon="mdi:weather-pouring", + native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + device_class=SensorDeviceClass.PRECIPITATION, ), SensorEntityDescription( key="minrain_5d", name="Minimum rain 5d", - native_unit_of_measurement=LENGTH_MILLIMETERS, - icon="mdi:weather-pouring", + native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + device_class=SensorDeviceClass.PRECIPITATION, ), # new in json api (>1.0.0): SensorEntityDescription( key="maxrain_1d", name="Maximum rain 1d", - native_unit_of_measurement=LENGTH_MILLIMETERS, - icon="mdi:weather-pouring", + native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + device_class=SensorDeviceClass.PRECIPITATION, ), SensorEntityDescription( key="maxrain_2d", name="Maximum rain 2d", - native_unit_of_measurement=LENGTH_MILLIMETERS, - icon="mdi:weather-pouring", + native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + device_class=SensorDeviceClass.PRECIPITATION, ), SensorEntityDescription( key="maxrain_3d", name="Maximum rain 3d", - native_unit_of_measurement=LENGTH_MILLIMETERS, - icon="mdi:weather-pouring", + native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + device_class=SensorDeviceClass.PRECIPITATION, ), SensorEntityDescription( key="maxrain_4d", name="Maximum rain 4d", - native_unit_of_measurement=LENGTH_MILLIMETERS, - icon="mdi:weather-pouring", + native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + device_class=SensorDeviceClass.PRECIPITATION, ), SensorEntityDescription( key="maxrain_5d", name="Maximum rain 5d", - native_unit_of_measurement=LENGTH_MILLIMETERS, - icon="mdi:weather-pouring", + native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + device_class=SensorDeviceClass.PRECIPITATION, ), SensorEntityDescription( key="rainchance_1d", @@ -465,37 +463,32 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="windspeed_1d", name="Wind speed 1d", - native_unit_of_measurement=SPEED_KILOMETERS_PER_HOUR, - device_class=SensorDeviceClass.SPEED, - icon="mdi:weather-windy", + native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, + device_class=SensorDeviceClass.WIND_SPEED, ), SensorEntityDescription( key="windspeed_2d", name="Wind speed 2d", - native_unit_of_measurement=SPEED_KILOMETERS_PER_HOUR, - device_class=SensorDeviceClass.SPEED, - icon="mdi:weather-windy", + native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, + device_class=SensorDeviceClass.WIND_SPEED, ), SensorEntityDescription( key="windspeed_3d", name="Wind speed 3d", - native_unit_of_measurement=SPEED_KILOMETERS_PER_HOUR, - device_class=SensorDeviceClass.SPEED, - icon="mdi:weather-windy", + native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, + device_class=SensorDeviceClass.WIND_SPEED, ), SensorEntityDescription( key="windspeed_4d", name="Wind speed 4d", - native_unit_of_measurement=SPEED_KILOMETERS_PER_HOUR, - device_class=SensorDeviceClass.SPEED, - icon="mdi:weather-windy", + native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, + device_class=SensorDeviceClass.WIND_SPEED, ), SensorEntityDescription( key="windspeed_5d", name="Wind speed 5d", - native_unit_of_measurement=SPEED_KILOMETERS_PER_HOUR, - device_class=SensorDeviceClass.SPEED, - icon="mdi:weather-windy", + native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, + device_class=SensorDeviceClass.WIND_SPEED, ), SensorEntityDescription( key="winddirection_1d", diff --git a/homeassistant/components/buienradar/translations/sk.json b/homeassistant/components/buienradar/translations/sk.json index 825db0b86a7..92d20c47520 100644 --- a/homeassistant/components/buienradar/translations/sk.json +++ b/homeassistant/components/buienradar/translations/sk.json @@ -19,7 +19,9 @@ "step": { "init": { "data": { - "country_code": "K\u00f3d krajiny" + "country_code": "K\u00f3d krajiny", + "delta": "\u010casov\u00fd interval v sekund\u00e1ch medzi aktualiz\u00e1ciami sn\u00edmok z kamery", + "timeframe": "Predpove\u010f zr\u00e1\u017eok za p\u00e1r min\u00fat" } } } diff --git a/homeassistant/components/caldav/calendar.py b/homeassistant/components/caldav/calendar.py index d510c0c08e7..3fe5aab59c8 100644 --- a/homeassistant/components/caldav/calendar.py +++ b/homeassistant/components/caldav/calendar.py @@ -1,7 +1,7 @@ """Support for WebDav Calendar.""" from __future__ import annotations -from datetime import datetime, timedelta +from datetime import date, datetime, timedelta import logging import re @@ -184,9 +184,9 @@ class WebDavCalendarData: continue event_list.append( CalendarEvent( - summary=vevent.summary.value, - start=vevent.dtstart.value, - end=self.get_end_date(vevent), + summary=self.get_attr_value(vevent, "summary") or "", + start=self.to_local(vevent.dtstart.value), + end=self.to_local(self.get_end_date(vevent)), location=self.get_attr_value(vevent, "location"), description=self.get_attr_value(vevent, "description"), ) @@ -264,11 +264,13 @@ class WebDavCalendarData: return # Populate the entity attributes with the event values - (summary, offset) = extract_offset(vevent.summary.value, OFFSET) + (summary, offset) = extract_offset( + self.get_attr_value(vevent, "summary") or "", OFFSET + ) self.event = CalendarEvent( summary=summary, - start=vevent.dtstart.value, - end=self.get_end_date(vevent), + start=self.to_local(vevent.dtstart.value), + end=self.to_local(self.get_end_date(vevent)), location=self.get_attr_value(vevent, "location"), description=self.get_attr_value(vevent, "description"), ) @@ -306,15 +308,23 @@ class WebDavCalendarData: def to_datetime(obj): """Return a datetime.""" if isinstance(obj, datetime): - if obj.tzinfo is None: - # floating value, not bound to any time zone in particular - # represent same time regardless of which time zone is currently being observed - return obj.replace(tzinfo=dt.DEFAULT_TIME_ZONE) - return obj + return WebDavCalendarData.to_local(obj) return dt.dt.datetime.combine(obj, dt.dt.time.min).replace( tzinfo=dt.DEFAULT_TIME_ZONE ) + @staticmethod + def to_local(obj: datetime | date) -> datetime | date: + """Return a datetime as a local datetime, leaving dates unchanged. + + This handles giving floating times a timezone for comparison + with all day events and dropping the custom timezone object + used by the caldav client and dateutil so the datetime can be copied. + """ + if isinstance(obj, datetime): + return dt.as_local(obj) + return obj + @staticmethod def get_attr_value(obj, attribute): """Return the value of the attribute if defined.""" diff --git a/homeassistant/components/calendar/__init__.py b/homeassistant/components/calendar/__init__.py index c89b36ce636..01c8d4fd5ed 100644 --- a/homeassistant/components/calendar/__init__.py +++ b/homeassistant/components/calendar/__init__.py @@ -74,6 +74,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: websocket_api.async_register_command(hass, handle_calendar_event_create) websocket_api.async_register_command(hass, handle_calendar_event_delete) + websocket_api.async_register_command(hass, handle_calendar_event_update) await component.async_setup(config) return True @@ -297,6 +298,16 @@ class CalendarEntity(Entity): """Delete an event on the calendar.""" raise NotImplementedError() + async def async_update_event( + self, + uid: str, + event: dict[str, Any], + recurrence_id: str | None = None, + recurrence_range: str | None = None, + ) -> None: + """Delete an event on the calendar.""" + raise NotImplementedError() + class CalendarEventView(http.HomeAssistantView): """View to retrieve calendar content.""" @@ -500,3 +511,61 @@ async def handle_calendar_event_delete( connection.send_error(msg["id"], "failed", str(ex)) else: connection.send_result(msg["id"]) + + +@websocket_api.websocket_command( + { + vol.Required("type"): "calendar/event/update", + vol.Required("entity_id"): cv.entity_id, + vol.Required(EVENT_UID): cv.string, + vol.Optional(EVENT_RECURRENCE_ID): cv.string, + vol.Optional(EVENT_RECURRENCE_RANGE): cv.string, + vol.Required(CONF_EVENT): vol.Schema( + vol.All( + { + vol.Required(EVENT_START): vol.Any(cv.date, cv.datetime), + vol.Required(EVENT_END): vol.Any(cv.date, cv.datetime), + vol.Required(EVENT_SUMMARY): cv.string, + vol.Optional(EVENT_DESCRIPTION): cv.string, + vol.Optional(EVENT_RRULE): _validate_rrule, + }, + _has_same_type(EVENT_START, EVENT_END), + _has_consistent_timezone(EVENT_START, EVENT_END), + _is_sorted(EVENT_START, EVENT_END), + ) + ), + } +) +@websocket_api.async_response +async def handle_calendar_event_update( + hass: HomeAssistant, connection: ActiveConnection, msg: dict[str, Any] +) -> None: + """Handle creation of a calendar event.""" + component: EntityComponent[CalendarEntity] = hass.data[DOMAIN] + if not (entity := component.get_entity(msg["entity_id"])): + connection.send_error(msg["id"], ERR_NOT_FOUND, "Entity not found") + return + + if ( + not entity.supported_features + or not entity.supported_features & CalendarEntityFeature.UPDATE_EVENT + ): + connection.send_message( + websocket_api.error_message( + msg["id"], ERR_NOT_SUPPORTED, "Calendar does not support event update" + ) + ) + return + + try: + await entity.async_update_event( + msg[EVENT_UID], + msg[CONF_EVENT], + recurrence_id=msg.get(EVENT_RECURRENCE_ID), + recurrence_range=msg.get(EVENT_RECURRENCE_RANGE), + ) + except (HomeAssistantError, ValueError) as ex: + _LOGGER.error("Error handling Calendar Event call: %s", ex) + connection.send_error(msg["id"], "failed", str(ex)) + else: + connection.send_result(msg["id"]) diff --git a/homeassistant/components/calendar/const.py b/homeassistant/components/calendar/const.py index adee190200a..4a29a28d71d 100644 --- a/homeassistant/components/calendar/const.py +++ b/homeassistant/components/calendar/const.py @@ -10,6 +10,7 @@ class CalendarEntityFeature(IntEnum): CREATE_EVENT = 1 DELETE_EVENT = 2 + UPDATE_EVENT = 4 # rfc5545 fields diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index 51e874ef3b7..f329be16f1d 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -773,7 +773,7 @@ class CameraMjpegStream(CameraView): # Compose camera stream from stills interval = float(interval_str) if interval < MIN_STREAM_INTERVAL: - raise ValueError(f"Stream interval must be be > {MIN_STREAM_INTERVAL}") + raise ValueError(f"Stream interval must be > {MIN_STREAM_INTERVAL}") return await camera.handle_async_still_stream(request, interval) except ValueError as err: raise web.HTTPBadRequest() from err @@ -836,7 +836,10 @@ async def ws_camera_web_rtc_offer( connection.send_error( msg["id"], "web_rtc_offer_failed", - f"Camera does not support WebRTC, frontend_stream_type={camera.frontend_stream_type}", + ( + "Camera does not support WebRTC," + f" frontend_stream_type={camera.frontend_stream_type}" + ), ) return try: diff --git a/homeassistant/components/camera/prefs.py b/homeassistant/components/camera/prefs.py index 0a8785457e8..f1bb0a0a840 100644 --- a/homeassistant/components/camera/prefs.py +++ b/homeassistant/components/camera/prefs.py @@ -73,7 +73,8 @@ class CameraPreferences: ) else: raise HomeAssistantError( - "Orientation is only supported on entities set up through config flows" + "Orientation is only supported on entities set up through config" + " flows" ) if dynamic_stream_settings: dynamic_stream_settings.orientation = orientation diff --git a/homeassistant/components/camera/translations/lt.json b/homeassistant/components/camera/translations/lt.json index 24091a05733..1687427f7a1 100644 --- a/homeassistant/components/camera/translations/lt.json +++ b/homeassistant/components/camera/translations/lt.json @@ -2,7 +2,7 @@ "state": { "_": { "idle": "Laukimo re\u017eimas", - "recording": "\u012era\u0161ymas", + "recording": "\u012era\u0161oma", "streaming": "Transliuojama" } } diff --git a/homeassistant/components/canary/coordinator.py b/homeassistant/components/canary/coordinator.py index b2a8ef4daaa..d81589020e3 100644 --- a/homeassistant/components/canary/coordinator.py +++ b/homeassistant/components/canary/coordinator.py @@ -7,7 +7,7 @@ import logging from async_timeout import timeout from canary.api import Api -from canary.model import Location +from canary.model import Location, Reading from requests.exceptions import ConnectTimeout, HTTPError from homeassistant.core import HomeAssistant @@ -19,7 +19,7 @@ from .model import CanaryData _LOGGER = logging.getLogger(__name__) -class CanaryDataUpdateCoordinator(DataUpdateCoordinator): +class CanaryDataUpdateCoordinator(DataUpdateCoordinator[CanaryData]): """Class to manage fetching Canary data.""" def __init__(self, hass: HomeAssistant, *, api: Api) -> None: @@ -37,7 +37,7 @@ class CanaryDataUpdateCoordinator(DataUpdateCoordinator): def _update_data(self) -> CanaryData: """Fetch data from Canary via sync functions.""" locations_by_id: dict[str, Location] = {} - readings_by_device_id: dict[str, ValuesView] = {} + readings_by_device_id: dict[str, ValuesView[Reading]] = {} for location in self.canary.get_locations(): location_id = location.location_id diff --git a/homeassistant/components/canary/model.py b/homeassistant/components/canary/model.py index 12fb8209108..4ed868a7e60 100644 --- a/homeassistant/components/canary/model.py +++ b/homeassistant/components/canary/model.py @@ -2,16 +2,13 @@ from __future__ import annotations from collections.abc import ValuesView -from typing import Optional, TypedDict +from typing import TypedDict -from canary.model import Location +from canary.model import Location, Reading class CanaryData(TypedDict): """TypedDict for Canary Coordinator Data.""" locations: dict[str, Location] - readings: dict[str, ValuesView] - - -SensorTypeItem = tuple[str, Optional[str], Optional[str], Optional[str], list[str]] + readings: dict[str, ValuesView[Reading]] diff --git a/homeassistant/components/canary/sensor.py b/homeassistant/components/canary/sensor.py index c80c178fbb5..0b77815ce42 100644 --- a/homeassistant/components/canary/sensor.py +++ b/homeassistant/components/canary/sensor.py @@ -1,7 +1,7 @@ """Support for Canary sensors.""" from __future__ import annotations -from typing import Final +from typing import Final, Optional from canary.model import Device, Location, SensorType @@ -10,7 +10,7 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( PERCENTAGE, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo @@ -19,7 +19,10 @@ from homeassistant.helpers.update_coordinator import CoordinatorEntity from .const import DATA_COORDINATOR, DOMAIN, MANUFACTURER from .coordinator import CanaryDataUpdateCoordinator -from .model import SensorTypeItem + +SensorTypeItem = tuple[ + str, Optional[str], Optional[str], Optional[SensorDeviceClass], list[str] +] SENSOR_VALUE_PRECISION: Final = 2 ATTR_AIR_QUALITY: Final = "air_quality" @@ -34,7 +37,13 @@ CANARY_FLEX: Final = "Canary Flex" # Sensor types are defined like so: # sensor type name, unit_of_measurement, icon, device class, products supported SENSOR_TYPES: Final[list[SensorTypeItem]] = [ - ("temperature", TEMP_CELSIUS, None, SensorDeviceClass.TEMPERATURE, [CANARY_PRO]), + ( + "temperature", + UnitOfTemperature.CELSIUS, + None, + SensorDeviceClass.TEMPERATURE, + [CANARY_PRO], + ), ("humidity", PERCENTAGE, None, SensorDeviceClass.HUMIDITY, [CANARY_PRO]), ("air_quality", None, "mdi:weather-windy", None, [CANARY_PRO]), ( diff --git a/homeassistant/components/canary/translations/pt.json b/homeassistant/components/canary/translations/pt.json index e328e4f580b..d1f625f46b7 100644 --- a/homeassistant/components/canary/translations/pt.json +++ b/homeassistant/components/canary/translations/pt.json @@ -5,7 +5,7 @@ "unknown": "Erro inesperado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "user": { diff --git a/homeassistant/components/canary/translations/sk.json b/homeassistant/components/canary/translations/sk.json index 4facc537244..e1520021626 100644 --- a/homeassistant/components/canary/translations/sk.json +++ b/homeassistant/components/canary/translations/sk.json @@ -13,6 +13,17 @@ "data": { "password": "Heslo", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" + }, + "title": "Pripojenie ku Canary" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "ffmpeg_arguments": "Argumenty odovzdan\u00e9 ffmpeg pre kamery", + "timeout": "\u010casov\u00fd limit po\u017eiadavky (v sekund\u00e1ch)" } } } diff --git a/homeassistant/components/cast/config_flow.py b/homeassistant/components/cast/config_flow.py index 1c983d6f67a..a5fc4360097 100644 --- a/homeassistant/components/cast/config_flow.py +++ b/homeassistant/components/cast/config_flow.py @@ -7,11 +7,12 @@ import voluptuous as vol from homeassistant import config_entries from homeassistant.components import onboarding, zeroconf +from homeassistant.const import CONF_UUID from homeassistant.core import callback from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers import config_validation as cv -from .const import CONF_IGNORE_CEC, CONF_KNOWN_HOSTS, CONF_UUID, DOMAIN +from .const import CONF_IGNORE_CEC, CONF_KNOWN_HOSTS, DOMAIN IGNORE_CEC_SCHEMA = vol.Schema(vol.All(cv.ensure_list, [cv.string])) KNOWN_HOSTS_SCHEMA = vol.Schema(vol.All(cv.ensure_list, [cv.string])) diff --git a/homeassistant/components/cast/const.py b/homeassistant/components/cast/const.py index 06db70b830a..e8e38a6e72b 100644 --- a/homeassistant/components/cast/const.py +++ b/homeassistant/components/cast/const.py @@ -25,4 +25,3 @@ SIGNAL_HASS_CAST_SHOW_VIEW = "cast_show_view" CONF_IGNORE_CEC = "ignore_cec" CONF_KNOWN_HOSTS = "known_hosts" -CONF_UUID = "uuid" diff --git a/homeassistant/components/cast/helpers.py b/homeassistant/components/cast/helpers.py index 48f57c39bd5..26759ca9606 100644 --- a/homeassistant/components/cast/helpers.py +++ b/homeassistant/components/cast/helpers.py @@ -76,7 +76,10 @@ class ChromecastInfo: ) _LOGGER.info( - "Fetched cast details for unknown model '%s' manufacturer: '%s', type: '%s'. Please %s", + ( + "Fetched cast details for unknown model '%s' manufacturer:" + " '%s', type: '%s'. Please %s" + ), cast_info.model_name, cast_info.manufacturer, cast_info.cast_type, diff --git a/homeassistant/components/cast/home_assistant_cast.py b/homeassistant/components/cast/home_assistant_cast.py index dc15bc3440e..14aa454c2e0 100644 --- a/homeassistant/components/cast/home_assistant_cast.py +++ b/homeassistant/components/cast/home_assistant_cast.py @@ -16,7 +16,10 @@ from .const import DOMAIN, SIGNAL_HASS_CAST_SHOW_VIEW SERVICE_SHOW_VIEW = "show_lovelace_view" ATTR_VIEW_PATH = "view_path" ATTR_URL_PATH = "dashboard_path" -NO_URL_AVAILABLE_ERROR = "Home Assistant Cast requires your instance to be reachable via HTTPS. Enable Home Assistant Cloud or set up an external URL with valid SSL certificates" +NO_URL_AVAILABLE_ERROR = ( + "Home Assistant Cast requires your instance to be reachable via HTTPS. Enable Home" + " Assistant Cloud or set up an external URL with valid SSL certificates" +) async def async_setup_ha_cast( diff --git a/homeassistant/components/cast/media_player.py b/homeassistant/components/cast/media_player.py index 786a530e36c..b18c2ccb133 100644 --- a/homeassistant/components/cast/media_player.py +++ b/homeassistant/components/cast/media_player.py @@ -43,6 +43,7 @@ from homeassistant.components.media_player import ( from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CAST_APP_ID_HOMEASSISTANT_LOVELACE, + CONF_UUID, EVENT_HOMEASSISTANT_STOP, ) from homeassistant.core import CALLBACK_TYPE, Event, HomeAssistant, callback @@ -59,7 +60,6 @@ from .const import ( ADDED_CAST_DEVICES_KEY, CAST_MULTIZONE_MANAGER_KEY, CONF_IGNORE_CEC, - CONF_UUID, DOMAIN as CAST_DOMAIN, SIGNAL_CAST_DISCOVERED, SIGNAL_CAST_REMOVED, @@ -396,9 +396,11 @@ class CastMediaPlayerEntity(CastDevice, MediaPlayerEntity): url_description = f" from internal_url ({internal_url})" _LOGGER.error( - "Failed to cast media %s%s. Please make sure the URL is: " - "Reachable from the cast device and either a publicly resolvable " - "hostname or an IP address", + ( + "Failed to cast media %s%s. Please make sure the URL is: " + "Reachable from the cast device and either a publicly resolvable " + "hostname or an IP address" + ), media_status.content_id, url_description, ) diff --git a/homeassistant/components/cast/translations/de.json b/homeassistant/components/cast/translations/de.json index b337a8575e0..33b0fe6c434 100644 --- a/homeassistant/components/cast/translations/de.json +++ b/homeassistant/components/cast/translations/de.json @@ -12,7 +12,7 @@ "known_hosts": "Bekannte Hosts" }, "description": "Bekannte Hosts - Eine durch Kommas getrennte Liste von Hostnamen oder IP-Adressen von Cast-Ger\u00e4ten, die verwendet wird, wenn die mDNS-Erkennung nicht funktioniert.", - "title": "Google Cast-Konfiguration" + "title": "Google Cast Konfiguration" }, "confirm": { "description": "M\u00f6chtest Du mit der Einrichtung beginnen?" diff --git a/homeassistant/components/cast/translations/en.json b/homeassistant/components/cast/translations/en.json index ac473a5efe5..026b1fd11b9 100644 --- a/homeassistant/components/cast/translations/en.json +++ b/homeassistant/components/cast/translations/en.json @@ -15,7 +15,7 @@ "title": "Google Cast configuration" }, "confirm": { - "description": "Do you want to start set up?" + "description": "Do you want to start setup?" } } }, diff --git a/homeassistant/components/cast/translations/it.json b/homeassistant/components/cast/translations/it.json index d96bb9763c6..a7cea7f882a 100644 --- a/homeassistant/components/cast/translations/it.json +++ b/homeassistant/components/cast/translations/it.json @@ -15,7 +15,7 @@ "title": "Configurazione di Google Cast" }, "confirm": { - "description": "Vuoi iniziare la configurazione?" + "description": "Vuoi avviare la configurazione?" } } }, diff --git a/homeassistant/components/cast/translations/pt.json b/homeassistant/components/cast/translations/pt.json index 0238c7cbc94..704eda7c011 100644 --- a/homeassistant/components/cast/translations/pt.json +++ b/homeassistant/components/cast/translations/pt.json @@ -8,7 +8,7 @@ "title": "Google Cast" }, "confirm": { - "description": "Quer dar inicio \u00e0 configura\u00e7\u00e3o?" + "description": "Quer dar in\u00edcio \u00e0 configura\u00e7\u00e3o?" } } } diff --git a/homeassistant/components/cast/translations/sk.json b/homeassistant/components/cast/translations/sk.json index 9fe44a03383..0a1f9b0434b 100644 --- a/homeassistant/components/cast/translations/sk.json +++ b/homeassistant/components/cast/translations/sk.json @@ -10,7 +10,9 @@ "config": { "data": { "known_hosts": "Zn\u00e1mi hostitelia" - } + }, + "description": "Zn\u00e1mi hostitelia \u2013 \u010diarkami oddelen\u00fd zoznam n\u00e1zvov hostite\u013eov alebo adries IP zariaden\u00ed na prenos, pou\u017eite, ak zis\u0165ovanie mDNS nefunguje.", + "title": "Konfigur\u00e1cia Google Cast" }, "confirm": { "description": "Chcete za\u010da\u0165 nastavova\u0165?" @@ -22,10 +24,20 @@ "invalid_known_hosts": "Zn\u00e1mi hostitelia musia by\u0165 v zozname hostite\u013eov oddelen\u00fd \u010diarkami." }, "step": { + "advanced_options": { + "data": { + "ignore_cec": "Ignorova\u0165 CEC", + "uuid": "Povolen\u00e9 UUID" + }, + "description": "Povolen\u00e9 UUID \u2013 zoznam UUID zariaden\u00ed Cast oddelen\u00fdch \u010diarkami, ktor\u00e9 sa maj\u00fa prida\u0165 do aplik\u00e1cie Home Assistant. Pou\u017eite iba vtedy, ak nechcete prida\u0165 v\u0161etky dostupn\u00e9 zariadenia na prenos.\n Ignorova\u0165 CEC \u2013 \u010diarkami oddelen\u00fd zoznam zariaden\u00ed Chromecast, ktor\u00e9 by pri ur\u010dovan\u00ed akt\u00edvneho vstupu mali ignorova\u0165 \u00fadaje CEC. Toto sa odo\u0161le do pychromecast.IGNORE_CEC.", + "title": "Pokro\u010dil\u00e1 konfigur\u00e1cia Google Cast" + }, "basic_options": { "data": { "known_hosts": "Zn\u00e1mi hostitelia" - } + }, + "description": "Zn\u00e1mi hostitelia \u2013 \u010diarkami oddelen\u00fd zoznam n\u00e1zvov hostite\u013eov alebo adries IP zariaden\u00ed na prenos, pou\u017eite, ak zis\u0165ovanie mDNS nefunguje.", + "title": "Konfigur\u00e1cia Google Cast" } } } diff --git a/homeassistant/components/cert_expiry/translations/pt.json b/homeassistant/components/cert_expiry/translations/pt.json index 9f00493666a..48b04cdabad 100644 --- a/homeassistant/components/cert_expiry/translations/pt.json +++ b/homeassistant/components/cert_expiry/translations/pt.json @@ -10,7 +10,7 @@ "step": { "user": { "data": { - "host": "Servidor", + "host": "Endere\u00e7o", "port": "Porta" } } diff --git a/homeassistant/components/citybikes/sensor.py b/homeassistant/components/citybikes/sensor.py index 5da74167a0b..c87427e0e7e 100644 --- a/homeassistant/components/citybikes/sensor.py +++ b/homeassistant/components/citybikes/sensor.py @@ -24,8 +24,7 @@ from homeassistant.const import ( CONF_LONGITUDE, CONF_NAME, CONF_RADIUS, - LENGTH_FEET, - LENGTH_METERS, + UnitOfLength, ) from homeassistant.core import HomeAssistant from homeassistant.exceptions import PlatformNotReady @@ -172,7 +171,9 @@ async def async_setup_platform( radius = config.get(CONF_RADIUS, 0) name = config[CONF_NAME] if hass.config.units is US_CUSTOMARY_SYSTEM: - radius = DistanceConverter.convert(radius, LENGTH_FEET, LENGTH_METERS) + radius = DistanceConverter.convert( + radius, UnitOfLength.FEET, UnitOfLength.METERS + ) # Create a single instance of CityBikesNetworks. networks = hass.data.setdefault(CITYBIKES_NETWORKS, CityBikesNetworks(hass)) diff --git a/homeassistant/components/climacell/translations/de.json b/homeassistant/components/climacell/translations/de.json index 7c3e929dde2..53f58636465 100644 --- a/homeassistant/components/climacell/translations/de.json +++ b/homeassistant/components/climacell/translations/de.json @@ -6,7 +6,7 @@ "timestep": "Minuten zwischen den NowCast Kurzvorhersagen" }, "description": "Wenn du die Vorhersage-Entitit\u00e4t \"Kurzvorhersage\" aktivierst, kannst du die Anzahl der Minuten zwischen den einzelnen Vorhersagen konfigurieren. Die Anzahl der bereitgestellten Vorhersagen h\u00e4ngt von der Anzahl der zwischen den Vorhersagen gew\u00e4hlten Minuten ab.", - "title": "ClimaCell-Optionen aktualisieren" + "title": "ClimaCell Optionen aktualisieren" } } } diff --git a/homeassistant/components/climacell/translations/sensor.ca.json b/homeassistant/components/climacell/translations/sensor.ca.json index 359857925da..21f921352bc 100644 --- a/homeassistant/components/climacell/translations/sensor.ca.json +++ b/homeassistant/components/climacell/translations/sensor.ca.json @@ -4,7 +4,7 @@ "good": "Bo", "hazardous": "Perill\u00f3s", "moderate": "Moderat", - "unhealthy": "Poc saludable", + "unhealthy": "No saludable", "unhealthy_for_sensitive_groups": "No saludable per a grups sensibles", "very_unhealthy": "Gens saludable" }, diff --git a/homeassistant/components/climacell/translations/sensor.sk.json b/homeassistant/components/climacell/translations/sensor.sk.json index f94574da180..66302bb3c64 100644 --- a/homeassistant/components/climacell/translations/sensor.sk.json +++ b/homeassistant/components/climacell/translations/sensor.sk.json @@ -1,17 +1,25 @@ { "state": { "climacell__health_concern": { - "unhealthy": "Nezdrav\u00e9" + "good": "Dobr\u00e1", + "hazardous": "Nebezpe\u010dn\u00e1", + "moderate": "Mierna", + "unhealthy": "Nezdrav\u00e9", + "unhealthy_for_sensitive_groups": "Nezdrav\u00e9 pre citliv\u00e9 skupiny", + "very_unhealthy": "Ve\u013emi nezdrav\u00e9" }, "climacell__pollen_index": { + "high": "Vysok\u00e1", "low": "N\u00edzke", "medium": "Stredn\u00e9", + "none": "\u017diadne", "very_high": "Ve\u013emi vysok\u00e9", "very_low": "Ve\u013emi n\u00edzke" }, "climacell__precipitation_type": { "freezing_rain": "Mrzn\u00faci d\u00e1\u017e\u010f", "ice_pellets": "\u013dadovec", + "none": "\u017diadne", "rain": "D\u00e1\u017e\u010f", "snow": "Sneh" } diff --git a/homeassistant/components/climacell/translations/sk.json b/homeassistant/components/climacell/translations/sk.json new file mode 100644 index 00000000000..954b33e8139 --- /dev/null +++ b/homeassistant/components/climacell/translations/sk.json @@ -0,0 +1,13 @@ +{ + "options": { + "step": { + "init": { + "data": { + "timestep": "Min. Medzi predpove\u010fami NowCast" + }, + "description": "Ak sa rozhodnete povoli\u0165 entitu progn\u00f3zy \u201enowcast\u201c, m\u00f4\u017eete nakonfigurova\u0165 po\u010det min\u00fat medzi jednotliv\u00fdmi progn\u00f3zami. Po\u010det poskytnut\u00fdch predpoved\u00ed z\u00e1vis\u00ed od po\u010dtu min\u00fat vybrat\u00fdch medzi predpove\u010fami.", + "title": "Aktualizujte mo\u017enosti ClimaCell" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climate/__init__.py b/homeassistant/components/climate/__init__.py index 68338819421..ed0f8f2a4aa 100644 --- a/homeassistant/components/climate/__init__.py +++ b/homeassistant/components/climate/__init__.py @@ -18,7 +18,7 @@ from homeassistant.const import ( SERVICE_TURN_ON, STATE_OFF, STATE_ON, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant, ServiceCall import homeassistant.helpers.config_validation as cv @@ -250,7 +250,7 @@ class ClimateEntity(Entity): """Return the precision of the system.""" if hasattr(self, "_attr_precision"): return self._attr_precision - if self.hass.config.units.temperature_unit == TEMP_CELSIUS: + if self.hass.config.units.temperature_unit == UnitOfTemperature.CELSIUS: return PRECISION_TENTHS return PRECISION_WHOLE @@ -561,7 +561,7 @@ class ClimateEntity(Entity): """Return the minimum temperature.""" if not hasattr(self, "_attr_min_temp"): return TemperatureConverter.convert( - DEFAULT_MIN_TEMP, TEMP_CELSIUS, self.temperature_unit + DEFAULT_MIN_TEMP, UnitOfTemperature.CELSIUS, self.temperature_unit ) return self._attr_min_temp @@ -570,7 +570,7 @@ class ClimateEntity(Entity): """Return the maximum temperature.""" if not hasattr(self, "_attr_max_temp"): return TemperatureConverter.convert( - DEFAULT_MAX_TEMP, TEMP_CELSIUS, self.temperature_unit + DEFAULT_MAX_TEMP, UnitOfTemperature.CELSIUS, self.temperature_unit ) return self._attr_max_temp diff --git a/homeassistant/components/climate/device_trigger.py b/homeassistant/components/climate/device_trigger.py index a7b723eb41a..0b0bedb49bb 100644 --- a/homeassistant/components/climate/device_trigger.py +++ b/homeassistant/components/climate/device_trigger.py @@ -174,12 +174,15 @@ async def async_get_trigger_capabilities( if trigger_type == "hvac_mode_changed": return { "extra_fields": vol.Schema( - {vol.Optional(CONF_FOR): cv.positive_time_period_dict} + { + vol.Required(state_trigger.CONF_TO): vol.In(const.HVAC_MODES), + vol.Optional(CONF_FOR): cv.positive_time_period_dict, + } ) } if trigger_type == "current_temperature_changed": - unit_of_measurement = hass.config.units.temperature_unit + unit_of_measurement: str = hass.config.units.temperature_unit else: unit_of_measurement = PERCENTAGE diff --git a/homeassistant/components/climate/strings.json b/homeassistant/components/climate/strings.json index 1caf184b998..8c6c8f2d97a 100644 --- a/homeassistant/components/climate/strings.json +++ b/homeassistant/components/climate/strings.json @@ -25,5 +25,82 @@ "dry": "Dry", "fan_only": "Fan only" } + }, + "state_attributes": { + "_": { + "aux_heat": { "name": "Aux heat" }, + "current_humidity": { "name": "Current humidity" }, + "current_temperature": { "name": "Current temperature" }, + "fan_mode": { + "name": "Fan mode", + "state": { + "off": "[%key:common::state::off%]", + "on": "[%key:common::state::on%]", + "auto": "Auto", + "low": "Low", + "medium": "Medium", + "high": "High", + "top": "Top", + "middle": "Middle", + "focus": "Focus", + "diffuse": "Diffuse" + } + }, + "fan_modes": { + "name": "Fan modes" + }, + "humidity": { "name": "Target humidity" }, + "hvac_action": { + "name": "Current action", + "state": { + "off": "Off", + "heating": "Heating", + "cooling": "Cooling", + "drying": "Drying", + "idle": "Idle", + "fan": "Fan" + } + }, + "hvac_modes": { + "name": "HVAC modes" + }, + "max_humidity": { "name": "Max target humidity" }, + "max_temp": { "name": "Max target temperature" }, + "min_humidity": { "name": "Min target humidity" }, + "min_temp": { "name": "Min target temperature" }, + "preset_mode": { + "name": "Preset", + "state": { + "none": "None", + "eco": "Eco", + "away": "Away", + "boost": "Boost", + "comfort": "Comfort", + "home": "Home", + "sleep": "Sleep", + "activity": "Activity" + } + }, + "preset_modes": { + "name": "Presets" + }, + "swing_mode": { + "name": "Swing mode", + "state": { + "off": "[%key:common::state::off%]", + "on": "[%key:common::state::on%]", + "both": "Both", + "vertical": "Vertical", + "horizontal": "Horizontal" + } + }, + "swing_modes": { + "name": "Swing modes" + }, + "target_temp_high": { "name": "Upper target temperature" }, + "target_temp_low": { "name": "Lower target temperature" }, + "target_temp_step": { "name": "Target temperature step" }, + "temperature": { "name": "Target temperature" } + } } } diff --git a/homeassistant/components/climate/translations/bg.json b/homeassistant/components/climate/translations/bg.json index 6c3eb3b612a..a798e05aa07 100644 --- a/homeassistant/components/climate/translations/bg.json +++ b/homeassistant/components/climate/translations/bg.json @@ -25,5 +25,55 @@ "off": "\u0418\u0437\u043a\u043b\u044e\u0447\u0435\u043d" } }, + "state_attributes": { + "_": { + "current_humidity": { + "name": "\u0422\u0435\u043a\u0443\u0449\u0430 \u0432\u043b\u0430\u0436\u043d\u043e\u0441\u0442" + }, + "current_temperature": { + "name": "\u0422\u0435\u043a\u0443\u0449\u0430 \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430" + }, + "fan_mode": { + "name": "\u0420\u0435\u0436\u0438\u043c \u043d\u0430 \u0432\u0435\u043d\u0442\u0438\u043b\u0430\u0442\u043e\u0440\u0430", + "state": { + "auto": "\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u043d", + "high": "\u0421\u0438\u043b\u0435\u043d", + "low": "\u0421\u043b\u0430\u0431", + "medium": "\u0421\u0440\u0435\u0434\u0435\u043d", + "middle": "\u0421\u0440\u0435\u0434\u0435\u043d", + "off": "\u0418\u0437\u043a\u043b.", + "on": "\u0412\u043a\u043b." + } + }, + "fan_modes": { + "name": "\u0420\u0435\u0436\u0438\u043c\u0438 \u043d\u0430 \u0432\u0435\u043d\u0442\u0438\u043b\u0430\u0442\u043e\u0440\u0430" + }, + "hvac_action": { + "state": { + "cooling": "\u041e\u0445\u043b\u0430\u0436\u0434\u0430\u043d\u0435", + "drying": "\u0418\u0437\u0441\u0443\u0448\u0430\u0432\u0430\u043d\u0435", + "fan": "\u0412\u0435\u043d\u0442\u0438\u043b\u0430\u0442\u043e\u0440", + "heating": "\u041e\u0442\u043e\u043f\u043b\u0435\u043d\u0438\u0435", + "off": "\u0418\u0437\u043a\u043b\u044e\u0447\u0435\u043d" + } + }, + "hvac_modes": { + "name": "HVAC \u0440\u0435\u0436\u0438\u043c\u0438" + }, + "swing_mode": { + "name": "\u0420\u0435\u0436\u0438\u043c \u043d\u0430 \u043b\u044e\u043b\u0435\u0435\u043d\u0435", + "state": { + "both": "\u0418 \u0434\u0432\u0435\u0442\u0435", + "horizontal": "\u0425\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u043d\u043e", + "off": "\u0418\u0437\u043a\u043b.", + "on": "\u0412\u043a\u043b.", + "vertical": "\u0412\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u043d\u043e" + } + }, + "swing_modes": { + "name": "\u0420\u0435\u0436\u0438\u043c\u0438 \u043d\u0430 \u043b\u044e\u043b\u0435\u0435\u043d\u0435" + } + } + }, "title": "\u041a\u043b\u0438\u043c\u0430\u0442" } \ No newline at end of file diff --git a/homeassistant/components/climate/translations/ca.json b/homeassistant/components/climate/translations/ca.json index 3eb99744751..8a8e6d074b2 100644 --- a/homeassistant/components/climate/translations/ca.json +++ b/homeassistant/components/climate/translations/ca.json @@ -25,5 +25,106 @@ "off": "OFF" } }, + "state_attributes": { + "_": { + "aux_heat": { + "name": "Escalfor auxiliar" + }, + "current_humidity": { + "name": "Humitat actual" + }, + "current_temperature": { + "name": "Temperatura actual" + }, + "fan_mode": { + "name": "Mode del ventilador", + "state": { + "auto": "Autom\u00e0tic", + "diffuse": "Dif\u00fas", + "focus": "Enfocament", + "high": "Alt", + "low": "Baix", + "medium": "Mitj\u00e0", + "middle": "Mig", + "off": "OFF", + "on": "ON", + "top": "Superior" + } + }, + "fan_modes": { + "name": "Modes del ventilador" + }, + "humidity": { + "name": "Humitat objectiu" + }, + "hvac_action": { + "name": "Acci\u00f3 actual", + "state": { + "cooling": "Refredant", + "drying": "Assecant", + "fan": "Ventilador", + "heating": "Escalfant", + "idle": "Inactiu", + "off": "OFF" + } + }, + "hvac_modes": { + "name": "Modes HVAC" + }, + "max_humidity": { + "name": "Humitat m\u00e0xima objectiu" + }, + "max_temp": { + "name": "Temperatura m\u00e0xima objectiu" + }, + "min_humidity": { + "name": "Humitat objectiu m\u00ednima" + }, + "min_temp": { + "name": "Temperatura objectiu m\u00ednima" + }, + "preset_mode": { + "name": "Configuraci\u00f3", + "state": { + "activity": "Activitat", + "away": "A fora", + "boost": "Incrementat", + "comfort": "Confort", + "eco": "Eco", + "home": "A casa", + "none": "Cap", + "sleep": "Dormint" + } + }, + "preset_modes": { + "name": "Configuracions" + }, + "swing_mode": { + "name": "Mode d'oscil\u00b7laci\u00f3", + "state": { + "both": "Ambd\u00f3s", + "horizontal": "Horitzontal", + "off": "OFF", + "on": "ON", + "vertical": "Vertical" + } + }, + "swing_modes": { + "name": "Modes d'oscil\u00b7laci\u00f3" + }, + "target_temp_high": { + "name": "Temperatura objectiu superior" + }, + "target_temp_low": { + "name": "Temperatura objectiu inferior" + }, + "target_temp_step": { + "name": "Pas de la temperatura objectiu" + }, + "temperature": { + "name": "Temperatura objectiu" + } + } + }, "title": "Climatitzaci\u00f3" } \ No newline at end of file diff --git a/homeassistant/components/climate/translations/de.json b/homeassistant/components/climate/translations/de.json index b720df9f007..fe99202b745 100644 --- a/homeassistant/components/climate/translations/de.json +++ b/homeassistant/components/climate/translations/de.json @@ -25,5 +25,106 @@ "off": "Aus" } }, + "state_attributes": { + "_": { + "aux_heat": { + "name": "Hilfsw\u00e4rme" + }, + "current_humidity": { + "name": "Aktuelle Luftfeuchtigkeit" + }, + "current_temperature": { + "name": "Aktuelle Temperatur" + }, + "fan_mode": { + "name": "L\u00fcftermodus", + "state": { + "auto": "Automatisch", + "diffuse": "Diffus", + "focus": "Fokus", + "high": "Hoch", + "low": "Niedrig", + "medium": "Mittel", + "middle": "Mittel", + "off": "Aus", + "on": "An", + "top": "Top" + } + }, + "fan_modes": { + "name": "L\u00fcftermodi" + }, + "humidity": { + "name": "Ziel-Luftfeuchtigkeit" + }, + "hvac_action": { + "name": "Aktuelle Aktion", + "state": { + "cooling": "K\u00fchlung", + "drying": "Trocknen", + "fan": "L\u00fcfter", + "heating": "Heizbetrieb", + "idle": "Leerlauf", + "off": "Aus" + } + }, + "hvac_modes": { + "name": "HLK-Modi" + }, + "max_humidity": { + "name": "Maximale Zielfeuchtigkeit" + }, + "max_temp": { + "name": "Maximale Zieltemperatur" + }, + "min_humidity": { + "name": "Minimale Zielfeuchtigkeit" + }, + "min_temp": { + "name": "Minimale Zieltemperatur" + }, + "preset_mode": { + "name": "Voreinstellung", + "state": { + "activity": "Aktivit\u00e4t", + "away": "Abwesend", + "boost": "Boost", + "comfort": "Komfort", + "eco": "Eco", + "home": "Zu Hause", + "none": "Keine", + "sleep": "Schlafen" + } + }, + "preset_modes": { + "name": "Voreinstellungen" + }, + "swing_mode": { + "name": "Schwenk-Modus", + "state": { + "both": "Beide", + "horizontal": "Horizontal", + "off": "Aus", + "on": "An", + "vertical": "Vertikal" + } + }, + "swing_modes": { + "name": "Schwenk-Modi" + }, + "target_temp_high": { + "name": "Obere Zieltemperatur" + }, + "target_temp_low": { + "name": "Untere Zieltemperatur" + }, + "target_temp_step": { + "name": "Zieltemperaturschritt" + }, + "temperature": { + "name": "Zieltemperatur" + } + } + }, "title": "Klima" } \ No newline at end of file diff --git a/homeassistant/components/climate/translations/el.json b/homeassistant/components/climate/translations/el.json index b195ee8124c..9b1f13f5b8f 100644 --- a/homeassistant/components/climate/translations/el.json +++ b/homeassistant/components/climate/translations/el.json @@ -25,5 +25,106 @@ "off": "\u0391\u03bd\u03b5\u03bd\u03b5\u03c1\u03b3\u03cc" } }, + "state_attributes": { + "_": { + "aux_heat": { + "name": "\u0392\u03bf\u03b7\u03b8\u03b7\u03c4\u03b9\u03ba\u03ae \u03b8\u03ad\u03c1\u03bc\u03b1\u03bd\u03c3\u03b7" + }, + "current_humidity": { + "name": "\u03a4\u03c1\u03ad\u03c7\u03bf\u03c5\u03c3\u03b1 \u03c5\u03b3\u03c1\u03b1\u03c3\u03af\u03b1" + }, + "current_temperature": { + "name": "\u03a4\u03c1\u03ad\u03c7\u03bf\u03c5\u03c3\u03b1 \u03b8\u03b5\u03c1\u03bc\u03bf\u03ba\u03c1\u03b1\u03c3\u03af\u03b1" + }, + "fan_mode": { + "name": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03b1\u03bd\u03b5\u03bc\u03b9\u03c3\u03c4\u03ae\u03c1\u03b1", + "state": { + "auto": "\u0391\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03bf", + "diffuse": "\u0394\u03b9\u03ac\u03c7\u03c5\u03c3\u03b7", + "focus": "\u0395\u03c3\u03c4\u03af\u03b1\u03c3\u03b7", + "high": "\u03a5\u03c8\u03b7\u03bb\u03cc", + "low": "\u03a7\u03b1\u03bc\u03b7\u03bb\u03cc", + "medium": "\u039c\u03b5\u03c3\u03b1\u03af\u03bf", + "middle": "\u039c\u03ad\u03c3\u03b7", + "off": "\u0391\u03bd\u03b5\u03bd\u03b5\u03c1\u03b3\u03cc", + "on": "\u0395\u03bd\u03b5\u03c1\u03b3\u03cc", + "top": "\u039a\u03bf\u03c1\u03c5\u03c6\u03ae" + } + }, + "fan_modes": { + "name": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b5\u03c2 \u03b1\u03bd\u03b5\u03bc\u03b9\u03c3\u03c4\u03ae\u03c1\u03b1" + }, + "humidity": { + "name": "\u03a3\u03c4\u03cc\u03c7\u03bf\u03c2 \u03c5\u03b3\u03c1\u03b1\u03c3\u03af\u03b1\u03c2" + }, + "hvac_action": { + "name": "\u03a4\u03c1\u03ad\u03c7\u03bf\u03c5\u03c3\u03b1 \u03b4\u03c1\u03ac\u03c3\u03b7", + "state": { + "cooling": "\u03a8\u03cd\u03be\u03b7", + "drying": "\u0391\u03c6\u03cd\u03b3\u03c1\u03b1\u03bd\u03c3\u03b7", + "fan": "\u0391\u03bd\u03b5\u03bc\u03b9\u03c3\u03c4\u03ae\u03c1\u03b1\u03c2", + "heating": "\u0398\u03ad\u03c1\u03bc\u03b1\u03bd\u03c3\u03b7", + "idle": "\u0391\u03b4\u03c1\u03ac\u03bd\u03b5\u03b9\u03b1", + "off": "\u0391\u03bd\u03b5\u03bd\u03b5\u03c1\u03b3\u03cc" + } + }, + "hvac_modes": { + "name": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b5\u03c2 HVAC" + }, + "max_humidity": { + "name": "\u039c\u03ad\u03b3\u03b9\u03c3\u03c4\u03b7 \u03b5\u03c0\u03b9\u03b8\u03c5\u03bc\u03b7\u03c4\u03ae \u03c5\u03b3\u03c1\u03b1\u03c3\u03af\u03b1" + }, + "max_temp": { + "name": "\u039c\u03ad\u03b3\u03b9\u03c3\u03c4\u03b7 \u03b5\u03c0\u03b9\u03b8\u03c5\u03bc\u03b7\u03c4\u03ae \u03b8\u03b5\u03c1\u03bc\u03bf\u03ba\u03c1\u03b1\u03c3\u03af\u03b1" + }, + "min_humidity": { + "name": "\u0395\u03bb\u03ac\u03c7\u03b9\u03c3\u03c4\u03b7 \u03b5\u03c0\u03b9\u03b8\u03c5\u03bc\u03b7\u03c4\u03ae \u03c5\u03b3\u03c1\u03b1\u03c3\u03af\u03b1" + }, + "min_temp": { + "name": "\u0395\u03bb\u03ac\u03c7\u03b9\u03c3\u03c4\u03b7 \u03b5\u03c0\u03b9\u03b8\u03c5\u03bc\u03b7\u03c4\u03ae \u03b8\u03b5\u03c1\u03bc\u03bf\u03ba\u03c1\u03b1\u03c3\u03af\u03b1" + }, + "preset_mode": { + "name": "\u03a0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae", + "state": { + "activity": "\u0394\u03c1\u03b1\u03c3\u03c4\u03b7\u03c1\u03b9\u03cc\u03c4\u03b7\u03c4\u03b1", + "away": "\u0395\u03ba\u03c4\u03cc\u03c2 \u03a3\u03c0\u03b9\u03c4\u03b9\u03bf\u03cd", + "boost": "\u0395\u03bd\u03af\u03c3\u03c7\u03c5\u03c3\u03b7", + "comfort": "\u0386\u03bd\u03b5\u03c3\u03b7", + "eco": "Eco", + "home": "\u03a3\u03c0\u03af\u03c4\u03b9", + "none": "\u039a\u03b1\u03bd\u03ad\u03bd\u03b1", + "sleep": "\u038e\u03c0\u03bd\u03bf\u03c2" + } + }, + "preset_modes": { + "name": "\u03a0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2" + }, + "swing_mode": { + "name": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03b1\u03b9\u03ce\u03c1\u03b7\u03c3\u03b7\u03c2", + "state": { + "both": "\u039a\u03b1\u03b9 \u03c4\u03b1 \u03b4\u03c5\u03bf", + "horizontal": "\u039f\u03c1\u03b9\u03b6\u03cc\u03bd\u03c4\u03b9\u03b1", + "off": "\u0391\u03bd\u03b5\u03bd\u03b5\u03c1\u03b3\u03cc", + "on": "\u0395\u03bd\u03b5\u03c1\u03b3\u03cc", + "vertical": "\u039a\u03ac\u03b8\u03b5\u03c4\u03b1" + } + }, + "swing_modes": { + "name": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b5\u03c2 \u03b1\u03b9\u03ce\u03c1\u03b7\u03c3\u03b7\u03c2" + }, + "target_temp_high": { + "name": "\u0391\u03bd\u03ce\u03c4\u03b5\u03c1\u03b7 \u03b5\u03c0\u03b9\u03b8\u03c5\u03bc\u03b7\u03c4\u03ae \u03b8\u03b5\u03c1\u03bc\u03bf\u03ba\u03c1\u03b1\u03c3\u03af\u03b1" + }, + "target_temp_low": { + "name": "\u03a7\u03b1\u03bc\u03b7\u03bb\u03cc\u03c4\u03b5\u03c1\u03b7 \u03b5\u03c0\u03b9\u03b8\u03bc\u03b7\u03c4\u03ae \u03b8\u03b5\u03c1\u03bc\u03bf\u03ba\u03c1\u03b1\u03c3\u03af\u03b1" + }, + "target_temp_step": { + "name": "\u03a3\u03c4\u03ac\u03b4\u03b9\u03bf \u03b5\u03c0\u03b9\u03b8\u03c5\u03bc\u03b7\u03c4\u03ae\u03c2 \u03b8\u03b5\u03c1\u03bc\u03bf\u03ba\u03c1\u03b1\u03c3\u03af\u03b1\u03c2" + }, + "temperature": { + "name": "\u0395\u03c0\u03b9\u03b8\u03c5\u03bc\u03b7\u03c4\u03ae \u03b8\u03b5\u03c1\u03bc\u03bf\u03ba\u03c1\u03b1\u03c3\u03af\u03b1" + } + } + }, "title": "\u039a\u03bb\u03af\u03bc\u03b1" } \ No newline at end of file diff --git a/homeassistant/components/climate/translations/en.json b/homeassistant/components/climate/translations/en.json index 92ff71be756..2f31a9a4bfc 100644 --- a/homeassistant/components/climate/translations/en.json +++ b/homeassistant/components/climate/translations/en.json @@ -25,5 +25,106 @@ "off": "Off" } }, + "state_attributes": { + "_": { + "aux_heat": { + "name": "Aux heat" + }, + "current_humidity": { + "name": "Current humidity" + }, + "current_temperature": { + "name": "Current temperature" + }, + "fan_mode": { + "name": "Fan mode", + "state": { + "auto": "Auto", + "diffuse": "Diffuse", + "focus": "Focus", + "high": "High", + "low": "Low", + "medium": "Medium", + "middle": "Middle", + "off": "Off", + "on": "On", + "top": "Top" + } + }, + "fan_modes": { + "name": "Fan modes" + }, + "humidity": { + "name": "Target humidity" + }, + "hvac_action": { + "name": "Current action", + "state": { + "cooling": "Cooling", + "drying": "Drying", + "fan": "Fan", + "heating": "Heating", + "idle": "Idle", + "off": "Off" + } + }, + "hvac_modes": { + "name": "HVAC modes" + }, + "max_humidity": { + "name": "Max target humidity" + }, + "max_temp": { + "name": "Max target temperature" + }, + "min_humidity": { + "name": "Min target humidity" + }, + "min_temp": { + "name": "Min target temperature" + }, + "preset_mode": { + "name": "Preset", + "state": { + "activity": "Activity", + "away": "Away", + "boost": "Boost", + "comfort": "Comfort", + "eco": "Eco", + "home": "Home", + "none": "None", + "sleep": "Sleep" + } + }, + "preset_modes": { + "name": "Presets" + }, + "swing_mode": { + "name": "Swing mode", + "state": { + "both": "Both", + "horizontal": "Horizontal", + "off": "Off", + "on": "On", + "vertical": "Vertical" + } + }, + "swing_modes": { + "name": "Swing modes" + }, + "target_temp_high": { + "name": "Upper target temperature" + }, + "target_temp_low": { + "name": "Lower target temperature" + }, + "target_temp_step": { + "name": "Target temperature step" + }, + "temperature": { + "name": "Target temperature" + } + } + }, "title": "Climate" } \ No newline at end of file diff --git a/homeassistant/components/climate/translations/es.json b/homeassistant/components/climate/translations/es.json index 188adec66c5..a3c5dd73889 100644 --- a/homeassistant/components/climate/translations/es.json +++ b/homeassistant/components/climate/translations/es.json @@ -25,5 +25,106 @@ "off": "Apagado" } }, + "state_attributes": { + "_": { + "aux_heat": { + "name": "Calor auxiliar" + }, + "current_humidity": { + "name": "Humedad actual" + }, + "current_temperature": { + "name": "Temperatura actual" + }, + "fan_mode": { + "name": "Modo de ventilador", + "state": { + "auto": "Autom\u00e1tico", + "diffuse": "Difuso", + "focus": "Enfoque", + "high": "Alto", + "low": "Bajo", + "medium": "Medio", + "middle": "Parte media", + "off": "Apagado", + "on": "Encendido", + "top": "Parte superior" + } + }, + "fan_modes": { + "name": "Modos de ventilador" + }, + "humidity": { + "name": "Humedad objetivo" + }, + "hvac_action": { + "name": "Acci\u00f3n en curso", + "state": { + "cooling": "Refrigeraci\u00f3n", + "drying": "Secando", + "fan": "Ventilador", + "heating": "Calefacci\u00f3n", + "idle": "Inactivo", + "off": "Apagado" + } + }, + "hvac_modes": { + "name": "Modos del sistema de climatizaci\u00f3n" + }, + "max_humidity": { + "name": "Humedad objetivo m\u00e1xima" + }, + "max_temp": { + "name": "Temperatura objetivo m\u00e1xima" + }, + "min_humidity": { + "name": "Humedad objetivo m\u00ednima" + }, + "min_temp": { + "name": "Temperatura objetivo m\u00ednima" + }, + "preset_mode": { + "name": "Preestablecido", + "state": { + "activity": "Actividad", + "away": "Ausente", + "boost": "Impulso", + "comfort": "Confort", + "eco": "Eco", + "home": "En casa", + "none": "Ninguno", + "sleep": "Dormir" + } + }, + "preset_modes": { + "name": "Preajustes" + }, + "swing_mode": { + "name": "Modo de oscilaci\u00f3n", + "state": { + "both": "Ambos", + "horizontal": "Horizontal", + "off": "Apagado", + "on": "Encendido", + "vertical": "Vertical" + } + }, + "swing_modes": { + "name": "Modos de oscilaci\u00f3n" + }, + "target_temp_high": { + "name": "Temperatura objetivo superior" + }, + "target_temp_low": { + "name": "Temperatura objetivo m\u00e1s baja" + }, + "target_temp_step": { + "name": "Paso de temperatura objetivo" + }, + "temperature": { + "name": "Temperatura objetivo" + } + } + }, "title": "Climatizaci\u00f3n" } \ No newline at end of file diff --git a/homeassistant/components/climate/translations/et.json b/homeassistant/components/climate/translations/et.json index 2f947675510..9fc5c285e1e 100644 --- a/homeassistant/components/climate/translations/et.json +++ b/homeassistant/components/climate/translations/et.json @@ -25,5 +25,106 @@ "off": "V\u00e4ljas" } }, + "state_attributes": { + "_": { + "aux_heat": { + "name": "Lisak\u00fcte" + }, + "current_humidity": { + "name": "Praegune niiskus" + }, + "current_temperature": { + "name": "Praegune temperatuur" + }, + "fan_mode": { + "name": "Ventilaatori re\u017eiim", + "state": { + "auto": "Auto", + "diffuse": "Hajuta", + "focus": "Suuna", + "high": "K\u00f5rge", + "low": "Madal", + "medium": "Keskmine", + "middle": "Kiire", + "off": "V\u00e4ljas", + "on": "Sees", + "top": "Suurim" + } + }, + "fan_modes": { + "name": "Ventilaatori re\u017eiimid" + }, + "humidity": { + "name": "Soovitud niiskusem\u00e4\u00e4r" + }, + "hvac_action": { + "name": "Praegune tegevus", + "state": { + "cooling": "Jahutus", + "drying": "Kuivatus", + "fan": "Vent", + "heating": "K\u00fcte", + "idle": "Ootel", + "off": "V\u00e4ljas" + } + }, + "hvac_modes": { + "name": "HVAC re\u017eiimid" + }, + "max_humidity": { + "name": "Max sihtniiskus" + }, + "max_temp": { + "name": "Max sihttemperatuur" + }, + "min_humidity": { + "name": "Min sihtniiskus" + }, + "min_temp": { + "name": "Min sihttemperatuur" + }, + "preset_mode": { + "name": "Eelseade", + "state": { + "activity": "Tegevus", + "away": "Eemal", + "boost": "Turbo", + "comfort": "Mugav", + "eco": "\u00d6ko", + "home": "Kodus", + "none": "Puudub", + "sleep": "Uneaeg" + } + }, + "preset_modes": { + "name": "Eelseaded" + }, + "swing_mode": { + "name": "\u00d5\u00f5tsumise re\u017eiim", + "state": { + "both": "M\u00f5lemad", + "horizontal": "Horisontaalne", + "off": "V\u00e4ljas", + "on": "Sees", + "vertical": "Vertikaalne" + } + }, + "swing_modes": { + "name": "\u00d5\u00f5tsumise re\u017eiimid" + }, + "target_temp_high": { + "name": "K\u00f5rgeim sihttemperatuur" + }, + "target_temp_low": { + "name": "Madalaim sihttemperatuur" + }, + "target_temp_step": { + "name": "Sihttemperatuuri samm" + }, + "temperature": { + "name": "Soovitud temperatuur" + } + } + }, "title": "Kliimaseade" } \ No newline at end of file diff --git a/homeassistant/components/climate/translations/he.json b/homeassistant/components/climate/translations/he.json index abf976c2b5b..ff82a25764f 100644 --- a/homeassistant/components/climate/translations/he.json +++ b/homeassistant/components/climate/translations/he.json @@ -25,5 +25,106 @@ "off": "\u05db\u05d1\u05d5\u05d9" } }, + "state_attributes": { + "_": { + "aux_heat": { + "name": "\u05de\u05e1\u05d9\u05d9\u05e2 \u05d7\u05d9\u05de\u05d5\u05dd" + }, + "current_humidity": { + "name": "\u05dc\u05d7\u05d5\u05ea \u05e0\u05d5\u05db\u05d7\u05d9\u05ea" + }, + "current_temperature": { + "name": "\u05d8\u05de\u05e4\u05e8\u05d8\u05d5\u05e8\u05d4 \u05e0\u05d5\u05db\u05d7\u05d9\u05ea" + }, + "fan_mode": { + "name": "\u05de\u05e6\u05d1 \u05de\u05d0\u05d5\u05e8\u05e8", + "state": { + "auto": "\u05d0\u05d5\u05d8\u05d5\u05de\u05d8\u05d9", + "diffuse": "\u05e4\u05d9\u05d6\u05d5\u05e8", + "focus": "\u05de\u05d9\u05e7\u05d5\u05d3", + "high": "\u05d2\u05d1\u05d5\u05d4", + "low": "\u05e0\u05de\u05d5\u05da", + "medium": "\u05d1\u05d9\u05e0\u05d5\u05e0\u05d9", + "middle": "\u05d0\u05de\u05e6\u05e2", + "off": "\u05db\u05d1\u05d5\u05d9", + "on": "\u05de\u05d5\u05e4\u05e2\u05dc", + "top": "\u05dc\u05de\u05e2\u05dc\u05d4" + } + }, + "fan_modes": { + "name": "\u05de\u05e6\u05d1\u05d9 \u05de\u05d0\u05d5\u05d5\u05e8\u05e8" + }, + "humidity": { + "name": "\u05d9\u05e2\u05d3 \u05dc\u05d7\u05d5\u05ea" + }, + "hvac_action": { + "name": "\u05e4\u05e2\u05d5\u05dc\u05d4 \u05e0\u05d5\u05db\u05d7\u05d9\u05ea", + "state": { + "cooling": "\u05e7\u05d9\u05e8\u05d5\u05e8", + "drying": "\u05d9\u05d9\u05d1\u05d5\u05e9", + "fan": "\u05de\u05d0\u05d5\u05d5\u05e8\u05e8", + "heating": "\u05d7\u05d9\u05de\u05d5\u05dd", + "idle": "\u05de\u05de\u05ea\u05d9\u05df", + "off": "\u05db\u05d1\u05d5\u05d9" + } + }, + "hvac_modes": { + "name": "\u05de\u05e6\u05d1\u05d9 HVAC" + }, + "max_humidity": { + "name": "\u05dc\u05d7\u05d5\u05ea \u05d9\u05e2\u05d3 \u05de\u05e7\u05e1\u05d9\u05de\u05dc\u05d9\u05ea" + }, + "max_temp": { + "name": "\u05d8\u05de\u05e4\u05e8\u05d8\u05d5\u05e8\u05ea \u05d9\u05e2\u05d3 \u05de\u05e7\u05e1\u05d9\u05de\u05dc\u05d9\u05ea" + }, + "min_humidity": { + "name": "\u05dc\u05d7\u05d5\u05ea \u05d9\u05e2\u05d3 \u05de\u05d9\u05e0\u05d9\u05de\u05dc\u05d9\u05ea" + }, + "min_temp": { + "name": "\u05d8\u05de\u05e4\u05e8\u05d8\u05d5\u05e8\u05ea \u05d9\u05e2\u05d3 \u05de\u05d9\u05e0\u05d9\u05de\u05dc\u05d9\u05ea" + }, + "preset_mode": { + "name": "\u05de\u05d5\u05d2\u05d3\u05e8 \u05de\u05e8\u05d0\u05e9", + "state": { + "activity": "\u05e4\u05e2\u05d9\u05dc\u05d5\u05ea", + "away": "\u05d1\u05d7\u05d5\u05e5", + "boost": "\u05de\u05d5\u05d2\u05d1\u05e8", + "comfort": "\u05e0\u05d5\u05d7", + "eco": "\u05d7\u05e1\u05db\u05d5\u05e0\u05d9", + "home": "\u05d1\u05d9\u05ea", + "none": "\u05dc\u05dc\u05d0", + "sleep": "\u05e9\u05d9\u05e0\u05d4" + } + }, + "preset_modes": { + "name": "\u05e7\u05d1\u05d5\u05e2\u05d5\u05ea \u05de\u05e8\u05d0\u05e9" + }, + "swing_mode": { + "name": "\u05de\u05e6\u05d1 \u05e0\u05d3\u05e0\u05d5\u05d3", + "state": { + "both": "\u05e9\u05e0\u05d9\u05d4\u05dd", + "horizontal": "\u05d0\u05d5\u05e4\u05e7\u05d9", + "off": "\u05db\u05d1\u05d5\u05d9", + "on": "\u05de\u05d5\u05e4\u05e2\u05dc", + "vertical": "\u05d0\u05e0\u05db\u05d9" + } + }, + "swing_modes": { + "name": "\u05de\u05e6\u05d1\u05d9 \u05e0\u05d3\u05e0\u05d5\u05d3" + }, + "target_temp_high": { + "name": "\u05d8\u05de\u05e4\u05e8\u05d8\u05d5\u05e8\u05ea \u05d9\u05e2\u05d3 \u05d2\u05d1\u05d5\u05d4\u05d4" + }, + "target_temp_low": { + "name": "\u05d8\u05de\u05e4\u05e8\u05d8\u05d5\u05e8\u05ea \u05d9\u05e2\u05d3 \u05e0\u05de\u05d5\u05db\u05d4" + }, + "target_temp_step": { + "name": "\u05e9\u05dc\u05d1 \u05d8\u05de\u05e4\u05e8\u05d8\u05d5\u05e8\u05ea \u05d4\u05d9\u05e2\u05d3" + }, + "temperature": { + "name": "\u05d8\u05de\u05e4\u05e8\u05d8\u05d5\u05e8\u05ea \u05d4\u05d9\u05e2\u05d3" + } + } + }, "title": "\u05d0\u05e7\u05dc\u05d9\u05dd" } \ No newline at end of file diff --git a/homeassistant/components/climate/translations/hu.json b/homeassistant/components/climate/translations/hu.json index f0fe007c7c5..f0621cddc3f 100644 --- a/homeassistant/components/climate/translations/hu.json +++ b/homeassistant/components/climate/translations/hu.json @@ -25,5 +25,106 @@ "off": "Ki" } }, + "state_attributes": { + "_": { + "aux_heat": { + "name": "K\u00fcls\u0151 h\u0151forr\u00e1s" + }, + "current_humidity": { + "name": "Aktu\u00e1lis p\u00e1ratartalom" + }, + "current_temperature": { + "name": "Aktu\u00e1lis h\u0151m\u00e9rs\u00e9klet" + }, + "fan_mode": { + "name": "Ventil\u00e1tor m\u00f3d", + "state": { + "auto": "Automatikus", + "diffuse": "Diff\u00faz", + "focus": "F\u00f3kusz", + "high": "Magas", + "low": "Alacsony", + "medium": "K\u00f6zepes", + "middle": "K\u00f6zepes", + "off": "Ki", + "on": "Be", + "top": "Fels\u0151" + } + }, + "fan_modes": { + "name": "Ventil\u00e1tor m\u00f3dok" + }, + "humidity": { + "name": "K\u00edv\u00e1nt p\u00e1ratartalom" + }, + "hvac_action": { + "name": "Aktu\u00e1lis m\u0171velet", + "state": { + "cooling": "H\u0171t\u00e9s", + "drying": "P\u00e1r\u00e1tlan\u00edt\u00e1s", + "fan": "Ventil\u00e1tor", + "heating": "F\u0171t\u00e9s", + "idle": "T\u00e9tlen", + "off": "Ki" + } + }, + "hvac_modes": { + "name": "HVAC m\u00f3dok" + }, + "max_humidity": { + "name": "Maxim\u00e1lis c\u00e9l p\u00e1ratartalom" + }, + "max_temp": { + "name": "Maxim\u00e1lis c\u00e9lh\u0151m\u00e9rs\u00e9klet" + }, + "min_humidity": { + "name": "Minim\u00e1lis c\u00e9l p\u00e1ratartalom" + }, + "min_temp": { + "name": "Minim\u00e1lis c\u00e9lh\u0151m\u00e9rs\u00e9klet" + }, + "preset_mode": { + "name": "\u00dczemm\u00f3d", + "state": { + "activity": "Tev\u00e9kenys\u00e9g", + "away": "T\u00e1vol", + "boost": "Turb\u00f3", + "comfort": "Komfort", + "eco": "Takar\u00e9kos", + "home": "Otthon", + "none": "Nincs", + "sleep": "Alv\u00e1s" + } + }, + "preset_modes": { + "name": "El\u0151be\u00e1ll\u00edt\u00e1sok" + }, + "swing_mode": { + "name": "Legyez\u00e9s", + "state": { + "both": "Mindkett\u0151", + "horizontal": "V\u00edzszintes", + "off": "Ki", + "on": "Be", + "vertical": "F\u00fcgg\u0151leges" + } + }, + "swing_modes": { + "name": "Legyez\u00e9s" + }, + "target_temp_high": { + "name": "Fels\u0151 c\u00e9lh\u0151m\u00e9rs\u00e9klet" + }, + "target_temp_low": { + "name": "Als\u00f3 c\u00e9lh\u0151m\u00e9rs\u00e9klet" + }, + "target_temp_step": { + "name": "C\u00e9lh\u0151m\u00e9rs\u00e9klet l\u00e9pt\u00e9k" + }, + "temperature": { + "name": "K\u00edv\u00e1nt h\u0151m\u00e9rs\u00e9klet" + } + } + }, "title": "H\u0171t\u00e9s/f\u0171t\u00e9s" } \ No newline at end of file diff --git a/homeassistant/components/climate/translations/id.json b/homeassistant/components/climate/translations/id.json index bdae7d60067..bb60d49c20a 100644 --- a/homeassistant/components/climate/translations/id.json +++ b/homeassistant/components/climate/translations/id.json @@ -25,5 +25,106 @@ "off": "Mati" } }, + "state_attributes": { + "_": { + "aux_heat": { + "name": "Pemanasan tambahan" + }, + "current_humidity": { + "name": "Kelembaban saat ini" + }, + "current_temperature": { + "name": "Suhu saat ini" + }, + "fan_mode": { + "name": "Mode kipas", + "state": { + "auto": "Otomatis", + "diffuse": "Membaur", + "focus": "Fokus", + "high": "Tinggi", + "low": "Rendah", + "medium": "Sedang", + "middle": "Tengah", + "off": "Mati", + "on": "Nyala", + "top": "Atas" + } + }, + "fan_modes": { + "name": "Mode kipas" + }, + "humidity": { + "name": "Target kelembaban" + }, + "hvac_action": { + "name": "Aksi saat ini", + "state": { + "cooling": "Mendinginkan", + "drying": "Mengeringkan", + "fan": "Kipas Angin", + "heating": "Memanaskan", + "idle": "Siaga", + "off": "Mati" + } + }, + "hvac_modes": { + "name": "Mode HVAC" + }, + "max_humidity": { + "name": "Target kelembaban maksimum" + }, + "max_temp": { + "name": "Target suhu maksimum" + }, + "min_humidity": { + "name": "Target kelembaban minimum" + }, + "min_temp": { + "name": "Target suhu minimum" + }, + "preset_mode": { + "name": "Prasetel", + "state": { + "activity": "Aktivitas", + "away": "Keluar", + "boost": "Kencang", + "comfort": "Nyaman", + "eco": "Eco", + "home": "Di Rumah", + "none": "Tidak Ada", + "sleep": "Tidur" + } + }, + "preset_modes": { + "name": "Prasetel" + }, + "swing_mode": { + "name": "Mode ayunan", + "state": { + "both": "Keduanya", + "horizontal": "Horizontal", + "off": "Mati", + "on": "Nyala", + "vertical": "Vertikal" + } + }, + "swing_modes": { + "name": "Mode ayunan" + }, + "target_temp_high": { + "name": "Target suhu atas" + }, + "target_temp_low": { + "name": "Target suhu bawah" + }, + "target_temp_step": { + "name": "Langkah target suhu" + }, + "temperature": { + "name": "Target suhu" + } + } + }, "title": "Cuaca" } \ No newline at end of file diff --git a/homeassistant/components/climate/translations/it.json b/homeassistant/components/climate/translations/it.json index 4f427209325..1abe9345a43 100644 --- a/homeassistant/components/climate/translations/it.json +++ b/homeassistant/components/climate/translations/it.json @@ -25,5 +25,106 @@ "off": "Spento" } }, + "state_attributes": { + "_": { + "aux_heat": { + "name": "Riscaldamento ausiliario" + }, + "current_humidity": { + "name": "Umidit\u00e0 attuale" + }, + "current_temperature": { + "name": "Temperatura attuale" + }, + "fan_mode": { + "name": "Modalit\u00e0 ventilatore", + "state": { + "auto": "Automatico", + "diffuse": "Diffuso", + "focus": "Messa a fuoco", + "high": "Alto", + "low": "Basso", + "medium": "Medio", + "middle": "Mezzo", + "off": "Spento", + "on": "Acceso", + "top": "Superiore" + } + }, + "fan_modes": { + "name": "Modalit\u00e0 di ventilazione" + }, + "humidity": { + "name": "Umidit\u00e0 desiderata" + }, + "hvac_action": { + "name": "Azione in corso", + "state": { + "cooling": "Raffreddamento", + "drying": "Deumidificazione", + "fan": "Ventilatore", + "heating": "Riscaldamento", + "idle": "Inattivo", + "off": "Spento" + } + }, + "hvac_modes": { + "name": "Modalit\u00e0 HVAC" + }, + "max_humidity": { + "name": "Umidit\u00e0 desiderata massima" + }, + "max_temp": { + "name": "Temperatura desiderata massima" + }, + "min_humidity": { + "name": "Umidit\u00e0 desiderata minima" + }, + "min_temp": { + "name": "Temperatura desiderata minima" + }, + "preset_mode": { + "name": "Preimpostato", + "state": { + "activity": "Attivit\u00e0", + "away": "Fuori casa", + "boost": "Velocizza", + "comfort": "Comfort", + "eco": "Eco", + "home": "In casa", + "none": "Nessuno", + "sleep": "Notte" + } + }, + "preset_modes": { + "name": "Preimpostazioni" + }, + "swing_mode": { + "name": "Modalit\u00e0 di oscillazione", + "state": { + "both": "Entrambi", + "horizontal": "Orizzontale", + "off": "Spento", + "on": "Acceso", + "vertical": "Verticale" + } + }, + "swing_modes": { + "name": "Modalit\u00e0 di oscillazione" + }, + "target_temp_high": { + "name": "Temperatura obiettivo pi\u00f9 alta" + }, + "target_temp_low": { + "name": "Temperatura obiettivo pi\u00f9 bassa" + }, + "target_temp_step": { + "name": "passo di temperatura obiettivo" + }, + "temperature": { + "name": "Temperatura obiettivo" + } + } + }, "title": "Termostato" } \ No newline at end of file diff --git a/homeassistant/components/climate/translations/ja.json b/homeassistant/components/climate/translations/ja.json index 0c89bac48d8..d69d5d95fd0 100644 --- a/homeassistant/components/climate/translations/ja.json +++ b/homeassistant/components/climate/translations/ja.json @@ -25,5 +25,83 @@ "off": "\u30aa\u30d5" } }, + "state_attributes": { + "_": { + "aux_heat": { + "name": "\u88dc\u52a9\u71b1" + }, + "current_humidity": { + "name": "\u73fe\u5728\u306e\u6e7f\u5ea6" + }, + "current_temperature": { + "name": "\u73fe\u5728\u306e\u6e29\u5ea6" + }, + "fan_mode": { + "name": "\u30d5\u30a1\u30f3\u30e2\u30fc\u30c9", + "state": { + "auto": "\u30aa\u30fc\u30c8", + "diffuse": "\u30c7\u30a3\u30d5\u30e5\u30fc\u30ba", + "focus": "\u30d5\u30a9\u30fc\u30ab\u30b9", + "high": "\u9ad8", + "low": "\u4f4e", + "medium": "\u4e2d\u7a0b\u5ea6", + "middle": "\u4e2d", + "off": "\u30aa\u30d5", + "on": "\u30aa\u30f3", + "top": "\u30c8\u30c3\u30d7" + } + }, + "fan_modes": { + "name": "\u30d5\u30a1\u30f3\u30e2\u30fc\u30c9" + }, + "humidity": { + "name": "\u76ee\u6a19\u6e7f\u5ea6" + }, + "hvac_action": { + "name": "\u73fe\u5728\u306e\u30a2\u30af\u30b7\u30e7\u30f3", + "state": { + "cooling": "\u51b7\u623f", + "drying": "\u4e7e\u71e5", + "fan": "\u30d5\u30a1\u30f3", + "heating": "\u6696\u623f", + "idle": "\u30a2\u30a4\u30c9\u30eb" + } + }, + "preset_mode": { + "state": { + "none": "\u306a\u3057", + "sleep": "\u30b9\u30ea\u30fc\u30d7" + } + }, + "preset_modes": { + "name": "\u30d7\u30ea\u30bb\u30c3\u30c8" + }, + "swing_mode": { + "name": "\u30b9\u30a4\u30f3\u30b0\u30e2\u30fc\u30c9", + "state": { + "both": "\u4e21\u65b9", + "horizontal": "\u6c34\u5e73", + "off": "\u30aa\u30d5", + "on": "\u30aa\u30f3", + "vertical": "\u5782\u76f4" + } + }, + "swing_modes": { + "name": "\u30b9\u30a4\u30f3\u30b0\u30e2\u30fc\u30c9" + }, + "target_temp_high": { + "name": "\u76ee\u6a19\u6e29\u5ea6\u3092\u4e0a\u3052\u308b" + }, + "target_temp_low": { + "name": "\u76ee\u6a19\u6e29\u5ea6\u3092\u4e0b\u3052\u308b" + }, + "target_temp_step": { + "name": "\u76ee\u6a19\u6e29\u5ea6\u30b9\u30c6\u30c3\u30d7" + }, + "temperature": { + "name": "\u76ee\u6a19\u6e29\u5ea6" + } + } + }, "title": "\u6c17\u5019" } \ No newline at end of file diff --git a/homeassistant/components/climate/translations/lt.json b/homeassistant/components/climate/translations/lt.json index 1f60d1cd5c2..d9a5d057d40 100644 --- a/homeassistant/components/climate/translations/lt.json +++ b/homeassistant/components/climate/translations/lt.json @@ -1,6 +1,10 @@ { "state": { "_": { + "cool": "V\u0117sina", + "dry": "D\u017eiovina", + "heat": "\u0160ildo", + "heat_cool": "\u0160ildo/V\u0117sina", "off": "I\u0161jungta" } } diff --git a/homeassistant/components/climate/translations/nl.json b/homeassistant/components/climate/translations/nl.json index 016131858a4..b24b2991bd1 100644 --- a/homeassistant/components/climate/translations/nl.json +++ b/homeassistant/components/climate/translations/nl.json @@ -25,5 +25,106 @@ "off": "Uit" } }, + "state_attributes": { + "_": { + "aux_heat": { + "name": "Extra verwarming" + }, + "current_humidity": { + "name": "Huidige luchtvochtigheid" + }, + "current_temperature": { + "name": "Huidige temperatuur" + }, + "fan_mode": { + "name": "Ventilator mode", + "state": { + "auto": "Automatisch", + "diffuse": "Diffuus", + "focus": "Concentratie", + "high": "Hoog", + "low": "Laag", + "medium": "Gemiddeld", + "middle": "Tussenstand", + "off": "Uit", + "on": "Aan", + "top": "Maximaal" + } + }, + "fan_modes": { + "name": "Ventilator modes" + }, + "humidity": { + "name": "Luchtvochtigheid instelling" + }, + "hvac_action": { + "name": "Huidige operatie", + "state": { + "cooling": "Koelen", + "drying": "Drogen", + "fan": "Ventilator", + "heating": "Verwarmen", + "idle": "Niet actief", + "off": "Uit" + } + }, + "hvac_modes": { + "name": "Airconditioner modi" + }, + "max_humidity": { + "name": "Maximale luchtvochtigheid instelling" + }, + "max_temp": { + "name": "Maximale insteltemperatuur" + }, + "min_humidity": { + "name": "Minimale luchtvochtigheid instelling" + }, + "min_temp": { + "name": "Minimale insteltemperatuur" + }, + "preset_mode": { + "name": "Voorinstelling", + "state": { + "activity": "Activiteit", + "away": "Niet thuis", + "boost": "Boost", + "comfort": "Comfort", + "eco": "ECO", + "home": "Thuis", + "none": "Geen", + "sleep": "Slapen" + } + }, + "preset_modes": { + "name": "Voorinstellingen" + }, + "swing_mode": { + "name": "Beweging", + "state": { + "both": "Beide", + "horizontal": "Horizontaal", + "off": "Uit", + "on": "Aan", + "vertical": "Verticaal" + } + }, + "swing_modes": { + "name": "Beweging" + }, + "target_temp_high": { + "name": "Maximale temperatuurinstelling" + }, + "target_temp_low": { + "name": "Minimale temperatuurinstelling" + }, + "target_temp_step": { + "name": "Temperatuurinstelling stapgrootte" + }, + "temperature": { + "name": "Temperatuurinstelling" + } + } + }, "title": "Klimaat" } \ No newline at end of file diff --git a/homeassistant/components/climate/translations/no.json b/homeassistant/components/climate/translations/no.json index e7540c923ec..0667c5126d8 100644 --- a/homeassistant/components/climate/translations/no.json +++ b/homeassistant/components/climate/translations/no.json @@ -25,5 +25,106 @@ "off": "Av" } }, + "state_attributes": { + "_": { + "aux_heat": { + "name": "Aux varme" + }, + "current_humidity": { + "name": "Aktuell fuktighet" + }, + "current_temperature": { + "name": "Gjeldende temperatur" + }, + "fan_mode": { + "name": "Viftemodus", + "state": { + "auto": "Auto", + "diffuse": "Diffus", + "focus": "Fokus", + "high": "H\u00f8y", + "low": "Lav", + "medium": "Medium", + "middle": "Midten", + "off": "Av", + "on": "P\u00e5", + "top": "Topp" + } + }, + "fan_modes": { + "name": "Viftemoduser" + }, + "humidity": { + "name": "M\u00e5l fuktighet" + }, + "hvac_action": { + "name": "Gjeldende handling", + "state": { + "cooling": "Kj\u00f8ling", + "drying": "T\u00f8rking", + "fan": "Vifte", + "heating": "Oppvarming", + "idle": "Inaktiv", + "off": "Av" + } + }, + "hvac_modes": { + "name": "HVAC-moduser" + }, + "max_humidity": { + "name": "Maks m\u00e5lfuktighet" + }, + "max_temp": { + "name": "Maks m\u00e5ltemperatur" + }, + "min_humidity": { + "name": "Minimum m\u00e5lfuktighet" + }, + "min_temp": { + "name": "Minimum m\u00e5ltemperatur" + }, + "preset_mode": { + "name": "Forh\u00e5ndsinnstilt", + "state": { + "activity": "Aktivitet", + "away": "Borte", + "boost": "\u00d8ke", + "comfort": "Komfort", + "eco": "\u00d8ko", + "home": "Hjemme", + "none": "Ingen", + "sleep": "Sove" + } + }, + "preset_modes": { + "name": "Forh\u00e5ndsinnstillinger" + }, + "swing_mode": { + "name": "Svingmodus", + "state": { + "both": "Begge", + "horizontal": "Horisontal", + "off": "Av", + "on": "P\u00e5", + "vertical": "Vertikal" + } + }, + "swing_modes": { + "name": "Svingmoduser" + }, + "target_temp_high": { + "name": "\u00d8vre m\u00e5ltemperatur" + }, + "target_temp_low": { + "name": "Lavere m\u00e5ltemperatur" + }, + "target_temp_step": { + "name": "Trinn for m\u00e5ltemperatur" + }, + "temperature": { + "name": "M\u00e5ltemperatur" + } + } + }, "title": "Klima" } \ No newline at end of file diff --git a/homeassistant/components/climate/translations/pl.json b/homeassistant/components/climate/translations/pl.json index ceebac3851c..145f0bb6217 100644 --- a/homeassistant/components/climate/translations/pl.json +++ b/homeassistant/components/climate/translations/pl.json @@ -25,5 +25,106 @@ "off": "wy\u0142." } }, + "state_attributes": { + "_": { + "aux_heat": { + "name": "Dodatkowe \u017ar\u00f3d\u0142o ciep\u0142a" + }, + "current_humidity": { + "name": "Aktualna wilgotno\u015b\u0107" + }, + "current_temperature": { + "name": "Aktualna temperatura" + }, + "fan_mode": { + "name": "Tryb wentylatora", + "state": { + "auto": "auto", + "diffuse": "rozpraszanie", + "focus": "skupianie", + "high": "wysoki", + "low": "niski", + "medium": "\u015bredni", + "middle": "po\u015bredni", + "off": "wy\u0142.", + "on": "w\u0142.", + "top": "do g\u00f3ry" + } + }, + "fan_modes": { + "name": "Tryby wentylatora" + }, + "humidity": { + "name": "Docelowa wilgotno\u015b\u0107" + }, + "hvac_action": { + "name": "Bie\u017c\u0105ca akcja", + "state": { + "cooling": "ch\u0142odzenie", + "drying": "osuszanie", + "fan": "wentylator", + "heating": "grzanie", + "idle": "bezczynny", + "off": "wy\u0142\u0105czony" + } + }, + "hvac_modes": { + "name": "Tryby pracy HVAC" + }, + "max_humidity": { + "name": "Maksymalna wilgotno\u015b\u0107 docelowa" + }, + "max_temp": { + "name": "Maksymalna temperatura docelowa" + }, + "min_humidity": { + "name": "Minimalna wilgotno\u015b\u0107 docelowa" + }, + "min_temp": { + "name": "Minimalna temperatura docelowa" + }, + "preset_mode": { + "name": "Ustawienie predefiniowane", + "state": { + "activity": "aktywno\u015b\u0107", + "away": "poza domem", + "boost": "dogrzewanie", + "comfort": "komfortowo", + "eco": "Eco", + "home": "w domu", + "none": "brak", + "sleep": "noc" + } + }, + "preset_modes": { + "name": "Ustawienia predefiniowane" + }, + "swing_mode": { + "name": "Tryb", + "state": { + "both": "oba", + "horizontal": "poziomo", + "off": "wy\u0142.", + "on": "w\u0142.", + "vertical": "pionowo" + } + }, + "swing_modes": { + "name": "Tryby" + }, + "target_temp_high": { + "name": "G\u00f3rna temperatura docelowa" + }, + "target_temp_low": { + "name": "Dolna temperatura docelowa" + }, + "target_temp_step": { + "name": "Krok docelowej temperatury" + }, + "temperature": { + "name": "Temperatura docelowa" + } + } + }, "title": "Klimat" } \ No newline at end of file diff --git a/homeassistant/components/climate/translations/pt-BR.json b/homeassistant/components/climate/translations/pt-BR.json index fc745aaef39..0bec26ebc0b 100644 --- a/homeassistant/components/climate/translations/pt-BR.json +++ b/homeassistant/components/climate/translations/pt-BR.json @@ -25,5 +25,106 @@ "off": "Desligado" } }, + "state_attributes": { + "_": { + "aux_heat": { + "name": "Aquecimento auxiliar" + }, + "current_humidity": { + "name": "Umidade atual" + }, + "current_temperature": { + "name": "Temperatura atual" + }, + "fan_mode": { + "name": "Modo ventila\u00e7\u00e3o", + "state": { + "auto": "Auto", + "diffuse": "Difuso", + "focus": "Foco", + "high": "Alto", + "low": "Baixo", + "medium": "M\u00e9dio", + "middle": "Meio", + "off": "Desligado", + "on": "Ligado", + "top": "Topo" + } + }, + "fan_modes": { + "name": "Modos de ventila\u00e7\u00e3o" + }, + "humidity": { + "name": "Umidade alvo" + }, + "hvac_action": { + "name": "A\u00e7\u00e3o atual", + "state": { + "cooling": "Resfriamento", + "drying": "Secagem", + "fan": "Ventilador", + "heating": "Aquecimento", + "idle": "Ocioso", + "off": "Desligado" + } + }, + "hvac_modes": { + "name": "modos HVAC" + }, + "max_humidity": { + "name": "Umidade alvo m\u00e1xima" + }, + "max_temp": { + "name": "Temperatura alvo m\u00e1xima" + }, + "min_humidity": { + "name": "Umidade alvo m\u00ednima" + }, + "min_temp": { + "name": "Temperatura alvo m\u00ednima" + }, + "preset_mode": { + "name": "Predefinido", + "state": { + "activity": "Atividade", + "away": "Fora", + "boost": "Boost", + "comfort": "Conforto", + "eco": "Eco", + "home": "Casa", + "none": "Nenhum", + "sleep": "Sono" + } + }, + "preset_modes": { + "name": "Predefini\u00e7\u00f5es" + }, + "swing_mode": { + "name": "Modo Swing", + "state": { + "both": "Ambos", + "horizontal": "Horizontal", + "off": "Desligado", + "on": "Ligado", + "vertical": "Vertical" + } + }, + "swing_modes": { + "name": "Modos de oscila\u00e7\u00e3o" + }, + "target_temp_high": { + "name": "Temperatura desejada superior" + }, + "target_temp_low": { + "name": "Temperatura desejada mais baixa" + }, + "target_temp_step": { + "name": "Etapa de temperatura desejada" + }, + "temperature": { + "name": "Temperatura desejada" + } + } + }, "title": "Clima" } \ No newline at end of file diff --git a/homeassistant/components/climate/translations/ru.json b/homeassistant/components/climate/translations/ru.json index 4f8efaa5858..37c630c9a02 100644 --- a/homeassistant/components/climate/translations/ru.json +++ b/homeassistant/components/climate/translations/ru.json @@ -25,5 +25,106 @@ "off": "\u0412\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u043e" } }, + "state_attributes": { + "_": { + "aux_heat": { + "name": "\u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043d\u0430\u0433\u0440\u0435\u0432" + }, + "current_humidity": { + "name": "\u0422\u0435\u043a\u0443\u0449\u0430\u044f \u0432\u043b\u0430\u0436\u043d\u043e\u0441\u0442\u044c" + }, + "current_temperature": { + "name": "\u0422\u0435\u043a\u0443\u0449\u0430\u044f \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430" + }, + "fan_mode": { + "name": "\u0420\u0435\u0436\u0438\u043c \u0432\u0435\u043d\u0442\u0438\u043b\u044f\u0446\u0438\u0438", + "state": { + "auto": "\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438", + "diffuse": "\u0414\u0438\u0444\u0444\u0443\u0437\u043d\u044b\u0439", + "focus": "\u0424\u043e\u043a\u0443\u0441", + "high": "\u0412\u044b\u0441\u043e\u043a\u0438\u0439", + "low": "\u041d\u0438\u0437\u043a\u0438\u0439", + "medium": "\u0421\u0440\u0435\u0434\u043d\u0438\u0439", + "middle": "\u0421\u0440\u0435\u0434\u043d\u0435\u0435", + "off": "\u0412\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u043e", + "on": "\u0412\u043a\u043b\u044e\u0447\u0435\u043d\u043e", + "top": "\u0412\u0432\u0435\u0440\u0445" + } + }, + "fan_modes": { + "name": "\u0420\u0435\u0436\u0438\u043c\u044b \u0440\u0430\u0431\u043e\u0442\u044b \u0432\u0435\u043d\u0442\u0438\u043b\u044f\u0442\u043e\u0440\u0430" + }, + "humidity": { + "name": "\u0426\u0435\u043b\u0435\u0432\u0430\u044f \u0432\u043b\u0430\u0436\u043d\u043e\u0441\u0442\u044c" + }, + "hvac_action": { + "name": "\u0422\u0435\u043a\u0443\u0449\u0435\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435", + "state": { + "cooling": "\u041e\u0445\u043b\u0430\u0436\u0434\u0435\u043d\u0438\u0435", + "drying": "\u041e\u0441\u0443\u0448\u0435\u043d\u0438\u0435", + "fan": "\u0412\u0435\u043d\u0442\u0438\u043b\u044f\u0446\u0438\u044f", + "heating": "\u041e\u0431\u043e\u0433\u0440\u0435\u0432", + "idle": "\u0411\u0435\u0437\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435", + "off": "\u0412\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u043e" + } + }, + "hvac_modes": { + "name": "\u0420\u0435\u0436\u0438\u043c\u044b \u0440\u0430\u0431\u043e\u0442\u044b \u041e\u0412\u0438\u041a" + }, + "max_humidity": { + "name": "\u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u0430\u044f \u0446\u0435\u043b\u0435\u0432\u0430\u044f \u0432\u043b\u0430\u0436\u043d\u043e\u0441\u0442\u044c" + }, + "max_temp": { + "name": "\u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u0430\u044f \u0446\u0435\u043b\u0435\u0432\u0430\u044f \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430" + }, + "min_humidity": { + "name": "\u041c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u0430\u044f \u0446\u0435\u043b\u0435\u0432\u0430\u044f \u0432\u043b\u0430\u0436\u043d\u043e\u0441\u0442\u044c" + }, + "min_temp": { + "name": "\u041c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u0430\u044f \u0446\u0435\u043b\u0435\u0432\u0430\u044f \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430" + }, + "preset_mode": { + "name": "\u041f\u0440\u0435\u0434\u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430", + "state": { + "activity": "\u0410\u043a\u0442\u0438\u0432\u043d\u043e\u0441\u0442\u044c", + "away": "\u041d\u0435 \u0434\u043e\u043c\u0430", + "boost": "\u0422\u0443\u0440\u0431\u043e", + "comfort": "\u041a\u043e\u043c\u0444\u043e\u0440\u0442", + "eco": "\u042d\u043a\u043e", + "home": "\u0414\u043e\u043c\u0430", + "none": "\u041d\u0435\u0442", + "sleep": "\u0421\u043e\u043d" + } + }, + "preset_modes": { + "name": "\u041f\u0440\u0435\u0434\u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438" + }, + "swing_mode": { + "name": "\u0420\u0435\u0436\u0438\u043c \u043a\u0430\u0447\u0430\u043d\u0438\u044f", + "state": { + "both": "\u041e\u0431\u0430", + "horizontal": "\u0413\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u044c\u043d\u043e", + "off": "\u0412\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u043e", + "on": "\u0412\u043a\u043b\u044e\u0447\u0435\u043d\u043e", + "vertical": "\u0412\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u044c\u043d\u043e" + } + }, + "swing_modes": { + "name": "\u0420\u0435\u0436\u0438\u043c\u044b \u043a\u0430\u0447\u0430\u043d\u0438\u044f \u0432\u043e\u0437\u0434\u0443\u0448\u043d\u044b\u0445 \u0448\u0442\u043e\u0440\u043e\u043a" + }, + "target_temp_high": { + "name": "\u0412\u0435\u0440\u0445\u043d\u044f\u044f \u0446\u0435\u043b\u0435\u0432\u0430\u044f \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430" + }, + "target_temp_low": { + "name": "\u041d\u0438\u0436\u043d\u044f\u044f \u0446\u0435\u043b\u0435\u0432\u0430\u044f \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430" + }, + "target_temp_step": { + "name": "\u0428\u0430\u0433 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u0446\u0435\u043b\u0435\u0432\u043e\u0439 \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u044b" + }, + "temperature": { + "name": "\u0426\u0435\u043b\u0435\u0432\u0430\u044f \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430" + } + } + }, "title": "\u041a\u043b\u0438\u043c\u0430\u0442" } \ No newline at end of file diff --git a/homeassistant/components/climate/translations/sk.json b/homeassistant/components/climate/translations/sk.json index 0b964a90799..9b4d3216c11 100644 --- a/homeassistant/components/climate/translations/sk.json +++ b/homeassistant/components/climate/translations/sk.json @@ -1,8 +1,17 @@ { "device_automation": { + "action_type": { + "set_hvac_mode": "Zmeni\u0165 re\u017eim HVAC na {entity_name}", + "set_preset_mode": "Zmeni\u0165 prednastaven\u00fd re\u017eim na {entity_name}" + }, + "condition_type": { + "is_hvac_mode": "{entity_name} je nastaven\u00e9 na ur\u010dit\u00fd re\u017eim HVAC", + "is_preset_mode": "{entity_name} je nastaven\u00e9 na prednastaven\u00fd re\u017eim" + }, "trigger_type": { "current_humidity_changed": "{entity_name} sa zmenila nameran\u00e1 vlhkos\u0165", - "current_temperature_changed": "{entity_name} sa zmenila nameran\u00e1 teplota" + "current_temperature_changed": "{entity_name} sa zmenila nameran\u00e1 teplota", + "hvac_mode_changed": "Re\u017eim HVAC na {entity_name} zmenen\u00fd" } }, "state": { @@ -16,5 +25,106 @@ "off": "Vypnut\u00e9" } }, + "state_attributes": { + "_": { + "aux_heat": { + "name": "Pr\u00eddavn\u00e9 k\u00farenie" + }, + "current_humidity": { + "name": "Aktu\u00e1lna vlhkos\u0165" + }, + "current_temperature": { + "name": "Aktu\u00e1lna teplota" + }, + "fan_mode": { + "name": "Re\u017eim ventil\u00e1tora", + "state": { + "auto": "Auto", + "diffuse": "Dif\u00fazny", + "focus": "Zameran\u00fd", + "high": "Vysok\u00fd", + "low": "Slab\u00fd", + "medium": "Stredn\u00fd", + "middle": "Stredn\u00fd", + "off": "Neakt\u00edvny", + "on": "Akt\u00edvny", + "top": "Hore" + } + }, + "fan_modes": { + "name": "Re\u017eimy ventil\u00e1tora" + }, + "humidity": { + "name": "Cie\u013eov\u00e1 vlhkos\u0165" + }, + "hvac_action": { + "name": "Aktu\u00e1lna akcia", + "state": { + "cooling": "Chladenie", + "drying": "Su\u0161enie", + "fan": "Ventil\u00e1tor", + "heating": "Vykurovanie", + "idle": "Ne\u010dinn\u00fd", + "off": "Vypnut\u00e9" + } + }, + "hvac_modes": { + "name": "Re\u017eimy HVAC" + }, + "max_humidity": { + "name": "Maxim\u00e1lna cie\u013eov\u00e1 vlhkos\u0165" + }, + "max_temp": { + "name": "Maxim\u00e1lna cie\u013eov\u00e1 teplota" + }, + "min_humidity": { + "name": "Minim\u00e1lna cie\u013eov\u00e1 vlhkos\u0165" + }, + "min_temp": { + "name": "Minim\u00e1lna cie\u013eov\u00e1 teplota" + }, + "preset_mode": { + "name": "Predvo\u013eba", + "state": { + "activity": "Aktivita", + "away": "Pre\u010d", + "boost": "Turbo", + "comfort": "Komfort", + "eco": "Eco", + "home": "Doma", + "none": "\u017diadny", + "sleep": "Sp\u00e1nok" + } + }, + "preset_modes": { + "name": "Predvo\u013eby" + }, + "swing_mode": { + "name": "Vej\u00e1rov\u00fd re\u017eim", + "state": { + "both": "Obidva", + "horizontal": "Horizont\u00e1lny", + "off": "Neakt\u00edvny", + "on": "Akt\u00edvny", + "vertical": "Vertik\u00e1lny" + } + }, + "swing_modes": { + "name": "Re\u017eimy vej\u00e1ra" + }, + "target_temp_high": { + "name": "Horn\u00e1 cie\u013eov\u00e1 teplota" + }, + "target_temp_low": { + "name": "Doln\u00e1 cie\u013eov\u00e1 teplota" + }, + "target_temp_step": { + "name": "Cie\u013eov\u00fd teplotn\u00fd krok" + }, + "temperature": { + "name": "Cie\u013eov\u00e1 teplota" + } + } + }, "title": "Klimatiz\u00e1cia" } \ No newline at end of file diff --git a/homeassistant/components/climate/translations/sv.json b/homeassistant/components/climate/translations/sv.json index a5dcd72d66b..8953c4f1a19 100644 --- a/homeassistant/components/climate/translations/sv.json +++ b/homeassistant/components/climate/translations/sv.json @@ -25,5 +25,103 @@ "off": "Av" } }, + "state_attributes": { + "_": { + "current_humidity": { + "name": "Nuvarande luftfuktighet" + }, + "current_temperature": { + "name": "Nuvarande temperatur" + }, + "fan_mode": { + "name": "Fl\u00e4ktl\u00e4ge", + "state": { + "auto": "Automatisk", + "diffuse": "Diffus", + "focus": "Fokus", + "high": "H\u00f6g", + "low": "L\u00e5g", + "medium": "Medium", + "middle": "Mellan", + "off": "Av", + "on": "P\u00e5", + "top": "Topp" + } + }, + "fan_modes": { + "name": "Fl\u00e4ktl\u00e4gen" + }, + "humidity": { + "name": "M\u00e5lluftfuktighet" + }, + "hvac_action": { + "name": "Nuvarande \u00e5tg\u00e4rd", + "state": { + "cooling": "Kyler", + "drying": "Avfuktar", + "fan": "Fl\u00e4kt", + "heating": "V\u00e4rmer", + "idle": "Inaktiv", + "off": "Av" + } + }, + "hvac_modes": { + "name": "VVS l\u00e4gen" + }, + "max_humidity": { + "name": "Max m\u00e5lluftfuktighet" + }, + "max_temp": { + "name": "Max m\u00e5ltemperatur" + }, + "min_humidity": { + "name": "Min m\u00e5lluftfuktighet" + }, + "min_temp": { + "name": "Min m\u00e5ltemperatur" + }, + "preset_mode": { + "name": "F\u00f6rvalt l\u00e4ge", + "state": { + "activity": "Aktivitet", + "away": "Borta", + "boost": "\u00d6kad", + "comfort": "Bekv\u00e4mt", + "eco": "Eco", + "home": "Hemma", + "none": "Ingen", + "sleep": "Sovl\u00e4ge" + } + }, + "preset_modes": { + "name": "F\u00f6rvalda l\u00e4gen" + }, + "swing_mode": { + "name": "Sv\u00e4ngl\u00e4ge", + "state": { + "both": "B\u00e5da", + "horizontal": "Horisontell", + "off": "\u00c4v", + "on": "P\u00e5", + "vertical": "Vertikal" + } + }, + "swing_modes": { + "name": "Sv\u00e4ngl\u00e4gen" + }, + "target_temp_high": { + "name": "H\u00f6gsta m\u00e5ltemperatur" + }, + "target_temp_low": { + "name": "L\u00e4gsta m\u00e5ltemperatur" + }, + "target_temp_step": { + "name": "M\u00e5ltemperatursteg" + }, + "temperature": { + "name": "M\u00e5ltemperatur" + } + } + }, "title": "Klimat" } \ No newline at end of file diff --git a/homeassistant/components/climate/translations/zh-Hant.json b/homeassistant/components/climate/translations/zh-Hant.json index e8f43f589ee..e0f21e2a4d4 100644 --- a/homeassistant/components/climate/translations/zh-Hant.json +++ b/homeassistant/components/climate/translations/zh-Hant.json @@ -25,5 +25,106 @@ "off": "\u95dc\u9589" } }, + "state_attributes": { + "_": { + "aux_heat": { + "name": "\u8f14\u52a9\u6696\u6c23" + }, + "current_humidity": { + "name": "\u76ee\u524d\u6fd5\u5ea6" + }, + "current_temperature": { + "name": "\u76ee\u524d\u6eab\u5ea6" + }, + "fan_mode": { + "name": "\u98a8\u901f\u6a21\u5f0f", + "state": { + "auto": "\u81ea\u52d5", + "diffuse": "\u767c\u6563", + "focus": "\u5c08\u6ce8", + "high": "\u9ad8", + "low": "\u4f4e", + "medium": "\u4e2d", + "middle": "\u4e2d", + "off": "\u95dc\u9589", + "on": "\u958b\u555f", + "top": "\u9ad8" + } + }, + "fan_modes": { + "name": "\u98a8\u901f\u6a21\u5f0f" + }, + "humidity": { + "name": "\u8a2d\u5b9a\u6fd5\u5ea6" + }, + "hvac_action": { + "name": "\u76ee\u524d\u52d5\u4f5c", + "state": { + "cooling": "\u51b7\u6c23", + "drying": "\u9664\u6fd5", + "fan": "\u9001\u98a8", + "heating": "\u6696\u6c23", + "idle": "\u9592\u7f6e", + "off": "\u95dc\u9589" + } + }, + "hvac_modes": { + "name": "HVAC \u6a21\u5f0f" + }, + "max_humidity": { + "name": "\u6700\u9ad8\u8a2d\u5b9a\u6fd5\u5ea6" + }, + "max_temp": { + "name": "\u6700\u9ad8\u8a2d\u5b9a\u6eab\u5ea6" + }, + "min_humidity": { + "name": "\u6700\u4f4e\u8a2d\u5b9a\u6fd5\u5ea6" + }, + "min_temp": { + "name": "\u6700\u4f4e\u8a2d\u5b9a\u6eab\u5ea6" + }, + "preset_mode": { + "name": "\u9810\u7f6e", + "state": { + "activity": "\u6d3b\u52d5\u6a21\u5f0f", + "away": "\u96e2\u5bb6\u6a21\u5f0f", + "boost": "\u5168\u901f\u6a21\u5f0f", + "comfort": "\u8212\u9069\u6a21\u5f0f", + "eco": "\u7bc0\u80fd\u6a21\u5f0f", + "home": "\u5728\u5bb6\u6a21\u5f0f", + "none": "\u7121", + "sleep": "\u7761\u7720\u6a21\u5f0f" + } + }, + "preset_modes": { + "name": "\u9810\u7f6e" + }, + "swing_mode": { + "name": "\u64fa\u52d5\u6a21\u5f0f", + "state": { + "both": "\u5168\u90e8", + "horizontal": "\u6c34\u5e73\u64fa\u52d5", + "off": "\u95dc\u9589", + "on": "\u958b\u555f", + "vertical": "\u5782\u76f4\u64fa\u52d5" + } + }, + "swing_modes": { + "name": "\u64fa\u52d5\u6a21\u5f0f" + }, + "target_temp_high": { + "name": "\u8f03\u9ad8\u8a2d\u5b9a\u6eab\u5ea6" + }, + "target_temp_low": { + "name": "\u8f03\u4f4e\u8a2d\u5b9a\u6eab\u5ea6" + }, + "target_temp_step": { + "name": "\u8a2d\u5b9a\u6eab\u5ea6\u6b65\u9a5f" + }, + "temperature": { + "name": "\u8a2d\u5b9a\u6eab\u5ea6" + } + } + }, "title": "\u6eab\u63a7" } \ No newline at end of file diff --git a/homeassistant/components/cloud/__init__.py b/homeassistant/components/cloud/__init__.py index c5918dcf28f..e9b852ada8d 100644 --- a/homeassistant/components/cloud/__init__.py +++ b/homeassistant/components/cloud/__init__.py @@ -97,7 +97,6 @@ GACTIONS_SCHEMA = ASSISTANT_SCHEMA.extend( {vol.Optional(CONF_ENTITY_CONFIG): {cv.entity_id: GOOGLE_ENTITY_SCHEMA}} ) -# pylint: disable=no-value-for-parameter CONFIG_SCHEMA = vol.Schema( { DOMAIN: vol.Schema( diff --git a/homeassistant/components/cloud/alexa_config.py b/homeassistant/components/cloud/alexa_config.py index 1e59c9a6512..8f9c1775721 100644 --- a/homeassistant/components/cloud/alexa_config.py +++ b/homeassistant/components/cloud/alexa_config.py @@ -178,9 +178,11 @@ class CloudAlexaConfig(alexa_config.AbstractConfig): if self.should_report_state: persistent_notification.async_create( self.hass, - f"There was an error reporting state to Alexa ({body['reason']}). " - "Please re-link your Alexa skill via the Alexa app to " - "continue using it.", + ( + "There was an error reporting state to Alexa" + f" ({body['reason']}). Please re-link your Alexa skill via" + " the Alexa app to continue using it." + ), "Alexa state reporting disabled", "cloud_alexa_report", ) diff --git a/homeassistant/components/cloud/client.py b/homeassistant/components/cloud/client.py index 04b9a9aab97..08d43644249 100644 --- a/homeassistant/components/cloud/client.py +++ b/homeassistant/components/cloud/client.py @@ -142,7 +142,10 @@ class CloudClient(Interface): except aiohttp.ClientError as err: # If no internet available yet if self._hass.is_running: logging.getLogger(__package__).warning( - "Unable to activate Alexa Report State: %s. Retrying in 30 seconds", + ( + "Unable to activate Alexa Report State: %s. Retrying in 30" + " seconds" + ), err, ) async_call_later(self._hass, 30, enable_alexa) diff --git a/homeassistant/components/cloud/http_api.py b/homeassistant/components/cloud/http_api.py index 4345afae746..ea1a0aa27e6 100644 --- a/homeassistant/components/cloud/http_api.py +++ b/homeassistant/components/cloud/http_api.py @@ -386,8 +386,10 @@ async def websocket_update_prefs( connection.send_error( msg["id"], "alexa_relink", - "Please go to the Alexa app and re-link the Home Assistant " - "skill and then try to enable state reporting.", + ( + "Please go to the Alexa app and re-link the Home Assistant " + "skill and then try to enable state reporting." + ), ) await alexa_config.set_authorized(False) return diff --git a/homeassistant/components/cloud/repairs.py b/homeassistant/components/cloud/repairs.py index bf2df23aca9..440254399db 100644 --- a/homeassistant/components/cloud/repairs.py +++ b/homeassistant/components/cloud/repairs.py @@ -106,7 +106,7 @@ class LegacySubscriptionRepairFlow(RepairsFlow): async def async_step_complete(self, _: None = None) -> FlowResult: """Handle the final step of a fix flow.""" - return self.async_create_entry(title="", data={}) + return self.async_create_entry(data={}) async def async_step_timeout(self, _: None = None) -> FlowResult: """Handle the final step of a fix flow.""" diff --git a/homeassistant/components/cloud/stt.py b/homeassistant/components/cloud/stt.py index b1798b2f3be..70618ab38ef 100644 --- a/homeassistant/components/cloud/stt.py +++ b/homeassistant/components/cloud/stt.py @@ -91,7 +91,10 @@ class CloudProvider(Provider): self, metadata: SpeechMetadata, stream: StreamReader ) -> SpeechResult: """Process an audio stream to STT service.""" - content = f"audio/{metadata.format!s}; codecs=audio/{metadata.codec!s}; samplerate=16000" + content = ( + f"audio/{metadata.format!s}; codecs=audio/{metadata.codec!s};" + " samplerate=16000" + ) # Process STT try: diff --git a/homeassistant/components/cloud/subscription.py b/homeassistant/components/cloud/subscription.py index 4c18b4f0253..b85a50b20cd 100644 --- a/homeassistant/components/cloud/subscription.py +++ b/homeassistant/components/cloud/subscription.py @@ -21,7 +21,10 @@ async def async_subscription_info(cloud: Cloud) -> dict[str, Any] | None: return await cloud_api.async_subscription_info(cloud) except asyncio.TimeoutError: _LOGGER.error( - "A timeout of %s was reached while trying to fetch subscription information", + ( + "A timeout of %s was reached while trying to fetch subscription" + " information" + ), REQUEST_TIMEOUT, ) except ClientError: diff --git a/homeassistant/components/cloud/translations/ca.json b/homeassistant/components/cloud/translations/ca.json index c5fec79a89d..60c1f2a6ad0 100644 --- a/homeassistant/components/cloud/translations/ca.json +++ b/homeassistant/components/cloud/translations/ca.json @@ -1,4 +1,19 @@ { + "issues": { + "legacy_subscription": { + "fix_flow": { + "abort": { + "operation_took_too_long": "L'operaci\u00f3 ha trigat massa. Torna-ho a intentar m\u00e9s tard." + }, + "step": { + "confirm_change_plan": { + "description": "Hem actualitzat el nostre sistema de subscripci\u00f3 recentment. Per continuar utilitzant Home Assistant Cloud, has d'aprovar un \u00fanic canvi a PayPal. \n\nNecessitar\u00e0s 1 minut. Aix\u00f2 NO augmentar\u00e0 el preu." + } + } + }, + "title": "S'ha detectat una subscripci\u00f3 heretada" + } + }, "system_health": { "info": { "alexa_enabled": "Alexa activada", diff --git a/homeassistant/components/cloud/translations/de.json b/homeassistant/components/cloud/translations/de.json index 4528ab9bd67..1ba0637a85f 100644 --- a/homeassistant/components/cloud/translations/de.json +++ b/homeassistant/components/cloud/translations/de.json @@ -22,7 +22,7 @@ "can_reach_cloud_auth": "Authentifizierungsserver erreichbar", "google_enabled": "Google aktiviert", "logged_in": "Angemeldet", - "relayer_connected": "Relay verbunden", + "relayer_connected": "Relais verbunden", "remote_connected": "Remote verbunden", "remote_enabled": "Remote aktiviert", "remote_server": "Remote-Server", diff --git a/homeassistant/components/cloud/translations/el.json b/homeassistant/components/cloud/translations/el.json index 75408e05a55..5acc3448f15 100644 --- a/homeassistant/components/cloud/translations/el.json +++ b/homeassistant/components/cloud/translations/el.json @@ -1,4 +1,19 @@ { + "issues": { + "legacy_subscription": { + "fix_flow": { + "abort": { + "operation_took_too_long": "\u0397 \u03b5\u03c0\u03ad\u03bc\u03b2\u03b1\u03c3\u03b7 \u03ba\u03c1\u03ac\u03c4\u03b7\u03c3\u03b5 \u03c0\u03ac\u03c1\u03b1 \u03c0\u03bf\u03bb\u03cd. \u03a0\u03b1\u03c1\u03b1\u03ba\u03b1\u03bb\u03ce \u03c0\u03c1\u03bf\u03c3\u03c0\u03b1\u03b8\u03ae\u03c3\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac \u03b1\u03c1\u03b3\u03cc\u03c4\u03b5\u03c1\u03b1." + }, + "step": { + "confirm_change_plan": { + "description": "\u03a0\u03c1\u03cc\u03c3\u03c6\u03b1\u03c4\u03b1 \u03b5\u03bd\u03b7\u03bc\u03b5\u03c1\u03ce\u03c3\u03b1\u03bc\u03b5 \u03c4\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 \u03c3\u03c5\u03bd\u03b4\u03c1\u03bf\u03bc\u03ce\u03bd \u03bc\u03b1\u03c2. \u0393\u03b9\u03b1 \u03bd\u03b1 \u03c3\u03c5\u03bd\u03b5\u03c7\u03af\u03c3\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b5 \u03c4\u03bf Home Assistant Cloud \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b5\u03b3\u03ba\u03c1\u03af\u03bd\u03b5\u03c4\u03b5 \u03b5\u03c6\u03ac\u03c0\u03b1\u03be \u03c4\u03b7\u03bd \u03b1\u03bb\u03bb\u03b1\u03b3\u03ae \u03c3\u03c4\u03bf PayPal.\n\n\u0391\u03c5\u03c4\u03cc \u03b4\u03b9\u03b1\u03c1\u03ba\u03b5\u03af 1 \u03bb\u03b5\u03c0\u03c4\u03cc \u03ba\u03b1\u03b9 \u03b4\u03b5\u03bd \u03b8\u03b1 \u03b1\u03c5\u03be\u03ae\u03c3\u03b5\u03b9 \u03c4\u03b7\u03bd \u03c4\u03b9\u03bc\u03ae." + } + } + }, + "title": "\u0391\u03bd\u03b9\u03c7\u03bd\u03b5\u03cd\u03b8\u03b7\u03ba\u03b5 \u03c3\u03c5\u03bd\u03b4\u03c1\u03bf\u03bc\u03ae \u03c0\u03b1\u03bb\u03b1\u03b9\u03bf\u03cd \u03c4\u03cd\u03c0\u03bf\u03c5" + } + }, "system_health": { "info": { "alexa_enabled": "Alexa \u0395\u03bd\u03b5\u03c1\u03b3\u03ae", diff --git a/homeassistant/components/cloud/translations/hu.json b/homeassistant/components/cloud/translations/hu.json index 3ecfa262ed5..83d1e4915d1 100644 --- a/homeassistant/components/cloud/translations/hu.json +++ b/homeassistant/components/cloud/translations/hu.json @@ -1,4 +1,19 @@ { + "issues": { + "legacy_subscription": { + "fix_flow": { + "abort": { + "operation_took_too_long": "A m\u0171velet t\u00fal sok\u00e1ig tartott. K\u00e9rj\u00fck, pr\u00f3b\u00e1lja meg k\u00e9s\u0151bb \u00fajra." + }, + "step": { + "confirm_change_plan": { + "description": "Nemr\u00e9g friss\u00edtett\u00fck el\u0151fizet\u00e9si rendszer\u00fcnket. A Home Assistant Cloud tov\u00e1bbi haszn\u00e1lat\u00e1hoz egyszeri alkalommal j\u00f3v\u00e1 kell hagynia a v\u00e1ltoz\u00e1st a PayPal rendszerben.\n\nEz 1 percet vesz ig\u00e9nybe, \u00e9s nem n\u00f6veli az \u00e1rat." + } + } + }, + "title": "R\u00e9gi t\u00edpus\u00fa el\u0151fizet\u00e9s \u00e9szlelve" + } + }, "system_health": { "info": { "alexa_enabled": "Alexa enged\u00e9lyezve", diff --git a/homeassistant/components/cloud/translations/it.json b/homeassistant/components/cloud/translations/it.json index 5c8976d63a3..e2f8de1b2e0 100644 --- a/homeassistant/components/cloud/translations/it.json +++ b/homeassistant/components/cloud/translations/it.json @@ -1,4 +1,19 @@ { + "issues": { + "legacy_subscription": { + "fix_flow": { + "abort": { + "operation_took_too_long": "L'operazione ha richiesto troppo tempo. Riprova pi\u00f9 tardi." + }, + "step": { + "confirm_change_plan": { + "description": "Abbiamo recentemente aggiornato il nostro sistema di abbonamento. Per continuare a utilizzare Home Assistant Cloud \u00e8 necessario approvare una volta la modifica in PayPal. \n\nQuesto richieder\u00e0 1 minuto e non aumenter\u00e0 il prezzo." + } + } + }, + "title": "Abbonamento precedente rilevato" + } + }, "system_health": { "info": { "alexa_enabled": "Alexa abilitato", diff --git a/homeassistant/components/cloud/translations/ko.json b/homeassistant/components/cloud/translations/ko.json index 269afab2ce9..b931e5970bb 100644 --- a/homeassistant/components/cloud/translations/ko.json +++ b/homeassistant/components/cloud/translations/ko.json @@ -1,4 +1,19 @@ { + "issues": { + "legacy_subscription": { + "fix_flow": { + "abort": { + "operation_took_too_long": "\uc791\uc5c5\uc774 \ub108\ubb34 \uc624\ub798 \uac78\ub9bd\ub2c8\ub2e4. \ub098\uc911\uc5d0 \ub2e4\uc2dc \uc2dc\ub3c4 \ud574\uc8fc\uc2ed\uc2dc\uc624." + }, + "step": { + "confirm_change_plan": { + "description": "\ucd5c\uadfc \uad6c\ub3c5 \uc2dc\uc2a4\ud15c\uc744 \uc5c5\ub370\uc774\ud2b8\ud588\uc2b5\ub2c8\ub2e4. Home Assistant \ud074\ub77c\uc6b0\ub4dc\ub97c \uacc4\uc18d \uc0ac\uc6a9\ud558\ub824\uba74 PayPal\uc5d0\uc11c \ubcc0\uacbd \uc0ac\ud56d\uc744 \ud55c \ubc88 \uc2b9\uc778\ud574\uc57c \ud569\ub2c8\ub2e4. \n\n 1\ubd84\uc774 \uc18c\uc694\ub418\uba70 \uac00\uaca9\uc774 \uc778\uc0c1\ub418\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4." + } + } + }, + "title": "\uae30\uc874 \uad6c\ub3c5\uc2dc\uc2a4\ud15c \uac10\uc9c0\ub428" + } + }, "system_health": { "info": { "alexa_enabled": "Alexa \ud65c\uc131\ud654", diff --git a/homeassistant/components/cloud/translations/pl.json b/homeassistant/components/cloud/translations/pl.json index d8fafb78b90..e8533e61390 100644 --- a/homeassistant/components/cloud/translations/pl.json +++ b/homeassistant/components/cloud/translations/pl.json @@ -1,4 +1,19 @@ { + "issues": { + "legacy_subscription": { + "fix_flow": { + "abort": { + "operation_took_too_long": "Operacja trwa\u0142a zbyt d\u0142ugo. Spr\u00f3buj ponownie p\u00f3\u017aniej." + }, + "step": { + "confirm_change_plan": { + "description": "Niedawno zaktualizowali\u015bmy nasz system subskrypcji. Aby nadal korzysta\u0107 z Home Assistant Cloud, musisz jednorazowo zatwierdzi\u0107 zmian\u0119 w systemie PayPal. \n\nZajmie to 1 minut\u0119 i nie zwi\u0119kszy ceny." + } + } + }, + "title": "Wykryto starsz\u0105 subskrypcj\u0119" + } + }, "system_health": { "info": { "alexa_enabled": "Alexa w\u0142\u0105czona", diff --git a/homeassistant/components/cloud/translations/sk.json b/homeassistant/components/cloud/translations/sk.json index f96b0cacfc1..3b0fa8f6c70 100644 --- a/homeassistant/components/cloud/translations/sk.json +++ b/homeassistant/components/cloud/translations/sk.json @@ -1,11 +1,32 @@ { + "issues": { + "legacy_subscription": { + "fix_flow": { + "abort": { + "operation_took_too_long": "Oper\u00e1cia trvala pr\u00edli\u0161 dlho. Sk\u00faste nesk\u00f4r pros\u00edm." + }, + "step": { + "confirm_change_plan": { + "description": "Ned\u00e1vno sme aktualizovali n\u00e1\u0161 syst\u00e9m predplatn\u00e9ho. Ak chcete pokra\u010dova\u0165 v pou\u017e\u00edvan\u00ed Home Assistant Cloud, mus\u00edte jednorazovo schv\u00e1li\u0165 zmenu v slu\u017ebe PayPal. \n\n Trv\u00e1 to 1 min\u00fatu a nezv\u00fd\u0161i to cenu." + } + } + }, + "title": "Zisten\u00e9 star\u0161ie predplatn\u00e9" + } + }, "system_health": { "info": { "alexa_enabled": "Alexa povolen\u00e1", "can_reach_cert_server": "Dosiahnutie servera certifik\u00e1tov", + "can_reach_cloud": "Home Assistant Cloud dosiahnut\u00fd", + "can_reach_cloud_auth": "Overovac\u00ed server dosiahnut\u00fd", "google_enabled": "Google povolen\u00e9", + "logged_in": "Prihl\u00e1sen\u00fd", + "relayer_connected": "Relayer pripojen\u00fd", "remote_connected": "Vzdialene pripojen\u00e9", - "remote_enabled": "Povolen\u00e9 vzdialen\u00e9 ovl\u00e1danie" + "remote_enabled": "Povolen\u00e9 vzdialen\u00e9 ovl\u00e1danie", + "remote_server": "Vzdialen\u00fd server", + "subscription_expiration": "Platnos\u0165 predplatn\u00e9ho" } } } \ No newline at end of file diff --git a/homeassistant/components/cloudflare/__init__.py b/homeassistant/components/cloudflare/__init__.py index 3a8f6b39ae7..9608347c8e7 100644 --- a/homeassistant/components/cloudflare/__init__.py +++ b/homeassistant/components/cloudflare/__init__.py @@ -10,6 +10,7 @@ from pycfdns.exceptions import ( CloudflareAuthenticationException, CloudflareConnectionException, CloudflareException, + CloudflareZoneException, ) from homeassistant.config_entries import ConfigEntry @@ -47,7 +48,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: zone_id = await cfupdate.get_zone_id() except CloudflareAuthenticationException as error: raise ConfigEntryAuthFailed from error - except CloudflareConnectionException as error: + except (CloudflareConnectionException, CloudflareZoneException) as error: raise ConfigEntryNotReady from error async def update_records(now): diff --git a/homeassistant/components/cloudflare/translations/ko.json b/homeassistant/components/cloudflare/translations/ko.json index 4dbe263a138..1fcd933ec38 100644 --- a/homeassistant/components/cloudflare/translations/ko.json +++ b/homeassistant/components/cloudflare/translations/ko.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4", "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.", "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" }, @@ -11,6 +12,11 @@ }, "flow_title": "Cloudflare: {name}", "step": { + "reauth_confirm": { + "data": { + "api_token": "API \ud1a0\ud070" + } + }, "records": { "data": { "records": "\ub808\ucf54\ub4dc" diff --git a/homeassistant/components/cloudflare/translations/pt.json b/homeassistant/components/cloudflare/translations/pt.json index dd836339b2b..e2c36fdd4ce 100644 --- a/homeassistant/components/cloudflare/translations/pt.json +++ b/homeassistant/components/cloudflare/translations/pt.json @@ -6,7 +6,7 @@ "unknown": "Erro inesperado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida" }, "step": { diff --git a/homeassistant/components/co2signal/translations/de.json b/homeassistant/components/co2signal/translations/de.json index f88316ba6fc..df3cb413e27 100644 --- a/homeassistant/components/co2signal/translations/de.json +++ b/homeassistant/components/co2signal/translations/de.json @@ -6,7 +6,7 @@ "unknown": "Unerwarteter Fehler" }, "error": { - "api_ratelimit": "API Ratelimit \u00fcberschritten", + "api_ratelimit": "API Ratenlimit \u00fcberschritten", "invalid_auth": "Ung\u00fcltige Authentifizierung", "unknown": "Unerwarteter Fehler" }, diff --git a/homeassistant/components/co2signal/translations/he.json b/homeassistant/components/co2signal/translations/he.json index 9ff327c584b..a5128fef4f6 100644 --- a/homeassistant/components/co2signal/translations/he.json +++ b/homeassistant/components/co2signal/translations/he.json @@ -2,9 +2,11 @@ "config": { "abort": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", + "api_ratelimit": "\u05d7\u05e8\u05d2\u05ea \u05de\u05de\u05d2\u05d1\u05dc\u05ea \u05d4\u05ea\u05e2\u05e8\u05d9\u05e3 \u05e9\u05dc API", "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" }, "error": { + "api_ratelimit": "\u05d7\u05e8\u05d2\u05ea \u05de\u05de\u05d2\u05d1\u05dc\u05ea \u05d4\u05ea\u05e2\u05e8\u05d9\u05e3 \u05e9\u05dc API", "invalid_auth": "\u05d0\u05d9\u05de\u05d5\u05ea \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9", "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" }, @@ -22,8 +24,10 @@ }, "user": { "data": { - "api_key": "\u05d0\u05e1\u05d9\u05de\u05d5\u05df \u05d2\u05d9\u05e9\u05d4" - } + "api_key": "\u05d0\u05e1\u05d9\u05de\u05d5\u05df \u05d2\u05d9\u05e9\u05d4", + "location": "\u05e7\u05d1\u05dc\u05ea \u05e0\u05ea\u05d5\u05e0\u05d9\u05dd \u05e2\u05d1\u05d5\u05e8" + }, + "description": "\u05d9\u05e9 \u05dc\u05d1\u05e7\u05e8 https://co2signal.com/ \u05db\u05d3\u05d9 \u05dc\u05d1\u05e7\u05e9 \u05d0\u05e1\u05d9\u05de\u05d5\u05df." } } } diff --git a/homeassistant/components/co2signal/translations/ko.json b/homeassistant/components/co2signal/translations/ko.json new file mode 100644 index 00000000000..28bdb2945d0 --- /dev/null +++ b/homeassistant/components/co2signal/translations/ko.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "coordinates": { + "data": { + "latitude": "\uc704\ub3c4", + "longitude": "\uacbd\ub3c4" + } + }, + "user": { + "data": { + "api_key": "\uc561\uc138\uc2a4 \ud1a0\ud070" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/coinbase/sensor.py b/homeassistant/components/coinbase/sensor.py index e264fed0215..abd113e71ad 100644 --- a/homeassistant/components/coinbase/sensor.py +++ b/homeassistant/components/coinbase/sensor.py @@ -75,8 +75,10 @@ async def async_setup_entry( for currency in desired_currencies: if currency not in provided_currencies: _LOGGER.warning( - "The currency %s is no longer provided by your account, please check " - "your settings in Coinbase's developer tools", + ( + "The currency %s is no longer provided by your account, please" + " check your settings in Coinbase's developer tools" + ), currency, ) continue diff --git a/homeassistant/components/coinbase/translations/ko.json b/homeassistant/components/coinbase/translations/ko.json new file mode 100644 index 00000000000..750aba71432 --- /dev/null +++ b/homeassistant/components/coinbase/translations/ko.json @@ -0,0 +1,24 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "api_key": "API \ud0a4" + } + } + } + }, + "options": { + "error": { + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/coinbase/translations/pt.json b/homeassistant/components/coinbase/translations/pt.json index dec932ffe10..cf51d6be93e 100644 --- a/homeassistant/components/coinbase/translations/pt.json +++ b/homeassistant/components/coinbase/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, diff --git a/homeassistant/components/coinbase/translations/sk.json b/homeassistant/components/coinbase/translations/sk.json index ec8f33d2d7b..d354ad972be 100644 --- a/homeassistant/components/coinbase/translations/sk.json +++ b/homeassistant/components/coinbase/translations/sk.json @@ -7,20 +7,36 @@ "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie", "invalid_auth_key": "Poverenia API zamietnut\u00e9 spolo\u010dnos\u0165ou Coinbase z d\u00f4vodu neplatn\u00e9ho k\u013e\u00fa\u010da API.", + "invalid_auth_secret": "Prihlasovacie \u00fadaje API odmietnut\u00e9 spolo\u010dnos\u0165ou Coinbase z d\u00f4vodu neplatn\u00e9ho API Secret.", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { "data": { - "api_key": "API k\u013e\u00fa\u010d" - } + "api_key": "API k\u013e\u00fa\u010d", + "api_token": "API Secret" + }, + "description": "Zadajte podrobnosti o va\u0161om k\u013e\u00fa\u010di API, ako ich poskytuje Coinbase.", + "title": "Podrobnosti o k\u013e\u00fa\u010di API Coinbase" } } }, "options": { "error": { "currency_unavailable": "Jeden alebo viacero po\u017eadovan\u00fdch zostatkov mien nie s\u00fa poskytovan\u00e9 Va\u0161\u00edm Coinbase API.", + "exchange_rate_unavailable": "Spolo\u010dnos\u0165 Coinbase neposkytuje jeden alebo viacero po\u017eadovan\u00fdch v\u00fdmenn\u00fdch kurzov.", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "step": { + "init": { + "data": { + "account_balance_currencies": "Zostatky v pe\u0148a\u017eenke na reportovanie.", + "exchange_base": "Z\u00e1kladn\u00e1 mena pre sn\u00edma\u010de zmenov\u00fdch kurzov.", + "exchange_rate_currencies": "V\u00fdmenn\u00e9 kurzy na reportovanie.", + "exchnage_rate_precision": "Po\u010det desatinn\u00fdch miest pre v\u00fdmenn\u00e9 kurzy." + }, + "description": "Upravte mo\u017enosti Coinbase" + } } } } \ No newline at end of file diff --git a/homeassistant/components/color_extractor/__init__.py b/homeassistant/components/color_extractor/__init__.py index b0884643be9..61ec27b3241 100644 --- a/homeassistant/components/color_extractor/__init__.py +++ b/homeassistant/components/color_extractor/__init__.py @@ -104,7 +104,10 @@ async def async_setup(hass: HomeAssistant, hass_config: ConfigType) -> bool: """Handle call for URL based image.""" if not hass.config.is_allowed_external_url(url): _LOGGER.error( - "External URL '%s' is not allowed, please add to 'allowlist_external_urls'", + ( + "External URL '%s' is not allowed, please add to" + " 'allowlist_external_urls'" + ), url, ) return None @@ -134,7 +137,10 @@ async def async_setup(hass: HomeAssistant, hass_config: ConfigType) -> bool: """Handle call for local file based image.""" if not hass.config.is_allowed_path(file_path): _LOGGER.error( - "File path '%s' is not allowed, please add to 'allowlist_external_dirs'", + ( + "File path '%s' is not allowed, please add to" + " 'allowlist_external_dirs'" + ), file_path, ) return None diff --git a/homeassistant/components/comfoconnect/manifest.json b/homeassistant/components/comfoconnect/manifest.json index 907211dbae6..785385ce8d9 100644 --- a/homeassistant/components/comfoconnect/manifest.json +++ b/homeassistant/components/comfoconnect/manifest.json @@ -2,7 +2,7 @@ "domain": "comfoconnect", "name": "Zehnder ComfoAir Q", "documentation": "https://www.home-assistant.io/integrations/comfoconnect", - "requirements": ["pycomfoconnect==0.4"], + "requirements": ["pycomfoconnect==0.5.1"], "codeowners": ["@michaelarnauts"], "iot_class": "local_push", "loggers": ["pycomfoconnect"] diff --git a/homeassistant/components/comfoconnect/sensor.py b/homeassistant/components/comfoconnect/sensor.py index efb6b11a6d9..21e6eda255d 100644 --- a/homeassistant/components/comfoconnect/sensor.py +++ b/homeassistant/components/comfoconnect/sensor.py @@ -38,13 +38,13 @@ from homeassistant.components.sensor import ( ) from homeassistant.const import ( CONF_RESOURCES, - ENERGY_KILO_WATT_HOUR, PERCENTAGE, - POWER_WATT, REVOLUTIONS_PER_MINUTE, - TEMP_CELSIUS, - TIME_DAYS, - VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR, + UnitOfEnergy, + UnitOfPower, + UnitOfTemperature, + UnitOfTime, + UnitOfVolumeFlowRate, ) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv @@ -101,7 +101,7 @@ SENSOR_TYPES = ( device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, name="Inside temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, sensor_id=SENSOR_TEMPERATURE_EXTRACT, multiplier=0.1, ), @@ -118,7 +118,7 @@ SENSOR_TYPES = ( device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, name="Current RMOT", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, sensor_id=SENSOR_CURRENT_RMOT, multiplier=0.1, ), @@ -127,7 +127,7 @@ SENSOR_TYPES = ( device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, name="Outside temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, sensor_id=SENSOR_TEMPERATURE_OUTDOOR, multiplier=0.1, ), @@ -144,7 +144,7 @@ SENSOR_TYPES = ( device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, name="Supply temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, sensor_id=SENSOR_TEMPERATURE_SUPPLY, multiplier=0.1, ), @@ -193,7 +193,7 @@ SENSOR_TYPES = ( device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, name="Exhaust temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, sensor_id=SENSOR_TEMPERATURE_EXHAUST, multiplier=0.1, ), @@ -209,7 +209,7 @@ SENSOR_TYPES = ( key=ATTR_AIR_FLOW_SUPPLY, state_class=SensorStateClass.MEASUREMENT, name="Supply airflow", - native_unit_of_measurement=VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR, + native_unit_of_measurement=UnitOfVolumeFlowRate.CUBIC_METERS_PER_HOUR, icon="mdi:fan-plus", sensor_id=SENSOR_FAN_SUPPLY_FLOW, ), @@ -217,7 +217,7 @@ SENSOR_TYPES = ( key=ATTR_AIR_FLOW_EXHAUST, state_class=SensorStateClass.MEASUREMENT, name="Exhaust airflow", - native_unit_of_measurement=VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR, + native_unit_of_measurement=UnitOfVolumeFlowRate.CUBIC_METERS_PER_HOUR, icon="mdi:fan-minus", sensor_id=SENSOR_FAN_EXHAUST_FLOW, ), @@ -232,7 +232,7 @@ SENSOR_TYPES = ( ComfoconnectSensorEntityDescription( key=ATTR_DAYS_TO_REPLACE_FILTER, name="Days to replace filter", - native_unit_of_measurement=TIME_DAYS, + native_unit_of_measurement=UnitOfTime.DAYS, icon="mdi:calendar", sensor_id=SENSOR_DAYS_TO_REPLACE_FILTER, ), @@ -241,7 +241,7 @@ SENSOR_TYPES = ( device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, name="Power usage", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, sensor_id=SENSOR_POWER_CURRENT, ), ComfoconnectSensorEntityDescription( @@ -249,7 +249,7 @@ SENSOR_TYPES = ( device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, name="Energy total", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, sensor_id=SENSOR_POWER_TOTAL, ), ComfoconnectSensorEntityDescription( @@ -257,7 +257,7 @@ SENSOR_TYPES = ( device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, name="Preheater power usage", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, sensor_id=SENSOR_PREHEATER_POWER_CURRENT, ), ComfoconnectSensorEntityDescription( @@ -265,7 +265,7 @@ SENSOR_TYPES = ( device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, name="Preheater energy total", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, sensor_id=SENSOR_PREHEATER_POWER_TOTAL, ), ) diff --git a/homeassistant/components/compensation/__init__.py b/homeassistant/components/compensation/__init__.py index 9fb5b8e9b2b..e36737c7d35 100644 --- a/homeassistant/components/compensation/__init__.py +++ b/homeassistant/components/compensation/__init__.py @@ -35,7 +35,8 @@ def datapoints_greater_than_degree(value: dict) -> dict: """Validate data point list is greater than polynomial degrees.""" if len(value[CONF_DATAPOINTS]) <= value[CONF_DEGREE]: raise vol.Invalid( - f"{CONF_DATAPOINTS} must have at least {value[CONF_DEGREE]+1} {CONF_DATAPOINTS}" + f"{CONF_DATAPOINTS} must have at least" + f" {value[CONF_DEGREE]+1} {CONF_DATAPOINTS}" ) return value diff --git a/homeassistant/components/config/__init__.py b/homeassistant/components/config/__init__.py index 329134e5486..def7edd4950 100644 --- a/homeassistant/components/config/__init__.py +++ b/homeassistant/components/config/__init__.py @@ -130,7 +130,7 @@ class BaseEditConfigView(HomeAssistantView): # We just validate, we don't store that data because # we don't want to store the defaults. if self.data_validator: - await self.data_validator(hass, data) + await self.data_validator(hass, config_key, data) else: self.data_schema(data) except (vol.Invalid, HomeAssistantError) as err: diff --git a/homeassistant/components/config/area_registry.py b/homeassistant/components/config/area_registry.py index bf63a516b58..d41c712dffb 100644 --- a/homeassistant/components/config/area_registry.py +++ b/homeassistant/components/config/area_registry.py @@ -35,6 +35,7 @@ def websocket_list_areas( @websocket_api.websocket_command( { vol.Required("type"): "config/area_registry/create", + vol.Optional("aliases"): list, vol.Required("name"): str, vol.Optional("picture"): vol.Any(str, None), } @@ -53,6 +54,10 @@ def websocket_create_area( data.pop("type") data.pop("id") + if "aliases" in data: + # Convert aliases to a set + data["aliases"] = set(data["aliases"]) + try: entry = registry.async_create(**data) except ValueError as err: @@ -88,6 +93,7 @@ def websocket_delete_area( @websocket_api.websocket_command( { vol.Required("type"): "config/area_registry/update", + vol.Optional("aliases"): list, vol.Required("area_id"): str, vol.Optional("name"): str, vol.Optional("picture"): vol.Any(str, None), @@ -107,6 +113,10 @@ def websocket_update_area( data.pop("type") data.pop("id") + if "aliases" in data: + # Convert aliases to a set + data["aliases"] = set(data["aliases"]) + try: entry = registry.async_update(**data) except ValueError as err: @@ -118,4 +128,9 @@ def websocket_update_area( @callback def _entry_dict(entry): """Convert entry to API format.""" - return {"area_id": entry.id, "name": entry.name, "picture": entry.picture} + return { + "aliases": entry.aliases, + "area_id": entry.id, + "name": entry.name, + "picture": entry.picture, + } diff --git a/homeassistant/components/config/device_registry.py b/homeassistant/components/config/device_registry.py index 6287d586343..42d2386977f 100644 --- a/homeassistant/components/config/device_registry.py +++ b/homeassistant/components/config/device_registry.py @@ -21,24 +21,6 @@ from homeassistant.helpers.device_registry import ( async_get, ) -WS_TYPE_LIST = "config/device_registry/list" -SCHEMA_WS_LIST = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend( - {vol.Required("type"): WS_TYPE_LIST} -) - -WS_TYPE_UPDATE = "config/device_registry/update" -SCHEMA_WS_UPDATE = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend( - { - vol.Required("type"): WS_TYPE_UPDATE, - vol.Required("device_id"): str, - vol.Optional("area_id"): vol.Any(str, None), - vol.Optional("name_by_user"): vol.Any(str, None), - # We only allow setting disabled_by user via API. - # No Enum support like this in voluptuous, use .value - vol.Optional("disabled_by"): vol.Any(DeviceEntryDisabler.USER.value, None), - } -) - async def async_setup(hass): """Enable the Device Registry views.""" @@ -51,14 +33,23 @@ async def async_setup(hass): cached_list_devices = None @callback - def websocket_list_devices(hass, connection, msg): + @websocket_api.websocket_command( + { + vol.Required("type"): "config/device_registry/list", + } + ) + def websocket_list_devices( + hass: HomeAssistant, + connection: websocket_api.ActiveConnection, + msg: dict[str, Any], + ) -> None: """Handle list devices command.""" nonlocal cached_list_devices if not cached_list_devices: registry = async_get(hass) cached_list_devices = message_to_json( websocket_api.result_message( - IDEN_TEMPLATE, + IDEN_TEMPLATE, # type: ignore[arg-type] [_entry_dict(entry) for entry in registry.devices.values()], ) ) @@ -72,12 +63,8 @@ async def async_setup(hass): run_immediately=True, ) - websocket_api.async_register_command( - hass, WS_TYPE_LIST, websocket_list_devices, SCHEMA_WS_LIST - ) - websocket_api.async_register_command( - hass, WS_TYPE_UPDATE, websocket_update_device, SCHEMA_WS_UPDATE - ) + websocket_api.async_register_command(hass, websocket_list_devices) + websocket_api.async_register_command(hass, websocket_update_device) websocket_api.async_register_command( hass, websocket_remove_config_entry_from_device ) @@ -85,9 +72,24 @@ async def async_setup(hass): @require_admin +@websocket_api.websocket_command( + { + vol.Required("type"): "config/device_registry/update", + vol.Optional("area_id"): vol.Any(str, None), + vol.Required("device_id"): str, + # We only allow setting disabled_by user via API. + # No Enum support like this in voluptuous, use .value + vol.Optional("disabled_by"): vol.Any(DeviceEntryDisabler.USER.value, None), + vol.Optional("name_by_user"): vol.Any(str, None), + } +) @callback -def websocket_update_device(hass, connection, msg): - """Handle update area websocket command.""" +def websocket_update_device( + hass: HomeAssistant, + connection: websocket_api.ActiveConnection, + msg: dict[str, Any], +) -> None: + """Handle update device websocket command.""" registry = async_get(hass) msg.pop("type") @@ -105,8 +107,8 @@ def websocket_update_device(hass, connection, msg): @websocket_api.websocket_command( { "type": "config/device_registry/remove_config_entry", - "device_id": str, "config_entry_id": str, + "device_id": str, } ) @websocket_api.async_response diff --git a/homeassistant/components/config/entity_registry.py b/homeassistant/components/config/entity_registry.py index b4bd7403c43..dffb44c8153 100644 --- a/homeassistant/components/config/entity_registry.py +++ b/homeassistant/components/config/entity_registry.py @@ -102,6 +102,7 @@ def websocket_get_entity( vol.Required("type"): "config/entity_registry/update", vol.Required("entity_id"): cv.entity_id, # If passed in, we update value. Passing None will remove old value. + vol.Optional("aliases"): list, vol.Optional("area_id"): vol.Any(str, None), vol.Optional("device_class"): vol.Any(str, None), vol.Optional("icon"): vol.Any(str, None), @@ -160,6 +161,10 @@ def websocket_update_entity( if key in msg: changes[key] = msg[key] + if "aliases" in msg: + # Convert aliases to a set + changes["aliases"] = set(msg["aliases"]) + if "disabled_by" in msg and msg["disabled_by"] is None: # Don't allow enabling an entity of a disabled device if entity_entry.device_id: @@ -247,16 +252,17 @@ def _entry_dict(entry: er.RegistryEntry) -> dict[str, Any]: "config_entry_id": entry.config_entry_id, "device_id": entry.device_id, "disabled_by": entry.disabled_by, - "has_entity_name": entry.has_entity_name, "entity_category": entry.entity_category, "entity_id": entry.entity_id, + "has_entity_name": entry.has_entity_name, "hidden_by": entry.hidden_by, "icon": entry.icon, "id": entry.id, - "unique_id": entry.unique_id, "name": entry.name, "original_name": entry.original_name, "platform": entry.platform, + "translation_key": entry.translation_key, + "unique_id": entry.unique_id, } @@ -264,6 +270,7 @@ def _entry_dict(entry: er.RegistryEntry) -> dict[str, Any]: def _entry_ext_dict(entry: er.RegistryEntry) -> dict[str, Any]: """Convert entry to API format.""" data = _entry_dict(entry) + data["aliases"] = entry.aliases data["capabilities"] = entry.capabilities data["device_class"] = entry.device_class data["options"] = entry.options diff --git a/homeassistant/components/control4/__init__.py b/homeassistant/components/control4/__init__.py index 4253a4eca02..c99af1f89ce 100644 --- a/homeassistant/components/control4/__init__.py +++ b/homeassistant/components/control4/__init__.py @@ -60,7 +60,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: raise ConfigEntryNotReady from exception except BadCredentials as exception: _LOGGER.error( - "Error authenticating with Control4 account API, incorrect username or password: %s", + ( + "Error authenticating with Control4 account API, incorrect username or" + " password: %s" + ), exception, ) return False diff --git a/homeassistant/components/control4/light.py b/homeassistant/components/control4/light.py index 1f738803c2e..2c92010901b 100644 --- a/homeassistant/components/control4/light.py +++ b/homeassistant/components/control4/light.py @@ -115,7 +115,10 @@ async def async_setup_entry( director = entry_data[CONF_DIRECTOR] item_variables = await director.getItemVariables(item_id) _LOGGER.warning( - "Couldn't get light state data for %s, skipping setup. Available variables from Control4: %s", + ( + "Couldn't get light state data for %s, skipping setup. Available" + " variables from Control4: %s" + ), item_name, item_variables, ) diff --git a/homeassistant/components/control4/translations/pt.json b/homeassistant/components/control4/translations/pt.json index 5f67ee940b1..fdbfd5bc6cb 100644 --- a/homeassistant/components/control4/translations/pt.json +++ b/homeassistant/components/control4/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, diff --git a/homeassistant/components/conversation/__init__.py b/homeassistant/components/conversation/__init__.py index deab740909e..b95b9361624 100644 --- a/homeassistant/components/conversation/__init__.py +++ b/homeassistant/components/conversation/__init__.py @@ -1,7 +1,6 @@ """Support for functionality to have conversations with Home Assistant.""" from __future__ import annotations -from http import HTTPStatus import logging import re from typing import Any @@ -16,12 +15,13 @@ from homeassistant.helpers import config_validation as cv, intent from homeassistant.helpers.typing import ConfigType from homeassistant.loader import bind_hass -from .agent import AbstractConversationAgent +from .agent import AbstractConversationAgent, ConversationResult from .default_agent import DefaultAgent, async_register _LOGGER = logging.getLogger(__name__) ATTR_TEXT = "text" +ATTR_LANGUAGE = "language" DOMAIN = "conversation" @@ -31,7 +31,9 @@ DATA_CONFIG = "conversation_config" SERVICE_PROCESS = "process" -SERVICE_PROCESS_SCHEMA = vol.Schema({vol.Required(ATTR_TEXT): cv.string}) +SERVICE_PROCESS_SCHEMA = vol.Schema( + {vol.Required(ATTR_TEXT): cv.string, vol.Optional(ATTR_LANGUAGE): cv.string} +) CONFIG_SCHEMA = vol.Schema( { @@ -66,7 +68,9 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: _LOGGER.debug("Processing: <%s>", text) agent = await _get_agent(hass) try: - await agent.async_process(text, service.context) + await agent.async_process( + text, service.context, language=service.data.get(ATTR_LANGUAGE) + ) except intent.IntentHandleError as err: _LOGGER.error("Error processing %s: %s", text, err) @@ -82,7 +86,12 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: @websocket_api.websocket_command( - {"type": "conversation/process", "text": str, vol.Optional("conversation_id"): str} + { + "type": "conversation/process", + "text": str, + vol.Optional("conversation_id"): vol.Any(str, None), + vol.Optional("language"): str, + } ) @websocket_api.async_response async def websocket_process( @@ -91,12 +100,14 @@ async def websocket_process( msg: dict[str, Any], ) -> None: """Process text.""" - connection.send_result( - msg["id"], - await _async_converse( - hass, msg["text"], msg.get("conversation_id"), connection.context(msg) - ), + result = await _async_converse( + hass, + msg["text"], + msg.get("conversation_id"), + connection.context(msg), + msg.get("language"), ) + connection.send_result(msg["id"], result.as_dict()) @websocket_api.websocket_command({"type": "conversation/agent/info"}) @@ -143,30 +154,26 @@ class ConversationProcessView(http.HomeAssistantView): name = "api:conversation:process" @RequestDataValidator( - vol.Schema({vol.Required("text"): str, vol.Optional("conversation_id"): str}) + vol.Schema( + { + vol.Required("text"): str, + vol.Optional("conversation_id"): str, + vol.Optional("language"): str, + } + ) ) async def post(self, request, data): """Send a request for processing.""" hass = request.app["hass"] + result = await _async_converse( + hass, + text=data["text"], + conversation_id=data.get("conversation_id"), + context=self.context(request), + language=data.get("language"), + ) - try: - intent_result = await _async_converse( - hass, data["text"], data.get("conversation_id"), self.context(request) - ) - except intent.IntentError as err: - _LOGGER.error("Error handling intent: %s", err) - return self.json( - { - "success": False, - "error": { - "code": str(err.__class__.__name__).lower(), - "message": str(err), - }, - }, - status_code=HTTPStatus.INTERNAL_SERVER_ERROR, - ) - - return self.json(intent_result) + return self.json(result.as_dict()) async def _get_agent(hass: core.HomeAssistant) -> AbstractConversationAgent: @@ -182,17 +189,51 @@ async def _async_converse( text: str, conversation_id: str | None, context: core.Context, -) -> intent.IntentResponse: + language: str | None = None, +) -> ConversationResult: """Process text and get intent.""" agent = await _get_agent(hass) + if language is None: + language = hass.config.language + + result: ConversationResult | None = None + intent_response: intent.IntentResponse | None = None + try: - intent_result = await agent.async_process(text, context, conversation_id) + result = await agent.async_process(text, context, conversation_id, language) except intent.IntentHandleError as err: - intent_result = intent.IntentResponse() - intent_result.async_set_speech(str(err)) + # Match was successful, but target(s) were invalid + intent_response = intent.IntentResponse(language=language) + intent_response.async_set_error( + intent.IntentResponseErrorCode.NO_VALID_TARGETS, + str(err), + ) + except intent.IntentUnexpectedError as err: + # Match was successful, but an error occurred while handling intent + intent_response = intent.IntentResponse(language=language) + intent_response.async_set_error( + intent.IntentResponseErrorCode.FAILED_TO_HANDLE, + str(err), + ) + except intent.IntentError as err: + # Unknown error + intent_response = intent.IntentResponse(language=language) + intent_response.async_set_error( + intent.IntentResponseErrorCode.UNKNOWN, + str(err), + ) - if intent_result is None: - intent_result = intent.IntentResponse() - intent_result.async_set_speech("Sorry, I didn't understand that") + if result is None: + if intent_response is None: + # Match was not successful + intent_response = intent.IntentResponse(language=language) + intent_response.async_set_error( + intent.IntentResponseErrorCode.NO_INTENT_MATCH, + "Sorry, I didn't understand that", + ) - return intent_result + result = ConversationResult( + response=intent_response, conversation_id=conversation_id + ) + + return result diff --git a/homeassistant/components/conversation/agent.py b/homeassistant/components/conversation/agent.py index a19ae6d697b..0bd3f018589 100644 --- a/homeassistant/components/conversation/agent.py +++ b/homeassistant/components/conversation/agent.py @@ -2,11 +2,28 @@ from __future__ import annotations from abc import ABC, abstractmethod +from dataclasses import dataclass +from typing import Any from homeassistant.core import Context from homeassistant.helpers import intent +@dataclass +class ConversationResult: + """Result of async_process.""" + + response: intent.IntentResponse + conversation_id: str | None = None + + def as_dict(self) -> dict[str, Any]: + """Return result as a dict.""" + return { + "response": self.response.as_dict(), + "conversation_id": self.conversation_id, + } + + class AbstractConversationAgent(ABC): """Abstract conversation agent.""" @@ -25,6 +42,10 @@ class AbstractConversationAgent(ABC): @abstractmethod async def async_process( - self, text: str, context: Context, conversation_id: str | None = None - ) -> intent.IntentResponse | None: + self, + text: str, + context: Context, + conversation_id: str | None = None, + language: str | None = None, + ) -> ConversationResult | None: """Process a sentence.""" diff --git a/homeassistant/components/conversation/default_agent.py b/homeassistant/components/conversation/default_agent.py index 9079f7893ec..2e8e78d6f38 100644 --- a/homeassistant/components/conversation/default_agent.py +++ b/homeassistant/components/conversation/default_agent.py @@ -14,7 +14,7 @@ from homeassistant.core import callback from homeassistant.helpers import intent from homeassistant.setup import ATTR_COMPONENT -from .agent import AbstractConversationAgent +from .agent import AbstractConversationAgent, ConversationResult from .const import DOMAIN from .util import create_matcher @@ -111,8 +111,12 @@ class DefaultAgent(AbstractConversationAgent): async_register(self.hass, intent_type, sentences) async def async_process( - self, text: str, context: core.Context, conversation_id: str | None = None - ) -> intent.IntentResponse | None: + self, + text: str, + context: core.Context, + conversation_id: str | None = None, + language: str | None = None, + ) -> ConversationResult | None: """Process a sentence.""" intents = self.hass.data[DOMAIN] @@ -121,13 +125,18 @@ class DefaultAgent(AbstractConversationAgent): if not (match := matcher.match(text)): continue - return await intent.async_handle( + intent_response = await intent.async_handle( self.hass, DOMAIN, intent_type, {key: {"value": value} for key, value in match.groupdict().items()}, text, context, + language, + ) + + return ConversationResult( + response=intent_response, conversation_id=conversation_id ) return None diff --git a/homeassistant/components/coolmaster/climate.py b/homeassistant/components/coolmaster/climate.py index d2b0685cdf0..933072aac9d 100644 --- a/homeassistant/components/coolmaster/climate.py +++ b/homeassistant/components/coolmaster/climate.py @@ -8,7 +8,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS, TEMP_FAHRENHEIT +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -100,9 +100,9 @@ class CoolmasterClimate(CoordinatorEntity, ClimateEntity): def temperature_unit(self) -> str: """Return the unit of measurement.""" if self._unit.temperature_unit == "celsius": - return TEMP_CELSIUS + return UnitOfTemperature.CELSIUS - return TEMP_FAHRENHEIT + return UnitOfTemperature.FAHRENHEIT @property def current_temperature(self): diff --git a/homeassistant/components/coolmaster/strings.json b/homeassistant/components/coolmaster/strings.json index 7afc012a191..970660e6e63 100644 --- a/homeassistant/components/coolmaster/strings.json +++ b/homeassistant/components/coolmaster/strings.json @@ -2,7 +2,7 @@ "config": { "step": { "user": { - "title": "Setup your CoolMasterNet connection details.", + "title": "Set up your CoolMasterNet connection details.", "data": { "host": "[%key:common::config_flow::data::host%]", "off": "Can be turned off", diff --git a/homeassistant/components/coolmaster/translations/en.json b/homeassistant/components/coolmaster/translations/en.json index f3467de9121..9f12a4ebb30 100644 --- a/homeassistant/components/coolmaster/translations/en.json +++ b/homeassistant/components/coolmaster/translations/en.json @@ -15,7 +15,7 @@ "host": "Host", "off": "Can be turned off" }, - "title": "Setup your CoolMasterNet connection details." + "title": "Set up your CoolMasterNet connection details." } } } diff --git a/homeassistant/components/coolmaster/translations/it.json b/homeassistant/components/coolmaster/translations/it.json index 2a0cf07f5ed..2d3cf61e6d6 100644 --- a/homeassistant/components/coolmaster/translations/it.json +++ b/homeassistant/components/coolmaster/translations/it.json @@ -15,7 +15,7 @@ "host": "Host", "off": "Pu\u00f2 essere spento" }, - "title": "Imposta i dettagli della connessione CoolMasterNet." + "title": "Configura i dettagli della tua connessione CoolMasterNet." } } } diff --git a/homeassistant/components/coolmaster/translations/no.json b/homeassistant/components/coolmaster/translations/no.json index 7ccb23927bd..8e7384bfad1 100644 --- a/homeassistant/components/coolmaster/translations/no.json +++ b/homeassistant/components/coolmaster/translations/no.json @@ -15,7 +15,7 @@ "host": "Vert", "off": "Kan sl\u00e5s av" }, - "title": "Konfigurer informasjonen om CoolMasterNet-tilkoblingen." + "title": "Sett opp tilkoblingsdetaljer for CoolMasterNet." } } } diff --git a/homeassistant/components/coolmaster/translations/pt.json b/homeassistant/components/coolmaster/translations/pt.json index f13cad90edc..5a5dfb4da46 100644 --- a/homeassistant/components/coolmaster/translations/pt.json +++ b/homeassistant/components/coolmaster/translations/pt.json @@ -1,12 +1,12 @@ { "config": { "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "user": { "data": { - "host": "Servidor" + "host": "Endere\u00e7o" } } } diff --git a/homeassistant/components/coolmaster/translations/sk.json b/homeassistant/components/coolmaster/translations/sk.json index 4bd1fb9cf1d..4151e5afd04 100644 --- a/homeassistant/components/coolmaster/translations/sk.json +++ b/homeassistant/components/coolmaster/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "error": { - "cannot_connect": "Nepodarilo sa pripoji\u0165" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "no_units": "V hostite\u013eovi CoolMasterNet nen\u00e1jden\u00e9 \u017eiadne jednotky HVAC." }, "step": { "user": { @@ -11,7 +12,8 @@ "fan_only": "Podpora re\u017eimu iba ventil\u00e1tora", "heat": "Podpora re\u017eimu vykurovania", "heat_cool": "Podpora automatick\u00e9ho re\u017eimu vykurovania/chladenia", - "host": "Hostite\u013e" + "host": "Hostite\u013e", + "off": "Mo\u017en\u00e9 vypn\u00fa\u0165" }, "title": "Nastavte podrobnosti pripojenia CoolMasterNet." } diff --git a/homeassistant/components/coronavirus/translations/pt.json b/homeassistant/components/coronavirus/translations/pt.json index 308eaef73f0..2ad6dd98303 100644 --- a/homeassistant/components/coronavirus/translations/pt.json +++ b/homeassistant/components/coronavirus/translations/pt.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "O servi\u00e7o j\u00e1 est\u00e1 configurado", - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "user": { diff --git a/homeassistant/components/coronavirus/translations/sk.json b/homeassistant/components/coronavirus/translations/sk.json index c9e214a81d2..b2edc251763 100644 --- a/homeassistant/components/coronavirus/translations/sk.json +++ b/homeassistant/components/coronavirus/translations/sk.json @@ -8,7 +8,8 @@ "user": { "data": { "country": "Krajina" - } + }, + "title": "Vyberte krajinu, ktor\u00fa chcete sledova\u0165" } } } diff --git a/homeassistant/components/cover/__init__.py b/homeassistant/components/cover/__init__.py index 072710aa947..98bb2f4909f 100644 --- a/homeassistant/components/cover/__init__.py +++ b/homeassistant/components/cover/__init__.py @@ -218,7 +218,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: class CoverEntityDescription(EntityDescription): """A class that describes cover entities.""" - device_class: CoverDeviceClass | str | None = None + device_class: CoverDeviceClass | None = None class CoverEntity(Entity): @@ -227,7 +227,7 @@ class CoverEntity(Entity): entity_description: CoverEntityDescription _attr_current_cover_position: int | None = None _attr_current_cover_tilt_position: int | None = None - _attr_device_class: CoverDeviceClass | str | None + _attr_device_class: CoverDeviceClass | None _attr_is_closed: bool | None _attr_is_closing: bool | None = None _attr_is_opening: bool | None = None @@ -253,7 +253,7 @@ class CoverEntity(Entity): return self._attr_current_cover_tilt_position @property - def device_class(self) -> CoverDeviceClass | str | None: + def device_class(self) -> CoverDeviceClass | None: """Return the class of this entity.""" if hasattr(self, "_attr_device_class"): return self._attr_device_class diff --git a/homeassistant/components/cover/translations/sk.json b/homeassistant/components/cover/translations/sk.json index 335ab691980..48348e73d52 100644 --- a/homeassistant/components/cover/translations/sk.json +++ b/homeassistant/components/cover/translations/sk.json @@ -2,8 +2,11 @@ "device_automation": { "action_type": { "close": "Zavrie\u0165 {entity_name}", + "close_tilt": "Zn\u00ed\u017ei\u0165 n\u00e1klon {entity_name}", "open": "Otvori\u0165 {entity_name}", + "open_tilt": "Zv\u00fd\u0161i\u0165 n\u00e1klon {entity_name}", "set_position": "Nastavi\u0165 poz\u00edciu {entity_name}", + "set_tilt_position": "Nastavi\u0165 n\u00e1klon {entity_name}", "stop": "Zastavi\u0165 {entity_name}" }, "condition_type": { @@ -18,7 +21,9 @@ "closed": "{entity_name} zatvoren\u00e9", "closing": "zatv\u00e1renie {entity_name}", "opened": "{entity_name} otvoren\u00e9", - "position": "zmeny poz\u00edcie {entity_name}" + "opening": "{entity_name} sa otv\u00e1ra", + "position": "zmeny poz\u00edcie {entity_name}", + "tilt_position": "Pri zmene n\u00e1klonu {entity_name}" } }, "state": { diff --git a/homeassistant/components/cpuspeed/sensor.py b/homeassistant/components/cpuspeed/sensor.py index 6b64b8709a3..c71de53ebbe 100644 --- a/homeassistant/components/cpuspeed/sensor.py +++ b/homeassistant/components/cpuspeed/sensor.py @@ -3,9 +3,9 @@ from __future__ import annotations from cpuinfo import cpuinfo -from homeassistant.components.sensor import SensorEntity +from homeassistant.components.sensor import SensorDeviceClass, SensorEntity from homeassistant.config_entries import ConfigEntry -from homeassistant.const import FREQUENCY_GIGAHERTZ +from homeassistant.const import UnitOfFrequency from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -32,9 +32,10 @@ async def async_setup_entry( class CPUSpeedSensor(SensorEntity): """Representation of a CPU sensor.""" + _attr_device_class = SensorDeviceClass.FREQUENCY _attr_icon = "mdi:pulse" _attr_has_entity_name = True - _attr_native_unit_of_measurement = FREQUENCY_GIGAHERTZ + _attr_native_unit_of_measurement = UnitOfFrequency.GIGAHERTZ def __init__(self, entry: ConfigEntry) -> None: """Initialize the CPU sensor.""" diff --git a/homeassistant/components/cpuspeed/translations/en.json b/homeassistant/components/cpuspeed/translations/en.json index d482e5d3d3d..cbcc9de944d 100644 --- a/homeassistant/components/cpuspeed/translations/en.json +++ b/homeassistant/components/cpuspeed/translations/en.json @@ -6,7 +6,7 @@ }, "step": { "user": { - "description": "Do you want to start set up?", + "description": "Do you want to start setup?", "title": "CPU Speed" } } diff --git a/homeassistant/components/cpuspeed/translations/it.json b/homeassistant/components/cpuspeed/translations/it.json index ff84b5bf5ad..90c1a683bcd 100644 --- a/homeassistant/components/cpuspeed/translations/it.json +++ b/homeassistant/components/cpuspeed/translations/it.json @@ -6,7 +6,7 @@ }, "step": { "user": { - "description": "Vuoi iniziare la configurazione?", + "description": "Vuoi avviare la configurazione?", "title": "Velocit\u00e0 della CPU" } } diff --git a/homeassistant/components/cpuspeed/translations/ko.json b/homeassistant/components/cpuspeed/translations/ko.json index 758f3336cd4..f70d33320db 100644 --- a/homeassistant/components/cpuspeed/translations/ko.json +++ b/homeassistant/components/cpuspeed/translations/ko.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." + }, "step": { "user": { "description": "\uc124\uc815\uc744 \uc2dc\uc791\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" diff --git a/homeassistant/components/cpuspeed/translations/pt.json b/homeassistant/components/cpuspeed/translations/pt.json index f2a86ca8ca8..96a0dcd92fa 100644 --- a/homeassistant/components/cpuspeed/translations/pt.json +++ b/homeassistant/components/cpuspeed/translations/pt.json @@ -5,7 +5,7 @@ }, "step": { "user": { - "description": "Quer dar inicio \u00e0 configura\u00e7\u00e3o?", + "description": "Quer dar in\u00edcio \u00e0 configura\u00e7\u00e3o?", "title": "Velocidade da CPU" } } diff --git a/homeassistant/components/crownstone/entry_manager.py b/homeassistant/components/crownstone/entry_manager.py index 2f74daa8629..c527e3ba2b9 100644 --- a/homeassistant/components/crownstone/entry_manager.py +++ b/homeassistant/components/crownstone/entry_manager.py @@ -146,9 +146,13 @@ class CrownstoneEntryManager: # Show notification to ensure the user knows the cloud is now used persistent_notification.async_create( self.hass, - f"Setup of Crownstone USB dongle was unsuccessful on port {serial_port}.\n \ - Crownstone Cloud will be used to switch Crownstones.\n \ - Please check if your port is correct and set up the USB again from integration options.", + ( + "Setup of Crownstone USB dongle was unsuccessful on port" + f" {serial_port}.\n Crownstone Cloud will be used" + " to switch Crownstones.\n Please check if your" + " port is correct and set up the USB again from integration" + " options." + ), "Crownstone", "crownstone_usb_dongle_setup", ) diff --git a/homeassistant/components/crownstone/strings.json b/homeassistant/components/crownstone/strings.json index f2e885d73db..bcd818effb0 100644 --- a/homeassistant/components/crownstone/strings.json +++ b/homeassistant/components/crownstone/strings.json @@ -23,7 +23,7 @@ "usb_path": "[%key:common::config_flow::data::usb_path%]" }, "title": "Crownstone USB dongle configuration", - "description": "Select the serial port of the Crownstone USB dongle, or select 'Don't use USB' if you don't want to setup a USB dongle.\n\nLook for a device with VID 10C4 and PID EA60." + "description": "Select the serial port of the Crownstone USB dongle, or select 'Don't use USB' if you don't want to set up a USB dongle.\n\nLook for a device with VID 10C4 and PID EA60." }, "usb_manual_config": { "data": { diff --git a/homeassistant/components/crownstone/translations/en.json b/homeassistant/components/crownstone/translations/en.json index 09a26b9739c..80cb827b685 100644 --- a/homeassistant/components/crownstone/translations/en.json +++ b/homeassistant/components/crownstone/translations/en.json @@ -15,7 +15,7 @@ "data": { "usb_path": "USB Device Path" }, - "description": "Select the serial port of the Crownstone USB dongle, or select 'Don't use USB' if you don't want to setup a USB dongle.\n\nLook for a device with VID 10C4 and PID EA60.", + "description": "Select the serial port of the Crownstone USB dongle, or select 'Don't use USB' if you don't want to set up a USB dongle.\n\nLook for a device with VID 10C4 and PID EA60.", "title": "Crownstone USB dongle configuration" }, "usb_manual_config": { diff --git a/homeassistant/components/crownstone/translations/it.json b/homeassistant/components/crownstone/translations/it.json index 5600ff09de8..416b8246693 100644 --- a/homeassistant/components/crownstone/translations/it.json +++ b/homeassistant/components/crownstone/translations/it.json @@ -15,7 +15,7 @@ "data": { "usb_path": "Percorso del dispositivo USB" }, - "description": "Seleziona la porta seriale della chiavetta USB Crownstone. \n\nCerca un dispositivo con VID 10C4 e PID EA60.", + "description": "Seleziona la porta seriale del dongle USB Crownstone o seleziona \"Non utilizzare USB\" se non desideri configurare un dongle USB.\n\nCerca un dispositivo con VID 10C4 e PID EA60.", "title": "Configurazione della chiavetta USB Crownstone" }, "usb_manual_config": { diff --git a/homeassistant/components/crownstone/translations/ko.json b/homeassistant/components/crownstone/translations/ko.json index 22a5729e256..15fa87f3ede 100644 --- a/homeassistant/components/crownstone/translations/ko.json +++ b/homeassistant/components/crownstone/translations/ko.json @@ -1,6 +1,43 @@ { + "config": { + "abort": { + "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "usb_config": { + "data": { + "usb_path": "USB \uc7a5\uce58 \uacbd\ub85c" + } + }, + "usb_manual_config": { + "data": { + "usb_manual_path": "USB \uc7a5\uce58 \uacbd\ub85c" + } + }, + "user": { + "data": { + "email": "\uc774\uba54\uc77c", + "password": "\ube44\ubc00\ubc88\ud638" + } + } + } + }, "options": { "step": { + "usb_config": { + "data": { + "usb_path": "USB \uc7a5\uce58 \uacbd\ub85c" + } + }, + "usb_manual_config": { + "data": { + "usb_manual_path": "USB \uc7a5\uce58 \uacbd\ub85c" + } + }, "usb_sphere_config": { "title": "\ud06c\ub77c\uc6b4\uc2a4\ud1a4 USB \uc2a4\ud53c\uc5b4" } diff --git a/homeassistant/components/crownstone/translations/no.json b/homeassistant/components/crownstone/translations/no.json index 33e61211205..d2efc1edefc 100644 --- a/homeassistant/components/crownstone/translations/no.json +++ b/homeassistant/components/crownstone/translations/no.json @@ -15,7 +15,7 @@ "data": { "usb_path": "USB enhetsbane" }, - "description": "Velg den serielle porten p\u00e5 Crownstone USB -dongelen, eller velg 'Ikke bruk USB' hvis du ikke vil konfigurere en USB -dongle. \n\n Se etter en enhet med VID 10C4 og PID EA60.", + "description": "Velg serieporten til Crownstone USB-dongelen, eller velg 'Ikke bruk USB' hvis du ikke vil sette opp en USB-dongel. \n\n Se etter en enhet med VID 10C4 og PID EA60.", "title": "Crownstone USB -dongle -konfigurasjon" }, "usb_manual_config": { diff --git a/homeassistant/components/crownstone/translations/pt.json b/homeassistant/components/crownstone/translations/pt.json index 5a97b161335..e18efc9f76c 100644 --- a/homeassistant/components/crownstone/translations/pt.json +++ b/homeassistant/components/crownstone/translations/pt.json @@ -16,7 +16,7 @@ }, "user": { "data": { - "email": "Email", + "email": "", "password": "Palavra-passe" } } diff --git a/homeassistant/components/crownstone/translations/sk.json b/homeassistant/components/crownstone/translations/sk.json index 3c0ec2452c2..cea3ff01a26 100644 --- a/homeassistant/components/crownstone/translations/sk.json +++ b/homeassistant/components/crownstone/translations/sk.json @@ -1,7 +1,9 @@ { "config": { "abort": { - "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", + "usb_setup_complete": "Nastavenie Crownstone USB je dokon\u010den\u00e9.", + "usb_setup_unsuccessful": "Nastavenie Crownstone USB bolo ne\u00faspe\u0161n\u00e9." }, "error": { "account_not_verified": "\u00da\u010det nie je overen\u00fd. Aktivujte si svoj \u00fa\u010det prostredn\u00edctvom aktiva\u010dn\u00e9ho e-mailu od Crownstone.", @@ -12,32 +14,61 @@ "usb_config": { "data": { "usb_path": "Cesta k zariadeniu USB" - } + }, + "description": "Vyberte s\u00e9riov\u00fd port Crownstone USB dongle alebo zvo\u013ete \u201eNepou\u017e\u00edva\u0165 USB\u201c, ak nechcete nastavova\u0165 USB dongle. \n\n H\u013eadajte zariadenie s VID 10C4 a PID EA60.", + "title": "Konfigur\u00e1cia hardv\u00e9rov\u00e9ho k\u013e\u00fa\u010da Crownstone USB" }, "usb_manual_config": { "data": { "usb_manual_path": "Cesta k zariadeniu USB" - } + }, + "description": "Manu\u00e1lne zadajte cestu k usb hardwarov\u00e9ho k\u013e\u00fa\u010du Crownstone.", + "title": "Manu\u00e1lna cesta k hardv\u00e9rov\u00e9mu k\u013e\u00fa\u010du Crownstone USB" + }, + "usb_sphere_config": { + "data": { + "usb_sphere": "Crownstone Sphere" + }, + "description": "Vyberte Crownstone Sphere, kde sa nach\u00e1dza USB.", + "title": "Crownstone USB Sphere" }, "user": { "data": { "email": "Email", "password": "Heslo" - } + }, + "title": "Crownstone \u00fa\u010det" } } }, "options": { "step": { + "init": { + "data": { + "usb_sphere_option": "Crownstone Sphere, kde sa nach\u00e1dza USB", + "use_usb_option": "Na lok\u00e1lny prenos \u00fadajov pou\u017eite dongle Crownstone USB" + } + }, "usb_config": { "data": { "usb_path": "Cesta k zariadeniu USB" - } + }, + "description": "Vyberte s\u00e9riov\u00fd port Crownstone USB dongle. \n\n H\u013eadajte zariadenie s VID 10C4 a PID EA60.", + "title": "Konfigur\u00e1cia dongle USB Crownstone" }, "usb_manual_config": { "data": { "usb_manual_path": "Cesta k zariadeniu USB" - } + }, + "description": "Manu\u00e1lne zadajte cestu k usb hardwarov\u00e9ho k\u013e\u00fa\u010du Crownstone.", + "title": "Manu\u00e1lna cesta k hardv\u00e9rov\u00e9mu k\u013e\u00fa\u010du Crownstone USB" + }, + "usb_sphere_config": { + "data": { + "usb_sphere": "Crownstone Sphere" + }, + "description": "Vyberte Crownstone Sphere, kde sa nach\u00e1dza USB.", + "title": "Crownstone USB Sphere" } } } diff --git a/homeassistant/components/daikin/__init__.py b/homeassistant/components/daikin/__init__.py index 536e2fa48d1..481a072bdb3 100644 --- a/homeassistant/components/daikin/__init__.py +++ b/homeassistant/components/daikin/__init__.py @@ -8,7 +8,13 @@ from async_timeout import timeout from pydaikin.daikin_base import Appliance from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_API_KEY, CONF_HOST, CONF_PASSWORD, Platform +from homeassistant.const import ( + CONF_API_KEY, + CONF_HOST, + CONF_PASSWORD, + CONF_UUID, + Platform, +) from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers.aiohttp_client import async_get_clientsession @@ -17,7 +23,7 @@ from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC from homeassistant.helpers.entity import DeviceInfo from homeassistant.util import Throttle -from .const import CONF_UUID, DOMAIN, KEY_MAC, TIMEOUT +from .const import DOMAIN, KEY_MAC, TIMEOUT _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/daikin/climate.py b/homeassistant/components/daikin/climate.py index bd4763d3254..bd0e846ea4d 100644 --- a/homeassistant/components/daikin/climate.py +++ b/homeassistant/components/daikin/climate.py @@ -22,7 +22,12 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, CONF_HOST, CONF_NAME, TEMP_CELSIUS +from homeassistant.const import ( + ATTR_TEMPERATURE, + CONF_HOST, + CONF_NAME, + UnitOfTemperature, +) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -118,7 +123,7 @@ class DaikinClimate(ClimateEntity): _attr_name = None _attr_has_entity_name = True - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS def __init__(self, api: DaikinApi) -> None: """Initialize the climate device.""" diff --git a/homeassistant/components/daikin/config_flow.py b/homeassistant/components/daikin/config_flow.py index fb34c947fa7..a64f2059972 100644 --- a/homeassistant/components/daikin/config_flow.py +++ b/homeassistant/components/daikin/config_flow.py @@ -11,11 +11,11 @@ import voluptuous as vol from homeassistant import config_entries from homeassistant.components import zeroconf -from homeassistant.const import CONF_API_KEY, CONF_HOST, CONF_PASSWORD +from homeassistant.const import CONF_API_KEY, CONF_HOST, CONF_PASSWORD, CONF_UUID from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers.aiohttp_client import async_get_clientsession -from .const import CONF_UUID, DOMAIN, KEY_MAC, TIMEOUT +from .const import DOMAIN, KEY_MAC, TIMEOUT _LOGGER = logging.getLogger(__name__) @@ -134,8 +134,10 @@ class FlowHandler(config_entries.ConfigFlow, domain=DOMAIN): devices = Discovery().poll(ip=discovery_info.host) if not devices: _LOGGER.debug( - "Could not find MAC-address for %s," - " make sure the required UDP ports are open (see integration documentation)", + ( + "Could not find MAC-address for %s, make sure the required UDP" + " ports are open (see integration documentation)" + ), discovery_info.host, ) return self.async_abort(reason="cannot_connect") diff --git a/homeassistant/components/daikin/const.py b/homeassistant/components/daikin/const.py index a978e96178d..67d014ecdb3 100644 --- a/homeassistant/components/daikin/const.py +++ b/homeassistant/components/daikin/const.py @@ -20,8 +20,6 @@ ATTR_TOTAL_ENERGY_TODAY = "total_energy_today" ATTR_STATE_ON = "on" ATTR_STATE_OFF = "off" -CONF_UUID = "uuid" - KEY_MAC = "mac" KEY_IP = "ip" diff --git a/homeassistant/components/daikin/sensor.py b/homeassistant/components/daikin/sensor.py index 3d9deba59ab..2660a1a9d3a 100644 --- a/homeassistant/components/daikin/sensor.py +++ b/homeassistant/components/daikin/sensor.py @@ -14,11 +14,11 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - ENERGY_KILO_WATT_HOUR, - FREQUENCY_HERTZ, PERCENTAGE, - POWER_KILO_WATT, - TEMP_CELSIUS, + UnitOfEnergy, + UnitOfFrequency, + UnitOfPower, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo @@ -58,7 +58,7 @@ SENSOR_TYPES: tuple[DaikinSensorEntityDescription, ...] = ( name="Inside temperature", device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, value_func=lambda device: device.inside_temperature, ), DaikinSensorEntityDescription( @@ -66,7 +66,7 @@ SENSOR_TYPES: tuple[DaikinSensorEntityDescription, ...] = ( name="Outside temperature", device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, value_func=lambda device: device.outside_temperature, ), DaikinSensorEntityDescription( @@ -90,7 +90,7 @@ SENSOR_TYPES: tuple[DaikinSensorEntityDescription, ...] = ( name="Compressor estimated power consumption", device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=POWER_KILO_WATT, + native_unit_of_measurement=UnitOfPower.KILO_WATT, value_func=lambda device: round(device.current_total_power_consumption, 2), ), DaikinSensorEntityDescription( @@ -98,7 +98,7 @@ SENSOR_TYPES: tuple[DaikinSensorEntityDescription, ...] = ( name="Cool energy consumption", icon="mdi:snowflake", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, entity_registry_enabled_default=False, value_func=lambda device: round(device.last_hour_cool_energy_consumption, 2), ), @@ -107,7 +107,7 @@ SENSOR_TYPES: tuple[DaikinSensorEntityDescription, ...] = ( name="Heat energy consumption", icon="mdi:fire", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, entity_registry_enabled_default=False, value_func=lambda device: round(device.last_hour_heat_energy_consumption, 2), ), @@ -116,7 +116,7 @@ SENSOR_TYPES: tuple[DaikinSensorEntityDescription, ...] = ( name="Energy consumption", device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, value_func=lambda device: round(device.today_energy_consumption, 2), ), DaikinSensorEntityDescription( @@ -125,7 +125,7 @@ SENSOR_TYPES: tuple[DaikinSensorEntityDescription, ...] = ( icon="mdi:fan", device_class=SensorDeviceClass.FREQUENCY, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=FREQUENCY_HERTZ, + native_unit_of_measurement=UnitOfFrequency.HERTZ, entity_registry_enabled_default=False, value_func=lambda device: device.compressor_frequency, ), @@ -134,7 +134,7 @@ SENSOR_TYPES: tuple[DaikinSensorEntityDescription, ...] = ( name="Compressor energy consumption", device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, entity_registry_enabled_default=False, value_func=lambda device: round(device.today_total_energy_consumption, 2), ), diff --git a/homeassistant/components/daikin/translations/ko.json b/homeassistant/components/daikin/translations/ko.json index e87db9f29d3..89e2bb8e607 100644 --- a/homeassistant/components/daikin/translations/ko.json +++ b/homeassistant/components/daikin/translations/ko.json @@ -5,6 +5,7 @@ "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" }, "error": { + "api_password": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4 , API \ud0a4 \ub610\ub294 \ube44\ubc00\ubc88\ud638\ub97c \uc0ac\uc6a9\ud558\uc2ed\uc2dc\uc624.", "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" diff --git a/homeassistant/components/daikin/translations/pt.json b/homeassistant/components/daikin/translations/pt.json index 0d1c8f842c0..8c38c62a16d 100644 --- a/homeassistant/components/daikin/translations/pt.json +++ b/homeassistant/components/daikin/translations/pt.json @@ -2,11 +2,11 @@ "config": { "abort": { "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "error": { "api_password": "Autentica\u00e7\u00e3o inv\u00e1lida, use a chave de API ou a palavra-passe.", - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, @@ -14,7 +14,7 @@ "user": { "data": { "api_key": "Chave da API", - "host": "Servidor", + "host": "Endere\u00e7o", "password": "Palavra-passe" }, "description": "Introduza Endere\u00e7o IP do seu Daikin AC.\n\nAten\u00e7\u00e3o que [%chave:common::config_flow::data::api_key%] e Palavra-passe s\u00f3 s\u00e3o utilizador pelos dispositivos BRP072Cxx e SKYFi, respectivamente.", diff --git a/homeassistant/components/daikin/translations/sk.json b/homeassistant/components/daikin/translations/sk.json index b0c325dc02a..63d042dc53c 100644 --- a/homeassistant/components/daikin/translations/sk.json +++ b/homeassistant/components/daikin/translations/sk.json @@ -17,6 +17,7 @@ "host": "Hostite\u013e", "password": "Heslo" }, + "description": "Zadajte IP adresa v\u00e1\u0161ho Daikin AC. \n\nUpozor\u0148ujeme, \u017ee API k\u013e\u00fa\u010d a Heslo pou\u017e\u00edvaj\u00fa iba zariadenia BRP072Cxx a SKYFi.", "title": "Konfigur\u00e1cia Daikin AC" } } diff --git a/homeassistant/components/danfoss_air/sensor.py b/homeassistant/components/danfoss_air/sensor.py index 51eab3e471c..024bb50ba34 100644 --- a/homeassistant/components/danfoss_air/sensor.py +++ b/homeassistant/components/danfoss_air/sensor.py @@ -10,7 +10,7 @@ from homeassistant.components.sensor import ( SensorEntity, SensorStateClass, ) -from homeassistant.const import PERCENTAGE, REVOLUTIONS_PER_MINUTE, TEMP_CELSIUS +from homeassistant.const import PERCENTAGE, REVOLUTIONS_PER_MINUTE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType @@ -32,28 +32,28 @@ def setup_platform( sensors = [ [ "Danfoss Air Exhaust Temperature", - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, ReadCommand.exhaustTemperature, SensorDeviceClass.TEMPERATURE, SensorStateClass.MEASUREMENT, ], [ "Danfoss Air Outdoor Temperature", - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, ReadCommand.outdoorTemperature, SensorDeviceClass.TEMPERATURE, SensorStateClass.MEASUREMENT, ], [ "Danfoss Air Supply Temperature", - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, ReadCommand.supplyTemperature, SensorDeviceClass.TEMPERATURE, SensorStateClass.MEASUREMENT, ], [ "Danfoss Air Extract Temperature", - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, ReadCommand.extractTemperature, SensorDeviceClass.TEMPERATURE, SensorStateClass.MEASUREMENT, diff --git a/homeassistant/components/darksky/sensor.py b/homeassistant/components/darksky/sensor.py index 504029d339b..24bdd0747c6 100644 --- a/homeassistant/components/darksky/sensor.py +++ b/homeassistant/components/darksky/sensor.py @@ -25,18 +25,13 @@ from homeassistant.const import ( CONF_NAME, CONF_SCAN_INTERVAL, DEGREE, - LENGTH_CENTIMETERS, - LENGTH_INCHES, - LENGTH_KILOMETERS, - LENGTH_MILES, PERCENTAGE, - PRESSURE_MBAR, - SPEED_KILOMETERS_PER_HOUR, - SPEED_METERS_PER_SECOND, - SPEED_MILES_PER_HOUR, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, UV_INDEX, + UnitOfLength, + UnitOfPrecipitationDepth, + UnitOfPressure, + UnitOfSpeed, + UnitOfTemperature, UnitOfVolumetricFlux, ) from homeassistant.core import HomeAssistant @@ -117,11 +112,11 @@ SENSOR_TYPES: dict[str, DarkskySensorEntityDescription] = { "nearest_storm_distance": DarkskySensorEntityDescription( key="nearest_storm_distance", name="Nearest Storm Distance", - si_unit=LENGTH_KILOMETERS, - us_unit=LENGTH_MILES, - ca_unit=LENGTH_KILOMETERS, - uk_unit=LENGTH_KILOMETERS, - uk2_unit=LENGTH_MILES, + si_unit=UnitOfLength.KILOMETERS, + us_unit=UnitOfLength.MILES, + ca_unit=UnitOfLength.KILOMETERS, + uk_unit=UnitOfLength.KILOMETERS, + uk2_unit=UnitOfLength.MILES, icon="mdi:weather-lightning", forecast_mode=["currently"], ), @@ -167,11 +162,12 @@ SENSOR_TYPES: dict[str, DarkskySensorEntityDescription] = { "precip_accumulation": DarkskySensorEntityDescription( key="precip_accumulation", name="Precip Accumulation", - si_unit=LENGTH_CENTIMETERS, - us_unit=LENGTH_INCHES, - ca_unit=LENGTH_CENTIMETERS, - uk_unit=LENGTH_CENTIMETERS, - uk2_unit=LENGTH_CENTIMETERS, + device_class=SensorDeviceClass.PRECIPITATION, + si_unit=UnitOfPrecipitationDepth.CENTIMETERS, + us_unit=UnitOfPrecipitationDepth.INCHES, + ca_unit=UnitOfPrecipitationDepth.CENTIMETERS, + uk_unit=UnitOfPrecipitationDepth.CENTIMETERS, + uk2_unit=UnitOfPrecipitationDepth.CENTIMETERS, icon="mdi:weather-snowy", forecast_mode=["hourly", "daily"], ), @@ -180,11 +176,11 @@ SENSOR_TYPES: dict[str, DarkskySensorEntityDescription] = { name="Temperature", device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, - si_unit=TEMP_CELSIUS, - us_unit=TEMP_FAHRENHEIT, - ca_unit=TEMP_CELSIUS, - uk_unit=TEMP_CELSIUS, - uk2_unit=TEMP_CELSIUS, + si_unit=UnitOfTemperature.CELSIUS, + us_unit=UnitOfTemperature.FAHRENHEIT, + ca_unit=UnitOfTemperature.CELSIUS, + uk_unit=UnitOfTemperature.CELSIUS, + uk2_unit=UnitOfTemperature.CELSIUS, forecast_mode=["currently", "hourly"], ), "apparent_temperature": DarkskySensorEntityDescription( @@ -192,11 +188,11 @@ SENSOR_TYPES: dict[str, DarkskySensorEntityDescription] = { name="Apparent Temperature", device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, - si_unit=TEMP_CELSIUS, - us_unit=TEMP_FAHRENHEIT, - ca_unit=TEMP_CELSIUS, - uk_unit=TEMP_CELSIUS, - uk2_unit=TEMP_CELSIUS, + si_unit=UnitOfTemperature.CELSIUS, + us_unit=UnitOfTemperature.FAHRENHEIT, + ca_unit=UnitOfTemperature.CELSIUS, + uk_unit=UnitOfTemperature.CELSIUS, + uk2_unit=UnitOfTemperature.CELSIUS, forecast_mode=["currently", "hourly"], ), "dew_point": DarkskySensorEntityDescription( @@ -204,22 +200,22 @@ SENSOR_TYPES: dict[str, DarkskySensorEntityDescription] = { name="Dew Point", device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, - si_unit=TEMP_CELSIUS, - us_unit=TEMP_FAHRENHEIT, - ca_unit=TEMP_CELSIUS, - uk_unit=TEMP_CELSIUS, - uk2_unit=TEMP_CELSIUS, + si_unit=UnitOfTemperature.CELSIUS, + us_unit=UnitOfTemperature.FAHRENHEIT, + ca_unit=UnitOfTemperature.CELSIUS, + uk_unit=UnitOfTemperature.CELSIUS, + uk2_unit=UnitOfTemperature.CELSIUS, forecast_mode=["currently", "hourly", "daily"], ), "wind_speed": DarkskySensorEntityDescription( key="wind_speed", name="Wind Speed", - si_unit=SPEED_METERS_PER_SECOND, - us_unit=SPEED_MILES_PER_HOUR, - ca_unit=SPEED_KILOMETERS_PER_HOUR, - uk_unit=SPEED_MILES_PER_HOUR, - uk2_unit=SPEED_MILES_PER_HOUR, - icon="mdi:weather-windy", + device_class=SensorDeviceClass.WIND_SPEED, + si_unit=UnitOfSpeed.METERS_PER_SECOND, + us_unit=UnitOfSpeed.MILES_PER_HOUR, + ca_unit=UnitOfSpeed.KILOMETERS_PER_HOUR, + uk_unit=UnitOfSpeed.MILES_PER_HOUR, + uk2_unit=UnitOfSpeed.MILES_PER_HOUR, forecast_mode=["currently", "hourly", "daily"], ), "wind_bearing": DarkskySensorEntityDescription( @@ -236,11 +232,12 @@ SENSOR_TYPES: dict[str, DarkskySensorEntityDescription] = { "wind_gust": DarkskySensorEntityDescription( key="wind_gust", name="Wind Gust", - si_unit=SPEED_METERS_PER_SECOND, - us_unit=SPEED_MILES_PER_HOUR, - ca_unit=SPEED_KILOMETERS_PER_HOUR, - uk_unit=SPEED_MILES_PER_HOUR, - uk2_unit=SPEED_MILES_PER_HOUR, + device_class=SensorDeviceClass.WIND_SPEED, + si_unit=UnitOfSpeed.METERS_PER_SECOND, + us_unit=UnitOfSpeed.MILES_PER_HOUR, + ca_unit=UnitOfSpeed.KILOMETERS_PER_HOUR, + uk_unit=UnitOfSpeed.MILES_PER_HOUR, + uk2_unit=UnitOfSpeed.MILES_PER_HOUR, icon="mdi:weather-windy-variant", forecast_mode=["currently", "hourly", "daily"], ), @@ -271,21 +268,21 @@ SENSOR_TYPES: dict[str, DarkskySensorEntityDescription] = { key="pressure", name="Pressure", device_class=SensorDeviceClass.PRESSURE, - si_unit=PRESSURE_MBAR, - us_unit=PRESSURE_MBAR, - ca_unit=PRESSURE_MBAR, - uk_unit=PRESSURE_MBAR, - uk2_unit=PRESSURE_MBAR, + si_unit=UnitOfPressure.MBAR, + us_unit=UnitOfPressure.MBAR, + ca_unit=UnitOfPressure.MBAR, + uk_unit=UnitOfPressure.MBAR, + uk2_unit=UnitOfPressure.MBAR, forecast_mode=["currently", "hourly", "daily"], ), "visibility": DarkskySensorEntityDescription( key="visibility", name="Visibility", - si_unit=LENGTH_KILOMETERS, - us_unit=LENGTH_MILES, - ca_unit=LENGTH_KILOMETERS, - uk_unit=LENGTH_KILOMETERS, - uk2_unit=LENGTH_MILES, + si_unit=UnitOfLength.KILOMETERS, + us_unit=UnitOfLength.MILES, + ca_unit=UnitOfLength.KILOMETERS, + uk_unit=UnitOfLength.KILOMETERS, + uk2_unit=UnitOfLength.MILES, icon="mdi:eye", forecast_mode=["currently", "hourly", "daily"], ), @@ -304,88 +301,88 @@ SENSOR_TYPES: dict[str, DarkskySensorEntityDescription] = { key="apparent_temperature_max", name="Daily High Apparent Temperature", device_class=SensorDeviceClass.TEMPERATURE, - si_unit=TEMP_CELSIUS, - us_unit=TEMP_FAHRENHEIT, - ca_unit=TEMP_CELSIUS, - uk_unit=TEMP_CELSIUS, - uk2_unit=TEMP_CELSIUS, + si_unit=UnitOfTemperature.CELSIUS, + us_unit=UnitOfTemperature.FAHRENHEIT, + ca_unit=UnitOfTemperature.CELSIUS, + uk_unit=UnitOfTemperature.CELSIUS, + uk2_unit=UnitOfTemperature.CELSIUS, forecast_mode=["daily"], ), "apparent_temperature_high": DarkskySensorEntityDescription( key="apparent_temperature_high", name="Daytime High Apparent Temperature", device_class=SensorDeviceClass.TEMPERATURE, - si_unit=TEMP_CELSIUS, - us_unit=TEMP_FAHRENHEIT, - ca_unit=TEMP_CELSIUS, - uk_unit=TEMP_CELSIUS, - uk2_unit=TEMP_CELSIUS, + si_unit=UnitOfTemperature.CELSIUS, + us_unit=UnitOfTemperature.FAHRENHEIT, + ca_unit=UnitOfTemperature.CELSIUS, + uk_unit=UnitOfTemperature.CELSIUS, + uk2_unit=UnitOfTemperature.CELSIUS, forecast_mode=["daily"], ), "apparent_temperature_min": DarkskySensorEntityDescription( key="apparent_temperature_min", name="Daily Low Apparent Temperature", device_class=SensorDeviceClass.TEMPERATURE, - si_unit=TEMP_CELSIUS, - us_unit=TEMP_FAHRENHEIT, - ca_unit=TEMP_CELSIUS, - uk_unit=TEMP_CELSIUS, - uk2_unit=TEMP_CELSIUS, + si_unit=UnitOfTemperature.CELSIUS, + us_unit=UnitOfTemperature.FAHRENHEIT, + ca_unit=UnitOfTemperature.CELSIUS, + uk_unit=UnitOfTemperature.CELSIUS, + uk2_unit=UnitOfTemperature.CELSIUS, forecast_mode=["daily"], ), "apparent_temperature_low": DarkskySensorEntityDescription( key="apparent_temperature_low", name="Overnight Low Apparent Temperature", device_class=SensorDeviceClass.TEMPERATURE, - si_unit=TEMP_CELSIUS, - us_unit=TEMP_FAHRENHEIT, - ca_unit=TEMP_CELSIUS, - uk_unit=TEMP_CELSIUS, - uk2_unit=TEMP_CELSIUS, + si_unit=UnitOfTemperature.CELSIUS, + us_unit=UnitOfTemperature.FAHRENHEIT, + ca_unit=UnitOfTemperature.CELSIUS, + uk_unit=UnitOfTemperature.CELSIUS, + uk2_unit=UnitOfTemperature.CELSIUS, forecast_mode=["daily"], ), "temperature_max": DarkskySensorEntityDescription( key="temperature_max", name="Daily High Temperature", device_class=SensorDeviceClass.TEMPERATURE, - si_unit=TEMP_CELSIUS, - us_unit=TEMP_FAHRENHEIT, - ca_unit=TEMP_CELSIUS, - uk_unit=TEMP_CELSIUS, - uk2_unit=TEMP_CELSIUS, + si_unit=UnitOfTemperature.CELSIUS, + us_unit=UnitOfTemperature.FAHRENHEIT, + ca_unit=UnitOfTemperature.CELSIUS, + uk_unit=UnitOfTemperature.CELSIUS, + uk2_unit=UnitOfTemperature.CELSIUS, forecast_mode=["daily"], ), "temperature_high": DarkskySensorEntityDescription( key="temperature_high", name="Daytime High Temperature", device_class=SensorDeviceClass.TEMPERATURE, - si_unit=TEMP_CELSIUS, - us_unit=TEMP_FAHRENHEIT, - ca_unit=TEMP_CELSIUS, - uk_unit=TEMP_CELSIUS, - uk2_unit=TEMP_CELSIUS, + si_unit=UnitOfTemperature.CELSIUS, + us_unit=UnitOfTemperature.FAHRENHEIT, + ca_unit=UnitOfTemperature.CELSIUS, + uk_unit=UnitOfTemperature.CELSIUS, + uk2_unit=UnitOfTemperature.CELSIUS, forecast_mode=["daily"], ), "temperature_min": DarkskySensorEntityDescription( key="temperature_min", name="Daily Low Temperature", device_class=SensorDeviceClass.TEMPERATURE, - si_unit=TEMP_CELSIUS, - us_unit=TEMP_FAHRENHEIT, - ca_unit=TEMP_CELSIUS, - uk_unit=TEMP_CELSIUS, - uk2_unit=TEMP_CELSIUS, + si_unit=UnitOfTemperature.CELSIUS, + us_unit=UnitOfTemperature.FAHRENHEIT, + ca_unit=UnitOfTemperature.CELSIUS, + uk_unit=UnitOfTemperature.CELSIUS, + uk2_unit=UnitOfTemperature.CELSIUS, forecast_mode=["daily"], ), "temperature_low": DarkskySensorEntityDescription( key="temperature_low", name="Overnight Low Temperature", device_class=SensorDeviceClass.TEMPERATURE, - si_unit=TEMP_CELSIUS, - us_unit=TEMP_FAHRENHEIT, - ca_unit=TEMP_CELSIUS, - uk_unit=TEMP_CELSIUS, - uk2_unit=TEMP_CELSIUS, + si_unit=UnitOfTemperature.CELSIUS, + us_unit=UnitOfTemperature.FAHRENHEIT, + ca_unit=UnitOfTemperature.CELSIUS, + uk_unit=UnitOfTemperature.CELSIUS, + uk2_unit=UnitOfTemperature.CELSIUS, forecast_mode=["daily"], ), "precip_intensity_max": DarkskySensorEntityDescription( diff --git a/homeassistant/components/debugpy/manifest.json b/homeassistant/components/debugpy/manifest.json index 33746d1a5dd..3d24903971e 100644 --- a/homeassistant/components/debugpy/manifest.json +++ b/homeassistant/components/debugpy/manifest.json @@ -2,7 +2,7 @@ "domain": "debugpy", "name": "Remote Python Debugger", "documentation": "https://www.home-assistant.io/integrations/debugpy", - "requirements": ["debugpy==1.6.3"], + "requirements": ["debugpy==1.6.4"], "codeowners": ["@frenck"], "quality_scale": "internal", "iot_class": "local_push", diff --git a/homeassistant/components/deconz/climate.py b/homeassistant/components/deconz/climate.py index c5b9571ed34..eb1d0d6b672 100644 --- a/homeassistant/components/deconz/climate.py +++ b/homeassistant/components/deconz/climate.py @@ -28,7 +28,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -99,7 +99,7 @@ class DeconzThermostat(DeconzDevice[Thermostat], ClimateEntity): TYPE = DOMAIN - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS def __init__(self, device: Thermostat, gateway: DeconzGateway) -> None: """Set up thermostat device.""" diff --git a/homeassistant/components/deconz/device_trigger.py b/homeassistant/components/deconz/device_trigger.py index 76844b026ce..70ef46bafc6 100644 --- a/homeassistant/components/deconz/device_trigger.py +++ b/homeassistant/components/deconz/device_trigger.py @@ -128,6 +128,18 @@ HUE_TAP_REMOTE = { (CONF_SHORT_PRESS, CONF_BUTTON_4): {CONF_EVENT: 18}, } +HUE_WALL_REMOTE_MODEL = "RDM001" # Hue wall switch +HUE_WALL_REMOTE = { + (CONF_SHORT_PRESS, CONF_BUTTON_1): {CONF_EVENT: 1000}, + (CONF_SHORT_RELEASE, CONF_BUTTON_1): {CONF_EVENT: 1002}, + (CONF_LONG_PRESS, CONF_BUTTON_1): {CONF_EVENT: 1001}, + (CONF_LONG_RELEASE, CONF_BUTTON_1): {CONF_EVENT: 1003}, + (CONF_SHORT_PRESS, CONF_BUTTON_2): {CONF_EVENT: 2000}, + (CONF_SHORT_RELEASE, CONF_BUTTON_2): {CONF_EVENT: 2002}, + (CONF_LONG_PRESS, CONF_BUTTON_2): {CONF_EVENT: 2001}, + (CONF_LONG_RELEASE, CONF_BUTTON_2): {CONF_EVENT: 2003}, +} + FRIENDS_OF_HUE_SWITCH_MODEL = "FOHSWITCH" FRIENDS_OF_HUE_SWITCH = { (CONF_SHORT_PRESS, CONF_BUTTON_1): {CONF_EVENT: 1000}, @@ -158,18 +170,18 @@ FRIENDS_OF_HUE_SWITCH = { STYRBAR_REMOTE_MODEL = "Remote Control N2" STYRBAR_REMOTE = { - (CONF_SHORT_RELEASE, CONF_TURN_ON): {CONF_EVENT: 1002}, - (CONF_LONG_PRESS, CONF_TURN_ON): {CONF_EVENT: 1001}, - (CONF_LONG_RELEASE, CONF_TURN_ON): {CONF_EVENT: 1003}, - (CONF_SHORT_RELEASE, CONF_DIM_UP): {CONF_EVENT: 2002}, - (CONF_LONG_PRESS, CONF_DIM_UP): {CONF_EVENT: 2001}, - (CONF_LONG_RELEASE, CONF_DIM_UP): {CONF_EVENT: 2003}, - (CONF_SHORT_RELEASE, CONF_DIM_DOWN): {CONF_EVENT: 3002}, - (CONF_LONG_PRESS, CONF_DIM_DOWN): {CONF_EVENT: 3001}, - (CONF_LONG_RELEASE, CONF_DIM_DOWN): {CONF_EVENT: 3003}, - (CONF_SHORT_RELEASE, CONF_TURN_OFF): {CONF_EVENT: 4002}, - (CONF_LONG_PRESS, CONF_TURN_OFF): {CONF_EVENT: 4001}, - (CONF_LONG_RELEASE, CONF_TURN_OFF): {CONF_EVENT: 4003}, + (CONF_SHORT_RELEASE, CONF_DIM_UP): {CONF_EVENT: 1002}, + (CONF_LONG_PRESS, CONF_DIM_UP): {CONF_EVENT: 1001}, + (CONF_LONG_RELEASE, CONF_DIM_UP): {CONF_EVENT: 1003}, + (CONF_SHORT_RELEASE, CONF_DIM_DOWN): {CONF_EVENT: 2002}, + (CONF_LONG_PRESS, CONF_DIM_DOWN): {CONF_EVENT: 2001}, + (CONF_LONG_RELEASE, CONF_DIM_DOWN): {CONF_EVENT: 2003}, + (CONF_SHORT_RELEASE, CONF_LEFT): {CONF_EVENT: 3002}, + (CONF_LONG_PRESS, CONF_LEFT): {CONF_EVENT: 3001}, + (CONF_LONG_RELEASE, CONF_LEFT): {CONF_EVENT: 3003}, + (CONF_SHORT_RELEASE, CONF_RIGHT): {CONF_EVENT: 4002}, + (CONF_LONG_PRESS, CONF_RIGHT): {CONF_EVENT: 4001}, + (CONF_LONG_RELEASE, CONF_RIGHT): {CONF_EVENT: 4003}, } SYMFONISK_SOUND_CONTROLLER_MODEL = "SYMFONISK Sound Controller" @@ -582,6 +594,7 @@ REMOTES = { HUE_DIMMER_REMOTE_MODEL_GEN3: HUE_DIMMER_REMOTE, HUE_BUTTON_REMOTE_MODEL: HUE_BUTTON_REMOTE, HUE_TAP_REMOTE_MODEL: HUE_TAP_REMOTE, + HUE_WALL_REMOTE_MODEL: HUE_WALL_REMOTE, FRIENDS_OF_HUE_SWITCH_MODEL: FRIENDS_OF_HUE_SWITCH, STYRBAR_REMOTE_MODEL: STYRBAR_REMOTE, SYMFONISK_SOUND_CONTROLLER_MODEL: SYMFONISK_SOUND_CONTROLLER, diff --git a/homeassistant/components/deconz/logbook.py b/homeassistant/components/deconz/logbook.py index 65ed7f8e31d..67801b84344 100644 --- a/homeassistant/components/deconz/logbook.py +++ b/homeassistant/components/deconz/logbook.py @@ -4,7 +4,7 @@ from __future__ import annotations from collections.abc import Callable from homeassistant.components.logbook import LOGBOOK_ENTRY_MESSAGE, LOGBOOK_ENTRY_NAME -from homeassistant.const import ATTR_DEVICE_ID, CONF_EVENT +from homeassistant.const import ATTR_DEVICE_ID, CONF_EVENT, CONF_ID from homeassistant.core import Event, HomeAssistant, callback import homeassistant.helpers.device_registry as dr @@ -130,27 +130,34 @@ def async_describe_events( @callback def async_describe_deconz_alarm_event(event: Event) -> dict[str, str]: """Describe deCONZ logbook alarm event.""" - device = device_registry.devices[event.data[ATTR_DEVICE_ID]] - deconz_alarm_event = _get_deconz_event_from_device(hass, device) + if device := device_registry.devices.get(event.data[ATTR_DEVICE_ID]): + deconz_alarm_event = _get_deconz_event_from_device(hass, device) + name = deconz_alarm_event.device.name + else: + name = event.data[CONF_ID] data = event.data[CONF_EVENT] return { - LOGBOOK_ENTRY_NAME: f"{deconz_alarm_event.device.name}", + LOGBOOK_ENTRY_NAME: name, LOGBOOK_ENTRY_MESSAGE: f"fired event '{data}'", } @callback def async_describe_deconz_event(event: Event) -> dict[str, str]: """Describe deCONZ logbook event.""" - device = device_registry.devices[event.data[ATTR_DEVICE_ID]] - deconz_event = _get_deconz_event_from_device(hass, device) + if device := device_registry.devices.get(event.data[ATTR_DEVICE_ID]): + deconz_event = _get_deconz_event_from_device(hass, device) + name = deconz_event.device.name + else: + deconz_event = None + name = event.data[CONF_ID] action = None interface = None data = event.data.get(CONF_EVENT) or event.data.get(CONF_GESTURE, "") - if data and deconz_event.device.model_id in REMOTES: + if data and deconz_event and deconz_event.device.model_id in REMOTES: action, interface = _get_device_event_description( deconz_event.device.model_id, data ) @@ -158,27 +165,29 @@ def async_describe_events( # Unknown event if not data: return { - LOGBOOK_ENTRY_NAME: f"{deconz_event.device.name}", + LOGBOOK_ENTRY_NAME: name, LOGBOOK_ENTRY_MESSAGE: "fired an unknown event", } # No device event match if not action: return { - LOGBOOK_ENTRY_NAME: f"{deconz_event.device.name}", + LOGBOOK_ENTRY_NAME: name, LOGBOOK_ENTRY_MESSAGE: f"fired event '{data}'", } # Gesture event if not interface: return { - LOGBOOK_ENTRY_NAME: f"{deconz_event.device.name}", + LOGBOOK_ENTRY_NAME: name, LOGBOOK_ENTRY_MESSAGE: f"fired event '{ACTIONS[action]}'", } return { - LOGBOOK_ENTRY_NAME: f"{deconz_event.device.name}", - LOGBOOK_ENTRY_MESSAGE: f"'{ACTIONS[action]}' event for '{INTERFACES[interface]}' was fired", + LOGBOOK_ENTRY_NAME: name, + LOGBOOK_ENTRY_MESSAGE: ( + f"'{ACTIONS[action]}' event for '{INTERFACES[interface]}' was fired" + ), } async_describe_event( diff --git a/homeassistant/components/deconz/manifest.json b/homeassistant/components/deconz/manifest.json index 5de15b16177..2bf17cabbbf 100644 --- a/homeassistant/components/deconz/manifest.json +++ b/homeassistant/components/deconz/manifest.json @@ -3,7 +3,7 @@ "name": "deCONZ", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/deconz", - "requirements": ["pydeconz==105"], + "requirements": ["pydeconz==106"], "ssdp": [ { "manufacturer": "Royal Philips Electronics", diff --git a/homeassistant/components/deconz/sensor.py b/homeassistant/components/deconz/sensor.py index 6b47560b150..31a8a244b1a 100644 --- a/homeassistant/components/deconz/sensor.py +++ b/homeassistant/components/deconz/sensor.py @@ -34,12 +34,12 @@ from homeassistant.const import ( ATTR_TEMPERATURE, ATTR_VOLTAGE, CONCENTRATION_PARTS_PER_BILLION, - ENERGY_KILO_WATT_HOUR, LIGHT_LUX, PERCENTAGE, - POWER_WATT, - PRESSURE_HPA, - TEMP_CELSIUS, + UnitOfEnergy, + UnitOfPower, + UnitOfPressure, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import EntityCategory @@ -122,7 +122,6 @@ ENTITY_DESCRIPTIONS: tuple[DeconzSensorDescription, ...] = ( instance_check=AirQuality, name_suffix="PPB", old_unique_id_suffix="ppb", - device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS, state_class=SensorStateClass.MEASUREMENT, native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION, ), @@ -134,7 +133,7 @@ ENTITY_DESCRIPTIONS: tuple[DeconzSensorDescription, ...] = ( instance_check=Consumption, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, ), DeconzSensorDescription[Daylight]( key="daylight_status", @@ -180,7 +179,7 @@ ENTITY_DESCRIPTIONS: tuple[DeconzSensorDescription, ...] = ( instance_check=Power, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, ), DeconzSensorDescription[Pressure]( key="pressure", @@ -190,7 +189,7 @@ ENTITY_DESCRIPTIONS: tuple[DeconzSensorDescription, ...] = ( instance_check=Pressure, device_class=SensorDeviceClass.PRESSURE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=PRESSURE_HPA, + native_unit_of_measurement=UnitOfPressure.HPA, ), DeconzSensorDescription[Temperature]( key="temperature", @@ -200,7 +199,7 @@ ENTITY_DESCRIPTIONS: tuple[DeconzSensorDescription, ...] = ( instance_check=Temperature, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, ), DeconzSensorDescription[Time]( key="last_set", @@ -231,7 +230,7 @@ ENTITY_DESCRIPTIONS: tuple[DeconzSensorDescription, ...] = ( old_unique_id_suffix="temperature", device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, ), ) diff --git a/homeassistant/components/deconz/translations/lb.json b/homeassistant/components/deconz/translations/lb.json index b5bb383d29a..e8a2d451662 100644 --- a/homeassistant/components/deconz/translations/lb.json +++ b/homeassistant/components/deconz/translations/lb.json @@ -18,7 +18,7 @@ }, "link": { "description": "Entsperrt \u00e4r deCONZ gateway fir se mat Home Assistant ze registr\u00e9ieren.\n\n1. Gidd op deCONZ System Astellungen\n2. Dr\u00e9ckt \"Unlock\" Gateway Kn\u00e4ppchen", - "title": "Link mat deCONZ" + "title": "Mat deCONZ verbannen" }, "manual_input": { "data": { diff --git a/homeassistant/components/deconz/translations/sk.json b/homeassistant/components/deconz/translations/sk.json index ed0cc142a44..61886b254e8 100644 --- a/homeassistant/components/deconz/translations/sk.json +++ b/homeassistant/components/deconz/translations/sk.json @@ -2,7 +2,10 @@ "config": { "abort": { "already_configured": "Bridge je u\u017e nakonfigurovan\u00fd", - "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "no_bridges": "Nena\u0161li sa \u017eiadne deCONZ bridge", + "no_hardware_available": "\u017diadny r\u00e1diov\u00fd hardv\u00e9r pripojen\u00fd k deCONZ", + "updated_instance": "Aktualizovan\u00e1 in\u0161tancia deCONZ s novou adresou hostite\u013ea" }, "error": { "linking_not_possible": "Nepodarilo sa prepoji\u0165 s br\u00e1nou", @@ -11,9 +14,11 @@ "flow_title": "{host}", "step": { "hassio_confirm": { - "description": "Chcete nakonfigurova\u0165 Home Assistant na pripojenie k br\u00e1ne deCONZ poskytovanej doplnkom {addon}?" + "description": "Chcete nakonfigurova\u0165 Home Assistant na pripojenie k br\u00e1ne deCONZ poskytovanej doplnkom {addon}?", + "title": "Br\u00e1na deCONZ Zigbee prostredn\u00edctvom doplnku Home Assistant" }, "link": { + "description": "Odomknite svoju br\u00e1nu deCONZ a zaregistrujte sa v aplik\u00e1cii Home Assistant. \n\n 1. Cho\u010fte do deCONZ Settings - > Gateway - > Advanced\n 2. Stla\u010dte tla\u010didlo \u201eAutentifik\u00e1cia aplik\u00e1cie\u201c.", "title": "Prepojenie s deCONZ" }, "manual_input": { @@ -53,6 +58,7 @@ "side_4": "Strana 4", "side_5": "Strana 5", "side_6": "Strana 6", + "top_buttons": "Horn\u00e9 tla\u010didl\u00e1", "turn_off": "Vypn\u00fa\u0165", "turn_on": "Zapn\u00fa\u0165" }, @@ -63,11 +69,26 @@ "remote_button_long_release": "Tla\u010didlo \"{subtype}\" uvo\u013enen\u00e9 po dlhom stla\u010den\u00ed", "remote_button_quadruple_press": "Tla\u010didlo \"{subtype}\" kliknut\u00e9 \u0161tyrikr\u00e1t", "remote_button_quintuple_press": "Tla\u010didlo \"{subtype}\" kliknut\u00e9 p\u00e4\u0165kr\u00e1t", + "remote_button_rotated": "Oto\u010den\u00e9 tla\u010didlo \u201e{subtype}\u201c", + "remote_button_rotated_fast": "Tla\u010didlo sa r\u00fdchlo ot\u00e1\u010dalo \u201e{subtype}\u201c", + "remote_button_rotation_stopped": "Oto\u010denie tla\u010didla \"{subtype}\" bolo zastaven\u00e9", "remote_button_short_press": "Stla\u010den\u00e9 tla\u010didlo \"{subtype}\"", "remote_button_short_release": "Tla\u010didlo \"{subtype}\" bolo uvo\u013enen\u00e9", "remote_button_triple_press": "Trojklik na tla\u010didlo \"{subtype}\"", "remote_double_tap": "Zariadenie \"{subtype}\" dvojit\u00e9 klepnutie", "remote_double_tap_any_side": "Zariadenie dvakr\u00e1t klepnut\u00e9 na \u013eubovo\u013en\u00fa stranu", + "remote_falling": "Zariadenie vo vo\u013enom p\u00e1de", + "remote_flip_180_degrees": "Zariadenie oto\u010den\u00e9 o 180 stup\u0148ov", + "remote_flip_90_degrees": "Zariadenie oto\u010den\u00e9 o 90 stup\u0148ov", + "remote_gyro_activated": "Zariadenie sa zatriaslo", + "remote_moved": "Zariadenie presunut\u00e9 s \"{subtype}\" nahor", + "remote_moved_any_side": "Zariadenie sa pohybuje \u013eubovo\u013enou stranou nahor", + "remote_rotate_from_side_1": "Zariadenie oto\u010den\u00e9 zo \"strany 1\" na \"{subtype}\"", + "remote_rotate_from_side_2": "Zariadenie oto\u010den\u00e9 zo \"strany 2\" na \"{subtype}\"", + "remote_rotate_from_side_3": "Zariadenie oto\u010den\u00e9 zo \"strany 3\" na \"{subtype}\"", + "remote_rotate_from_side_4": "Zariadenie oto\u010den\u00e9 zo \"strany 4\" na \"{subtype}\"", + "remote_rotate_from_side_5": "Zariadenie oto\u010den\u00e9 zo \"strany 5\" na \"{subtype}\"", + "remote_rotate_from_side_6": "Zariadenie oto\u010den\u00e9 zo \"strany 6\" na \"{subtype}\"", "remote_turned_clockwise": "Zariadenie oto\u010den\u00e9 v smere hodinov\u00fdch ru\u010di\u010diek", "remote_turned_counter_clockwise": "Zariadenie oto\u010den\u00e9 proti smeru hodinov\u00fdch ru\u010di\u010diek" } @@ -76,8 +97,11 @@ "step": { "deconz_devices": { "data": { - "allow_clip_sensor": "Povoli\u0165 senzory deCONZ CLIP" + "allow_clip_sensor": "Povoli\u0165 senzory deCONZ CLIP", + "allow_deconz_groups": "Povoli\u0165 skupiny svetiel deCONZ", + "allow_new_devices": "Povoli\u0165 automatick\u00e9 prid\u00e1vanie nov\u00fdch zariaden\u00ed" }, + "description": "Nastavte vidite\u013enos\u0165 typov zariaden\u00ed deCONZ", "title": "mo\u017enosti deCONZ" } } diff --git a/homeassistant/components/deconz/translations/zh-Hant.json b/homeassistant/components/deconz/translations/zh-Hant.json index bd945ecc360..8987441fc9a 100644 --- a/homeassistant/components/deconz/translations/zh-Hant.json +++ b/homeassistant/components/deconz/translations/zh-Hant.json @@ -97,7 +97,7 @@ "step": { "deconz_devices": { "data": { - "allow_clip_sensor": "\u5141\u8a31 deCONZ CLIP \u611f\u61c9\u5668", + "allow_clip_sensor": "\u5141\u8a31 deCONZ CLIP \u611f\u6e2c\u5668", "allow_deconz_groups": "\u5141\u8a31 deCONZ \u71c8\u5149\u7fa4\u7d44", "allow_new_devices": "\u5141\u8a31\u81ea\u52d5\u5316\u65b0\u589e\u88dd\u7f6e" }, diff --git a/homeassistant/components/deluge/__init__.py b/homeassistant/components/deluge/__init__.py index 566b97b5b04..97605d08fe9 100644 --- a/homeassistant/components/deluge/__init__.py +++ b/homeassistant/components/deluge/__init__.py @@ -80,7 +80,9 @@ class DelugeEntity(CoordinatorEntity[DelugeDataUpdateCoordinator]): super().__init__(coordinator) self._server_unique_id = coordinator.config_entry.entry_id self._attr_device_info = DeviceInfo( - configuration_url=f"http://{coordinator.api.host}:{coordinator.api.web_port}", + configuration_url=( + f"http://{coordinator.api.host}:{coordinator.api.web_port}" + ), entry_type=DeviceEntryType.SERVICE, identifiers={(DOMAIN, coordinator.config_entry.entry_id)}, manufacturer=DEFAULT_NAME, diff --git a/homeassistant/components/deluge/sensor.py b/homeassistant/components/deluge/sensor.py index bcdca8b3d92..12b7ce0dd8d 100644 --- a/homeassistant/components/deluge/sensor.py +++ b/homeassistant/components/deluge/sensor.py @@ -6,12 +6,13 @@ from dataclasses import dataclass from typing import Any from homeassistant.components.sensor import ( + SensorDeviceClass, SensorEntity, SensorEntityDescription, SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import DATA_RATE_KILOBYTES_PER_SECOND, STATE_IDLE, Platform +from homeassistant.const import STATE_IDLE, Platform, UnitOfDataRate from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_platform from homeassistant.helpers.typing import StateType @@ -53,14 +54,16 @@ SENSOR_TYPES: tuple[DelugeSensorEntityDescription, ...] = ( DelugeSensorEntityDescription( key=DOWNLOAD_SPEED, name="Down speed", - native_unit_of_measurement=DATA_RATE_KILOBYTES_PER_SECOND, + device_class=SensorDeviceClass.DATA_RATE, + native_unit_of_measurement=UnitOfDataRate.KILOBYTES_PER_SECOND, state_class=SensorStateClass.MEASUREMENT, value=lambda data: get_state(data, DOWNLOAD_SPEED), ), DelugeSensorEntityDescription( key=UPLOAD_SPEED, name="Up speed", - native_unit_of_measurement=DATA_RATE_KILOBYTES_PER_SECOND, + device_class=SensorDeviceClass.DATA_RATE, + native_unit_of_measurement=UnitOfDataRate.KILOBYTES_PER_SECOND, state_class=SensorStateClass.MEASUREMENT, value=lambda data: get_state(data, UPLOAD_SPEED), ), diff --git a/homeassistant/components/deluge/translations/ko.json b/homeassistant/components/deluge/translations/ko.json new file mode 100644 index 00000000000..fd10005f7c9 --- /dev/null +++ b/homeassistant/components/deluge/translations/ko.json @@ -0,0 +1,21 @@ +{ + "config": { + "abort": { + "already_configured": "\uc11c\ube44\uc2a4\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8", + "password": "\ube44\ubc00\ubc88\ud638", + "port": "\ud3ec\ud2b8", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/deluge/translations/pt.json b/homeassistant/components/deluge/translations/pt.json index fb1af357526..1b624677aa7 100644 --- a/homeassistant/components/deluge/translations/pt.json +++ b/homeassistant/components/deluge/translations/pt.json @@ -9,7 +9,7 @@ "step": { "user": { "data": { - "host": "Servidor", + "host": "Endere\u00e7o", "username": "Nome de Utilizador" } } diff --git a/homeassistant/components/deluge/translations/sk.json b/homeassistant/components/deluge/translations/sk.json index d2976f03929..75969b61c95 100644 --- a/homeassistant/components/deluge/translations/sk.json +++ b/homeassistant/components/deluge/translations/sk.json @@ -15,7 +15,8 @@ "port": "Port", "username": "U\u017e\u00edvate\u013esk\u00e9 meno", "web_port": "Webov\u00fd port (pre n\u00e1v\u0161tevu slu\u017eby)" - } + }, + "description": "Aby ste mohli pou\u017e\u00edva\u0165 t\u00fato integr\u00e1ciu, mus\u00edte povoli\u0165 nasleduj\u00facu mo\u017enos\u0165 v nastaveniach z\u00e1plavy: D\u00e9mon > Povoli\u0165 dia\u013ekov\u00e9 ovl\u00e1danie" } } } diff --git a/homeassistant/components/demo/__init__.py b/homeassistant/components/demo/__init__.py index ae6912fa0f9..d4c07cfa730 100644 --- a/homeassistant/components/demo/__init__.py +++ b/homeassistant/components/demo/__init__.py @@ -16,14 +16,12 @@ from homeassistant.components.recorder.statistics import ( from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( ATTR_ENTITY_ID, - ENERGY_KILO_WATT_HOUR, - ENERGY_MEGA_WATT_HOUR, EVENT_HOMEASSISTANT_START, - SOUND_PRESSURE_DB, - TEMP_CELSIUS, - VOLUME_CUBIC_FEET, - VOLUME_CUBIC_METERS, Platform, + UnitOfEnergy, + UnitOfSoundPressure, + UnitOfTemperature, + UnitOfVolume, ) import homeassistant.core as ha from homeassistant.core import Event, HomeAssistant @@ -163,7 +161,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: "min": 0, "max": 10, "name": "Allowed Noise", - "unit_of_measurement": SOUND_PRESSURE_DB, + "unit_of_measurement": UnitOfSoundPressure.DECIBEL, } } }, @@ -306,7 +304,7 @@ async def _insert_statistics(hass: HomeAssistant) -> None: "source": DOMAIN, "name": "Outdoor temperature", "statistic_id": f"{DOMAIN}:temperature_outdoor", - "unit_of_measurement": TEMP_CELSIUS, + "unit_of_measurement": UnitOfTemperature.CELSIUS, "has_mean": True, "has_sum": False, } @@ -319,7 +317,7 @@ async def _insert_statistics(hass: HomeAssistant) -> None: "source": DOMAIN, "name": "Energy consumption 1", "statistic_id": f"{DOMAIN}:energy_consumption_kwh", - "unit_of_measurement": ENERGY_KILO_WATT_HOUR, + "unit_of_measurement": UnitOfEnergy.KILO_WATT_HOUR, "has_mean": False, "has_sum": True, } @@ -331,7 +329,7 @@ async def _insert_statistics(hass: HomeAssistant) -> None: "source": DOMAIN, "name": "Energy consumption 2", "statistic_id": f"{DOMAIN}:energy_consumption_mwh", - "unit_of_measurement": ENERGY_MEGA_WATT_HOUR, + "unit_of_measurement": UnitOfEnergy.MEGA_WATT_HOUR, "has_mean": False, "has_sum": True, } @@ -345,7 +343,7 @@ async def _insert_statistics(hass: HomeAssistant) -> None: "source": DOMAIN, "name": "Gas consumption 1", "statistic_id": f"{DOMAIN}:gas_consumption_m3", - "unit_of_measurement": VOLUME_CUBIC_METERS, + "unit_of_measurement": UnitOfVolume.CUBIC_METERS, "has_mean": False, "has_sum": True, } @@ -359,7 +357,7 @@ async def _insert_statistics(hass: HomeAssistant) -> None: "source": DOMAIN, "name": "Gas consumption 2", "statistic_id": f"{DOMAIN}:gas_consumption_ft3", - "unit_of_measurement": VOLUME_CUBIC_FEET, + "unit_of_measurement": UnitOfVolume.CUBIC_FEET, "has_mean": False, "has_sum": True, } diff --git a/homeassistant/components/demo/climate.py b/homeassistant/components/demo/climate.py index 00e049c6034..7c0a4a5c9c8 100644 --- a/homeassistant/components/demo/climate.py +++ b/homeassistant/components/demo/climate.py @@ -12,7 +12,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS, TEMP_FAHRENHEIT +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -36,7 +36,7 @@ async def async_setup_platform( unique_id="climate_1", name="HeatPump", target_temperature=68, - unit_of_measurement=TEMP_FAHRENHEIT, + unit_of_measurement=UnitOfTemperature.FAHRENHEIT, preset=None, current_temperature=77, fan_mode=None, @@ -54,7 +54,7 @@ async def async_setup_platform( unique_id="climate_2", name="Hvac", target_temperature=21, - unit_of_measurement=TEMP_CELSIUS, + unit_of_measurement=UnitOfTemperature.CELSIUS, preset=None, current_temperature=22, fan_mode="On High", @@ -72,7 +72,7 @@ async def async_setup_platform( unique_id="climate_3", name="Ecobee", target_temperature=None, - unit_of_measurement=TEMP_CELSIUS, + unit_of_measurement=UnitOfTemperature.CELSIUS, preset="home", preset_modes=["home", "eco"], current_temperature=23, @@ -104,6 +104,7 @@ class DemoClimate(ClimateEntity): """Representation of a demo climate device.""" _attr_should_poll = False + _attr_translation_key = "ubercool" def __init__( self, @@ -157,9 +158,9 @@ class DemoClimate(ClimateEntity): self._hvac_mode = hvac_mode self._aux = aux self._current_swing_mode = swing_mode - self._fan_modes = ["On Low", "On High", "Auto Low", "Auto High", "Off"] + self._fan_modes = ["on_low", "on_high", "auto_low", "auto_high", "off"] self._hvac_modes = hvac_modes - self._swing_modes = ["Auto", "1", "2", "3", "Off"] + self._swing_modes = ["auto", "1", "2", "3", "off"] self._target_temperature_high = target_temp_high self._target_temperature_low = target_temp_low diff --git a/homeassistant/components/demo/geo_location.py b/homeassistant/components/demo/geo_location.py index c47f4e49d4a..cc29205b720 100644 --- a/homeassistant/components/demo/geo_location.py +++ b/homeassistant/components/demo/geo_location.py @@ -7,7 +7,7 @@ from math import cos, pi, radians, sin import random from homeassistant.components.geo_location import GeolocationEvent -from homeassistant.const import LENGTH_KILOMETERS +from homeassistant.const import UnitOfLength from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.event import track_time_interval @@ -80,7 +80,7 @@ class DemoManager: event_name = random.choice(EVENT_NAMES) return DemoGeolocationEvent( - event_name, radius_in_km, latitude, longitude, LENGTH_KILOMETERS + event_name, radius_in_km, latitude, longitude, UnitOfLength.KILOMETERS ) def _init_regular_updates(self) -> None: diff --git a/homeassistant/components/demo/number.py b/homeassistant/components/demo/number.py index 9613aa247fb..25ed7347bda 100644 --- a/homeassistant/components/demo/number.py +++ b/homeassistant/components/demo/number.py @@ -3,7 +3,7 @@ from __future__ import annotations from homeassistant.components.number import NumberDeviceClass, NumberEntity, NumberMode from homeassistant.config_entries import ConfigEntry -from homeassistant.const import DEVICE_DEFAULT_NAME, TEMP_CELSIUS +from homeassistant.const import DEVICE_DEFAULT_NAME, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -71,7 +71,7 @@ async def async_setup_platform( native_max_value=35.0, native_step=1, mode=NumberMode.BOX, - unit_of_measurement=TEMP_CELSIUS, + unit_of_measurement=UnitOfTemperature.CELSIUS, ), ] ) diff --git a/homeassistant/components/demo/repairs.py b/homeassistant/components/demo/repairs.py index 1ea00374457..41db200dd72 100644 --- a/homeassistant/components/demo/repairs.py +++ b/homeassistant/components/demo/repairs.py @@ -24,7 +24,7 @@ class DemoFixFlow(RepairsFlow): ) -> data_entry_flow.FlowResult: """Handle the confirm step of a fix flow.""" if user_input is not None: - return self.async_create_entry(title="", data={}) + return self.async_create_entry(data={}) return self.async_show_form(step_id="confirm", data_schema=vol.Schema({})) diff --git a/homeassistant/components/demo/select.py b/homeassistant/components/demo/select.py index 683008abdbc..e30d65c9f0e 100644 --- a/homeassistant/components/demo/select.py +++ b/homeassistant/components/demo/select.py @@ -25,13 +25,13 @@ async def async_setup_platform( unique_id="speed", name="Speed", icon="mdi:speedometer", - device_class="demo__speed", current_option="ridiculous_speed", options=[ "light_speed", "ridiculous_speed", "ludicrous_speed", ], + translation_key="speed", ), ] ) @@ -56,17 +56,17 @@ class DemoSelect(SelectEntity): unique_id: str, name: str, icon: str, - device_class: str | None, current_option: str | None, options: list[str], + translation_key: str, ) -> None: """Initialize the Demo select entity.""" self._attr_unique_id = unique_id self._attr_name = name or DEVICE_DEFAULT_NAME self._attr_current_option = current_option self._attr_icon = icon - self._attr_device_class = device_class self._attr_options = options + self._attr_translation_key = translation_key self._attr_device_info = DeviceInfo( identifiers={(DOMAIN, unique_id)}, name=name, diff --git a/homeassistant/components/demo/sensor.py b/homeassistant/components/demo/sensor.py index 1adc8616593..67a7b346a3e 100644 --- a/homeassistant/components/demo/sensor.py +++ b/homeassistant/components/demo/sensor.py @@ -15,13 +15,11 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( ATTR_BATTERY_LEVEL, CONCENTRATION_PARTS_PER_MILLION, - ENERGY_KILO_WATT_HOUR, - ENERGY_MEGA_WATT_HOUR, PERCENTAGE, - POWER_WATT, - TEMP_CELSIUS, - VOLUME_CUBIC_FEET, - VOLUME_CUBIC_METERS, + UnitOfEnergy, + UnitOfPower, + UnitOfTemperature, + UnitOfVolume, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import DeviceInfo @@ -47,7 +45,7 @@ async def async_setup_platform( 15.6, SensorDeviceClass.TEMPERATURE, SensorStateClass.MEASUREMENT, - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, 12, ), DemoSensor( @@ -83,7 +81,7 @@ async def async_setup_platform( 100, SensorDeviceClass.POWER, SensorStateClass.MEASUREMENT, - POWER_WATT, + UnitOfPower.WATT, None, ), DemoSumSensor( @@ -92,7 +90,7 @@ async def async_setup_platform( 0.5, # 6kWh / h SensorDeviceClass.ENERGY, SensorStateClass.TOTAL, - ENERGY_KILO_WATT_HOUR, + UnitOfEnergy.KILO_WATT_HOUR, None, "total_energy_kwh", ), @@ -102,7 +100,7 @@ async def async_setup_platform( 0.00025, # 0.003 MWh/h (3 kWh / h) SensorDeviceClass.ENERGY, SensorStateClass.TOTAL, - ENERGY_MEGA_WATT_HOUR, + UnitOfEnergy.MEGA_WATT_HOUR, None, "total_energy_mwh", ), @@ -112,7 +110,7 @@ async def async_setup_platform( 0.025, # 0.30 m³/h (10.6 ft³ / h) SensorDeviceClass.GAS, SensorStateClass.TOTAL, - VOLUME_CUBIC_METERS, + UnitOfVolume.CUBIC_METERS, None, "total_gas_m3", ), @@ -122,10 +120,21 @@ async def async_setup_platform( 1.0, # 12 ft³/h (0.34 m³ / h) SensorDeviceClass.GAS, SensorStateClass.TOTAL, - VOLUME_CUBIC_FEET, + UnitOfVolume.CUBIC_FEET, None, "total_gas_ft3", ), + DemoSensor( + unique_id="sensor_10", + name="Thermostat mode", + state="eco", + device_class=SensorDeviceClass.ENUM, + state_class=None, + unit_of_measurement=None, + battery=None, + options=["away", "comfort", "eco", "sleep"], + translation_key="thermostat_mode", + ), ] ) @@ -153,6 +162,8 @@ class DemoSensor(SensorEntity): state_class: SensorStateClass | None, unit_of_measurement: str | None, battery: StateType, + options: list[str] | None = None, + translation_key: str | None = None, ) -> None: """Initialize the sensor.""" self._attr_device_class = device_class @@ -161,6 +172,8 @@ class DemoSensor(SensorEntity): self._attr_native_value = state self._attr_state_class = state_class self._attr_unique_id = unique_id + self._attr_options = options + self._attr_translation_key = translation_key self._attr_device_info = DeviceInfo( identifiers={(DOMAIN, unique_id)}, diff --git a/homeassistant/components/demo/strings.json b/homeassistant/components/demo/strings.json index 7be1a133a74..286bd5d0937 100644 --- a/homeassistant/components/demo/strings.json +++ b/homeassistant/components/demo/strings.json @@ -61,5 +61,58 @@ } } } + }, + "entity": { + "climate": { + "ubercool": { + "state_attributes": { + "fan_mode": { + "state": { + "auto_high": "Auto High", + "auto_low": "Auto Low", + "on_high": "On High", + "on_low": "On Low" + } + }, + "swing_mode": { + "state": { + "1": "1", + "2": "2", + "3": "3", + "auto": "Auto", + "off": "Off" + } + } + } + } + }, + "select": { + "speed": { + "state": { + "light_speed": "Light Speed", + "ludicrous_speed": "Ludicrous Speed", + "ridiculous_speed": "Ridiculous Speed" + } + } + }, + "sensor": { + "thermostat_mode": { + "state": { + "away": "Away", + "comfort": "Comfort", + "eco": "Eco", + "sleep": "Sleep" + } + } + }, + "vacuum": { + "model_s": { + "state_attributes": { + "cleaned_area": { + "name": "Cleaned Area" + } + } + } + } } } diff --git a/homeassistant/components/demo/strings.select.json b/homeassistant/components/demo/strings.select.json deleted file mode 100644 index f797ab562bc..00000000000 --- a/homeassistant/components/demo/strings.select.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "state": { - "demo__speed": { - "light_speed": "Light Speed", - "ludicrous_speed": "Ludicrous Speed", - "ridiculous_speed": "Ridiculous Speed" - } - } -} diff --git a/homeassistant/components/demo/translations/bg.json b/homeassistant/components/demo/translations/bg.json index 98f28e6d881..c06270f57c9 100644 --- a/homeassistant/components/demo/translations/bg.json +++ b/homeassistant/components/demo/translations/bg.json @@ -1,4 +1,31 @@ { + "entity": { + "climate": { + "ubercool": { + "state_attributes": { + "swing_mode": { + "state": { + "1": "1", + "2": "2", + "3": "3", + "auto": "\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u043d", + "off": "\u0418\u0437\u043a\u043b." + } + } + } + } + }, + "sensor": { + "thermostat_mode": { + "state": { + "away": "\u041e\u0442\u0441\u044a\u0441\u0442\u0432\u0430", + "comfort": "\u041a\u043e\u043c\u0444\u043e\u0440\u0442", + "eco": "\u0415\u043a\u043e", + "sleep": "\u0421\u044a\u043d" + } + } + } + }, "issues": { "bad_psu": { "fix_flow": { diff --git a/homeassistant/components/demo/translations/ca.json b/homeassistant/components/demo/translations/ca.json index 5a126471bf4..44c1ff5c368 100644 --- a/homeassistant/components/demo/translations/ca.json +++ b/homeassistant/components/demo/translations/ca.json @@ -1,4 +1,57 @@ { + "entity": { + "climate": { + "ubercool": { + "state_attributes": { + "fan_mode": { + "state": { + "auto_high": "Autom\u00e0tic alt", + "auto_low": "Autom\u00e0tic baix", + "on_high": "ON alt", + "on_low": "ON baix" + } + }, + "swing_mode": { + "state": { + "1": "1", + "2": "2", + "3": "3", + "auto": "Autom\u00e0tic", + "off": "OFF" + } + } + } + } + }, + "select": { + "speed": { + "state": { + "light_speed": "Velocitat de la llum", + "ludicrous_speed": "Velocitat Ludicrous", + "ridiculous_speed": "Velocitat rid\u00edcula" + } + } + }, + "sensor": { + "thermostat_mode": { + "state": { + "away": "A fora", + "comfort": "Confort", + "eco": "Eco", + "sleep": "Dormint" + } + } + }, + "vacuum": { + "model_s": { + "state_attributes": { + "cleaned_area": { + "name": "\u00c0rea netejada" + } + } + } + } + }, "issues": { "bad_psu": { "fix_flow": { diff --git a/homeassistant/components/demo/translations/de.json b/homeassistant/components/demo/translations/de.json index 0318ba7e0cf..1deb5d79e51 100644 --- a/homeassistant/components/demo/translations/de.json +++ b/homeassistant/components/demo/translations/de.json @@ -1,4 +1,57 @@ { + "entity": { + "climate": { + "ubercool": { + "state_attributes": { + "fan_mode": { + "state": { + "auto_high": "Auto hoch", + "auto_low": "Auto Niedrig", + "on_high": "Auf Hoch", + "on_low": "Auf Niedrig" + } + }, + "swing_mode": { + "state": { + "1": "1", + "2": "2", + "3": "3", + "auto": "Auto", + "off": "Aus" + } + } + } + } + }, + "select": { + "speed": { + "state": { + "light_speed": "Lichtgeschwindigkeit", + "ludicrous_speed": "Wahnsinnige Geschwindigkeit", + "ridiculous_speed": "L\u00e4cherliche Geschwindigkeit" + } + } + }, + "sensor": { + "thermostat_mode": { + "state": { + "away": "Abwesend", + "comfort": "Komfort", + "eco": "Eco", + "sleep": "Schlafen" + } + } + }, + "vacuum": { + "model_s": { + "state_attributes": { + "cleaned_area": { + "name": "Gereinigter Bereich" + } + } + } + } + }, "issues": { "bad_psu": { "fix_flow": { diff --git a/homeassistant/components/demo/translations/el.json b/homeassistant/components/demo/translations/el.json index cb47f52ed42..94d3048e5b9 100644 --- a/homeassistant/components/demo/translations/el.json +++ b/homeassistant/components/demo/translations/el.json @@ -1,4 +1,57 @@ { + "entity": { + "climate": { + "ubercool": { + "state_attributes": { + "fan_mode": { + "state": { + "auto_high": "\u0391\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03bf \u03c5\u03c8\u03b7\u03bb\u03cc", + "auto_low": "\u0391\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03bf \u03c7\u03b1\u03bc\u03b7\u03bb\u03cc", + "on_high": "\u0395\u03bd\u03b5\u03c1\u03b3\u03cc \u03c5\u03c8\u03b7\u03bb\u03cc", + "on_low": "\u0395\u03bd\u03b5\u03c1\u03b3\u03cc \u03c7\u03b1\u03bc\u03b7\u03bb\u03cc" + } + }, + "swing_mode": { + "state": { + "1": "1", + "2": "2", + "3": "3", + "auto": "\u0391\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03bf", + "off": "\u0391\u03bd\u03b5\u03bd\u03b5\u03c1\u03b3\u03cc" + } + } + } + } + }, + "select": { + "speed": { + "state": { + "light_speed": "\u03a4\u03b1\u03c7\u03cd\u03c4\u03b7\u03c4\u03b1 \u03c6\u03c9\u03c4\u03cc\u03c2", + "ludicrous_speed": "\u039b\u03c5\u03c3\u03c3\u03b1\u03bb\u03ad\u03b1 \u03c4\u03b1\u03c7\u03cd\u03c4\u03b7\u03c4\u03b1", + "ridiculous_speed": "\u0393\u03b5\u03bb\u03bf\u03af\u03b1 \u03a4\u03b1\u03c7\u03cd\u03c4\u03b7\u03c4\u03b1" + } + } + }, + "sensor": { + "thermostat_mode": { + "state": { + "away": "\u0395\u03ba\u03c4\u03cc\u03c2 \u03c3\u03c0\u03b9\u03c4\u03b9\u03bf\u03cd", + "comfort": "\u0386\u03bd\u03b5\u03c3\u03b7", + "eco": "Eco", + "sleep": "\u038e\u03c0\u03bd\u03bf\u03c2" + } + } + }, + "vacuum": { + "model_s": { + "state_attributes": { + "cleaned_area": { + "name": "\u039a\u03b1\u03b8\u03b1\u03c1\u03b9\u03c3\u03bc\u03ad\u03bd\u03b7 \u03c0\u03b5\u03c1\u03b9\u03bf\u03c7\u03ae" + } + } + } + } + }, "issues": { "bad_psu": { "fix_flow": { diff --git a/homeassistant/components/demo/translations/en.json b/homeassistant/components/demo/translations/en.json index 9f32e982947..ea5da9f5f1a 100644 --- a/homeassistant/components/demo/translations/en.json +++ b/homeassistant/components/demo/translations/en.json @@ -1,4 +1,57 @@ { + "entity": { + "climate": { + "ubercool": { + "state_attributes": { + "fan_mode": { + "state": { + "auto_high": "Auto High", + "auto_low": "Auto Low", + "on_high": "On High", + "on_low": "On Low" + } + }, + "swing_mode": { + "state": { + "1": "1", + "2": "2", + "3": "3", + "auto": "Auto", + "off": "Off" + } + } + } + } + }, + "select": { + "speed": { + "state": { + "light_speed": "Light Speed", + "ludicrous_speed": "Ludicrous Speed", + "ridiculous_speed": "Ridiculous Speed" + } + } + }, + "sensor": { + "thermostat_mode": { + "state": { + "away": "Away", + "comfort": "Comfort", + "eco": "Eco", + "sleep": "Sleep" + } + } + }, + "vacuum": { + "model_s": { + "state_attributes": { + "cleaned_area": { + "name": "Cleaned Area" + } + } + } + } + }, "issues": { "bad_psu": { "fix_flow": { diff --git a/homeassistant/components/demo/translations/es.json b/homeassistant/components/demo/translations/es.json index 70fc94480a7..1c790837980 100644 --- a/homeassistant/components/demo/translations/es.json +++ b/homeassistant/components/demo/translations/es.json @@ -1,4 +1,57 @@ { + "entity": { + "climate": { + "ubercool": { + "state_attributes": { + "fan_mode": { + "state": { + "auto_high": "Alto autom\u00e1tico", + "auto_low": "Bajo autom\u00e1tico", + "on_high": "En alto", + "on_low": "En bajo" + } + }, + "swing_mode": { + "state": { + "1": "1", + "2": "2", + "3": "3", + "auto": "Autom\u00e1tico", + "off": "Apagado" + } + } + } + } + }, + "select": { + "speed": { + "state": { + "light_speed": "Velocidad de la luz", + "ludicrous_speed": "Velocidad ludicrous", + "ridiculous_speed": "Velocidad rid\u00edcula" + } + } + }, + "sensor": { + "thermostat_mode": { + "state": { + "away": "Ausente", + "comfort": "Confort", + "eco": "Eco", + "sleep": "Dormir" + } + } + }, + "vacuum": { + "model_s": { + "state_attributes": { + "cleaned_area": { + "name": "\u00c1rea Limpiada" + } + } + } + } + }, "issues": { "bad_psu": { "fix_flow": { diff --git a/homeassistant/components/demo/translations/et.json b/homeassistant/components/demo/translations/et.json index 9cf984ccdd8..26a6ee2a574 100644 --- a/homeassistant/components/demo/translations/et.json +++ b/homeassistant/components/demo/translations/et.json @@ -1,4 +1,57 @@ { + "entity": { + "climate": { + "ubercool": { + "state_attributes": { + "fan_mode": { + "state": { + "auto_high": "Automaatne k\u00f5rge", + "auto_low": "Automaatne madal", + "on_high": "K\u00f5rge", + "on_low": "Madal" + } + }, + "swing_mode": { + "state": { + "1": "1", + "2": "2", + "3": "3", + "auto": "Auto", + "off": "V\u00e4ljas" + } + } + } + } + }, + "select": { + "speed": { + "state": { + "light_speed": "Valguse kiirus", + "ludicrous_speed": "Meeletu kiirus", + "ridiculous_speed": "Naeruv\u00e4\u00e4rne kiirus" + } + } + }, + "sensor": { + "thermostat_mode": { + "state": { + "away": "Eemal", + "comfort": "Mugav", + "eco": "\u00d6ko", + "sleep": "Uneaeg" + } + } + }, + "vacuum": { + "model_s": { + "state_attributes": { + "cleaned_area": { + "name": "Puhastatud ala" + } + } + } + } + }, "issues": { "bad_psu": { "fix_flow": { diff --git a/homeassistant/components/demo/translations/hu.json b/homeassistant/components/demo/translations/hu.json index ece6bad6bfd..1fb950b323c 100644 --- a/homeassistant/components/demo/translations/hu.json +++ b/homeassistant/components/demo/translations/hu.json @@ -1,4 +1,57 @@ { + "entity": { + "climate": { + "ubercool": { + "state_attributes": { + "fan_mode": { + "state": { + "auto_high": "Auto Magas", + "auto_low": "Auto Alacsony", + "on_high": "Be Maga", + "on_low": "Be Alacsony" + } + }, + "swing_mode": { + "state": { + "1": "1", + "2": "2", + "3": "3", + "auto": "Automatikus", + "off": "Ki" + } + } + } + } + }, + "select": { + "speed": { + "state": { + "light_speed": "F\u00e9nysebess\u00e9g", + "ludicrous_speed": "Hihetetlen sebess\u00e9g", + "ridiculous_speed": "K\u00e9ptelen sebess\u00e9g" + } + } + }, + "sensor": { + "thermostat_mode": { + "state": { + "away": "T\u00e1vol", + "comfort": "Komfort", + "eco": "Takar\u00e9kos", + "sleep": "Alv\u00e1s" + } + } + }, + "vacuum": { + "model_s": { + "state_attributes": { + "cleaned_area": { + "name": "Tiszt\u00edtott ter\u00fclet" + } + } + } + } + }, "issues": { "bad_psu": { "fix_flow": { @@ -11,6 +64,18 @@ }, "title": "A t\u00e1pegys\u00e9g nem m\u0171k\u00f6dik megb\u00edzhat\u00f3 m\u00f3don" }, + "cold_tea": { + "fix_flow": { + "abort": { + "not_tea_time": "A te\u00e1t jelenleg nem lehet \u00fajra felmeleg\u00edteni." + }, + "step": { + "one": "\u00dcres", + "other": "\u00dcres" + } + }, + "title": "A tea hideg" + }, "out_of_blinker_fluid": { "fix_flow": { "step": { diff --git a/homeassistant/components/demo/translations/id.json b/homeassistant/components/demo/translations/id.json index 08594a7353b..f0585f9ed7a 100644 --- a/homeassistant/components/demo/translations/id.json +++ b/homeassistant/components/demo/translations/id.json @@ -1,4 +1,57 @@ { + "entity": { + "climate": { + "ubercool": { + "state_attributes": { + "fan_mode": { + "state": { + "auto_high": "Tinggi Otomatis", + "auto_low": "Rendah Otomatis", + "on_high": "Nyala Tinggi", + "on_low": "Nyala Rendah" + } + }, + "swing_mode": { + "state": { + "1": "1", + "2": "2", + "3": "3", + "auto": "Otomatis", + "off": "Mati" + } + } + } + } + }, + "select": { + "speed": { + "state": { + "light_speed": "Kecepatan Cahaya", + "ludicrous_speed": "Kecepatan Menggelikan", + "ridiculous_speed": "Kecepatan Konyol" + } + } + }, + "sensor": { + "thermostat_mode": { + "state": { + "away": "Keluar", + "comfort": "Nyaman", + "eco": "Eco", + "sleep": "Tidur" + } + } + }, + "vacuum": { + "model_s": { + "state_attributes": { + "cleaned_area": { + "name": "Area yang Dibersihkan" + } + } + } + } + }, "issues": { "bad_psu": { "fix_flow": { diff --git a/homeassistant/components/demo/translations/it.json b/homeassistant/components/demo/translations/it.json index df45eb12982..5fe730a6ba2 100644 --- a/homeassistant/components/demo/translations/it.json +++ b/homeassistant/components/demo/translations/it.json @@ -1,4 +1,57 @@ { + "entity": { + "climate": { + "ubercool": { + "state_attributes": { + "fan_mode": { + "state": { + "auto_high": "Automatico Alto", + "auto_low": "Automatico Basso", + "on_high": "Acceso Alto", + "on_low": "Acceso Basso" + } + }, + "swing_mode": { + "state": { + "1": "1", + "2": "2", + "3": "3", + "auto": "Automatico", + "off": "Spento" + } + } + } + } + }, + "select": { + "speed": { + "state": { + "light_speed": "Velocit\u00e0 della luce", + "ludicrous_speed": "Velocit\u00e0 assurda", + "ridiculous_speed": "Velocit\u00e0 ridicola" + } + } + }, + "sensor": { + "thermostat_mode": { + "state": { + "away": "Fuori Casa", + "comfort": "Comfort", + "eco": "Eco", + "sleep": "Sonno" + } + } + }, + "vacuum": { + "model_s": { + "state_attributes": { + "cleaned_area": { + "name": "Area pulita" + } + } + } + } + }, "issues": { "bad_psu": { "fix_flow": { diff --git a/homeassistant/components/demo/translations/lb.json b/homeassistant/components/demo/translations/lb.json index bfb094c00f9..f1e146c519b 100644 --- a/homeassistant/components/demo/translations/lb.json +++ b/homeassistant/components/demo/translations/lb.json @@ -1,4 +1,29 @@ { + "entity": { + "climate": { + "ubercool": { + "state_attributes": { + "fan_mode": { + "state": { + "auto_high": "Auto h\u00e9ich", + "auto_low": "Auto niddereg", + "on_high": "Un H\u00e9ich", + "on_low": "Un Niddereg" + } + }, + "swing_mode": { + "state": { + "1": "", + "2": "", + "3": "", + "auto": "", + "off": "Aus" + } + } + } + } + } + }, "options": { "step": { "options_1": { diff --git a/homeassistant/components/demo/translations/nl.json b/homeassistant/components/demo/translations/nl.json index c1a0cf5590b..3bffdfe51bb 100644 --- a/homeassistant/components/demo/translations/nl.json +++ b/homeassistant/components/demo/translations/nl.json @@ -1,4 +1,38 @@ { + "entity": { + "climate": { + "ubercool": { + "state_attributes": { + "fan_mode": { + "state": { + "auto_high": "Auto-hoog", + "auto_low": "Auto-laag", + "on_high": "Hoog", + "on_low": "Laag" + } + }, + "swing_mode": { + "state": { + "1": "1", + "2": "2", + "3": "3", + "auto": "Auto", + "off": "Uit" + } + } + } + } + }, + "vacuum": { + "model_s": { + "state_attributes": { + "cleaned_area": { + "name": "Schoongemaakt gebied" + } + } + } + } + }, "issues": { "bad_psu": { "fix_flow": { diff --git a/homeassistant/components/demo/translations/no.json b/homeassistant/components/demo/translations/no.json index 680ddc11288..1eb49b2b64d 100644 --- a/homeassistant/components/demo/translations/no.json +++ b/homeassistant/components/demo/translations/no.json @@ -1,4 +1,57 @@ { + "entity": { + "climate": { + "ubercool": { + "state_attributes": { + "fan_mode": { + "state": { + "auto_high": "Auto h\u00f8y", + "auto_low": "Auto lav", + "on_high": "P\u00e5 H\u00f8yt", + "on_low": "P\u00e5 lavt" + } + }, + "swing_mode": { + "state": { + "1": "1", + "2": "2", + "3": "3", + "auto": "Auto", + "off": "Av" + } + } + } + } + }, + "select": { + "speed": { + "state": { + "light_speed": "Lyshastighet", + "ludicrous_speed": "Latterlig hastighet", + "ridiculous_speed": "Latterlig hastighet" + } + } + }, + "sensor": { + "thermostat_mode": { + "state": { + "away": "Borte", + "comfort": "Komfort", + "eco": "\u00d8ko", + "sleep": "Sove" + } + } + }, + "vacuum": { + "model_s": { + "state_attributes": { + "cleaned_area": { + "name": "Rengjort omr\u00e5de" + } + } + } + } + }, "issues": { "bad_psu": { "fix_flow": { diff --git a/homeassistant/components/demo/translations/pl.json b/homeassistant/components/demo/translations/pl.json index 6fa5f2509c4..d42e9e256bd 100644 --- a/homeassistant/components/demo/translations/pl.json +++ b/homeassistant/components/demo/translations/pl.json @@ -1,4 +1,25 @@ { + "entity": { + "select": { + "speed": { + "state": { + "light_speed": "pr\u0119dko\u015b\u0107 \u015bwiat\u0142a", + "ludicrous_speed": "absurdalna pr\u0119dko\u015b\u0107", + "ridiculous_speed": "niewiarygodna pr\u0119dko\u015b\u0107" + } + } + }, + "sensor": { + "thermostat_mode": { + "state": { + "away": "poza domem", + "comfort": "komfortowo", + "eco": "Eco", + "sleep": "noc" + } + } + } + }, "issues": { "bad_psu": { "fix_flow": { @@ -11,6 +32,14 @@ }, "title": "Zasilacz nie jest stabilny" }, + "cold_tea": { + "fix_flow": { + "abort": { + "not_tea_time": "W tej chwili nie mo\u017cna ponownie podgrza\u0107 herbatki" + } + }, + "title": "Herbatka jest zimna" + }, "out_of_blinker_fluid": { "fix_flow": { "step": { diff --git a/homeassistant/components/demo/translations/pt-BR.json b/homeassistant/components/demo/translations/pt-BR.json index 16e4bb1396d..59ccd2d3e94 100644 --- a/homeassistant/components/demo/translations/pt-BR.json +++ b/homeassistant/components/demo/translations/pt-BR.json @@ -1,4 +1,57 @@ { + "entity": { + "climate": { + "ubercool": { + "state_attributes": { + "fan_mode": { + "state": { + "auto_high": "Auto Alto", + "auto_low": "Auto Baixo", + "on_high": "No alto", + "on_low": "Em baixo" + } + }, + "swing_mode": { + "state": { + "1": "1", + "2": "2", + "3": "3", + "auto": "Auto", + "off": "Desligado" + } + } + } + } + }, + "select": { + "speed": { + "state": { + "light_speed": "Velocidade da luz", + "ludicrous_speed": "Velocidade absurda", + "ridiculous_speed": "Velocidade rid\u00edcula" + } + } + }, + "sensor": { + "thermostat_mode": { + "state": { + "away": "Fora", + "comfort": "Conforto", + "eco": "Eco", + "sleep": "Sono" + } + } + }, + "vacuum": { + "model_s": { + "state_attributes": { + "cleaned_area": { + "name": "\u00c1rea limpa" + } + } + } + } + }, "issues": { "bad_psu": { "fix_flow": { diff --git a/homeassistant/components/demo/translations/ru.json b/homeassistant/components/demo/translations/ru.json index 7f2e10564f8..e15ce6a5fed 100644 --- a/homeassistant/components/demo/translations/ru.json +++ b/homeassistant/components/demo/translations/ru.json @@ -1,4 +1,49 @@ { + "entity": { + "climate": { + "ubercool": { + "state_attributes": { + "swing_mode": { + "state": { + "1": "1", + "2": "2", + "3": "3", + "auto": "\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438", + "off": "\u0412\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u043e" + } + } + } + } + }, + "select": { + "speed": { + "state": { + "light_speed": "\u0421\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u0441\u0432\u0435\u0442\u0430", + "ludicrous_speed": "\u0427\u0443\u0434\u043e\u0432\u0438\u0449\u043d\u0430\u044f \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c", + "ridiculous_speed": "\u041d\u0435\u0432\u0435\u0440\u043e\u044f\u0442\u043d\u0430\u044f \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c" + } + } + }, + "sensor": { + "thermostat_mode": { + "state": { + "away": "\u041d\u0435 \u0434\u043e\u043c\u0430", + "comfort": "\u041a\u043e\u043c\u0444\u043e\u0440\u0442", + "eco": "\u042d\u043a\u043e", + "sleep": "\u0421\u043e\u043d" + } + } + }, + "vacuum": { + "model_s": { + "state_attributes": { + "cleaned_area": { + "name": "\u041e\u0447\u0438\u0449\u0435\u043d\u043d\u0430\u044f \u043f\u043b\u043e\u0449\u0430\u0434\u044c" + } + } + } + } + }, "issues": { "bad_psu": { "fix_flow": { diff --git a/homeassistant/components/demo/translations/select.sk.json b/homeassistant/components/demo/translations/select.sk.json new file mode 100644 index 00000000000..298fe5ed1bb --- /dev/null +++ b/homeassistant/components/demo/translations/select.sk.json @@ -0,0 +1,9 @@ +{ + "state": { + "demo__speed": { + "light_speed": "R\u00fdchlos\u0165 svetla", + "ludicrous_speed": "Smie\u0161na r\u00fdchlos\u0165", + "ridiculous_speed": "Smie\u0161na r\u00fdchlos\u0165" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/demo/translations/sk.json b/homeassistant/components/demo/translations/sk.json index 85f8ceb1ea0..19251f58deb 100644 --- a/homeassistant/components/demo/translations/sk.json +++ b/homeassistant/components/demo/translations/sk.json @@ -1,9 +1,63 @@ { + "entity": { + "climate": { + "ubercool": { + "state_attributes": { + "fan_mode": { + "state": { + "auto_high": "Automatick\u00e1 vysok\u00e1", + "auto_low": "Automatick\u00e1 n\u00edzka", + "on_high": "Na vysokej \u00farovni", + "on_low": "Na n\u00edzkej \u00farovni" + } + }, + "swing_mode": { + "state": { + "1": "1", + "2": "2", + "3": "3", + "auto": "Auto", + "off": "Vypnut\u00e9" + } + } + } + } + }, + "select": { + "speed": { + "state": { + "light_speed": "R\u00fdchlos\u0165 svetla", + "ludicrous_speed": "Smie\u0161na r\u00fdchlos\u0165", + "ridiculous_speed": "Smie\u0161na r\u00fdchlos\u0165" + } + } + }, + "sensor": { + "thermostat_mode": { + "state": { + "away": "Pre\u010d", + "comfort": "Komfort", + "eco": "Eco", + "sleep": "Sp\u00e1nok" + } + } + }, + "vacuum": { + "model_s": { + "state_attributes": { + "cleaned_area": { + "name": "Oblas\u0165 vy\u010disten\u00e1" + } + } + } + } + }, "issues": { "bad_psu": { "fix_flow": { "step": { "confirm": { + "description": "Stla\u010den\u00edm tla\u010didla SUBMIT potvr\u010fte v\u00fdmenu zdroja nap\u00e1jania", "title": "Nap\u00e1jac\u00ed zdroj je potrebn\u00e9 vymeni\u0165" } } @@ -11,16 +65,52 @@ "title": "Nap\u00e1janie nie je stabiln\u00e9" }, "cold_tea": { + "fix_flow": { + "abort": { + "not_tea_time": "V tento moment nejde \u010daj znovu ohria\u0165" + }, + "step": { + "few": "Pr\u00e1zdnych", + "many": "Pr\u00e1zdnych", + "one": "Pr\u00e1zdny", + "other": "Pr\u00e1zdny" + } + }, "title": "\u010caj je studen\u00fd" }, + "out_of_blinker_fluid": { + "fix_flow": { + "step": { + "confirm": { + "description": "Po doplnen\u00ed kvapaliny do blika\u010da stla\u010dte SUBMIT", + "title": "Kvapalinu blika\u010da treba doplni\u0165" + } + } + }, + "title": "Kvapalina blika\u010da je pr\u00e1zdna a je potrebn\u00e9 ju doplni\u0165" + }, + "transmogrifier_deprecated": { + "description": "Komponent transmogrifier je teraz zastaran\u00fd z d\u00f4vodu nedostatku lok\u00e1lneho ovl\u00e1dania dostupn\u00e9ho v novom API", + "title": "Komponent transmogrifier je zastaran\u00fd" + }, "unfixable_problem": { + "description": "Tento probl\u00e9m nikdy nezmizne.", "title": "Tento probl\u00e9m sa ned\u00e1 odstr\u00e1ni\u0165" } }, "options": { "step": { + "init": { + "data": { + "few": "Pr\u00e1zdnych", + "many": "Pr\u00e1zdnych", + "one": "Pr\u00e1zdny", + "other": "Pr\u00e1zdny" + } + }, "options_1": { "data": { + "bool": "Volite\u013en\u00e1 booleovsk\u00e1 hodnota", "constant": "Kon\u0161tanta", "int": "\u010c\u00edseln\u00fd vstup" } @@ -33,5 +123,6 @@ } } } - } + }, + "title": "Demo" } \ No newline at end of file diff --git a/homeassistant/components/demo/translations/zh-Hant.json b/homeassistant/components/demo/translations/zh-Hant.json index 0018f67e065..bacfa77e6d3 100644 --- a/homeassistant/components/demo/translations/zh-Hant.json +++ b/homeassistant/components/demo/translations/zh-Hant.json @@ -1,4 +1,57 @@ { + "entity": { + "climate": { + "ubercool": { + "state_attributes": { + "fan_mode": { + "state": { + "auto_high": "\u81ea\u52d5\u5f37", + "auto_low": "\u81ea\u52d5\u5f31", + "on_high": "\u958b\u555f\u5f37", + "on_low": "\u958b\u555f\u5f31" + } + }, + "swing_mode": { + "state": { + "1": "1", + "2": "2", + "3": "3", + "auto": "\u81ea\u52d5", + "off": "\u95dc\u9589" + } + } + } + } + }, + "select": { + "speed": { + "state": { + "light_speed": "\u5149\u901f", + "ludicrous_speed": "\u53ef\u7b11\u7684\u901f\u5ea6", + "ridiculous_speed": "\u8352\u8b2c\u7684\u901f\u5ea6" + } + } + }, + "sensor": { + "thermostat_mode": { + "state": { + "away": "\u96e2\u5bb6\u6a21\u5f0f", + "comfort": "\u8212\u9069\u6a21\u5f0f", + "eco": "\u7bc0\u80fd\u6a21\u5f0f", + "sleep": "\u7761\u7720\u6a21\u5f0f" + } + } + }, + "vacuum": { + "model_s": { + "state_attributes": { + "cleaned_area": { + "name": "\u5df2\u6e05\u6383\u5340\u57df" + } + } + } + } + }, "issues": { "bad_psu": { "fix_flow": { diff --git a/homeassistant/components/demo/vacuum.py b/homeassistant/components/demo/vacuum.py index c283ab5456f..11b69272775 100644 --- a/homeassistant/components/demo/vacuum.py +++ b/homeassistant/components/demo/vacuum.py @@ -105,6 +105,7 @@ class DemoVacuum(VacuumEntity): """Representation of a demo vacuum.""" _attr_should_poll = False + _attr_translation_key = "model_s" def __init__(self, name: str, supported_features: VacuumEntityFeature) -> None: """Initialize the vacuum.""" @@ -247,6 +248,7 @@ class StateDemoVacuum(StateVacuumEntity): _attr_should_poll = False _attr_supported_features = SUPPORT_STATE_SERVICES + _attr_translation_key = "model_s" def __init__(self, name: str) -> None: """Initialize the vacuum.""" diff --git a/homeassistant/components/demo/water_heater.py b/homeassistant/components/demo/water_heater.py index 1fc164d5046..a21f492c439 100644 --- a/homeassistant/components/demo/water_heater.py +++ b/homeassistant/components/demo/water_heater.py @@ -8,7 +8,7 @@ from homeassistant.components.water_heater import ( WaterHeaterEntityFeature, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS, TEMP_FAHRENHEIT +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType @@ -29,8 +29,12 @@ async def async_setup_platform( """Set up the Demo water_heater devices.""" async_add_entities( [ - DemoWaterHeater("Demo Water Heater", 119, TEMP_FAHRENHEIT, False, "eco"), - DemoWaterHeater("Demo Water Heater Celsius", 45, TEMP_CELSIUS, True, "eco"), + DemoWaterHeater( + "Demo Water Heater", 119, UnitOfTemperature.FAHRENHEIT, False, "eco" + ), + DemoWaterHeater( + "Demo Water Heater Celsius", 45, UnitOfTemperature.CELSIUS, True, "eco" + ), ] ) diff --git a/homeassistant/components/denonavr/config_flow.py b/homeassistant/components/denonavr/config_flow.py index 2ec58df8126..2afe5828530 100644 --- a/homeassistant/components/denonavr/config_flow.py +++ b/homeassistant/components/denonavr/config_flow.py @@ -199,8 +199,10 @@ class DenonAvrFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): self._abort_if_unique_id_configured() else: _LOGGER.error( - "Could not get serial number of host %s, " - "unique_id's will not be available", + ( + "Could not get serial number of host %s, " + "unique_id's will not be available" + ), self.host, ) self._async_abort_entries_match({CONF_HOST: self.host}) diff --git a/homeassistant/components/denonavr/media_player.py b/homeassistant/components/denonavr/media_player.py index aa1fb7361e2..e1bd0f41808 100644 --- a/homeassistant/components/denonavr/media_player.py +++ b/homeassistant/components/denonavr/media_player.py @@ -159,8 +159,10 @@ def async_log_errors( available = False if self.available: _LOGGER.warning( - "Timeout connecting to Denon AVR receiver at host %s. " - "Device is unavailable", + ( + "Timeout connecting to Denon AVR receiver at host %s. " + "Device is unavailable" + ), self._receiver.host, ) self._attr_available = False @@ -168,8 +170,10 @@ def async_log_errors( available = False if self.available: _LOGGER.warning( - "Network error connecting to Denon AVR receiver at host %s. " - "Device is unavailable", + ( + "Network error connecting to Denon AVR receiver at host %s. " + "Device is unavailable" + ), self._receiver.host, ) self._attr_available = False @@ -177,9 +181,11 @@ def async_log_errors( available = False if self.available: _LOGGER.warning( - "Denon AVR receiver at host %s responded with HTTP 403 error. " - "Device is unavailable. Please consider power cycling your " - "receiver", + ( + "Denon AVR receiver at host %s responded with HTTP 403 error. " + "Device is unavailable. Please consider power cycling your " + "receiver" + ), self._receiver.host, ) self._attr_available = False diff --git a/homeassistant/components/denonavr/receiver.py b/homeassistant/components/denonavr/receiver.py index 28969d25792..7ea461f4e6f 100644 --- a/homeassistant/components/denonavr/receiver.py +++ b/homeassistant/components/denonavr/receiver.py @@ -51,7 +51,10 @@ class ConnectDenonAVR: or self._receiver.receiver_type is None ): _LOGGER.error( - "Missing receiver information: manufacturer '%s', name '%s', model '%s', type '%s'", + ( + "Missing receiver information: manufacturer '%s', name '%s', model" + " '%s', type '%s'" + ), self._receiver.manufacturer, self._receiver.name, self._receiver.model_name, diff --git a/homeassistant/components/denonavr/translations/de.json b/homeassistant/components/denonavr/translations/de.json index e4df128612b..b66b794c8c4 100644 --- a/homeassistant/components/denonavr/translations/de.json +++ b/homeassistant/components/denonavr/translations/de.json @@ -4,11 +4,11 @@ "already_configured": "Ger\u00e4t ist bereits konfiguriert", "already_in_progress": "Der Konfigurationsablauf wird bereits ausgef\u00fchrt", "cannot_connect": "Verbindung fehlgeschlagen. Bitte versuche es noch einmal. Trenne ggf. Strom- und Ethernetkabel und verbinde diese erneut.", - "not_denonavr_manufacturer": "Kein Denon AVR Network Receiver, festgestellter Hersteller stimmt nicht \u00fcberein", - "not_denonavr_missing": "Kein Denon AVR-Netzwerk-Receiver, Erkennungsinformationen nicht vollst\u00e4ndig" + "not_denonavr_manufacturer": "Kein Denon AVR Netzwerk-Receiver, festgestellter Hersteller stimmt nicht \u00fcberein", + "not_denonavr_missing": "Kein Denon AVR Netzwerk-Receiver, Erkennungsinformationen nicht vollst\u00e4ndig" }, "error": { - "discovery_error": "Denon AVR-Netzwerk-Receiver konnte nicht gefunden werden" + "discovery_error": "Denon AVR Netzwerk-Receiver konnte nicht gefunden werden" }, "flow_title": "{name}", "step": { @@ -37,7 +37,7 @@ "init": { "data": { "show_all_sources": "Alle Quellen anzeigen", - "update_audyssey": "Audyssey-Einstellungen aktualisieren", + "update_audyssey": "Audyssey Einstellungen aktualisieren", "zone2": "Zone 2 einrichten", "zone3": "Zone 3 einrichten" }, diff --git a/homeassistant/components/denonavr/translations/sk.json b/homeassistant/components/denonavr/translations/sk.json index 0957cca2860..499030e6f0f 100644 --- a/homeassistant/components/denonavr/translations/sk.json +++ b/homeassistant/components/denonavr/translations/sk.json @@ -2,14 +2,25 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", - "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "cannot_connect": "Nepodarilo sa pripoji\u0165, sk\u00faste to znova, odpojenie nap\u00e1jacieho a ethernetov\u00e9ho k\u00e1bla a ich op\u00e4tovn\u00e9 pripojenie m\u00f4\u017ee pom\u00f4c\u0165", + "not_denonavr_manufacturer": "Nejedn\u00e1 sa o sie\u0165ov\u00fd prij\u00edma\u010d Denon AVR, objaven\u00fd v\u00fdrobca sa nezhoduje", + "not_denonavr_missing": "Nejedn\u00e1 sa o sie\u0165ov\u00fd prij\u00edma\u010d Denon AVR, inform\u00e1cie o zis\u0165ovan\u00ed nie s\u00fa \u00fapln\u00e9" + }, + "error": { + "discovery_error": "Nepodarilo sa n\u00e1js\u0165 sie\u0165ov\u00fd prij\u00edma\u010d Denon AVR" }, "flow_title": "{name}", "step": { + "confirm": { + "description": "Potvr\u010fte pros\u00edm pridanie prij\u00edma\u010da" + }, "select": { "data": { "select_host": "IP adresa prij\u00edma\u010da" - } + }, + "description": "Ak chcete pripoji\u0165 \u010fal\u0161ie prij\u00edma\u010de, spustite nastavenie znova", + "title": "Vyberte prij\u00edma\u010d, ktor\u00fd chcete pripoji\u0165" }, "user": { "data": { @@ -25,7 +36,10 @@ "step": { "init": { "data": { - "show_all_sources": "Zobrazi\u0165 v\u0161etky zdroje" + "show_all_sources": "Zobrazi\u0165 v\u0161etky zdroje", + "update_audyssey": "Aktualizujte nastavenia Audyssey", + "zone2": "Nastavte z\u00f3nu 2", + "zone3": "Nastavte z\u00f3nu 3" }, "description": "Zadajte volite\u013en\u00e9 nastavenia" } diff --git a/homeassistant/components/derivative/config_flow.py b/homeassistant/components/derivative/config_flow.py index b250a910032..f0ce6803719 100644 --- a/homeassistant/components/derivative/config_flow.py +++ b/homeassistant/components/derivative/config_flow.py @@ -7,14 +7,7 @@ from typing import Any, cast import voluptuous as vol from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN -from homeassistant.const import ( - CONF_NAME, - CONF_SOURCE, - TIME_DAYS, - TIME_HOURS, - TIME_MINUTES, - TIME_SECONDS, -) +from homeassistant.const import CONF_NAME, CONF_SOURCE, UnitOfTime from homeassistant.helpers import selector from homeassistant.helpers.schema_config_entry_flow import ( SchemaConfigFlowHandler, @@ -41,10 +34,10 @@ UNIT_PREFIXES = [ selector.SelectOptionDict(value="P", label="P (peta)"), ] TIME_UNITS = [ - selector.SelectOptionDict(value=TIME_SECONDS, label="Seconds"), - selector.SelectOptionDict(value=TIME_MINUTES, label="Minutes"), - selector.SelectOptionDict(value=TIME_HOURS, label="Hours"), - selector.SelectOptionDict(value=TIME_DAYS, label="Days"), + selector.SelectOptionDict(value=UnitOfTime.SECONDS, label="Seconds"), + selector.SelectOptionDict(value=UnitOfTime.MINUTES, label="Minutes"), + selector.SelectOptionDict(value=UnitOfTime.HOURS, label="Hours"), + selector.SelectOptionDict(value=UnitOfTime.DAYS, label="Days"), ] OPTIONS_SCHEMA = vol.Schema( @@ -61,7 +54,7 @@ OPTIONS_SCHEMA = vol.Schema( vol.Required(CONF_UNIT_PREFIX, default="none"): selector.SelectSelector( selector.SelectSelectorConfig(options=UNIT_PREFIXES), ), - vol.Required(CONF_UNIT_TIME, default=TIME_HOURS): selector.SelectSelector( + vol.Required(CONF_UNIT_TIME, default=UnitOfTime.HOURS): selector.SelectSelector( selector.SelectSelectorConfig(options=TIME_UNITS), ), } diff --git a/homeassistant/components/derivative/sensor.py b/homeassistant/components/derivative/sensor.py index 8b8bc2f59f7..85d964a84cb 100644 --- a/homeassistant/components/derivative/sensor.py +++ b/homeassistant/components/derivative/sensor.py @@ -16,10 +16,7 @@ from homeassistant.const import ( CONF_SOURCE, STATE_UNAVAILABLE, STATE_UNKNOWN, - TIME_DAYS, - TIME_HOURS, - TIME_MINUTES, - TIME_SECONDS, + UnitOfTime, ) from homeassistant.core import Event, HomeAssistant, State, callback from homeassistant.helpers import config_validation as cv, entity_registry as er @@ -54,10 +51,10 @@ UNIT_PREFIXES = { # SI Time prefixes UNIT_TIME = { - TIME_SECONDS: 1, - TIME_MINUTES: 60, - TIME_HOURS: 60 * 60, - TIME_DAYS: 24 * 60 * 60, + UnitOfTime.SECONDS: 1, + UnitOfTime.MINUTES: 60, + UnitOfTime.HOURS: 60 * 60, + UnitOfTime.DAYS: 24 * 60 * 60, } ICON = "mdi:chart-line" @@ -71,7 +68,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( vol.Required(CONF_SOURCE): cv.entity_id, vol.Optional(CONF_ROUND_DIGITS, default=DEFAULT_ROUND): vol.Coerce(int), vol.Optional(CONF_UNIT_PREFIX, default=None): vol.In(UNIT_PREFIXES), - vol.Optional(CONF_UNIT_TIME, default=TIME_HOURS): vol.In(UNIT_TIME), + vol.Optional(CONF_UNIT_TIME, default=UnitOfTime.HOURS): vol.In(UNIT_TIME), vol.Optional(CONF_UNIT): cv.string, vol.Optional(CONF_TIME_WINDOW, default=DEFAULT_TIME_WINDOW): cv.time_period, } @@ -144,7 +141,7 @@ class DerivativeSensor(RestoreEntity, SensorEntity): time_window: timedelta, unit_of_measurement: str | None, unit_prefix: str | None, - unit_time: str, + unit_time: UnitOfTime, unique_id: str | None, ) -> None: """Initialize the derivative sensor.""" diff --git a/homeassistant/components/derivative/translations/sk.json b/homeassistant/components/derivative/translations/sk.json index bd461eb44e8..4dc80af51e2 100644 --- a/homeassistant/components/derivative/translations/sk.json +++ b/homeassistant/components/derivative/translations/sk.json @@ -7,8 +7,16 @@ "round": "Presnos\u0165", "source": "Vstupn\u00fd sn\u00edma\u010d", "time_window": "\u010casov\u00e9 okno", + "unit_prefix": "Metrick\u00fd prefix", "unit_time": "\u010casov\u00e1 jednotka" - } + }, + "data_description": { + "round": "Ovl\u00e1da po\u010det desatinn\u00fdch \u010d\u00edslic vo v\u00fdstupe.", + "time_window": "Ak je nastaven\u00e9, hodnota sn\u00edma\u010da je \u010dasovo v\u00e1\u017een\u00e1 k\u013azav\u00fdm priemerom deriv\u00e1ci\u00ed v tomto okne.", + "unit_prefix": "V\u00fdstup bude \u0161k\u00e1lovan\u00fd pod\u013ea zvolenej metrick\u00e9ho prefixu a \u010dasovej jednotky deriv\u00e1cie." + }, + "description": "Vytvorte sn\u00edma\u010d, ktor\u00fd odhadne deriv\u00e1ciu sn\u00edma\u010da.", + "title": "Pridanie deriva\u010dn\u00e9ho sn\u00edma\u010da" } } }, @@ -20,12 +28,16 @@ "round": "Presnos\u0165", "source": "Vstupn\u00fd sn\u00edma\u010d", "time_window": "\u010casov\u00e9 okno", + "unit_prefix": "Metrick\u00fd prefix", "unit_time": "\u010casov\u00e1 jednotka" }, "data_description": { - "unit_prefix": "." + "round": "Ovl\u00e1da po\u010det desatinn\u00fdch \u010d\u00edslic vo v\u00fdstupe.", + "time_window": "Ak je nastaven\u00e9, hodnota sn\u00edma\u010da je \u010dasovo v\u00e1\u017een\u00e1 k\u013azav\u00fdm priemerom deriv\u00e1ci\u00ed v tomto okne.", + "unit_prefix": "V\u00fdstup bude \u0161k\u00e1lovan\u00fd pod\u013ea zvolenej metrick\u00e9ho prefixu a \u010dasovej jednotky deriv\u00e1cie.." } } } - } + }, + "title": "Deriva\u010dn\u00fd sn\u00edma\u010d" } \ No newline at end of file diff --git a/homeassistant/components/deutsche_bahn/__init__.py b/homeassistant/components/deutsche_bahn/__init__.py deleted file mode 100644 index 0b696174fd5..00000000000 --- a/homeassistant/components/deutsche_bahn/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""The deutsche_bahn component.""" diff --git a/homeassistant/components/deutsche_bahn/manifest.json b/homeassistant/components/deutsche_bahn/manifest.json deleted file mode 100644 index 1eeb2241db5..00000000000 --- a/homeassistant/components/deutsche_bahn/manifest.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "domain": "deutsche_bahn", - "name": "Deutsche Bahn", - "documentation": "https://www.home-assistant.io/integrations/deutsche_bahn", - "requirements": ["schiene==0.23"], - "codeowners": [], - "iot_class": "cloud_polling", - "loggers": ["schiene"] -} diff --git a/homeassistant/components/deutsche_bahn/sensor.py b/homeassistant/components/deutsche_bahn/sensor.py deleted file mode 100644 index 8a563574f46..00000000000 --- a/homeassistant/components/deutsche_bahn/sensor.py +++ /dev/null @@ -1,141 +0,0 @@ -"""Support for information about the German train system.""" -from __future__ import annotations - -from datetime import timedelta -import logging - -import schiene -import voluptuous as vol - -from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity -from homeassistant.const import CONF_OFFSET -from homeassistant.core import HomeAssistant -import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.issue_registry import IssueSeverity, create_issue -from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -import homeassistant.util.dt as dt_util - -CONF_DESTINATION = "to" -CONF_START = "from" -DEFAULT_OFFSET = timedelta(minutes=0) -CONF_ONLY_DIRECT = "only_direct" -DEFAULT_ONLY_DIRECT = False - -ICON = "mdi:train" - -SCAN_INTERVAL = timedelta(minutes=2) - -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( - { - vol.Required(CONF_DESTINATION): cv.string, - vol.Required(CONF_START): cv.string, - vol.Optional(CONF_OFFSET, default=DEFAULT_OFFSET): cv.time_period, - vol.Optional(CONF_ONLY_DIRECT, default=DEFAULT_ONLY_DIRECT): cv.boolean, - } -) - -_LOGGER = logging.getLogger(__name__) - - -def setup_platform( - hass: HomeAssistant, - config: ConfigType, - add_entities: AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> None: - """Set up the Deutsche Bahn Sensor.""" - start = config.get(CONF_START) - destination = config[CONF_DESTINATION] - offset = config[CONF_OFFSET] - only_direct = config[CONF_ONLY_DIRECT] - create_issue( - hass, - "deutsche_bahn", - "pending_removal", - breaks_in_ha_version="2022.11.0", - is_fixable=False, - severity=IssueSeverity.WARNING, - translation_key="pending_removal", - ) - _LOGGER.warning( - "The Deutsche Bahn sensor component is deprecated and will be removed in Home Assistant 2022.11" - ) - add_entities([DeutscheBahnSensor(start, destination, offset, only_direct)], True) - - -class DeutscheBahnSensor(SensorEntity): - """Implementation of a Deutsche Bahn sensor.""" - - def __init__(self, start, goal, offset, only_direct): - """Initialize the sensor.""" - self._name = f"{start} to {goal}" - self.data = SchieneData(start, goal, offset, only_direct) - self._state = None - - @property - def name(self): - """Return the name of the sensor.""" - return self._name - - @property - def icon(self): - """Return the icon for the frontend.""" - return ICON - - @property - def native_value(self): - """Return the departure time of the next train.""" - return self._state - - @property - def extra_state_attributes(self): - """Return the state attributes.""" - connections = self.data.connections[0] - if len(self.data.connections) > 1: - connections["next"] = self.data.connections[1]["departure"] - if len(self.data.connections) > 2: - connections["next_on"] = self.data.connections[2]["departure"] - return connections - - def update(self) -> None: - """Get the latest delay from bahn.de and updates the state.""" - self.data.update() - self._state = self.data.connections[0].get("departure", "Unknown") - if self.data.connections[0].get("delay", 0) != 0: - self._state += f" + {self.data.connections[0]['delay']}" - - -class SchieneData: - """Pull data from the bahn.de web page.""" - - def __init__(self, start, goal, offset, only_direct): - """Initialize the sensor.""" - self.start = start - self.goal = goal - self.offset = offset - self.only_direct = only_direct - self.schiene = schiene.Schiene() - self.connections = [{}] - - def update(self): - """Update the connection data.""" - self.connections = self.schiene.connections( - self.start, - self.goal, - dt_util.as_local(dt_util.utcnow() + self.offset), - self.only_direct, - ) - - if not self.connections: - self.connections = [{}] - - for con in self.connections: - # Detail info is not useful. Having a more consistent interface - # simplifies usage of template sensors. - if "details" in con: - con.pop("details") - delay = con.get("delay", {"delay_departure": 0, "delay_arrival": 0}) - con["delay"] = delay["delay_departure"] - con["delay_arrival"] = delay["delay_arrival"] - con["ontime"] = con.get("ontime", False) diff --git a/homeassistant/components/deutsche_bahn/strings.json b/homeassistant/components/deutsche_bahn/strings.json deleted file mode 100644 index c88668862da..00000000000 --- a/homeassistant/components/deutsche_bahn/strings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "issues": { - "pending_removal": { - "title": "The Deutsche Bahn integration is being removed", - "description": "The Deutsche Bahn integration is pending removal from Home Assistant and will no longer be available as of Home Assistant 2022.11.\n\nThe integration is being removed, because it relies on webscraping, which is not allowed.\n\nRemove the Deutsche Bahn YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue." - } - } -} diff --git a/homeassistant/components/deutsche_bahn/translations/bg.json b/homeassistant/components/deutsche_bahn/translations/bg.json deleted file mode 100644 index 84d8d17f23a..00000000000 --- a/homeassistant/components/deutsche_bahn/translations/bg.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "issues": { - "pending_removal": { - "title": "\u0418\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u043d\u0430 Deutsche Bahn \u0441\u0435 \u043f\u0440\u0435\u043c\u0430\u0445\u0432\u0430" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/deutsche_bahn/translations/ca.json b/homeassistant/components/deutsche_bahn/translations/ca.json deleted file mode 100644 index 406d134c92f..00000000000 --- a/homeassistant/components/deutsche_bahn/translations/ca.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "issues": { - "pending_removal": { - "description": "La integraci\u00f3 de Deutsche Bahn est\u00e0 pendent d'eliminar-se de Home Assistant i ja no estar\u00e0 disponible a partir de Home Assistant 2022.11. \n\nLa integraci\u00f3 s'est\u00e0 eliminant, perqu\u00e8 es basa en el 'webscraping', que no est\u00e0 adm\u00e8s. \n\nElimina la configuraci\u00f3 YAML de Deutsche Bahn del fitxer configuration.yaml i reinicia Home Assistant per arreglar aquest error.", - "title": "La integraci\u00f3 Deutsche Bahn est\u00e0 sent eliminada" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/deutsche_bahn/translations/de.json b/homeassistant/components/deutsche_bahn/translations/de.json deleted file mode 100644 index 986aebfa7a6..00000000000 --- a/homeassistant/components/deutsche_bahn/translations/de.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "issues": { - "pending_removal": { - "description": "Die Deutsche Bahn-Integration wird derzeit aus Home Assistant entfernt und wird ab Home Assistant 2022.11 nicht mehr verf\u00fcgbar sein.\n\nDie Integration wird entfernt, weil sie auf Webscraping beruht, was nicht erlaubt ist.\n\nEntferne die YAML-Konfiguration der Deutschen Bahn aus deiner configuration.yaml-Datei und starte Home Assistant neu, um dieses Problem zu beheben.", - "title": "Die Deutsche-Bahn-Integration wird entfernt" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/deutsche_bahn/translations/el.json b/homeassistant/components/deutsche_bahn/translations/el.json deleted file mode 100644 index cc1cd6eb001..00000000000 --- a/homeassistant/components/deutsche_bahn/translations/el.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "issues": { - "pending_removal": { - "description": "\u0397 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03c4\u03b7\u03c2 Deutsche Bahn \u03b5\u03ba\u03ba\u03c1\u03b5\u03bc\u03b5\u03af \u03c0\u03c1\u03bf\u03c2 \u03b1\u03c6\u03b1\u03af\u03c1\u03b5\u03c3\u03b7 \u03b1\u03c0\u03cc \u03c4\u03bf Home Assistant \u03ba\u03b1\u03b9 \u03b4\u03b5\u03bd \u03b8\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u03c0\u03bb\u03ad\u03bf\u03bd \u03b4\u03b9\u03b1\u03b8\u03ad\u03c3\u03b9\u03bc\u03b7 \u03b1\u03c0\u03cc \u03c4\u03bf Home Assistant 2022.11.\n\n\u0397 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03b1\u03c6\u03b1\u03b9\u03c1\u03b5\u03af\u03c4\u03b1\u03b9, \u03b5\u03c0\u03b5\u03b9\u03b4\u03ae \u03b2\u03b1\u03c3\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03c3\u03b5 webscraping, \u03c4\u03bf \u03bf\u03c0\u03bf\u03af\u03bf \u03b4\u03b5\u03bd \u03b5\u03c0\u03b9\u03c4\u03c1\u03ad\u03c0\u03b5\u03c4\u03b1\u03b9.\n\n\u0391\u03c6\u03b1\u03b9\u03c1\u03ad\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03c0\u03b1\u03c1\u03b1\u03bc\u03ad\u03c4\u03c1\u03c9\u03bd YAML \u03c4\u03b7\u03c2 Deutsche Bahn \u03b1\u03c0\u03cc \u03c4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf configuration.yaml \u03ba\u03b1\u03b9 \u03b5\u03c0\u03b1\u03bd\u03b5\u03ba\u03ba\u03b9\u03bd\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf Home Assistant \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c0\u03c1\u03cc\u03b2\u03bb\u03b7\u03bc\u03b1.", - "title": "\u0397 \u03b5\u03bd\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03c4\u03b7\u03c2 Deutsche Bahn \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b5\u03af\u03c4\u03b1\u03b9" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/deutsche_bahn/translations/en.json b/homeassistant/components/deutsche_bahn/translations/en.json deleted file mode 100644 index 0ad1f14d1cc..00000000000 --- a/homeassistant/components/deutsche_bahn/translations/en.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "issues": { - "pending_removal": { - "description": "The Deutsche Bahn integration is pending removal from Home Assistant and will no longer be available as of Home Assistant 2022.11.\n\nThe integration is being removed, because it relies on webscraping, which is not allowed.\n\nRemove the Deutsche Bahn YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue.", - "title": "The Deutsche Bahn integration is being removed" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/deutsche_bahn/translations/es.json b/homeassistant/components/deutsche_bahn/translations/es.json deleted file mode 100644 index 6d8b849c11b..00000000000 --- a/homeassistant/components/deutsche_bahn/translations/es.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "issues": { - "pending_removal": { - "description": "La integraci\u00f3n Deutsche Bahn est\u00e1 pendiente de eliminaci\u00f3n de Home Assistant y ya no estar\u00e1 disponible a partir de Home Assistant 2022.11. \n\nSe va a eliminar la integraci\u00f3n porque se basa en webscraping, algo que no est\u00e1 permitido. \n\nElimina la configuraci\u00f3n YAML de Deutsche Bahn de tu archivo configuration.yaml y reinicia Home Assistant para solucionar este problema.", - "title": "Se va a eliminar la integraci\u00f3n Deutsche Bahn" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/deutsche_bahn/translations/et.json b/homeassistant/components/deutsche_bahn/translations/et.json deleted file mode 100644 index a6029593a7b..00000000000 --- a/homeassistant/components/deutsche_bahn/translations/et.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "issues": { - "pending_removal": { - "description": "Deutsche Bahni integratsioon on Home Assistantist eemaldamisel ja see ei ole enam saadaval alates Home Assistant 2022.11.\n\nIntegratsioon eemaldatakse, sest see p\u00f5hineb veebiotsingul, mis on keelatud.\n\nProbleemi lahendamiseks eemaldage Deutsche Bahni YAML-konfiguratsioon oma configuration.yaml-failist ja k\u00e4ivitage Home Assistant uuesti.", - "title": "Deutsche Bahni integratsioon eemaldatakse" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/deutsche_bahn/translations/fr.json b/homeassistant/components/deutsche_bahn/translations/fr.json deleted file mode 100644 index a3e3b26fa78..00000000000 --- a/homeassistant/components/deutsche_bahn/translations/fr.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "issues": { - "pending_removal": { - "title": "L'int\u00e9gration Deutsche Bahn est en cours de suppression" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/deutsche_bahn/translations/hu.json b/homeassistant/components/deutsche_bahn/translations/hu.json deleted file mode 100644 index 5b1bf7e95e6..00000000000 --- a/homeassistant/components/deutsche_bahn/translations/hu.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "issues": { - "pending_removal": { - "description": "A Deutsche Bahn integr\u00e1ci\u00f3 elt\u00e1vol\u00edt\u00e1sra v\u00e1r a Home Assistantb\u00f3l, \u00e9s a 2022.11-es Home Assistant-t\u00f3l m\u00e1r nem lesz el\u00e9rhet\u0151.\n\nAz integr\u00e1ci\u00f3 elt\u00e1vol\u00edt\u00e1sra ker\u00fcl, mivel webscrapingre t\u00e1maszkodik, ami nem megengedett.\n\nA probl\u00e9ma megold\u00e1s\u00e1hoz t\u00e1vol\u00edtsa el a Deutsche Bahn YAML konfigur\u00e1ci\u00f3t a configuration.yaml f\u00e1jlb\u00f3l, \u00e9s ind\u00edtsa \u00fajra a Home Assistantot.", - "title": "A Deutsche Bahn integr\u00e1ci\u00f3 megsz\u00fcnik" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/deutsche_bahn/translations/id.json b/homeassistant/components/deutsche_bahn/translations/id.json deleted file mode 100644 index 2bd68daf49e..00000000000 --- a/homeassistant/components/deutsche_bahn/translations/id.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "issues": { - "pending_removal": { - "description": "Integrasi Deutsche Bahn sedang menunggu penghapusan dari Home Assistant dan tidak akan lagi tersedia pada Home Assistant 2022.11.\n\nIntegrasi ini dalam proses penghapusan, karena bergantung pada proses webscraping, yang tidak diizinkan.\n\nHapus konfigurasi Deutsche Bahn YAML dari file configuration.yaml Anda dan mulai ulang Home Assistant untuk memperbaiki masalah ini.", - "title": "Integrasi Deutsche Bahn dalam proses penghapusan" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/deutsche_bahn/translations/it.json b/homeassistant/components/deutsche_bahn/translations/it.json deleted file mode 100644 index 22449ef7a17..00000000000 --- a/homeassistant/components/deutsche_bahn/translations/it.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "issues": { - "pending_removal": { - "description": "L'integrazione Deutsce Bahn \u00e8 in attesa di rimozione da Home Assistant e non sar\u00e0 pi\u00f9 disponibile a partire da Home Assistant 2022.11. \n\nL'integrazione sar\u00e0 rimossa, perch\u00e9 si basa sul webscraping, che non \u00e8 consentito. \n\nRimuovi la configurazione YAML di Deutsce Bahn dal file configuration.yaml e riavvia Home Assistant per risolvere questo problema.", - "title": "L'integrazione Deutsche Bahn sar\u00e0 rimossa" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/deutsche_bahn/translations/ja.json b/homeassistant/components/deutsche_bahn/translations/ja.json deleted file mode 100644 index b76158e0b22..00000000000 --- a/homeassistant/components/deutsche_bahn/translations/ja.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "issues": { - "pending_removal": { - "description": "Deutsche Bahn(\u30c9\u30a4\u30c4\u9244\u9053)\u306e\u7d71\u5408\u306f\u3001Home Assistant\u304b\u3089\u306e\u524a\u9664\u306f\u4fdd\u7559\u3055\u308c\u3066\u3044\u307e\u3059\u304c\u3001Home Assistant 2022.11\u4ee5\u964d\u306f\u5229\u7528\u3067\u304d\u306a\u304f\u306a\u308a\u307e\u3059\u3002 \n\n\u3053\u306e\u7d71\u5408\u306f\u3001\u8a31\u53ef\u3055\u308c\u3066\u3044\u306a\u3044Web\u30b9\u30af\u30ec\u30a4\u30d4\u30f3\u30b0\u306b\u4f9d\u5b58\u3057\u3066\u3044\u308b\u305f\u3081\u3001\u524a\u9664\u3055\u308c\u308b\u4e88\u5b9a\u3067\u3059\u3002\n\n\u3053\u306e\u554f\u984c\u3092\u89e3\u6c7a\u3059\u308b\u306b\u306f\u3001configuration.yaml\u30d5\u30a1\u30a4\u30eb\u304b\u3089Deutsche Bahn YAML\u8a2d\u5b9a\u3092\u524a\u9664\u3057\u3001Home Assistant\u3092\u518d\u8d77\u52d5\u3057\u307e\u3059\u3002", - "title": "\u30c9\u30a4\u30c4\u9244\u9053(Deutsche Bahn)\u306e\u7d71\u5408\u306f\u524a\u9664\u3055\u308c\u3066\u3044\u307e\u3059" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/deutsche_bahn/translations/nl.json b/homeassistant/components/deutsche_bahn/translations/nl.json deleted file mode 100644 index 6aabd5b3a6d..00000000000 --- a/homeassistant/components/deutsche_bahn/translations/nl.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "issues": { - "pending_removal": { - "title": "De Deutsche Bahn-integratie wordt verwijderd" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/deutsche_bahn/translations/no.json b/homeassistant/components/deutsche_bahn/translations/no.json deleted file mode 100644 index 5852c5b716c..00000000000 --- a/homeassistant/components/deutsche_bahn/translations/no.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "issues": { - "pending_removal": { - "description": "Deutsche Bahn-integrasjonen venter p\u00e5 fjerning fra Home Assistant og vil ikke lenger v\u00e6re tilgjengelig fra og med Home Assistant 2022.11. \n\n Integrasjonen blir fjernet, fordi den er avhengig av webscraping, noe som ikke er tillatt. \n\n Fjern Deutsche Bahn YAML-konfigurasjonen fra configuration.yaml-filen og start Home Assistant p\u00e5 nytt for \u00e5 fikse dette problemet.", - "title": "Deutsche Bahn-integrasjonen fjernes" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/deutsche_bahn/translations/pl.json b/homeassistant/components/deutsche_bahn/translations/pl.json deleted file mode 100644 index 85cf97d0d17..00000000000 --- a/homeassistant/components/deutsche_bahn/translations/pl.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "issues": { - "pending_removal": { - "description": "Integracja Deutsche Bahn oczekuje na usuni\u0119cie z Home Assistanta i nie b\u0119dzie ju\u017c dost\u0119pna od Home Assistant 2022.11. \n\nIntegracja jest usuwana, poniewa\u017c opiera si\u0119 na webscrapingu, co jest niedozwolone. \n\nUsu\u0144 konfiguracj\u0119 YAML z pliku configuration.yaml i uruchom ponownie Home Assistanta, aby rozwi\u0105za\u0107 ten problem.", - "title": "Integracja Deutsche Bahn zostanie usuni\u0119ta" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/deutsche_bahn/translations/pt-BR.json b/homeassistant/components/deutsche_bahn/translations/pt-BR.json deleted file mode 100644 index abf4f29a0e5..00000000000 --- a/homeassistant/components/deutsche_bahn/translations/pt-BR.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "issues": { - "pending_removal": { - "description": "A integra\u00e7\u00e3o da Deutsche Bahn est\u00e1 com remo\u00e7\u00e3o pendente do Home Assistant e n\u00e3o estar\u00e1 mais dispon\u00edvel a partir do Home Assistant 2022.11. \n\n A integra\u00e7\u00e3o est\u00e1 sendo removida, pois depende de webscraping, o que n\u00e3o \u00e9 permitido. \n\n Remova a configura\u00e7\u00e3o YAML da Deutsche Bahn do arquivo configuration.yaml e reinicie o Home Assistant para corrigir esse problema.", - "title": "A integra\u00e7\u00e3o da Deutsche Bahn est\u00e1 sendo removida" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/deutsche_bahn/translations/ru.json b/homeassistant/components/deutsche_bahn/translations/ru.json deleted file mode 100644 index b5fe6857a73..00000000000 --- a/homeassistant/components/deutsche_bahn/translations/ru.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "issues": { - "pending_removal": { - "description": "\u0418\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f Deutsche Bahn \u043e\u0436\u0438\u0434\u0430\u0435\u0442 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0438\u0437 Home Assistant \u0438 \u0431\u043e\u043b\u044c\u0448\u0435 \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430 \u0441 Home Assistant \u0432\u0435\u0440\u0441\u0438\u0438 2022.11. \n\n\u0418\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043b\u0435\u043d\u0430, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043e\u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u0430 \u043d\u0430 \u0432\u0435\u0431-\u0441\u043a\u0440\u0430\u043f\u0438\u043d\u0433\u0435, \u0447\u0442\u043e \u0437\u0430\u043f\u0440\u0435\u0449\u0435\u043d\u043e. \n\n\u0423\u0434\u0430\u043b\u0438\u0442\u0435 YAML-\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u0438\u0437 \u0444\u0430\u0439\u043b\u0430 configuration.yaml \u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 Home Assistant, \u0447\u0442\u043e\u0431\u044b \u0443\u0441\u0442\u0440\u0430\u043d\u0438\u0442\u044c \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443.", - "title": "\u0418\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f Deutsche Bahn \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043b\u0435\u043d\u0430" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/deutsche_bahn/translations/sv.json b/homeassistant/components/deutsche_bahn/translations/sv.json deleted file mode 100644 index 7595e8bed60..00000000000 --- a/homeassistant/components/deutsche_bahn/translations/sv.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "issues": { - "pending_removal": { - "description": "Deutsche Bahn-integrationen v\u00e4ntar p\u00e5 borttagning fr\u00e5n Home Assistant och kommer inte l\u00e4ngre att vara tillg\u00e4nglig fr\u00e5n och med Home Assistant 2022.11. \n\n Integrationen tas bort, eftersom den f\u00f6rlitar sig p\u00e5 webbskrapning, vilket inte \u00e4r till\u00e5tet. \n\n Ta bort Deutsche Bahn YAML-konfigurationen fr\u00e5n filen configuration.yaml och starta om Home Assistant f\u00f6r att \u00e5tg\u00e4rda problemet.", - "title": "Deutsche Bahn-integrationen tas bort" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/deutsche_bahn/translations/tr.json b/homeassistant/components/deutsche_bahn/translations/tr.json deleted file mode 100644 index 1aeda9c4d18..00000000000 --- a/homeassistant/components/deutsche_bahn/translations/tr.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "issues": { - "pending_removal": { - "description": "Deutsche Bahn entegrasyonu, Home Assistant'tan kald\u0131r\u0131lmay\u0131 bekliyor ve Home Assistant 2022.11'den itibaren art\u0131k kullan\u0131lamayacak. \n\n Entegrasyon kald\u0131r\u0131l\u0131yor, \u00e7\u00fcnk\u00fc izin verilmeyen web taramaya dayan\u0131yor. \n\n Deutsche Bahn YAML yap\u0131land\u0131rmas\u0131n\u0131 configuration.yaml dosyan\u0131zdan kald\u0131r\u0131n ve bu sorunu gidermek i\u00e7in Home Assistant'\u0131 yeniden ba\u015flat\u0131n.", - "title": "Deutsche Bahn entegrasyonu kald\u0131r\u0131l\u0131yor" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/deutsche_bahn/translations/zh-Hant.json b/homeassistant/components/deutsche_bahn/translations/zh-Hant.json deleted file mode 100644 index fd60b2ae7fb..00000000000 --- a/homeassistant/components/deutsche_bahn/translations/zh-Hant.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "issues": { - "pending_removal": { - "description": "Deutsche Bahn \u6574\u5408\u5373\u5c07\u7531 Home Assistant \u4e2d\u79fb\u9664\u3001\u4e26\u65bc Home Assistant 2022.11 \u7248\u5f8c\u7121\u6cd5\u518d\u4f7f\u7528\u3002\n\n\u7531\u65bc\u4f7f\u7528\u4e86\u4e0d\u88ab\u5141\u8a31\u7684\u7db2\u8def\u8cc7\u6599\u64f7\u53d6\uff08webscraping\uff09\u65b9\u5f0f\u3001\u6574\u5408\u5373\u5c07\u79fb\u9664\u3002\n\n\u7531 configuration.yaml \u6a94\u6848\u4e2d\u79fb\u9664 Deutsche Bahn YAML \u8a2d\u5b9a\u4e26\u91cd\u555f Home Assistant to \u4ee5\u4fee\u6b63\u6b64\u554f\u984c\u3002", - "title": "Deutsche Bahn \u6574\u5408\u5373\u5c07\u79fb\u9664" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/device_tracker/legacy.py b/homeassistant/components/device_tracker/legacy.py index 09fd5dce432..bc792ee8920 100644 --- a/homeassistant/components/device_tracker/legacy.py +++ b/homeassistant/components/device_tracker/legacy.py @@ -379,8 +379,10 @@ def async_setup_scanner_platform( """Handle interval matches.""" if update_lock.locked(): LOGGER.warning( - "Updating device list from %s took longer than the scheduled " - "scan interval %s", + ( + "Updating device list from %s took longer than the scheduled " + "scan interval %s" + ), platform, interval, ) @@ -954,6 +956,6 @@ def get_gravatar_for_email(email: str) -> str: """ return ( - f"https://www.gravatar.com/avatar/" + "https://www.gravatar.com/avatar/" f"{hashlib.md5(email.encode('utf-8').lower()).hexdigest()}.jpg?s=80&d=wavatar" ) diff --git a/homeassistant/components/devolo_home_control/sensor.py b/homeassistant/components/devolo_home_control/sensor.py index 33a17929f71..51a804df7ad 100644 --- a/homeassistant/components/devolo_home_control/sensor.py +++ b/homeassistant/components/devolo_home_control/sensor.py @@ -21,7 +21,6 @@ from .devolo_device import DevoloDeviceEntity DEVICE_CLASS_MAPPING = { "battery": SensorDeviceClass.BATTERY, "temperature": SensorDeviceClass.TEMPERATURE, - "light": SensorDeviceClass.ILLUMINANCE, "humidity": SensorDeviceClass.HUMIDITY, "current": SensorDeviceClass.POWER, "total": SensorDeviceClass.ENERGY, @@ -169,9 +168,6 @@ class DevoloConsumptionEntity(DevoloMultiLevelDeviceEntity): device_instance.consumption_property[element_uid], f"{consumption}_unit" ) - if consumption == "total": - self._attr_state_class = SensorStateClass.TOTAL_INCREASING - self._value = getattr( device_instance.consumption_property[element_uid], consumption ) diff --git a/homeassistant/components/devolo_home_control/translations/ko.json b/homeassistant/components/devolo_home_control/translations/ko.json index f3832dec7c1..29fb4eb7ea6 100644 --- a/homeassistant/components/devolo_home_control/translations/ko.json +++ b/homeassistant/components/devolo_home_control/translations/ko.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" }, "error": { "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" diff --git a/homeassistant/components/devolo_home_control/translations/pt.json b/homeassistant/components/devolo_home_control/translations/pt.json index f8e1acc359d..2bfde2034d8 100644 --- a/homeassistant/components/devolo_home_control/translations/pt.json +++ b/homeassistant/components/devolo_home_control/translations/pt.json @@ -12,7 +12,7 @@ "data": { "mydevolo_url": "mydevolo [VOID]", "password": "Palavra-passe", - "username": "Email / devolo ID" + "username": "[VOID] / devolo ID" } }, "zeroconf_confirm": { diff --git a/homeassistant/components/devolo_home_network/device_tracker.py b/homeassistant/components/devolo_home_network/device_tracker.py index 26b30ec43d5..e465266a0e7 100644 --- a/homeassistant/components/devolo_home_network/device_tracker.py +++ b/homeassistant/components/devolo_home_network/device_tracker.py @@ -11,7 +11,7 @@ from homeassistant.components.device_tracker import ( SourceType, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import FREQUENCY_GIGAHERTZ, STATE_UNKNOWN +from homeassistant.const import STATE_UNKNOWN, UnitOfFrequency from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import entity_registry from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -119,7 +119,7 @@ class DevoloScannerEntity(CoordinatorEntity, ScannerEntity): if station: attrs["wifi"] = WIFI_APTYPE.get(station["vap_type"], STATE_UNKNOWN) attrs["band"] = ( - f"{WIFI_BANDS.get(station['band'])} {FREQUENCY_GIGAHERTZ}" + f"{WIFI_BANDS.get(station['band'])} {UnitOfFrequency.GIGAHERTZ}" if WIFI_BANDS.get(station["band"]) else STATE_UNKNOWN ) diff --git a/homeassistant/components/devolo_home_network/manifest.json b/homeassistant/components/devolo_home_network/manifest.json index 945c314a196..858afc6e5e8 100644 --- a/homeassistant/components/devolo_home_network/manifest.json +++ b/homeassistant/components/devolo_home_network/manifest.json @@ -4,7 +4,7 @@ "integration_type": "device", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/devolo_home_network", - "requirements": ["devolo-plc-api==0.8.0"], + "requirements": ["devolo-plc-api==0.9.0"], "zeroconf": [ { "type": "_dvl-deviceapi._tcp.local.", "properties": { "MT": "*" } } ], diff --git a/homeassistant/components/devolo_home_network/translations/de.json b/homeassistant/components/devolo_home_network/translations/de.json index 8a850e01bcf..630454f47b8 100644 --- a/homeassistant/components/devolo_home_network/translations/de.json +++ b/homeassistant/components/devolo_home_network/translations/de.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Ger\u00e4t ist bereits konfiguriert", - "home_control": "Die devolo Home Control-Zentraleinheit funktioniert nicht mit dieser Integration.", + "home_control": "Die devolo Home Control Zentraleinheit funktioniert nicht mit dieser Integration.", "reauth_successful": "Die erneute Authentifizierung war erfolgreich" }, "error": { diff --git a/homeassistant/components/devolo_home_network/translations/en.json b/homeassistant/components/devolo_home_network/translations/en.json index e98984738b0..aac1164de95 100644 --- a/homeassistant/components/devolo_home_network/translations/en.json +++ b/homeassistant/components/devolo_home_network/translations/en.json @@ -20,7 +20,7 @@ "data": { "ip_address": "IP Address" }, - "description": "Do you want to start set up?" + "description": "Do you want to start setup?" }, "zeroconf_confirm": { "description": "Do you want to add the devolo home network device with the hostname `{host_name}` to Home Assistant?", diff --git a/homeassistant/components/devolo_home_network/translations/it.json b/homeassistant/components/devolo_home_network/translations/it.json index 9494d34f01a..75c1f3f2064 100644 --- a/homeassistant/components/devolo_home_network/translations/it.json +++ b/homeassistant/components/devolo_home_network/translations/it.json @@ -20,7 +20,7 @@ "data": { "ip_address": "Indirizzo IP" }, - "description": "Vuoi iniziare la configurazione?" + "description": "Vuoi avviare la configurazione?" }, "zeroconf_confirm": { "description": "Vuoi aggiungere il dispositivo di rete domestica devolo con il nome host `{host_name}` a Home Assistant?", diff --git a/homeassistant/components/devolo_home_network/translations/ko.json b/homeassistant/components/devolo_home_network/translations/ko.json index 0e7d5b287f2..e78ee8de1b1 100644 --- a/homeassistant/components/devolo_home_network/translations/ko.json +++ b/homeassistant/components/devolo_home_network/translations/ko.json @@ -1,7 +1,23 @@ { "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, "step": { + "reauth_confirm": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638" + } + }, "user": { + "data": { + "ip_address": "IP \uc8fc\uc18c" + }, "description": "\uc124\uc815\uc744 \uc2dc\uc791\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" }, "zeroconf_confirm": { diff --git a/homeassistant/components/devolo_home_network/translations/pt.json b/homeassistant/components/devolo_home_network/translations/pt.json index fb6f8840412..d9badba08c3 100644 --- a/homeassistant/components/devolo_home_network/translations/pt.json +++ b/homeassistant/components/devolo_home_network/translations/pt.json @@ -1,16 +1,24 @@ { "config": { + "abort": { + "reauth_successful": "Reautentica\u00e7\u00e3o bem sucedida" + }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "unknown": "Erro inesperado" }, "flow_title": "{product} ({name})", "step": { + "reauth_confirm": { + "data": { + "password": "Palavra-passe" + } + }, "user": { "data": { "ip_address": "Endere\u00e7o IP" }, - "description": "Quer dar inicio \u00e0 configura\u00e7\u00e3o?" + "description": "Quer dar in\u00edcio \u00e0 configura\u00e7\u00e3o?" } } } diff --git a/homeassistant/components/devolo_home_network/translations/sk.json b/homeassistant/components/devolo_home_network/translations/sk.json index 45116ef4db3..265d186fc5e 100644 --- a/homeassistant/components/devolo_home_network/translations/sk.json +++ b/homeassistant/components/devolo_home_network/translations/sk.json @@ -23,6 +23,7 @@ "description": "Chcete za\u010da\u0165 nastavova\u0165?" }, "zeroconf_confirm": { + "description": "Chcete prida\u0165 dom\u00e1ce sie\u0165ov\u00e9 zariadenie devolo s n\u00e1zvom hostite\u013ea `{host_name}` do Home Assistant?", "title": "Objaven\u00e9 zariadenie dom\u00e1cej siete devolo" } } diff --git a/homeassistant/components/dexcom/strings.json b/homeassistant/components/dexcom/strings.json index 588556eda49..35d80371c12 100644 --- a/homeassistant/components/dexcom/strings.json +++ b/homeassistant/components/dexcom/strings.json @@ -2,7 +2,7 @@ "config": { "step": { "user": { - "title": "Setup Dexcom integration", + "title": "Set up Dexcom integration", "description": "Enter Dexcom Share credentials", "data": { "username": "[%key:common::config_flow::data::username%]", diff --git a/homeassistant/components/dexcom/translations/ca.json b/homeassistant/components/dexcom/translations/ca.json index 7b97a209e49..f3251016a80 100644 --- a/homeassistant/components/dexcom/translations/ca.json +++ b/homeassistant/components/dexcom/translations/ca.json @@ -16,7 +16,7 @@ "username": "Nom d'usuari" }, "description": "Introdueix les credencials de Dexcom Share", - "title": "Configura la integraci\u00f3 de Dexcom" + "title": "Configura la integraci\u00f3 Dexcom" } } }, diff --git a/homeassistant/components/dexcom/translations/en.json b/homeassistant/components/dexcom/translations/en.json index f7558c31468..0720e90ed83 100644 --- a/homeassistant/components/dexcom/translations/en.json +++ b/homeassistant/components/dexcom/translations/en.json @@ -16,7 +16,7 @@ "username": "Username" }, "description": "Enter Dexcom Share credentials", - "title": "Setup Dexcom integration" + "title": "Set up Dexcom integration" } } }, diff --git a/homeassistant/components/dexcom/translations/it.json b/homeassistant/components/dexcom/translations/it.json index 21904e36378..c608df3a4dc 100644 --- a/homeassistant/components/dexcom/translations/it.json +++ b/homeassistant/components/dexcom/translations/it.json @@ -16,7 +16,7 @@ "username": "Nome utente" }, "description": "Inserisci le credenziali di Dexcom Share", - "title": "Configura l'integrazione di Dexcom" + "title": "Configura l'integrazione Dexcom" } } }, diff --git a/homeassistant/components/dexcom/translations/no.json b/homeassistant/components/dexcom/translations/no.json index 3ec0c755f9e..6e83c0ca391 100644 --- a/homeassistant/components/dexcom/translations/no.json +++ b/homeassistant/components/dexcom/translations/no.json @@ -16,7 +16,7 @@ "username": "Brukernavn" }, "description": "Angi Dexcom Share-legitimasjon", - "title": "Setup Dexcom integrasjon" + "title": "Sett opp Dexcom-integrasjon" } } }, diff --git a/homeassistant/components/dexcom/translations/pt-BR.json b/homeassistant/components/dexcom/translations/pt-BR.json index ce21e4d51c8..fa0ac7ddd5c 100644 --- a/homeassistant/components/dexcom/translations/pt-BR.json +++ b/homeassistant/components/dexcom/translations/pt-BR.json @@ -16,7 +16,7 @@ "username": "Usu\u00e1rio" }, "description": "Insira as credenciais do Dexcom Share", - "title": "Configurar integra\u00e7\u00e3o Dexcom" + "title": "Configure a integra\u00e7\u00e3o Dexcom" } } }, diff --git a/homeassistant/components/dexcom/translations/pt.json b/homeassistant/components/dexcom/translations/pt.json index 8af2ff4345a..69f639338e6 100644 --- a/homeassistant/components/dexcom/translations/pt.json +++ b/homeassistant/components/dexcom/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "Conta j\u00e1 configurada" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, diff --git a/homeassistant/components/dexcom/translations/sk.json b/homeassistant/components/dexcom/translations/sk.json index 607228fd38e..f97497c5a5d 100644 --- a/homeassistant/components/dexcom/translations/sk.json +++ b/homeassistant/components/dexcom/translations/sk.json @@ -15,6 +15,7 @@ "server": "Server", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" }, + "description": "Zadajte poverenia Dexcom Share", "title": "Nastavte integr\u00e1ciu Dexcom" } } diff --git a/homeassistant/components/dhcp/__init__.py b/homeassistant/components/dhcp/__init__.py index ab8787f4bff..74dd1f66abf 100644 --- a/homeassistant/components/dhcp/__init__.py +++ b/homeassistant/components/dhcp/__init__.py @@ -1,7 +1,7 @@ """The dhcp integration.""" from __future__ import annotations -from abc import abstractmethod +from abc import ABC, abstractmethod import asyncio from collections.abc import Callable, Iterable import contextlib @@ -115,7 +115,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: return True -class WatcherBase: +class WatcherBase(ABC): """Base class for dhcp and device tracker watching.""" def __init__( diff --git a/homeassistant/components/diagnostics/__init__.py b/homeassistant/components/diagnostics/__init__.py index 29315d6c467..b54a710e807 100644 --- a/homeassistant/components/diagnostics/__init__.py +++ b/homeassistant/components/diagnostics/__init__.py @@ -1,6 +1,8 @@ """The Diagnostics integration.""" from __future__ import annotations +from collections.abc import Callable, Coroutine +from dataclasses import dataclass, field from http import HTTPStatus import json import logging @@ -31,9 +33,28 @@ __all__ = ["REDACTED", "async_redact_data"] _LOGGER = logging.getLogger(__name__) +@dataclass +class DiagnosticsPlatformData: + """Diagnostic platform data.""" + + config_entry_diagnostics: Callable[ + [HomeAssistant, ConfigEntry], Coroutine[Any, Any, Any] + ] | None + device_diagnostics: Callable[ + [HomeAssistant, ConfigEntry, DeviceEntry], Coroutine[Any, Any, Any] + ] | None + + +@dataclass +class DiagnosticsData: + """Diagnostic data.""" + + platforms: dict[str, DiagnosticsPlatformData] = field(default_factory=dict) + + async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up Diagnostics from a config entry.""" - hass.data[DOMAIN] = {} + hass.data[DOMAIN] = DiagnosticsData() await integration_platform.async_process_integration_platforms( hass, DOMAIN, _register_diagnostics_platform @@ -62,16 +83,13 @@ class DiagnosticsProtocol(Protocol): async def _register_diagnostics_platform( hass: HomeAssistant, integration_domain: str, platform: DiagnosticsProtocol -): +) -> None: """Register a diagnostics platform.""" - hass.data[DOMAIN][integration_domain] = { - DiagnosticsType.CONFIG_ENTRY.value: getattr( - platform, "async_get_config_entry_diagnostics", None - ), - DiagnosticsSubType.DEVICE.value: getattr( - platform, "async_get_device_diagnostics", None - ), - } + diagnostics_data: DiagnosticsData = hass.data[DOMAIN] + diagnostics_data.platforms[integration_domain] = DiagnosticsPlatformData( + getattr(platform, "async_get_config_entry_diagnostics", None), + getattr(platform, "async_get_device_diagnostics", None), + ) @websocket_api.require_admin @@ -79,18 +97,20 @@ async def _register_diagnostics_platform( @callback def handle_info( hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict -): +) -> None: """List all possible diagnostic handlers.""" - connection.send_result( - msg["id"], - [ - { - "domain": domain, - "handlers": {key: val is not None for key, val in info.items()}, - } - for domain, info in hass.data[DOMAIN].items() - ], - ) + diagnostics_data: DiagnosticsData = hass.data[DOMAIN] + result = [ + { + "domain": domain, + "handlers": { + DiagnosticsType.CONFIG_ENTRY: info.config_entry_diagnostics is not None, + DiagnosticsSubType.DEVICE: info.device_diagnostics is not None, + }, + } + for domain, info in diagnostics_data.platforms.items() + ] + connection.send_result(msg["id"], result) @websocket_api.require_admin @@ -103,11 +123,12 @@ def handle_info( @callback def handle_get( hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict -): - """List all possible diagnostic handlers.""" +) -> None: + """List all diagnostic handlers for a domain.""" domain = msg["domain"] + diagnostics_data: DiagnosticsData = hass.data[DOMAIN] - if (info := hass.data[DOMAIN].get(domain)) is None: + if (info := diagnostics_data.platforms.get(domain)) is None: connection.send_error( msg["id"], websocket_api.ERR_NOT_FOUND, "Domain not supported" ) @@ -117,7 +138,10 @@ def handle_get( msg["id"], { "domain": domain, - "handlers": {key: val is not None for key, val in info.items()}, + "handlers": { + DiagnosticsType.CONFIG_ENTRY: info.config_entry_diagnostics is not None, + DiagnosticsSubType.DEVICE: info.device_diagnostics is not None, + }, }, ) @@ -127,9 +151,7 @@ async def _async_get_json_file_response( data: Any, filename: str, domain: str, - d_type: DiagnosticsType, d_id: str, - sub_type: DiagnosticsSubType | None = None, sub_id: str | None = None, ) -> web.Response: """Return JSON file from dictionary.""" @@ -159,9 +181,11 @@ async def _async_get_json_file_response( except TypeError: _LOGGER.error( "Failed to serialize to JSON: %s/%s%s. Bad data at %s", - d_type.value, + DiagnosticsType.CONFIG_ENTRY.value, d_id, - f"/{sub_type.value}/{sub_id}" if sub_type is not None else "", + f"/{DiagnosticsSubType.DEVICE.value}/{sub_id}" + if sub_id is not None + else "", format_unserializable_data(find_paths_unserializable_data(data)), ) return web.Response(status=HTTPStatus.INTERNAL_SERVER_ERROR) @@ -189,49 +213,55 @@ class DownloadDiagnosticsView(http.HomeAssistantView): sub_id: str | None = None, ) -> web.Response: """Download diagnostics.""" - # t_type handling + # Validate d_type and sub_type try: - d_type = DiagnosticsType(d_type) + DiagnosticsType(d_type) except ValueError: return web.Response(status=HTTPStatus.BAD_REQUEST) - hass = request.app["hass"] + if sub_type is not None: + try: + DiagnosticsSubType(sub_type) + except ValueError: + return web.Response(status=HTTPStatus.BAD_REQUEST) + + device_diagnostics = sub_type is not None + + hass: HomeAssistant = request.app["hass"] if (config_entry := hass.config_entries.async_get_entry(d_id)) is None: return web.Response(status=HTTPStatus.NOT_FOUND) - if (info := hass.data[DOMAIN].get(config_entry.domain)) is None: + diagnostics_data: DiagnosticsData = hass.data[DOMAIN] + if (info := diagnostics_data.platforms.get(config_entry.domain)) is None: return web.Response(status=HTTPStatus.NOT_FOUND) filename = f"{config_entry.domain}-{config_entry.entry_id}" - if sub_type is None: - if info[d_type.value] is None: + if not device_diagnostics: + # Config entry diagnostics + if info.config_entry_diagnostics is None: return web.Response(status=HTTPStatus.NOT_FOUND) - data = await info[d_type.value](hass, config_entry) - filename = f"{d_type}-{filename}" + data = await info.config_entry_diagnostics(hass, config_entry) + filename = f"{DiagnosticsType.CONFIG_ENTRY}-{filename}" return await _async_get_json_file_response( - hass, data, filename, config_entry.domain, d_type.value, d_id + hass, data, filename, config_entry.domain, d_id ) - # sub_type handling - try: - sub_type = DiagnosticsSubType(sub_type) - except ValueError: - return web.Response(status=HTTPStatus.BAD_REQUEST) - + # Device diagnostics dev_reg = async_get(hass) - assert sub_id + if sub_id is None: + return web.Response(status=HTTPStatus.BAD_REQUEST) if (device := dev_reg.async_get(sub_id)) is None: return web.Response(status=HTTPStatus.NOT_FOUND) filename += f"-{device.name}-{device.id}" - if info[sub_type.value] is None: + if info.device_diagnostics is None: return web.Response(status=HTTPStatus.NOT_FOUND) - data = await info[sub_type.value](hass, config_entry, device) + data = await info.device_diagnostics(hass, config_entry, device) return await _async_get_json_file_response( - hass, data, filename, config_entry.domain, d_type, d_id, sub_type, sub_id + hass, data, filename, config_entry.domain, d_id, sub_id ) diff --git a/homeassistant/components/dialogflow/__init__.py b/homeassistant/components/dialogflow/__init__.py index 30c85ac7599..d34b27233be 100644 --- a/homeassistant/components/dialogflow/__init__.py +++ b/homeassistant/components/dialogflow/__init__.py @@ -106,7 +106,8 @@ async def async_handle_message(hass, message): _api_version = get_api_version(message) if _api_version is V1: _LOGGER.warning( - "Dialogflow V1 API will be removed on October 23, 2019. Please change your DialogFlow settings to use the V2 api" + "Dialogflow V1 API will be removed on October 23, 2019. Please change your" + " DialogFlow settings to use the V2 api" ) req = message.get("result") if req.get("actionIncomplete", True): diff --git a/homeassistant/components/dialogflow/strings.json b/homeassistant/components/dialogflow/strings.json index 4bd7ca461a3..4e59e53ca8c 100644 --- a/homeassistant/components/dialogflow/strings.json +++ b/homeassistant/components/dialogflow/strings.json @@ -12,7 +12,7 @@ "webhook_not_internet_accessible": "[%key:common::config_flow::abort::webhook_not_internet_accessible%]" }, "create_entry": { - "default": "To send events to Home Assistant, you will need to setup [webhook integration of Dialogflow]({dialogflow_url}).\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n- Content Type: application/json\n\nSee [the documentation]({docs_url}) for further details." + "default": "To send events to Home Assistant, you will need to set up [webhook integration of Dialogflow]({dialogflow_url}).\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n- Content Type: application/json\n\nSee [the documentation]({docs_url}) for further details." } } } diff --git a/homeassistant/components/dialogflow/translations/ca.json b/homeassistant/components/dialogflow/translations/ca.json index c59076ec57d..58ef140bf87 100644 --- a/homeassistant/components/dialogflow/translations/ca.json +++ b/homeassistant/components/dialogflow/translations/ca.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "La teva inst\u00e0ncia de Home Assistant ha de ser accessible des d'Internet per poder rebre missatges webhook." }, "create_entry": { - "default": "Per enviar esdeveniments a Home Assistant, haur\u00e0s de configurar la [integraci\u00f3 webhook de Dialogflow]({dialogflow_url}). \n\n Completa la seg\u00fcent informaci\u00f3: \n\n- URL: `{webhook_url}` \n- M\u00e8tode: POST \n- Tipus de contingut: application/json\n\nConsulta la [documentaci\u00f3]({docs_url}) per a m\u00e9s detalls." + "default": "Per enviar esdeveniments a Home Assistant, has de configurar la [integraci\u00f3 webhook de Dialogflow]({dialogflow_url}). \n\n Completa la seg\u00fcent informaci\u00f3: \n\n- URL: `{webhook_url}` \n- M\u00e8tode: POST \n- Tipus de contingut: application/json\n\nConsulta la [documentaci\u00f3]({docs_url}) per a m\u00e9s detalls." }, "step": { "user": { diff --git a/homeassistant/components/dialogflow/translations/de.json b/homeassistant/components/dialogflow/translations/de.json index bf6a4aca128..5d071660009 100644 --- a/homeassistant/components/dialogflow/translations/de.json +++ b/homeassistant/components/dialogflow/translations/de.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "Deine Home Assistant-Instanz muss \u00fcber das Internet erreichbar sein, um Webhook-Nachrichten empfangen zu k\u00f6nnen." }, "create_entry": { - "default": "Um Ereignisse an den Home Assistant zu senden, musst du [Webhook-Integration von Dialogflow]({dialogflow_url}) einrichten. \n\nF\u00fclle die folgenden Informationen aus: \n\n - URL: ` {webhook_url} ` \n - Methode: POST \n - Inhaltstyp: application / json \n\nWeitere Informationen findest du in der [Dokumentation]({docs_url})." + "default": "Um Ereignisse an Home Assistant zu senden, muss [Webhook-Integration von Dialogflow]({dialogflow_url}) eingerichtet werden. \n\nF\u00fcge die folgenden Informationen ein:\n\n- URL: `{webhook_url}` \n- Methode: POST \n- Inhaltstyp: application/json \n\nWeitere Informationen finden sich in der [Dokumentation]({docs_url})." }, "step": { "user": { diff --git a/homeassistant/components/dialogflow/translations/en.json b/homeassistant/components/dialogflow/translations/en.json index 9c8157ce06d..e11e68cfd24 100644 --- a/homeassistant/components/dialogflow/translations/en.json +++ b/homeassistant/components/dialogflow/translations/en.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "Your Home Assistant instance needs to be accessible from the internet to receive webhook messages." }, "create_entry": { - "default": "To send events to Home Assistant, you will need to setup [webhook integration of Dialogflow]({dialogflow_url}).\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n- Content Type: application/json\n\nSee [the documentation]({docs_url}) for further details." + "default": "To send events to Home Assistant, you will need to set up [webhook integration of Dialogflow]({dialogflow_url}).\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n- Content Type: application/json\n\nSee [the documentation]({docs_url}) for further details." }, "step": { "user": { diff --git a/homeassistant/components/dialogflow/translations/es.json b/homeassistant/components/dialogflow/translations/es.json index eb4c3b179a6..203ee75ff92 100644 --- a/homeassistant/components/dialogflow/translations/es.json +++ b/homeassistant/components/dialogflow/translations/es.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "Tu instancia de Home Assistant debe estar accesible desde Internet para recibir mensajes webhook." }, "create_entry": { - "default": "Para enviar eventos a Home Assistant, necesitas configurar la [Integraci\u00f3n webhook de Dialogflow]({dialogflow_url}).\n\nCompleta la siguiente informaci\u00f3n:\n\n- URL: `{webhook_url}`\n- Method: POST\n- Content Type: application/json\n\nConsulta [la documentaci\u00f3n]({docs_url}) para m\u00e1s detalles." + "default": "Para enviar eventos a Home Assistant, necesitar\u00e1s configurar la [integraci\u00f3n webhook de Dialogflow]({dialogflow_url}).\n\nCompleta la siguiente informaci\u00f3n:\n\n- URL: `{webhook_url}`\n- M\u00e9todo: POST\n- Content Type: application/json\n\nConsulta [la documentaci\u00f3n]({docs_url}) para obtener m\u00e1s detalles." }, "step": { "user": { diff --git a/homeassistant/components/dialogflow/translations/it.json b/homeassistant/components/dialogflow/translations/it.json index 5f8c44d358b..a57bcf71e68 100644 --- a/homeassistant/components/dialogflow/translations/it.json +++ b/homeassistant/components/dialogflow/translations/it.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "L'istanza di Home Assistant deve essere accessibile da Internet per ricevere messaggi webhook." }, "create_entry": { - "default": "Per inviare eventi a Home Assistant, dovrai configurare [l'integrazione webhook di Dialogflow]({dialogflow_url})\n\n Compila le seguenti informazioni: \n\n - URL: `{webhook_url}` \n - Metodo: POST \n - Tipo di contenuto: application/json \n\n Vedi [la documentazione]({docs_url}) per ulteriori dettagli." + "default": "Per inviare eventi a Home Assistant, dovrai configurare l'[integrazione webhook di Dialogflow]({dialogflow_url}). \n\nCompila le seguenti informazioni: \n\n - URL: `{webhook_url}`\n - Metodo: POST\n - Tipo di contenuto: applicazione/json \n\nConsulta [la documentazione]({docs_url}) per ulteriori dettagli." }, "step": { "user": { diff --git a/homeassistant/components/dialogflow/translations/ko.json b/homeassistant/components/dialogflow/translations/ko.json index e0414018787..7536e08ab26 100644 --- a/homeassistant/components/dialogflow/translations/ko.json +++ b/homeassistant/components/dialogflow/translations/ko.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "cloud_not_connected": "Home Assistant \ud074\ub77c\uc6b0\ub4dc\uc5d0 \uc5f0\uacb0\ub418\uc5b4 \uc788\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.", "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.", "webhook_not_internet_accessible": "\uc6f9 \ud6c5 \uba54\uc2dc\uc9c0\ub97c \ubc1b\uc73c\ub824\uba74 \uc778\ud130\ub137\uc5d0\uc11c Home Assistant \uc778\uc2a4\ud134\uc2a4\uc5d0 \uc811\uadfc\ud560 \uc218 \uc788\uc5b4\uc57c \ud569\ub2c8\ub2e4." }, diff --git a/homeassistant/components/dialogflow/translations/no.json b/homeassistant/components/dialogflow/translations/no.json index cceb7dd033d..d410d6c025f 100644 --- a/homeassistant/components/dialogflow/translations/no.json +++ b/homeassistant/components/dialogflow/translations/no.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "Home Assistant forekomsten din m\u00e5 v\u00e6re tilgjengelig fra internett for \u00e5 kunne motta webhook meldinger" }, "create_entry": { - "default": "For \u00e5 sende hendelser til Home Assistant, m\u00e5 du sette opp [webhook integrasjon av Dialogflow]({dialogflow_url}). \n\nFyll ut f\u00f8lgende informasjon: \n\n- URL: `{webhook_url}` \n- Metode: POST\n- Innholdstype: application/json\n\nSe [dokumentasjonen]({docs_url}) for ytterligere detaljer." + "default": "For \u00e5 sende hendelser til Home Assistant, m\u00e5 du konfigurere [webhook-integrasjon av Dialogflow]( {dialogflow_url} ). \n\n Fyll inn f\u00f8lgende informasjon: \n\n - URL: ` {webhook_url} `\n - Metode: POST\n - Innholdstype: application/json \n\n Se [dokumentasjonen]( {docs_url} ) for ytterligere detaljer." }, "step": { "user": { diff --git a/homeassistant/components/dialogflow/translations/pt-BR.json b/homeassistant/components/dialogflow/translations/pt-BR.json index 7b5c5a96464..f3d2d337692 100644 --- a/homeassistant/components/dialogflow/translations/pt-BR.json +++ b/homeassistant/components/dialogflow/translations/pt-BR.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "Sua inst\u00e2ncia do Home Assistant precisa estar acess\u00edvel pela Internet para receber mensagens de webhook." }, "create_entry": { - "default": "Para enviar eventos para o Home Assistant, voc\u00ea precisar\u00e1 configurar [Integra\u00e7\u00e3o do webhook da Dialogflow] ( {dialogflow_url} ). \n\n Preencha as seguintes informa\u00e7\u00f5es: \n\n - URL: ` {webhook_url} ` \n - M\u00e9todo: POST \n - Tipo de Conte\u00fado: application / json \n\n Veja [a documenta\u00e7\u00e3o] ( {docs_url} ) para mais detalhes." + "default": "Para enviar eventos para o Home Assistant, voc\u00ea precisar\u00e1 configurar [Integra\u00e7\u00e3o do webhook da Dialogflow] ({dialogflow_url}). \n\n Preencha as seguintes informa\u00e7\u00f5es: \n\n - URL: `{webhook_url}` \n - M\u00e9todo: POST \n - Tipo de Conte\u00fado: application / json \n\n Veja [a documenta\u00e7\u00e3o] ({docs_url}) para mais detalhes." }, "step": { "user": { diff --git a/homeassistant/components/dialogflow/translations/sk.json b/homeassistant/components/dialogflow/translations/sk.json index 933f73976d2..df1ee36cf26 100644 --- a/homeassistant/components/dialogflow/translations/sk.json +++ b/homeassistant/components/dialogflow/translations/sk.json @@ -4,6 +4,15 @@ "cloud_not_connected": "Nie je pripojen\u00e9 k Home Assistant Cloud.", "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia.", "webhook_not_internet_accessible": "Va\u0161a in\u0161tancia Home Assistant mus\u00ed by\u0165 pr\u00edstupn\u00e1 z internetu, aby ste mohli prij\u00edma\u0165 spr\u00e1vy webhooku." + }, + "create_entry": { + "default": "Chcete odosla\u0165 udalosti do Home Assistant, budete musie\u0165 nastavi\u0165 [integr\u00e1ciu Dialogflow]({dialogflow_url}). \n\nVypl\u0148te nasleduj\u00face inform\u00e1cie: \n\n - URL: `{webhook_url}' \n - Met\u00f3da: POST \n - Typ obsahu: application/json\n\nPodrobnej\u0161ie inform\u00e1cie n\u00e1jdete v [dokument\u00e1cii]({docs_url})." + }, + "step": { + "user": { + "description": "Skuto\u010dne chcete nastavi\u0165 Dialogflow?", + "title": "Nastavi\u0165 Dialogflow Webhook" + } } } } \ No newline at end of file diff --git a/homeassistant/components/directv/translations/pt.json b/homeassistant/components/directv/translations/pt.json index 7880adf5fff..d00e6171a4b 100644 --- a/homeassistant/components/directv/translations/pt.json +++ b/homeassistant/components/directv/translations/pt.json @@ -5,7 +5,7 @@ "unknown": "Erro inesperado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "user": { diff --git a/homeassistant/components/directv/translations/sk.json b/homeassistant/components/directv/translations/sk.json index 22f56dce8ab..1b5c95c8808 100644 --- a/homeassistant/components/directv/translations/sk.json +++ b/homeassistant/components/directv/translations/sk.json @@ -10,6 +10,12 @@ "flow_title": "{name}", "step": { "ssdp_confirm": { + "data": { + "few": "Pr\u00e1zdnych", + "many": "Pr\u00e1zdnych", + "one": "Pr\u00e1zdny", + "other": "Pr\u00e1zdny" + }, "description": "Chcete nastavi\u0165 {name}?" }, "user": { diff --git a/homeassistant/components/discogs/sensor.py b/homeassistant/components/discogs/sensor.py index 51c69449d22..5e2d4bfa770 100644 --- a/homeassistant/components/discogs/sensor.py +++ b/homeassistant/components/discogs/sensor.py @@ -127,7 +127,9 @@ class DiscogsSensor(SensorEntity): return { "cat_no": self._attrs["labels"][0]["catno"], "cover_image": self._attrs["cover_image"], - "format": f"{self._attrs['formats'][0]['name']} ({self._attrs['formats'][0]['descriptions'][0]})", + "format": ( + f"{self._attrs['formats'][0]['name']} ({self._attrs['formats'][0]['descriptions'][0]})" + ), "label": self._attrs["labels"][0]["name"], "released": self._attrs["year"], ATTR_IDENTITY: self._discogs_data["user"], @@ -146,7 +148,10 @@ class DiscogsSensor(SensorEntity): random_record = collection.releases[random_index].release self._attrs = random_record.data - return f"{random_record.data['artists'][0]['name']} - {random_record.data['title']}" + return ( + f"{random_record.data['artists'][0]['name']} -" + f" {random_record.data['title']}" + ) return None diff --git a/homeassistant/components/discord/notify.py b/homeassistant/components/discord/notify.py index 8fcab7cefba..43301290490 100644 --- a/homeassistant/components/discord/notify.py +++ b/homeassistant/components/discord/notify.py @@ -87,7 +87,10 @@ class DiscordNotificationService(BaseNotificationService): if content_length is not None and int(content_length) > max_file_size: _LOGGER.error( - "Attachment too large (Content-Length reports %s). Max size: %s bytes", + ( + "Attachment too large (Content-Length reports %s). Max size: %s" + " bytes" + ), int(content_length), max_file_size, ) diff --git a/homeassistant/components/discord/translations/ko.json b/homeassistant/components/discord/translations/ko.json new file mode 100644 index 00000000000..d874e73e505 --- /dev/null +++ b/homeassistant/components/discord/translations/ko.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "\uc11c\ube44\uc2a4\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "reauth_confirm": { + "data": { + "api_token": "API \ud1a0\ud070" + } + }, + "user": { + "data": { + "api_token": "API \ud1a0\ud070" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/discord/translations/pt.json b/homeassistant/components/discord/translations/pt.json index 2c961f1708a..94718f260c2 100644 --- a/homeassistant/components/discord/translations/pt.json +++ b/homeassistant/components/discord/translations/pt.json @@ -5,7 +5,7 @@ "reauth_successful": "Reautentica\u00e7\u00e3o bem sucedida" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, diff --git a/homeassistant/components/discord/translations/sk.json b/homeassistant/components/discord/translations/sk.json index f20eb067fd9..5ca3a800b01 100644 --- a/homeassistant/components/discord/translations/sk.json +++ b/homeassistant/components/discord/translations/sk.json @@ -13,12 +13,14 @@ "reauth_confirm": { "data": { "api_token": "API token" - } + }, + "description": "Inform\u00e1cie o z\u00edskan\u00ed k\u013e\u00fa\u010da robota Discord n\u00e1jdete v dokument\u00e1cii. \n\n {url}" }, "user": { "data": { "api_token": "API token" - } + }, + "description": "Inform\u00e1cie o z\u00edskan\u00ed k\u013e\u00fa\u010da robota Discord n\u00e1jdete v dokument\u00e1cii. \n\n {url}" } } } diff --git a/homeassistant/components/discovery/__init__.py b/homeassistant/components/discovery/__init__.py index 75016c28048..0ffd6fe49ef 100644 --- a/homeassistant/components/discovery/__init__.py +++ b/homeassistant/components/discovery/__init__.py @@ -146,8 +146,10 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: for platform in enabled_platforms: if platform in DEFAULT_ENABLED: logger.warning( - "Please remove %s from your discovery.enable configuration " - "as it is now enabled by default", + ( + "Please remove %s from your discovery.enable configuration " + "as it is now enabled by default" + ), platform, ) diff --git a/homeassistant/components/dlink/switch.py b/homeassistant/components/dlink/switch.py index f1ca99c51f2..b38460ddb8f 100644 --- a/homeassistant/components/dlink/switch.py +++ b/homeassistant/components/dlink/switch.py @@ -16,7 +16,7 @@ from homeassistant.const import ( CONF_NAME, CONF_PASSWORD, CONF_USERNAME, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv @@ -85,7 +85,9 @@ class SmartPlugSwitch(SwitchEntity): def extra_state_attributes(self): """Return the state attributes of the device.""" try: - ui_temp = self.units.temperature(int(self.data.temperature), TEMP_CELSIUS) + ui_temp = self.units.temperature( + int(self.data.temperature), UnitOfTemperature.CELSIUS + ) temperature = ui_temp except (ValueError, TypeError): temperature = None diff --git a/homeassistant/components/dlna_dmr/config_flow.py b/homeassistant/components/dlna_dmr/config_flow.py index c7b37537e2b..d1d1aad0ce9 100644 --- a/homeassistant/components/dlna_dmr/config_flow.py +++ b/homeassistant/components/dlna_dmr/config_flow.py @@ -2,6 +2,8 @@ from __future__ import annotations from collections.abc import Callable, Mapping +from functools import partial +from ipaddress import IPv6Address, ip_address import logging from pprint import pformat from typing import Any, Optional, cast @@ -10,14 +12,16 @@ from urllib.parse import urlparse from async_upnp_client.client import UpnpError from async_upnp_client.profiles.dlna import DmrDevice from async_upnp_client.profiles.profile import find_device_of_type +from getmac import get_mac_address import voluptuous as vol from homeassistant import config_entries from homeassistant.components import ssdp -from homeassistant.const import CONF_DEVICE_ID, CONF_HOST, CONF_TYPE, CONF_URL -from homeassistant.core import callback +from homeassistant.const import CONF_DEVICE_ID, CONF_HOST, CONF_MAC, CONF_TYPE, CONF_URL +from homeassistant.core import HomeAssistant, callback from homeassistant.data_entry_flow import FlowResult from homeassistant.exceptions import IntegrationError +from homeassistant.helpers import device_registry import homeassistant.helpers.config_validation as cv from .const import ( @@ -56,6 +60,7 @@ class DlnaDmrFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): self._udn: str | None = None self._device_type: str | None = None self._name: str | None = None + self._mac: str | None = None self._options: dict[str, Any] = {} @staticmethod @@ -130,32 +135,56 @@ class DlnaDmrFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): return self.async_abort(reason="alternative_integration") # Abort if the device doesn't support all services required for a DmrDevice. - # Use the discovery_info instead of DmrDevice.is_profile_device to avoid - # contacting the device again. - discovery_service_list = discovery_info.upnp.get(ssdp.ATTR_UPNP_SERVICE_LIST) - if not discovery_service_list: + if not _is_dmr_device(discovery_info): return self.async_abort(reason="not_dmr") - services = discovery_service_list.get("service") - if not services: - discovery_service_ids: set[str] = set() - elif isinstance(services, list): - discovery_service_ids = {service.get("serviceId") for service in services} - else: - # Only one service defined (etree_to_dict failed to make a list) - discovery_service_ids = {services.get("serviceId")} - - if not DmrDevice.SERVICE_IDS.issubset(discovery_service_ids): - return self.async_abort(reason="not_dmr") - - # Abort if another config entry has the same location, in case the - # device doesn't have a static and unique UDN (breaking the UPnP spec). - self._async_abort_entries_match({CONF_URL: self._location}) + # Abort if another config entry has the same location or MAC address, in + # case the device doesn't have a static and unique UDN (breaking the + # UPnP spec). + for entry in self._async_current_entries(include_ignore=True): + if self._location == entry.data[CONF_URL]: + return self.async_abort(reason="already_configured") + if self._mac and self._mac == entry.data.get(CONF_MAC): + return self.async_abort(reason="already_configured") self.context["title_placeholders"] = {"name": self._name} return await self.async_step_confirm() + async def async_step_ignore(self, user_input: Mapping[str, Any]) -> FlowResult: + """Ignore this config flow, and add MAC address as secondary identifier. + + Not all DMR devices correctly implement the spec, so their UDN may + change between boots. Use the MAC address as a secondary identifier so + they can still be ignored in this case. + """ + LOGGER.debug("async_step_ignore: user_input: %s", user_input) + self._udn = user_input["unique_id"] + assert self._udn + await self.async_set_unique_id(self._udn, raise_on_progress=False) + + # Try to get relevant info from SSDP discovery, but don't worry if it's + # not available - the data values will just be None in that case + for dev_type in DmrDevice.DEVICE_TYPES: + discovery = await ssdp.async_get_discovery_info_by_udn_st( + self.hass, self._udn, dev_type + ) + if discovery: + await self._async_set_info_from_discovery( + discovery, abort_if_configured=False + ) + break + + return self.async_create_entry( + title=user_input["title"], + data={ + CONF_URL: self._location, + CONF_DEVICE_ID: self._udn, + CONF_TYPE: self._device_type, + CONF_MAC: self._mac, + }, + ) + async def async_step_unignore(self, user_input: Mapping[str, Any]) -> FlowResult: """Rediscover previously ignored devices by their unique_id.""" LOGGER.debug("async_step_unignore: user_input: %s", user_input) @@ -224,6 +253,9 @@ class DlnaDmrFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): if not self._name: self._name = device.name + if not self._mac and (host := urlparse(self._location).hostname): + self._mac = await _async_get_mac_address(self.hass, host) + def _create_entry(self) -> FlowResult: """Create a config entry, assuming all required information is now known.""" LOGGER.debug( @@ -238,6 +270,7 @@ class DlnaDmrFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): CONF_URL: self._location, CONF_DEVICE_ID: self._udn, CONF_TYPE: self._device_type, + CONF_MAC: self._mac, } return self.async_create_entry(title=title, data=data, options=self._options) @@ -256,13 +289,7 @@ class DlnaDmrFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): assert isinstance(self._location, str) self._udn = discovery_info.ssdp_udn - await self.async_set_unique_id(self._udn) - - if abort_if_configured: - # Abort if already configured, but update the last-known location - self._abort_if_unique_id_configured( - updates={CONF_URL: self._location}, reload_on_update=False - ) + await self.async_set_unique_id(self._udn, raise_on_progress=abort_if_configured) self._device_type = discovery_info.ssdp_nt or discovery_info.ssdp_st self._name = ( @@ -271,6 +298,17 @@ class DlnaDmrFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): or DEFAULT_NAME ) + if host := discovery_info.ssdp_headers.get("_host"): + self._mac = await _async_get_mac_address(self.hass, host) + + if abort_if_configured: + # Abort if already configured, but update the last-known location + updates = {CONF_URL: self._location} + # Set the MAC address for older entries + if self._mac: + updates[CONF_MAC] = self._mac + self._abort_if_unique_id_configured(updates=updates, reload_on_update=False) + async def _async_get_discoveries(self) -> list[ssdp.SsdpServiceInfo]: """Get list of unconfigured DLNA devices discovered by SSDP.""" LOGGER.debug("_get_discoveries") @@ -408,3 +446,59 @@ def _is_ignored_device(discovery_info: ssdp.SsdpServiceInfo) -> bool: return True return False + + +def _is_dmr_device(discovery_info: ssdp.SsdpServiceInfo) -> bool: + """Determine if discovery is a complete DLNA DMR device. + + Use the discovery_info instead of DmrDevice.is_profile_device to avoid + contacting the device again. + """ + # Abort if the device doesn't support all services required for a DmrDevice. + discovery_service_list = discovery_info.upnp.get(ssdp.ATTR_UPNP_SERVICE_LIST) + if not discovery_service_list: + return False + + services = discovery_service_list.get("service") + if not services: + discovery_service_ids: set[str] = set() + elif isinstance(services, list): + discovery_service_ids = {service.get("serviceId") for service in services} + else: + # Only one service defined (etree_to_dict failed to make a list) + discovery_service_ids = {services.get("serviceId")} + + if not DmrDevice.SERVICE_IDS.issubset(discovery_service_ids): + return False + + return True + + +async def _async_get_mac_address(hass: HomeAssistant, host: str) -> str | None: + """Get mac address from host name, IPv4 address, or IPv6 address.""" + # Help mypy, which has trouble with the async_add_executor_job + partial call + mac_address: str | None + # getmac has trouble using IPv6 addresses as the "hostname" parameter so + # assume host is an IP address, then handle the case it's not. + try: + ip_addr = ip_address(host) + except ValueError: + mac_address = await hass.async_add_executor_job( + partial(get_mac_address, hostname=host) + ) + else: + if ip_addr.version == 4: + mac_address = await hass.async_add_executor_job( + partial(get_mac_address, ip=host) + ) + else: + # Drop scope_id from IPv6 address by converting via int + ip_addr = IPv6Address(int(ip_addr)) + mac_address = await hass.async_add_executor_job( + partial(get_mac_address, ip6=str(ip_addr)) + ) + + if not mac_address: + return None + + return device_registry.format_mac(mac_address) diff --git a/homeassistant/components/dlna_dmr/manifest.json b/homeassistant/components/dlna_dmr/manifest.json index 3da3de8434f..acb2cfd4405 100644 --- a/homeassistant/components/dlna_dmr/manifest.json +++ b/homeassistant/components/dlna_dmr/manifest.json @@ -3,7 +3,7 @@ "name": "DLNA Digital Media Renderer", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/dlna_dmr", - "requirements": ["async-upnp-client==0.32.3"], + "requirements": ["async-upnp-client==0.33.0", "getmac==0.8.2"], "dependencies": ["ssdp"], "after_dependencies": ["media_source"], "ssdp": [ diff --git a/homeassistant/components/dlna_dmr/media_player.py b/homeassistant/components/dlna_dmr/media_player.py index d2c61e9a318..658adc58ba6 100644 --- a/homeassistant/components/dlna_dmr/media_player.py +++ b/homeassistant/components/dlna_dmr/media_player.py @@ -28,7 +28,7 @@ from homeassistant.components.media_player import ( RepeatMode, async_process_play_media_url, ) -from homeassistant.const import CONF_DEVICE_ID, CONF_TYPE, CONF_URL +from homeassistant.const import CONF_DEVICE_ID, CONF_MAC, CONF_TYPE, CONF_URL from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry, entity_registry from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -98,6 +98,7 @@ async def async_setup_entry( event_callback_url=entry.options.get(CONF_CALLBACK_URL_OVERRIDE), poll_availability=entry.options.get(CONF_POLL_AVAILABILITY, False), location=entry.data[CONF_URL], + mac_address=entry.data.get(CONF_MAC), browse_unfiltered=entry.options.get(CONF_BROWSE_UNFILTERED, False), ) @@ -139,6 +140,7 @@ class DlnaDmrEntity(MediaPlayerEntity): event_callback_url: str | None, poll_availability: bool, location: str, + mac_address: str | None, browse_unfiltered: bool, ) -> None: """Initialize DLNA DMR entity.""" @@ -148,6 +150,7 @@ class DlnaDmrEntity(MediaPlayerEntity): self._event_addr = EventListenAddr(None, event_port, event_callback_url) self.poll_availability = poll_availability self.location = location + self.mac_address = mac_address self.browse_unfiltered = browse_unfiltered self._device_lock = asyncio.Lock() @@ -272,6 +275,11 @@ class DlnaDmrEntity(MediaPlayerEntity): self.poll_availability = entry.options.get(CONF_POLL_AVAILABILITY, False) self.browse_unfiltered = entry.options.get(CONF_BROWSE_UNFILTERED, False) + new_mac_address = entry.data.get(CONF_MAC) + if new_mac_address != self.mac_address: + self.mac_address = new_mac_address + self._update_device_registry(set_mac=True) + new_port = entry.options.get(CONF_LISTEN_PORT) or 0 new_callback_url = entry.options.get(CONF_CALLBACK_URL_OVERRIDE) @@ -338,27 +346,42 @@ class DlnaDmrEntity(MediaPlayerEntity): _LOGGER.debug("Error while subscribing during device connect: %r", err) raise - if ( - not self.registry_entry - or not self.registry_entry.config_entry_id - or self.registry_entry.device_id - ): - return + self._update_device_registry() + + def _update_device_registry(self, set_mac: bool = False) -> None: + """Update the device registry with new information about the DMR.""" + if not self._device: + return # Can't get all the required information without a connection + + if not self.registry_entry or not self.registry_entry.config_entry_id: + return # No config registry entry to link to + + if self.registry_entry.device_id and not set_mac: + return # No new information + + connections = set() + # Connections based on the root device's UDN, and the DMR embedded + # device's UDN. They may be the same, if the DMR is the root device. + connections.add( + ( + device_registry.CONNECTION_UPNP, + self._device.profile_device.root_device.udn, + ) + ) + connections.add((device_registry.CONNECTION_UPNP, self._device.udn)) + + if self.mac_address: + # Connection based on MAC address, if known + connections.add( + # Device MAC is obtained from the config entry, which uses getmac + (device_registry.CONNECTION_NETWORK_MAC, self.mac_address) + ) # Create linked HA DeviceEntry now the information is known. dev_reg = device_registry.async_get(self.hass) device_entry = dev_reg.async_get_or_create( config_entry_id=self.registry_entry.config_entry_id, - # Connections are based on the root device's UDN, and the DMR - # embedded device's UDN. They may be the same, if the DMR is the - # root device. - connections={ - ( - device_registry.CONNECTION_UPNP, - self._device.profile_device.root_device.udn, - ), - (device_registry.CONNECTION_UPNP, self._device.udn), - }, + connections=connections, identifiers={(DOMAIN, self.unique_id)}, default_manufacturer=self._device.manufacturer, default_model=self._device.model_name, diff --git a/homeassistant/components/dlna_dmr/translations/de.json b/homeassistant/components/dlna_dmr/translations/de.json index f9f7ce997fe..6fa1f89e633 100644 --- a/homeassistant/components/dlna_dmr/translations/de.json +++ b/homeassistant/components/dlna_dmr/translations/de.json @@ -4,7 +4,7 @@ "already_configured": "Ger\u00e4t ist bereits konfiguriert", "alternative_integration": "Das Ger\u00e4t wird besser durch eine andere Integration unterst\u00fctzt", "cannot_connect": "Verbindung fehlgeschlagen", - "discovery_error": "Ein passendes DLNA-Ger\u00e4t konnte nicht gefunden werden", + "discovery_error": "Ein passendes DLNA Ger\u00e4t konnte nicht gefunden werden", "incomplete_config": "In der Konfiguration fehlt eine erforderliche Variable", "non_unique_id": "Mehrere Ger\u00e4te mit derselben eindeutigen ID gefunden", "not_dmr": "Ger\u00e4t ist kein unterst\u00fctzter Digital Media Renderer" @@ -33,7 +33,7 @@ "host": "Host" }, "description": "W\u00e4hle ein zu konfigurierendes Ger\u00e4t oder lasse es leer, um eine URL einzugeben.", - "title": "Erkannte DLNA-DMR-Ger\u00e4te" + "title": "Erkannte DLNA DMR-Ger\u00e4te" } } }, diff --git a/homeassistant/components/dlna_dmr/translations/en.json b/homeassistant/components/dlna_dmr/translations/en.json index 153f3f892d0..a343e13bb5d 100644 --- a/homeassistant/components/dlna_dmr/translations/en.json +++ b/homeassistant/components/dlna_dmr/translations/en.json @@ -16,7 +16,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Do you want to start set up?" + "description": "Do you want to start setup?" }, "import_turn_on": { "description": "Please turn on the device and click submit to continue migration" diff --git a/homeassistant/components/dlna_dmr/translations/it.json b/homeassistant/components/dlna_dmr/translations/it.json index e9b725de2d3..3b62a545b4e 100644 --- a/homeassistant/components/dlna_dmr/translations/it.json +++ b/homeassistant/components/dlna_dmr/translations/it.json @@ -16,7 +16,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Vuoi iniziare la configurazione?" + "description": "Vuoi avviare la configurazione?" }, "import_turn_on": { "description": "Accendi il dispositivo e fai clic su Invia per continuare la migrazione" diff --git a/homeassistant/components/dlna_dmr/translations/ko.json b/homeassistant/components/dlna_dmr/translations/ko.json index c25d0fcabfc..01510b1f539 100644 --- a/homeassistant/components/dlna_dmr/translations/ko.json +++ b/homeassistant/components/dlna_dmr/translations/ko.json @@ -1,16 +1,27 @@ { "config": { "abort": { - "alternative_integration": "\uae30\uae30\uac00 \ub2e4\ub978 \ud1b5\ud569\uad6c\uc131\uc694\uc18c\uc5d0\uc11c \ub354 \uc798 \uc9c0\uc6d0\ub429\ub2c8\ub2e4." + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "alternative_integration": "\uae30\uae30\uac00 \ub2e4\ub978 \ud1b5\ud569\uad6c\uc131\uc694\uc18c\uc5d0\uc11c \ub354 \uc798 \uc9c0\uc6d0\ub429\ub2c8\ub2e4.", + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" }, "step": { "confirm": { "description": "\uc124\uc815\uc744 \uc2dc\uc791\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" }, "manual": { + "data": { + "url": "URL \uc8fc\uc18c" + }, "description": "\uc7a5\uce58 \uc124\uba85 XML \ud30c\uc77c\uc758 URL" }, "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8" + }, "title": "DLNA DMR \uc7a5\uce58 \ubc1c\uacac" } } diff --git a/homeassistant/components/dlna_dmr/translations/pt.json b/homeassistant/components/dlna_dmr/translations/pt.json index ea08ade3537..bd919ef4d6d 100644 --- a/homeassistant/components/dlna_dmr/translations/pt.json +++ b/homeassistant/components/dlna_dmr/translations/pt.json @@ -2,10 +2,10 @@ "config": { "abort": { "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "confirm": { diff --git a/homeassistant/components/dlna_dmr/translations/sk.json b/homeassistant/components/dlna_dmr/translations/sk.json index 186ad1deb41..9cf81e25c1e 100644 --- a/homeassistant/components/dlna_dmr/translations/sk.json +++ b/homeassistant/components/dlna_dmr/translations/sk.json @@ -4,33 +4,53 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "alternative_integration": "Zariadenie je lep\u0161ie podporovan\u00e9 inou integr\u00e1ciou", "cannot_connect": "Nepodarilo sa pripoji\u0165", - "non_unique_id": "Viacer\u00e9 zariadenia n\u00e1jden\u00e9 s rovnak\u00fdm jedine\u010dn\u00fdm identifik\u00e1torom" + "discovery_error": "Nepodarilo sa n\u00e1js\u0165 zodpovedaj\u00face zariadenie DLNA", + "incomplete_config": "V konfigur\u00e1cii ch\u00fdba povinn\u00e1 premenn\u00e1", + "non_unique_id": "Viacer\u00e9 zariadenia n\u00e1jden\u00e9 s rovnak\u00fdm jedine\u010dn\u00fdm identifik\u00e1torom", + "not_dmr": "Zariadenie nie je podporovan\u00e9 DMR" }, "error": { - "cannot_connect": "Nepodarilo sa pripoji\u0165" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "not_dmr": "Zariadenie nie je podporovan\u00e9 DMR" }, "flow_title": "{name}", "step": { "confirm": { "description": "Chcete za\u010da\u0165 nastavova\u0165?" }, + "import_turn_on": { + "description": "Ak chcete pokra\u010dova\u0165 v migr\u00e1cii, zapnite zariadenie a kliknite na tla\u010didlo Odosla\u0165" + }, "manual": { "data": { "url": "URL" }, - "description": "URL adresa k XML s\u00faboru popisu zariadenia" + "description": "URL adresa k XML s\u00faboru popisu zariadenia", + "title": "Manu\u00e1lne pripojenie zariadenia DLNA DMR" }, "user": { "data": { "host": "Hostite\u013e" }, - "description": "Vyberte zariadenie, ktor\u00e9 chcete nakonfigurova\u0165, alebo nechajte pr\u00e1zdne a zadajte adresu URL" + "description": "Vyberte zariadenie, ktor\u00e9 chcete nakonfigurova\u0165, alebo nechajte pr\u00e1zdne a zadajte adresu URL", + "title": "Objaven\u00e9 zariadenia DLNA DMR" } } }, "options": { "error": { "invalid_url": "Neplatn\u00e1 adresa URL" + }, + "step": { + "init": { + "data": { + "browse_unfiltered": "Zobrazi\u0165 nekompatibiln\u00e9 m\u00e9di\u00e1 pri prehliadan\u00ed", + "callback_url_override": "Adresa URL sp\u00e4tn\u00e9ho volania prij\u00edma\u010da udalost\u00ed", + "listen_port": "Port prij\u00edma\u010da udalost\u00ed (n\u00e1hodn\u00fd, ak nie je nastaven\u00fd)", + "poll_availability": "Dopytovanie sa na dostupnos\u0165 zariadenia" + }, + "title": "Konfigur\u00e1cia DLNA Digital Media Renderer" + } } } } \ No newline at end of file diff --git a/homeassistant/components/dlna_dms/manifest.json b/homeassistant/components/dlna_dms/manifest.json index 3630e5ce309..f141b2c1519 100644 --- a/homeassistant/components/dlna_dms/manifest.json +++ b/homeassistant/components/dlna_dms/manifest.json @@ -3,7 +3,7 @@ "name": "DLNA Digital Media Server", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/dlna_dms", - "requirements": ["async-upnp-client==0.32.3"], + "requirements": ["async-upnp-client==0.33.0"], "dependencies": ["ssdp"], "after_dependencies": ["media_source"], "ssdp": [ diff --git a/homeassistant/components/dlna_dms/translations/de.json b/homeassistant/components/dlna_dms/translations/de.json index ebe1946707e..e32e9a945dd 100644 --- a/homeassistant/components/dlna_dms/translations/de.json +++ b/homeassistant/components/dlna_dms/translations/de.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "Ger\u00e4t ist bereits konfiguriert", "already_in_progress": "Der Konfigurationsablauf wird bereits ausgef\u00fchrt", - "bad_ssdp": "In den SSDP-Daten fehlt ein erforderlicher Wert", + "bad_ssdp": "In den SSDP Daten fehlt ein erforderlicher Wert", "no_devices_found": "Keine Ger\u00e4te im Netzwerk gefunden", "not_dms": "Das Ger\u00e4t ist kein unterst\u00fctzter Medienserver" }, @@ -17,7 +17,7 @@ "host": "Host" }, "description": "W\u00e4hle ein zu konfigurierendes Ger\u00e4t aus", - "title": "Erkannte DLNA DMA-Ger\u00e4te" + "title": "Erkannte DLNA DMA Ger\u00e4te" } } } diff --git a/homeassistant/components/dlna_dms/translations/en.json b/homeassistant/components/dlna_dms/translations/en.json index 6d07a25a27d..e799e6cc5b4 100644 --- a/homeassistant/components/dlna_dms/translations/en.json +++ b/homeassistant/components/dlna_dms/translations/en.json @@ -10,7 +10,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Do you want to start set up?" + "description": "Do you want to start setup?" }, "user": { "data": { diff --git a/homeassistant/components/dlna_dms/translations/it.json b/homeassistant/components/dlna_dms/translations/it.json index c671bc9b619..4184b186690 100644 --- a/homeassistant/components/dlna_dms/translations/it.json +++ b/homeassistant/components/dlna_dms/translations/it.json @@ -10,7 +10,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Vuoi iniziare la configurazione?" + "description": "Vuoi avviare la configurazione?" }, "user": { "data": { diff --git a/homeassistant/components/dlna_dms/translations/ko.json b/homeassistant/components/dlna_dms/translations/ko.json index 853a3eb2b1a..f50f5b8bbb8 100644 --- a/homeassistant/components/dlna_dms/translations/ko.json +++ b/homeassistant/components/dlna_dms/translations/ko.json @@ -1,10 +1,18 @@ { "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", + "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4" + }, "step": { "confirm": { "description": "\uc124\uc815\uc744 \uc2dc\uc791\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" }, "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8" + }, "title": "DLNA DMA \uc7a5\uce58 \ubc1c\uacac" } } diff --git a/homeassistant/components/dlna_dms/translations/pt.json b/homeassistant/components/dlna_dms/translations/pt.json index 1a5e788da4b..ae4e263ed1f 100644 --- a/homeassistant/components/dlna_dms/translations/pt.json +++ b/homeassistant/components/dlna_dms/translations/pt.json @@ -8,7 +8,7 @@ "step": { "user": { "data": { - "host": "Servidor" + "host": "Endere\u00e7o" } } } diff --git a/homeassistant/components/dlna_dms/translations/sk.json b/homeassistant/components/dlna_dms/translations/sk.json index 537609a4407..e1caff186a7 100644 --- a/homeassistant/components/dlna_dms/translations/sk.json +++ b/homeassistant/components/dlna_dms/translations/sk.json @@ -3,7 +3,9 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", - "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + "bad_ssdp": "V \u00fadajoch SSDP ch\u00fdba po\u017eadovan\u00e1 hodnota", + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", + "not_dms": "Zariadenie nie je podporovan\u00fdm serverom m\u00e9di\u00ed" }, "flow_title": "{name}", "step": { @@ -14,7 +16,8 @@ "data": { "host": "Hostite\u013e" }, - "description": "Vyberte zariadenie, ktor\u00e9 chcete nakonfigurova\u0165" + "description": "Vyberte zariadenie, ktor\u00e9 chcete nakonfigurova\u0165", + "title": "Objaven\u00e9 zariadenia DLNA DMA" } } } diff --git a/homeassistant/components/dnsip/config_flow.py b/homeassistant/components/dnsip/config_flow.py index a5b51f06a45..eb48095539a 100644 --- a/homeassistant/components/dnsip/config_flow.py +++ b/homeassistant/components/dnsip/config_flow.py @@ -19,6 +19,7 @@ from .const import ( CONF_HOSTNAME, CONF_IPV4, CONF_IPV6, + CONF_IPV6_V4, CONF_RESOLVER, CONF_RESOLVER_IPV6, DEFAULT_HOSTNAME, @@ -61,10 +62,12 @@ async def async_validate_hostname( tasks = await asyncio.gather( async_check(hostname, resolver_ipv4, "A"), async_check(hostname, resolver_ipv6, "AAAA"), + async_check(hostname, resolver_ipv4, "AAAA"), ) result[CONF_IPV4] = tasks[0] result[CONF_IPV6] = tasks[1] + result[CONF_IPV6_V4] = tasks[2] return result @@ -98,7 +101,15 @@ class DnsIPConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): validate = await async_validate_hostname(hostname, resolver, resolver_ipv6) - if not validate[CONF_IPV4] and not validate[CONF_IPV6]: + set_resolver = resolver + if validate[CONF_IPV6]: + set_resolver = resolver_ipv6 + + if ( + not validate[CONF_IPV4] + and not validate[CONF_IPV6] + and not validate[CONF_IPV6_V4] + ): errors["base"] = "invalid_hostname" else: await self.async_set_unique_id(hostname) @@ -110,11 +121,11 @@ class DnsIPConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): CONF_HOSTNAME: hostname, CONF_NAME: name, CONF_IPV4: validate[CONF_IPV4], - CONF_IPV6: validate[CONF_IPV6], + CONF_IPV6: validate[CONF_IPV6] or validate[CONF_IPV6_V4], }, options={ CONF_RESOLVER: resolver, - CONF_RESOLVER_IPV6: resolver_ipv6, + CONF_RESOLVER_IPV6: set_resolver, }, ) diff --git a/homeassistant/components/dnsip/const.py b/homeassistant/components/dnsip/const.py index 4e4d6f472ad..a4f2c2fee2d 100644 --- a/homeassistant/components/dnsip/const.py +++ b/homeassistant/components/dnsip/const.py @@ -9,6 +9,7 @@ CONF_RESOLVER = "resolver" CONF_RESOLVER_IPV6 = "resolver_ipv6" CONF_IPV4 = "ipv4" CONF_IPV6 = "ipv6" +CONF_IPV6_V4 = "ipv6_v4" DEFAULT_HOSTNAME = "myip.opendns.com" DEFAULT_IPV6 = False diff --git a/homeassistant/components/dnsip/translations/bs.json b/homeassistant/components/dnsip/translations/bs.json new file mode 100644 index 00000000000..e68ac527db6 --- /dev/null +++ b/homeassistant/components/dnsip/translations/bs.json @@ -0,0 +1,7 @@ +{ + "config": { + "error": { + "invalid_hostname": "Neplatn\u00fd n\u00e1zev hostitele" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/dnsip/translations/sk.json b/homeassistant/components/dnsip/translations/sk.json index 06557ec1e79..98b1d48c344 100644 --- a/homeassistant/components/dnsip/translations/sk.json +++ b/homeassistant/components/dnsip/translations/sk.json @@ -2,11 +2,28 @@ "config": { "error": { "invalid_hostname": "Neplatn\u00fd n\u00e1zov hostite\u013ea" + }, + "step": { + "user": { + "data": { + "hostname": "N\u00e1zov hostite\u013ea, pre ktor\u00fd sa m\u00e1 vykona\u0165 po\u017eiadavka DNS", + "resolver": "Resolver pre lookup IPV4", + "resolver_ipv6": "Resolver pre lookup IPV6" + } + } } }, "options": { "error": { "invalid_resolver": "Neplatn\u00e1 adresa IP pre resolver" + }, + "step": { + "init": { + "data": { + "resolver": "Resolver pre lookup IPV4", + "resolver_ipv6": "Resolver pre lookup IPV6" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/doods/image_processing.py b/homeassistant/components/doods/image_processing.py index 0265736c143..677aafc0b52 100644 --- a/homeassistant/components/doods/image_processing.py +++ b/homeassistant/components/doods/image_processing.py @@ -297,7 +297,10 @@ class Doods(ImageProcessingEntity): if self._aspect and abs((img_width / img_height) - self._aspect) > 0.1: _LOGGER.debug( - "The image aspect: %s and the detector aspect: %s differ by more than 0.1", + ( + "The image aspect: %s and the detector aspect: %s differ by more" + " than 0.1" + ), (img_width / img_height), self._aspect, ) diff --git a/homeassistant/components/doorbird/__init__.py b/homeassistant/components/doorbird/__init__.py index 62554f9662c..f5b2d2aad32 100644 --- a/homeassistant/components/doorbird/__init__.py +++ b/homeassistant/components/doorbird/__init__.py @@ -175,10 +175,12 @@ async def _async_register_events( except requests.exceptions.HTTPError: persistent_notification.async_create( hass, - "Doorbird configuration failed. Please verify that API " - "Operator permission is enabled for the Doorbird user. " - "A restart will be required once permissions have been " - "verified.", + ( + "Doorbird configuration failed. Please verify that API " + "Operator permission is enabled for the Doorbird user. " + "A restart will be required once permissions have been " + "verified." + ), title="Doorbird Configuration Failure", notification_id="doorbird_schedule_error", ) diff --git a/homeassistant/components/doorbird/translations/pt.json b/homeassistant/components/doorbird/translations/pt.json index ceb6c92004c..6f4f300f9a0 100644 --- a/homeassistant/components/doorbird/translations/pt.json +++ b/homeassistant/components/doorbird/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, diff --git a/homeassistant/components/doorbird/translations/sk.json b/homeassistant/components/doorbird/translations/sk.json index b6e9348c1fc..f318e43b7ef 100644 --- a/homeassistant/components/doorbird/translations/sk.json +++ b/homeassistant/components/doorbird/translations/sk.json @@ -2,7 +2,8 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", - "link_local_address": "Lok\u00e1lne adresy odkazov nie s\u00fa podporovan\u00e9" + "link_local_address": "Lok\u00e1lne adresy odkazov nie s\u00fa podporovan\u00e9", + "not_doorbird_device": "Toto zariadenie nie je DoorBird" }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", @@ -26,6 +27,9 @@ "init": { "data": { "events": "Zoznam udalost\u00ed oddelen\u00fdch \u010diarkou." + }, + "data_description": { + "events": "Pre ka\u017ed\u00fa udalos\u0165, ktor\u00fa chcete sledova\u0165, pridajte n\u00e1zov udalosti oddelen\u00fd \u010diarkou. Po ich zadan\u00ed sem ich pomocou aplik\u00e1cie DoorBird prira\u010fte ku konkr\u00e9tnej udalosti. \n\n Pr\u00edklad: niekto_stla\u010dil_tla\u010didlo, pohyb" } } } diff --git a/homeassistant/components/dovado/sensor.py b/homeassistant/components/dovado/sensor.py index ee23592ef2d..92b35af9d51 100644 --- a/homeassistant/components/dovado/sensor.py +++ b/homeassistant/components/dovado/sensor.py @@ -9,10 +9,11 @@ import voluptuous as vol from homeassistant.components.sensor import ( PLATFORM_SCHEMA, + SensorDeviceClass, SensorEntity, SensorEntityDescription, ) -from homeassistant.const import CONF_SENSORS, DATA_GIGABYTES, PERCENTAGE +from homeassistant.const import CONF_SENSORS, PERCENTAGE, UnitOfInformation from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -65,14 +66,16 @@ SENSOR_TYPES: tuple[DovadoSensorEntityDescription, ...] = ( identifier=SENSOR_UPLOAD, key="traffic modem tx", name="Sent", - native_unit_of_measurement=DATA_GIGABYTES, + native_unit_of_measurement=UnitOfInformation.GIGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:cloud-upload", ), DovadoSensorEntityDescription( identifier=SENSOR_DOWNLOAD, key="traffic modem rx", name="Received", - native_unit_of_measurement=DATA_GIGABYTES, + native_unit_of_measurement=UnitOfInformation.GIGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:cloud-download", ), ) diff --git a/homeassistant/components/dsmr/sensor.py b/homeassistant/components/dsmr/sensor.py index 5cbd3e14be0..36f734273f2 100644 --- a/homeassistant/components/dsmr/sensor.py +++ b/homeassistant/components/dsmr/sensor.py @@ -28,7 +28,7 @@ from homeassistant.const import ( CONF_HOST, CONF_PORT, EVENT_HOMEASSISTANT_STOP, - VOLUME_CUBIC_METERS, + UnitOfVolume, ) from homeassistant.core import CoreState, Event, HomeAssistant, callback from homeassistant.helpers.entity import DeviceInfo, EntityCategory @@ -55,7 +55,7 @@ from .const import ( LOGGER, ) -UNIT_CONVERSION = {"m3": VOLUME_CUBIC_METERS} +UNIT_CONVERSION = {"m3": UnitOfVolume.CUBIC_METERS} @dataclass @@ -97,6 +97,9 @@ SENSORS: tuple[DSMRSensorEntityDescription, ...] = ( name="Active tariff", obis_reference=obis_references.ELECTRICITY_ACTIVE_TARIFF, dsmr_versions={"2.2", "4", "5", "5B", "5L"}, + device_class=SensorDeviceClass.ENUM, + options=["low", "normal"], + translation_key="electricity_tariff", icon="mdi:flash", ), DSMRSensorEntityDescription( @@ -398,7 +401,7 @@ async def async_setup_entry( ) @Throttle(min_time_between_updates) - def update_entities_telegram(telegram: dict[str, DSMRObject]) -> None: + def update_entities_telegram(telegram: dict[str, DSMRObject] | None) -> None: """Update entities with latest telegram and trigger state update.""" # Make all device entities aware of new telegram for entity in entities: @@ -442,6 +445,11 @@ async def async_setup_entry( while hass.state == CoreState.not_running or hass.is_running: # Start DSMR asyncio.Protocol reader + + # Reflect connected state in devices state by setting an + # empty telegram resulting in `unknown` states + update_entities_telegram({}) + try: transport, protocol = await hass.loop.create_task(reader_factory()) @@ -469,8 +477,8 @@ async def async_setup_entry( protocol = None # Reflect disconnect state in devices state by setting an - # empty telegram resulting in `unknown` states - update_entities_telegram({}) + # None telegram resulting in `unavailable` states + update_entities_telegram(None) # throttle reconnect attempts await asyncio.sleep( @@ -484,11 +492,19 @@ async def async_setup_entry( transport = None protocol = None + # Reflect disconnect state in devices state by setting an + # None telegram resulting in `unavailable` states + update_entities_telegram(None) + # throttle reconnect attempts await asyncio.sleep( entry.data.get(CONF_RECONNECT_INTERVAL, DEFAULT_RECONNECT_INTERVAL) ) except CancelledError: + # Reflect disconnect state in devices state by setting an + # None telegram resulting in `unavailable` states + update_entities_telegram(None) + if stop_listener and ( hass.state == CoreState.not_running or hass.is_running ): @@ -531,7 +547,7 @@ class DSMREntity(SensorEntity): """Initialize entity.""" self.entity_description = entity_description self._entry = entry - self.telegram: dict[str, DSMRObject] = {} + self.telegram: dict[str, DSMRObject] | None = {} device_serial = entry.data[CONF_SERIAL_ID] device_name = DEVICE_NAME_ELECTRICITY @@ -548,16 +564,21 @@ class DSMREntity(SensorEntity): self._attr_unique_id = f"{device_serial}_{entity_description.key}" @callback - def update_data(self, telegram: dict[str, DSMRObject]) -> None: + def update_data(self, telegram: dict[str, DSMRObject] | None) -> None: """Update data.""" self.telegram = telegram - if self.hass and self.entity_description.obis_reference in self.telegram: + if self.hass and ( + telegram is None or self.entity_description.obis_reference in telegram + ): self.async_write_ha_state() def get_dsmr_object_attr(self, attribute: str) -> str | None: """Read attribute from last received telegram for this DSMR object.""" # Make sure telegram contains an object for this entities obis - if self.entity_description.obis_reference not in self.telegram: + if ( + self.telegram is None + or self.entity_description.obis_reference not in self.telegram + ): return None # Get the attribute value if the object has it @@ -565,6 +586,11 @@ class DSMREntity(SensorEntity): attr: str | None = getattr(dsmr_object, attribute) return attr + @property + def available(self) -> bool: + """Entity is only available if there is a telegram.""" + return self.telegram is not None + @property def native_value(self) -> StateType: """Return the state of sensor, if available, translate if needed.""" diff --git a/homeassistant/components/dsmr/strings.json b/homeassistant/components/dsmr/strings.json index cc9cd2ae86a..5db3a8bb863 100644 --- a/homeassistant/components/dsmr/strings.json +++ b/homeassistant/components/dsmr/strings.json @@ -40,6 +40,16 @@ "cannot_communicate": "Failed to communicate" } }, + "entity": { + "sensor": { + "electricity_tariff": { + "state": { + "low": "Low", + "normal": "Normal" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/dsmr/translations/ca.json b/homeassistant/components/dsmr/translations/ca.json index 1d61426ebea..555befc93b5 100644 --- a/homeassistant/components/dsmr/translations/ca.json +++ b/homeassistant/components/dsmr/translations/ca.json @@ -40,6 +40,16 @@ } } }, + "entity": { + "sensor": { + "electricity_tariff": { + "state": { + "low": "Baix", + "normal": "Normal" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/dsmr/translations/de.json b/homeassistant/components/dsmr/translations/de.json index fe94109634a..6050b344f7d 100644 --- a/homeassistant/components/dsmr/translations/de.json +++ b/homeassistant/components/dsmr/translations/de.json @@ -13,7 +13,7 @@ "step": { "setup_network": { "data": { - "dsmr_version": "DSMR-Version ausw\u00e4hlen", + "dsmr_version": "DSMR Version ausw\u00e4hlen", "host": "Host", "port": "Port" }, @@ -21,7 +21,7 @@ }, "setup_serial": { "data": { - "dsmr_version": "DSMR-Version ausw\u00e4hlen", + "dsmr_version": "DSMR Version ausw\u00e4hlen", "port": "Ger\u00e4t w\u00e4hlen" }, "title": "Ger\u00e4t" @@ -40,6 +40,16 @@ } } }, + "entity": { + "sensor": { + "electricity_tariff": { + "state": { + "low": "Niedrig", + "normal": "Normal" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/dsmr/translations/el.json b/homeassistant/components/dsmr/translations/el.json index 44d598fe811..faa1bcdbcff 100644 --- a/homeassistant/components/dsmr/translations/el.json +++ b/homeassistant/components/dsmr/translations/el.json @@ -42,6 +42,16 @@ } } }, + "entity": { + "sensor": { + "electricity_tariff": { + "state": { + "low": "\u03a7\u03b1\u03bc\u03b7\u03bb\u03ae", + "normal": "\u039a\u03b1\u03bd\u03bf\u03bd\u03b9\u03ba\u03ae" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/dsmr/translations/en.json b/homeassistant/components/dsmr/translations/en.json index 6f873729bc8..8d29cdd936f 100644 --- a/homeassistant/components/dsmr/translations/en.json +++ b/homeassistant/components/dsmr/translations/en.json @@ -40,6 +40,16 @@ } } }, + "entity": { + "sensor": { + "electricity_tariff": { + "state": { + "low": "Low", + "normal": "Normal" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/dsmr/translations/es.json b/homeassistant/components/dsmr/translations/es.json index a6363ebdea1..d8e3b189859 100644 --- a/homeassistant/components/dsmr/translations/es.json +++ b/homeassistant/components/dsmr/translations/es.json @@ -40,6 +40,16 @@ } } }, + "entity": { + "sensor": { + "electricity_tariff": { + "state": { + "low": "Valle", + "normal": "Normal" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/dsmr/translations/et.json b/homeassistant/components/dsmr/translations/et.json index 5e9131621d3..5f1640e6fcc 100644 --- a/homeassistant/components/dsmr/translations/et.json +++ b/homeassistant/components/dsmr/translations/et.json @@ -44,6 +44,16 @@ } } }, + "entity": { + "sensor": { + "electricity_tariff": { + "state": { + "low": "Madal", + "normal": "Tavaline" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/dsmr/translations/hu.json b/homeassistant/components/dsmr/translations/hu.json index 1bca962e2f5..5ad4abbcc73 100644 --- a/homeassistant/components/dsmr/translations/hu.json +++ b/homeassistant/components/dsmr/translations/hu.json @@ -44,6 +44,16 @@ } } }, + "entity": { + "sensor": { + "electricity_tariff": { + "state": { + "low": "Alacsony", + "normal": "Norm\u00e1l" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/dsmr/translations/id.json b/homeassistant/components/dsmr/translations/id.json index 2c1eeccca17..62a4b3c4cc5 100644 --- a/homeassistant/components/dsmr/translations/id.json +++ b/homeassistant/components/dsmr/translations/id.json @@ -40,6 +40,16 @@ } } }, + "entity": { + "sensor": { + "electricity_tariff": { + "state": { + "low": "Rendah", + "normal": "Normal" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/dsmr/translations/it.json b/homeassistant/components/dsmr/translations/it.json index 67ca750dd02..9ba5accb378 100644 --- a/homeassistant/components/dsmr/translations/it.json +++ b/homeassistant/components/dsmr/translations/it.json @@ -44,6 +44,16 @@ } } }, + "entity": { + "sensor": { + "electricity_tariff": { + "state": { + "low": "Basso", + "normal": "Normale" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/dsmr/translations/ko.json b/homeassistant/components/dsmr/translations/ko.json index 73837e1b4f2..4a97972621b 100644 --- a/homeassistant/components/dsmr/translations/ko.json +++ b/homeassistant/components/dsmr/translations/ko.json @@ -1,7 +1,25 @@ { "config": { "abort": { - "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "setup_network": { + "data": { + "host": "\ud638\uc2a4\ud2b8", + "port": "\ud3ec\ud2b8" + } + }, + "setup_serial_manual_path": { + "data": { + "port": "USB \uc7a5\uce58 \uacbd\ub85c" + } + } } }, "options": { diff --git a/homeassistant/components/dsmr/translations/nl.json b/homeassistant/components/dsmr/translations/nl.json index ed6171279f1..24f801015ed 100644 --- a/homeassistant/components/dsmr/translations/nl.json +++ b/homeassistant/components/dsmr/translations/nl.json @@ -44,6 +44,16 @@ } } }, + "entity": { + "sensor": { + "electricity_tariff": { + "state": { + "low": "Laag", + "normal": "Normaal" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/dsmr/translations/no.json b/homeassistant/components/dsmr/translations/no.json index ef9d2798f2b..fe2b0aa45f4 100644 --- a/homeassistant/components/dsmr/translations/no.json +++ b/homeassistant/components/dsmr/translations/no.json @@ -40,6 +40,16 @@ } } }, + "entity": { + "sensor": { + "electricity_tariff": { + "state": { + "low": "Lav", + "normal": "Vanlig" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/dsmr/translations/pt-BR.json b/homeassistant/components/dsmr/translations/pt-BR.json index 97722b24982..c31d4faecb0 100644 --- a/homeassistant/components/dsmr/translations/pt-BR.json +++ b/homeassistant/components/dsmr/translations/pt-BR.json @@ -42,6 +42,16 @@ } } }, + "entity": { + "sensor": { + "electricity_tariff": { + "state": { + "low": "Baixo", + "normal": "Normal" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/dsmr/translations/pt.json b/homeassistant/components/dsmr/translations/pt.json index 311a677d6de..1583477d4ab 100644 --- a/homeassistant/components/dsmr/translations/pt.json +++ b/homeassistant/components/dsmr/translations/pt.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "error": { "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" @@ -10,7 +10,7 @@ "step": { "setup_network": { "data": { - "host": "Servidor" + "host": "Endere\u00e7o" }, "title": "Seleccione o endere\u00e7o de liga\u00e7\u00e3o" }, diff --git a/homeassistant/components/dsmr/translations/ru.json b/homeassistant/components/dsmr/translations/ru.json index 0ee5d273156..ac1530c12aa 100644 --- a/homeassistant/components/dsmr/translations/ru.json +++ b/homeassistant/components/dsmr/translations/ru.json @@ -40,6 +40,16 @@ } } }, + "entity": { + "sensor": { + "electricity_tariff": { + "state": { + "low": "\u041d\u0438\u0437\u043a\u0438\u0439", + "normal": "\u041d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u044b\u0439" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/dsmr/translations/sk.json b/homeassistant/components/dsmr/translations/sk.json index d9e7b05f578..33e7f11fc67 100644 --- a/homeassistant/components/dsmr/translations/sk.json +++ b/homeassistant/components/dsmr/translations/sk.json @@ -8,9 +8,17 @@ "error": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "cannot_communicate": "Komunik\u00e1cia zlyhala", - "cannot_connect": "Nepodarilo sa pripoji\u0165" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "few": "Pr\u00e1zdnych", + "many": "Pr\u00e1zdnych", + "one": "Pr\u00e1zdny", + "other": "Pr\u00e1zdny" }, "step": { + "few": "Pr\u00e1zdnych", + "many": "Pr\u00e1zdnych", + "one": "Pr\u00e1zdny", + "other": "Pr\u00e1zdny", "setup_network": { "data": { "dsmr_version": "Vyberte verziu DSMR", @@ -40,6 +48,16 @@ } } }, + "entity": { + "sensor": { + "electricity_tariff": { + "state": { + "low": "N\u00edzka", + "normal": "Norm\u00e1lna" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/dsmr/translations/zh-Hant.json b/homeassistant/components/dsmr/translations/zh-Hant.json index 427011d5585..155edc7f4b8 100644 --- a/homeassistant/components/dsmr/translations/zh-Hant.json +++ b/homeassistant/components/dsmr/translations/zh-Hant.json @@ -40,6 +40,16 @@ } } }, + "entity": { + "sensor": { + "electricity_tariff": { + "state": { + "low": "\u4f4e", + "normal": "\u6b63\u5e38" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/dsmr_reader/definitions.py b/homeassistant/components/dsmr_reader/definitions.py index ac61837afec..0721819e312 100644 --- a/homeassistant/components/dsmr_reader/definitions.py +++ b/homeassistant/components/dsmr_reader/definitions.py @@ -12,16 +12,16 @@ from homeassistant.components.sensor import ( ) from homeassistant.const import ( CURRENCY_EURO, - ELECTRIC_CURRENT_AMPERE, - ELECTRIC_POTENTIAL_VOLT, - ENERGY_KILO_WATT_HOUR, - POWER_KILO_WATT, - VOLUME_CUBIC_METERS, + UnitOfElectricCurrent, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfPower, + UnitOfVolume, ) from homeassistant.util import dt as dt_util -PRICE_EUR_KWH: Final = f"EUR/{ENERGY_KILO_WATT_HOUR}" -PRICE_EUR_M3: Final = f"EUR/{VOLUME_CUBIC_METERS}" +PRICE_EUR_KWH: Final = f"EUR/{UnitOfEnergy.KILO_WATT_HOUR}" +PRICE_EUR_M3: Final = f"EUR/{UnitOfVolume.CUBIC_METERS}" def dsmr_transform(value): @@ -50,42 +50,42 @@ SENSORS: tuple[DSMRReaderSensorEntityDescription, ...] = ( key="dsmr/reading/electricity_delivered_1", name="Low tariff usage", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, state_class=SensorStateClass.TOTAL_INCREASING, ), DSMRReaderSensorEntityDescription( key="dsmr/reading/electricity_returned_1", name="Low tariff returned", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, state_class=SensorStateClass.TOTAL_INCREASING, ), DSMRReaderSensorEntityDescription( key="dsmr/reading/electricity_delivered_2", name="High tariff usage", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, state_class=SensorStateClass.TOTAL_INCREASING, ), DSMRReaderSensorEntityDescription( key="dsmr/reading/electricity_returned_2", name="High tariff returned", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, state_class=SensorStateClass.TOTAL_INCREASING, ), DSMRReaderSensorEntityDescription( key="dsmr/reading/electricity_currently_delivered", name="Current power usage", device_class=SensorDeviceClass.POWER, - native_unit_of_measurement=POWER_KILO_WATT, + native_unit_of_measurement=UnitOfPower.KILO_WATT, state_class=SensorStateClass.MEASUREMENT, ), DSMRReaderSensorEntityDescription( key="dsmr/reading/electricity_currently_returned", name="Current power return", device_class=SensorDeviceClass.POWER, - native_unit_of_measurement=POWER_KILO_WATT, + native_unit_of_measurement=UnitOfPower.KILO_WATT, state_class=SensorStateClass.MEASUREMENT, ), DSMRReaderSensorEntityDescription( @@ -93,7 +93,7 @@ SENSORS: tuple[DSMRReaderSensorEntityDescription, ...] = ( name="Current power usage L1", entity_registry_enabled_default=False, device_class=SensorDeviceClass.POWER, - native_unit_of_measurement=POWER_KILO_WATT, + native_unit_of_measurement=UnitOfPower.KILO_WATT, state_class=SensorStateClass.MEASUREMENT, ), DSMRReaderSensorEntityDescription( @@ -101,7 +101,7 @@ SENSORS: tuple[DSMRReaderSensorEntityDescription, ...] = ( name="Current power usage L2", entity_registry_enabled_default=False, device_class=SensorDeviceClass.POWER, - native_unit_of_measurement=POWER_KILO_WATT, + native_unit_of_measurement=UnitOfPower.KILO_WATT, state_class=SensorStateClass.MEASUREMENT, ), DSMRReaderSensorEntityDescription( @@ -109,7 +109,7 @@ SENSORS: tuple[DSMRReaderSensorEntityDescription, ...] = ( name="Current power usage L3", entity_registry_enabled_default=False, device_class=SensorDeviceClass.POWER, - native_unit_of_measurement=POWER_KILO_WATT, + native_unit_of_measurement=UnitOfPower.KILO_WATT, state_class=SensorStateClass.MEASUREMENT, ), DSMRReaderSensorEntityDescription( @@ -117,7 +117,7 @@ SENSORS: tuple[DSMRReaderSensorEntityDescription, ...] = ( name="Current power return L1", entity_registry_enabled_default=False, device_class=SensorDeviceClass.POWER, - native_unit_of_measurement=POWER_KILO_WATT, + native_unit_of_measurement=UnitOfPower.KILO_WATT, state_class=SensorStateClass.MEASUREMENT, ), DSMRReaderSensorEntityDescription( @@ -125,7 +125,7 @@ SENSORS: tuple[DSMRReaderSensorEntityDescription, ...] = ( name="Current power return L2", entity_registry_enabled_default=False, device_class=SensorDeviceClass.POWER, - native_unit_of_measurement=POWER_KILO_WATT, + native_unit_of_measurement=UnitOfPower.KILO_WATT, state_class=SensorStateClass.MEASUREMENT, ), DSMRReaderSensorEntityDescription( @@ -133,7 +133,7 @@ SENSORS: tuple[DSMRReaderSensorEntityDescription, ...] = ( name="Current power return L3", entity_registry_enabled_default=False, device_class=SensorDeviceClass.POWER, - native_unit_of_measurement=POWER_KILO_WATT, + native_unit_of_measurement=UnitOfPower.KILO_WATT, state_class=SensorStateClass.MEASUREMENT, ), DSMRReaderSensorEntityDescription( @@ -141,7 +141,7 @@ SENSORS: tuple[DSMRReaderSensorEntityDescription, ...] = ( name="Gas meter usage", entity_registry_enabled_default=False, icon="mdi:fire", - native_unit_of_measurement=VOLUME_CUBIC_METERS, + native_unit_of_measurement=UnitOfVolume.CUBIC_METERS, state_class=SensorStateClass.TOTAL_INCREASING, ), DSMRReaderSensorEntityDescription( @@ -149,7 +149,7 @@ SENSORS: tuple[DSMRReaderSensorEntityDescription, ...] = ( name="Current voltage L1", entity_registry_enabled_default=False, device_class=SensorDeviceClass.VOLTAGE, - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, state_class=SensorStateClass.MEASUREMENT, ), DSMRReaderSensorEntityDescription( @@ -157,7 +157,7 @@ SENSORS: tuple[DSMRReaderSensorEntityDescription, ...] = ( name="Current voltage L2", entity_registry_enabled_default=False, device_class=SensorDeviceClass.VOLTAGE, - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, state_class=SensorStateClass.MEASUREMENT, ), DSMRReaderSensorEntityDescription( @@ -165,7 +165,7 @@ SENSORS: tuple[DSMRReaderSensorEntityDescription, ...] = ( name="Current voltage L3", entity_registry_enabled_default=False, device_class=SensorDeviceClass.VOLTAGE, - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, state_class=SensorStateClass.MEASUREMENT, ), DSMRReaderSensorEntityDescription( @@ -173,7 +173,7 @@ SENSORS: tuple[DSMRReaderSensorEntityDescription, ...] = ( name="Phase power current L1", entity_registry_enabled_default=False, device_class=SensorDeviceClass.CURRENT, - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, state_class=SensorStateClass.MEASUREMENT, ), DSMRReaderSensorEntityDescription( @@ -181,7 +181,7 @@ SENSORS: tuple[DSMRReaderSensorEntityDescription, ...] = ( name="Phase power current L2", entity_registry_enabled_default=False, device_class=SensorDeviceClass.CURRENT, - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, state_class=SensorStateClass.MEASUREMENT, ), DSMRReaderSensorEntityDescription( @@ -189,7 +189,7 @@ SENSORS: tuple[DSMRReaderSensorEntityDescription, ...] = ( name="Phase power current L3", entity_registry_enabled_default=False, device_class=SensorDeviceClass.CURRENT, - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, state_class=SensorStateClass.MEASUREMENT, ), DSMRReaderSensorEntityDescription( @@ -203,14 +203,14 @@ SENSORS: tuple[DSMRReaderSensorEntityDescription, ...] = ( key="dsmr/consumption/gas/delivered", name="Gas usage", device_class=SensorDeviceClass.GAS, - native_unit_of_measurement=VOLUME_CUBIC_METERS, + native_unit_of_measurement=UnitOfVolume.CUBIC_METERS, state_class=SensorStateClass.TOTAL_INCREASING, ), DSMRReaderSensorEntityDescription( key="dsmr/consumption/gas/currently_delivered", name="Current gas usage", device_class=SensorDeviceClass.GAS, - native_unit_of_measurement=VOLUME_CUBIC_METERS, + native_unit_of_measurement=UnitOfVolume.CUBIC_METERS, state_class=SensorStateClass.MEASUREMENT, ), DSMRReaderSensorEntityDescription( @@ -222,63 +222,69 @@ SENSORS: tuple[DSMRReaderSensorEntityDescription, ...] = ( ), DSMRReaderSensorEntityDescription( key="dsmr/day-consumption/electricity1", - name="Low tariff usage", + name="Low tariff usage (daily)", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, + state_class=SensorStateClass.TOTAL_INCREASING, ), DSMRReaderSensorEntityDescription( key="dsmr/day-consumption/electricity2", - name="High tariff usage", + name="High tariff usage (daily)", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, + state_class=SensorStateClass.TOTAL_INCREASING, ), DSMRReaderSensorEntityDescription( key="dsmr/day-consumption/electricity1_returned", - name="Low tariff return", + name="Low tariff return (daily)", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, + state_class=SensorStateClass.TOTAL_INCREASING, ), DSMRReaderSensorEntityDescription( key="dsmr/day-consumption/electricity2_returned", - name="High tariff return", + name="High tariff return (daily)", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, + state_class=SensorStateClass.TOTAL_INCREASING, ), DSMRReaderSensorEntityDescription( key="dsmr/day-consumption/electricity_merged", - name="Power usage total", + name="Power usage total (daily)", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, + state_class=SensorStateClass.TOTAL_INCREASING, ), DSMRReaderSensorEntityDescription( key="dsmr/day-consumption/electricity_returned_merged", - name="Power return total", + name="Power return total (daily)", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, + state_class=SensorStateClass.TOTAL_INCREASING, ), DSMRReaderSensorEntityDescription( key="dsmr/day-consumption/electricity1_cost", - name="Low tariff cost", + name="Low tariff cost (daily)", icon="mdi:currency-eur", native_unit_of_measurement=CURRENCY_EURO, ), DSMRReaderSensorEntityDescription( key="dsmr/day-consumption/electricity2_cost", - name="High tariff cost", + name="High tariff cost (daily)", icon="mdi:currency-eur", native_unit_of_measurement=CURRENCY_EURO, ), DSMRReaderSensorEntityDescription( key="dsmr/day-consumption/electricity_cost_merged", - name="Power total cost", + name="Power total cost (daily)", icon="mdi:currency-eur", native_unit_of_measurement=CURRENCY_EURO, ), DSMRReaderSensorEntityDescription( key="dsmr/day-consumption/gas", - name="Gas usage", + name="Gas usage (daily)", icon="mdi:counter", - native_unit_of_measurement=VOLUME_CUBIC_METERS, + native_unit_of_measurement=UnitOfVolume.CUBIC_METERS, ), DSMRReaderSensorEntityDescription( key="dsmr/day-consumption/gas_cost", @@ -399,37 +405,37 @@ SENSORS: tuple[DSMRReaderSensorEntityDescription, ...] = ( key="dsmr/current-month/electricity1", name="Current month low tariff usage", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, ), DSMRReaderSensorEntityDescription( key="dsmr/current-month/electricity2", name="Current month high tariff usage", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, ), DSMRReaderSensorEntityDescription( key="dsmr/current-month/electricity1_returned", name="Current month low tariff returned", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, ), DSMRReaderSensorEntityDescription( key="dsmr/current-month/electricity2_returned", name="Current month high tariff returned", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, ), DSMRReaderSensorEntityDescription( key="dsmr/current-month/electricity_merged", name="Current month power usage total", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, ), DSMRReaderSensorEntityDescription( key="dsmr/current-month/electricity_returned_merged", name="Current month power return total", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, ), DSMRReaderSensorEntityDescription( key="dsmr/current-month/electricity1_cost", @@ -453,7 +459,7 @@ SENSORS: tuple[DSMRReaderSensorEntityDescription, ...] = ( key="dsmr/current-month/gas", name="Current month gas usage", icon="mdi:counter", - native_unit_of_measurement=VOLUME_CUBIC_METERS, + native_unit_of_measurement=UnitOfVolume.CUBIC_METERS, ), DSMRReaderSensorEntityDescription( key="dsmr/current-month/gas_cost", @@ -477,37 +483,37 @@ SENSORS: tuple[DSMRReaderSensorEntityDescription, ...] = ( key="dsmr/current-year/electricity1", name="Current year low tariff usage", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, ), DSMRReaderSensorEntityDescription( key="dsmr/current-year/electricity2", name="Current year high tariff usage", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, ), DSMRReaderSensorEntityDescription( key="dsmr/current-year/electricity1_returned", name="Current year low tariff returned", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, ), DSMRReaderSensorEntityDescription( key="dsmr/current-year/electricity2_returned", - name="Current year high tariff usage", + name="Current year high tariff returned", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, ), DSMRReaderSensorEntityDescription( key="dsmr/current-year/electricity_merged", name="Current year power usage total", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, ), DSMRReaderSensorEntityDescription( key="dsmr/current-year/electricity_returned_merged", name="Current year power returned total", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, ), DSMRReaderSensorEntityDescription( key="dsmr/current-year/electricity1_cost", @@ -531,7 +537,7 @@ SENSORS: tuple[DSMRReaderSensorEntityDescription, ...] = ( key="dsmr/current-year/gas", name="Current year gas usage", icon="mdi:counter", - native_unit_of_measurement=VOLUME_CUBIC_METERS, + native_unit_of_measurement=UnitOfVolume.CUBIC_METERS, ), DSMRReaderSensorEntityDescription( key="dsmr/current-year/gas_cost", @@ -551,4 +557,24 @@ SENSORS: tuple[DSMRReaderSensorEntityDescription, ...] = ( icon="mdi:currency-eur", native_unit_of_measurement=CURRENCY_EURO, ), + DSMRReaderSensorEntityDescription( + key="dsmr/consumption/quarter-hour-peak-electricity/average_delivered", + name="Previous quarter-hour peak usage", + device_class=SensorDeviceClass.ENERGY, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, + ), + DSMRReaderSensorEntityDescription( + key="dsmr/consumption/quarter-hour-peak-electricity/read_at_start", + name="Quarter-hour peak start time", + entity_registry_enabled_default=False, + device_class=SensorDeviceClass.TIMESTAMP, + state=dt_util.parse_datetime, + ), + DSMRReaderSensorEntityDescription( + key="dsmr/consumption/quarter-hour-peak-electricity/read_at_end", + name="Quarter-hour peak end time", + entity_registry_enabled_default=False, + device_class=SensorDeviceClass.TIMESTAMP, + state=dt_util.parse_datetime, + ), ) diff --git a/homeassistant/components/dsmr_reader/translations/ko.json b/homeassistant/components/dsmr_reader/translations/ko.json new file mode 100644 index 00000000000..416fb8d164e --- /dev/null +++ b/homeassistant/components/dsmr_reader/translations/ko.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/dsmr_reader/translations/nl.json b/homeassistant/components/dsmr_reader/translations/nl.json index 703ac8614c4..9010d20f75b 100644 --- a/homeassistant/components/dsmr_reader/translations/nl.json +++ b/homeassistant/components/dsmr_reader/translations/nl.json @@ -2,6 +2,17 @@ "config": { "abort": { "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." + }, + "step": { + "confirm": { + "description": "Zorg ervoor dat u de 'split topic' gegevensbronnen configureert in DSMR Reader." + } + } + }, + "issues": { + "deprecated_yaml": { + "description": "Het configureren van DSMR Reader met YAML wordt verwijderd. \n\nUw bestaande YAML-configuratie is automatisch in de gebruikersinterface ge\u00efmporteerd. \n\nVerwijder de DSMR Reader YAML-configuratie uit uw configuration.yaml-bestand en start Home Assistant opnieuw om dit probleem op te lossen.", + "title": "De DSMR Reader YAML-configuratie wordt verwijderd" } } } \ No newline at end of file diff --git a/homeassistant/components/dsmr_reader/translations/pt.json b/homeassistant/components/dsmr_reader/translations/pt.json new file mode 100644 index 00000000000..25538aa0036 --- /dev/null +++ b/homeassistant/components/dsmr_reader/translations/pt.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "J\u00e1 configurado. Apenas uma \u00fanica configura\u00e7\u00e3o \u00e9 poss\u00edvel." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/dsmr_reader/translations/sk.json b/homeassistant/components/dsmr_reader/translations/sk.json index c294bc45d7c..e4c3aef8b19 100644 --- a/homeassistant/components/dsmr_reader/translations/sk.json +++ b/homeassistant/components/dsmr_reader/translations/sk.json @@ -2,6 +2,17 @@ "config": { "abort": { "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, + "step": { + "confirm": { + "description": "Uistite sa, \u017ee ste nakonfigurovali zdroje \u00fadajov \u201erozdelen\u00e1 t\u00e9ma\u201c v aplik\u00e1cii DSMR Reader." + } + } + }, + "issues": { + "deprecated_yaml": { + "description": "Konfigur\u00e1cia DSMR Reader pomocou YAML sa odstra\u0148uje. \n\n Va\u0161a existuj\u00faca konfigur\u00e1cia YAML bola importovan\u00e1 do pou\u017e\u00edvate\u013esk\u00e9ho rozhrania automaticky. \n\n Odstr\u00e1\u0148te konfigur\u00e1ciu DSMR Reader YAML zo s\u00faboru configuration.yaml a re\u0161tartujte Home Assistant, aby ste tento probl\u00e9m vyrie\u0161ili.", + "title": "Konfigur\u00e1cia DSMR Reader sa odstra\u0148uje" } } } \ No newline at end of file diff --git a/homeassistant/components/dte_energy_bridge/sensor.py b/homeassistant/components/dte_energy_bridge/sensor.py index b97a8eb83eb..34c78ed824b 100644 --- a/homeassistant/components/dte_energy_bridge/sensor.py +++ b/homeassistant/components/dte_energy_bridge/sensor.py @@ -9,10 +9,11 @@ import voluptuous as vol from homeassistant.components.sensor import ( PLATFORM_SCHEMA, + SensorDeviceClass, SensorEntity, SensorStateClass, ) -from homeassistant.const import CONF_NAME, POWER_KILO_WATT +from homeassistant.const import CONF_NAME, UnitOfPower from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -26,8 +27,6 @@ CONF_VERSION = "version" DEFAULT_NAME = "Current Energy Usage" DEFAULT_VERSION = 1 -ICON = "mdi:flash" - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( { vol.Required(CONF_IP_ADDRESS): cv.string, @@ -56,8 +55,8 @@ def setup_platform( class DteEnergyBridgeSensor(SensorEntity): """Implementation of the DTE Energy Bridge sensors.""" - _attr_icon = ICON - _attr_native_unit_of_measurement = POWER_KILO_WATT + _attr_device_class = SensorDeviceClass.POWER + _attr_native_unit_of_measurement = UnitOfPower.KILO_WATT _attr_state_class = SensorStateClass.MEASUREMENT def __init__(self, ip_address, name, version): diff --git a/homeassistant/components/dublin_bus_transport/sensor.py b/homeassistant/components/dublin_bus_transport/sensor.py index 6b9d47aecb3..657e3165976 100644 --- a/homeassistant/components/dublin_bus_transport/sensor.py +++ b/homeassistant/components/dublin_bus_transport/sensor.py @@ -14,7 +14,7 @@ import requests import voluptuous as vol from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity -from homeassistant.const import CONF_NAME, TIME_MINUTES +from homeassistant.const import CONF_NAME, UnitOfTime from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -117,7 +117,7 @@ class DublinPublicTransportSensor(SensorEntity): @property def native_unit_of_measurement(self): """Return the unit this state is expressed in.""" - return TIME_MINUTES + return UnitOfTime.MINUTES @property def icon(self): diff --git a/homeassistant/components/dunehd/translations/pt.json b/homeassistant/components/dunehd/translations/pt.json index 7fe3a6078c3..3d6f36b1064 100644 --- a/homeassistant/components/dunehd/translations/pt.json +++ b/homeassistant/components/dunehd/translations/pt.json @@ -5,8 +5,8 @@ }, "error": { "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", - "cannot_connect": "Falha na liga\u00e7\u00e3o", - "invalid_host": "Nome de servidor ou endere\u00e7o IP inv\u00e1lido." + "cannot_connect": "A liga\u00e7\u00e3o falhou", + "invalid_host": "Endere\u00e7o IP ou hostname inv\u00e1lido." }, "step": { "user": { diff --git a/homeassistant/components/dunehd/translations/sk.json b/homeassistant/components/dunehd/translations/sk.json index 55314bfd4fa..dcd9eb31d5f 100644 --- a/homeassistant/components/dunehd/translations/sk.json +++ b/homeassistant/components/dunehd/translations/sk.json @@ -12,7 +12,8 @@ "user": { "data": { "host": "Hostite\u013e" - } + }, + "description": "Uistite sa, \u017ee je v\u00e1\u0161 prehr\u00e1va\u010d zapnut\u00fd." } } } diff --git a/homeassistant/components/dynalite/cover.py b/homeassistant/components/dynalite/cover.py index bbca16d3db6..98005ccee0a 100644 --- a/homeassistant/components/dynalite/cover.py +++ b/homeassistant/components/dynalite/cover.py @@ -1,10 +1,10 @@ """Support for the Dynalite channels as covers.""" +from contextlib import suppress from typing import Any from homeassistant.components.cover import ( ATTR_CURRENT_POSITION, - DEVICE_CLASSES, CoverDeviceClass, CoverEntity, ) @@ -14,8 +14,6 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from .dynalitebase import DynaliteBase, async_setup_entry_base -DEFAULT_COVER_CLASS = CoverDeviceClass.SHUTTER - async def async_setup_entry( hass: HomeAssistant, @@ -39,13 +37,12 @@ class DynaliteCover(DynaliteBase, CoverEntity): """Representation of a Dynalite Channel as a Home Assistant Cover.""" @property - def device_class(self) -> str: + def device_class(self) -> CoverDeviceClass: """Return the class of the device.""" - dev_cls = self._device.device_class - ret_val = DEFAULT_COVER_CLASS - if dev_cls in DEVICE_CLASSES: - ret_val = dev_cls - return ret_val + if device_class := self._device.device_class: + with suppress(ValueError): + return CoverDeviceClass(device_class) + return CoverDeviceClass.SHUTTER @property def current_cover_position(self) -> int: diff --git a/homeassistant/components/eafm/sensor.py b/homeassistant/components/eafm/sensor.py index a695a38bb4b..2650aa35489 100644 --- a/homeassistant/components/eafm/sensor.py +++ b/homeassistant/components/eafm/sensor.py @@ -7,7 +7,7 @@ import async_timeout from homeassistant.components.sensor import SensorEntity, SensorStateClass from homeassistant.config_entries import ConfigEntry -from homeassistant.const import LENGTH_METERS +from homeassistant.const import UnitOfLength from homeassistant.core import HomeAssistant from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.device_registry import DeviceEntryType @@ -23,7 +23,7 @@ from .const import DOMAIN _LOGGER = logging.getLogger(__name__) UNIT_MAPPING = { - "http://qudt.org/1.1/vocab/unit#Meter": LENGTH_METERS, + "http://qudt.org/1.1/vocab/unit#Meter": UnitOfLength.METERS, } diff --git a/homeassistant/components/eafm/translations/sk.json b/homeassistant/components/eafm/translations/sk.json index a3d22ff7b90..9fd91efa24b 100644 --- a/homeassistant/components/eafm/translations/sk.json +++ b/homeassistant/components/eafm/translations/sk.json @@ -1,13 +1,16 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "no_stations": "Nena\u0161li sa \u017eiadne stanice na monitorovanie vytopenia." }, "step": { "user": { "data": { "station": "Stanica" - } + }, + "description": "Vyberte stanicu, ktor\u00fa chcete sledova\u0165", + "title": "Sledujte povod\u0148ov\u00fa monitorovaciu stanicu" } } } diff --git a/homeassistant/components/ebox/sensor.py b/homeassistant/components/ebox/sensor.py index 3e1a2fa2413..ea52b1e422e 100644 --- a/homeassistant/components/ebox/sensor.py +++ b/homeassistant/components/ebox/sensor.py @@ -14,6 +14,7 @@ import voluptuous as vol from homeassistant.components.sensor import ( PLATFORM_SCHEMA, + SensorDeviceClass, SensorEntity, SensorEntityDescription, ) @@ -22,9 +23,9 @@ from homeassistant.const import ( CONF_NAME, CONF_PASSWORD, CONF_USERNAME, - DATA_GIGABITS, PERCENTAGE, - TIME_DAYS, + UnitOfInformation, + UnitOfTime, ) from homeassistant.core import HomeAssistant from homeassistant.exceptions import PlatformNotReady @@ -61,67 +62,77 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="limit", name="Data limit", - native_unit_of_measurement=DATA_GIGABITS, + native_unit_of_measurement=UnitOfInformation.GIGABITS, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:download", ), SensorEntityDescription( key="days_left", name="Days left", - native_unit_of_measurement=TIME_DAYS, + native_unit_of_measurement=UnitOfTime.DAYS, icon="mdi:calendar-today", ), SensorEntityDescription( key="before_offpeak_download", name="Download before offpeak", - native_unit_of_measurement=DATA_GIGABITS, + native_unit_of_measurement=UnitOfInformation.GIGABITS, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:download", ), SensorEntityDescription( key="before_offpeak_upload", name="Upload before offpeak", - native_unit_of_measurement=DATA_GIGABITS, + native_unit_of_measurement=UnitOfInformation.GIGABITS, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:upload", ), SensorEntityDescription( key="before_offpeak_total", name="Total before offpeak", - native_unit_of_measurement=DATA_GIGABITS, + native_unit_of_measurement=UnitOfInformation.GIGABITS, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:download", ), SensorEntityDescription( key="offpeak_download", name="Offpeak download", - native_unit_of_measurement=DATA_GIGABITS, + native_unit_of_measurement=UnitOfInformation.GIGABITS, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:download", ), SensorEntityDescription( key="offpeak_upload", name="Offpeak Upload", - native_unit_of_measurement=DATA_GIGABITS, + native_unit_of_measurement=UnitOfInformation.GIGABITS, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:upload", ), SensorEntityDescription( key="offpeak_total", name="Offpeak Total", - native_unit_of_measurement=DATA_GIGABITS, + native_unit_of_measurement=UnitOfInformation.GIGABITS, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:download", ), SensorEntityDescription( key="download", name="Download", - native_unit_of_measurement=DATA_GIGABITS, + native_unit_of_measurement=UnitOfInformation.GIGABITS, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:download", ), SensorEntityDescription( key="upload", name="Upload", - native_unit_of_measurement=DATA_GIGABITS, + native_unit_of_measurement=UnitOfInformation.GIGABITS, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:upload", ), SensorEntityDescription( key="total", name="Total", - native_unit_of_measurement=DATA_GIGABITS, + native_unit_of_measurement=UnitOfInformation.GIGABITS, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:download", ), ) diff --git a/homeassistant/components/ebusd/const.py b/homeassistant/components/ebusd/const.py index ce631297db6..9bc489f40f2 100644 --- a/homeassistant/components/ebusd/const.py +++ b/homeassistant/components/ebusd/const.py @@ -1,11 +1,11 @@ """Constants for ebus component.""" from homeassistant.components.sensor import SensorDeviceClass from homeassistant.const import ( - ENERGY_KILO_WATT_HOUR, PERCENTAGE, - PRESSURE_BAR, - TEMP_CELSIUS, - TIME_SECONDS, + UnitOfEnergy, + UnitOfPressure, + UnitOfTemperature, + UnitOfTime, ) DOMAIN = "ebusd" @@ -17,21 +17,21 @@ SENSOR_TYPES = { "700": { "ActualFlowTemperatureDesired": [ "Hc1ActualFlowTempDesired", - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, None, 0, SensorDeviceClass.TEMPERATURE, ], "MaxFlowTemperatureDesired": [ "Hc1MaxFlowTempDesired", - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, None, 0, SensorDeviceClass.TEMPERATURE, ], "MinFlowTemperatureDesired": [ "Hc1MinFlowTempDesired", - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, None, 0, SensorDeviceClass.TEMPERATURE, @@ -39,28 +39,28 @@ SENSOR_TYPES = { "PumpStatus": ["Hc1PumpStatus", None, "mdi:toggle-switch", 2, None], "HCSummerTemperatureLimit": [ "Hc1SummerTempLimit", - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, "mdi:weather-sunny", 0, SensorDeviceClass.TEMPERATURE, ], "HolidayTemperature": [ "HolidayTemp", - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, None, 0, SensorDeviceClass.TEMPERATURE, ], "HWTemperatureDesired": [ "HwcTempDesired", - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, None, 0, SensorDeviceClass.TEMPERATURE, ], "HWActualTemperature": [ "HwcStorageTemp", - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, None, 0, SensorDeviceClass.TEMPERATURE, @@ -73,39 +73,45 @@ SENSOR_TYPES = { "HWTimerSaturday": ["hwcTimer.Saturday", None, "mdi:timer-outline", 1, None], "HWTimerSunday": ["hwcTimer.Sunday", None, "mdi:timer-outline", 1, None], "HWOperativeMode": ["HwcOpMode", None, "mdi:math-compass", 3, None], - "WaterPressure": ["WaterPressure", PRESSURE_BAR, "mdi:water-pump", 0, None], + "WaterPressure": [ + "WaterPressure", + UnitOfPressure.BAR, + "mdi:water-pump", + 0, + SensorDeviceClass.PRESSURE, + ], "Zone1RoomZoneMapping": ["z1RoomZoneMapping", None, "mdi:label", 0, None], "Zone1NightTemperature": [ "z1NightTemp", - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, "mdi:weather-night", 0, SensorDeviceClass.TEMPERATURE, ], "Zone1DayTemperature": [ "z1DayTemp", - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, "mdi:weather-sunny", 0, SensorDeviceClass.TEMPERATURE, ], "Zone1HolidayTemperature": [ "z1HolidayTemp", - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, None, 0, SensorDeviceClass.TEMPERATURE, ], "Zone1RoomTemperature": [ "z1RoomTemp", - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, None, 0, SensorDeviceClass.TEMPERATURE, ], "Zone1ActualRoomTemperatureDesired": [ "z1ActualRoomTempDesired", - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, None, 0, SensorDeviceClass.TEMPERATURE, @@ -126,37 +132,37 @@ SENSOR_TYPES = { "Zone1OperativeMode": ["z1OpMode", None, "mdi:math-compass", 3, None], "ContinuosHeating": [ "ContinuosHeating", - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, "mdi:weather-snowy", 0, SensorDeviceClass.TEMPERATURE, ], "PowerEnergyConsumptionLastMonth": [ "PrEnergySumHcLastMonth", - ENERGY_KILO_WATT_HOUR, + UnitOfEnergy.KILO_WATT_HOUR, "mdi:flash", 0, - None, + SensorDeviceClass.ENERGY, ], "PowerEnergyConsumptionThisMonth": [ "PrEnergySumHcThisMonth", - ENERGY_KILO_WATT_HOUR, + UnitOfEnergy.KILO_WATT_HOUR, "mdi:flash", 0, - None, + SensorDeviceClass.ENERGY, ], }, "ehp": { "HWTemperature": [ "HwcTemp", - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, None, 4, SensorDeviceClass.TEMPERATURE, ], "OutsideTemp": [ "OutsideTemp", - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, None, 4, SensorDeviceClass.TEMPERATURE, @@ -165,57 +171,63 @@ SENSOR_TYPES = { "bai": { "HotWaterTemperature": [ "HwcTemp", - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, None, 4, SensorDeviceClass.TEMPERATURE, ], "StorageTemperature": [ "StorageTemp", - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, None, 4, SensorDeviceClass.TEMPERATURE, ], "DesiredStorageTemperature": [ "StorageTempDesired", - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, None, 0, SensorDeviceClass.TEMPERATURE, ], "OutdoorsTemperature": [ "OutdoorstempSensor", - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, None, 4, SensorDeviceClass.TEMPERATURE, ], - "WaterPressure": ["WaterPressure", PRESSURE_BAR, "mdi:pipe", 4, None], + "WaterPressure": [ + "WaterPressure", + UnitOfPressure.BAR, + "mdi:pipe", + 4, + SensorDeviceClass.PRESSURE, + ], "AverageIgnitionTime": [ "averageIgnitiontime", - TIME_SECONDS, + UnitOfTime.SECONDS, "mdi:av-timer", 0, - None, + SensorDeviceClass.DURATION, ], "MaximumIgnitionTime": [ "maxIgnitiontime", - TIME_SECONDS, + UnitOfTime.SECONDS, "mdi:av-timer", 0, - None, + SensorDeviceClass.DURATION, ], "MinimumIgnitionTime": [ "minIgnitiontime", - TIME_SECONDS, + UnitOfTime.SECONDS, "mdi:av-timer", 0, - None, + SensorDeviceClass.DURATION, ], "ReturnTemperature": [ "ReturnTemp", - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, None, 4, SensorDeviceClass.TEMPERATURE, @@ -224,14 +236,14 @@ SENSOR_TYPES = { "HeatingSwitch": ["HeatingSwitch", None, "mdi:toggle-switch", 2, None], "DesiredFlowTemperature": [ "FlowTempDesired", - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, None, 0, SensorDeviceClass.TEMPERATURE, ], "FlowTemperature": [ "FlowTemp", - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, None, 4, SensorDeviceClass.TEMPERATURE, @@ -239,25 +251,25 @@ SENSOR_TYPES = { "Flame": ["Flame", None, "mdi:toggle-switch", 2, None], "PowerEnergyConsumptionHeatingCircuit": [ "PrEnergySumHc1", - ENERGY_KILO_WATT_HOUR, + UnitOfEnergy.KILO_WATT_HOUR, "mdi:flash", 0, - None, + SensorDeviceClass.ENERGY, ], "PowerEnergyConsumptionHotWaterCircuit": [ "PrEnergySumHwc1", - ENERGY_KILO_WATT_HOUR, + UnitOfEnergy.KILO_WATT_HOUR, "mdi:flash", 0, - None, + SensorDeviceClass.ENERGY, ], "RoomThermostat": ["DCRoomthermostat", None, "mdi:toggle-switch", 2, None], "HeatingPartLoad": [ "PartloadHcKW", - ENERGY_KILO_WATT_HOUR, + UnitOfEnergy.KILO_WATT_HOUR, "mdi:flash", 0, - None, + SensorDeviceClass.ENERGY, ], "StateNumber": ["StateNumber", None, "mdi:fire", 3, None], "ModulationPercentage": [ diff --git a/homeassistant/components/ebusd/translations/sk.json b/homeassistant/components/ebusd/translations/sk.json index 674290ece76..1cd469f7901 100644 --- a/homeassistant/components/ebusd/translations/sk.json +++ b/homeassistant/components/ebusd/translations/sk.json @@ -1,5 +1,6 @@ { "state": { - "day": "De\u0148" + "day": "De\u0148", + "night": "Noc" } } \ No newline at end of file diff --git a/homeassistant/components/ecoal_boiler/sensor.py b/homeassistant/components/ecoal_boiler/sensor.py index 5c8bf926fce..06dfec9ff01 100644 --- a/homeassistant/components/ecoal_boiler/sensor.py +++ b/homeassistant/components/ecoal_boiler/sensor.py @@ -2,7 +2,7 @@ from __future__ import annotations from homeassistant.components.sensor import SensorDeviceClass, SensorEntity -from homeassistant.const import TEMP_CELSIUS +from homeassistant.const import UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType @@ -31,7 +31,7 @@ class EcoalTempSensor(SensorEntity): """Representation of a temperature sensor using ecoal status data.""" _attr_device_class = SensorDeviceClass.TEMPERATURE - _attr_native_unit_of_measurement = TEMP_CELSIUS + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS def __init__(self, ecoal_contr, name, status_attr): """Initialize the sensor.""" diff --git a/homeassistant/components/ecobee/climate.py b/homeassistant/components/ecobee/climate.py index 028979b2a85..24609bcd0af 100644 --- a/homeassistant/components/ecobee/climate.py +++ b/homeassistant/components/ecobee/climate.py @@ -26,7 +26,7 @@ from homeassistant.const import ( PRECISION_TENTHS, STATE_OFF, STATE_ON, - TEMP_FAHRENHEIT, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant, ServiceCall from homeassistant.helpers import entity_platform @@ -181,11 +181,13 @@ async def async_setup_entry( thermostat = data.ecobee.get_thermostat(index) if not thermostat["modelNumber"] in ECOBEE_MODEL_TO_NAME: _LOGGER.error( - "Model number for ecobee thermostat %s not recognized. " - "Please visit this link to open a new issue: " - "https://github.com/home-assistant/core/issues " - "and include the following information: " - "Unrecognized model number: %s", + ( + "Model number for ecobee thermostat %s not recognized. " + "Please visit this link to open a new issue: " + "https://github.com/home-assistant/core/issues " + "and include the following information: " + "Unrecognized model number: %s" + ), thermostat["name"], thermostat["modelNumber"], ) @@ -304,7 +306,7 @@ class Thermostat(ClimateEntity): """A thermostat class for Ecobee.""" _attr_precision = PRECISION_TENTHS - _attr_temperature_unit = TEMP_FAHRENHEIT + _attr_temperature_unit = UnitOfTemperature.FAHRENHEIT def __init__(self, data, thermostat_index, thermostat): """Initialize the thermostat.""" @@ -766,12 +768,12 @@ class Thermostat(ClimateEntity): cool_temp = TemperatureConverter.convert( service_data[ATTR_COOL_TEMP], self.hass.config.units.temperature_unit, - TEMP_FAHRENHEIT, + UnitOfTemperature.FAHRENHEIT, ) heat_temp = TemperatureConverter.convert( service_data[ATTR_HEAT_TEMP], self.hass.config.units.temperature_unit, - TEMP_FAHRENHEIT, + UnitOfTemperature.FAHRENHEIT, ) start_date = service_data.get(ATTR_START_DATE) start_time = service_data.get(ATTR_START_TIME) @@ -794,8 +796,10 @@ class Thermostat(ClimateEntity): } _LOGGER.debug( - "Creating a vacation on thermostat %s with name %s, cool temp %s, heat temp %s, " - "and the following other parameters: %s", + ( + "Creating a vacation on thermostat %s with name %s, cool temp %s, heat" + " temp %s, and the following other parameters: %s" + ), self.name, vacation_name, cool_temp, diff --git a/homeassistant/components/ecobee/config_flow.py b/homeassistant/components/ecobee/config_flow.py index 0bc9df2ea78..304730e6dbf 100644 --- a/homeassistant/components/ecobee/config_flow.py +++ b/homeassistant/components/ecobee/config_flow.py @@ -94,7 +94,8 @@ class EcobeeFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): } except (HomeAssistantError, KeyError): _LOGGER.debug( - "No valid ecobee.conf configuration found for import, delegating to user step" + "No valid ecobee.conf configuration found for import, delegating to" + " user step" ) return await self.async_step_user( user_input={ @@ -106,7 +107,8 @@ class EcobeeFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): if await self.hass.async_add_executor_job(ecobee.refresh_tokens): # Credentials found and validated; create the entry. _LOGGER.debug( - "Valid ecobee configuration found for import, creating configuration entry" + "Valid ecobee configuration found for import, creating configuration" + " entry" ) return self.async_create_entry( title=DOMAIN, diff --git a/homeassistant/components/ecobee/const.py b/homeassistant/components/ecobee/const.py index 4a318f2be3c..3f629fa48f2 100644 --- a/homeassistant/components/ecobee/const.py +++ b/homeassistant/components/ecobee/const.py @@ -38,6 +38,7 @@ ECOBEE_MODEL_TO_NAME = { "apolloSmart": "ecobee4 Smart", "vulcanSmart": "ecobee4 Smart", "aresSmart": "ecobee Smart Premium", + "artemisSmart": "ecobee Smart Enhanced", } PLATFORMS = [ diff --git a/homeassistant/components/ecobee/humidifier.py b/homeassistant/components/ecobee/humidifier.py index 93d658a9ddc..502e84b7866 100644 --- a/homeassistant/components/ecobee/humidifier.py +++ b/homeassistant/components/ecobee/humidifier.py @@ -137,7 +137,8 @@ class EcobeeHumidifier(HumidifierEntity): """Set humidifier mode (auto, off, manual).""" if mode.lower() not in (self.available_modes): raise ValueError( - f"Invalid mode value: {mode} Valid values are {', '.join(self.available_modes)}." + f"Invalid mode value: {mode} Valid values are" + f" {', '.join(self.available_modes)}." ) self.data.ecobee.set_humidifier_mode(self.thermostat_index, mode) diff --git a/homeassistant/components/ecobee/sensor.py b/homeassistant/components/ecobee/sensor.py index 30949e36f8e..b9d61742838 100644 --- a/homeassistant/components/ecobee/sensor.py +++ b/homeassistant/components/ecobee/sensor.py @@ -16,7 +16,7 @@ from homeassistant.const import ( CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, CONCENTRATION_PARTS_PER_MILLION, PERCENTAGE, - TEMP_FAHRENHEIT, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo @@ -43,7 +43,7 @@ SENSOR_TYPES: tuple[EcobeeSensorEntityDescription, ...] = ( EcobeeSensorEntityDescription( key="temperature", name="Temperature", - native_unit_of_measurement=TEMP_FAHRENHEIT, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, runtime_key=None, diff --git a/homeassistant/components/ecobee/translations/de.json b/homeassistant/components/ecobee/translations/de.json index d9d138936cb..8320e3cc505 100644 --- a/homeassistant/components/ecobee/translations/de.json +++ b/homeassistant/components/ecobee/translations/de.json @@ -9,7 +9,7 @@ }, "step": { "authorize": { - "description": "Bitte autorisiere diese App unter https://www.ecobee.com/consumerportal/index.html mit Pincode:\n\n{pin}\n\nDr\u00fccke dann auf Senden.", + "description": "Bitte autorisiere diese App unter https://www.ecobee.com/consumerportal/index.html mit PIN Code:\n\n{pin}\n\nDr\u00fccke dann auf Senden.", "title": "App auf ecobee.com autorisieren" }, "user": { diff --git a/homeassistant/components/ecobee/translations/en_GB.json b/homeassistant/components/ecobee/translations/en-GB.json similarity index 100% rename from homeassistant/components/ecobee/translations/en_GB.json rename to homeassistant/components/ecobee/translations/en-GB.json diff --git a/homeassistant/components/ecobee/translations/lb.json b/homeassistant/components/ecobee/translations/lb.json index 1769da0253b..274a0bf1eae 100644 --- a/homeassistant/components/ecobee/translations/lb.json +++ b/homeassistant/components/ecobee/translations/lb.json @@ -9,7 +9,7 @@ }, "step": { "authorize": { - "description": "Autoris\u00e9iert d\u00ebs App op https://www.ecobee.com/consumerportal/index.html mam Pin Code:\n\n{pin}\n\nKlickt dann op ofsch\u00e9cken.", + "description": "Autoris\u00e9iert d\u00ebs App op https://www.ecobee.com/consumerportal/index.html mam PIN-Code:\n\n{pin}\n\nKlickt dann op ofsch\u00e9cken.", "title": "App autoris\u00e9ieren op ecobee.com" }, "user": { diff --git a/homeassistant/components/ecobee/translations/sk.json b/homeassistant/components/ecobee/translations/sk.json index 619f56c68f3..4d98c173559 100644 --- a/homeassistant/components/ecobee/translations/sk.json +++ b/homeassistant/components/ecobee/translations/sk.json @@ -4,16 +4,19 @@ "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." }, "error": { + "pin_request_failed": "Chyba pri vy\u017eiadan\u00ed PIN od ecobee; skontrolujte spr\u00e1vnos\u0165 k\u013e\u00fa\u010da API.", "token_request_failed": "Chyba pri vy\u017eiadan\u00ed tokenov od ecobee; pros\u00edm sk\u00faste znova." }, "step": { "authorize": { - "description": "Autorizujte t\u00fato aplik\u00e1ciu na https://www.ecobee.com/consumerportal/index.html pomocou k\u00f3du PIN: \n\n {pin}\n\n Potom stla\u010dte tla\u010didlo Odosla\u0165." + "description": "Autorizujte t\u00fato aplik\u00e1ciu na https://www.ecobee.com/consumerportal/index.html pomocou k\u00f3du PIN: \n\n {pin}\n\n Potom stla\u010dte tla\u010didlo Odosla\u0165.", + "title": "Overte aplik\u00e1ciu na ecobee.com" }, "user": { "data": { "api_key": "API k\u013e\u00fa\u010d" }, + "description": "Zadajte k\u013e\u00fa\u010d API z\u00edskan\u00fd z ecobee.com.", "title": "ecobee API k\u013e\u00fa\u010d" } } diff --git a/homeassistant/components/econet/__init__.py b/homeassistant/components/econet/__init__.py index 981d6cbad23..ee8db43baf2 100644 --- a/homeassistant/components/econet/__init__.py +++ b/homeassistant/components/econet/__init__.py @@ -13,7 +13,7 @@ from pyeconet.errors import ( ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_EMAIL, CONF_PASSWORD, TEMP_FAHRENHEIT, Platform +from homeassistant.const import CONF_EMAIL, CONF_PASSWORD, Platform, UnitOfTemperature from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers.dispatcher import async_dispatcher_connect, dispatcher_send @@ -131,7 +131,7 @@ class EcoNetEntity(Entity): @property def available(self): - """Return if the the device is online or not.""" + """Return if the device is online or not.""" return self._econet.connected @property @@ -156,4 +156,4 @@ class EcoNetEntity(Entity): @property def temperature_unit(self): """Return the unit of measurement.""" - return TEMP_FAHRENHEIT + return UnitOfTemperature.FAHRENHEIT diff --git a/homeassistant/components/econet/manifest.json b/homeassistant/components/econet/manifest.json index f8df1a4134e..19455d8dffb 100644 --- a/homeassistant/components/econet/manifest.json +++ b/homeassistant/components/econet/manifest.json @@ -3,7 +3,7 @@ "name": "Rheem EcoNet Products", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/econet", - "requirements": ["pyeconet==0.1.15"], + "requirements": ["pyeconet==0.1.17"], "codeowners": ["@vangorra", "@w1ll1am23"], "iot_class": "cloud_push", "loggers": ["paho_mqtt", "pyeconet"] diff --git a/homeassistant/components/econet/sensor.py b/homeassistant/components/econet/sensor.py index e39f55423d4..130cc6fce37 100644 --- a/homeassistant/components/econet/sensor.py +++ b/homeassistant/components/econet/sensor.py @@ -1,9 +1,9 @@ """Support for Rheem EcoNet water heaters.""" from pyeconet.equipment import EquipmentType -from homeassistant.components.sensor import SensorDeviceClass, SensorEntity +from homeassistant.components.sensor import SensorEntity from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ENERGY_KILO_WATT_HOUR, PERCENTAGE, VOLUME_GALLONS +from homeassistant.const import PERCENTAGE, UnitOfEnergy, UnitOfVolume from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -39,10 +39,10 @@ SENSOR_NAMES_TO_UNIT_OF_MEASUREMENT = { AVAILABLE_HOT_WATER: PERCENTAGE, COMPRESSOR_HEALTH: PERCENTAGE, OVERRIDE_STATUS: None, - WATER_USAGE_TODAY: VOLUME_GALLONS, + WATER_USAGE_TODAY: UnitOfVolume.GALLONS, POWER_USAGE_TODAY: None, # Depends on unit type ALERT_COUNT: None, - WIFI_SIGNAL: SensorDeviceClass.SIGNAL_STRENGTH, + WIFI_SIGNAL: None, RUNNING_STATE: None, # This is just a string } @@ -97,16 +97,16 @@ class EcoNetSensor(EcoNetEntity, SensorEntity): if self._econet.energy_type == ENERGY_KILO_BRITISH_THERMAL_UNIT.upper(): unit_of_measurement = ENERGY_KILO_BRITISH_THERMAL_UNIT else: - unit_of_measurement = ENERGY_KILO_WATT_HOUR + unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR return unit_of_measurement @property - def name(self): + def name(self) -> str: """Return the name of the entity.""" return f"{self._econet.device_name}_{self._device_name}" @property - def unique_id(self): + def unique_id(self) -> str: """Return the unique ID of the entity.""" return ( f"{self._econet.device_id}_{self._econet.device_name}_{self._device_name}" diff --git a/homeassistant/components/econet/strings.json b/homeassistant/components/econet/strings.json index 358f159cd7e..6e81085a9bf 100644 --- a/homeassistant/components/econet/strings.json +++ b/homeassistant/components/econet/strings.json @@ -11,7 +11,7 @@ }, "step": { "user": { - "title": "Setup Rheem EcoNet Account", + "title": "Set up Rheem EcoNet Account", "data": { "email": "[%key:common::config_flow::data::email%]", "password": "[%key:common::config_flow::data::password%]" diff --git a/homeassistant/components/econet/translations/ca.json b/homeassistant/components/econet/translations/ca.json index c53914f8cb9..5889725b80d 100644 --- a/homeassistant/components/econet/translations/ca.json +++ b/homeassistant/components/econet/translations/ca.json @@ -15,7 +15,7 @@ "email": "Correu electr\u00f2nic", "password": "Contrasenya" }, - "title": "Configuraci\u00f3 del compte Rheem EcoNet" + "title": "Configuraci\u00f3 de compte Rheem EcoNet" } } } diff --git a/homeassistant/components/econet/translations/en.json b/homeassistant/components/econet/translations/en.json index ad499b0e37c..0fe18cf4f3c 100644 --- a/homeassistant/components/econet/translations/en.json +++ b/homeassistant/components/econet/translations/en.json @@ -15,7 +15,7 @@ "email": "Email", "password": "Password" }, - "title": "Setup Rheem EcoNet Account" + "title": "Set up Rheem EcoNet Account" } } } diff --git a/homeassistant/components/econet/translations/it.json b/homeassistant/components/econet/translations/it.json index 1c966d7b5ec..69ed5be1876 100644 --- a/homeassistant/components/econet/translations/it.json +++ b/homeassistant/components/econet/translations/it.json @@ -15,7 +15,7 @@ "email": "Email", "password": "Password" }, - "title": "Imposta account Rheem EcoNet" + "title": "Configura l'account Rheem EcoNet" } } } diff --git a/homeassistant/components/econet/translations/no.json b/homeassistant/components/econet/translations/no.json index f54cedffda8..91aeaea500a 100644 --- a/homeassistant/components/econet/translations/no.json +++ b/homeassistant/components/econet/translations/no.json @@ -15,7 +15,7 @@ "email": "E-post", "password": "Passord" }, - "title": "Konfigurer Rheem EcoNet-konto" + "title": "Sett opp Rheem EcoNet-konto" } } } diff --git a/homeassistant/components/econet/translations/pt.json b/homeassistant/components/econet/translations/pt.json index 25aaf514180..38a70491190 100644 --- a/homeassistant/components/econet/translations/pt.json +++ b/homeassistant/components/econet/translations/pt.json @@ -2,12 +2,12 @@ "config": { "abort": { "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "user": { "data": { - "email": "Email", + "email": "", "password": "Palavra-passe" } } diff --git a/homeassistant/components/econet/translations/sk.json b/homeassistant/components/econet/translations/sk.json index aa68b67f678..922b64d9502 100644 --- a/homeassistant/components/econet/translations/sk.json +++ b/homeassistant/components/econet/translations/sk.json @@ -14,7 +14,8 @@ "data": { "email": "Email", "password": "Heslo" - } + }, + "title": "Nastavte \u00fa\u010det Rheem EcoNet" } } } diff --git a/homeassistant/components/ecowitt/sensor.py b/homeassistant/components/ecowitt/sensor.py index 200ae1d73fc..5bbe22b6972 100644 --- a/homeassistant/components/ecowitt/sensor.py +++ b/homeassistant/components/ecowitt/sensor.py @@ -15,25 +15,19 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - AREA_SQUARE_METERS, CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, CONCENTRATION_PARTS_PER_MILLION, DEGREE, - ELECTRIC_POTENTIAL_VOLT, - LENGTH_INCHES, - LENGTH_KILOMETERS, - LENGTH_MILES, - LENGTH_MILLIMETERS, LIGHT_LUX, PERCENTAGE, - POWER_WATT, - PRESSURE_HPA, - PRESSURE_INHG, - SPEED_KILOMETERS_PER_HOUR, - SPEED_MILES_PER_HOUR, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, UV_INDEX, + UnitOfElectricPotential, + UnitOfIrradiance, + UnitOfLength, + UnitOfPrecipitationDepth, + UnitOfPressure, + UnitOfSpeed, + UnitOfTemperature, UnitOfVolumetricFlux, ) from homeassistant.core import HomeAssistant @@ -74,7 +68,8 @@ ECOWITT_SENSORS_MAPPING: Final = { ), EcoWittSensorTypes.WATT_METERS_SQUARED: SensorEntityDescription( key="WATT_METERS_SQUARED", - native_unit_of_measurement=f"{POWER_WATT}/{AREA_SQUARE_METERS}", + device_class=SensorDeviceClass.IRRADIANCE, + native_unit_of_measurement=UnitOfIrradiance.WATTS_PER_SQUARE_METER, state_class=SensorStateClass.MEASUREMENT, ), EcoWittSensorTypes.UV_INDEX: SensorEntityDescription( @@ -103,7 +98,7 @@ ECOWITT_SENSORS_MAPPING: Final = { EcoWittSensorTypes.BATTERY_VOLTAGE: SensorEntityDescription( key="BATTERY_VOLTAGE", device_class=SensorDeviceClass.VOLTAGE, - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, state_class=SensorStateClass.MEASUREMENT, ), EcoWittSensorTypes.CO2_PPM: SensorEntityDescription( @@ -124,7 +119,7 @@ ECOWITT_SENSORS_MAPPING: Final = { EcoWittSensorTypes.VOLTAGE: SensorEntityDescription( key="VOLTAGE", device_class=SensorDeviceClass.VOLTAGE, - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, state_class=SensorStateClass.MEASUREMENT, ), EcoWittSensorTypes.LIGHTNING_COUNT: SensorEntityDescription( @@ -135,23 +130,25 @@ ECOWITT_SENSORS_MAPPING: Final = { EcoWittSensorTypes.TEMPERATURE_C: SensorEntityDescription( key="TEMPERATURE_C", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, ), EcoWittSensorTypes.TEMPERATURE_F: SensorEntityDescription( key="TEMPERATURE_F", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_FAHRENHEIT, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, state_class=SensorStateClass.MEASUREMENT, ), EcoWittSensorTypes.RAIN_COUNT_MM: SensorEntityDescription( key="RAIN_COUNT_MM", - native_unit_of_measurement=LENGTH_MILLIMETERS, + native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + device_class=SensorDeviceClass.PRECIPITATION, state_class=SensorStateClass.TOTAL, ), EcoWittSensorTypes.RAIN_COUNT_INCHES: SensorEntityDescription( key="RAIN_COUNT_INCHES", - native_unit_of_measurement=LENGTH_INCHES, + native_unit_of_measurement=UnitOfPrecipitationDepth.INCHES, + device_class=SensorDeviceClass.PRECIPITATION, state_class=SensorStateClass.TOTAL, ), EcoWittSensorTypes.RAIN_RATE_MM: SensorEntityDescription( @@ -168,34 +165,36 @@ ECOWITT_SENSORS_MAPPING: Final = { ), EcoWittSensorTypes.LIGHTNING_DISTANCE_KM: SensorEntityDescription( key="LIGHTNING_DISTANCE_KM", - native_unit_of_measurement=LENGTH_KILOMETERS, + native_unit_of_measurement=UnitOfLength.KILOMETERS, state_class=SensorStateClass.MEASUREMENT, ), EcoWittSensorTypes.LIGHTNING_DISTANCE_MILES: SensorEntityDescription( key="LIGHTNING_DISTANCE_MILES", - native_unit_of_measurement=LENGTH_MILES, + native_unit_of_measurement=UnitOfLength.MILES, state_class=SensorStateClass.MEASUREMENT, ), EcoWittSensorTypes.SPEED_KPH: SensorEntityDescription( key="SPEED_KPH", - native_unit_of_measurement=SPEED_KILOMETERS_PER_HOUR, + device_class=SensorDeviceClass.WIND_SPEED, + native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, state_class=SensorStateClass.MEASUREMENT, ), EcoWittSensorTypes.SPEED_MPH: SensorEntityDescription( key="SPEED_MPH", - native_unit_of_measurement=SPEED_MILES_PER_HOUR, + device_class=SensorDeviceClass.WIND_SPEED, + native_unit_of_measurement=UnitOfSpeed.MILES_PER_HOUR, state_class=SensorStateClass.MEASUREMENT, ), EcoWittSensorTypes.PRESSURE_HPA: SensorEntityDescription( key="PRESSURE_HPA", device_class=SensorDeviceClass.PRESSURE, - native_unit_of_measurement=PRESSURE_HPA, + native_unit_of_measurement=UnitOfPressure.HPA, state_class=SensorStateClass.MEASUREMENT, ), EcoWittSensorTypes.PRESSURE_INHG: SensorEntityDescription( key="PRESSURE_INHG", device_class=SensorDeviceClass.PRESSURE, - native_unit_of_measurement=PRESSURE_INHG, + native_unit_of_measurement=UnitOfPressure.INHG, state_class=SensorStateClass.MEASUREMENT, ), EcoWittSensorTypes.PERCENTAGE: SensorEntityDescription( diff --git a/homeassistant/components/ecowitt/translations/sk.json b/homeassistant/components/ecowitt/translations/sk.json new file mode 100644 index 00000000000..cead9f1f2f8 --- /dev/null +++ b/homeassistant/components/ecowitt/translations/sk.json @@ -0,0 +1,12 @@ +{ + "config": { + "create_entry": { + "default": "Na dokon\u010denie nastavenia integr\u00e1cie pou\u017eite aplik\u00e1ciu Ecowitt (na telef\u00f3ne) alebo prejdite do webov\u00e9ho rozhrania Ecowitt v prehliada\u010di na IP adrese stanice. \n\nVyberte si svoju stanicu - > Ponuka Ostatn\u00e9 - > Urob si s\u00e1m servery. Kliknite \u010falej a vyberte \u201ePrisp\u00f4soben\u00e9\u201c \n\n - IP servera: `{server}`\n - Cesta: `{path}`\n - Port: `{port}` \n\nKliknite na 'Ulo\u017ei\u0165'." + }, + "step": { + "user": { + "description": "Naozaj chcete nastavi\u0165 Ecowitt?" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/eddystone_temperature/sensor.py b/homeassistant/components/eddystone_temperature/sensor.py index d0bf4a87cc2..d3f70730a48 100644 --- a/homeassistant/components/eddystone_temperature/sensor.py +++ b/homeassistant/components/eddystone_temperature/sensor.py @@ -22,7 +22,7 @@ from homeassistant.const import ( EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, STATE_UNKNOWN, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import Event, HomeAssistant import homeassistant.helpers.config_validation as cv @@ -101,8 +101,10 @@ def get_from_conf(config: dict[str, str], config_key: str, length: int) -> str | string = config[config_key] if len(string) != length: _LOGGER.error( - "Error in configuration parameter %s: Must be exactly %d " - "bytes. Device will not be added", + ( + "Error in configuration parameter %s: Must be exactly %d " + "bytes. Device will not be added" + ), config_key, length / 2, ) @@ -114,7 +116,7 @@ class EddystoneTemp(SensorEntity): """Representation of a temperature sensor.""" _attr_device_class = SensorDeviceClass.TEMPERATURE - _attr_native_unit_of_measurement = TEMP_CELSIUS + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS _attr_should_poll = False def __init__(self, name: str, namespace: str, instance: str) -> None: diff --git a/homeassistant/components/edl21/sensor.py b/homeassistant/components/edl21/sensor.py index c598827d244..2fc023ab033 100644 --- a/homeassistant/components/edl21/sensor.py +++ b/homeassistant/components/edl21/sensor.py @@ -18,12 +18,11 @@ from homeassistant.components.sensor import ( from homeassistant.const import ( CONF_NAME, DEGREE, - ELECTRIC_CURRENT_AMPERE, - ELECTRIC_POTENTIAL_VOLT, - ENERGY_KILO_WATT_HOUR, - ENERGY_WATT_HOUR, - FREQUENCY_HERTZ, - POWER_WATT, + UnitOfElectricCurrent, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfFrequency, + UnitOfPower, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import config_validation as cv, entity_registry as er @@ -255,13 +254,13 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SENSORS = {desc.key: desc for desc in SENSOR_TYPES} SENSOR_UNIT_MAPPING = { - "Wh": ENERGY_WATT_HOUR, - "kWh": ENERGY_KILO_WATT_HOUR, - "W": POWER_WATT, - "A": ELECTRIC_CURRENT_AMPERE, - "V": ELECTRIC_POTENTIAL_VOLT, + "Wh": UnitOfEnergy.WATT_HOUR, + "kWh": UnitOfEnergy.KILO_WATT_HOUR, + "W": UnitOfPower.WATT, + "A": UnitOfElectricCurrent.AMPERE, + "V": UnitOfElectricPotential.VOLT, "°": DEGREE, - "Hz": FREQUENCY_HERTZ, + "Hz": UnitOfFrequency.HERTZ, } diff --git a/homeassistant/components/efergy/sensor.py b/homeassistant/components/efergy/sensor.py index abe34a21bcc..0fb58319b48 100644 --- a/homeassistant/components/efergy/sensor.py +++ b/homeassistant/components/efergy/sensor.py @@ -14,7 +14,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ENERGY_KILO_WATT_HOUR, POWER_WATT +from homeassistant.const import UnitOfEnergy, UnitOfPower from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_platform from homeassistant.helpers.typing import StateType @@ -27,14 +27,14 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( key="instant_readings", name="Power Usage", device_class=SensorDeviceClass.POWER, - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="energy_day", name="Daily Consumption", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, state_class=SensorStateClass.TOTAL_INCREASING, entity_registry_enabled_default=False, ), @@ -42,7 +42,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( key="energy_week", name="Weekly Consumption", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, state_class=SensorStateClass.TOTAL_INCREASING, entity_registry_enabled_default=False, ), @@ -50,14 +50,14 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( key="energy_month", name="Monthly Consumption", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, state_class=SensorStateClass.TOTAL_INCREASING, ), SensorEntityDescription( key="energy_year", name="Yearly Consumption", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, state_class=SensorStateClass.TOTAL_INCREASING, entity_registry_enabled_default=False, ), @@ -97,7 +97,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( key=CONF_CURRENT_VALUES, name="Power Usage", device_class=SensorDeviceClass.POWER, - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, state_class=SensorStateClass.MEASUREMENT, ), ) diff --git a/homeassistant/components/efergy/translations/ko.json b/homeassistant/components/efergy/translations/ko.json new file mode 100644 index 00000000000..71eaa40ac0f --- /dev/null +++ b/homeassistant/components/efergy/translations/ko.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "api_key": "API \ud0a4" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/egardia/alarm_control_panel.py b/homeassistant/components/egardia/alarm_control_panel.py index de179c248bb..60b80fffd23 100644 --- a/homeassistant/components/egardia/alarm_control_panel.py +++ b/homeassistant/components/egardia/alarm_control_panel.py @@ -145,8 +145,7 @@ class EgardiaAlarm(alarm.AlarmControlPanelEntity): self._egardiasystem.alarm_arm_home() except requests.exceptions.RequestException as err: _LOGGER.error( - "Egardia device exception occurred when " - "sending arm home command: %s", + "Egardia device exception occurred when sending arm home command: %s", err, ) @@ -156,7 +155,6 @@ class EgardiaAlarm(alarm.AlarmControlPanelEntity): self._egardiasystem.alarm_arm_away() except requests.exceptions.RequestException as err: _LOGGER.error( - "Egardia device exception occurred when " - "sending arm away command: %s", + "Egardia device exception occurred when sending arm away command: %s", err, ) diff --git a/homeassistant/components/eight_sleep/translations/ko.json b/homeassistant/components/eight_sleep/translations/ko.json new file mode 100644 index 00000000000..0dcc103fe2b --- /dev/null +++ b/homeassistant/components/eight_sleep/translations/ko.json @@ -0,0 +1,15 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/eight_sleep/translations/sk.json b/homeassistant/components/eight_sleep/translations/sk.json index ef0fd942acb..594768c79c7 100644 --- a/homeassistant/components/eight_sleep/translations/sk.json +++ b/homeassistant/components/eight_sleep/translations/sk.json @@ -1,7 +1,11 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "cannot_connect": "Nemo\u017eno sa pripoji\u0165 k Eight Sleep cloudu: {error}" + }, + "error": { + "cannot_connect": "Ned\u00e1 sa pripoji\u0165 k cloudu Eight Sleep: {error}" }, "step": { "user": { diff --git a/homeassistant/components/elgato/translations/pt.json b/homeassistant/components/elgato/translations/pt.json index 0bf8113ccaa..3b30b7e49cc 100644 --- a/homeassistant/components/elgato/translations/pt.json +++ b/homeassistant/components/elgato/translations/pt.json @@ -2,10 +2,10 @@ "config": { "abort": { "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "user": { diff --git a/homeassistant/components/elgato/translations/sk.json b/homeassistant/components/elgato/translations/sk.json index d44c8833bc3..e0f4b43813f 100644 --- a/homeassistant/components/elgato/translations/sk.json +++ b/homeassistant/components/elgato/translations/sk.json @@ -13,7 +13,12 @@ "data": { "host": "Hostite\u013e", "port": "Port" - } + }, + "description": "Nastavte si Elgato Light na integr\u00e1ciu s Home Assistant." + }, + "zeroconf_confirm": { + "description": "Chcete prida\u0165 Elgato Light so s\u00e9riov\u00fdm \u010d\u00edslom `{serial_number}` do Home Assistant?", + "title": "Objaven\u00e9 zariadenie Elgato Light" } } } diff --git a/homeassistant/components/eliqonline/sensor.py b/homeassistant/components/eliqonline/sensor.py index 9b81ebad78a..bea60b94a1c 100644 --- a/homeassistant/components/eliqonline/sensor.py +++ b/homeassistant/components/eliqonline/sensor.py @@ -10,10 +10,11 @@ import voluptuous as vol from homeassistant.components.sensor import ( PLATFORM_SCHEMA, + SensorDeviceClass, SensorEntity, SensorStateClass, ) -from homeassistant.const import CONF_ACCESS_TOKEN, CONF_NAME, POWER_WATT +from homeassistant.const import CONF_ACCESS_TOKEN, CONF_NAME, UnitOfPower from homeassistant.core import HomeAssistant from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv @@ -26,12 +27,8 @@ CONF_CHANNEL_ID = "channel_id" DEFAULT_NAME = "ELIQ Online" -ICON = "mdi:gauge" - SCAN_INTERVAL = timedelta(seconds=60) -UNIT_OF_MEASUREMENT = POWER_WATT - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( { vol.Required(CONF_ACCESS_TOKEN): cv.string, @@ -68,41 +65,22 @@ async def async_setup_platform( class EliqSensor(SensorEntity): """Implementation of an ELIQ Online sensor.""" + _attr_device_class = SensorDeviceClass.POWER + _attr_native_unit_of_measurement = UnitOfPower.WATT _attr_state_class = SensorStateClass.MEASUREMENT def __init__(self, api, channel_id, name): """Initialize the sensor.""" - self._name = name - self._state = None + self._attr_name = name self._api = api self._channel_id = channel_id - @property - def name(self): - """Return the name of the sensor.""" - return self._name - - @property - def icon(self): - """Return icon.""" - return ICON - - @property - def native_unit_of_measurement(self): - """Return the unit of measurement of this entity, if any.""" - return UNIT_OF_MEASUREMENT - - @property - def native_value(self): - """Return the state of the device.""" - return self._state - async def async_update(self) -> None: """Get the latest data.""" try: response = await self._api.get_data_now(channelid=self._channel_id) - self._state = int(response["power"]) - _LOGGER.debug("Updated power from server %d W", self._state) + self._attr_native_value = int(response["power"]) + _LOGGER.debug("Updated power from server %d W", self.native_value) except KeyError: _LOGGER.warning("Invalid response from ELIQ Online API") except (OSError, asyncio.TimeoutError) as error: diff --git a/homeassistant/components/elkm1/__init__.py b/homeassistant/components/elkm1/__init__.py index 7b14c7e85ed..3047cf32479 100644 --- a/homeassistant/components/elkm1/__init__.py +++ b/homeassistant/components/elkm1/__init__.py @@ -25,9 +25,8 @@ from homeassistant.const import ( CONF_TEMPERATURE_UNIT, CONF_USERNAME, CONF_ZONE, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, Platform, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant, ServiceCall, callback from homeassistant.exceptions import ConfigEntryNotReady, HomeAssistantError @@ -298,7 +297,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: raise ConfigEntryNotReady(f"Timed out connecting to {conf[CONF_HOST]}") from exc elk_temp_unit = elk.panel.temperature_units - temperature_unit = TEMP_CELSIUS if elk_temp_unit == "C" else TEMP_FAHRENHEIT + if elk_temp_unit == "C": + temperature_unit = UnitOfTemperature.CELSIUS + else: + temperature_unit = UnitOfTemperature.FAHRENHEIT config["temperature_unit"] = temperature_unit hass.data[DOMAIN][entry.entry_id] = { "elk": elk, diff --git a/homeassistant/components/elkm1/manifest.json b/homeassistant/components/elkm1/manifest.json index 7043fdfcb9d..32f40596122 100644 --- a/homeassistant/components/elkm1/manifest.json +++ b/homeassistant/components/elkm1/manifest.json @@ -2,7 +2,7 @@ "domain": "elkm1", "name": "Elk-M1 Control", "documentation": "https://www.home-assistant.io/integrations/elkm1", - "requirements": ["elkm1-lib==2.0.2"], + "requirements": ["elkm1-lib==2.2.1"], "dhcp": [{ "registered_devices": true }, { "macaddress": "00409D*" }], "codeowners": ["@gwww", "@bdraco"], "dependencies": ["network"], diff --git a/homeassistant/components/elkm1/sensor.py b/homeassistant/components/elkm1/sensor.py index 1d84af259ee..aac9f630f9e 100644 --- a/homeassistant/components/elkm1/sensor.py +++ b/homeassistant/components/elkm1/sensor.py @@ -16,7 +16,7 @@ import voluptuous as vol from homeassistant.components.sensor import SensorEntity from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ELECTRIC_POTENTIAL_VOLT +from homeassistant.const import UnitOfElectricPotential from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_platform @@ -278,7 +278,7 @@ class ElkZone(ElkSensor): if self._element.definition == ZoneType.TEMPERATURE: return self._temperature_unit if self._element.definition == ZoneType.ANALOG_ZONE: - return ELECTRIC_POTENTIAL_VOLT + return UnitOfElectricPotential.VOLT return None def _element_changed(self, _: Element, changeset: Any) -> None: diff --git a/homeassistant/components/elkm1/translations/ko.json b/homeassistant/components/elkm1/translations/ko.json index 5b16671c05b..4439e1e8202 100644 --- a/homeassistant/components/elkm1/translations/ko.json +++ b/homeassistant/components/elkm1/translations/ko.json @@ -2,7 +2,11 @@ "config": { "abort": { "address_already_configured": "\uc774 \uc8fc\uc18c\ub85c ElkM1\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", - "already_configured": "\uc774 \uc811\ub450\uc0ac\ub85c ElkM1\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + "already_configured": "\uc774 \uc811\ub450\uc0ac\ub85c ElkM1\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" }, "error": { "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", @@ -18,6 +22,12 @@ "description": "\uc790\ub3d9\uac80\uc0c9\ub41c \uc2dc\uc2a4\ud15c\uc5d0 \uc5f0\uacb0: {mac_address} ({host})", "title": "Elk-M1 \uc81c\uc5b4\uc5d0 \uc5f0\uacb0\ud558\uae30" }, + "manual_connection": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + } + }, "user": { "description": "\uc8fc\uc18c \ubb38\uc790\uc5f4\uc740 '\ubcf4\uc548' \ubc0f '\ube44\ubcf4\uc548'\uc5d0 \ub300\ud574 'address[:port]' \ud615\uc2dd\uc774\uc5b4\uc57c \ud569\ub2c8\ub2e4. \uc608: '192.168.1.1'. \ud3ec\ud2b8\ub294 \uc120\ud0dd \uc0ac\ud56d\uc774\uba70 \uae30\ubcf8\uac12\uc740 '\ube44\ubcf4\uc548' \uc758 \uacbd\uc6b0 2101 \uc774\uace0 '\ubcf4\uc548' \uc758 \uacbd\uc6b0 2601 \uc785\ub2c8\ub2e4. \uc2dc\ub9ac\uc5bc \ud504\ub85c\ud1a0\ucf5c\uc758 \uacbd\uc6b0 \uc8fc\uc18c\ub294 'tty[:baud]' \ud615\uc2dd\uc774\uc5b4\uc57c \ud569\ub2c8\ub2e4. \uc608: '/dev/ttyS1'. \uc804\uc1a1 \uc18d\ub3c4\ub294 \uc120\ud0dd \uc0ac\ud56d\uc774\uba70 \uae30\ubcf8\uac12\uc740 115200 \uc785\ub2c8\ub2e4.", "title": "Elk-M1 \uc81c\uc5b4\uc5d0 \uc5f0\uacb0\ud558\uae30" diff --git a/homeassistant/components/elkm1/translations/pt.json b/homeassistant/components/elkm1/translations/pt.json index 08fe97d2354..76ee7386362 100644 --- a/homeassistant/components/elkm1/translations/pt.json +++ b/homeassistant/components/elkm1/translations/pt.json @@ -1,7 +1,7 @@ { "config": { "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, diff --git a/homeassistant/components/elkm1/translations/sk.json b/homeassistant/components/elkm1/translations/sk.json index 3ec7110ece0..0ece3fd0388 100644 --- a/homeassistant/components/elkm1/translations/sk.json +++ b/homeassistant/components/elkm1/translations/sk.json @@ -1,6 +1,8 @@ { "config": { "abort": { + "address_already_configured": "ElkM1 s touto adresou je u\u017e nastaven\u00fd", + "already_configured": "ElkM1 s t\u00fdmto prefixom je u\u017e nastaven\u00fd", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie", @@ -32,12 +34,14 @@ "temperature_unit": "Jednotka teploty pou\u017e\u00edva ElkM1.", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" }, + "description": "Re\u0165azec adresy mus\u00ed by\u0165 v tvare 'adresa[:port]' pre 'zabezpe\u010den\u00e9' a 'nezabezpe\u010den\u00e9'. Pr\u00edklad: '192.168.1.1'. Port je volite\u013en\u00fd a \u0161tandardne je nastaven\u00fd na 2101 pre \u201enezabezpe\u010den\u00fd\u201c a 2601 pre \u201ezabezpe\u010den\u00fd\u201c. Pre s\u00e9riov\u00fd protokol mus\u00ed by\u0165 adresa v tvare 'tty[:baud]'. Pr\u00edklad: '/dev/ttyS1'. Prenosov\u00e1 r\u00fdchlos\u0165 je volite\u013en\u00e1 a predvolen\u00e1 je 115200.", "title": "Pripojte k Elk-M1 Control" }, "user": { "data": { "device": "Zariadenie" }, + "description": "Vyberte objaven\u00fd syst\u00e9m alebo 'Manu\u00e1lne zadanie', ak neboli objaven\u00e9 \u017eiadne zariadenia.", "title": "Pripojte k Elk-M1 Control" } } diff --git a/homeassistant/components/elmax/alarm_control_panel.py b/homeassistant/components/elmax/alarm_control_panel.py new file mode 100644 index 00000000000..33b8749cb48 --- /dev/null +++ b/homeassistant/components/elmax/alarm_control_panel.py @@ -0,0 +1,108 @@ +"""Elmax sensor platform.""" +from __future__ import annotations + +from elmax_api.model.alarm_status import AlarmArmStatus, AlarmStatus +from elmax_api.model.command import AreaCommand +from elmax_api.model.panel import PanelStatus + +from homeassistant.components.alarm_control_panel import ( + AlarmControlPanelEntity, + AlarmControlPanelEntityFeature, + CodeFormat, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import STATE_ALARM_ARMED_AWAY, STATE_ALARM_DISARMED +from homeassistant.core import HomeAssistant, callback +from homeassistant.exceptions import InvalidStateError +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from . import ElmaxCoordinator +from .common import ElmaxEntity +from .const import DOMAIN + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up the Elmax area platform.""" + coordinator: ElmaxCoordinator = hass.data[DOMAIN][config_entry.entry_id] + known_devices = set() + + def _discover_new_devices(): + panel_status: PanelStatus = coordinator.data + # In case the panel is offline, its status will be None. In that case, simply do nothing + if panel_status is None: + return + + # Otherwise, add all the entities we found + entities = [ + ElmaxArea( + panel=coordinator.panel_entry, + elmax_device=area, + panel_version=panel_status.release, + coordinator=coordinator, + ) + for area in panel_status.areas + if area.endpoint_id not in known_devices + ] + + if entities: + async_add_entities(entities) + known_devices.update([e.unique_id for e in entities]) + + # Register a listener for the discovery of new devices + config_entry.async_on_unload(coordinator.async_add_listener(_discover_new_devices)) + + # Immediately run a discovery, so we don't need to wait for the next update + _discover_new_devices() + + +class ElmaxArea(ElmaxEntity, AlarmControlPanelEntity): + """Elmax Area entity implementation.""" + + _attr_code_format = CodeFormat.NUMBER + _attr_code_arm_required = False + _attr_has_entity_name = True + _attr_supported_features = AlarmControlPanelEntityFeature.ARM_AWAY + + async def async_alarm_arm_away(self, code: str | None = None) -> None: + """Send arm away command.""" + if self._attr_state == AlarmStatus.NOT_ARMED_NOT_ARMABLE: + raise InvalidStateError( + f"Cannot arm {self.name}: please check for open windows/doors first" + ) + + await self.coordinator.http_client.execute_command( + endpoint_id=self._device.endpoint_id, + command=AreaCommand.ARM_TOTALLY, + extra_payload={"code": code}, + ) + await self.coordinator.async_refresh() + + async def async_alarm_disarm(self, code: str | None = None) -> None: + """Send disarm command.""" + await self.coordinator.http_client.execute_command( + endpoint_id=self._device.endpoint_id, + command=AreaCommand.DISARM, + extra_payload={"code": code}, + ) + await self.coordinator.async_refresh() + + @callback + def _handle_coordinator_update(self) -> None: + """Handle updated data from the coordinator.""" + self._attr_state = ALARM_STATE_TO_HA.get( + self.coordinator.get_area_state(self._device.endpoint_id).armed_status + ) + super()._handle_coordinator_update() + + +ALARM_STATE_TO_HA = { + AlarmArmStatus.ARMED_TOTALLY: STATE_ALARM_ARMED_AWAY, + AlarmArmStatus.ARMED_P1_P2: STATE_ALARM_ARMED_AWAY, + AlarmArmStatus.ARMED_P2: STATE_ALARM_ARMED_AWAY, + AlarmArmStatus.ARMED_P1: STATE_ALARM_ARMED_AWAY, + AlarmArmStatus.NOT_ARMED: STATE_ALARM_DISARMED, +} diff --git a/homeassistant/components/elmax/common.py b/homeassistant/components/elmax/common.py index 4116ff05f44..f1ffe87fde9 100644 --- a/homeassistant/components/elmax/common.py +++ b/homeassistant/components/elmax/common.py @@ -14,6 +14,7 @@ from elmax_api.exceptions import ( ) from elmax_api.http import Elmax from elmax_api.model.actuator import Actuator +from elmax_api.model.area import Area from elmax_api.model.endpoint import DeviceEndpoint from elmax_api.model.panel import PanelEntry, PanelStatus @@ -62,15 +63,21 @@ class ElmaxCoordinator(DataUpdateCoordinator[PanelStatus]): def get_actuator_state(self, actuator_id: str) -> Actuator: """Return state of a specific actuator.""" if self._state_by_endpoint is not None: - return self._state_by_endpoint.get(actuator_id) + return self._state_by_endpoint[actuator_id] raise HomeAssistantError("Unknown actuator") def get_zone_state(self, zone_id: str) -> Actuator: """Return state of a specific zone.""" if self._state_by_endpoint is not None: - return self._state_by_endpoint.get(zone_id) + return self._state_by_endpoint[zone_id] raise HomeAssistantError("Unknown zone") + def get_area_state(self, area_id: str) -> Area: + """Return state of a specific area.""" + if self._state_by_endpoint is not None and area_id: + return self._state_by_endpoint[area_id] + raise HomeAssistantError("Unknown area") + @property def http_client(self): """Return the current http client being used by this instance.""" @@ -89,7 +96,8 @@ class ElmaxCoordinator(DataUpdateCoordinator[PanelStatus]): # reconfigure it in order to make it work again if not panel: raise ConfigEntryAuthFailed( - f"Panel ID {self._panel_id} is no more linked to this user account" + f"Panel ID {self._panel_id} is no more linked to this user" + " account" ) self._panel_entry = panel diff --git a/homeassistant/components/elmax/const.py b/homeassistant/components/elmax/const.py index 514412d6897..cd35211e592 100644 --- a/homeassistant/components/elmax/const.py +++ b/homeassistant/components/elmax/const.py @@ -11,7 +11,11 @@ CONF_ELMAX_PANEL_NAME = "panel_name" CONF_CONFIG_ENTRY_ID = "config_entry_id" CONF_ENDPOINT_ID = "endpoint_id" -ELMAX_PLATFORMS = [Platform.SWITCH, Platform.BINARY_SENSOR] +ELMAX_PLATFORMS = [ + Platform.SWITCH, + Platform.BINARY_SENSOR, + Platform.ALARM_CONTROL_PANEL, +] POLLING_SECONDS = 30 DEFAULT_TIMEOUT = 10.0 diff --git a/homeassistant/components/elmax/translations/de.json b/homeassistant/components/elmax/translations/de.json index 55e947d5bd0..d7ec72ed5d7 100644 --- a/homeassistant/components/elmax/translations/de.json +++ b/homeassistant/components/elmax/translations/de.json @@ -24,7 +24,7 @@ "password": "Passwort", "username": "Benutzername" }, - "description": "Bitte melde dich mit deinen Zugangsdaten bei der Elmax-Cloud an" + "description": "Bitte melde dich mit deinen Zugangsdaten bei der Elmax Cloud an" } } } diff --git a/homeassistant/components/elmax/translations/ko.json b/homeassistant/components/elmax/translations/ko.json index b9f74068f56..86c70a7e758 100644 --- a/homeassistant/components/elmax/translations/ko.json +++ b/homeassistant/components/elmax/translations/ko.json @@ -1,10 +1,23 @@ { "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, "step": { "panels": { "data": { "panel_pin": "PIN \ucf54\ub4dc" } + }, + "user": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + } } } } diff --git a/homeassistant/components/elmax/translations/lb.json b/homeassistant/components/elmax/translations/lb.json new file mode 100644 index 00000000000..ac0135ccbcc --- /dev/null +++ b/homeassistant/components/elmax/translations/lb.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "panels": { + "data": { + "panel_pin": "PIN-Code" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/elmax/translations/sk.json b/homeassistant/components/elmax/translations/sk.json index 43c7507b1b9..8331bda2512 100644 --- a/homeassistant/components/elmax/translations/sk.json +++ b/homeassistant/components/elmax/translations/sk.json @@ -7,20 +7,24 @@ "invalid_auth": "Neplatn\u00e9 overenie", "invalid_pin": "Poskytnut\u00fd k\u00f3d PIN je neplatn\u00fd", "network_error": "Vyskytla sa chyba siete", + "no_panel_online": "Nebol n\u00e1jden\u00fd \u017eiadny online ovl\u00e1dac\u00ed panel Elmax.", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "panels": { "data": { "panel_id": "ID panela", + "panel_name": "N\u00e1zov panela", "panel_pin": "PIN k\u00f3d" - } + }, + "description": "Vyberte panel, ktor\u00fd chcete pomocou tejto integr\u00e1cie ovl\u00e1da\u0165. Upozor\u0148ujeme, \u017ee panel mus\u00ed by\u0165 zapnut\u00fd, aby bolo mo\u017en\u00e9 ho nakonfigurova\u0165." }, "user": { "data": { "password": "Heslo", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" - } + }, + "description": "Prihl\u00e1ste sa do cloudu Elmax pomocou svojich prihlasovac\u00edch \u00fadajov" } } } diff --git a/homeassistant/components/emoncms/sensor.py b/homeassistant/components/emoncms/sensor.py index e4148d1dea5..f9382d1060b 100644 --- a/homeassistant/components/emoncms/sensor.py +++ b/homeassistant/components/emoncms/sensor.py @@ -21,8 +21,8 @@ from homeassistant.const import ( CONF_UNIT_OF_MEASUREMENT, CONF_URL, CONF_VALUE_TEMPLATE, - POWER_WATT, STATE_UNKNOWN, + UnitOfPower, ) from homeassistant.core import HomeAssistant from homeassistant.helpers import template @@ -46,7 +46,7 @@ CONF_ONLY_INCLUDE_FEEDID = "include_only_feed_id" CONF_SENSOR_NAMES = "sensor_names" DECIMALS = 2 -DEFAULT_UNIT = POWER_WATT +DEFAULT_UNIT = UnitOfPower.WATT MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=5) ONLY_INCL_EXCL_NONE = "only_include_exclude_or_none" @@ -290,8 +290,10 @@ class EmonCmsData: self.data = req.json() else: _LOGGER.error( - "Please verify if the specified configuration value " - "'%s' is correct! (HTTP Status_code = %d)", + ( + "Please verify if the specified configuration value " + "'%s' is correct! (HTTP Status_code = %d)" + ), CONF_URL, req.status_code, ) diff --git a/homeassistant/components/emonitor/sensor.py b/homeassistant/components/emonitor/sensor.py index 27117d88fe4..d5e677abcc9 100644 --- a/homeassistant/components/emonitor/sensor.py +++ b/homeassistant/components/emonitor/sensor.py @@ -10,7 +10,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import POWER_WATT +from homeassistant.const import UnitOfPower from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry as dr from homeassistant.helpers.entity import DeviceInfo @@ -64,7 +64,7 @@ class EmonitorPowerSensor(CoordinatorEntity, SensorEntity): """Representation of an Emonitor power sensor entity.""" _attr_device_class = SensorDeviceClass.POWER - _attr_native_unit_of_measurement = POWER_WATT + _attr_native_unit_of_measurement = UnitOfPower.WATT _attr_state_class = SensorStateClass.MEASUREMENT def __init__( diff --git a/homeassistant/components/emonitor/strings.json b/homeassistant/components/emonitor/strings.json index 9f62efc4d8c..675db107935 100644 --- a/homeassistant/components/emonitor/strings.json +++ b/homeassistant/components/emonitor/strings.json @@ -8,8 +8,8 @@ } }, "confirm": { - "title": "Setup SiteSage Emonitor", - "description": "Do you want to setup {name} ({host})?" + "title": "Set up SiteSage Emonitor", + "description": "Do you want to set up {name} ({host})?" } }, "error": { diff --git a/homeassistant/components/emonitor/translations/ca.json b/homeassistant/components/emonitor/translations/ca.json index 4ab24587c15..edc54123143 100644 --- a/homeassistant/components/emonitor/translations/ca.json +++ b/homeassistant/components/emonitor/translations/ca.json @@ -11,7 +11,7 @@ "step": { "confirm": { "description": "Vols configurar {name} ({host})?", - "title": "Configura SiteSage Emonitor" + "title": "Configuraci\u00f3 de SiteSage Emonitor" }, "user": { "data": { diff --git a/homeassistant/components/emonitor/translations/en.json b/homeassistant/components/emonitor/translations/en.json index de153509dde..60504f17c27 100644 --- a/homeassistant/components/emonitor/translations/en.json +++ b/homeassistant/components/emonitor/translations/en.json @@ -10,8 +10,8 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Do you want to setup {name} ({host})?", - "title": "Setup SiteSage Emonitor" + "description": "Do you want to set up {name} ({host})?", + "title": "Set up SiteSage Emonitor" }, "user": { "data": { diff --git a/homeassistant/components/emonitor/translations/it.json b/homeassistant/components/emonitor/translations/it.json index 36115111583..06fe8ef3a85 100644 --- a/homeassistant/components/emonitor/translations/it.json +++ b/homeassistant/components/emonitor/translations/it.json @@ -10,8 +10,8 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Vuoi impostare {name} ({host})?", - "title": "Imposta SiteSage Emonitor" + "description": "Vuoi configurare {name} ({host})?", + "title": "Configura SiteSage Emonitor" }, "user": { "data": { diff --git a/homeassistant/components/emonitor/translations/no.json b/homeassistant/components/emonitor/translations/no.json index 5b559af8f17..fd75389eb63 100644 --- a/homeassistant/components/emonitor/translations/no.json +++ b/homeassistant/components/emonitor/translations/no.json @@ -10,8 +10,8 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Vil du konfigurere {name} ({host})?", - "title": "Konfigurer SiteSage Emonitor" + "description": "Vil du sette opp {name} ( {host} )?", + "title": "Sett opp SiteSage Emonitor" }, "user": { "data": { diff --git a/homeassistant/components/emonitor/translations/pt.json b/homeassistant/components/emonitor/translations/pt.json index 04374af8e82..476b143ab02 100644 --- a/homeassistant/components/emonitor/translations/pt.json +++ b/homeassistant/components/emonitor/translations/pt.json @@ -10,7 +10,7 @@ "step": { "user": { "data": { - "host": "Servidor" + "host": "Endere\u00e7o" } } } diff --git a/homeassistant/components/emonitor/translations/sk.json b/homeassistant/components/emonitor/translations/sk.json index 323aa28cafb..ccad6f16af1 100644 --- a/homeassistant/components/emonitor/translations/sk.json +++ b/homeassistant/components/emonitor/translations/sk.json @@ -10,7 +10,8 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Chcete nastavi\u0165 {name} ({host})?" + "description": "Chcete nastavi\u0165 {name} ({host})?", + "title": "Nastavi\u0165 SiteSage Emonitor" }, "user": { "data": { diff --git a/homeassistant/components/emulated_roku/translations/de.json b/homeassistant/components/emulated_roku/translations/de.json index 39c8da5197f..f2458823748 100644 --- a/homeassistant/components/emulated_roku/translations/de.json +++ b/homeassistant/components/emulated_roku/translations/de.json @@ -6,7 +6,7 @@ "step": { "user": { "data": { - "advertise_ip": "IP Adresse annoncieren", + "advertise_ip": "IP-Adresse ank\u00fcndigen", "advertise_port": "Port annoncieren", "host_ip": "Host-IP-Adresse", "listen_port": "Listen-Port", diff --git a/homeassistant/components/emulated_roku/translations/sk.json b/homeassistant/components/emulated_roku/translations/sk.json index 96ca4e8f40a..dbbfa6ba88b 100644 --- a/homeassistant/components/emulated_roku/translations/sk.json +++ b/homeassistant/components/emulated_roku/translations/sk.json @@ -6,11 +6,16 @@ "step": { "user": { "data": { + "advertise_ip": "Ozn\u00e1menie IP adresy", + "advertise_port": "Port odosielania", "host_ip": "IP adresa hostite\u013ea", - "name": "N\u00e1zov" + "listen_port": "Port prij\u00edmania", + "name": "N\u00e1zov", + "upnp_bind_multicast": "Viazanie multicast (True/False)" }, "title": "Definujte konfigur\u00e1ciu servera" } } - } + }, + "title": "Emulovan\u00fd Roku" } \ No newline at end of file diff --git a/homeassistant/components/energy/sensor.py b/homeassistant/components/energy/sensor.py index 71e385f2fec..1509eb10afe 100644 --- a/homeassistant/components/energy/sensor.py +++ b/homeassistant/components/energy/sensor.py @@ -16,15 +16,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.components.sensor.recorder import reset_detected -from homeassistant.const import ( - ATTR_UNIT_OF_MEASUREMENT, - VOLUME_CUBIC_FEET, - VOLUME_CUBIC_METERS, - VOLUME_GALLONS, - VOLUME_LITERS, - UnitOfEnergy, - UnitOfVolume, -) +from homeassistant.const import ATTR_UNIT_OF_MEASUREMENT, UnitOfEnergy, UnitOfVolume from homeassistant.core import ( HomeAssistant, State, @@ -55,15 +47,17 @@ VALID_ENERGY_UNITS: set[str] = { UnitOfEnergy.GIGA_JOULE, } VALID_ENERGY_UNITS_GAS = { - VOLUME_CUBIC_FEET, - VOLUME_CUBIC_METERS, + UnitOfVolume.CUBIC_FEET, + UnitOfVolume.CENTUM_CUBIC_FEET, + UnitOfVolume.CUBIC_METERS, *VALID_ENERGY_UNITS, } -VALID_VOLUME_UNITS_WATER = { - VOLUME_CUBIC_FEET, - VOLUME_CUBIC_METERS, - VOLUME_GALLONS, - VOLUME_LITERS, +VALID_VOLUME_UNITS_WATER: set[str] = { + UnitOfVolume.CUBIC_FEET, + UnitOfVolume.CENTUM_CUBIC_FEET, + UnitOfVolume.CUBIC_METERS, + UnitOfVolume.GALLONS, + UnitOfVolume.LITERS, } _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/energy/validate.py b/homeassistant/components/energy/validate.py index cf4ff3ef63e..ea799fcdf06 100644 --- a/homeassistant/components/energy/validate.py +++ b/homeassistant/components/energy/validate.py @@ -11,11 +11,8 @@ from homeassistant.const import ( ATTR_DEVICE_CLASS, STATE_UNAVAILABLE, STATE_UNKNOWN, - VOLUME_CUBIC_FEET, - VOLUME_CUBIC_METERS, - VOLUME_GALLONS, - VOLUME_LITERS, UnitOfEnergy, + UnitOfVolume, ) from homeassistant.core import HomeAssistant, callback, valid_entity_id @@ -47,7 +44,7 @@ GAS_USAGE_UNITS = { UnitOfEnergy.MEGA_WATT_HOUR, UnitOfEnergy.GIGA_JOULE, ), - sensor.SensorDeviceClass.GAS: (VOLUME_CUBIC_METERS, VOLUME_CUBIC_FEET), + sensor.SensorDeviceClass.GAS: (UnitOfVolume.CUBIC_METERS, UnitOfVolume.CUBIC_FEET), } GAS_PRICE_UNITS = tuple( f"/{unit}" for units in GAS_USAGE_UNITS.values() for unit in units @@ -57,10 +54,10 @@ GAS_PRICE_UNIT_ERROR = "entity_unexpected_unit_gas_price" WATER_USAGE_DEVICE_CLASSES = (sensor.SensorDeviceClass.WATER,) WATER_USAGE_UNITS = { sensor.SensorDeviceClass.WATER: ( - VOLUME_CUBIC_METERS, - VOLUME_CUBIC_FEET, - VOLUME_GALLONS, - VOLUME_LITERS, + UnitOfVolume.CUBIC_METERS, + UnitOfVolume.CUBIC_FEET, + UnitOfVolume.GALLONS, + UnitOfVolume.LITERS, ), } WATER_PRICE_UNITS = tuple( diff --git a/homeassistant/components/enocean/sensor.py b/homeassistant/components/enocean/sensor.py index ae75dfe25c1..702b721ab09 100644 --- a/homeassistant/components/enocean/sensor.py +++ b/homeassistant/components/enocean/sensor.py @@ -19,10 +19,10 @@ from homeassistant.const import ( CONF_ID, CONF_NAME, PERCENTAGE, - POWER_WATT, STATE_CLOSED, STATE_OPEN, - TEMP_CELSIUS, + UnitOfPower, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv @@ -62,7 +62,7 @@ class EnOceanSensorEntityDescription( SENSOR_DESC_TEMPERATURE = EnOceanSensorEntityDescription( key=SENSOR_TYPE_TEMPERATURE, name="Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, icon="mdi:thermometer", device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, @@ -82,7 +82,7 @@ SENSOR_DESC_HUMIDITY = EnOceanSensorEntityDescription( SENSOR_DESC_POWER = EnOceanSensorEntityDescription( key=SENSOR_TYPE_POWER, name="Power", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, icon="mdi:power-plug", device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, diff --git a/homeassistant/components/enocean/translations/sk.json b/homeassistant/components/enocean/translations/sk.json index a550ef6b29a..5aabc0ee3f3 100644 --- a/homeassistant/components/enocean/translations/sk.json +++ b/homeassistant/components/enocean/translations/sk.json @@ -6,6 +6,20 @@ }, "error": { "invalid_dongle_path": "Pre t\u00fato cestu nebol n\u00e1jden\u00fd \u017eiadny platn\u00fd k\u013e\u00fa\u010d" + }, + "step": { + "detect": { + "data": { + "path": "Cesta k hardwarov\u00e9mu USB k\u013e\u00fa\u010du" + }, + "title": "Vyberte cestu k v\u00e1\u0161mu ENOcean dongle" + }, + "manual": { + "data": { + "path": "Cesta k hardwarov\u00e9mu USB k\u013e\u00fa\u010du" + }, + "title": "Zadajte cestu k v\u00e1\u0161mu hardwarov\u00e9mu k\u013e\u00fa\u010du ENOcean" + } } } } \ No newline at end of file diff --git a/homeassistant/components/enphase_envoy/const.py b/homeassistant/components/enphase_envoy/const.py index 7c493168526..16aae3f9a64 100644 --- a/homeassistant/components/enphase_envoy/const.py +++ b/homeassistant/components/enphase_envoy/const.py @@ -1,12 +1,10 @@ """The enphase_envoy component.""" - - from homeassistant.components.sensor import ( SensorDeviceClass, SensorEntityDescription, SensorStateClass, ) -from homeassistant.const import ENERGY_WATT_HOUR, POWER_WATT, Platform +from homeassistant.const import Platform, UnitOfEnergy, UnitOfPower DOMAIN = "enphase_envoy" @@ -20,54 +18,56 @@ SENSORS = ( SensorEntityDescription( key="production", name="Current Power Production", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, state_class=SensorStateClass.MEASUREMENT, + device_class=SensorDeviceClass.POWER, ), SensorEntityDescription( key="daily_production", name="Today's Energy Production", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, state_class=SensorStateClass.TOTAL_INCREASING, device_class=SensorDeviceClass.ENERGY, ), SensorEntityDescription( key="seven_days_production", name="Last Seven Days Energy Production", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, state_class=SensorStateClass.TOTAL_INCREASING, device_class=SensorDeviceClass.ENERGY, ), SensorEntityDescription( key="lifetime_production", name="Lifetime Energy Production", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, state_class=SensorStateClass.TOTAL_INCREASING, device_class=SensorDeviceClass.ENERGY, ), SensorEntityDescription( key="consumption", name="Current Power Consumption", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, state_class=SensorStateClass.MEASUREMENT, + device_class=SensorDeviceClass.POWER, ), SensorEntityDescription( key="daily_consumption", name="Today's Energy Consumption", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, state_class=SensorStateClass.TOTAL_INCREASING, device_class=SensorDeviceClass.ENERGY, ), SensorEntityDescription( key="seven_days_consumption", name="Last Seven Days Energy Consumption", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, state_class=SensorStateClass.TOTAL_INCREASING, device_class=SensorDeviceClass.ENERGY, ), SensorEntityDescription( key="lifetime_consumption", name="Lifetime Energy Consumption", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, state_class=SensorStateClass.TOTAL_INCREASING, device_class=SensorDeviceClass.ENERGY, ), diff --git a/homeassistant/components/enphase_envoy/sensor.py b/homeassistant/components/enphase_envoy/sensor.py index c8d791907a6..2870a61d9a0 100644 --- a/homeassistant/components/enphase_envoy/sensor.py +++ b/homeassistant/components/enphase_envoy/sensor.py @@ -14,7 +14,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import POWER_WATT +from homeassistant.const import UnitOfPower from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -60,8 +60,9 @@ def _inverter_last_report_time( INVERTER_SENSORS = ( EnvoySensorEntityDescription( key=INVERTERS_KEY, - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, state_class=SensorStateClass.MEASUREMENT, + device_class=SensorDeviceClass.POWER, value_fn=lambda watt_report_time: watt_report_time[0], ), EnvoySensorEntityDescription( diff --git a/homeassistant/components/enphase_envoy/translations/pt.json b/homeassistant/components/enphase_envoy/translations/pt.json index 1aa61659a93..2d70bc6e3ef 100644 --- a/homeassistant/components/enphase_envoy/translations/pt.json +++ b/homeassistant/components/enphase_envoy/translations/pt.json @@ -9,7 +9,7 @@ "step": { "user": { "data": { - "host": "Servidor" + "host": "Endere\u00e7o" } } } diff --git a/homeassistant/components/enphase_envoy/translations/sk.json b/homeassistant/components/enphase_envoy/translations/sk.json index 90056cd5864..a0f360ba85f 100644 --- a/homeassistant/components/enphase_envoy/translations/sk.json +++ b/homeassistant/components/enphase_envoy/translations/sk.json @@ -16,7 +16,8 @@ "host": "Hostite\u013e", "password": "Heslo", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" - } + }, + "description": "V pr\u00edpade nov\u0161\u00edch modelov zadajte pou\u017e\u00edvate\u013esk\u00e9 meno \u201eenvoy\u201c bez hesla. Pri star\u0161\u00edch modeloch zadajte pou\u017e\u00edvate\u013esk\u00e9 meno `installer` bez hesla. Pre v\u0161etky ostatn\u00e9 modely zadajte platn\u00e9 pou\u017e\u00edvate\u013esk\u00e9 meno a heslo." } } } diff --git a/homeassistant/components/entur_public_transport/sensor.py b/homeassistant/components/entur_public_transport/sensor.py index fd470ff7c9f..3e8b7bbe390 100644 --- a/homeassistant/components/entur_public_transport/sensor.py +++ b/homeassistant/components/entur_public_transport/sensor.py @@ -12,7 +12,7 @@ from homeassistant.const import ( CONF_LONGITUDE, CONF_NAME, CONF_SHOW_ON_MAP, - TIME_MINUTES, + UnitOfTime, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.aiohttp_client import async_get_clientsession @@ -190,7 +190,7 @@ class EnturPublicTransportSensor(SensorEntity): @property def native_unit_of_measurement(self) -> str: """Return the unit this state is expressed in.""" - return TIME_MINUTES + return UnitOfTime.MINUTES @property def icon(self) -> str: diff --git a/homeassistant/components/environment_canada/sensor.py b/homeassistant/components/environment_canada/sensor.py index 88ec055ad03..d3848086bf5 100644 --- a/homeassistant/components/environment_canada/sensor.py +++ b/homeassistant/components/environment_canada/sensor.py @@ -15,13 +15,13 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( ATTR_LOCATION, DEGREE, - LENGTH_KILOMETERS, - LENGTH_MILLIMETERS, PERCENTAGE, - PRESSURE_KPA, - SPEED_KILOMETERS_PER_HOUR, - TEMP_CELSIUS, UV_INDEX, + UnitOfLength, + UnitOfPrecipitationDepth, + UnitOfPressure, + UnitOfSpeed, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -59,7 +59,7 @@ SENSOR_TYPES: tuple[ECSensorEntityDescription, ...] = ( key="dewpoint", name="Dew point", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, value_fn=lambda data: data.conditions.get("dewpoint", {}).get("value"), ), @@ -67,7 +67,7 @@ SENSOR_TYPES: tuple[ECSensorEntityDescription, ...] = ( key="high_temp", name="High temperature", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, value_fn=lambda data: data.conditions.get("high_temp", {}).get("value"), ), @@ -75,7 +75,7 @@ SENSOR_TYPES: tuple[ECSensorEntityDescription, ...] = ( key="humidex", name="Humidex", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, value_fn=lambda data: data.conditions.get("humidex", {}).get("value"), ), @@ -96,7 +96,7 @@ SENSOR_TYPES: tuple[ECSensorEntityDescription, ...] = ( key="low_temp", name="Low temperature", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, value_fn=lambda data: data.conditions.get("low_temp", {}).get("value"), ), @@ -104,14 +104,14 @@ SENSOR_TYPES: tuple[ECSensorEntityDescription, ...] = ( key="normal_high", name="Normal high temperature", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, value_fn=lambda data: data.conditions.get("normal_high", {}).get("value"), ), ECSensorEntityDescription( key="normal_low", name="Normal low temperature", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, value_fn=lambda data: data.conditions.get("normal_low", {}).get("value"), ), ECSensorEntityDescription( @@ -123,7 +123,8 @@ SENSOR_TYPES: tuple[ECSensorEntityDescription, ...] = ( ECSensorEntityDescription( key="precip_yesterday", name="Precipitation yesterday", - native_unit_of_measurement=LENGTH_MILLIMETERS, + device_class=SensorDeviceClass.PRECIPITATION, + native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, state_class=SensorStateClass.MEASUREMENT, value_fn=lambda data: data.conditions.get("precip_yesterday", {}).get("value"), ), @@ -131,7 +132,7 @@ SENSOR_TYPES: tuple[ECSensorEntityDescription, ...] = ( key="pressure", name="Barometric pressure", device_class=SensorDeviceClass.PRESSURE, - native_unit_of_measurement=PRESSURE_KPA, + native_unit_of_measurement=UnitOfPressure.KPA, state_class=SensorStateClass.MEASUREMENT, value_fn=lambda data: data.conditions.get("pressure", {}).get("value"), ), @@ -139,7 +140,7 @@ SENSOR_TYPES: tuple[ECSensorEntityDescription, ...] = ( key="temperature", name="Temperature", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, value_fn=lambda data: data.conditions.get("temperature", {}).get("value"), ), @@ -171,7 +172,7 @@ SENSOR_TYPES: tuple[ECSensorEntityDescription, ...] = ( ECSensorEntityDescription( key="visibility", name="Visibility", - native_unit_of_measurement=LENGTH_KILOMETERS, + native_unit_of_measurement=UnitOfLength.KILOMETERS, device_class=SensorDeviceClass.DISTANCE, state_class=SensorStateClass.MEASUREMENT, value_fn=lambda data: data.conditions.get("visibility", {}).get("value"), @@ -186,7 +187,7 @@ SENSOR_TYPES: tuple[ECSensorEntityDescription, ...] = ( key="wind_chill", name="Wind chill", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, value_fn=lambda data: data.conditions.get("wind_chill", {}).get("value"), ), @@ -198,16 +199,16 @@ SENSOR_TYPES: tuple[ECSensorEntityDescription, ...] = ( ECSensorEntityDescription( key="wind_gust", name="Wind gust", - native_unit_of_measurement=SPEED_KILOMETERS_PER_HOUR, - device_class=SensorDeviceClass.SPEED, + native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, + device_class=SensorDeviceClass.WIND_SPEED, state_class=SensorStateClass.MEASUREMENT, value_fn=lambda data: data.conditions.get("wind_gust", {}).get("value"), ), ECSensorEntityDescription( key="wind_speed", name="Wind speed", - native_unit_of_measurement=SPEED_KILOMETERS_PER_HOUR, - device_class=SensorDeviceClass.SPEED, + native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, + device_class=SensorDeviceClass.WIND_SPEED, state_class=SensorStateClass.MEASUREMENT, value_fn=lambda data: data.conditions.get("wind_speed", {}).get("value"), ), diff --git a/homeassistant/components/environment_canada/translations/ko.json b/homeassistant/components/environment_canada/translations/ko.json new file mode 100644 index 00000000000..6341f560313 --- /dev/null +++ b/homeassistant/components/environment_canada/translations/ko.json @@ -0,0 +1,16 @@ +{ + "config": { + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "latitude": "\uc704\ub3c4", + "longitude": "\uacbd\ub3c4" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/environment_canada/translations/sk.json b/homeassistant/components/environment_canada/translations/sk.json index 7558d93d309..60c5901b5f1 100644 --- a/homeassistant/components/environment_canada/translations/sk.json +++ b/homeassistant/components/environment_canada/translations/sk.json @@ -3,6 +3,7 @@ "error": { "bad_station_id": "ID stanice je neplatn\u00e9, ch\u00fdba alebo sa nenach\u00e1dza v datab\u00e1ze ID stanice", "cannot_connect": "Nepodarilo sa pripoji\u0165", + "error_response": "Chybn\u00e1 odpove\u010f od Environment Canada", "too_many_attempts": "Spojenie s Environment Canada m\u00e1 obmedzen\u00fa r\u00fdchlos\u0165; Sk\u00faste to znova o 60 sek\u00fand", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, @@ -13,7 +14,9 @@ "latitude": "Zemepisn\u00e1 \u0161\u00edrka", "longitude": "Zemepisn\u00e1 d\u013a\u017eka", "station": "ID meteorologickej stanice" - } + }, + "description": "Mus\u00edte zada\u0165 ID stanice alebo zemepisn\u00fa \u0161\u00edrku/d\u013a\u017eku. Predvolen\u00e1 zemepisn\u00e1 \u0161\u00edrka/d\u013a\u017eka s\u00fa hodnoty nakonfigurovan\u00e9 v in\u0161tal\u00e1cii aplik\u00e1cie Home Assistant. Pri zad\u00e1van\u00ed s\u00faradn\u00edc sa pou\u017eije meteorologick\u00e1 stanica, ktor\u00e1 je najbli\u017e\u0161ie k s\u00faradniciam. Ak sa pou\u017eije k\u00f3d stanice, mus\u00ed ma\u0165 form\u00e1t: PP/k\u00f3d, kde PP je dvojp\u00edsmenov\u00e1 provincia a k\u00f3d je ID stanice. Zoznam ID stan\u00edc n\u00e1jdete tu: https://dd.weather.gc.ca/citypage_weather/docs/site_list_towns_en.csv. Inform\u00e1cie o po\u010das\u00ed je mo\u017en\u00e9 z\u00edska\u0165 v angli\u010dtine alebo franc\u00faz\u0161tine.", + "title": "Environment Canada: poloha a jazyk po\u010dasia" } } } diff --git a/homeassistant/components/ephember/climate.py b/homeassistant/components/ephember/climate.py index 9c83a1c8a67..3735b4d16c2 100644 --- a/homeassistant/components/ephember/climate.py +++ b/homeassistant/components/ephember/climate.py @@ -29,7 +29,7 @@ from homeassistant.const import ( ATTR_TEMPERATURE, CONF_PASSWORD, CONF_USERNAME, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv @@ -82,7 +82,7 @@ class EphEmberThermostat(ClimateEntity): """Representation of a EphEmber thermostat.""" _attr_hvac_modes = OPERATION_LIST - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS def __init__(self, ember, zone): """Initialize the thermostat.""" diff --git a/homeassistant/components/epson/translations/pt.json b/homeassistant/components/epson/translations/pt.json index 38336a1d5de..65c2b96fda0 100644 --- a/homeassistant/components/epson/translations/pt.json +++ b/homeassistant/components/epson/translations/pt.json @@ -1,12 +1,12 @@ { "config": { "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "user": { "data": { - "host": "Servidor", + "host": "Endere\u00e7o", "name": "Nome" } } diff --git a/homeassistant/components/epson/translations/sk.json b/homeassistant/components/epson/translations/sk.json index 1820d9d8d42..36de75cdda8 100644 --- a/homeassistant/components/epson/translations/sk.json +++ b/homeassistant/components/epson/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "error": { - "cannot_connect": "Nepodarilo sa pripoji\u0165" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "powered_off": "Je projektor zapnut\u00fd? Na \u00favodn\u00fa konfigur\u00e1ciu mus\u00edte zapn\u00fa\u0165 projektor." }, "step": { "user": { diff --git a/homeassistant/components/eq3btsmart/climate.py b/homeassistant/components/eq3btsmart/climate.py index 027366c96ef..1ac4531a376 100644 --- a/homeassistant/components/eq3btsmart/climate.py +++ b/homeassistant/components/eq3btsmart/climate.py @@ -21,7 +21,7 @@ from homeassistant.const import ( CONF_DEVICES, CONF_MAC, PRECISION_HALVES, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv @@ -107,7 +107,7 @@ class EQ3BTSmartThermostat(ClimateEntity): _attr_supported_features = ( ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE ) - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS def __init__(self, mac: str, name: str) -> None: """Initialize the thermostat.""" diff --git a/homeassistant/components/escea/climate.py b/homeassistant/components/escea/climate.py index a764c019c50..df191afb859 100644 --- a/homeassistant/components/escea/climate.py +++ b/homeassistant/components/escea/climate.py @@ -16,7 +16,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, TEMP_CELSIUS +from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, UnitOfTemperature from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import DeviceInfo @@ -84,7 +84,7 @@ class ControllerEntity(ClimateEntity): ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.FAN_MODE ) _attr_target_temperature_step = PRECISION_WHOLE - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS def __init__(self, controller: Controller) -> None: """Initialise ControllerDevice.""" diff --git a/homeassistant/components/escea/translations/ko.json b/homeassistant/components/escea/translations/ko.json new file mode 100644 index 00000000000..00e55839e84 --- /dev/null +++ b/homeassistant/components/escea/translations/ko.json @@ -0,0 +1,8 @@ +{ + "config": { + "abort": { + "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4", + "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/escea/translations/pt.json b/homeassistant/components/escea/translations/pt.json index 3650c239009..29991705a77 100644 --- a/homeassistant/components/escea/translations/pt.json +++ b/homeassistant/components/escea/translations/pt.json @@ -1,5 +1,9 @@ { "config": { + "abort": { + "no_devices_found": "Nenhum dispositivo encontrado na rede", + "single_instance_allowed": "J\u00e1 configurado. Apenas uma \u00fanica configura\u00e7\u00e3o \u00e9 poss\u00edvel." + }, "step": { "confirm": { "description": "Quer montar uma lareira Escea?" diff --git a/homeassistant/components/escea/translations/sk.json b/homeassistant/components/escea/translations/sk.json index 99798036ffd..c47fc3eaef8 100644 --- a/homeassistant/components/escea/translations/sk.json +++ b/homeassistant/components/escea/translations/sk.json @@ -3,6 +3,11 @@ "abort": { "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, + "step": { + "confirm": { + "description": "Chcete nastavi\u0165 Escea krb?" + } } } } \ No newline at end of file diff --git a/homeassistant/components/esphome/__init__.py b/homeassistant/components/esphome/__init__.py index 3315711f4ad..bfee5658679 100644 --- a/homeassistant/components/esphome/__init__.py +++ b/homeassistant/components/esphome/__init__.py @@ -12,7 +12,6 @@ from aioesphomeapi import ( APIConnectionError, APIIntEnum, APIVersion, - BadNameAPIError, DeviceInfo as EsphomeDeviceInfo, EntityCategory as EsphomeEntityCategory, EntityInfo, @@ -43,6 +42,7 @@ from homeassistant.exceptions import TemplateError from homeassistant.helpers import template import homeassistant.helpers.config_validation as cv import homeassistant.helpers.device_registry as dr +from homeassistant.helpers.device_registry import format_mac from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import DeviceInfo, Entity, EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -65,8 +65,12 @@ CONF_NOISE_PSK = "noise_psk" _LOGGER = logging.getLogger(__name__) _R = TypeVar("_R") -STABLE_BLE_VERSION_STR = "2022.11.0" +STABLE_BLE_VERSION_STR = "2022.12.0" STABLE_BLE_VERSION = AwesomeVersion(STABLE_BLE_VERSION_STR) +PROJECT_URLS = { + "esphome.bluetooth-proxy": "https://esphome.github.io/bluetooth-proxies/", +} +DEFAULT_URL = f"https://esphome.io/changelog/{STABLE_BLE_VERSION_STR}.html" @callback @@ -74,13 +78,13 @@ def _async_check_firmware_version( hass: HomeAssistant, device_info: EsphomeDeviceInfo ) -> None: """Create or delete an the ble_firmware_outdated issue.""" - # ESPHome device_info.name is the unique_id - issue = f"ble_firmware_outdated-{device_info.name}" + # ESPHome device_info.mac_address is the unique_id + issue = f"ble_firmware_outdated-{device_info.mac_address}" if ( not device_info.bluetooth_proxy_version # If the device has a project name its up to that project # to tell them about the firmware version update so we don't notify here - or device_info.project_name + or (device_info.project_name and device_info.project_name not in PROJECT_URLS) or AwesomeVersion(device_info.esphome_version) >= STABLE_BLE_VERSION ): async_delete_issue(hass, DOMAIN, issue) @@ -91,9 +95,12 @@ def _async_check_firmware_version( issue, is_fixable=False, severity=IssueSeverity.WARNING, - learn_more_url=f"https://esphome.io/changelog/{STABLE_BLE_VERSION_STR}.html", + learn_more_url=PROJECT_URLS.get(device_info.project_name, DEFAULT_URL), translation_key="ble_firmware_outdated", - translation_placeholders={"name": device_info.name}, + translation_placeholders={ + "name": device_info.name, + "version": STABLE_BLE_VERSION_STR, + }, ) @@ -252,13 +259,26 @@ async def async_setup_entry( # noqa: C901 nonlocal device_id try: device_info = await cli.device_info() + + # Migrate config entry to new unique ID if necessary + # This was changed in 2023.1 + if entry.unique_id != format_mac(device_info.mac_address): + hass.config_entries.async_update_entry( + entry, unique_id=format_mac(device_info.mac_address) + ) + entry_data.device_info = device_info assert cli.api_version is not None entry_data.api_version = cli.api_version entry_data.available = True if entry_data.device_info.name: - cli.expected_name = entry_data.device_info.name reconnect_logic.name = entry_data.device_info.name + + if device_info.bluetooth_proxy_version: + entry_data.disconnect_callbacks.append( + await async_connect_scanner(hass, entry, cli, entry_data) + ) + device_id = _async_setup_device_registry( hass, entry, entry_data.device_info ) @@ -270,10 +290,6 @@ async def async_setup_entry( # noqa: C901 await cli.subscribe_states(entry_data.async_update_state) await cli.subscribe_service_calls(async_on_service_call) await cli.subscribe_home_assistant_states(async_on_state_subscription) - if entry_data.device_info.bluetooth_proxy_version: - entry_data.disconnect_callbacks.append( - await async_connect_scanner(hass, entry, cli, entry_data) - ) hass.async_create_task(entry_data.async_save_to_store()) except APIConnectionError as err: @@ -297,12 +313,6 @@ async def async_setup_entry( # noqa: C901 """Start reauth flow if appropriate connect error type.""" if isinstance(err, (RequiresEncryptionAPIError, InvalidEncryptionKeyAPIError)): entry.async_start_reauth(hass) - if isinstance(err, BadNameAPIError): - _LOGGER.warning( - "Name of device %s changed to %s, potentially due to IP reassignment", - cli.expected_name, - err.received_name, - ) reconnect_logic = ReconnectLogic( client=cli, @@ -318,11 +328,10 @@ async def async_setup_entry( # noqa: C901 await _setup_services(hass, entry_data, services) if entry_data.device_info is not None and entry_data.device_info.name: - cli.expected_name = entry_data.device_info.name reconnect_logic.name = entry_data.device_info.name if entry.unique_id is None: hass.config_entries.async_update_entry( - entry, unique_id=entry_data.device_info.name + entry, unique_id=format_mac(entry_data.device_info.mac_address) ) await reconnect_logic.start() @@ -462,7 +471,10 @@ async def _register_service( ) service_desc = { - "description": f"Calls the service {service.name} of the node {entry_data.device_info.name}", + "description": ( + f"Calls the service {service.name} of the node" + f" {entry_data.device_info.name}" + ), "fields": fields, } @@ -688,10 +700,7 @@ class EsphomeEntity(Entity, Generic[_InfoT, _StateT]): self.async_on_remove( async_dispatcher_connect( self.hass, - ( - f"esphome_{self._entry_id}_remove_" - f"{self._component_key}_{self._key}" - ), + f"esphome_{self._entry_id}_remove_{self._component_key}_{self._key}", functools.partial(self.async_remove, force_remove=True), ) ) diff --git a/homeassistant/components/esphome/bluetooth/__init__.py b/homeassistant/components/esphome/bluetooth/__init__.py index 4b6281c7c5a..4a70b906b1f 100644 --- a/homeassistant/components/esphome/bluetooth/__init__.py +++ b/homeassistant/components/esphome/bluetooth/__init__.py @@ -2,6 +2,7 @@ from __future__ import annotations from collections.abc import Callable +from functools import partial import logging from aioesphomeapi import APIClient @@ -32,7 +33,11 @@ def _async_can_connect_factory( """Check if a given source can make another connection.""" can_connect = bool(entry_data.available and entry_data.ble_connections_free) _LOGGER.debug( - "%s: Checking can connect, available=%s, ble_connections_free=%s result=%s", + ( + "%s [%s]: Checking can connect, available=%s, ble_connections_free=%s" + " result=%s" + ), + entry_data.name, source, entry_data.available, entry_data.ble_connections_free, @@ -57,13 +62,16 @@ async def async_connect_scanner( version = entry_data.device_info.bluetooth_proxy_version connectable = version >= 2 _LOGGER.debug( - "%s: Connecting scanner version=%s, connectable=%s", + "%s [%s]: Connecting scanner version=%s, connectable=%s", + entry.title, source, version, connectable, ) connector = HaBluetoothConnector( - client=ESPHomeClient, + # MyPy doesn't like partials, but this is correct + # https://github.com/python/mypy/issues/1484 + client=partial(ESPHomeClient, config_entry=entry), # type: ignore[arg-type] source=source, can_connect=_async_can_connect_factory(entry_data, source), ) diff --git a/homeassistant/components/esphome/bluetooth/client.py b/homeassistant/components/esphome/bluetooth/client.py index 541eb831ca5..6aa21c315a3 100644 --- a/homeassistant/components/esphome/bluetooth/client.py +++ b/homeassistant/components/esphome/bluetooth/client.py @@ -23,6 +23,7 @@ from bleak.backends.service import BleakGATTServiceCollection from bleak.exc import BleakError from homeassistant.components.bluetooth import async_scanner_by_source +from homeassistant.config_entries import ConfigEntry from homeassistant.core import CALLBACK_TYPE, HomeAssistant from ..domain_data import DomainData @@ -77,7 +78,7 @@ def verify_connected(func: _WrapFuncType) -> _WrapFuncType: with contextlib.suppress(asyncio.CancelledError): await task raise BleakError( - f"{self._source}: {self._ble_device.name} - {self._ble_device.address}: " # pylint: disable=protected-access + f"{self._source_name}: {self._ble_device.name} - {self._ble_device.address}: " # pylint: disable=protected-access "Disconnected during operation" ) return next(iter(done)).result() @@ -108,7 +109,7 @@ def api_error_as_bleak_error(func: _WrapFuncType) -> _WrapFuncType: if ex.error.error == -1: _LOGGER.debug( "%s: %s - %s: BLE device disconnected during %s operation", - self._source, + self._source_name, self._ble_device.name, self._ble_device.address, func.__name__, @@ -125,7 +126,11 @@ class ESPHomeClient(BaseBleakClient): """ESPHome Bleak Client.""" def __init__( - self, address_or_ble_device: BLEDevice | str, *args: Any, **kwargs: Any + self, + address_or_ble_device: BLEDevice | str, + *args: Any, + config_entry: ConfigEntry, + **kwargs: Any, ) -> None: """Initialize the ESPHomeClient.""" assert isinstance(address_or_ble_device, BLEDevice) @@ -136,7 +141,6 @@ class ESPHomeClient(BaseBleakClient): assert self._ble_device.details is not None self._source = self._ble_device.details["source"] self.domain_data = DomainData.get(self._hass) - config_entry = self.domain_data.get_by_unique_id(self._source) self.entry_data = self.domain_data.get_entry_data(config_entry) self._client = self.entry_data.client self._is_connected = False @@ -150,6 +154,7 @@ class ESPHomeClient(BaseBleakClient): assert device_info is not None self._connection_version = device_info.bluetooth_proxy_version self._address_type = address_or_ble_device.details["address_type"] + self._source_name = f"{config_entry.title} [{self._source}]" def __str__(self) -> str: """Return the string representation of the client.""" @@ -163,8 +168,11 @@ class ESPHomeClient(BaseBleakClient): self._cancel_connection_state() except (AssertionError, ValueError) as ex: _LOGGER.debug( - "%s: %s - %s: Failed to unsubscribe from connection state (likely connection dropped): %s", - self._source, + ( + "%s: %s - %s: Failed to unsubscribe from connection state (likely" + " connection dropped): %s" + ), + self._source_name, self._ble_device.name, self._ble_device.address, ex, @@ -190,7 +198,7 @@ class ESPHomeClient(BaseBleakClient): if was_connected: _LOGGER.debug( "%s: %s - %s: BLE device disconnected", - self._source, + self._source_name, self._ble_device.name, self._ble_device.address, ) @@ -200,7 +208,7 @@ class ESPHomeClient(BaseBleakClient): """Handle the esp32 client disconnecting from hass.""" _LOGGER.debug( "%s: %s - %s: ESP device disconnected", - self._source, + self._source_name, self._ble_device.name, self._ble_device.address, ) @@ -225,12 +233,14 @@ class ESPHomeClient(BaseBleakClient): Boolean representing connection status. """ await self._wait_for_free_connection_slot(CONNECT_FREE_SLOT_TIMEOUT) + domain_data = self.domain_data entry_data = self.entry_data - self._mtu = entry_data.get_gatt_mtu_cache(self._address_as_int) + + self._mtu = domain_data.get_gatt_mtu_cache(self._address_as_int) has_cache = bool( dangerous_use_bleak_cache and self._connection_version >= MIN_BLUETOOTH_PROXY_VERSION_HAS_CACHE - and entry_data.get_gatt_services_cache(self._address_as_int) + and domain_data.get_gatt_services_cache(self._address_as_int) and self._mtu ) connected_future: asyncio.Future[bool] = asyncio.Future() @@ -241,7 +251,7 @@ class ESPHomeClient(BaseBleakClient): """Handle a connect or disconnect.""" _LOGGER.debug( "%s: %s - %s: Connection state changed to connected=%s mtu=%s error=%s", - self._source, + self._source_name, self._ble_device.name, self._ble_device.address, connected, @@ -252,7 +262,7 @@ class ESPHomeClient(BaseBleakClient): self._is_connected = True if not self._mtu: self._mtu = mtu - entry_data.set_gatt_mtu_cache(self._address_as_int, mtu) + domain_data.set_gatt_mtu_cache(self._address_as_int, mtu) else: self._async_ble_device_disconnected() @@ -271,7 +281,8 @@ class ESPHomeClient(BaseBleakClient): ) connected_future.set_exception( BleakError( - f"Error {ble_connection_error_name} while connecting: {human_error}" + f"Error {ble_connection_error_name} while connecting:" + f" {human_error}" ) ) return @@ -282,7 +293,7 @@ class ESPHomeClient(BaseBleakClient): _LOGGER.debug( "%s: %s - %s: connected, registering for disconnected callbacks", - self._source, + self._source_name, self._ble_device.name, self._ble_device.address, ) @@ -291,7 +302,7 @@ class ESPHomeClient(BaseBleakClient): timeout = kwargs.get("timeout", self._timeout) if not (scanner := async_scanner_by_source(self._hass, self._source)): - raise BleakError("Scanner disappeared for {self._source}") + raise BleakError("Scanner disappeared for {self._source_name}") with scanner.connecting(): try: self._cancel_connection_state = ( @@ -348,7 +359,7 @@ class ESPHomeClient(BaseBleakClient): return _LOGGER.debug( "%s: %s - %s: Out of connection slots, waiting for a free one", - self._source, + self._source_name, self._ble_device.name, self._ble_device.address, ) @@ -387,17 +398,17 @@ class ESPHomeClient(BaseBleakClient): A :py:class:`bleak.backends.service.BleakGATTServiceCollection` with this device's services tree. """ address_as_int = self._address_as_int - entry_data = self.entry_data + domain_data = self.domain_data # If the connection version >= 3, we must use the cache # because the esp has already wiped the services list to # save memory. if ( self._connection_version >= MIN_BLUETOOTH_PROXY_VERSION_HAS_CACHE or dangerous_use_bleak_cache - ) and (cached_services := entry_data.get_gatt_services_cache(address_as_int)): + ) and (cached_services := domain_data.get_gatt_services_cache(address_as_int)): _LOGGER.debug( "%s: %s - %s: Cached services hit", - self._source, + self._source_name, self._ble_device.name, self._ble_device.address, ) @@ -405,7 +416,7 @@ class ESPHomeClient(BaseBleakClient): return self.services _LOGGER.debug( "%s: %s - %s: Cached services miss", - self._source, + self._source_name, self._ble_device.name, self._ble_device.address, ) @@ -414,7 +425,7 @@ class ESPHomeClient(BaseBleakClient): ) _LOGGER.debug( "%s: %s - %s: Got services: %s", - self._source, + self._source_name, self._ble_device.name, self._ble_device.address, esphome_services, @@ -449,11 +460,11 @@ class ESPHomeClient(BaseBleakClient): self.services = services _LOGGER.debug( "%s: %s - %s: Cached services saved", - self._source, + self._source_name, self._ble_device.name, self._ble_device.address, ) - entry_data.set_gatt_services_cache(address_as_int, services) + domain_data.set_gatt_services_cache(address_as_int, services) return services def _resolve_characteristic( @@ -470,8 +481,8 @@ class ESPHomeClient(BaseBleakClient): async def clear_cache(self) -> None: """Clear the GATT cache.""" - self.entry_data.clear_gatt_services_cache(self._address_as_int) - self.entry_data.clear_gatt_mtu_cache(self._address_as_int) + self.domain_data.clear_gatt_services_cache(self._address_as_int) + self.domain_data.clear_gatt_mtu_cache(self._address_as_int) @verify_connected @api_error_as_bleak_error @@ -580,7 +591,8 @@ class ESPHomeClient(BaseBleakClient): and "indicate" not in characteristic.properties ): raise BleakError( - f"Characteristic {characteristic.uuid} does not have notify or indicate property set." + f"Characteristic {characteristic.uuid} does not have notify or indicate" + " property set." ) self._notify_cancels[ @@ -607,8 +619,11 @@ class ESPHomeClient(BaseBleakClient): ) _LOGGER.debug( - "%s: %s - %s: Writing to CCD descriptor %s for notifications with properties=%s", - self._source, + ( + "%s: %s - %s: Writing to CCD descriptor %s for notifications with" + " properties=%s" + ), + self._source_name, self._ble_device.name, self._ble_device.address, cccd_descriptor.handle, @@ -645,8 +660,11 @@ class ESPHomeClient(BaseBleakClient): """Destructor to make sure the connection state is unsubscribed.""" if self._cancel_connection_state: _LOGGER.warning( - "%s: %s - %s: ESPHomeClient bleak client was not properly disconnected before destruction", - self._source, + ( + "%s: %s - %s: ESPHomeClient bleak client was not properly" + " disconnected before destruction" + ), + self._source_name, self._ble_device.name, self._ble_device.address, ) diff --git a/homeassistant/components/esphome/bluetooth/scanner.py b/homeassistant/components/esphome/bluetooth/scanner.py index ea44ff45d1c..19d59843746 100644 --- a/homeassistant/components/esphome/bluetooth/scanner.py +++ b/homeassistant/components/esphome/bluetooth/scanner.py @@ -1,8 +1,6 @@ """Bluetooth scanner for esphome.""" from __future__ import annotations -from typing import Any - from aioesphomeapi import BluetoothLEAdvertisement from homeassistant.components.bluetooth import BaseHaRemoteScanner @@ -27,19 +25,3 @@ class ESPHomeScanner(BaseHaRemoteScanner): None, {"address_type": adv.address_type}, ) - - async def async_diagnostics(self) -> dict[str, Any]: - """Return diagnostic information about the scanner.""" - return await super().async_diagnostics() | { - "type": self.__class__.__name__, - "discovered_devices_and_advertisement_data": [ - { - "name": device_adv[0].name, - "address": device_adv[0].address, - "rssi": device_adv[0].rssi, - "advertisement_data": device_adv[1], - "details": device_adv[0].details, - } - for device_adv in self.discovered_devices_and_advertisement_data.values() - ], - } diff --git a/homeassistant/components/esphome/climate.py b/homeassistant/components/esphome/climate.py index 352068aaa57..058e557b1a8 100644 --- a/homeassistant/components/esphome/climate.py +++ b/homeassistant/components/esphome/climate.py @@ -49,7 +49,7 @@ from homeassistant.const import ( PRECISION_HALVES, PRECISION_TENTHS, PRECISION_WHOLE, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -136,7 +136,7 @@ _PRESETS: EsphomeEnumMapper[ClimatePreset, str] = EsphomeEnumMapper( class EsphomeClimateEntity(EsphomeEntity[ClimateInfo, ClimateState], ClimateEntity): """A climate implementation for ESPHome.""" - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS @property def precision(self) -> float: diff --git a/homeassistant/components/esphome/config_flow.py b/homeassistant/components/esphome/config_flow.py index 542aa011a7b..7186eda039b 100644 --- a/homeassistant/components/esphome/config_flow.py +++ b/homeassistant/components/esphome/config_flow.py @@ -21,8 +21,9 @@ from homeassistant.config_entries import ConfigEntry, ConfigFlow from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PASSWORD, CONF_PORT from homeassistant.core import callback from homeassistant.data_entry_flow import FlowResult +from homeassistant.helpers.device_registry import format_mac -from . import CONF_NOISE_PSK, DOMAIN, DomainData +from . import CONF_NOISE_PSK, DOMAIN ERROR_REQUIRES_ENCRYPTION_KEY = "requires_encryption_key" ESPHOME_URL = "https://esphome.io/" @@ -149,93 +150,35 @@ class EsphomeFlowHandler(ConfigFlow, domain=DOMAIN): self, discovery_info: zeroconf.ZeroconfServiceInfo ) -> FlowResult: """Handle zeroconf discovery.""" + mac_address: str | None = discovery_info.properties.get("mac") + + # Mac address was added in Sept 20, 2021. + # https://github.com/esphome/esphome/pull/2303 + if mac_address is None: + return self.async_abort(reason="mdns_missing_mac") + + # mac address is lowercase and without :, normalize it + mac_address = format_mac(mac_address) + # Hostname is format: livingroom.local. - local_name = discovery_info.hostname[:-1] - node_name = local_name[: -len(".local")] - address = discovery_info.properties.get("address", local_name) - - # Check if already configured - await self.async_set_unique_id(node_name) - self._abort_if_unique_id_configured(updates={CONF_HOST: discovery_info.host}) - - for entry in self._async_current_entries(): - already_configured = False - - if CONF_HOST in entry.data and entry.data[CONF_HOST] in ( - address, - discovery_info.host, - ): - # Is this address or IP address already configured? - already_configured = True - elif DomainData.get(self.hass).is_entry_loaded(entry): - # Does a config entry with this name already exist? - data = DomainData.get(self.hass).get_entry_data(entry) - - # Node names are unique in the network - if data.device_info is not None: - already_configured = data.device_info.name == node_name - - if already_configured: - # Backwards compat, we update old entries - if not entry.unique_id: - self.hass.config_entries.async_update_entry( - entry, - data={ - **entry.data, - CONF_HOST: discovery_info.host, - }, - unique_id=node_name, - ) - - return self.async_abort(reason="already_configured") - + self._name = discovery_info.hostname[: -len(".local.")] self._host = discovery_info.host self._port = discovery_info.port - self._name = node_name + + # Check if already configured + await self.async_set_unique_id(mac_address) + self._abort_if_unique_id_configured( + updates={CONF_HOST: self._host, CONF_PORT: self._port} + ) return await self.async_step_discovery_confirm() async def async_step_dhcp(self, discovery_info: dhcp.DhcpServiceInfo) -> FlowResult: """Handle DHCP discovery.""" - node_name = discovery_info.hostname - - await self.async_set_unique_id(node_name) + await self.async_set_unique_id(format_mac(discovery_info.macaddress)) self._abort_if_unique_id_configured(updates={CONF_HOST: discovery_info.ip}) - - for entry in self._async_current_entries(): - found = False - - if CONF_HOST in entry.data and entry.data[CONF_HOST] in ( - discovery_info.ip, - f"{node_name}.local", - ): - # Is this address or IP address already configured? - found = True - elif DomainData.get(self.hass).is_entry_loaded(entry): - # Does a config entry with this name already exist? - data = DomainData.get(self.hass).get_entry_data(entry) - - # Node names are unique in the network - if data.device_info is not None: - found = data.device_info.name == node_name - - if found: - # Backwards compat, we update old entries - if not entry.unique_id: - self.hass.config_entries.async_update_entry( - entry, - data={ - **entry.data, - CONF_HOST: discovery_info.ip, - }, - unique_id=node_name, - ) - self.hass.async_create_task( - self.hass.config_entries.async_reload(entry.entry_id) - ) - - break - + # This should never happen since we only listen to DHCP requests + # for configured devices. return self.async_abort(reason="already_configured") @callback @@ -334,9 +277,13 @@ class EsphomeFlowHandler(ConfigFlow, domain=DOMAIN): await cli.disconnect(force=True) self._name = self._device_info.name - await self.async_set_unique_id(self._name, raise_on_progress=False) + await self.async_set_unique_id( + self._device_info.mac_address, raise_on_progress=False + ) if not self._reauth_entry: - self._abort_if_unique_id_configured(updates={CONF_HOST: self._host}) + self._abort_if_unique_id_configured( + updates={CONF_HOST: self._host, CONF_PORT: self._port} + ) return None diff --git a/homeassistant/components/esphome/cover.py b/homeassistant/components/esphome/cover.py index 1b3e42d5a17..8c28639ff34 100644 --- a/homeassistant/components/esphome/cover.py +++ b/homeassistant/components/esphome/cover.py @@ -1,6 +1,7 @@ """Support for ESPHome covers.""" from __future__ import annotations +from contextlib import suppress from typing import Any from aioesphomeapi import CoverInfo, CoverOperation, CoverState @@ -8,7 +9,7 @@ from aioesphomeapi import CoverInfo, CoverOperation, CoverState from homeassistant.components.cover import ( ATTR_POSITION, ATTR_TILT_POSITION, - DEVICE_CLASSES, + CoverDeviceClass, CoverEntity, CoverEntityFeature, ) @@ -54,11 +55,11 @@ class EsphomeCover(EsphomeEntity[CoverInfo, CoverState], CoverEntity): return flags @property - def device_class(self) -> str | None: + def device_class(self) -> CoverDeviceClass | None: """Return the class of this device, from component DEVICE_CLASSES.""" - if self._static_info.device_class not in DEVICE_CLASSES: - return None - return self._static_info.device_class + with suppress(ValueError): + return CoverDeviceClass(self._static_info.device_class) + return None @property def assumed_state(self) -> bool: diff --git a/homeassistant/components/esphome/domain_data.py b/homeassistant/components/esphome/domain_data.py index 01f0a4d6b13..93ff69852a0 100644 --- a/homeassistant/components/esphome/domain_data.py +++ b/homeassistant/components/esphome/domain_data.py @@ -1,9 +1,13 @@ """Support for esphome domain data.""" from __future__ import annotations +from collections.abc import MutableMapping from dataclasses import dataclass, field from typing import TypeVar, cast +from bleak.backends.service import BleakGATTServiceCollection +from lru import LRU # pylint: disable=no-name-in-module + from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.json import JSONEncoder @@ -13,6 +17,7 @@ from .entry_data import RuntimeEntryData STORAGE_VERSION = 1 DOMAIN = "esphome" +MAX_CACHED_SERVICES = 128 _DomainDataSelfT = TypeVar("_DomainDataSelfT", bound="DomainData") @@ -23,11 +28,40 @@ class DomainData: _entry_datas: dict[str, RuntimeEntryData] = field(default_factory=dict) _stores: dict[str, Store] = field(default_factory=dict) - _entry_by_unique_id: dict[str, ConfigEntry] = field(default_factory=dict) + _gatt_services_cache: MutableMapping[int, BleakGATTServiceCollection] = field( + default_factory=lambda: LRU(MAX_CACHED_SERVICES) # type: ignore[no-any-return] + ) + _gatt_mtu_cache: MutableMapping[int, int] = field( + default_factory=lambda: LRU(MAX_CACHED_SERVICES) # type: ignore[no-any-return] + ) - def get_by_unique_id(self, unique_id: str) -> ConfigEntry: - """Get the config entry by its unique ID.""" - return self._entry_by_unique_id[unique_id] + def get_gatt_services_cache( + self, address: int + ) -> BleakGATTServiceCollection | None: + """Get the BleakGATTServiceCollection for the given address.""" + return self._gatt_services_cache.get(address) + + def set_gatt_services_cache( + self, address: int, services: BleakGATTServiceCollection + ) -> None: + """Set the BleakGATTServiceCollection for the given address.""" + self._gatt_services_cache[address] = services + + def clear_gatt_services_cache(self, address: int) -> None: + """Clear the BleakGATTServiceCollection for the given address.""" + self._gatt_services_cache.pop(address, None) + + def get_gatt_mtu_cache(self, address: int) -> int | None: + """Get the mtu cache for the given address.""" + return self._gatt_mtu_cache.get(address) + + def set_gatt_mtu_cache(self, address: int, mtu: int) -> None: + """Set the mtu cache for the given address.""" + self._gatt_mtu_cache[address] = mtu + + def clear_gatt_mtu_cache(self, address: int) -> None: + """Clear the mtu cache for the given address.""" + self._gatt_mtu_cache.pop(address, None) def get_entry_data(self, entry: ConfigEntry) -> RuntimeEntryData: """Return the runtime entry data associated with this config entry. @@ -41,13 +75,9 @@ class DomainData: if entry.entry_id in self._entry_datas: raise ValueError("Entry data for this entry is already set") self._entry_datas[entry.entry_id] = entry_data - if entry.unique_id: - self._entry_by_unique_id[entry.unique_id] = entry def pop_entry_data(self, entry: ConfigEntry) -> RuntimeEntryData: """Pop the runtime entry data instance associated with this config entry.""" - if entry.unique_id: - del self._entry_by_unique_id[entry.unique_id] return self._entry_datas.pop(entry.entry_id) def is_entry_loaded(self, entry: ConfigEntry) -> bool: diff --git a/homeassistant/components/esphome/entry_data.py b/homeassistant/components/esphome/entry_data.py index 2e05e01309e..959bf2f2877 100644 --- a/homeassistant/components/esphome/entry_data.py +++ b/homeassistant/components/esphome/entry_data.py @@ -2,7 +2,7 @@ from __future__ import annotations import asyncio -from collections.abc import Callable, MutableMapping +from collections.abc import Callable from dataclasses import dataclass, field import logging from typing import Any, cast @@ -30,8 +30,6 @@ from aioesphomeapi import ( UserService, ) from aioesphomeapi.model import ButtonInfo -from bleak.backends.service import BleakGATTServiceCollection -from lru import LRU # pylint: disable=no-name-in-module from homeassistant.config_entries import ConfigEntry from homeassistant.const import Platform @@ -59,7 +57,6 @@ INFO_TYPE_TO_PLATFORM: dict[type[EntityInfo], str] = { SwitchInfo: Platform.SWITCH, TextSensorInfo: Platform.SENSOR, } -MAX_CACHED_SERVICES = 128 @dataclass @@ -95,52 +92,19 @@ class RuntimeEntryData: _ble_connection_free_futures: list[asyncio.Future[int]] = field( default_factory=list ) - _gatt_services_cache: MutableMapping[int, BleakGATTServiceCollection] = field( - default_factory=lambda: LRU(MAX_CACHED_SERVICES) # type: ignore[no-any-return] - ) - _gatt_mtu_cache: MutableMapping[int, int] = field( - default_factory=lambda: LRU(MAX_CACHED_SERVICES) # type: ignore[no-any-return] - ) @property def name(self) -> str: """Return the name of the device.""" return self.device_info.name if self.device_info else self.entry_id - def get_gatt_services_cache( - self, address: int - ) -> BleakGATTServiceCollection | None: - """Get the BleakGATTServiceCollection for the given address.""" - return self._gatt_services_cache.get(address) - - def set_gatt_services_cache( - self, address: int, services: BleakGATTServiceCollection - ) -> None: - """Set the BleakGATTServiceCollection for the given address.""" - self._gatt_services_cache[address] = services - - def clear_gatt_services_cache(self, address: int) -> None: - """Clear the BleakGATTServiceCollection for the given address.""" - self._gatt_services_cache.pop(address, None) - - def get_gatt_mtu_cache(self, address: int) -> int | None: - """Get the mtu cache for the given address.""" - return self._gatt_mtu_cache.get(address) - - def set_gatt_mtu_cache(self, address: int, mtu: int) -> None: - """Set the mtu cache for the given address.""" - self._gatt_mtu_cache[address] = mtu - - def clear_gatt_mtu_cache(self, address: int) -> None: - """Clear the mtu cache for the given address.""" - self._gatt_mtu_cache.pop(address, None) - @callback def async_update_ble_connection_limits(self, free: int, limit: int) -> None: """Update the BLE connection limits.""" _LOGGER.debug( - "%s: BLE connection limits: used=%s free=%s limit=%s", + "%s [%s]: BLE connection limits: used=%s free=%s limit=%s", self.name, + self.device_info.mac_address if self.device_info else "unknown", limit - free, free, limit, diff --git a/homeassistant/components/esphome/manifest.json b/homeassistant/components/esphome/manifest.json index 83ebc60edd6..ce3dc116715 100644 --- a/homeassistant/components/esphome/manifest.json +++ b/homeassistant/components/esphome/manifest.json @@ -7,7 +7,8 @@ "zeroconf": ["_esphomelib._tcp.local."], "dhcp": [{ "registered_devices": true }], "codeowners": ["@OttoWinter", "@jesserockz"], - "after_dependencies": ["bluetooth", "zeroconf", "tag"], + "dependencies": ["bluetooth"], + "after_dependencies": ["zeroconf", "tag"], "iot_class": "local_push", "integration_type": "device", "loggers": ["aioesphomeapi", "noiseprotocol"] diff --git a/homeassistant/components/esphome/strings.json b/homeassistant/components/esphome/strings.json index 0ec4d93b405..c403de8d881 100644 --- a/homeassistant/components/esphome/strings.json +++ b/homeassistant/components/esphome/strings.json @@ -3,7 +3,8 @@ "abort": { "already_configured": "[%key:common::config_flow::abort::already_configured_device%]", "already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]", - "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]" + "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]", + "mdns_missing_mac": "Missing MAC address in MDNS properties." }, "error": { "resolve_error": "Can't resolve address of the ESP. If this error persists, please set a static IP address", @@ -46,8 +47,8 @@ }, "issues": { "ble_firmware_outdated": { - "title": "Update {name} with ESPHome 2022.11.0 or later", - "description": "To improve Bluetooth reliability and performance, we highly recommend updating {name} with ESPHome 2022.11.0 or later." + "title": "Update {name} with ESPHome {version} or later", + "description": "To improve Bluetooth reliability and performance, we highly recommend updating {name} with ESPHome {version} or later. When updating the device to ESPHome {version}, it is recommended to use a serial cable instead of an over-the-air update to take advantage of the new partition scheme." } } } diff --git a/homeassistant/components/esphome/translations/bg.json b/homeassistant/components/esphome/translations/bg.json index 553ca45a10f..bd21d0deb08 100644 --- a/homeassistant/components/esphome/translations/bg.json +++ b/homeassistant/components/esphome/translations/bg.json @@ -2,6 +2,7 @@ "config": { "abort": { "already_configured": "ESP \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", + "mdns_missing_mac": "\u041b\u0438\u043f\u0441\u0432\u0430 MAC \u0430\u0434\u0440\u0435\u0441 \u0432 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430\u0442\u0430 \u043d\u0430 MDNS.", "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" }, "error": { diff --git a/homeassistant/components/esphome/translations/bs.json b/homeassistant/components/esphome/translations/bs.json new file mode 100644 index 00000000000..334dd614e88 --- /dev/null +++ b/homeassistant/components/esphome/translations/bs.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "mdns_missing_mac": "Chyb\u011bj\u00edc\u00ed MAC adresa ve vlastnostech MDNS." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/esphome/translations/ca.json b/homeassistant/components/esphome/translations/ca.json index ce4b9ce5da2..fe5a4799616 100644 --- a/homeassistant/components/esphome/translations/ca.json +++ b/homeassistant/components/esphome/translations/ca.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "El dispositiu ja est\u00e0 configurat", "already_in_progress": "El flux de configuraci\u00f3 ja est\u00e0 en curs", + "mdns_missing_mac": "Falta l'adre\u00e7a MAC a les propietats MDNS.", "reauth_successful": "Re-autenticaci\u00f3 realitzada correctament" }, "error": { @@ -46,8 +47,8 @@ }, "issues": { "ble_firmware_outdated": { - "description": "Per millorar la fiabilitat i el rendiment de Bluetooth, et recomanem que actualitzis {name} a ESPHome 2022.11.0 o posterior.", - "title": "Actualitza {name} amb ESPHome 2022.11.0 o posterior" + "description": "Per millorar la fiabilitat i el rendiment de Bluetooth, et recomanem que actualitzis {name} a ESPHome {version} o posterior. Quan actualitzis el dispositiu a ESPHome {version}, es recomana utilitzar un cable s\u00e8rie enlloc de fer una actualitzaci\u00f3 remota, per poder aprofitar el nou esquema de particions.", + "title": "Actualitza {name} amb ESPHome {version} o posterior" } } } \ No newline at end of file diff --git a/homeassistant/components/esphome/translations/cs.json b/homeassistant/components/esphome/translations/cs.json index 9b4976ddaa1..1b580481363 100644 --- a/homeassistant/components/esphome/translations/cs.json +++ b/homeassistant/components/esphome/translations/cs.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno", "already_in_progress": "Konfigurace ji\u017e prob\u00edh\u00e1", + "mdns_missing_mac": "Chyb\u011bj\u00edc\u00ed MAC adresa ve vlastnostech MDNS.", "reauth_successful": "Op\u011btovn\u00e9 ov\u011b\u0159en\u00ed bylo \u00fasp\u011b\u0161n\u00e9" }, "error": { diff --git a/homeassistant/components/esphome/translations/de.json b/homeassistant/components/esphome/translations/de.json index bf7bcd386e5..29f702c47b0 100644 --- a/homeassistant/components/esphome/translations/de.json +++ b/homeassistant/components/esphome/translations/de.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Ger\u00e4t ist bereits konfiguriert", "already_in_progress": "Der Konfigurationsablauf wird bereits ausgef\u00fchrt", + "mdns_missing_mac": "Fehlende MAC-Adresse in MDNS-Eigenschaften.", "reauth_successful": "Die erneute Authentifizierung war erfolgreich" }, "error": { @@ -17,7 +18,7 @@ "data": { "password": "Passwort" }, - "description": "Bitte gebe das Passwort der ESPHome-Konfiguration f\u00fcr {name} ein:" + "description": "Bitte gebe das Passwort der ESPHome Konfiguration f\u00fcr {name} ein:" }, "discovery_confirm": { "description": "Willst du den ESPHome-Knoten `{name}` zu Home Assistant hinzuf\u00fcgen?", @@ -40,14 +41,14 @@ "host": "Host", "port": "Port" }, - "description": "Bitte gib die Verbindungseinstellungen deines [ESPHome]( {esphome_url} )-Knotens ein." + "description": "Bitte gib die Verbindungseinstellungen deines [ESPHome]({esphome_url}) Knotens ein." } } }, "issues": { "ble_firmware_outdated": { - "description": "Um die Zuverl\u00e4ssigkeit und Leistung von Bluetooth zu verbessern, empfehlen wir dringend, {name} mit ESPHome 2022.11.0 oder h\u00f6her zu aktualisieren.", - "title": "Aktualisieren von {name} mit ESPHome 2022.11.0 oder h\u00f6her" + "description": "Um die Zuverl\u00e4ssigkeit und Leistung von Bluetooth zu verbessern, empfehlen wir dringend, {name} mit ESPHome {version} oder h\u00f6her zu aktualisieren. Wenn du das Ger\u00e4t auf ESPHome {version} aktualisierst, wird empfohlen, ein serielles Kabel anstelle eines Over-the-Air-Updates zu verwenden, um das neue Partitionsschema zu nutzen.", + "title": "Aktualisieren von {name} mit ESPHome {version} oder h\u00f6her" } } } \ No newline at end of file diff --git a/homeassistant/components/esphome/translations/el.json b/homeassistant/components/esphome/translations/el.json index 041628a3673..b477a7fa485 100644 --- a/homeassistant/components/esphome/translations/el.json +++ b/homeassistant/components/esphome/translations/el.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b5\u03af\u03bd\u03b1\u03b9 \u03ae\u03b4\u03b7 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03bc\u03ad\u03bd\u03b7", "already_in_progress": "\u0397 \u03c1\u03bf\u03ae \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7\u03c2 \u03b2\u03c1\u03af\u03c3\u03ba\u03b5\u03c4\u03b1\u03b9 \u03ae\u03b4\u03b7 \u03c3\u03b5 \u03b5\u03be\u03ad\u03bb\u03b9\u03be\u03b7", + "mdns_missing_mac": "\u039b\u03b5\u03af\u03c0\u03b5\u03b9 \u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 MAC \u03c3\u03c4\u03b9\u03c2 \u03b9\u03b4\u03b9\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2 MDNS.", "reauth_successful": "\u0397 \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03ae\u03c4\u03b1\u03bd \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2" }, "error": { diff --git a/homeassistant/components/esphome/translations/en.json b/homeassistant/components/esphome/translations/en.json index 173113f64cd..63fc3ed0573 100644 --- a/homeassistant/components/esphome/translations/en.json +++ b/homeassistant/components/esphome/translations/en.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Device is already configured", "already_in_progress": "Configuration flow is already in progress", + "mdns_missing_mac": "Missing MAC address in MDNS properties.", "reauth_successful": "Re-authentication was successful" }, "error": { @@ -46,8 +47,8 @@ }, "issues": { "ble_firmware_outdated": { - "description": "To improve Bluetooth reliability and performance, we highly recommend updating {name} with ESPHome 2022.11.0 or later.", - "title": "Update {name} with ESPHome 2022.11.0 or later" + "description": "To improve Bluetooth reliability and performance, we highly recommend updating {name} with ESPHome {version} or later. When updating the device to ESPHome {version}, it is recommended to use a serial cable instead of an over-the-air update to take advantage of the new partition scheme.", + "title": "Update {name} with ESPHome {version} or later" } } } \ No newline at end of file diff --git a/homeassistant/components/esphome/translations/es.json b/homeassistant/components/esphome/translations/es.json index 2b10be769e4..0e6f96a2803 100644 --- a/homeassistant/components/esphome/translations/es.json +++ b/homeassistant/components/esphome/translations/es.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "El dispositivo ya est\u00e1 configurado", "already_in_progress": "El flujo de configuraci\u00f3n ya est\u00e1 en curso", + "mdns_missing_mac": "Falta la direcci\u00f3n MAC en las propiedades de mDNS.", "reauth_successful": "La autenticaci\u00f3n se volvi\u00f3 a realizar correctamente" }, "error": { @@ -46,8 +47,8 @@ }, "issues": { "ble_firmware_outdated": { - "description": "Para mejorar la confiabilidad y el rendimiento de Bluetooth, recomendamos actualizar {name} con ESPHome 2022.11.0 o posterior.", - "title": "Actualizar {name} con ESPHome 2022.11.0 o posterior" + "description": "Para mejorar la confiabilidad y el rendimiento de Bluetooth, recomendamos actualizar {name} con ESPHome {version} o posterior. Al actualizar el dispositivo a ESPHome {version}, se recomienda utilizar un cable serie en lugar de una actualizaci\u00f3n inal\u00e1mbrica para aprovechar el nuevo esquema de partici\u00f3n.", + "title": "Actualizar {name} con ESPHome {version} o posterior" } } } \ No newline at end of file diff --git a/homeassistant/components/esphome/translations/et.json b/homeassistant/components/esphome/translations/et.json index 07ec62741f9..4e7304ac45c 100644 --- a/homeassistant/components/esphome/translations/et.json +++ b/homeassistant/components/esphome/translations/et.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Seade on juba h\u00e4\u00e4lestatud", "already_in_progress": "Seadistamine on juba k\u00e4imas", + "mdns_missing_mac": "MDNS-i atribuutides puudub MAC-aadress.", "reauth_successful": "Taastuvastamine \u00f5nnestus" }, "error": { @@ -46,8 +47,8 @@ }, "issues": { "ble_firmware_outdated": { - "description": "Bluetoothi t\u00f6\u00f6kindluse ja j\u00f5udluse parandamiseks soovitame tungivalt v\u00e4rskendada {name} versiooniga ESPHome 2022.11.0 v\u00f5i uuema versiooniga.", - "title": "Uuenda {nimi} ESPHome 2022.11.0 v\u00f5i uuema versiooniga" + "description": "Bluetoothi t\u00f6\u00f6kindluse ja j\u00f5udluse parandamiseks soovitame tungivalt v\u00e4rskendada {name} ESPHome'i {version} v\u00f5i uuema versiooniga. Seadme v\u00e4rskendamisel versioonile ESPHome {version} on uue partitsiooniskeemi \u00e4rakasutamiseks soovitatav kasutada \u00f5hu kaudu toimuva v\u00e4rskenduse asemel jadakaablit.", + "title": "V\u00e4rskenda {name} ESPHome'i {version} v\u00f5i uuema versiooniga" } } } \ No newline at end of file diff --git a/homeassistant/components/esphome/translations/he.json b/homeassistant/components/esphome/translations/he.json index 11eaf41ff1a..7f4bea02a32 100644 --- a/homeassistant/components/esphome/translations/he.json +++ b/homeassistant/components/esphome/translations/he.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", + "mdns_missing_mac": "\u05db\u05ea\u05d5\u05d1\u05ea MAC \u05d7\u05e1\u05e8\u05d4 \u05d1\u05de\u05d0\u05e4\u05d9\u05d9\u05e0\u05d9 MDNS.", "reauth_successful": "\u05d4\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05d4\u05e6\u05dc\u05d9\u05d7" }, "error": { diff --git a/homeassistant/components/esphome/translations/hu.json b/homeassistant/components/esphome/translations/hu.json index 5fd2dfc2565..62b71d79c04 100644 --- a/homeassistant/components/esphome/translations/hu.json +++ b/homeassistant/components/esphome/translations/hu.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", + "mdns_missing_mac": "Hi\u00e1nyz\u00f3 MAC-c\u00edm az MDNS-tulajdons\u00e1gokban.", "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt." }, "error": { diff --git a/homeassistant/components/esphome/translations/id.json b/homeassistant/components/esphome/translations/id.json index 8a82425a924..d1b61034c4b 100644 --- a/homeassistant/components/esphome/translations/id.json +++ b/homeassistant/components/esphome/translations/id.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Perangkat sudah dikonfigurasi", "already_in_progress": "Alur konfigurasi sedang berlangsung", + "mdns_missing_mac": "Alamat MAC tidak ada dalam properti MDNS.", "reauth_successful": "Autentikasi ulang berhasil" }, "error": { @@ -46,8 +47,8 @@ }, "issues": { "ble_firmware_outdated": { - "description": "Untuk meningkatkan keandalan dan performa Bluetooth, kami sangat menyarankan untuk memperbarui {name} dengan ESPHome 2022.11.0 atau yang lebih baru.", - "title": "Perbarui {name} dengan ESPHome 2022.11.0 atau yang lebih baru" + "description": "Untuk meningkatkan keandalan dan performa Bluetooth, kami sangat menyarankan untuk memperbarui {name} dengan ESPHome {version} atau yang lebih baru. Saat memperbarui perangkat ke ESPHome {version}, disarankan untuk menggunakan kabel serial, bukan pembaruan OTA, untuk memanfaatkan skema partisi yang baru.", + "title": "Perbarui {name} dengan ESPHome {version} atau yang lebih baru" } } } \ No newline at end of file diff --git a/homeassistant/components/esphome/translations/it.json b/homeassistant/components/esphome/translations/it.json index 0f0b31f7094..627f172a136 100644 --- a/homeassistant/components/esphome/translations/it.json +++ b/homeassistant/components/esphome/translations/it.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato", "already_in_progress": "Il flusso di configurazione \u00e8 gi\u00e0 in corso", + "mdns_missing_mac": "Indirizzo MAC mancante nelle propriet\u00e0 MDNS.", "reauth_successful": "La nuova autenticazione \u00e8 stata eseguita correttamente" }, "error": { @@ -46,8 +47,8 @@ }, "issues": { "ble_firmware_outdated": { - "description": "Per migliorare l'affidabilit\u00e0 e le prestazioni Bluetooth, si consiglia vivamente di aggiornare {name} con ESPHome 2022.11.0 o versioni successive.", - "title": "Aggiorna {name} con ESPHome 2022.11.0 o versioni successive" + "description": "Per migliorare l'affidabilit\u00e0 e le prestazioni del Bluetooth, ti consigliamo vivamente di aggiornare {name} con ESPHome {version} o successiva. Quando si aggiorna il dispositivo a ESPHome {version}, si consiglia di utilizzare un cavo seriale invece di un aggiornamento via etere per sfruttare il nuovo schema di partizione.", + "title": "Aggiorna {name} con ESPHome {version} o successiva" } } } \ No newline at end of file diff --git a/homeassistant/components/esphome/translations/ko.json b/homeassistant/components/esphome/translations/ko.json index 3e98bdabeb5..2aa69e43aaf 100644 --- a/homeassistant/components/esphome/translations/ko.json +++ b/homeassistant/components/esphome/translations/ko.json @@ -2,7 +2,8 @@ "config": { "abort": { "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", - "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4" + "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" }, "error": { "connection_error": "ESP\uc5d0 \uc5f0\uacb0\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4. YAML \ud30c\uc77c\uc5d0 'api:'\ub97c \uad6c\uc131\ud588\ub294\uc9c0 \ud655\uc778\ud574\uc8fc\uc138\uc694.", @@ -29,5 +30,11 @@ "description": "[ESPHome](https://esphomelib.com/) \ub178\ub4dc\uc758 \uc5f0\uacb0 \uad6c\uc131\uc744 \uc785\ub825\ud574\uc8fc\uc138\uc694." } } + }, + "issues": { + "ble_firmware_outdated": { + "description": "Bluetooth \uc548\uc815\uc131 \ubc0f \uc131\ub2a5\uc744 \uac1c\uc120\ud558\ub824\uba74 ESPHome 2022.11.0 \uc774\uc0c1\uc73c\ub85c {name} \uc744(\ub97c) \uc5c5\ub370\uc774\ud2b8\ud558\ub294 \uac83\uc774 \uc88b\uc2b5\ub2c8\ub2e4.", + "title": "{name} \uc744 ESPHome 2022.11.0 \uc774\uc0c1\uc73c\ub85c \uc5c5\ub370\uc774\ud2b8\ud558\uae30" + } } } \ No newline at end of file diff --git a/homeassistant/components/esphome/translations/no.json b/homeassistant/components/esphome/translations/no.json index d24d557f49e..061855915ce 100644 --- a/homeassistant/components/esphome/translations/no.json +++ b/homeassistant/components/esphome/translations/no.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Enheten er allerede konfigurert", "already_in_progress": "Konfigurasjonsflyten p\u00e5g\u00e5r allerede", + "mdns_missing_mac": "Manglende MAC-adresse i MDNS-egenskaper.", "reauth_successful": "Re-autentisering var vellykket" }, "error": { @@ -46,8 +47,8 @@ }, "issues": { "ble_firmware_outdated": { - "description": "For \u00e5 forbedre Bluetooth-p\u00e5litelighet og ytelse anbefaler vi p\u00e5 det sterkeste \u00e5 oppdatere {name} med ESPHome 2022.11.0 eller nyere.", - "title": "Oppdater {name} med ESPHome 2022.11.0 eller nyere" + "description": "For \u00e5 forbedre Bluetooth-p\u00e5litelighet og ytelse anbefaler vi p\u00e5 det sterkeste \u00e5 oppdatere {name} med ESPHome {version} eller nyere. N\u00e5r du oppdaterer enheten til ESPHome {version} , anbefales det \u00e5 bruke en seriell kabel i stedet for en tr\u00e5dl\u00f8s oppdatering for \u00e5 dra nytte av det nye partisjonsskjemaet.", + "title": "Oppdater {name} med ESPHome {version} eller nyere" } } } \ No newline at end of file diff --git a/homeassistant/components/esphome/translations/pl.json b/homeassistant/components/esphome/translations/pl.json index ae9ae8c7b18..ab0f43c6969 100644 --- a/homeassistant/components/esphome/translations/pl.json +++ b/homeassistant/components/esphome/translations/pl.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane", "already_in_progress": "Konfiguracja jest ju\u017c w toku", + "mdns_missing_mac": "Brak adresu MAC we w\u0142a\u015bciwo\u015bciach MDNS.", "reauth_successful": "Ponowne uwierzytelnienie powiod\u0142o si\u0119" }, "error": { @@ -46,8 +47,8 @@ }, "issues": { "ble_firmware_outdated": { - "description": "Aby poprawi\u0107 niezawodno\u015b\u0107 i wydajno\u015b\u0107 Bluetooth, zdecydowanie zalecamy zaktualizowanie {name} do wersji ESPHome 2022.11.0 lub nowszej.", - "title": "Zaktualizuj {name} do wersji ESPHome 2022.11.0 lub nowszej" + "description": "Aby poprawi\u0107 niezawodno\u015b\u0107 i wydajno\u015b\u0107 Bluetooth, zdecydowanie zalecamy zaktualizowanie {name} do ESPHome {version} lub nowszej. Podczas aktualizacji urz\u0105dzenia do ESPHome {version} zaleca si\u0119 u\u017cycie kabla szeregowego zamiast aktualizacji bezprzewodowej, aby skorzysta\u0107 z nowego schematu partycji.", + "title": "Zaktualizuj {name} do wersji ESPHome {version} lub nowszej" } } } \ No newline at end of file diff --git a/homeassistant/components/esphome/translations/pt-BR.json b/homeassistant/components/esphome/translations/pt-BR.json index ae78ce6b88c..e0ad60b490b 100644 --- a/homeassistant/components/esphome/translations/pt-BR.json +++ b/homeassistant/components/esphome/translations/pt-BR.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Dispositivo j\u00e1 est\u00e1 configurado", "already_in_progress": "O fluxo de configura\u00e7\u00e3o j\u00e1 est\u00e1 em andamento", + "mdns_missing_mac": "Endere\u00e7o MAC ausente nas propriedades MDNS.", "reauth_successful": "A reautentica\u00e7\u00e3o foi bem-sucedida" }, "error": { @@ -46,8 +47,8 @@ }, "issues": { "ble_firmware_outdated": { - "description": "Para melhorar a confiabilidade e o desempenho do Bluetooth, recomendamos atualizar {name} com ESPHome 2022.11.0 ou posterior.", - "title": "Atualize {name} com ESPHome 2022.11.0 ou posterior" + "description": "Para melhorar a confiabilidade e o desempenho do Bluetooth, recomendamos atualizar {name} com ESPHome {version} ou posterior. Ao atualizar o dispositivo para ESPHome {version}, \u00e9 recomend\u00e1vel usar um cabo serial em vez de uma atualiza\u00e7\u00e3o over-the-air para aproveitar o novo esquema de parti\u00e7\u00e3o.", + "title": "Atualize {name} com ESPHome {version} ou posterior" } } } \ No newline at end of file diff --git a/homeassistant/components/esphome/translations/pt.json b/homeassistant/components/esphome/translations/pt.json index 5b5e85922d9..aca210dbdee 100644 --- a/homeassistant/components/esphome/translations/pt.json +++ b/homeassistant/components/esphome/translations/pt.json @@ -27,7 +27,7 @@ }, "user": { "data": { - "host": "Servidor", + "host": "Endere\u00e7o", "port": "Porta" }, "description": "Por favor, insira as configura\u00e7\u00f5es de liga\u00e7\u00e3o ao seu n\u00f3 [ESPHome] (https://esphomelib.com/)." diff --git a/homeassistant/components/esphome/translations/ru.json b/homeassistant/components/esphome/translations/ru.json index 7aa09cf7ff4..937857ac9a2 100644 --- a/homeassistant/components/esphome/translations/ru.json +++ b/homeassistant/components/esphome/translations/ru.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant.", "already_in_progress": "\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f.", + "mdns_missing_mac": "\u041e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442 MAC-\u0430\u0434\u0440\u0435\u0441 \u0432 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430\u0445 MDNS.", "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e." }, "error": { @@ -46,8 +47,8 @@ }, "issues": { "ble_firmware_outdated": { - "description": "\u0414\u043b\u044f \u043f\u043e\u0432\u044b\u0448\u0435\u043d\u0438\u044f \u043d\u0430\u0434\u0435\u0436\u043d\u043e\u0441\u0442\u0438 \u0438 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 Bluetooth \u043c\u044b \u043d\u0430\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u043c \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c {name} \u0434\u043e \u0432\u0435\u0440\u0441\u0438\u0438 ESPHome 2022.11.0 \u0438\u043b\u0438 \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0437\u0434\u043d\u0435\u0439.", - "title": "\u041e\u0431\u043d\u043e\u0432\u0438\u0442\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e ESPHome {name} \u0434\u043e \u0432\u0435\u0440\u0441\u0438\u0438 2022.11.0 \u0438\u043b\u0438 \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0437\u0434\u043d\u0435\u0439" + "description": "\u0414\u043b\u044f \u043f\u043e\u0432\u044b\u0448\u0435\u043d\u0438\u044f \u043d\u0430\u0434\u0435\u0436\u043d\u043e\u0441\u0442\u0438 \u0438 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 Bluetooth \u043c\u044b \u043d\u0430\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u043c \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e ESPHome {name} \u0434\u043e \u0432\u0435\u0440\u0441\u0438\u0438 {version} \u0438\u043b\u0438 \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0437\u0434\u043d\u0435\u0439. \u0412 \u0441\u0432\u044f\u0437\u0438 \u0441 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435\u043c \u0441\u0445\u0435\u043c\u044b \u0440\u0430\u0437\u0434\u0435\u043b\u043e\u0432, \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 UART.", + "title": "\u041e\u0431\u043d\u043e\u0432\u0438\u0442\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e ESPHome {name} \u0434\u043e \u0432\u0435\u0440\u0441\u0438\u0438 {version} \u0438\u043b\u0438 \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0437\u0434\u043d\u0435\u0439" } } } \ No newline at end of file diff --git a/homeassistant/components/esphome/translations/sk.json b/homeassistant/components/esphome/translations/sk.json index f87d3e7e940..e222676e018 100644 --- a/homeassistant/components/esphome/translations/sk.json +++ b/homeassistant/components/esphome/translations/sk.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "mdns_missing_mac": "Ch\u00fdba MAC adresa vo vlastnostiach MDNS.", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { @@ -46,8 +47,8 @@ }, "issues": { "ble_firmware_outdated": { - "description": "Ak chcete zlep\u0161i\u0165 spo\u013eahlivos\u0165 a v\u00fdkon Bluetooth, d\u00f4razne odpor\u00fa\u010dame aktualizova\u0165 {name} na ESPHome 2022.11.0 alebo nov\u0161\u00ed.", - "title": "Aktualizujte {name} pomocou ESPHome 2022.11.0 alebo nov\u0161ieho" + "description": "Ak chcete zlep\u0161i\u0165 spo\u013eahlivos\u0165 a v\u00fdkon Bluetooth, d\u00f4razne odpor\u00fa\u010dame aktualizova\u0165 {name} na ESPHome {version} alebo nov\u0161\u00ed. Pri aktualiz\u00e1cii zariadenia na ESPHome {version} sa odpor\u00fa\u010da pou\u017ei\u0165 namiesto bezdr\u00f4tovej aktualiz\u00e1cie s\u00e9riov\u00fd k\u00e1bel, aby ste mohli vyu\u017ei\u0165 nov\u00fa sch\u00e9mu oddielov.", + "title": "Aktualizujte {name} pomocou ESPHome {version} alebo nov\u0161ieho" } } } \ No newline at end of file diff --git a/homeassistant/components/esphome/translations/zh-Hant.json b/homeassistant/components/esphome/translations/zh-Hant.json index b08d5d97c34..4de09120be0 100644 --- a/homeassistant/components/esphome/translations/zh-Hant.json +++ b/homeassistant/components/esphome/translations/zh-Hant.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", "already_in_progress": "\u8a2d\u5b9a\u5df2\u7d93\u9032\u884c\u4e2d", + "mdns_missing_mac": "MDNS \u5c6c\u6027\u4e2d\u7f3a\u5c11 MAC \u4f4d\u5740\u3002", "reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f" }, "error": { @@ -46,8 +47,8 @@ }, "issues": { "ble_firmware_outdated": { - "description": "\u6b32\u6539\u5584\u85cd\u82bd\u53ef\u9760\u6027\u8207\u6548\u80fd\uff0c\u5f37\u70c8\u5efa\u8b70\u60a8\u66f4\u65b0\u81f3 2022.11.0 \u7248\u6216\u66f4\u65b0\u7248\u672c ESPHome \u4e4b {name}\u3002", - "title": "\u66f4\u65b0\u81f3 2022.11.0 \u7248\u6216\u66f4\u65b0\u7248\u672c ESPHome \u4e4b {name} " + "description": "\u6b32\u6539\u5584\u85cd\u82bd\u53ef\u9760\u6027\u8207\u6548\u80fd\uff0c\u5f37\u70c8\u5efa\u8b70\u60a8\u66f4\u65b0\u81f3{version} \u7248\u6216\u66f4\u65b0\u7248\u672c ESPHome \u4e4b {name}\u3002\u7576\u9032\u884c\u66f4\u65b0\u81f3 ESPHome {version} \u7248\u6642\uff0c\u5efa\u8b70\u4f7f\u7528\u5e8f\u5217\u7dda\u3001\u800c\u975e\u4f7f\u7528\u7121\u7dda\u7db2\u8def\u4ee5\u4f7f\u7528\u65b0\u5206\u5340\u7684\u65b0\u529f\u80fd\u3002", + "title": "\u66f4\u65b0\u81f3 {version} \u7248\u6216\u66f4\u65b0\u7248\u672c ESPHome \u4e4b {name}" } } } \ No newline at end of file diff --git a/homeassistant/components/evil_genius_labs/translations/ko.json b/homeassistant/components/evil_genius_labs/translations/ko.json new file mode 100644 index 00000000000..7c7a326d1b3 --- /dev/null +++ b/homeassistant/components/evil_genius_labs/translations/ko.json @@ -0,0 +1,16 @@ +{ + "config": { + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "timeout": "\uc5f0\uacb0 \uc124\uc815 \uc2dc\uac04 \ucd08\uacfc", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/evil_genius_labs/translations/pt.json b/homeassistant/components/evil_genius_labs/translations/pt.json index bf245f20e6e..39596180690 100644 --- a/homeassistant/components/evil_genius_labs/translations/pt.json +++ b/homeassistant/components/evil_genius_labs/translations/pt.json @@ -1,13 +1,13 @@ { "config": { "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", - "timeout": "Tempo limite para estabelecer liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou", + "timeout": "Excedido o tempo limite para estabelecer liga\u00e7\u00e3o" }, "step": { "user": { "data": { - "host": "Servidor" + "host": "Endere\u00e7o" } } } diff --git a/homeassistant/components/evohome/__init__.py b/homeassistant/components/evohome/__init__.py index 9c46ff43032..162a56e837a 100644 --- a/homeassistant/components/evohome/__init__.py +++ b/homeassistant/components/evohome/__init__.py @@ -146,19 +146,23 @@ def _handle_exception(err) -> None: except evohomeasync2.AuthenticationError: _LOGGER.error( - "Failed to authenticate with the vendor's server. " - "Check your username and password. NB: Some special password characters " - "that work correctly via the website will not work via the web API. " - "Message is: %s", + ( + "Failed to authenticate with the vendor's server. Check your username" + " and password. NB: Some special password characters that work" + " correctly via the website will not work via the web API. Message" + " is: %s" + ), err, ) except aiohttp.ClientConnectionError: # this appears to be a common occurrence with the vendor's servers _LOGGER.warning( - "Unable to connect with the vendor's server. " - "Check your network and the vendor's service status page. " - "Message is: %s", + ( + "Unable to connect with the vendor's server. " + "Check your network and the vendor's service status page. " + "Message is: %s" + ), err, ) @@ -171,8 +175,10 @@ def _handle_exception(err) -> None: elif err.status == HTTPStatus.TOO_MANY_REQUESTS: _LOGGER.warning( - "The vendor's API rate limit has been exceeded. " - "If this message persists, consider increasing the %s", + ( + "The vendor's API rate limit has been exceeded. " + "If this message persists, consider increasing the %s" + ), CONF_SCAN_INTERVAL, ) @@ -188,7 +194,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: tokens = dict(app_storage or {}) if tokens.pop(CONF_USERNAME, None) != config[DOMAIN][CONF_USERNAME]: - # any tokens won't be valid, and store might be be corrupt + # any tokens won't be valid, and store might be corrupt await store.async_save({}) return ({}, None) @@ -224,8 +230,10 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: loc_config = client_v2.installation_info[loc_idx] except IndexError: _LOGGER.error( - "Config error: '%s' = %s, but the valid range is 0-%s. " - "Unable to continue. Fix any configuration errors and restart HA", + ( + "Config error: '%s' = %s, but the valid range is 0-%s. " + "Unable to continue. Fix any configuration errors and restart HA" + ), CONF_LOCATION_IDX, loc_idx, len(client_v2.installation_info) - 1, @@ -469,10 +477,12 @@ class EvoBroker: except aiohttp.ClientError as err: _LOGGER.warning( - "Unable to obtain the latest high-precision temperatures. " - "Check your network and the vendor's service status page. " - "Proceeding with low-precision temperatures. " - "Message is: %s", + ( + "Unable to obtain the latest high-precision temperatures. " + "Check your network and the vendor's service status page. " + "Proceeding with low-precision temperatures. " + "Message is: %s" + ), err, ) self.temps = None # these are now stale, will fall back to v2 temps diff --git a/homeassistant/components/evohome/climate.py b/homeassistant/components/evohome/climate.py index 4594268623c..d4ee8f3d5df 100644 --- a/homeassistant/components/evohome/climate.py +++ b/homeassistant/components/evohome/climate.py @@ -14,7 +14,7 @@ from homeassistant.components.climate import ( ClimateEntityFeature, HVACMode, ) -from homeassistant.const import PRECISION_TENTHS, TEMP_CELSIUS +from homeassistant.const import PRECISION_TENTHS, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -112,8 +112,10 @@ async def async_setup_platform( else: _LOGGER.warning( - "Ignoring: %s (%s), id=%s, name=%s: unknown/invalid zone type, " - "report as an issue if you feel this zone type should be supported", + ( + "Ignoring: %s (%s), id=%s, name=%s: unknown/invalid zone type, " + "report as an issue if you feel this zone type should be supported" + ), zone.zoneType, zone.modelType, zone.zoneId, @@ -126,7 +128,7 @@ async def async_setup_platform( class EvoClimateEntity(EvoDevice, ClimateEntity): """Base for an evohome Climate device.""" - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS @property def hvac_modes(self) -> list[str]: diff --git a/homeassistant/components/evohome/water_heater.py b/homeassistant/components/evohome/water_heater.py index 86bbfc7d017..87c0a8a1ecd 100644 --- a/homeassistant/components/evohome/water_heater.py +++ b/homeassistant/components/evohome/water_heater.py @@ -12,7 +12,7 @@ from homeassistant.const import ( PRECISION_WHOLE, STATE_OFF, STATE_ON, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -60,7 +60,7 @@ class EvoDHW(EvoChild, WaterHeaterEntity): _attr_name = "DHW controller" _attr_icon = "mdi:thermometer-lines" _attr_operation_list = list(HA_STATE_TO_EVO) - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS def __init__(self, evo_broker, evo_device) -> None: """Initialize an evohome DHW controller.""" diff --git a/homeassistant/components/ezviz/camera.py b/homeassistant/components/ezviz/camera.py index 1f9de4f611c..9fb7c1f8b5a 100644 --- a/homeassistant/components/ezviz/camera.py +++ b/homeassistant/components/ezviz/camera.py @@ -108,7 +108,10 @@ async def async_setup_entry( ) _LOGGER.warning( - "Found camera with serial %s without configuration. Please go to integration to complete setup", + ( + "Found camera with serial %s without configuration. Please go to" + " integration to complete setup" + ), camera, ) diff --git a/homeassistant/components/ezviz/translations/sk.json b/homeassistant/components/ezviz/translations/sk.json index 15dbad718a4..551b98e3ff5 100644 --- a/homeassistant/components/ezviz/translations/sk.json +++ b/homeassistant/components/ezviz/translations/sk.json @@ -2,6 +2,7 @@ "config": { "abort": { "already_configured_account": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", + "ezviz_cloud_account_missing": "Ch\u00fdba cloudov\u00fd \u00fa\u010det EZVIZ. Prekonfigurujte cloudov\u00fd \u00fa\u010det EZVIZ", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "error": { @@ -15,7 +16,9 @@ "data": { "password": "Heslo", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" - } + }, + "description": "Zadajte prihlasovacie \u00fadaje RTSP pre kameru EZVIZ {serial} s IP {ip_address}", + "title": "Objaven\u00e1 kamera EZVIZ" }, "user": { "data": { @@ -35,5 +38,15 @@ "title": "Pripojenie k vlastnej adrese URL EZVIZ" } } + }, + "options": { + "step": { + "init": { + "data": { + "ffmpeg_arguments": "Argumenty odovzdan\u00e9 ffmpeg pre kamery", + "timeout": "\u010casov\u00fd limit po\u017eiadavky (sekundy)" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/faa_delays/translations/pt.json b/homeassistant/components/faa_delays/translations/pt.json index 49cb628dd85..f0ec08d0e4e 100644 --- a/homeassistant/components/faa_delays/translations/pt.json +++ b/homeassistant/components/faa_delays/translations/pt.json @@ -1,7 +1,7 @@ { "config": { "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "unknown": "Erro inesperado" } } diff --git a/homeassistant/components/faa_delays/translations/sk.json b/homeassistant/components/faa_delays/translations/sk.json index bbea900554a..7b2234be889 100644 --- a/homeassistant/components/faa_delays/translations/sk.json +++ b/homeassistant/components/faa_delays/translations/sk.json @@ -1,9 +1,21 @@ { "config": { + "abort": { + "already_configured": "Airport je u\u017e nakonfigurovan\u00fd." + }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_airport": "K\u00f3d letiska je neplatn\u00fd", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "step": { + "user": { + "data": { + "id": "Airport" + }, + "description": "Zadajte k\u00f3d letiska v USA vo form\u00e1te IATA", + "title": "Oneskorenia FAA" + } } } } \ No newline at end of file diff --git a/homeassistant/components/fan/__init__.py b/homeassistant/components/fan/__init__.py index e143b93258e..6aa29d8b804 100644 --- a/homeassistant/components/fan/__init__.py +++ b/homeassistant/components/fan/__init__.py @@ -244,7 +244,8 @@ class FanEntity(ToggleEntity): preset_modes = self.preset_modes if not preset_modes or preset_mode not in preset_modes: raise NotValidPresetModeError( - f"The preset_mode {preset_mode} is not a valid preset_mode: {preset_modes}" + f"The preset_mode {preset_mode} is not a valid preset_mode:" + f" {preset_modes}" ) def set_direction(self, direction: str) -> None: diff --git a/homeassistant/components/fan/translations/sk.json b/homeassistant/components/fan/translations/sk.json index 9251981601d..4169880d38a 100644 --- a/homeassistant/components/fan/translations/sk.json +++ b/homeassistant/components/fan/translations/sk.json @@ -1,6 +1,7 @@ { "device_automation": { "action_type": { + "toggle": "Prepn\u00fa\u0165 {entity_name}", "turn_off": "Vypn\u00fa\u0165 {entity_name}", "turn_on": "Zapn\u00fa\u0165 {entity_name}" }, diff --git a/homeassistant/components/fastdotcom/sensor.py b/homeassistant/components/fastdotcom/sensor.py index 8363981b526..1df431d0bf3 100644 --- a/homeassistant/components/fastdotcom/sensor.py +++ b/homeassistant/components/fastdotcom/sensor.py @@ -3,8 +3,8 @@ from __future__ import annotations from typing import Any -from homeassistant.components.sensor import SensorEntity -from homeassistant.const import DATA_RATE_MEGABITS_PER_SECOND +from homeassistant.components.sensor import SensorDeviceClass, SensorEntity +from homeassistant.const import UnitOfDataRate from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -13,8 +13,6 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from . import DATA_UPDATED, DOMAIN as FASTDOTCOM_DOMAIN -ICON = "mdi:speedometer" - async def async_setup_platform( hass: HomeAssistant, @@ -30,8 +28,9 @@ class SpeedtestSensor(RestoreEntity, SensorEntity): """Implementation of a FAst.com sensor.""" _attr_name = "Fast.com Download" - _attr_native_unit_of_measurement = DATA_RATE_MEGABITS_PER_SECOND - _attr_icon = ICON + _attr_device_class = SensorDeviceClass.DATA_RATE + _attr_native_unit_of_measurement = UnitOfDataRate.MEGABITS_PER_SECOND + _attr_icon = "mdi:speedometer" _attr_should_poll = False _attr_native_value = None diff --git a/homeassistant/components/fibaro/climate.py b/homeassistant/components/fibaro/climate.py index 692a0781797..43bd99520e1 100644 --- a/homeassistant/components/fibaro/climate.py +++ b/homeassistant/components/fibaro/climate.py @@ -15,12 +15,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - ATTR_TEMPERATURE, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, - Platform, -) +from homeassistant.const import ATTR_TEMPERATURE, Platform, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -176,9 +171,9 @@ class FibaroThermostat(FibaroDevice, ClimateEntity): self._attr_supported_features |= ClimateEntityFeature.FAN_MODE if tempunit == "F": - self._attr_temperature_unit = TEMP_FAHRENHEIT + self._attr_temperature_unit = UnitOfTemperature.FAHRENHEIT else: - self._attr_temperature_unit = TEMP_CELSIUS + self._attr_temperature_unit = UnitOfTemperature.CELSIUS if self._fan_mode_device: fan_modes = ( @@ -222,11 +217,13 @@ class FibaroThermostat(FibaroDevice, ClimateEntity): async def async_added_to_hass(self) -> None: """Call when entity is added to hass.""" _LOGGER.debug( - "Climate %s\n" - "- _temp_sensor_device %s\n" - "- _target_temp_device %s\n" - "- _op_mode_device %s\n" - "- _fan_mode_device %s", + ( + "Climate %s\n" + "- _temp_sensor_device %s\n" + "- _target_temp_device %s\n" + "- _op_mode_device %s\n" + "- _fan_mode_device %s" + ), self.ha_id, self._temp_sensor_device.ha_id if self._temp_sensor_device else "None", self._target_temp_device.ha_id if self._target_temp_device else "None", diff --git a/homeassistant/components/fibaro/translations/de.json b/homeassistant/components/fibaro/translations/de.json index e16bd4a56a7..ac47c8f961b 100644 --- a/homeassistant/components/fibaro/translations/de.json +++ b/homeassistant/components/fibaro/translations/de.json @@ -19,7 +19,7 @@ }, "user": { "data": { - "import_plugins": "Entit\u00e4ten aus Fibaro-Plugins importieren?", + "import_plugins": "Entit\u00e4ten aus Fibaro Plugins importieren?", "password": "Passwort", "url": "URL im Format http://HOST/api/", "username": "Benutzername" diff --git a/homeassistant/components/fibaro/translations/ko.json b/homeassistant/components/fibaro/translations/ko.json new file mode 100644 index 00000000000..2b39c5cc04e --- /dev/null +++ b/homeassistant/components/fibaro/translations/ko.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638" + }, + "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" + }, + "user": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/fibaro/translations/pt.json b/homeassistant/components/fibaro/translations/pt.json index f98b7b7b7b3..28e5a85e3d7 100644 --- a/homeassistant/components/fibaro/translations/pt.json +++ b/homeassistant/components/fibaro/translations/pt.json @@ -1,14 +1,19 @@ { "config": { "abort": { - "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", + "reauth_successful": "Reautentica\u00e7\u00e3o bem sucedida" }, "error": { "cannot_connect": "Falha na liga\u00e7\u00e3o" }, "step": { "reauth_confirm": { - "description": "Atualize sua senha para {username}" + "data": { + "password": "Palavra-passe" + }, + "description": "Atualize sua senha para {username}", + "title": "Reautenticar integra\u00e7\u00e3o" } } } diff --git a/homeassistant/components/fibaro/translations/sk.json b/homeassistant/components/fibaro/translations/sk.json index 33e7cce96e0..605acc06563 100644 --- a/homeassistant/components/fibaro/translations/sk.json +++ b/homeassistant/components/fibaro/translations/sk.json @@ -19,6 +19,7 @@ }, "user": { "data": { + "import_plugins": "Importova\u0165 entity z doplnkov Fibaro?", "password": "Heslo", "url": "URL vo form\u00e1te http://HOST/api/", "username": "U\u017e\u00edvate\u013esk\u00e9 meno" diff --git a/homeassistant/components/fido/sensor.py b/homeassistant/components/fido/sensor.py index 3eeb4dd0dc8..e0e6e287508 100644 --- a/homeassistant/components/fido/sensor.py +++ b/homeassistant/components/fido/sensor.py @@ -15,6 +15,7 @@ import voluptuous as vol from homeassistant.components.sensor import ( PLATFORM_SCHEMA, + SensorDeviceClass, SensorEntity, SensorEntityDescription, ) @@ -23,8 +24,8 @@ from homeassistant.const import ( CONF_NAME, CONF_PASSWORD, CONF_USERNAME, - DATA_KILOBITS, - TIME_MINUTES, + UnitOfInformation, + UnitOfTime, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.aiohttp_client import async_get_clientsession @@ -59,19 +60,22 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="data_used", name="Data used", - native_unit_of_measurement=DATA_KILOBITS, + native_unit_of_measurement=UnitOfInformation.KILOBITS, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:download", ), SensorEntityDescription( key="data_limit", name="Data limit", - native_unit_of_measurement=DATA_KILOBITS, + native_unit_of_measurement=UnitOfInformation.KILOBITS, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:download", ), SensorEntityDescription( key="data_remaining", name="Data remaining", - native_unit_of_measurement=DATA_KILOBITS, + native_unit_of_measurement=UnitOfInformation.KILOBITS, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:download", ), SensorEntityDescription( @@ -131,37 +135,37 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="talk_used", name="Talk used", - native_unit_of_measurement=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, icon="mdi:cellphone", ), SensorEntityDescription( key="talk_limit", name="Talk limit", - native_unit_of_measurement=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, icon="mdi:cellphone", ), SensorEntityDescription( key="talk_remaining", name="Talk remaining", - native_unit_of_measurement=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, icon="mdi:cellphone", ), SensorEntityDescription( key="other_talk_used", name="Other Talk used", - native_unit_of_measurement=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, icon="mdi:cellphone", ), SensorEntityDescription( key="other_talk_limit", name="Other Talk limit", - native_unit_of_measurement=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, icon="mdi:cellphone", ), SensorEntityDescription( key="other_talk_remaining", name="Other Talk remaining", - native_unit_of_measurement=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, icon="mdi:cellphone", ), ) diff --git a/homeassistant/components/file/notify.py b/homeassistant/components/file/notify.py index eb9a512b230..4a0b4c11ca6 100644 --- a/homeassistant/components/file/notify.py +++ b/homeassistant/components/file/notify.py @@ -57,7 +57,10 @@ class FileNotificationService(BaseNotificationService): filepath: str = os.path.join(self.hass.config.config_dir, self.filename) with open(filepath, "a", encoding="utf8") as file: if os.stat(filepath).st_size == 0: - title = f"{kwargs.get(ATTR_TITLE, ATTR_TITLE_DEFAULT)} notifications (Log started: {dt_util.utcnow().isoformat()})\n{'-' * 80}\n" + title = ( + f"{kwargs.get(ATTR_TITLE, ATTR_TITLE_DEFAULT)} notifications (Log" + f" started: {dt_util.utcnow().isoformat()})\n{'-' * 80}\n" + ) file.write(title) if self.add_timestamp: diff --git a/homeassistant/components/filesize/sensor.py b/homeassistant/components/filesize/sensor.py index 06e567f14cb..fb5b498e106 100644 --- a/homeassistant/components/filesize/sensor.py +++ b/homeassistant/components/filesize/sensor.py @@ -13,7 +13,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_FILE_PATH, DATA_BYTES, DATA_MEGABYTES +from homeassistant.const import CONF_FILE_PATH, UnitOfInformation from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceEntryType from homeassistant.helpers.entity import DeviceInfo, EntityCategory @@ -36,7 +36,8 @@ SENSOR_TYPES = ( key="file", icon=ICON, name="Size", - native_unit_of_measurement=DATA_MEGABYTES, + native_unit_of_measurement=UnitOfInformation.MEGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( @@ -44,7 +45,8 @@ SENSOR_TYPES = ( entity_registry_enabled_default=False, icon=ICON, name="Size bytes", - native_unit_of_measurement=DATA_BYTES, + native_unit_of_measurement=UnitOfInformation.BYTES, + device_class=SensorDeviceClass.DATA_SIZE, state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, ), diff --git a/homeassistant/components/filesize/translations/ko.json b/homeassistant/components/filesize/translations/ko.json new file mode 100644 index 00000000000..e1300423811 --- /dev/null +++ b/homeassistant/components/filesize/translations/ko.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "\uc11c\ube44\uc2a4\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/fireservicerota/translations/ko.json b/homeassistant/components/fireservicerota/translations/ko.json index 6326713da73..d8da07f3b00 100644 --- a/homeassistant/components/fireservicerota/translations/ko.json +++ b/homeassistant/components/fireservicerota/translations/ko.json @@ -11,6 +11,11 @@ "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" }, "step": { + "reauth_confirm": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638" + } + }, "user": { "data": { "password": "\ube44\ubc00\ubc88\ud638", diff --git a/homeassistant/components/fireservicerota/translations/nl.json b/homeassistant/components/fireservicerota/translations/nl.json index 3a7179a7c1f..ff33f7d35b9 100644 --- a/homeassistant/components/fireservicerota/translations/nl.json +++ b/homeassistant/components/fireservicerota/translations/nl.json @@ -11,6 +11,11 @@ "invalid_auth": "Ongeldige authenticatie" }, "step": { + "reauth_confirm": { + "data": { + "password": "Wachtwoord" + } + }, "user": { "data": { "password": "Wachtwoord", diff --git a/homeassistant/components/fireservicerota/translations/pt.json b/homeassistant/components/fireservicerota/translations/pt.json index 7c8a7dfb9d4..525199e12b0 100644 --- a/homeassistant/components/fireservicerota/translations/pt.json +++ b/homeassistant/components/fireservicerota/translations/pt.json @@ -11,6 +11,11 @@ "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida" }, "step": { + "reauth_confirm": { + "data": { + "password": "Palavra-passe" + } + }, "user": { "data": { "password": "Palavra-passe", diff --git a/homeassistant/components/firmata/board.py b/homeassistant/components/firmata/board.py index 002775edb0c..8b5c76e2582 100644 --- a/homeassistant/components/firmata/board.py +++ b/homeassistant/components/firmata/board.py @@ -92,8 +92,7 @@ class FirmataBoard: ) except RuntimeError as err: _LOGGER.error( - "Error setting sampling interval for PyMata \ -board %s: %s", + "Error setting sampling interval for PyMata board %s: %s", self.name, err, ) diff --git a/homeassistant/components/firmata/translations/pt.json b/homeassistant/components/firmata/translations/pt.json index 32a5bb07569..de57c562b8e 100644 --- a/homeassistant/components/firmata/translations/pt.json +++ b/homeassistant/components/firmata/translations/pt.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "one": "Um", diff --git a/homeassistant/components/firmata/translations/sk.json b/homeassistant/components/firmata/translations/sk.json index 641bd9c13ee..7c74bf99bc8 100644 --- a/homeassistant/components/firmata/translations/sk.json +++ b/homeassistant/components/firmata/translations/sk.json @@ -2,6 +2,12 @@ "config": { "abort": { "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, + "step": { + "few": "Pr\u00e1zdnych", + "many": "Pr\u00e1zdnych", + "one": "Pr\u00e1zdny", + "other": "Pr\u00e1zdny" } } } \ No newline at end of file diff --git a/homeassistant/components/fitbit/const.py b/homeassistant/components/fitbit/const.py index 26db1f91659..d746e63ca52 100644 --- a/homeassistant/components/fitbit/const.py +++ b/homeassistant/components/fitbit/const.py @@ -4,16 +4,19 @@ from __future__ import annotations from dataclasses import dataclass from typing import Final -from homeassistant.components.sensor import SensorEntityDescription, SensorStateClass +from homeassistant.components.sensor import ( + SensorDeviceClass, + SensorEntityDescription, + SensorStateClass, +) from homeassistant.const import ( CONF_CLIENT_ID, CONF_CLIENT_SECRET, - LENGTH_FEET, - MASS_KILOGRAMS, - MASS_MILLIGRAMS, PERCENTAGE, - TIME_MILLISECONDS, - TIME_MINUTES, + UnitOfLength, + UnitOfMass, + UnitOfTime, + UnitOfVolume, ) ATTR_ACCESS_TOKEN: Final = "access_token" @@ -47,226 +50,237 @@ DEFAULT_CLOCK_FORMAT: Final = "24H" @dataclass -class FitbitRequiredKeysMixin: - """Mixin for required keys.""" - - unit_type: str | None - - -@dataclass -class FitbitSensorEntityDescription(SensorEntityDescription, FitbitRequiredKeysMixin): +class FitbitSensorEntityDescription(SensorEntityDescription): """Describes Fitbit sensor entity.""" + unit_type: str | None = None + FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = ( FitbitSensorEntityDescription( key="activities/activityCalories", name="Activity Calories", - unit_type="cal", + native_unit_of_measurement="cal", icon="mdi:fire", ), FitbitSensorEntityDescription( key="activities/calories", name="Calories", - unit_type="cal", + native_unit_of_measurement="cal", icon="mdi:fire", ), FitbitSensorEntityDescription( key="activities/caloriesBMR", name="Calories BMR", - unit_type="cal", + native_unit_of_measurement="cal", icon="mdi:fire", ), FitbitSensorEntityDescription( key="activities/distance", name="Distance", - unit_type="", + unit_type="distance", icon="mdi:map-marker", + device_class=SensorDeviceClass.DISTANCE, ), FitbitSensorEntityDescription( key="activities/elevation", name="Elevation", - unit_type="", + unit_type="elevation", icon="mdi:walk", + device_class=SensorDeviceClass.DISTANCE, ), FitbitSensorEntityDescription( key="activities/floors", name="Floors", - unit_type="floors", + native_unit_of_measurement="floors", icon="mdi:walk", ), FitbitSensorEntityDescription( key="activities/heart", name="Resting Heart Rate", - unit_type="bpm", + native_unit_of_measurement="bpm", icon="mdi:heart-pulse", ), FitbitSensorEntityDescription( key="activities/minutesFairlyActive", name="Minutes Fairly Active", - unit_type=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, icon="mdi:walk", + device_class=SensorDeviceClass.DURATION, ), FitbitSensorEntityDescription( key="activities/minutesLightlyActive", name="Minutes Lightly Active", - unit_type=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, icon="mdi:walk", + device_class=SensorDeviceClass.DURATION, ), FitbitSensorEntityDescription( key="activities/minutesSedentary", name="Minutes Sedentary", - unit_type=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, icon="mdi:seat-recline-normal", + device_class=SensorDeviceClass.DURATION, ), FitbitSensorEntityDescription( key="activities/minutesVeryActive", name="Minutes Very Active", - unit_type=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, icon="mdi:run", + device_class=SensorDeviceClass.DURATION, ), FitbitSensorEntityDescription( key="activities/steps", name="Steps", - unit_type="steps", + native_unit_of_measurement="steps", icon="mdi:walk", ), FitbitSensorEntityDescription( key="activities/tracker/activityCalories", name="Tracker Activity Calories", - unit_type="cal", + native_unit_of_measurement="cal", icon="mdi:fire", ), FitbitSensorEntityDescription( key="activities/tracker/calories", name="Tracker Calories", - unit_type="cal", + native_unit_of_measurement="cal", icon="mdi:fire", ), FitbitSensorEntityDescription( key="activities/tracker/distance", name="Tracker Distance", - unit_type="", + unit_type="distance", icon="mdi:map-marker", + device_class=SensorDeviceClass.DISTANCE, ), FitbitSensorEntityDescription( key="activities/tracker/elevation", name="Tracker Elevation", - unit_type="", + unit_type="elevation", icon="mdi:walk", + device_class=SensorDeviceClass.DISTANCE, ), FitbitSensorEntityDescription( key="activities/tracker/floors", name="Tracker Floors", - unit_type="floors", + native_unit_of_measurement="floors", icon="mdi:walk", ), FitbitSensorEntityDescription( key="activities/tracker/minutesFairlyActive", name="Tracker Minutes Fairly Active", - unit_type=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, icon="mdi:walk", + device_class=SensorDeviceClass.DURATION, ), FitbitSensorEntityDescription( key="activities/tracker/minutesLightlyActive", name="Tracker Minutes Lightly Active", - unit_type=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, icon="mdi:walk", + device_class=SensorDeviceClass.DURATION, ), FitbitSensorEntityDescription( key="activities/tracker/minutesSedentary", name="Tracker Minutes Sedentary", - unit_type=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, icon="mdi:seat-recline-normal", + device_class=SensorDeviceClass.DURATION, ), FitbitSensorEntityDescription( key="activities/tracker/minutesVeryActive", name="Tracker Minutes Very Active", - unit_type=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, icon="mdi:run", + device_class=SensorDeviceClass.DURATION, ), FitbitSensorEntityDescription( key="activities/tracker/steps", name="Tracker Steps", - unit_type="steps", + native_unit_of_measurement="steps", icon="mdi:walk", ), FitbitSensorEntityDescription( key="body/bmi", name="BMI", - unit_type="BMI", + native_unit_of_measurement="BMI", icon="mdi:human", state_class=SensorStateClass.MEASUREMENT, ), FitbitSensorEntityDescription( key="body/fat", name="Body Fat", - unit_type=PERCENTAGE, + native_unit_of_measurement=PERCENTAGE, icon="mdi:human", state_class=SensorStateClass.MEASUREMENT, ), FitbitSensorEntityDescription( key="body/weight", name="Weight", - unit_type="", + unit_type="weight", icon="mdi:human", state_class=SensorStateClass.MEASUREMENT, + device_class=SensorDeviceClass.WEIGHT, ), FitbitSensorEntityDescription( key="sleep/awakeningsCount", name="Awakenings Count", - unit_type="times awaken", + native_unit_of_measurement="times awaken", icon="mdi:sleep", ), FitbitSensorEntityDescription( key="sleep/efficiency", name="Sleep Efficiency", - unit_type=PERCENTAGE, + native_unit_of_measurement=PERCENTAGE, icon="mdi:sleep", state_class=SensorStateClass.MEASUREMENT, ), FitbitSensorEntityDescription( key="sleep/minutesAfterWakeup", name="Minutes After Wakeup", - unit_type=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, icon="mdi:sleep", + device_class=SensorDeviceClass.DURATION, ), FitbitSensorEntityDescription( key="sleep/minutesAsleep", name="Sleep Minutes Asleep", - unit_type=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, icon="mdi:sleep", + device_class=SensorDeviceClass.DURATION, ), FitbitSensorEntityDescription( key="sleep/minutesAwake", name="Sleep Minutes Awake", - unit_type=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, icon="mdi:sleep", + device_class=SensorDeviceClass.DURATION, ), FitbitSensorEntityDescription( key="sleep/minutesToFallAsleep", name="Sleep Minutes to Fall Asleep", - unit_type=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, icon="mdi:sleep", + device_class=SensorDeviceClass.DURATION, ), FitbitSensorEntityDescription( key="sleep/startTime", name="Sleep Start Time", - unit_type=None, icon="mdi:clock", ), FitbitSensorEntityDescription( key="sleep/timeInBed", name="Sleep Time in Bed", - unit_type=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, icon="mdi:hotel", + device_class=SensorDeviceClass.DURATION, ), ) FITBIT_RESOURCE_BATTERY = FitbitSensorEntityDescription( key="devices/battery", name="Battery", - unit_type=None, icon="mdi:battery", ) @@ -276,35 +290,35 @@ FITBIT_RESOURCES_KEYS: Final[list[str]] = [ FITBIT_MEASUREMENTS: Final[dict[str, dict[str, str]]] = { "en_US": { - ATTR_DURATION: TIME_MILLISECONDS, - ATTR_DISTANCE: "mi", - ATTR_ELEVATION: LENGTH_FEET, - ATTR_HEIGHT: "in", - ATTR_WEIGHT: "lbs", - ATTR_BODY: "in", - ATTR_LIQUIDS: "fl. oz.", - ATTR_BLOOD_GLUCOSE: f"{MASS_MILLIGRAMS}/dL", + ATTR_DURATION: UnitOfTime.MILLISECONDS, + ATTR_DISTANCE: UnitOfLength.MILES, + ATTR_ELEVATION: UnitOfLength.FEET, + ATTR_HEIGHT: UnitOfLength.INCHES, + ATTR_WEIGHT: UnitOfMass.POUNDS, + ATTR_BODY: UnitOfLength.INCHES, + ATTR_LIQUIDS: UnitOfVolume.FLUID_OUNCES, + ATTR_BLOOD_GLUCOSE: f"{UnitOfMass.MILLIGRAMS}/dL", ATTR_BATTERY: "", }, "en_GB": { - ATTR_DURATION: TIME_MILLISECONDS, - ATTR_DISTANCE: "kilometers", - ATTR_ELEVATION: "meters", - ATTR_HEIGHT: "centimeters", - ATTR_WEIGHT: "stone", - ATTR_BODY: "centimeters", - ATTR_LIQUIDS: "milliliters", + ATTR_DURATION: UnitOfTime.MILLISECONDS, + ATTR_DISTANCE: UnitOfLength.KILOMETERS, + ATTR_ELEVATION: UnitOfLength.METERS, + ATTR_HEIGHT: UnitOfLength.CENTIMETERS, + ATTR_WEIGHT: UnitOfMass.STONES, + ATTR_BODY: UnitOfLength.CENTIMETERS, + ATTR_LIQUIDS: UnitOfVolume.MILLILITERS, ATTR_BLOOD_GLUCOSE: "mmol/L", ATTR_BATTERY: "", }, "metric": { - ATTR_DURATION: TIME_MILLISECONDS, - ATTR_DISTANCE: "kilometers", - ATTR_ELEVATION: "meters", - ATTR_HEIGHT: "centimeters", - ATTR_WEIGHT: MASS_KILOGRAMS, - ATTR_BODY: "centimeters", - ATTR_LIQUIDS: "milliliters", + ATTR_DURATION: UnitOfTime.MILLISECONDS, + ATTR_DISTANCE: UnitOfLength.KILOMETERS, + ATTR_ELEVATION: UnitOfLength.METERS, + ATTR_HEIGHT: UnitOfLength.CENTIMETERS, + ATTR_WEIGHT: UnitOfMass.KILOGRAMS, + ATTR_BODY: UnitOfLength.CENTIMETERS, + ATTR_LIQUIDS: UnitOfVolume.MILLILITERS, ATTR_BLOOD_GLUCOSE: "mmol/L", ATTR_BATTERY: "", }, diff --git a/homeassistant/components/fitbit/sensor.py b/homeassistant/components/fitbit/sensor.py index 78929307c02..0f3f4c22ff7 100644 --- a/homeassistant/components/fitbit/sensor.py +++ b/homeassistant/components/fitbit/sensor.py @@ -86,7 +86,10 @@ def request_app_setup( if os.path.isfile(config_path): config_file = load_json(config_path) if config_file == DEFAULT_CONFIG: - error_msg = f"You didn't correctly modify {FITBIT_CONFIG_FILE}, please try again." + error_msg = ( + f"You didn't correctly modify {FITBIT_CONFIG_FILE}, please try" + " again." + ) configurator.notify_errors(hass, _CONFIGURING["fitbit"], error_msg) else: @@ -365,8 +368,7 @@ class FitbitSensor(SensorEntity): self._attr_name = f"{self.extra.get('deviceVersion')} Battery" self._attr_unique_id = f"{self._attr_unique_id}_{self.extra.get('id')}" - if (unit_type := description.unit_type) == "": - split_resource = description.key.rsplit("/", maxsplit=1)[-1] + if description.unit_type: try: measurement_system = FITBIT_MEASUREMENTS[self.client.system] except KeyError: @@ -374,8 +376,9 @@ class FitbitSensor(SensorEntity): measurement_system = FITBIT_MEASUREMENTS["metric"] else: measurement_system = FITBIT_MEASUREMENTS["en_US"] + split_resource = description.key.rsplit("/", maxsplit=1)[-1] unit_type = measurement_system[split_resource] - self._attr_native_unit_of_measurement = unit_type + self._attr_native_unit_of_measurement = unit_type @property def icon(self) -> str | None: diff --git a/homeassistant/components/fivem/translations/ko.json b/homeassistant/components/fivem/translations/ko.json new file mode 100644 index 00000000000..08d879d2c3d --- /dev/null +++ b/homeassistant/components/fivem/translations/ko.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "\uc11c\ube44\uc2a4\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "unknown_error": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8", + "name": "\uc774\ub984", + "port": "\ud3ec\ud2b8" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/fivem/translations/sk.json b/homeassistant/components/fivem/translations/sk.json index f9219d753f0..e3c842bf25b 100644 --- a/homeassistant/components/fivem/translations/sk.json +++ b/homeassistant/components/fivem/translations/sk.json @@ -4,6 +4,8 @@ "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" }, "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165. Skontrolujte hostite\u013ea a port a sk\u00faste to znova. Tie\u017e sa uistite, \u017ee pou\u017e\u00edvate najnov\u0161\u00ed server FiveM.", + "invalid_game_name": "Rozhranie API hry, ku ktorej sa pok\u00fa\u0161ate pripoji\u0165, nie je hra FiveM.", "unknown_error": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { diff --git a/homeassistant/components/fjaraskupan/number.py b/homeassistant/components/fjaraskupan/number.py index d70f9b6a423..b5109883641 100644 --- a/homeassistant/components/fjaraskupan/number.py +++ b/homeassistant/components/fjaraskupan/number.py @@ -3,7 +3,7 @@ from __future__ import annotations from homeassistant.components.number import NumberEntity from homeassistant.config_entries import ConfigEntry -from homeassistant.const import TIME_MINUTES +from homeassistant.const import UnitOfTime from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo, Entity, EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -36,7 +36,7 @@ class PeriodicVentingTime(CoordinatorEntity[Coordinator], NumberEntity): _attr_native_min_value: float = 0 _attr_native_step: float = 1 _attr_entity_category = EntityCategory.CONFIG - _attr_native_unit_of_measurement = TIME_MINUTES + _attr_native_unit_of_measurement = UnitOfTime.MINUTES def __init__( self, diff --git a/homeassistant/components/fjaraskupan/translations/ko.json b/homeassistant/components/fjaraskupan/translations/ko.json index 0a4087d0200..3e02f846df3 100644 --- a/homeassistant/components/fjaraskupan/translations/ko.json +++ b/homeassistant/components/fjaraskupan/translations/ko.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c \uc548\uc5d0 \ubc1c\uacac\ub41c \ub514\ubc14\uc774\uc2a4 \uc5c6\uc74c" + "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c \uc548\uc5d0 \ubc1c\uacac\ub41c \ub514\ubc14\uc774\uc2a4 \uc5c6\uc74c", + "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." } } } \ No newline at end of file diff --git a/homeassistant/components/fjaraskupan/translations/sk.json b/homeassistant/components/fjaraskupan/translations/sk.json index 99798036ffd..84274efd7f6 100644 --- a/homeassistant/components/fjaraskupan/translations/sk.json +++ b/homeassistant/components/fjaraskupan/translations/sk.json @@ -3,6 +3,11 @@ "abort": { "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, + "step": { + "confirm": { + "description": "Chcete nastavi\u0165 Fj\u00e4r\u00e5skupan?" + } } } } \ No newline at end of file diff --git a/homeassistant/components/flexit/climate.py b/homeassistant/components/flexit/climate.py index 27f2314c1f2..ac8f4b4da8c 100644 --- a/homeassistant/components/flexit/climate.py +++ b/homeassistant/components/flexit/climate.py @@ -27,7 +27,7 @@ from homeassistant.const import ( CONF_NAME, CONF_SLAVE, DEVICE_DEFAULT_NAME, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv @@ -67,7 +67,7 @@ class Flexit(ClimateEntity): _attr_supported_features = ( ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.FAN_MODE ) - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS def __init__( self, hub: ModbusHub, modbus_slave: int | None, name: str | None diff --git a/homeassistant/components/flick_electric/sensor.py b/homeassistant/components/flick_electric/sensor.py index d0ff91d7094..18daaa9d4e2 100644 --- a/homeassistant/components/flick_electric/sensor.py +++ b/homeassistant/components/flick_electric/sensor.py @@ -8,7 +8,7 @@ from pyflick import FlickAPI, FlickPrice from homeassistant.components.sensor import SensorEntity from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_FRIENDLY_NAME, CURRENCY_CENT, ENERGY_KILO_WATT_HOUR +from homeassistant.const import ATTR_FRIENDLY_NAME, CURRENCY_CENT, UnitOfEnergy from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.util.dt import utcnow @@ -35,7 +35,7 @@ class FlickPricingSensor(SensorEntity): """Entity object for Flick Electric sensor.""" _attr_attribution = "Data provided by Flick Electric" - _attr_native_unit_of_measurement = f"{CURRENCY_CENT}/{ENERGY_KILO_WATT_HOUR}" + _attr_native_unit_of_measurement = f"{CURRENCY_CENT}/{UnitOfEnergy.KILO_WATT_HOUR}" def __init__(self, api: FlickAPI) -> None: """Entity object for Flick Electric sensor.""" diff --git a/homeassistant/components/flick_electric/translations/pt.json b/homeassistant/components/flick_electric/translations/pt.json index c2bf0536ccf..9b81a181da2 100644 --- a/homeassistant/components/flick_electric/translations/pt.json +++ b/homeassistant/components/flick_electric/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "Conta j\u00e1 configurada" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, diff --git a/homeassistant/components/flick_electric/translations/sk.json b/homeassistant/components/flick_electric/translations/sk.json index e711bdb9556..86a5365b0ed 100644 --- a/homeassistant/components/flick_electric/translations/sk.json +++ b/homeassistant/components/flick_electric/translations/sk.json @@ -12,6 +12,7 @@ "user": { "data": { "client_id": "ID klienta (volite\u013en\u00e9)", + "client_secret": "Client Secret (volite\u013en\u00e9)", "password": "Heslo", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" }, diff --git a/homeassistant/components/flipr/sensor.py b/homeassistant/components/flipr/sensor.py index 99b44cc95a0..57044289110 100644 --- a/homeassistant/components/flipr/sensor.py +++ b/homeassistant/components/flipr/sensor.py @@ -8,7 +8,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ELECTRIC_POTENTIAL_MILLIVOLT, PERCENTAGE, TEMP_CELSIUS +from homeassistant.const import PERCENTAGE, UnitOfElectricPotential, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -19,7 +19,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="chlorine", name="Chlorine", - native_unit_of_measurement=ELECTRIC_POTENTIAL_MILLIVOLT, + native_unit_of_measurement=UnitOfElectricPotential.MILLIVOLT, icon="mdi:pool", state_class=SensorStateClass.MEASUREMENT, ), @@ -33,7 +33,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( key="temperature", name="Water Temp", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( @@ -44,7 +44,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="red_ox", name="Red OX", - native_unit_of_measurement=ELECTRIC_POTENTIAL_MILLIVOLT, + native_unit_of_measurement=UnitOfElectricPotential.MILLIVOLT, icon="mdi:pool", state_class=SensorStateClass.MEASUREMENT, ), diff --git a/homeassistant/components/flipr/translations/de.json b/homeassistant/components/flipr/translations/de.json index 4dbbfbc9ef9..16a4867d3ab 100644 --- a/homeassistant/components/flipr/translations/de.json +++ b/homeassistant/components/flipr/translations/de.json @@ -22,7 +22,7 @@ "email": "E-Mail", "password": "Passwort" }, - "description": "Verbinde dich mit deinem Flipr-Konto.", + "description": "Verbinde dich mit deinem Flipr Konto.", "title": "Mit Flipr verbinden" } } diff --git a/homeassistant/components/flipr/translations/ko.json b/homeassistant/components/flipr/translations/ko.json new file mode 100644 index 00000000000..13ebab4dc44 --- /dev/null +++ b/homeassistant/components/flipr/translations/ko.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "email": "\uc774\uba54\uc77c", + "password": "\ube44\ubc00\ubc88\ud638" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/flipr/translations/pt.json b/homeassistant/components/flipr/translations/pt.json index 12a3977fdde..91a0c4562f2 100644 --- a/homeassistant/components/flipr/translations/pt.json +++ b/homeassistant/components/flipr/translations/pt.json @@ -1,7 +1,7 @@ { "config": { "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida" }, "step": { diff --git a/homeassistant/components/flipr/translations/sk.json b/homeassistant/components/flipr/translations/sk.json index b9832f49cc6..7cfebc69cb3 100644 --- a/homeassistant/components/flipr/translations/sk.json +++ b/homeassistant/components/flipr/translations/sk.json @@ -6,6 +6,7 @@ "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie", + "no_flipr_id_found": "K v\u00e1\u0161mu \u00fa\u010dtu moment\u00e1lne nie je priraden\u00e9 \u017eiadne ID flipr. Najprv by ste si mali overi\u0165, \u010di funguje s mobilnou aplik\u00e1ciou Flipr.", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { @@ -13,7 +14,8 @@ "data": { "flipr_id": "ID Flipr" }, - "description": "V zozname vyberte svoje Flipr ID" + "description": "V zozname vyberte svoje Flipr ID", + "title": "Vyberte si svoj Flipr" }, "user": { "data": { diff --git a/homeassistant/components/flo/sensor.py b/homeassistant/components/flo/sensor.py index 58e8417de09..2c89123dac1 100644 --- a/homeassistant/components/flo/sensor.py +++ b/homeassistant/components/flo/sensor.py @@ -9,9 +9,9 @@ from homeassistant.components.sensor import ( from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( PERCENTAGE, - PRESSURE_PSI, - TEMP_FAHRENHEIT, - VOLUME_GALLONS, + UnitOfPressure, + UnitOfTemperature, + UnitOfVolume, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -68,7 +68,7 @@ class FloDailyUsageSensor(FloEntity, SensorEntity): """Monitors the daily water usage.""" _attr_icon = WATER_ICON - _attr_native_unit_of_measurement = VOLUME_GALLONS + _attr_native_unit_of_measurement = UnitOfVolume.GALLONS _attr_state_class: SensorStateClass = SensorStateClass.TOTAL_INCREASING _attr_device_class = SensorDeviceClass.WATER @@ -125,7 +125,7 @@ class FloTemperatureSensor(FloEntity, SensorEntity): """Monitors the temperature.""" _attr_device_class = SensorDeviceClass.TEMPERATURE - _attr_native_unit_of_measurement = TEMP_FAHRENHEIT + _attr_native_unit_of_measurement = UnitOfTemperature.FAHRENHEIT _attr_state_class: SensorStateClass = SensorStateClass.MEASUREMENT def __init__(self, name, device): @@ -165,7 +165,7 @@ class FloPressureSensor(FloEntity, SensorEntity): """Monitors the water pressure.""" _attr_device_class = SensorDeviceClass.PRESSURE - _attr_native_unit_of_measurement = PRESSURE_PSI + _attr_native_unit_of_measurement = UnitOfPressure.PSI _attr_state_class: SensorStateClass = SensorStateClass.MEASUREMENT def __init__(self, device): diff --git a/homeassistant/components/flo/translations/pt.json b/homeassistant/components/flo/translations/pt.json index 561c8d77287..7e727215891 100644 --- a/homeassistant/components/flo/translations/pt.json +++ b/homeassistant/components/flo/translations/pt.json @@ -4,14 +4,14 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, "step": { "user": { "data": { - "host": "Servidor", + "host": "Endere\u00e7o", "password": "Palavra-passe", "username": "Nome de Utilizador" } diff --git a/homeassistant/components/flume/sensor.py b/homeassistant/components/flume/sensor.py index 5b8dbde69e4..c7ebbd1f456 100644 --- a/homeassistant/components/flume/sensor.py +++ b/homeassistant/components/flume/sensor.py @@ -10,7 +10,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import VOLUME_GALLONS +from homeassistant.const import UnitOfVolume from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -35,45 +35,45 @@ FLUME_QUERIES_SENSOR: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="current_interval", name="Current", - native_unit_of_measurement=f"{VOLUME_GALLONS}/m", + native_unit_of_measurement=f"{UnitOfVolume.GALLONS}/m", ), SensorEntityDescription( key="month_to_date", name="Current Month", - native_unit_of_measurement=VOLUME_GALLONS, + native_unit_of_measurement=UnitOfVolume.GALLONS, device_class=SensorDeviceClass.WATER, state_class=SensorStateClass.TOTAL_INCREASING, ), SensorEntityDescription( key="week_to_date", name="Current Week", - native_unit_of_measurement=VOLUME_GALLONS, + native_unit_of_measurement=UnitOfVolume.GALLONS, device_class=SensorDeviceClass.WATER, state_class=SensorStateClass.TOTAL_INCREASING, ), SensorEntityDescription( key="today", name="Current Day", - native_unit_of_measurement=VOLUME_GALLONS, + native_unit_of_measurement=UnitOfVolume.GALLONS, device_class=SensorDeviceClass.WATER, state_class=SensorStateClass.TOTAL_INCREASING, ), SensorEntityDescription( key="last_60_min", name="60 Minutes", - native_unit_of_measurement=f"{VOLUME_GALLONS}/h", + native_unit_of_measurement=f"{UnitOfVolume.GALLONS}/h", state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="last_24_hrs", name="24 Hours", - native_unit_of_measurement=f"{VOLUME_GALLONS}/d", + native_unit_of_measurement=f"{UnitOfVolume.GALLONS}/d", state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="last_30_days", name="30 Days", - native_unit_of_measurement=f"{VOLUME_GALLONS}/mo", + native_unit_of_measurement=f"{UnitOfVolume.GALLONS}/mo", state_class=SensorStateClass.MEASUREMENT, ), ) diff --git a/homeassistant/components/flume/translations/pt.json b/homeassistant/components/flume/translations/pt.json index c2bf0536ccf..9b81a181da2 100644 --- a/homeassistant/components/flume/translations/pt.json +++ b/homeassistant/components/flume/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "Conta j\u00e1 configurada" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, diff --git a/homeassistant/components/flume/translations/sk.json b/homeassistant/components/flume/translations/sk.json index 44f0b9addf1..b9a883e81df 100644 --- a/homeassistant/components/flume/translations/sk.json +++ b/homeassistant/components/flume/translations/sk.json @@ -20,9 +20,11 @@ "user": { "data": { "client_id": "ID klienta", + "client_secret": "Client Secret", "password": "Heslo", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" }, + "description": "Na pr\u00edstup k osobn\u00e9mu API Flume si budete musie\u0165 vy\u017eiada\u0165 \u201eClient ID\u201c a \u201eClient Secret\u201c na https://portal.flumetech.com/settings#token", "title": "Pripoji\u0165 sa k svojmu \u00fa\u010dtu Flume" } } diff --git a/homeassistant/components/flux/switch.py b/homeassistant/components/flux/switch.py index baa2d658d23..6da3f88eedf 100644 --- a/homeassistant/components/flux/switch.py +++ b/homeassistant/components/flux/switch.py @@ -316,8 +316,10 @@ class FluxSwitch(SwitchEntity, RestoreEntity): self.hass, self._lights, x_val, y_val, brightness, self._transition ) _LOGGER.debug( - "Lights updated to x:%s y:%s brightness:%s, %s%% " - "of %s cycle complete at %s", + ( + "Lights updated to x:%s y:%s brightness:%s, %s%% " + "of %s cycle complete at %s" + ), x_val, y_val, brightness, @@ -341,8 +343,10 @@ class FluxSwitch(SwitchEntity, RestoreEntity): self.hass, self._lights, mired, brightness, self._transition ) _LOGGER.debug( - "Lights updated to mired:%s brightness:%s, %s%% " - "of %s cycle complete at %s", + ( + "Lights updated to mired:%s brightness:%s, %s%% " + "of %s cycle complete at %s" + ), mired, brightness, round(percentage_complete * 100), diff --git a/homeassistant/components/flux_led/__init__.py b/homeassistant/components/flux_led/__init__.py index 717b4a685df..04d60fec25e 100644 --- a/homeassistant/components/flux_led/__init__.py +++ b/homeassistant/components/flux_led/__init__.py @@ -177,7 +177,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: if not mac_matches_by_one(mac, entry.unique_id): # The device is offline and another flux_led device is now using the ip address raise ConfigEntryNotReady( - f"Unexpected device found at {host}; Expected {entry.unique_id}, found {mac}" + f"Unexpected device found at {host}; Expected {entry.unique_id}, found" + f" {mac}" ) if not discovery_cached: diff --git a/homeassistant/components/flux_led/coordinator.py b/homeassistant/components/flux_led/coordinator.py index 5a7b3c89216..38c5ed70b5e 100644 --- a/homeassistant/components/flux_led/coordinator.py +++ b/homeassistant/components/flux_led/coordinator.py @@ -20,7 +20,7 @@ _LOGGER = logging.getLogger(__name__) REQUEST_REFRESH_DELAY: Final = 2.0 -class FluxLedUpdateCoordinator(DataUpdateCoordinator): +class FluxLedUpdateCoordinator(DataUpdateCoordinator[None]): """DataUpdateCoordinator to gather data for a specific flux_led device.""" def __init__( diff --git a/homeassistant/components/flux_led/strings.json b/homeassistant/components/flux_led/strings.json index 98fd86d3fa2..09d9ed399ff 100644 --- a/homeassistant/components/flux_led/strings.json +++ b/homeassistant/components/flux_led/strings.json @@ -9,7 +9,7 @@ } }, "discovery_confirm": { - "description": "Do you want to setup {model} {id} ({ipaddr})?" + "description": "Do you want to set up {model} {id} ({ipaddr})?" } }, "error": { diff --git a/homeassistant/components/flux_led/translations/de.json b/homeassistant/components/flux_led/translations/de.json index f036e7bd913..ce98d33ba34 100644 --- a/homeassistant/components/flux_led/translations/de.json +++ b/homeassistant/components/flux_led/translations/de.json @@ -11,7 +11,7 @@ "flow_title": "{model} {id} ({ipaddr})", "step": { "discovery_confirm": { - "description": "M\u00f6chtest du {model} {id} ({ipaddr})?" + "description": "M\u00f6chtest du {model} {id} ({ipaddr}) einrichten?" }, "user": { "data": { diff --git a/homeassistant/components/flux_led/translations/en.json b/homeassistant/components/flux_led/translations/en.json index 9a988408c30..eb4f4944381 100644 --- a/homeassistant/components/flux_led/translations/en.json +++ b/homeassistant/components/flux_led/translations/en.json @@ -11,7 +11,7 @@ "flow_title": "{model} {id} ({ipaddr})", "step": { "discovery_confirm": { - "description": "Do you want to setup {model} {id} ({ipaddr})?" + "description": "Do you want to set up {model} {id} ({ipaddr})?" }, "user": { "data": { diff --git a/homeassistant/components/flux_led/translations/ko.json b/homeassistant/components/flux_led/translations/ko.json new file mode 100644 index 00000000000..bea6bcf2bbc --- /dev/null +++ b/homeassistant/components/flux_led/translations/ko.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", + "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/flux_led/translations/no.json b/homeassistant/components/flux_led/translations/no.json index ec105c1ac14..a0136d26ef7 100644 --- a/homeassistant/components/flux_led/translations/no.json +++ b/homeassistant/components/flux_led/translations/no.json @@ -11,7 +11,7 @@ "flow_title": "{model} {id} ( {ipaddr} )", "step": { "discovery_confirm": { - "description": "Vil du konfigurere {model} {id} ( {ipaddr} )?" + "description": "Vil du sette opp {model} {id} ( {ipaddr} )?" }, "user": { "data": { diff --git a/homeassistant/components/flux_led/translations/pt.json b/homeassistant/components/flux_led/translations/pt.json index c8c04537f40..6d68e0effc0 100644 --- a/homeassistant/components/flux_led/translations/pt.json +++ b/homeassistant/components/flux_led/translations/pt.json @@ -7,7 +7,7 @@ "step": { "user": { "data": { - "host": "Servidor" + "host": "Endere\u00e7o" }, "description": "Se voc\u00ea deixar o host vazio, a descoberta ser\u00e1 usada para localizar dispositivos." } diff --git a/homeassistant/components/flux_led/translations/sk.json b/homeassistant/components/flux_led/translations/sk.json index 1fe408d6105..1d6f1e3974f 100644 --- a/homeassistant/components/flux_led/translations/sk.json +++ b/homeassistant/components/flux_led/translations/sk.json @@ -16,7 +16,8 @@ "user": { "data": { "host": "Hostite\u013e" - } + }, + "description": "Ak nech\u00e1te hostite\u013ea pr\u00e1zdneho, na vyh\u013ead\u00e1vanie zariaden\u00ed sa pou\u017eije zis\u0165ovanie." } } }, @@ -24,6 +25,9 @@ "step": { "init": { "data": { + "custom_effect_colors": "Vlastn\u00fd efekt: Zoznam 1 a\u017e 16 farieb [R,G,B]. Pr\u00edklad: [255,0,255],[60,128,0]", + "custom_effect_speed_pct": "Vlastn\u00fd efekt: R\u00fdchlos\u0165 v percent\u00e1ch pre efekt, ktor\u00fd prep\u00edna farby.", + "custom_effect_transition": "Vlastn\u00fd efekt: Typ prechodu medzi farbami.", "mode": "Zvolen\u00fd re\u017eim jasu." } } diff --git a/homeassistant/components/folder/sensor.py b/homeassistant/components/folder/sensor.py index 13295d069a3..8c71208b745 100644 --- a/homeassistant/components/folder/sensor.py +++ b/homeassistant/components/folder/sensor.py @@ -8,8 +8,12 @@ import os import voluptuous as vol -from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity -from homeassistant.const import DATA_MEGABYTES +from homeassistant.components.sensor import ( + PLATFORM_SCHEMA, + SensorDeviceClass, + SensorEntity, +) +from homeassistant.const import UnitOfInformation from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -31,14 +35,14 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( ) -def get_files_list(folder_path, filter_term): +def get_files_list(folder_path: str, filter_term: str) -> list[str]: """Return the list of files, applying filter.""" query = folder_path + filter_term files_list = glob.glob(query) return files_list -def get_size(files_list): +def get_size(files_list: list[str]) -> int: """Return the sum of the size in bytes of files in the list.""" size_list = [os.stat(f).st_size for f in files_list if os.path.isfile(f)] return sum(size_list) @@ -51,7 +55,7 @@ def setup_platform( discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up the folder sensor.""" - path = config[CONF_FOLDER_PATHS] + path: str = config[CONF_FOLDER_PATHS] if not hass.config.is_allowed_path(path): _LOGGER.error("Folder %s is not valid or allowed", path) @@ -63,55 +67,28 @@ def setup_platform( class Folder(SensorEntity): """Representation of a folder.""" - ICON = "mdi:folder" + _attr_device_class = SensorDeviceClass.DATA_SIZE + _attr_icon = "mdi:folder" + _attr_native_unit_of_measurement = UnitOfInformation.MEGABYTES - def __init__(self, folder_path, filter_term): + def __init__(self, folder_path: str, filter_term: str) -> None: """Initialize the data object.""" folder_path = os.path.join(folder_path, "") # If no trailing / add it self._folder_path = folder_path # Need to check its a valid path self._filter_term = filter_term - self._number_of_files = None - self._size = None - self._name = os.path.split(os.path.split(folder_path)[0])[1] - self._unit_of_measurement = DATA_MEGABYTES - self._file_list = None + self._attr_name = os.path.split(os.path.split(folder_path)[0])[1] def update(self) -> None: """Update the sensor.""" files_list = get_files_list(self._folder_path, self._filter_term) - self._file_list = files_list - self._number_of_files = len(files_list) - self._size = get_size(files_list) + number_of_files = len(files_list) + size = get_size(files_list) - @property - def name(self): - """Return the name of the sensor.""" - return self._name - - @property - def native_value(self): - """Return the state of the sensor.""" - decimals = 2 - size_mb = round(self._size / 1e6, decimals) - return size_mb - - @property - def icon(self): - """Icon to use in the frontend, if any.""" - return self.ICON - - @property - def extra_state_attributes(self): - """Return other details about the sensor state.""" - return { + self._attr_native_value = round(size / 1e6, 2) + self._attr_extra_state_attributes = { "path": self._folder_path, "filter": self._filter_term, - "number_of_files": self._number_of_files, - "bytes": self._size, - "file_list": self._file_list, + "number_of_files": number_of_files, + "bytes": size, + "file_list": files_list, } - - @property - def native_unit_of_measurement(self): - """Return the unit of measurement of this entity, if any.""" - return self._unit_of_measurement diff --git a/homeassistant/components/folder_watcher/manifest.json b/homeassistant/components/folder_watcher/manifest.json index 64bfbb3df37..58c8c9c04fa 100644 --- a/homeassistant/components/folder_watcher/manifest.json +++ b/homeassistant/components/folder_watcher/manifest.json @@ -2,7 +2,7 @@ "domain": "folder_watcher", "name": "Folder Watcher", "documentation": "https://www.home-assistant.io/integrations/folder_watcher", - "requirements": ["watchdog==2.1.9"], + "requirements": ["watchdog==2.2.0"], "codeowners": [], "quality_scale": "internal", "iot_class": "local_polling", diff --git a/homeassistant/components/foobot/sensor.py b/homeassistant/components/foobot/sensor.py index 82a48c810c3..a865dd33053 100644 --- a/homeassistant/components/foobot/sensor.py +++ b/homeassistant/components/foobot/sensor.py @@ -23,7 +23,7 @@ from homeassistant.const import ( CONF_TOKEN, CONF_USERNAME, PERCENTAGE, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.exceptions import PlatformNotReady @@ -52,7 +52,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="tmp", name=ATTR_TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, ), SensorEntityDescription( diff --git a/homeassistant/components/forecast_solar/__init__.py b/homeassistant/components/forecast_solar/__init__.py index ece451a9b0a..e10d9651c3b 100644 --- a/homeassistant/components/forecast_solar/__init__.py +++ b/homeassistant/components/forecast_solar/__init__.py @@ -1,66 +1,19 @@ """The Forecast.Solar integration.""" from __future__ import annotations -from datetime import timedelta -import logging - -from forecast_solar import Estimate, ForecastSolar - from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, Platform +from homeassistant.const import Platform from homeassistant.core import HomeAssistant -from homeassistant.helpers.aiohttp_client import async_get_clientsession -from homeassistant.helpers.update_coordinator import DataUpdateCoordinator -from .const import ( - CONF_AZIMUTH, - CONF_DAMPING, - CONF_DECLINATION, - CONF_INVERTER_SIZE, - CONF_MODULES_POWER, - DOMAIN, -) +from .const import DOMAIN +from .coordinator import ForecastSolarDataUpdateCoordinator PLATFORMS = [Platform.SENSOR] async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Forecast.Solar from a config entry.""" - # Our option flow may cause it to be an empty string, - # this if statement is here to catch that. - api_key = entry.options.get(CONF_API_KEY) or None - - if ( - inverter_size := entry.options.get(CONF_INVERTER_SIZE) - ) is not None and inverter_size > 0: - inverter_size = inverter_size / 1000 - - session = async_get_clientsession(hass) - forecast = ForecastSolar( - api_key=api_key, - session=session, - latitude=entry.data[CONF_LATITUDE], - longitude=entry.data[CONF_LONGITUDE], - declination=entry.options[CONF_DECLINATION], - azimuth=(entry.options[CONF_AZIMUTH] - 180), - kwp=(entry.options[CONF_MODULES_POWER] / 1000), - damping=entry.options.get(CONF_DAMPING, 0), - inverter=inverter_size, - ) - - # Free account have a resolution of 1 hour, using that as the default - # update interval. Using a higher value for accounts with an API key. - update_interval = timedelta(hours=1) - if api_key is not None: - update_interval = timedelta(minutes=30) - - coordinator: DataUpdateCoordinator[Estimate] = DataUpdateCoordinator( - hass, - logging.getLogger(__name__), - name=DOMAIN, - update_method=forecast.estimate, - update_interval=update_interval, - ) + coordinator = ForecastSolarDataUpdateCoordinator(hass, entry) await coordinator.async_config_entry_first_refresh() hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator diff --git a/homeassistant/components/forecast_solar/const.py b/homeassistant/components/forecast_solar/const.py index 185025fb5ce..c7663d6cf31 100644 --- a/homeassistant/components/forecast_solar/const.py +++ b/homeassistant/components/forecast_solar/const.py @@ -2,13 +2,15 @@ from __future__ import annotations from datetime import timedelta +import logging from homeassistant.components.sensor import SensorDeviceClass, SensorStateClass -from homeassistant.const import ENERGY_KILO_WATT_HOUR, POWER_WATT +from homeassistant.const import UnitOfEnergy, UnitOfPower from .models import ForecastSolarSensorEntityDescription DOMAIN = "forecast_solar" +LOGGER = logging.getLogger(__package__) CONF_DECLINATION = "declination" CONF_AZIMUTH = "azimuth" @@ -22,14 +24,14 @@ SENSORS: tuple[ForecastSolarSensorEntityDescription, ...] = ( name="Estimated energy production - today", state=lambda estimate: estimate.energy_production_today / 1000, device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, ), ForecastSolarSensorEntityDescription( key="energy_production_tomorrow", name="Estimated energy production - tomorrow", state=lambda estimate: estimate.energy_production_tomorrow / 1000, device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, ), ForecastSolarSensorEntityDescription( key="power_highest_peak_time_today", @@ -47,7 +49,7 @@ SENSORS: tuple[ForecastSolarSensorEntityDescription, ...] = ( device_class=SensorDeviceClass.POWER, state=lambda estimate: estimate.power_production_now, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, ), ForecastSolarSensorEntityDescription( key="power_production_next_hour", @@ -57,7 +59,7 @@ SENSORS: tuple[ForecastSolarSensorEntityDescription, ...] = ( name="Estimated power production - next hour", device_class=SensorDeviceClass.POWER, entity_registry_enabled_default=False, - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, ), ForecastSolarSensorEntityDescription( key="power_production_next_12hours", @@ -67,7 +69,7 @@ SENSORS: tuple[ForecastSolarSensorEntityDescription, ...] = ( name="Estimated power production - next 12 hours", device_class=SensorDeviceClass.POWER, entity_registry_enabled_default=False, - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, ), ForecastSolarSensorEntityDescription( key="power_production_next_24hours", @@ -77,20 +79,20 @@ SENSORS: tuple[ForecastSolarSensorEntityDescription, ...] = ( name="Estimated power production - next 24 hours", device_class=SensorDeviceClass.POWER, entity_registry_enabled_default=False, - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, ), ForecastSolarSensorEntityDescription( key="energy_current_hour", name="Estimated energy production - this hour", state=lambda estimate: estimate.energy_current_hour / 1000, device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, ), ForecastSolarSensorEntityDescription( key="energy_next_hour", state=lambda estimate: estimate.sum_energy_production(1) / 1000, name="Estimated energy production - next hour", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, ), ) diff --git a/homeassistant/components/forecast_solar/coordinator.py b/homeassistant/components/forecast_solar/coordinator.py new file mode 100644 index 00000000000..273d3a49a2f --- /dev/null +++ b/homeassistant/components/forecast_solar/coordinator.py @@ -0,0 +1,65 @@ +"""DataUpdateCoordinator for the Forecast.Solar integration.""" +from __future__ import annotations + +from datetime import timedelta + +from forecast_solar import Estimate, ForecastSolar + +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE +from homeassistant.core import HomeAssistant +from homeassistant.helpers.aiohttp_client import async_get_clientsession +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator + +from .const import ( + CONF_AZIMUTH, + CONF_DAMPING, + CONF_DECLINATION, + CONF_INVERTER_SIZE, + CONF_MODULES_POWER, + DOMAIN, + LOGGER, +) + + +class ForecastSolarDataUpdateCoordinator(DataUpdateCoordinator[Estimate]): + """The Forecast.Solar Data Update Coordinator.""" + + config_entry: ConfigEntry + + def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None: + """Initialize the Forecast.Solar coordinator.""" + self.config_entry = entry + + # Our option flow may cause it to be an empty string, + # this if statement is here to catch that. + api_key = entry.options.get(CONF_API_KEY) or None + + if ( + inverter_size := entry.options.get(CONF_INVERTER_SIZE) + ) is not None and inverter_size > 0: + inverter_size = inverter_size / 1000 + + self.forecast = ForecastSolar( + api_key=api_key, + session=async_get_clientsession(hass), + latitude=entry.data[CONF_LATITUDE], + longitude=entry.data[CONF_LONGITUDE], + declination=entry.options[CONF_DECLINATION], + azimuth=(entry.options[CONF_AZIMUTH] - 180), + kwp=(entry.options[CONF_MODULES_POWER] / 1000), + damping=entry.options.get(CONF_DAMPING, 0), + inverter=inverter_size, + ) + + # Free account have a resolution of 1 hour, using that as the default + # update interval. Using a higher value for accounts with an API key. + update_interval = timedelta(hours=1) + if api_key is not None: + update_interval = timedelta(minutes=30) + + super().__init__(hass, LOGGER, name=DOMAIN, update_interval=update_interval) + + async def _async_update_data(self) -> Estimate: + """Fetch Forecast.Solar estimates.""" + return await self.forecast.estimate() diff --git a/homeassistant/components/forecast_solar/sensor.py b/homeassistant/components/forecast_solar/sensor.py index 7bac69b1b6e..681e04f434f 100644 --- a/homeassistant/components/forecast_solar/sensor.py +++ b/homeassistant/components/forecast_solar/sensor.py @@ -10,12 +10,10 @@ from homeassistant.helpers.device_registry import DeviceEntryType from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import StateType -from homeassistant.helpers.update_coordinator import ( - CoordinatorEntity, - DataUpdateCoordinator, -) +from homeassistant.helpers.update_coordinator import CoordinatorEntity from .const import DOMAIN, SENSORS +from .coordinator import ForecastSolarDataUpdateCoordinator from .models import ForecastSolarSensorEntityDescription @@ -23,7 +21,7 @@ async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback ) -> None: """Defer sensor setup to the shared sensor module.""" - coordinator: DataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] + coordinator: ForecastSolarDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] async_add_entities( ForecastSolarSensorEntity( @@ -35,7 +33,9 @@ async def async_setup_entry( ) -class ForecastSolarSensorEntity(CoordinatorEntity, SensorEntity): +class ForecastSolarSensorEntity( + CoordinatorEntity[ForecastSolarDataUpdateCoordinator], SensorEntity +): """Defines a Forecast.Solar sensor.""" entity_description: ForecastSolarSensorEntityDescription @@ -45,7 +45,7 @@ class ForecastSolarSensorEntity(CoordinatorEntity, SensorEntity): self, *, entry_id: str, - coordinator: DataUpdateCoordinator, + coordinator: ForecastSolarDataUpdateCoordinator, entity_description: ForecastSolarSensorEntityDescription, ) -> None: """Initialize Forecast.Solar sensor.""" diff --git a/homeassistant/components/forecast_solar/translations/hu.json b/homeassistant/components/forecast_solar/translations/hu.json index 7ff078d1301..98407c0cb92 100644 --- a/homeassistant/components/forecast_solar/translations/hu.json +++ b/homeassistant/components/forecast_solar/translations/hu.json @@ -15,6 +15,9 @@ } }, "options": { + "error": { + "invalid_api_key": "\u00c9rv\u00e9nytelen API kulcs" + }, "step": { "init": { "data": { diff --git a/homeassistant/components/forecast_solar/translations/it.json b/homeassistant/components/forecast_solar/translations/it.json index 8d6d266fa95..dd715f77293 100644 --- a/homeassistant/components/forecast_solar/translations/it.json +++ b/homeassistant/components/forecast_solar/translations/it.json @@ -15,6 +15,9 @@ } }, "options": { + "error": { + "invalid_api_key": "Chiave API non valida" + }, "step": { "init": { "data": { diff --git a/homeassistant/components/forecast_solar/translations/ko.json b/homeassistant/components/forecast_solar/translations/ko.json new file mode 100644 index 00000000000..29d08113ec5 --- /dev/null +++ b/homeassistant/components/forecast_solar/translations/ko.json @@ -0,0 +1,18 @@ +{ + "config": { + "step": { + "user": { + "data": { + "latitude": "\uc704\ub3c4", + "longitude": "\uacbd\ub3c4", + "name": "\uc774\ub984" + } + } + } + }, + "options": { + "error": { + "invalid_api_key": "API \ud0a4\uac00 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/forecast_solar/translations/pl.json b/homeassistant/components/forecast_solar/translations/pl.json index c4be17eed34..9f2db346dcb 100644 --- a/homeassistant/components/forecast_solar/translations/pl.json +++ b/homeassistant/components/forecast_solar/translations/pl.json @@ -15,6 +15,9 @@ } }, "options": { + "error": { + "invalid_api_key": "Nieprawid\u0142owy klucz API" + }, "step": { "init": { "data": { diff --git a/homeassistant/components/forecast_solar/translations/pt.json b/homeassistant/components/forecast_solar/translations/pt.json index 2e6515edd09..b6e8a0fab2d 100644 --- a/homeassistant/components/forecast_solar/translations/pt.json +++ b/homeassistant/components/forecast_solar/translations/pt.json @@ -7,5 +7,10 @@ } } } + }, + "options": { + "error": { + "invalid_api_key": "Chave de API inv\u00e1lida" + } } } \ No newline at end of file diff --git a/homeassistant/components/forecast_solar/translations/sk.json b/homeassistant/components/forecast_solar/translations/sk.json index ff539ab78db..047786e13f7 100644 --- a/homeassistant/components/forecast_solar/translations/sk.json +++ b/homeassistant/components/forecast_solar/translations/sk.json @@ -25,6 +25,7 @@ "azimuth": "Azimut (360\u02da, 0 = sever, 90 = v\u00fdchod, 180 = juh, 270 = z\u00e1pad)", "damping": "\u010cinite\u013e tlmenia: upravuje v\u00fdsledky r\u00e1no a ve\u010der", "declination": "Deklin\u00e1cia (0 = horizont\u00e1lna, 90 = vertik\u00e1lna)", + "inverter_size": "Ve\u013ekos\u0165 meni\u010da (Watt)", "modules power": "Celkov\u00fd \u0161pi\u010dkov\u00fd v\u00fdkon sol\u00e1rnych modulov" }, "description": "Tieto hodnoty umo\u017e\u0148uj\u00fa upravi\u0165 v\u00fdsledn\u00e9 hodnoty Solar.Forecast. Ak je niektor\u00e9 pole nejasn\u00e9, pozrite si dokument\u00e1ciu." diff --git a/homeassistant/components/forked_daapd/translations/de.json b/homeassistant/components/forked_daapd/translations/de.json index 3754d70f449..21adbe05da8 100644 --- a/homeassistant/components/forked_daapd/translations/de.json +++ b/homeassistant/components/forked_daapd/translations/de.json @@ -32,9 +32,9 @@ "librespot_java_port": "Port f\u00fcr librespot-java pipe control (falls verwendet)", "max_playlists": "Maximale Anzahl der als Quellen verwendeten Wiedergabelisten", "tts_pause_time": "Sekunden bis zur Pause vor und nach der TTS", - "tts_volume": "TTS-Lautst\u00e4rke (Float im Bereich [0,1])" + "tts_volume": "TTS-Lautst\u00e4rke (Flie\u00dfkommazahl im Bereich [0,1])" }, - "description": "Lege verschiedene Optionen f\u00fcr die Owntone-Integration fest.", + "description": "Lege verschiedene Optionen f\u00fcr die Owntone Integration fest.", "title": "Konfigurieren der Owntone Optionen" } } diff --git a/homeassistant/components/forked_daapd/translations/sk.json b/homeassistant/components/forked_daapd/translations/sk.json index 950f5c33621..ecf21ec9e25 100644 --- a/homeassistant/components/forked_daapd/translations/sk.json +++ b/homeassistant/components/forked_daapd/translations/sk.json @@ -1,21 +1,41 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "not_forked_daapd": "Zariadenie nie je serverom Owntone." }, "error": { + "forbidden": "Nie je mo\u017en\u00e9 sa pripoji\u0165. Skontrolujte, pros\u00edm, opr\u00e1vnenia siete Owntone.", "unknown_error": "Neo\u010dak\u00e1van\u00e1 chyba", + "websocket_not_enabled": "Webov\u00fd soket servera Owntone nie je povolen\u00fd.", "wrong_host_or_port": "Ned\u00e1 sa pripoji\u0165. Skontrolujte hostite\u013ea a port.", - "wrong_password": "Nespr\u00e1vne heslo." + "wrong_password": "Nespr\u00e1vne heslo.", + "wrong_server_type": "Integr\u00e1cia so slu\u017ebou Owntone vy\u017eaduje server Owntone s verziou >= 27.0." }, "flow_title": "{name} ({host})", "step": { "user": { "data": { "host": "Hostite\u013e", + "name": "Priate\u013esk\u00e9 meno", "password": "Heslo API (ak heslo nem\u00e1te, nechajte pr\u00e1zdne)", "port": "API port" - } + }, + "title": "Nastavenie zariadenia Owntone" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "librespot_java_port": "Port pre librespot-java pipe control (ak sa pou\u017e\u00edva)", + "max_playlists": "Maxim\u00e1lny po\u010det zoznamov skladieb pou\u017eit\u00fdch ako zdroje", + "tts_pause_time": "Sekundy na pauzu pred a po TTS", + "tts_volume": "Objem TTS (pohybuje sa v rozsahu [0,1])" + }, + "description": "Nastavenie r\u00f4znych mo\u017enost\u00ed pre integr\u00e1ciu funkcie Owntone.", + "title": "Konfigur\u00e1cia mo\u017enost\u00ed funkcie Owntone" } } } diff --git a/homeassistant/components/foscam/camera.py b/homeassistant/components/foscam/camera.py index c0b6661a392..fe11b056880 100644 --- a/homeassistant/components/foscam/camera.py +++ b/homeassistant/components/foscam/camera.py @@ -117,7 +117,10 @@ class HassFoscamCamera(Camera): if ret == -3: LOGGER.info( - "Can't get motion detection status, camera %s configured with non-admin user", + ( + "Can't get motion detection status, camera %s configured with" + " non-admin user" + ), self.name, ) @@ -156,7 +159,10 @@ class HassFoscamCamera(Camera): if ret != 0: if ret == -3: LOGGER.info( - "Can't set motion detection status, camera %s configured with non-admin user", + ( + "Can't set motion detection status, camera %s configured" + " with non-admin user" + ), self.name, ) return @@ -164,7 +170,10 @@ class HassFoscamCamera(Camera): self._attr_motion_detection_enabled = True except TypeError: LOGGER.debug( - "Failed enabling motion detection on '%s'. Is it supported by the device?", + ( + "Failed enabling motion detection on '%s'. Is it supported by the" + " device?" + ), self.name, ) @@ -176,7 +185,10 @@ class HassFoscamCamera(Camera): if ret != 0: if ret == -3: LOGGER.info( - "Can't set motion detection status, camera %s configured with non-admin user", + ( + "Can't set motion detection status, camera %s configured" + " with non-admin user" + ), self.name, ) return @@ -184,7 +196,10 @@ class HassFoscamCamera(Camera): self._attr_motion_detection_enabled = False except TypeError: LOGGER.debug( - "Failed disabling motion detection on '%s'. Is it supported by the device?", + ( + "Failed disabling motion detection on '%s'. Is it supported by the" + " device?" + ), self.name, ) diff --git a/homeassistant/components/foscam/translations/de.json b/homeassistant/components/foscam/translations/de.json index 30d331848a4..788cc2f8b35 100644 --- a/homeassistant/components/foscam/translations/de.json +++ b/homeassistant/components/foscam/translations/de.json @@ -15,7 +15,7 @@ "host": "Host", "password": "Passwort", "port": "Port", - "rtsp_port": "RTSP-Port", + "rtsp_port": "RTSP Port", "stream": "Stream", "username": "Benutzername" } diff --git a/homeassistant/components/foscam/translations/pt.json b/homeassistant/components/foscam/translations/pt.json index f1afd77d051..3b2dab2ee2d 100644 --- a/homeassistant/components/foscam/translations/pt.json +++ b/homeassistant/components/foscam/translations/pt.json @@ -4,13 +4,13 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida" }, "step": { "user": { "data": { - "host": "Servidor", + "host": "Endere\u00e7o", "password": "Palavra-passe" } } diff --git a/homeassistant/components/foscam/translations/sk.json b/homeassistant/components/foscam/translations/sk.json index 701645cc48c..86e3b8408e4 100644 --- a/homeassistant/components/foscam/translations/sk.json +++ b/homeassistant/components/foscam/translations/sk.json @@ -16,6 +16,7 @@ "password": "Heslo", "port": "Port", "rtsp_port": "RTSP port", + "stream": "Stream", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } diff --git a/homeassistant/components/freebox/const.py b/homeassistant/components/freebox/const.py index 65e5576f9d7..32cf407f2a9 100644 --- a/homeassistant/components/freebox/const.py +++ b/homeassistant/components/freebox/const.py @@ -3,8 +3,7 @@ from __future__ import annotations import socket -from homeassistant.components.sensor import SensorEntityDescription -from homeassistant.const import DATA_RATE_KILOBYTES_PER_SECOND, PERCENTAGE, Platform +from homeassistant.const import Platform DOMAIN = "freebox" SERVICE_REBOOT = "reboot" @@ -26,38 +25,8 @@ STORAGE_KEY = DOMAIN STORAGE_VERSION = 1 -CONNECTION_SENSORS: tuple[SensorEntityDescription, ...] = ( - SensorEntityDescription( - key="rate_down", - name="Freebox download speed", - native_unit_of_measurement=DATA_RATE_KILOBYTES_PER_SECOND, - icon="mdi:download-network", - ), - SensorEntityDescription( - key="rate_up", - name="Freebox upload speed", - native_unit_of_measurement=DATA_RATE_KILOBYTES_PER_SECOND, - icon="mdi:upload-network", - ), -) -CONNECTION_SENSORS_KEYS: list[str] = [desc.key for desc in CONNECTION_SENSORS] +CONNECTION_SENSORS_KEYS = {"rate_down", "rate_up"} -CALL_SENSORS: tuple[SensorEntityDescription, ...] = ( - SensorEntityDescription( - key="missed", - name="Freebox missed calls", - icon="mdi:phone-missed", - ), -) - -DISK_PARTITION_SENSORS: tuple[SensorEntityDescription, ...] = ( - SensorEntityDescription( - key="partition_free_space", - name="free space", - native_unit_of_measurement=PERCENTAGE, - icon="mdi:harddisk", - ), -) # Icons DEVICE_ICONS = { diff --git a/homeassistant/components/freebox/sensor.py b/homeassistant/components/freebox/sensor.py index e2c42928e80..4d5ba490faf 100644 --- a/homeassistant/components/freebox/sensor.py +++ b/homeassistant/components/freebox/sensor.py @@ -10,18 +10,52 @@ from homeassistant.components.sensor import ( SensorEntityDescription, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import DATA_RATE_KILOBYTES_PER_SECOND, TEMP_CELSIUS +from homeassistant.const import PERCENTAGE, UnitOfDataRate, UnitOfTemperature from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback import homeassistant.util.dt as dt_util -from .const import CALL_SENSORS, CONNECTION_SENSORS, DISK_PARTITION_SENSORS, DOMAIN +from .const import DOMAIN from .router import FreeboxRouter _LOGGER = logging.getLogger(__name__) +CONNECTION_SENSORS: tuple[SensorEntityDescription, ...] = ( + SensorEntityDescription( + key="rate_down", + name="Freebox download speed", + device_class=SensorDeviceClass.DATA_RATE, + native_unit_of_measurement=UnitOfDataRate.KILOBYTES_PER_SECOND, + icon="mdi:download-network", + ), + SensorEntityDescription( + key="rate_up", + name="Freebox upload speed", + device_class=SensorDeviceClass.DATA_RATE, + native_unit_of_measurement=UnitOfDataRate.KILOBYTES_PER_SECOND, + icon="mdi:upload-network", + ), +) + +CALL_SENSORS: tuple[SensorEntityDescription, ...] = ( + SensorEntityDescription( + key="missed", + name="Freebox missed calls", + icon="mdi:phone-missed", + ), +) + +DISK_PARTITION_SENSORS: tuple[SensorEntityDescription, ...] = ( + SensorEntityDescription( + key="partition_free_space", + name="free space", + native_unit_of_measurement=PERCENTAGE, + icon="mdi:harddisk", + ), +) + async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback @@ -42,7 +76,7 @@ async def async_setup_entry( SensorEntityDescription( key=sensor_name, name=f"Freebox {sensor_name}", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, ), ) @@ -84,7 +118,7 @@ class FreeboxSensor(SensorEntity): def async_update_state(self) -> None: """Update the Freebox sensor.""" state = self._router.sensors[self.entity_description.key] - if self.native_unit_of_measurement == DATA_RATE_KILOBYTES_PER_SECOND: + if self.native_unit_of_measurement == UnitOfDataRate.KILOBYTES_PER_SECOND: self._attr_native_value = round(state / 1000, 2) else: self._attr_native_value = state diff --git a/homeassistant/components/freebox/switch.py b/homeassistant/components/freebox/switch.py index 9bef539bfd8..41edc3f02c9 100644 --- a/homeassistant/components/freebox/switch.py +++ b/homeassistant/components/freebox/switch.py @@ -57,7 +57,8 @@ class FreeboxSwitch(SwitchEntity): await self._router.wifi.set_global_config({"enabled": enabled}) except InsufficientPermissionsError: _LOGGER.warning( - "Home Assistant does not have permissions to modify the Freebox settings. Please refer to documentation" + "Home Assistant does not have permissions to modify the Freebox" + " settings. Please refer to documentation" ) async def async_turn_on(self, **kwargs: Any) -> None: diff --git a/homeassistant/components/freebox/translations/pt.json b/homeassistant/components/freebox/translations/pt.json index 7eacd09c9d9..daf080bc643 100644 --- a/homeassistant/components/freebox/translations/pt.json +++ b/homeassistant/components/freebox/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "Servidor j\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "register_failed": "Falha no registo, por favor tente novamente", "unknown": "Erro inesperado" }, diff --git a/homeassistant/components/freebox/translations/sk.json b/homeassistant/components/freebox/translations/sk.json index 41da4a04be1..f2d872d321b 100644 --- a/homeassistant/components/freebox/translations/sk.json +++ b/homeassistant/components/freebox/translations/sk.json @@ -9,6 +9,10 @@ "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { + "link": { + "description": "Kliknite na \u201eOdosla\u0165\u201c a potom sa dotknite \u0161\u00edpky doprava na smerova\u010di a zaregistrujte Freebox s Home Assistant. \n\n ![Umiestnenie tla\u010didla na smerova\u010di](/static/images/config_freebox.png)", + "title": "Prepoji\u0165 router Freebox" + }, "user": { "data": { "host": "Hostite\u013e", diff --git a/homeassistant/components/freedompro/climate.py b/homeassistant/components/freedompro/climate.py index 0b5f147c141..0ec08f0fdd0 100644 --- a/homeassistant/components/freedompro/climate.py +++ b/homeassistant/components/freedompro/climate.py @@ -15,7 +15,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, CONF_API_KEY, TEMP_CELSIUS +from homeassistant.const import ATTR_TEMPERATURE, CONF_API_KEY, UnitOfTemperature from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import aiohttp_client from homeassistant.helpers.entity import DeviceInfo @@ -61,7 +61,7 @@ class Device(CoordinatorEntity[FreedomproDataUpdateCoordinator], ClimateEntity): """Representation of an Freedompro climate.""" _attr_hvac_modes = SUPPORTED_HVAC_MODES - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS def __init__( self, diff --git a/homeassistant/components/freedompro/sensor.py b/homeassistant/components/freedompro/sensor.py index c5dc2a26bd0..286a528013a 100644 --- a/homeassistant/components/freedompro/sensor.py +++ b/homeassistant/components/freedompro/sensor.py @@ -7,7 +7,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import LIGHT_LUX, PERCENTAGE, TEMP_CELSIUS +from homeassistant.const import LIGHT_LUX, PERCENTAGE, UnitOfTemperature from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -27,7 +27,7 @@ STATE_CLASS_MAP = { "lightSensor": None, } UNIT_MAP = { - "temperatureSensor": TEMP_CELSIUS, + "temperatureSensor": UnitOfTemperature.CELSIUS, "humiditySensor": PERCENTAGE, "lightSensor": LIGHT_LUX, } diff --git a/homeassistant/components/freedompro/translations/ko.json b/homeassistant/components/freedompro/translations/ko.json new file mode 100644 index 00000000000..71b96fd3a8c --- /dev/null +++ b/homeassistant/components/freedompro/translations/ko.json @@ -0,0 +1,18 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "api_key": "API \ud0a4" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/freedompro/translations/sk.json b/homeassistant/components/freedompro/translations/sk.json index 6a9b7374d51..776a5b8e5f9 100644 --- a/homeassistant/components/freedompro/translations/sk.json +++ b/homeassistant/components/freedompro/translations/sk.json @@ -12,7 +12,8 @@ "data": { "api_key": "API k\u013e\u00fa\u010d" }, - "description": "Zadajte k\u013e\u00fa\u010d API z\u00edskan\u00fd z https://home.freedompro.eu" + "description": "Zadajte k\u013e\u00fa\u010d API z\u00edskan\u00fd z https://home.freedompro.eu", + "title": "K\u013e\u00fa\u010d API Freedompro" } } } diff --git a/homeassistant/components/fritz/common.py b/homeassistant/components/fritz/common.py index 14e0f154374..94053f47284 100644 --- a/homeassistant/components/fritz/common.py +++ b/homeassistant/components/fritz/common.py @@ -136,7 +136,7 @@ class HostInfo(TypedDict): status: bool -class FritzBoxTools(update_coordinator.DataUpdateCoordinator): +class FritzBoxTools(update_coordinator.DataUpdateCoordinator[None]): """FritzBoxTools class.""" def __init__( @@ -328,7 +328,10 @@ class FritzBoxTools(update_coordinator.DataUpdateCoordinator): ).get("NewDisallow") except FRITZ_EXCEPTIONS as ex: _LOGGER.debug( - "could not get WAN access rule for client device with IP '%s', error: %s", + ( + "could not get WAN access rule for client device with IP '%s'," + " error: %s" + ), ip_address, ex, ) @@ -574,21 +577,24 @@ class FritzBoxTools(update_coordinator.DataUpdateCoordinator): try: if service_call.service == SERVICE_REBOOT: _LOGGER.warning( - 'Service "fritz.reboot" is deprecated, please use the corresponding button entity instead' + 'Service "fritz.reboot" is deprecated, please use the corresponding' + " button entity instead" ) await self.async_trigger_reboot() return if service_call.service == SERVICE_RECONNECT: _LOGGER.warning( - 'Service "fritz.reconnect" is deprecated, please use the corresponding button entity instead' + 'Service "fritz.reconnect" is deprecated, please use the' + " corresponding button entity instead" ) await self.async_trigger_reconnect() return if service_call.service == SERVICE_CLEANUP: _LOGGER.warning( - 'Service "fritz.cleanup" is deprecated, please use the corresponding button entity instead' + 'Service "fritz.cleanup" is deprecated, please use the' + " corresponding button entity instead" ) await self.async_trigger_cleanup(config_entry) return @@ -634,7 +640,10 @@ class AvmWrapper(FritzBoxTools): return result except FritzSecurityError: _LOGGER.error( - "Authorization Error: Please check the provided credentials and verify that you can log into the web interface", + ( + "Authorization Error: Please check the provided credentials and" + " verify that you can log into the web interface" + ), exc_info=True, ) except FRITZ_EXCEPTIONS: @@ -646,7 +655,10 @@ class AvmWrapper(FritzBoxTools): ) except FritzConnectionException: _LOGGER.error( - "Connection Error: Please check the device is properly configured for remote login", + ( + "Connection Error: Please check the device is properly configured" + " for remote login" + ), exc_info=True, ) return {} diff --git a/homeassistant/components/fritz/sensor.py b/homeassistant/components/fritz/sensor.py index 821a0000a5e..842331229e7 100644 --- a/homeassistant/components/fritz/sensor.py +++ b/homeassistant/components/fritz/sensor.py @@ -18,10 +18,9 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - DATA_GIGABYTES, - DATA_RATE_KILOBITS_PER_SECOND, - DATA_RATE_KILOBYTES_PER_SECOND, SIGNAL_STRENGTH_DECIBELS, + UnitOfDataRate, + UnitOfInformation, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory @@ -186,7 +185,8 @@ SENSOR_TYPES: tuple[FritzSensorEntityDescription, ...] = ( key="kb_s_sent", name="Upload Throughput", state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=DATA_RATE_KILOBYTES_PER_SECOND, + native_unit_of_measurement=UnitOfDataRate.KILOBYTES_PER_SECOND, + device_class=SensorDeviceClass.DATA_RATE, icon="mdi:upload", value_fn=_retrieve_kb_s_sent_state, ), @@ -194,14 +194,16 @@ SENSOR_TYPES: tuple[FritzSensorEntityDescription, ...] = ( key="kb_s_received", name="Download Throughput", state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=DATA_RATE_KILOBYTES_PER_SECOND, + native_unit_of_measurement=UnitOfDataRate.KILOBYTES_PER_SECOND, + device_class=SensorDeviceClass.DATA_RATE, icon="mdi:download", value_fn=_retrieve_kb_s_received_state, ), FritzSensorEntityDescription( key="max_kb_s_sent", name="Max Connection Upload Throughput", - native_unit_of_measurement=DATA_RATE_KILOBITS_PER_SECOND, + native_unit_of_measurement=UnitOfDataRate.KILOBITS_PER_SECOND, + device_class=SensorDeviceClass.DATA_RATE, icon="mdi:upload", entity_category=EntityCategory.DIAGNOSTIC, value_fn=_retrieve_max_kb_s_sent_state, @@ -209,7 +211,8 @@ SENSOR_TYPES: tuple[FritzSensorEntityDescription, ...] = ( FritzSensorEntityDescription( key="max_kb_s_received", name="Max Connection Download Throughput", - native_unit_of_measurement=DATA_RATE_KILOBITS_PER_SECOND, + native_unit_of_measurement=UnitOfDataRate.KILOBITS_PER_SECOND, + device_class=SensorDeviceClass.DATA_RATE, icon="mdi:download", entity_category=EntityCategory.DIAGNOSTIC, value_fn=_retrieve_max_kb_s_received_state, @@ -218,7 +221,8 @@ SENSOR_TYPES: tuple[FritzSensorEntityDescription, ...] = ( key="gb_sent", name="GB sent", state_class=SensorStateClass.TOTAL_INCREASING, - native_unit_of_measurement=DATA_GIGABYTES, + native_unit_of_measurement=UnitOfInformation.GIGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:upload", value_fn=_retrieve_gb_sent_state, ), @@ -226,21 +230,24 @@ SENSOR_TYPES: tuple[FritzSensorEntityDescription, ...] = ( key="gb_received", name="GB received", state_class=SensorStateClass.TOTAL_INCREASING, - native_unit_of_measurement=DATA_GIGABYTES, + native_unit_of_measurement=UnitOfInformation.GIGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:download", value_fn=_retrieve_gb_received_state, ), FritzSensorEntityDescription( key="link_kb_s_sent", name="Link Upload Throughput", - native_unit_of_measurement=DATA_RATE_KILOBITS_PER_SECOND, + native_unit_of_measurement=UnitOfDataRate.KILOBITS_PER_SECOND, + device_class=SensorDeviceClass.DATA_RATE, icon="mdi:upload", value_fn=_retrieve_link_kb_s_sent_state, ), FritzSensorEntityDescription( key="link_kb_s_received", name="Link Download Throughput", - native_unit_of_measurement=DATA_RATE_KILOBITS_PER_SECOND, + native_unit_of_measurement=UnitOfDataRate.KILOBITS_PER_SECOND, + device_class=SensorDeviceClass.DATA_RATE, icon="mdi:download", value_fn=_retrieve_link_kb_s_received_state, ), diff --git a/homeassistant/components/fritz/services.py b/homeassistant/components/fritz/services.py index c4e7de8df5e..e0b41d0e87e 100644 --- a/homeassistant/components/fritz/services.py +++ b/homeassistant/components/fritz/services.py @@ -54,7 +54,8 @@ async def async_setup_services(hass: HomeAssistant) -> None: ) ): raise HomeAssistantError( - f"Failed to call service '{service_call.service}'. Config entry for target not found" + f"Failed to call service '{service_call.service}'. Config entry for" + " target not found" ) for entry_id in fritzbox_entry_ids: diff --git a/homeassistant/components/fritz/strings.json b/homeassistant/components/fritz/strings.json index 9d6628aca0e..2d76479f2b9 100644 --- a/homeassistant/components/fritz/strings.json +++ b/homeassistant/components/fritz/strings.json @@ -3,8 +3,8 @@ "flow_title": "{name}", "step": { "confirm": { - "title": "Setup FRITZ!Box Tools", - "description": "Discovered FRITZ!Box: {name}\n\nSetup FRITZ!Box Tools to control your {name}", + "title": "Set up FRITZ!Box Tools", + "description": "Discovered FRITZ!Box: {name}\n\nSet up FRITZ!Box Tools to control your {name}", "data": { "username": "[%key:common::config_flow::data::username%]", "password": "[%key:common::config_flow::data::password%]" @@ -19,8 +19,8 @@ } }, "user": { - "title": "Setup FRITZ!Box Tools", - "description": "Setup FRITZ!Box Tools to control your FRITZ!Box.\nMinimum needed: username, password.", + "title": "Set up FRITZ!Box Tools", + "description": "Set up FRITZ!Box Tools to control your FRITZ!Box.\nMinimum needed: username, password.", "data": { "host": "[%key:common::config_flow::data::host%]", "port": "[%key:common::config_flow::data::port%]", diff --git a/homeassistant/components/fritz/translations/de.json b/homeassistant/components/fritz/translations/de.json index fc5e522e18c..3cf7d0f54b0 100644 --- a/homeassistant/components/fritz/translations/de.json +++ b/homeassistant/components/fritz/translations/de.json @@ -39,7 +39,7 @@ "username": "Benutzername" }, "description": "FRITZ!Box Tools einrichten, um deine FRITZ!Box zu steuern.\nMindestens erforderlich: Benutzername, Passwort.", - "title": "Setup FRITZ!Box Tools" + "title": "FRITZ!Box Tools einrichten" } } }, diff --git a/homeassistant/components/fritz/translations/en.json b/homeassistant/components/fritz/translations/en.json index e7ee1568684..3ee3f46293d 100644 --- a/homeassistant/components/fritz/translations/en.json +++ b/homeassistant/components/fritz/translations/en.json @@ -20,8 +20,8 @@ "password": "Password", "username": "Username" }, - "description": "Discovered FRITZ!Box: {name}\n\nSetup FRITZ!Box Tools to control your {name}", - "title": "Setup FRITZ!Box Tools" + "description": "Discovered FRITZ!Box: {name}\n\nSet up FRITZ!Box Tools to control your {name}", + "title": "Set up FRITZ!Box Tools" }, "reauth_confirm": { "data": { @@ -38,8 +38,8 @@ "port": "Port", "username": "Username" }, - "description": "Setup FRITZ!Box Tools to control your FRITZ!Box.\nMinimum needed: username, password.", - "title": "Setup FRITZ!Box Tools" + "description": "Set up FRITZ!Box Tools to control your FRITZ!Box.\nMinimum needed: username, password.", + "title": "Set up FRITZ!Box Tools" } } }, diff --git a/homeassistant/components/fritz/translations/it.json b/homeassistant/components/fritz/translations/it.json index bb18e38d1eb..2d7adb0d1bc 100644 --- a/homeassistant/components/fritz/translations/it.json +++ b/homeassistant/components/fritz/translations/it.json @@ -20,8 +20,8 @@ "password": "Password", "username": "Nome utente" }, - "description": "FRITZ! Box rilevato: {name} \n\nConfigura gli strumenti del FRITZ! Box per controllare il tuo {name}", - "title": "Configura gli strumenti del FRITZ!Box" + "description": "FRITZ!Box rilevato: {name} \n\nConfigura gli strumenti di FRITZ!Box per controllare il tuo {name}", + "title": "Configura gli strumenti di FRITZ!Box" }, "reauth_confirm": { "data": { @@ -38,8 +38,8 @@ "port": "Porta", "username": "Nome utente" }, - "description": "Configura gli strumenti FRITZ!Box per controllare il tuo FRITZ! Box.\nMinimo necessario: nome utente, password.", - "title": "Configura gli strumenti del FRITZ!Box" + "description": "Configura gli strumenti di FRITZ!Box per controllare il tuo FRITZ!Box.\nMinimo necessario: nome utente, password.", + "title": "Configura gli strumenti di FRITZ!Box" } } }, diff --git a/homeassistant/components/fritz/translations/no.json b/homeassistant/components/fritz/translations/no.json index 7e773022475..f6561f6d05e 100644 --- a/homeassistant/components/fritz/translations/no.json +++ b/homeassistant/components/fritz/translations/no.json @@ -20,8 +20,8 @@ "password": "Passord", "username": "Brukernavn" }, - "description": "Oppdaget FRITZ!Box: {name} \n\n Konfigurer FRITZ!Box-verkt\u00f8y for \u00e5 kontrollere {name}", - "title": "Sett opp FRITZ!Box verkt\u00f8y" + "description": "Oppdaget FRITZ!Box: {name} \n\n Konfigurer FRITZ!Box Tools for \u00e5 kontrollere {name}", + "title": "Sett opp FRITZ!Box Tools" }, "reauth_confirm": { "data": { @@ -38,8 +38,8 @@ "port": "Port", "username": "Brukernavn" }, - "description": "Sett opp FRITZ!Box verkt\u00f8y for \u00e5 kontrollere fritz! Boksen.\nMinimum n\u00f8dvendig: brukernavn, passord.", - "title": "Sett opp FRITZ!Box verkt\u00f8y" + "description": "Sett opp FRITZ!Box Tools for \u00e5 kontrollere FRITZ!Boxen.\n Minimum n\u00f8dvendig: brukernavn, passord.", + "title": "Sett opp FRITZ!Box Tools" } } }, diff --git a/homeassistant/components/fritz/translations/sk.json b/homeassistant/components/fritz/translations/sk.json index 2dae4dc6f85..d362deef2d5 100644 --- a/homeassistant/components/fritz/translations/sk.json +++ b/homeassistant/components/fritz/translations/sk.json @@ -38,6 +38,7 @@ "port": "Port", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" }, + "description": "Nastavte n\u00e1stroje FRITZ!Box na ovl\u00e1danie v\u00e1\u0161ho zariadenia FRITZ!Box.\n Potrebn\u00e9 minimum: u\u017e\u00edvate\u013esk\u00e9 meno, heslo.", "title": "Nastavte n\u00e1stroje FRITZ!Box" } } @@ -46,6 +47,7 @@ "step": { "init": { "data": { + "consider_home": "Sekundy na \u010dakanie, zariadenia \u201edoma\u201c", "old_discovery": "Povoli\u0165 star\u00fa met\u00f3du zis\u0165ovania" } } diff --git a/homeassistant/components/fritzbox/__init__.py b/homeassistant/components/fritzbox/__init__.py index 43bd0bfeeb0..fc65ed96459 100644 --- a/homeassistant/components/fritzbox/__init__.py +++ b/homeassistant/components/fritzbox/__init__.py @@ -14,7 +14,7 @@ from homeassistant.const import ( CONF_PASSWORD, CONF_USERNAME, EVENT_HOMEASSISTANT_STOP, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import Event, HomeAssistant from homeassistant.exceptions import ConfigEntryAuthFailed @@ -62,7 +62,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: def _update_unique_id(entry: RegistryEntry) -> dict[str, str] | None: """Update unique ID of entity entry.""" if ( - entry.unit_of_measurement == TEMP_CELSIUS + entry.unit_of_measurement == UnitOfTemperature.CELSIUS and "_temperature" not in entry.unique_id ): new_unique_id = f"{entry.unique_id}_temperature" diff --git a/homeassistant/components/fritzbox/climate.py b/homeassistant/components/fritzbox/climate.py index b1898c41cc7..31cdac47ec2 100644 --- a/homeassistant/components/fritzbox/climate.py +++ b/homeassistant/components/fritzbox/climate.py @@ -16,7 +16,7 @@ from homeassistant.const import ( ATTR_BATTERY_LEVEL, ATTR_TEMPERATURE, PRECISION_HALVES, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -70,7 +70,7 @@ class FritzboxThermostat(FritzBoxDeviceEntity, ClimateEntity): _attr_supported_features = ( ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE ) - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS @property def current_temperature(self) -> float: diff --git a/homeassistant/components/fritzbox/light.py b/homeassistant/components/fritzbox/light.py index 03eb9eb9d78..24431f78aca 100644 --- a/homeassistant/components/fritzbox/light.py +++ b/homeassistant/components/fritzbox/light.py @@ -149,7 +149,8 @@ class FritzboxLight(FritzBoxDeviceEntity, LightEntity): if err.response.status_code != 400: raise LOGGER.debug( - "fritzbox does not support method 'setunmappedcolor', fallback to 'setcolor'" + "fritzbox does not support method 'setunmappedcolor', fallback to" + " 'setcolor'" ) # find supported hs values closest to what user selected hue = min( diff --git a/homeassistant/components/fritzbox/sensor.py b/homeassistant/components/fritzbox/sensor.py index 9d68821fdca..ef068e3af2a 100644 --- a/homeassistant/components/fritzbox/sensor.py +++ b/homeassistant/components/fritzbox/sensor.py @@ -17,12 +17,12 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - ELECTRIC_CURRENT_AMPERE, - ELECTRIC_POTENTIAL_VOLT, - ENERGY_KILO_WATT_HOUR, PERCENTAGE, - POWER_WATT, - TEMP_CELSIUS, + UnitOfElectricCurrent, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfPower, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory @@ -103,7 +103,7 @@ SENSOR_TYPES: Final[tuple[FritzSensorEntityDescription, ...]] = ( FritzSensorEntityDescription( key="temperature", name="Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, @@ -131,7 +131,7 @@ SENSOR_TYPES: Final[tuple[FritzSensorEntityDescription, ...]] = ( FritzSensorEntityDescription( key="power_consumption", name="Power Consumption", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, suitable=lambda device: device.has_powermeter, # type: ignore[no-any-return] @@ -140,7 +140,7 @@ SENSOR_TYPES: Final[tuple[FritzSensorEntityDescription, ...]] = ( FritzSensorEntityDescription( key="voltage", name="Voltage", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, suitable=lambda device: device.has_powermeter, # type: ignore[no-any-return] @@ -149,7 +149,7 @@ SENSOR_TYPES: Final[tuple[FritzSensorEntityDescription, ...]] = ( FritzSensorEntityDescription( key="electric_current", name="Electric Current", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, suitable=lambda device: device.has_powermeter, # type: ignore[no-any-return] @@ -158,7 +158,7 @@ SENSOR_TYPES: Final[tuple[FritzSensorEntityDescription, ...]] = ( FritzSensorEntityDescription( key="total_energy", name="Total Energy", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, suitable=lambda device: device.has_powermeter, # type: ignore[no-any-return] @@ -168,7 +168,7 @@ SENSOR_TYPES: Final[tuple[FritzSensorEntityDescription, ...]] = ( FritzSensorEntityDescription( key="comfort_temperature", name="Comfort Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, suitable=suitable_comfort_temperature, native_value=lambda device: device.comfort_temperature, # type: ignore[no-any-return] @@ -176,7 +176,7 @@ SENSOR_TYPES: Final[tuple[FritzSensorEntityDescription, ...]] = ( FritzSensorEntityDescription( key="eco_temperature", name="Eco Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, suitable=suitable_eco_temperature, native_value=lambda device: device.eco_temperature, # type: ignore[no-any-return] @@ -184,7 +184,7 @@ SENSOR_TYPES: Final[tuple[FritzSensorEntityDescription, ...]] = ( FritzSensorEntityDescription( key="nextchange_temperature", name="Next Scheduled Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, suitable=suitable_nextchange_temperature, native_value=lambda device: device.nextchange_temperature, # type: ignore[no-any-return] diff --git a/homeassistant/components/fritzbox/translations/pt.json b/homeassistant/components/fritzbox/translations/pt.json index 9d2eadee61c..a75ef4b59ff 100644 --- a/homeassistant/components/fritzbox/translations/pt.json +++ b/homeassistant/components/fritzbox/translations/pt.json @@ -17,7 +17,7 @@ }, "user": { "data": { - "host": "Servidor", + "host": "Endere\u00e7o", "password": "Palavra-passe", "username": "Nome de Utilizador" } diff --git a/homeassistant/components/fritzbox_callmonitor/__init__.py b/homeassistant/components/fritzbox_callmonitor/__init__.py index a47c3c24755..534f3b0c8fc 100644 --- a/homeassistant/components/fritzbox_callmonitor/__init__.py +++ b/homeassistant/components/fritzbox_callmonitor/__init__.py @@ -36,7 +36,10 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b await hass.async_add_executor_job(fritzbox_phonebook.init_phonebook) except FritzSecurityError as ex: _LOGGER.error( - "User has insufficient permissions to access AVM FRITZ!Box settings and its phonebooks: %s", + ( + "User has insufficient permissions to access AVM FRITZ!Box settings and" + " its phonebooks: %s" + ), ex, ) return False diff --git a/homeassistant/components/fritzbox_callmonitor/translations/sk.json b/homeassistant/components/fritzbox_callmonitor/translations/sk.json index 632d11fbc78..29670bfee7d 100644 --- a/homeassistant/components/fritzbox_callmonitor/translations/sk.json +++ b/homeassistant/components/fritzbox_callmonitor/translations/sk.json @@ -2,6 +2,7 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "insufficient_permissions": "Pou\u017e\u00edvate\u013e nem\u00e1 dostato\u010dn\u00e9 opr\u00e1vnenia na pr\u00edstup k nastaveniam AVM FRITZ!Box a jeho telef\u00f3nnym zoznamom.", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" }, "error": { @@ -23,5 +24,18 @@ } } } + }, + "options": { + "error": { + "malformed_prefixes": "Predpony maj\u00fa nespr\u00e1vny tvar, skontrolujte ich form\u00e1t." + }, + "step": { + "init": { + "data": { + "prefixes": "Predpony (zoznam oddelen\u00fd \u010diarkami)" + }, + "title": "Konfigurova\u0165 predpony" + } + } } } \ No newline at end of file diff --git a/homeassistant/components/fronius/sensor.py b/homeassistant/components/fronius/sensor.py index 35881225b68..53342864da7 100644 --- a/homeassistant/components/fronius/sensor.py +++ b/homeassistant/components/fronius/sensor.py @@ -11,15 +11,15 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - ELECTRIC_CURRENT_AMPERE, - ELECTRIC_POTENTIAL_VOLT, - ENERGY_WATT_HOUR, - FREQUENCY_HERTZ, PERCENTAGE, - POWER_VOLT_AMPERE, POWER_VOLT_AMPERE_REACTIVE, - POWER_WATT, - TEMP_CELSIUS, + UnitOfApparentPower, + UnitOfElectricCurrent, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfFrequency, + UnitOfPower, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import DeviceInfo, EntityCategory @@ -80,28 +80,28 @@ INVERTER_ENTITY_DESCRIPTIONS: list[SensorEntityDescription] = [ SensorEntityDescription( key="energy_day", name="Energy day", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), SensorEntityDescription( key="energy_year", name="Energy year", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), SensorEntityDescription( key="energy_total", name="Energy total", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), SensorEntityDescription( key="frequency_ac", name="Frequency AC", - native_unit_of_measurement=FREQUENCY_HERTZ, + native_unit_of_measurement=UnitOfFrequency.HERTZ, device_class=SensorDeviceClass.FREQUENCY, state_class=SensorStateClass.MEASUREMENT, entity_registry_enabled_default=False, @@ -109,14 +109,14 @@ INVERTER_ENTITY_DESCRIPTIONS: list[SensorEntityDescription] = [ SensorEntityDescription( key="current_ac", name="Current AC", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="current_dc", name="Current DC", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, icon="mdi:current-dc", @@ -124,7 +124,7 @@ INVERTER_ENTITY_DESCRIPTIONS: list[SensorEntityDescription] = [ SensorEntityDescription( key="current_dc_2", name="Current DC 2", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, icon="mdi:current-dc", @@ -132,14 +132,14 @@ INVERTER_ENTITY_DESCRIPTIONS: list[SensorEntityDescription] = [ SensorEntityDescription( key="power_ac", name="Power AC", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="voltage_ac", name="Voltage AC", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, entity_registry_enabled_default=False, @@ -147,7 +147,7 @@ INVERTER_ENTITY_DESCRIPTIONS: list[SensorEntityDescription] = [ SensorEntityDescription( key="voltage_dc", name="Voltage DC", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, icon="mdi:current-dc", @@ -155,7 +155,7 @@ INVERTER_ENTITY_DESCRIPTIONS: list[SensorEntityDescription] = [ SensorEntityDescription( key="voltage_dc_2", name="Voltage DC 2", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, icon="mdi:current-dc", @@ -215,7 +215,7 @@ METER_ENTITY_DESCRIPTIONS: list[SensorEntityDescription] = [ SensorEntityDescription( key="current_ac_phase_1", name="Current AC phase 1", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, entity_registry_enabled_default=False, @@ -223,7 +223,7 @@ METER_ENTITY_DESCRIPTIONS: list[SensorEntityDescription] = [ SensorEntityDescription( key="current_ac_phase_2", name="Current AC phase 2", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, entity_registry_enabled_default=False, @@ -231,7 +231,7 @@ METER_ENTITY_DESCRIPTIONS: list[SensorEntityDescription] = [ SensorEntityDescription( key="current_ac_phase_3", name="Current AC phase 3", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, entity_registry_enabled_default=False, @@ -255,7 +255,7 @@ METER_ENTITY_DESCRIPTIONS: list[SensorEntityDescription] = [ SensorEntityDescription( key="energy_real_ac_minus", name="Energy real AC minus", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, entity_registry_enabled_default=False, @@ -263,7 +263,7 @@ METER_ENTITY_DESCRIPTIONS: list[SensorEntityDescription] = [ SensorEntityDescription( key="energy_real_ac_plus", name="Energy real AC plus", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, entity_registry_enabled_default=False, @@ -271,21 +271,21 @@ METER_ENTITY_DESCRIPTIONS: list[SensorEntityDescription] = [ SensorEntityDescription( key="energy_real_consumed", name="Energy real consumed", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), SensorEntityDescription( key="energy_real_produced", name="Energy real produced", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), SensorEntityDescription( key="frequency_phase_average", name="Frequency phase average", - native_unit_of_measurement=FREQUENCY_HERTZ, + native_unit_of_measurement=UnitOfFrequency.HERTZ, device_class=SensorDeviceClass.FREQUENCY, state_class=SensorStateClass.MEASUREMENT, ), @@ -297,7 +297,7 @@ METER_ENTITY_DESCRIPTIONS: list[SensorEntityDescription] = [ SensorEntityDescription( key="power_apparent_phase_1", name="Power apparent phase 1", - native_unit_of_measurement=POWER_VOLT_AMPERE, + native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE, device_class=SensorDeviceClass.APPARENT_POWER, state_class=SensorStateClass.MEASUREMENT, icon="mdi:flash-outline", @@ -306,7 +306,7 @@ METER_ENTITY_DESCRIPTIONS: list[SensorEntityDescription] = [ SensorEntityDescription( key="power_apparent_phase_2", name="Power apparent phase 2", - native_unit_of_measurement=POWER_VOLT_AMPERE, + native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE, device_class=SensorDeviceClass.APPARENT_POWER, state_class=SensorStateClass.MEASUREMENT, icon="mdi:flash-outline", @@ -315,7 +315,7 @@ METER_ENTITY_DESCRIPTIONS: list[SensorEntityDescription] = [ SensorEntityDescription( key="power_apparent_phase_3", name="Power apparent phase 3", - native_unit_of_measurement=POWER_VOLT_AMPERE, + native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE, device_class=SensorDeviceClass.APPARENT_POWER, state_class=SensorStateClass.MEASUREMENT, icon="mdi:flash-outline", @@ -324,7 +324,7 @@ METER_ENTITY_DESCRIPTIONS: list[SensorEntityDescription] = [ SensorEntityDescription( key="power_apparent", name="Power apparent", - native_unit_of_measurement=POWER_VOLT_AMPERE, + native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE, device_class=SensorDeviceClass.APPARENT_POWER, state_class=SensorStateClass.MEASUREMENT, icon="mdi:flash-outline", @@ -333,28 +333,24 @@ METER_ENTITY_DESCRIPTIONS: list[SensorEntityDescription] = [ SensorEntityDescription( key="power_factor_phase_1", name="Power factor phase 1", - device_class=SensorDeviceClass.POWER_FACTOR, state_class=SensorStateClass.MEASUREMENT, entity_registry_enabled_default=False, ), SensorEntityDescription( key="power_factor_phase_2", name="Power factor phase 2", - device_class=SensorDeviceClass.POWER_FACTOR, state_class=SensorStateClass.MEASUREMENT, entity_registry_enabled_default=False, ), SensorEntityDescription( key="power_factor_phase_3", name="Power factor phase 3", - device_class=SensorDeviceClass.POWER_FACTOR, state_class=SensorStateClass.MEASUREMENT, entity_registry_enabled_default=False, ), SensorEntityDescription( key="power_factor", name="Power factor", - device_class=SensorDeviceClass.POWER_FACTOR, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( @@ -396,7 +392,7 @@ METER_ENTITY_DESCRIPTIONS: list[SensorEntityDescription] = [ SensorEntityDescription( key="power_real_phase_1", name="Power real phase 1", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, entity_registry_enabled_default=False, @@ -404,7 +400,7 @@ METER_ENTITY_DESCRIPTIONS: list[SensorEntityDescription] = [ SensorEntityDescription( key="power_real_phase_2", name="Power real phase 2", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, entity_registry_enabled_default=False, @@ -412,7 +408,7 @@ METER_ENTITY_DESCRIPTIONS: list[SensorEntityDescription] = [ SensorEntityDescription( key="power_real_phase_3", name="Power real phase 3", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, entity_registry_enabled_default=False, @@ -420,14 +416,14 @@ METER_ENTITY_DESCRIPTIONS: list[SensorEntityDescription] = [ SensorEntityDescription( key="power_real", name="Power real", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="voltage_ac_phase_1", name="Voltage AC phase 1", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, entity_registry_enabled_default=False, @@ -435,7 +431,7 @@ METER_ENTITY_DESCRIPTIONS: list[SensorEntityDescription] = [ SensorEntityDescription( key="voltage_ac_phase_2", name="Voltage AC phase 2", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, entity_registry_enabled_default=False, @@ -443,7 +439,7 @@ METER_ENTITY_DESCRIPTIONS: list[SensorEntityDescription] = [ SensorEntityDescription( key="voltage_ac_phase_3", name="Voltage AC phase 3", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, entity_registry_enabled_default=False, @@ -451,7 +447,7 @@ METER_ENTITY_DESCRIPTIONS: list[SensorEntityDescription] = [ SensorEntityDescription( key="voltage_ac_phase_to_phase_12", name="Voltage AC phase 1-2", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, entity_registry_enabled_default=False, @@ -459,7 +455,7 @@ METER_ENTITY_DESCRIPTIONS: list[SensorEntityDescription] = [ SensorEntityDescription( key="voltage_ac_phase_to_phase_23", name="Voltage AC phase 2-3", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, entity_registry_enabled_default=False, @@ -467,7 +463,7 @@ METER_ENTITY_DESCRIPTIONS: list[SensorEntityDescription] = [ SensorEntityDescription( key="voltage_ac_phase_to_phase_31", name="Voltage AC phase 3-1", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, entity_registry_enabled_default=False, @@ -478,21 +474,21 @@ OHMPILOT_ENTITY_DESCRIPTIONS: list[SensorEntityDescription] = [ SensorEntityDescription( key="energy_real_ac_consumed", name="Energy consumed", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), SensorEntityDescription( key="power_real_ac", name="Power", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="temperature_channel_1", name="Temperature channel 1", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), @@ -517,7 +513,7 @@ POWER_FLOW_ENTITY_DESCRIPTIONS: list[SensorEntityDescription] = [ SensorEntityDescription( key="energy_day", name="Energy day", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, entity_registry_enabled_default=False, @@ -525,7 +521,7 @@ POWER_FLOW_ENTITY_DESCRIPTIONS: list[SensorEntityDescription] = [ SensorEntityDescription( key="energy_year", name="Energy year", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, entity_registry_enabled_default=False, @@ -533,7 +529,7 @@ POWER_FLOW_ENTITY_DESCRIPTIONS: list[SensorEntityDescription] = [ SensorEntityDescription( key="energy_total", name="Energy total", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, entity_registry_enabled_default=False, @@ -546,28 +542,28 @@ POWER_FLOW_ENTITY_DESCRIPTIONS: list[SensorEntityDescription] = [ SensorEntityDescription( key="power_battery", name="Power battery", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="power_grid", name="Power grid", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="power_load", name="Power load", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="power_photovoltaics", name="Power photovoltaics", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), @@ -591,19 +587,19 @@ STORAGE_ENTITY_DESCRIPTIONS: list[SensorEntityDescription] = [ SensorEntityDescription( key="capacity_maximum", name="Capacity maximum", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, entity_category=EntityCategory.DIAGNOSTIC, ), SensorEntityDescription( key="capacity_designed", name="Capacity designed", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, entity_category=EntityCategory.DIAGNOSTIC, ), SensorEntityDescription( key="current_dc", name="Current DC", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, icon="mdi:current-dc", @@ -611,7 +607,7 @@ STORAGE_ENTITY_DESCRIPTIONS: list[SensorEntityDescription] = [ SensorEntityDescription( key="voltage_dc", name="Voltage DC", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, icon="mdi:current-dc", @@ -619,7 +615,7 @@ STORAGE_ENTITY_DESCRIPTIONS: list[SensorEntityDescription] = [ SensorEntityDescription( key="voltage_dc_maximum_cell", name="Voltage DC maximum cell", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, icon="mdi:current-dc", @@ -628,7 +624,7 @@ STORAGE_ENTITY_DESCRIPTIONS: list[SensorEntityDescription] = [ SensorEntityDescription( key="voltage_dc_minimum_cell", name="Voltage DC minimum cell", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, icon="mdi:current-dc", @@ -644,7 +640,7 @@ STORAGE_ENTITY_DESCRIPTIONS: list[SensorEntityDescription] = [ SensorEntityDescription( key="temperature_cell", name="Temperature cell", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), diff --git a/homeassistant/components/fronius/translations/de.json b/homeassistant/components/fronius/translations/de.json index eed094491a5..ec7a28f6b4a 100644 --- a/homeassistant/components/fronius/translations/de.json +++ b/homeassistant/components/fronius/translations/de.json @@ -17,7 +17,7 @@ "data": { "host": "Host" }, - "description": "Konfiguriere die IP-Adresse oder den lokalen Hostnamen deines Fronius-Ger\u00e4ts.", + "description": "Konfiguriere die IP-Adresse oder den lokalen Hostnamen deines Fronius Ger\u00e4ts.", "title": "Fronius SolarNet" } } diff --git a/homeassistant/components/fronius/translations/ko.json b/homeassistant/components/fronius/translations/ko.json index 5e3238d1be1..3f7da0621b7 100644 --- a/homeassistant/components/fronius/translations/ko.json +++ b/homeassistant/components/fronius/translations/ko.json @@ -1,8 +1,21 @@ { "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "invalid_host": "\ud638\uc2a4\ud2b8 \uc774\ub984 \ub610\ub294 IP \uc8fc\uc18c\uac00 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, "step": { "confirm_discovery": { "description": "{device} \ub97c \ud648\uc5b4\uc2dc\uc2a4\ud134\ud2b8\uc5d0 \ucd94\uac00\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" + }, + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8" + } } } } diff --git a/homeassistant/components/fronius/translations/pt.json b/homeassistant/components/fronius/translations/pt.json index f13cad90edc..5a5dfb4da46 100644 --- a/homeassistant/components/fronius/translations/pt.json +++ b/homeassistant/components/fronius/translations/pt.json @@ -1,12 +1,12 @@ { "config": { "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "user": { "data": { - "host": "Servidor" + "host": "Endere\u00e7o" } } } diff --git a/homeassistant/components/fronius/translations/sk.json b/homeassistant/components/fronius/translations/sk.json index 7ff40710d48..11c07669b63 100644 --- a/homeassistant/components/fronius/translations/sk.json +++ b/homeassistant/components/fronius/translations/sk.json @@ -17,7 +17,8 @@ "data": { "host": "Hostite\u013e" }, - "description": "Nakonfigurujte IP adresu alebo miestny n\u00e1zov hostite\u013ea v\u00e1\u0161ho zariadenia Fronius." + "description": "Nakonfigurujte IP adresu alebo miestny n\u00e1zov hostite\u013ea v\u00e1\u0161ho zariadenia Fronius.", + "title": "Fronius SolarNet" } } } diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index f5fe37c1819..eff8bc77a9a 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -146,7 +146,9 @@ class Manifest: MANIFEST_JSON = Manifest( { "background_color": "#FFFFFF", - "description": "Home automation platform that puts local control and privacy first.", + "description": ( + "Home automation platform that puts local control and privacy first." + ), "dir": "ltr", "display": "standalone", "icons": [ diff --git a/homeassistant/components/frontend/manifest.json b/homeassistant/components/frontend/manifest.json index 2b3962e2241..0091d5dcf98 100644 --- a/homeassistant/components/frontend/manifest.json +++ b/homeassistant/components/frontend/manifest.json @@ -2,7 +2,7 @@ "domain": "frontend", "name": "Home Assistant Frontend", "documentation": "https://www.home-assistant.io/integrations/frontend", - "requirements": ["home-assistant-frontend==20221213.1"], + "requirements": ["home-assistant-frontend==20230104.0"], "dependencies": [ "api", "auth", diff --git a/homeassistant/components/frontier_silicon/browse_media.py b/homeassistant/components/frontier_silicon/browse_media.py index 53deb399b87..7815b029735 100644 --- a/homeassistant/components/frontier_silicon/browse_media.py +++ b/homeassistant/components/frontier_silicon/browse_media.py @@ -87,7 +87,9 @@ async def browse_top_level(current_mode, afsapi: AFSAPI): title=name, media_class=MediaClass.DIRECTORY, media_content_type=MediaType.CHANNELS, - media_content_id=f"{current_mode or 'unknown'}/{top_level_media_content_id}", + media_content_id=( + f"{current_mode or 'unknown'}/{top_level_media_content_id}" + ), can_play=False, can_expand=True, ) diff --git a/homeassistant/components/fully_kiosk/config_flow.py b/homeassistant/components/fully_kiosk/config_flow.py index 5257030ecf0..785948f5632 100644 --- a/homeassistant/components/fully_kiosk/config_flow.py +++ b/homeassistant/components/fully_kiosk/config_flow.py @@ -41,10 +41,15 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): try: async with timeout(15): device_info = await fully.getDeviceInfo() - except (ClientConnectorError, FullyKioskError, asyncio.TimeoutError): + except ( + ClientConnectorError, + FullyKioskError, + asyncio.TimeoutError, + ) as error: + LOGGER.debug(error.args, exc_info=True) errors["base"] = "cannot_connect" - except Exception: # pylint: disable=broad-except - LOGGER.exception("Unexpected exception") + except Exception as error: # pylint: disable=broad-except + LOGGER.exception("Unexpected exception: %s", error) errors["base"] = "unknown" else: await self.async_set_unique_id(device_info["deviceID"]) diff --git a/homeassistant/components/fully_kiosk/coordinator.py b/homeassistant/components/fully_kiosk/coordinator.py index fbd08f8d2c5..4e35d614587 100644 --- a/homeassistant/components/fully_kiosk/coordinator.py +++ b/homeassistant/components/fully_kiosk/coordinator.py @@ -1,4 +1,4 @@ -"""Provides the The Fully Kiosk Browser DataUpdateCoordinator.""" +"""Provides the Fully Kiosk Browser DataUpdateCoordinator.""" import asyncio from typing import Any, cast diff --git a/homeassistant/components/fully_kiosk/manifest.json b/homeassistant/components/fully_kiosk/manifest.json index 5601cb074f0..ba45392ea5d 100644 --- a/homeassistant/components/fully_kiosk/manifest.json +++ b/homeassistant/components/fully_kiosk/manifest.json @@ -3,7 +3,7 @@ "name": "Fully Kiosk Browser", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/fullykiosk", - "requirements": ["python-fullykiosk==0.0.11"], + "requirements": ["python-fullykiosk==0.0.12"], "codeowners": ["@cgarwood"], "iot_class": "local_polling", "dhcp": [{ "registered_devices": true }] diff --git a/homeassistant/components/fully_kiosk/number.py b/homeassistant/components/fully_kiosk/number.py index d39f3f6391d..6e9dc424bb3 100644 --- a/homeassistant/components/fully_kiosk/number.py +++ b/homeassistant/components/fully_kiosk/number.py @@ -5,7 +5,7 @@ from contextlib import suppress from homeassistant.components.number import NumberEntity, NumberEntityDescription from homeassistant.config_entries import ConfigEntry -from homeassistant.const import TIME_SECONDS +from homeassistant.const import UnitOfTime from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -21,7 +21,7 @@ ENTITY_TYPES: tuple[NumberEntityDescription, ...] = ( native_max_value=9999, native_step=1, native_min_value=0, - native_unit_of_measurement=TIME_SECONDS, + native_unit_of_measurement=UnitOfTime.SECONDS, entity_category=EntityCategory.CONFIG, ), NumberEntityDescription( @@ -38,7 +38,7 @@ ENTITY_TYPES: tuple[NumberEntityDescription, ...] = ( native_max_value=9999, native_step=1, native_min_value=0, - native_unit_of_measurement=TIME_SECONDS, + native_unit_of_measurement=UnitOfTime.SECONDS, entity_category=EntityCategory.CONFIG, ), NumberEntityDescription( diff --git a/homeassistant/components/fully_kiosk/sensor.py b/homeassistant/components/fully_kiosk/sensor.py index 6b3ca3fbcb0..7e5b890d57a 100644 --- a/homeassistant/components/fully_kiosk/sensor.py +++ b/homeassistant/components/fully_kiosk/sensor.py @@ -12,7 +12,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import DATA_MEGABYTES, PERCENTAGE +from homeassistant.const import PERCENTAGE, UnitOfInformation from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -62,7 +62,8 @@ SENSORS: tuple[FullySensorEntityDescription, ...] = ( key="internalStorageFreeSpace", name="Internal storage free space", entity_category=EntityCategory.DIAGNOSTIC, - native_unit_of_measurement=DATA_MEGABYTES, + native_unit_of_measurement=UnitOfInformation.MEGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, state_class=SensorStateClass.MEASUREMENT, state_fn=round_storage, ), @@ -70,7 +71,8 @@ SENSORS: tuple[FullySensorEntityDescription, ...] = ( key="internalStorageTotalSpace", name="Internal storage total space", entity_category=EntityCategory.DIAGNOSTIC, - native_unit_of_measurement=DATA_MEGABYTES, + native_unit_of_measurement=UnitOfInformation.MEGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, state_class=SensorStateClass.MEASUREMENT, state_fn=round_storage, ), @@ -78,7 +80,8 @@ SENSORS: tuple[FullySensorEntityDescription, ...] = ( key="ramFreeMemory", name="Free memory", entity_category=EntityCategory.DIAGNOSTIC, - native_unit_of_measurement=DATA_MEGABYTES, + native_unit_of_measurement=UnitOfInformation.MEGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, state_class=SensorStateClass.MEASUREMENT, state_fn=round_storage, ), @@ -86,7 +89,8 @@ SENSORS: tuple[FullySensorEntityDescription, ...] = ( key="ramTotalMemory", name="Total memory", entity_category=EntityCategory.DIAGNOSTIC, - native_unit_of_measurement=DATA_MEGABYTES, + native_unit_of_measurement=UnitOfInformation.MEGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, state_class=SensorStateClass.MEASUREMENT, state_fn=round_storage, ), diff --git a/homeassistant/components/fully_kiosk/services.py b/homeassistant/components/fully_kiosk/services.py index 2283904dfa9..3d63bf4f23c 100644 --- a/homeassistant/components/fully_kiosk/services.py +++ b/homeassistant/components/fully_kiosk/services.py @@ -1,6 +1,10 @@ """Services for the Fully Kiosk Browser integration.""" from __future__ import annotations +from collections.abc import Callable +from typing import Any + +from fullykiosk import FullyKiosk import voluptuous as vol from homeassistant.const import ATTR_DEVICE_ID @@ -12,6 +16,7 @@ from .const import ( ATTR_APPLICATION, ATTR_URL, DOMAIN, + LOGGER, SERVICE_LOAD_URL, SERVICE_START_APPLICATION, ) @@ -20,54 +25,59 @@ from .const import ( async def async_setup_services(hass: HomeAssistant) -> None: """Set up the services for the Fully Kiosk Browser integration.""" - async def async_load_url(call: ServiceCall) -> None: - """Load a URL on the Fully Kiosk Browser.""" + async def execute_service( + call: ServiceCall, + fully_method: Callable, + *args: list[str], + **kwargs: dict[str, Any], + ) -> None: + """ + Execute a Fully service call. + + :param call: {ServiceCall} HA service call. + :param fully_method: {Callable} A method of the FullyKiosk class. + :param args: Arguments for fully_method. + :param kwargs: Key-word arguments for fully_method. + :return: None + """ + LOGGER.debug( + "Calling Fully service %s with args: %s, %s", ServiceCall, args, kwargs + ) registry = dr.async_get(hass) for target in call.data[ATTR_DEVICE_ID]: - device = registry.async_get(target) if device: coordinator = hass.data[DOMAIN][list(device.config_entries)[0]] - await coordinator.fully.loadUrl(call.data[ATTR_URL]) + # fully_method(coordinator.fully, *args, **kwargs) would make + # test_services.py fail. + await getattr(coordinator.fully, fully_method.__name__)(*args, **kwargs) + + async def async_load_url(call: ServiceCall) -> None: + """Load a URL on the Fully Kiosk Browser.""" + await execute_service(call, FullyKiosk.loadUrl, call.data[ATTR_URL]) async def async_start_app(call: ServiceCall) -> None: """Start an app on the device.""" - registry = dr.async_get(hass) - for target in call.data[ATTR_DEVICE_ID]: + await execute_service( + call, FullyKiosk.startApplication, call.data[ATTR_APPLICATION] + ) - device = registry.async_get(target) - if device: - coordinator = hass.data[DOMAIN][list(device.config_entries)[0]] - await coordinator.fully.startApplication(call.data[ATTR_APPLICATION]) - - hass.services.async_register( - DOMAIN, - SERVICE_LOAD_URL, - async_load_url, - schema=vol.Schema( - vol.All( - { - vol.Required(ATTR_DEVICE_ID): cv.ensure_list, - vol.Required( - ATTR_URL, - ): cv.string, - }, - ) - ), - ) - - hass.services.async_register( - DOMAIN, - SERVICE_START_APPLICATION, - async_start_app, - schema=vol.Schema( - vol.All( - { - vol.Required(ATTR_DEVICE_ID): cv.ensure_list, - vol.Required( - ATTR_APPLICATION, - ): cv.string, - }, - ) - ), - ) + # Register all the above services + service_mapping = [ + (async_load_url, SERVICE_LOAD_URL, ATTR_URL), + (async_start_app, SERVICE_START_APPLICATION, ATTR_APPLICATION), + ] + for service_handler, service_name, attrib in service_mapping: + hass.services.async_register( + DOMAIN, + service_name, + service_handler, + schema=vol.Schema( + vol.All( + { + vol.Required(ATTR_DEVICE_ID): cv.ensure_list, + vol.Required(attrib): cv.string, + } + ) + ), + ) diff --git a/homeassistant/components/fully_kiosk/translations/ko.json b/homeassistant/components/fully_kiosk/translations/ko.json new file mode 100644 index 00000000000..4de04caedf5 --- /dev/null +++ b/homeassistant/components/fully_kiosk/translations/ko.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8", + "password": "\ube44\ubc00\ubc88\ud638" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/fully_kiosk/translations/pt.json b/homeassistant/components/fully_kiosk/translations/pt.json new file mode 100644 index 00000000000..104a74f9320 --- /dev/null +++ b/homeassistant/components/fully_kiosk/translations/pt.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "Conta j\u00e1 configurada" + }, + "error": { + "cannot_connect": "A liga\u00e7\u00e3o falhou", + "unknown": "Erro inesperado" + }, + "step": { + "user": { + "data": { + "host": "Endere\u00e7o", + "password": "Palavra-passe" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/garages_amsterdam/__init__.py b/homeassistant/components/garages_amsterdam/__init__.py index 5f8f3e36671..63a17dbf285 100644 --- a/homeassistant/components/garages_amsterdam/__init__.py +++ b/homeassistant/components/garages_amsterdam/__init__.py @@ -3,7 +3,7 @@ from datetime import timedelta import logging import async_timeout -from garages_amsterdam import GaragesAmsterdam +from odp_amsterdam import ODPAmsterdam from homeassistant.config_entries import ConfigEntry from homeassistant.const import Platform @@ -43,7 +43,7 @@ async def get_coordinator( async with async_timeout.timeout(10): return { garage.garage_name: garage - for garage in await GaragesAmsterdam( + for garage in await ODPAmsterdam( session=aiohttp_client.async_get_clientsession(hass) ).all_garages() } diff --git a/homeassistant/components/garages_amsterdam/config_flow.py b/homeassistant/components/garages_amsterdam/config_flow.py index c8a61f9a160..cd1591c9bc0 100644 --- a/homeassistant/components/garages_amsterdam/config_flow.py +++ b/homeassistant/components/garages_amsterdam/config_flow.py @@ -5,7 +5,7 @@ import logging from typing import Any from aiohttp import ClientResponseError -from garages_amsterdam import GaragesAmsterdam +from odp_amsterdam import ODPAmsterdam import voluptuous as vol from homeassistant import config_entries @@ -30,7 +30,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): if self._options is None: self._options = [] try: - api_data = await GaragesAmsterdam( + api_data = await ODPAmsterdam( session=aiohttp_client.async_get_clientsession(self.hass) ).all_garages() except ClientResponseError: diff --git a/homeassistant/components/garages_amsterdam/manifest.json b/homeassistant/components/garages_amsterdam/manifest.json index aedfa3cca65..1d6e91293db 100644 --- a/homeassistant/components/garages_amsterdam/manifest.json +++ b/homeassistant/components/garages_amsterdam/manifest.json @@ -3,7 +3,7 @@ "name": "Garages Amsterdam", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/garages_amsterdam", - "requirements": ["garages-amsterdam==3.0.0"], + "requirements": ["odp-amsterdam==5.0.0"], "codeowners": ["@klaasnicolaas"], "iot_class": "cloud_polling" } diff --git a/homeassistant/components/garages_amsterdam/translations/ko.json b/homeassistant/components/garages_amsterdam/translations/ko.json index f5088fca3e1..fdf4407df02 100644 --- a/homeassistant/components/garages_amsterdam/translations/ko.json +++ b/homeassistant/components/garages_amsterdam/translations/ko.json @@ -2,7 +2,8 @@ "config": { "abort": { "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", - "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" } } } \ No newline at end of file diff --git a/homeassistant/components/garages_amsterdam/translations/sk.json b/homeassistant/components/garages_amsterdam/translations/sk.json index 30b5d246821..c6b8f3daab8 100644 --- a/homeassistant/components/garages_amsterdam/translations/sk.json +++ b/homeassistant/components/garages_amsterdam/translations/sk.json @@ -9,8 +9,10 @@ "user": { "data": { "garage_name": "N\u00e1zov gar\u00e1\u017ee" - } + }, + "title": "Vyberte si gar\u00e1\u017e na monitorovanie" } } - } + }, + "title": "Gar\u00e1\u017ee Amsterdam" } \ No newline at end of file diff --git a/homeassistant/components/gdacs/__init__.py b/homeassistant/components/gdacs/__init__.py index 269da061b58..9474e006dbb 100644 --- a/homeassistant/components/gdacs/__init__.py +++ b/homeassistant/components/gdacs/__init__.py @@ -11,8 +11,7 @@ from homeassistant.const import ( CONF_LONGITUDE, CONF_RADIUS, CONF_SCAN_INTERVAL, - LENGTH_KILOMETERS, - LENGTH_MILES, + UnitOfLength, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import aiohttp_client, config_validation as cv @@ -89,7 +88,9 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b radius = config_entry.data[CONF_RADIUS] if hass.config.units is US_CUSTOMARY_SYSTEM: - radius = DistanceConverter.convert(radius, LENGTH_MILES, LENGTH_KILOMETERS) + radius = DistanceConverter.convert( + radius, UnitOfLength.MILES, UnitOfLength.KILOMETERS + ) # Create feed entity manager for all platforms. manager = GdacsFeedEntityManager(hass, config_entry, radius) feeds[config_entry.entry_id] = manager diff --git a/homeassistant/components/gdacs/geo_location.py b/homeassistant/components/gdacs/geo_location.py index 5d3b8f3375b..06ab1aa0837 100644 --- a/homeassistant/components/gdacs/geo_location.py +++ b/homeassistant/components/gdacs/geo_location.py @@ -10,7 +10,7 @@ from aio_georss_gdacs.feed_entry import GdacsFeedEntry from homeassistant.components.geo_location import GeolocationEvent from homeassistant.config_entries import ConfigEntry -from homeassistant.const import LENGTH_KILOMETERS, LENGTH_MILES +from homeassistant.const import UnitOfLength from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import entity_registry as er from homeassistant.helpers.dispatcher import async_dispatcher_connect @@ -89,7 +89,7 @@ class GdacsEvent(GeolocationEvent): self._feed_manager = feed_manager self._external_id = external_id self._attr_unique_id = f"{integration_id}_{external_id}" - self._attr_unit_of_measurement = LENGTH_KILOMETERS + self._attr_unit_of_measurement = UnitOfLength.KILOMETERS self._alert_level = None self._country = None self._description = None @@ -108,7 +108,7 @@ class GdacsEvent(GeolocationEvent): async def async_added_to_hass(self) -> None: """Call when entity is added to hass.""" if self.hass.config.units is US_CUSTOMARY_SYSTEM: - self._attr_unit_of_measurement = LENGTH_MILES + self._attr_unit_of_measurement = UnitOfLength.MILES self._remove_signal_delete = async_dispatcher_connect( self.hass, f"gdacs_delete_{self._external_id}", self._delete_callback ) @@ -151,7 +151,7 @@ class GdacsEvent(GeolocationEvent): # Convert distance if not metric system. if self.hass.config.units is US_CUSTOMARY_SYSTEM: self._attr_distance = DistanceConverter.convert( - feed_entry.distance_to_home, LENGTH_KILOMETERS, LENGTH_MILES + feed_entry.distance_to_home, UnitOfLength.KILOMETERS, UnitOfLength.MILES ) else: self._attr_distance = feed_entry.distance_to_home diff --git a/homeassistant/components/gdacs/translations/sk.json b/homeassistant/components/gdacs/translations/sk.json index f04d4a327f4..7a07718d428 100644 --- a/homeassistant/components/gdacs/translations/sk.json +++ b/homeassistant/components/gdacs/translations/sk.json @@ -2,6 +2,14 @@ "config": { "abort": { "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + }, + "step": { + "user": { + "data": { + "radius": "Polomer" + }, + "title": "Vypl\u0148te podrobnosti o filtri." + } } } } \ No newline at end of file diff --git a/homeassistant/components/generic/translations/de.json b/homeassistant/components/generic/translations/de.json index d45de3644f4..aba92b601d5 100644 --- a/homeassistant/components/generic/translations/de.json +++ b/homeassistant/components/generic/translations/de.json @@ -9,12 +9,12 @@ "malformed_url": "Falsch formatierte URL", "no_still_image_or_stream_url": "Du musst mindestens eine Standbild- oder Stream-URL angeben", "relative_url": "Relative URLs sind nicht zul\u00e4ssig", - "stream_io_error": "Eingabe-/Ausgabefehler beim Versuch, eine Verbindung zum Stream herzustellen. Falsches RTSP-Transportprotokoll?", + "stream_io_error": "Eingabe-/Ausgabefehler beim Versuch, eine Verbindung zum Stream herzustellen. Falsches RTSP Transportprotokoll?", "stream_no_route_to_host": "Beim Versuch, eine Verbindung zum Stream herzustellen, konnte der Host nicht gefunden werden", - "stream_not_permitted": "Beim Versuch, eine Verbindung zum Stream herzustellen, ist ein Vorgang nicht zul\u00e4ssig. Falsches RTSP-Transportprotokoll?", + "stream_not_permitted": "Beim Versuch, eine Verbindung zum Stream herzustellen, ist ein Vorgang nicht zul\u00e4ssig. Falsches RTSP Transportprotokoll?", "template_error": "Fehler beim Rendern der Vorlage. \u00dcberpr\u00fcfe das Protokoll f\u00fcr weitere Informationen.", "timeout": "Zeit\u00fcberschreitung beim Laden der URL", - "unable_still_load": "Es konnte kein g\u00fcltiges Bild von der Standbild-URL geladen werden (z. B. ung\u00fcltiger Host, URL oder Authentifizierungsfehler). \u00dcberpr\u00fcfe das Protokoll f\u00fcr weitere Informationen.", + "unable_still_load": "Es konnte kein g\u00fcltiges Bild von der Standbild-URL geladen werden (z.B. ung\u00fcltiger Host, URL oder Authentifizierungsfehler). \u00dcberpr\u00fcfe das Protokoll f\u00fcr weitere Informationen.", "unknown": "Unerwarteter Fehler" }, "step": { @@ -25,8 +25,8 @@ "limit_refetch_to_url_change": "Neuabruf auf URL-\u00c4nderung beschr\u00e4nken", "password": "Passwort", "rtsp_transport": "RTSP-Transportprotokoll", - "still_image_url": "Standbild-URL (z.B. http://...)", - "stream_source": "Stream-Quell-URL (z.B. rtsp://...)", + "still_image_url": "Standbild-URL (z.B. http://\u2026)", + "stream_source": "Stream-Quell-URL (z.B. rtsp://\u2026)", "username": "Benutzername", "verify_ssl": "SSL-Zertifikat \u00fcberpr\u00fcfen" }, @@ -48,12 +48,12 @@ "malformed_url": "Falsch formatierte URL", "no_still_image_or_stream_url": "Du musst mindestens eine Standbild- oder Stream-URL angeben", "relative_url": "Relative URLs sind nicht zul\u00e4ssig", - "stream_io_error": "Eingabe-/Ausgabefehler beim Versuch, eine Verbindung zum Stream herzustellen. Falsches RTSP-Transportprotokoll?", + "stream_io_error": "Eingabe-/Ausgabefehler beim Versuch, eine Verbindung zum Stream herzustellen. Falsches RTSP Transportprotokoll?", "stream_no_route_to_host": "Beim Versuch, eine Verbindung zum Stream herzustellen, konnte der Host nicht gefunden werden", - "stream_not_permitted": "Beim Versuch, eine Verbindung zum Stream herzustellen, ist ein Vorgang nicht zul\u00e4ssig. Falsches RTSP-Transportprotokoll?", + "stream_not_permitted": "Beim Versuch, eine Verbindung zum Stream herzustellen, ist ein Vorgang nicht zul\u00e4ssig. Falsches RTSP Transportprotokoll?", "template_error": "Fehler beim Rendern der Vorlage. \u00dcberpr\u00fcfe das Protokoll f\u00fcr weitere Informationen.", "timeout": "Zeit\u00fcberschreitung beim Laden der URL", - "unable_still_load": "Es konnte kein g\u00fcltiges Bild von der Standbild-URL geladen werden (z. B. ung\u00fcltiger Host, URL oder Authentifizierungsfehler). \u00dcberpr\u00fcfe das Protokoll f\u00fcr weitere Informationen.", + "unable_still_load": "Es konnte kein g\u00fcltiges Bild von der Standbild-URL geladen werden (z.B. ung\u00fcltiger Host, URL oder Authentifizierungsfehler). \u00dcberpr\u00fcfe das Protokoll f\u00fcr weitere Informationen.", "unknown": "Unerwarteter Fehler" }, "step": { @@ -71,8 +71,8 @@ "limit_refetch_to_url_change": "Neuabruf auf URL-\u00c4nderung beschr\u00e4nken", "password": "Passwort", "rtsp_transport": "RTSP-Transportprotokoll", - "still_image_url": "Standbild-URL (z.B. http://...)", - "stream_source": "Stream-Quell-URL (z.B. rtsp://...)", + "still_image_url": "Standbild-URL (z.B. http://\u2026)", + "stream_source": "Stream-Quell-URL (z.B. rtsp://\u2026)", "use_wallclock_as_timestamps": "Wanduhr als Zeitstempel verwenden", "username": "Benutzername", "verify_ssl": "SSL-Zertifikat \u00fcberpr\u00fcfen" diff --git a/homeassistant/components/generic/translations/he.json b/homeassistant/components/generic/translations/he.json index e92df2f384b..3bd67fe842e 100644 --- a/homeassistant/components/generic/translations/he.json +++ b/homeassistant/components/generic/translations/he.json @@ -14,7 +14,7 @@ "stream_not_permitted": "\u05d4\u05e4\u05e2\u05d5\u05dc\u05d4 \u05d0\u05d9\u05e0\u05d4 \u05de\u05d5\u05ea\u05e8\u05ea \u05d1\u05e2\u05ea \u05e0\u05d9\u05e1\u05d9\u05d5\u05df \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05d4\u05d6\u05e8\u05de\u05d4. \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc \u05ea\u05e2\u05d1\u05d5\u05e8\u05d4 \u05e9\u05d2\u05d5\u05d9 \u05e9\u05dc RTSP?", "template_error": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05e2\u05d9\u05d1\u05d5\u05d3 \u05d4\u05ea\u05d1\u05e0\u05d9\u05ea. \u05e2\u05d9\u05d9\u05df \u05d1\u05d9\u05d5\u05de\u05df \u05dc\u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3.", "timeout": "\u05d6\u05de\u05df \u05e7\u05e6\u05d5\u05d1 \u05d1\u05e2\u05ea \u05d8\u05e2\u05d9\u05e0\u05ea \u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8", - "unable_still_load": "\u05d0\u05d9\u05df \u05d0\u05e4\u05e9\u05e8\u05d5\u05ea \u05dc\u05d8\u05e2\u05d5\u05df \u05ea\u05de\u05d5\u05e0\u05d4 \u05d7\u05d5\u05e7\u05d9\u05ea \u05de\u05db\u05ea\u05d5\u05d1\u05ea \u05d4\u05d0\u05ea\u05e8 \u05e9\u05dc \u05ea\u05de\u05d5\u05e0\u05ea \u05e1\u05d8\u05d9\u05dc\u05e1 (\u05dc\u05d3\u05d5\u05d2\u05de\u05d4, \u05db\u05e9\u05dc \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9 \u05d1\u05de\u05d7\u05e9\u05d1 \u05de\u05d0\u05e8\u05d7, \u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8 \u05d0\u05d5 \u05d0\u05d9\u05de\u05d5\u05ea). \u05e0\u05d0 \u05dc\u05e2\u05d9\u05d9\u05df \u05d1\u05d9\u05d5\u05de\u05df \u05d4\u05e8\u05d9\u05e9\u05d5\u05dd \u05dc\u05e7\u05d1\u05dc\u05ea \u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3.", + "unable_still_load": "\u05d0\u05d9\u05df \u05d0\u05e4\u05e9\u05e8\u05d5\u05ea \u05dc\u05d8\u05e2\u05d5\u05df \u05ea\u05de\u05d5\u05e0\u05d4 \u05d7\u05d5\u05e7\u05d9\u05ea \u05de\u05db\u05ea\u05d5\u05d1\u05ea \u05d4\u05d0\u05ea\u05e8 \u05e9\u05dc \u05ea\u05de\u05d5\u05e0\u05ea \u05e1\u05d8\u05d9\u05dc\u05e1 (\u05dc\u05d3\u05d5\u05d2\u05de\u05d4, \u05db\u05e9\u05dc \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9 \u05d1\u05de\u05d7\u05e9\u05d1 \u05de\u05d0\u05e8\u05d7, \u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8 \u05d0\u05d5 \u05d0\u05d9\u05de\u05d5\u05ea). \u05e0\u05d0 \u05dc\u05e2\u05d9\u05d9\u05df \u05d1\u05d9\u05d5\u05de\u05df \u05d4\u05e8\u05d9\u05e9\u05d5\u05dd \u05dc\u05e7\u05d1\u05dc\u05ea \u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3.", "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" }, "step": { @@ -53,7 +53,7 @@ "stream_not_permitted": "\u05d4\u05e4\u05e2\u05d5\u05dc\u05d4 \u05d0\u05d9\u05e0\u05d4 \u05de\u05d5\u05ea\u05e8\u05ea \u05d1\u05e2\u05ea \u05e0\u05d9\u05e1\u05d9\u05d5\u05df \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05d4\u05d6\u05e8\u05de\u05d4. \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc \u05ea\u05e2\u05d1\u05d5\u05e8\u05d4 \u05e9\u05d2\u05d5\u05d9 \u05e9\u05dc RTSP?", "template_error": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05e2\u05d9\u05d1\u05d5\u05d3 \u05d4\u05ea\u05d1\u05e0\u05d9\u05ea. \u05e2\u05d9\u05d9\u05df \u05d1\u05d9\u05d5\u05de\u05df \u05dc\u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3.", "timeout": "\u05d6\u05de\u05df \u05e7\u05e6\u05d5\u05d1 \u05d1\u05e2\u05ea \u05d8\u05e2\u05d9\u05e0\u05ea \u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8", - "unable_still_load": "\u05d0\u05d9\u05df \u05d0\u05e4\u05e9\u05e8\u05d5\u05ea \u05dc\u05d8\u05e2\u05d5\u05df \u05ea\u05de\u05d5\u05e0\u05d4 \u05d7\u05d5\u05e7\u05d9\u05ea \u05de\u05db\u05ea\u05d5\u05d1\u05ea \u05d4\u05d0\u05ea\u05e8 \u05e9\u05dc \u05ea\u05de\u05d5\u05e0\u05ea \u05e1\u05d8\u05d9\u05dc\u05e1 (\u05dc\u05d3\u05d5\u05d2\u05de\u05d4, \u05db\u05e9\u05dc \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9 \u05d1\u05de\u05d7\u05e9\u05d1 \u05de\u05d0\u05e8\u05d7, \u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8 \u05d0\u05d5 \u05d0\u05d9\u05de\u05d5\u05ea). \u05e0\u05d0 \u05dc\u05e2\u05d9\u05d9\u05df \u05d1\u05d9\u05d5\u05de\u05df \u05d4\u05e8\u05d9\u05e9\u05d5\u05dd \u05dc\u05e7\u05d1\u05dc\u05ea \u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3.", + "unable_still_load": "\u05d0\u05d9\u05df \u05d0\u05e4\u05e9\u05e8\u05d5\u05ea \u05dc\u05d8\u05e2\u05d5\u05df \u05ea\u05de\u05d5\u05e0\u05d4 \u05d7\u05d5\u05e7\u05d9\u05ea \u05de\u05db\u05ea\u05d5\u05d1\u05ea \u05d4\u05d0\u05ea\u05e8 \u05e9\u05dc \u05ea\u05de\u05d5\u05e0\u05ea \u05e1\u05d8\u05d9\u05dc\u05e1 (\u05dc\u05d3\u05d5\u05d2\u05de\u05d4, \u05db\u05e9\u05dc \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9 \u05d1\u05de\u05d7\u05e9\u05d1 \u05de\u05d0\u05e8\u05d7, \u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8 \u05d0\u05d5 \u05d0\u05d9\u05de\u05d5\u05ea). \u05e0\u05d0 \u05dc\u05e2\u05d9\u05d9\u05df \u05d1\u05d9\u05d5\u05de\u05df \u05d4\u05e8\u05d9\u05e9\u05d5\u05dd \u05dc\u05e7\u05d1\u05dc\u05ea \u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3.", "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" }, "step": { diff --git a/homeassistant/components/generic/translations/ko.json b/homeassistant/components/generic/translations/ko.json new file mode 100644 index 00000000000..a4f62e9349b --- /dev/null +++ b/homeassistant/components/generic/translations/ko.json @@ -0,0 +1,33 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." + }, + "error": { + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984", + "verify_ssl": "SSL \uc778\uc99d\uc11c \ud655\uc778" + } + } + } + }, + "options": { + "error": { + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "init": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984", + "verify_ssl": "SSL \uc778\uc99d\uc11c \ud655\uc778" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/generic/translations/sk.json b/homeassistant/components/generic/translations/sk.json index 92f13ab5e49..52e33f83732 100644 --- a/homeassistant/components/generic/translations/sk.json +++ b/homeassistant/components/generic/translations/sk.json @@ -7,9 +7,14 @@ "already_exists": "Kamera s t\u00fdmito nastaveniami URL u\u017e existuje.", "invalid_still_image": "Adresa URL nevr\u00e1tila platn\u00fd statick\u00fd obr\u00e1zok", "malformed_url": "Chybne vytvoren\u00e1 adresa URL", + "no_still_image_or_stream_url": "Mus\u00edte zada\u0165 aspo\u0148 adresu URL statick\u00e9ho obr\u00e1zka alebo streamu", "relative_url": "Relat\u00edvne adresy URL nie s\u00fa povolen\u00e9", + "stream_io_error": "Chyba vstupu/v\u00fdstupu pri pokuse o pripojenie k streamu. Nespr\u00e1vny transportn\u00fd protokol RTSP?", "stream_no_route_to_host": "Pri pokuse o pripojenie k streamu sa nepodarilo n\u00e1js\u0165 hostite\u013ea", + "stream_not_permitted": "Oper\u00e1cia nie je povolen\u00e1 pri pokuse o pripojenie k streamu. Nespr\u00e1vny transportn\u00fd protokol RTSP?", + "template_error": "Chyba pri vykres\u013eovan\u00ed \u0161abl\u00f3ny. Pre viac inform\u00e1ci\u00ed skontrolujte denn\u00edk.", "timeout": "\u010casov\u00fd limit pri na\u010d\u00edtan\u00ed adresy URL", + "unable_still_load": "Ned\u00e1 sa na\u010d\u00edta\u0165 platn\u00fd obr\u00e1zok z adresy URL statick\u00e9ho obr\u00e1zka (napr. neplatn\u00fd hostite\u013e, adresa URL alebo zlyhanie overenia). Pre viac inform\u00e1ci\u00ed skontrolujte denn\u00edk.", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { @@ -17,7 +22,11 @@ "data": { "authentication": "Overenie", "framerate": "Sn\u00edmkov\u00e1 frekvencia (Hz)", + "limit_refetch_to_url_change": "Obmedzi\u0165 op\u00e4tovn\u00e9 na\u010d\u00edtanie na zmenu url", "password": "Heslo", + "rtsp_transport": "Transportn\u00fd protokol RTSP", + "still_image_url": "Adresa URL statick\u00e9ho obr\u00e1zka (napr. http://...)", + "stream_source": "Adresa URL zdroja streamu (napr. rtsp://...)", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno", "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" }, @@ -27,7 +36,8 @@ "data": { "confirmed_ok": "Tento obr\u00e1zok vyzer\u00e1 dobre." }, - "description": "![Uk\u00e1\u017eka statick\u00e9ho obr\u00e1zka z fotoapar\u00e1tu]({preview_url})" + "description": "![Uk\u00e1\u017eka statick\u00e9ho obr\u00e1zka z fotoapar\u00e1tu]({preview_url})", + "title": "N\u00e1h\u013ead" } } }, @@ -36,9 +46,14 @@ "already_exists": "Kamera s t\u00fdmito nastaveniami URL u\u017e existuje.", "invalid_still_image": "Adresa URL nevr\u00e1tila platn\u00fd statick\u00fd obr\u00e1zok", "malformed_url": "Chybne vytvoren\u00e1 adresa URL", + "no_still_image_or_stream_url": "Mus\u00edte zada\u0165 aspo\u0148 adresu URL statick\u00e9ho obr\u00e1zka alebo streamu", "relative_url": "Relat\u00edvne adresy URL nie s\u00fa povolen\u00e9", + "stream_io_error": "Chyba vstupu/v\u00fdstupu pri pokuse o pripojenie k streamu. Nespr\u00e1vny transportn\u00fd protokol RTSP?", "stream_no_route_to_host": "Pri pokuse o pripojenie k streamu sa nepodarilo n\u00e1js\u0165 hostite\u013ea", + "stream_not_permitted": "Oper\u00e1cia nie je povolen\u00e1 pri pokuse o pripojenie k streamu. Nespr\u00e1vny transportn\u00fd protokol RTSP?", + "template_error": "Chyba pri vykres\u013eovan\u00ed \u0161abl\u00f3ny. Pre viac inform\u00e1ci\u00ed skontrolujte denn\u00edk.", "timeout": "\u010casov\u00fd limit pri na\u010d\u00edtan\u00ed adresy URL", + "unable_still_load": "Ned\u00e1 sa na\u010d\u00edta\u0165 platn\u00fd obr\u00e1zok z adresy URL statick\u00e9ho obr\u00e1zka (napr. neplatn\u00fd hostite\u013e, adresa URL alebo zlyhanie overenia). Pre viac inform\u00e1ci\u00ed skontrolujte denn\u00edk.", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { @@ -46,15 +61,24 @@ "data": { "confirmed_ok": "Tento obr\u00e1zok vyzer\u00e1 dobre." }, - "description": "![Uk\u00e1\u017eka statick\u00e9ho obr\u00e1zka z fotoapar\u00e1tu]({preview_url})" + "description": "![Uk\u00e1\u017eka statick\u00e9ho obr\u00e1zka z fotoapar\u00e1tu]({preview_url})", + "title": "N\u00e1h\u013ead" }, "init": { "data": { "authentication": "Overenie", "framerate": "Sn\u00edmkov\u00e1 frekvencia (Hz)", + "limit_refetch_to_url_change": "Obmedzi\u0165 op\u00e4tovn\u00e9 na\u010d\u00edtanie na zmenu url", "password": "Heslo", + "rtsp_transport": "Transportn\u00fd protokol RTSP", + "still_image_url": "Adresa URL statick\u00e9ho obr\u00e1zka (napr. http://...)", + "stream_source": "Adresa URL zdroja streamu (napr. rtsp://...)", + "use_wallclock_as_timestamps": "Pou\u017eite n\u00e1stenn\u00e9 hodiny ako \u010dasov\u00e9 pe\u010diatky", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno", "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" + }, + "data_description": { + "use_wallclock_as_timestamps": "T\u00e1to mo\u017enos\u0165 m\u00f4\u017ee opravi\u0165 probl\u00e9my so segmentovan\u00edm alebo zlyhan\u00edm, ktor\u00e9 vypl\u00fdvaj\u00fa z chybnej implement\u00e1cie \u010dasovej pe\u010diatky na niektor\u00fdch fotoapar\u00e1toch" } } } diff --git a/homeassistant/components/generic_hygrostat/humidifier.py b/homeassistant/components/generic_hygrostat/humidifier.py index e6caf2ca097..b8db68fbee9 100644 --- a/homeassistant/components/generic_hygrostat/humidifier.py +++ b/homeassistant/components/generic_hygrostat/humidifier.py @@ -358,8 +358,10 @@ class GenericHygrostat(HumidifierEntity, RestoreEntity): self._active = True force = True _LOGGER.info( - "Obtained current and target humidity. " - "Generic hygrostat active. %s, %s", + ( + "Obtained current and target humidity. " + "Generic hygrostat active. %s, %s" + ), self._cur_humidity, self._target_humidity, ) diff --git a/homeassistant/components/generic_thermostat/climate.py b/homeassistant/components/generic_thermostat/climate.py index dadfc995e03..22a3f98a9f0 100644 --- a/homeassistant/components/generic_thermostat/climate.py +++ b/homeassistant/components/generic_thermostat/climate.py @@ -409,7 +409,10 @@ class GenericThermostat(ClimateEntity, RestoreEntity): """Prevent the device from keep running if HVACMode.OFF.""" if self._hvac_mode == HVACMode.OFF and self._is_device_active: _LOGGER.warning( - "The climate mode is OFF, but the switch device is ON. Turning off device %s", + ( + "The climate mode is OFF, but the switch device is ON. Turning off" + " device %s" + ), self.heater_entity_id, ) await self._async_heater_turn_off() @@ -445,8 +448,10 @@ class GenericThermostat(ClimateEntity, RestoreEntity): ): self._active = True _LOGGER.info( - "Obtained current and target temperature. " - "Generic thermostat active. %s, %s", + ( + "Obtained current and target temperature. " + "Generic thermostat active. %s, %s" + ), self._cur_temp, self._target_temp, ) @@ -526,7 +531,8 @@ class GenericThermostat(ClimateEntity, RestoreEntity): """Set new preset mode.""" if preset_mode not in (self.preset_modes or []): raise ValueError( - f"Got unsupported preset_mode {preset_mode}. Must be one of {self.preset_modes}" + f"Got unsupported preset_mode {preset_mode}. Must be one of" + f" {self.preset_modes}" ) if preset_mode == self._attr_preset_mode: # I don't think we need to call async_write_ha_state if we didn't change the state diff --git a/homeassistant/components/geniushub/__init__.py b/homeassistant/components/geniushub/__init__.py index fc04f314812..32a15285d6c 100644 --- a/homeassistant/components/geniushub/__init__.py +++ b/homeassistant/components/geniushub/__init__.py @@ -17,8 +17,8 @@ from homeassistant.const import ( CONF_PASSWORD, CONF_TOKEN, CONF_USERNAME, - TEMP_CELSIUS, Platform, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant, ServiceCall, callback from homeassistant.helpers import config_validation as cv, entity_registry as er @@ -356,7 +356,7 @@ class GeniusHeatingZone(GeniusZone): @property def temperature_unit(self) -> str: """Return the unit of measurement.""" - return TEMP_CELSIUS + return UnitOfTemperature.CELSIUS async def async_set_temperature(self, **kwargs) -> None: """Set a new target temperature for this zone.""" diff --git a/homeassistant/components/geo_json_events/geo_location.py b/homeassistant/components/geo_json_events/geo_location.py index c0d6abe694f..166da1184c6 100644 --- a/homeassistant/components/geo_json_events/geo_location.py +++ b/homeassistant/components/geo_json_events/geo_location.py @@ -18,7 +18,7 @@ from homeassistant.const import ( CONF_SCAN_INTERVAL, CONF_URL, EVENT_HOMEASSISTANT_START, - LENGTH_KILOMETERS, + UnitOfLength, ) from homeassistant.core import Event, HomeAssistant, callback from homeassistant.helpers import aiohttp_client @@ -146,7 +146,7 @@ class GeoJsonLocationEvent(GeolocationEvent): _attr_should_poll = False _attr_source = SOURCE - _attr_unit_of_measurement = LENGTH_KILOMETERS + _attr_unit_of_measurement = UnitOfLength.KILOMETERS def __init__(self, feed_manager: GenericFeedManager, external_id: str) -> None: """Initialize entity with data from feed entry.""" diff --git a/homeassistant/components/geo_rss_events/sensor.py b/homeassistant/components/geo_rss_events/sensor.py index eba903a4cdf..2aade681e29 100644 --- a/homeassistant/components/geo_rss_events/sensor.py +++ b/homeassistant/components/geo_rss_events/sensor.py @@ -22,7 +22,7 @@ from homeassistant.const import ( CONF_RADIUS, CONF_UNIT_OF_MEASUREMENT, CONF_URL, - LENGTH_KILOMETERS, + UnitOfLength, ) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv @@ -162,7 +162,9 @@ class GeoRssServiceSensor(SensorEntity): # And now compute the attributes from the filtered events. matrix = {} for entry in feed_entries: - matrix[entry.title] = f"{entry.distance_to_home:.0f}{LENGTH_KILOMETERS}" + matrix[ + entry.title + ] = f"{entry.distance_to_home:.0f}{UnitOfLength.KILOMETERS}" self._state_attributes = matrix elif status == UPDATE_OK_NO_DATA: _LOGGER.debug("Update successful, but no data received from %s", self._feed) diff --git a/homeassistant/components/geocaching/translations/pt.json b/homeassistant/components/geocaching/translations/pt.json index 8685cf2d3c2..6c41a2e3e1a 100644 --- a/homeassistant/components/geocaching/translations/pt.json +++ b/homeassistant/components/geocaching/translations/pt.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "Conta j\u00e1 configurada", "missing_configuration": "O componente n\u00e3o est\u00e1 configurado. Por favor, siga a documenta\u00e7\u00e3o.", - "no_url_available": "Nenhum URL dispon\u00edvel. Para obter informa\u00e7\u00f5es sobre esse erro, [verifique a sec\u00e7\u00e3o de ajuda]({docs_url})" + "no_url_available": "Nenhum URL est\u00e1 dispon\u00edvel. Para obter informa\u00e7\u00f5es sobre esse erro, [consulte a sec\u00e7\u00e3o de ajuda]({docs_url})" }, "create_entry": { "default": "Autenticado com sucesso" diff --git a/homeassistant/components/geofency/strings.json b/homeassistant/components/geofency/strings.json index b42abcbfe6a..1ce926c3d2f 100644 --- a/homeassistant/components/geofency/strings.json +++ b/homeassistant/components/geofency/strings.json @@ -12,7 +12,7 @@ "webhook_not_internet_accessible": "[%key:common::config_flow::abort::webhook_not_internet_accessible%]" }, "create_entry": { - "default": "To send events to Home Assistant, you will need to setup the webhook feature in Geofency.\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n\nSee [the documentation]({docs_url}) for further details." + "default": "To send events to Home Assistant, you will need to set up the webhook feature in Geofency.\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n\nSee [the documentation]({docs_url}) for further details." } } } diff --git a/homeassistant/components/geofency/translations/ca.json b/homeassistant/components/geofency/translations/ca.json index d2bb9582a60..8e6b9923a72 100644 --- a/homeassistant/components/geofency/translations/ca.json +++ b/homeassistant/components/geofency/translations/ca.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "La teva inst\u00e0ncia de Home Assistant ha de ser accessible des d'Internet per poder rebre missatges webhook." }, "create_entry": { - "default": "Per enviar esdeveniments a Home Assistant, haur\u00e0s de configurar l'opci\u00f3 webhook de Geofency.\n\nCompleta la seg\u00fcent informaci\u00f3:\n\n- URL: `{webhook_url}` \n- M\u00e8tode: POST \n\nConsulta la [documentaci\u00f3]({docs_url}) per a m\u00e9s detalls." + "default": "Per enviar esdeveniments a Home Assistant, has de configurar l'opci\u00f3 webhook de Geofency.\n\nCompleta la seg\u00fcent informaci\u00f3:\n\n- URL: `{webhook_url}` \n- M\u00e8tode: POST \n\nConsulta la [documentaci\u00f3]({docs_url}) per a m\u00e9s detalls." }, "step": { "user": { diff --git a/homeassistant/components/geofency/translations/de.json b/homeassistant/components/geofency/translations/de.json index b9b06cfd020..37401d1bc63 100644 --- a/homeassistant/components/geofency/translations/de.json +++ b/homeassistant/components/geofency/translations/de.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "Deine Home Assistant-Instanz muss \u00fcber das Internet erreichbar sein, um Webhook-Nachrichten empfangen zu k\u00f6nnen." }, "create_entry": { - "default": "Um Ereignisse an den Home Assistant zu senden, musst das Webhook Feature in Geofency konfiguriert werden.\n\n F\u00fcge die folgenden Informationen ein: \n\n - URL: ` {webhook_url} ` \n - Methode: POST \n \n Weitere Informationen finden sich in der [Dokumentation]({docs_url})." + "default": "Um Ereignisse an Home Assistant zu senden, muss das Webhook Feature in Geofency konfiguriert werden.\n\nF\u00fcge die folgenden Informationen ein: \n\n- URL: `{webhook_url}` \n- Methode: POST \n \nWeitere Informationen finden sich in der [Dokumentation]({docs_url})." }, "step": { "user": { diff --git a/homeassistant/components/geofency/translations/en.json b/homeassistant/components/geofency/translations/en.json index 5b97afb3bcc..1baacd32aa6 100644 --- a/homeassistant/components/geofency/translations/en.json +++ b/homeassistant/components/geofency/translations/en.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "Your Home Assistant instance needs to be accessible from the internet to receive webhook messages." }, "create_entry": { - "default": "To send events to Home Assistant, you will need to setup the webhook feature in Geofency.\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n\nSee [the documentation]({docs_url}) for further details." + "default": "To send events to Home Assistant, you will need to set up the webhook feature in Geofency.\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n\nSee [the documentation]({docs_url}) for further details." }, "step": { "user": { diff --git a/homeassistant/components/geofency/translations/es.json b/homeassistant/components/geofency/translations/es.json index 2462ec5bcc7..b642c95cc29 100644 --- a/homeassistant/components/geofency/translations/es.json +++ b/homeassistant/components/geofency/translations/es.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "Tu instancia de Home Assistant debe estar accesible desde Internet para recibir mensajes webhook." }, "create_entry": { - "default": "Para enviar eventos a Home Assistant, necesitar\u00e1s configurar la funci\u00f3n de webhook en Geofency.\n\nCompleta la siguiente informaci\u00f3n:\n\n- URL: `{webhook_url}`\n- M\u00e9todo: POST\n\nConsulta [la documentaci\u00f3n]({docs_url}) para m\u00e1s detalles." + "default": "Para enviar eventos a Home Assistant, necesitar\u00e1s configurar la funci\u00f3n de webhook en Geofency.\n\nCompleta la siguiente informaci\u00f3n:\n\n- URL: `{webhook_url}`\n- M\u00e9todo: POST\n\nConsulta [la documentaci\u00f3n]({docs_url}) para obtener m\u00e1s detalles." }, "step": { "user": { diff --git a/homeassistant/components/geofency/translations/it.json b/homeassistant/components/geofency/translations/it.json index 07647dc6df3..8582a3ca0f7 100644 --- a/homeassistant/components/geofency/translations/it.json +++ b/homeassistant/components/geofency/translations/it.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "L'istanza di Home Assistant deve essere accessibile da Internet per ricevere messaggi webhook." }, "create_entry": { - "default": "Per inviare eventi a Home Assistant, dovrai configurare la funzionalit\u00e0 webhook in Geofency.\n\n Compila le seguenti informazioni: \n\n - URL: `{webhook_url}` \n - Metodo: POST \n\n Vedi [la documentazione]({docs_url}) per ulteriori dettagli." + "default": "Per inviare eventi a Home Assistant, dovrai configurare la funzione webhook in Geofency. \n\nInserisci le seguenti informazioni: \n\n - URL: `{webhook_url}`\n - Metodo: POST \n\nConsulta [la documentazione]({docs_url}) per ulteriori dettagli." }, "step": { "user": { diff --git a/homeassistant/components/geofency/translations/ko.json b/homeassistant/components/geofency/translations/ko.json index b9303110e35..d98e10789a1 100644 --- a/homeassistant/components/geofency/translations/ko.json +++ b/homeassistant/components/geofency/translations/ko.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "cloud_not_connected": "Home Assistant \ud074\ub77c\uc6b0\ub4dc\uc5d0 \uc5f0\uacb0\ub418\uc5b4 \uc788\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.", "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.", "webhook_not_internet_accessible": "\uc6f9 \ud6c5 \uba54\uc2dc\uc9c0\ub97c \ubc1b\uc73c\ub824\uba74 \uc778\ud130\ub137\uc5d0\uc11c Home Assistant \uc778\uc2a4\ud134\uc2a4\uc5d0 \uc811\uadfc\ud560 \uc218 \uc788\uc5b4\uc57c \ud569\ub2c8\ub2e4." }, diff --git a/homeassistant/components/geofency/translations/no.json b/homeassistant/components/geofency/translations/no.json index 1ccd77230b6..6decbe82832 100644 --- a/homeassistant/components/geofency/translations/no.json +++ b/homeassistant/components/geofency/translations/no.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "Home Assistant forekomsten din m\u00e5 v\u00e6re tilgjengelig fra internett for \u00e5 kunne motta webhook meldinger" }, "create_entry": { - "default": "For \u00e5 kunne sende hendelser til Home Assistant, m\u00e5 du sette opp webhook-funksjonen i Geofency. \n\n Fyll ut f\u00f8lgende informasjon: \n\n - URL: `{webhook_url}` \n - Metode: POST \n\n Se [dokumentasjonen]({docs_url}) for ytterligere detaljer." + "default": "For \u00e5 sende hendelser til Home Assistant, m\u00e5 du sette opp webhook-funksjonen i Geofency. \n\n Fyll inn f\u00f8lgende informasjon: \n\n - URL: ` {webhook_url} `\n - Metode: POST \n\n Se [dokumentasjonen]( {docs_url} ) for ytterligere detaljer." }, "step": { "user": { diff --git a/homeassistant/components/geofency/translations/pt-BR.json b/homeassistant/components/geofency/translations/pt-BR.json index 226c388532e..a58dbcba013 100644 --- a/homeassistant/components/geofency/translations/pt-BR.json +++ b/homeassistant/components/geofency/translations/pt-BR.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "Sua inst\u00e2ncia do Home Assistant precisa estar acess\u00edvel pela Internet para receber mensagens de webhook." }, "create_entry": { - "default": "Para enviar eventos para o Home Assistant, voc\u00ea precisar\u00e1 configurar o recurso webhook no Geofency. \n\n Preencha as seguintes informa\u00e7\u00f5es: \n\n - URL: ` {webhook_url} ` \n - M\u00e9todo: POST \n\n Veja [a documenta\u00e7\u00e3o] ( {docs_url} ) para mais detalhes." + "default": "Para enviar eventos para o Home Assistant, voc\u00ea precisar\u00e1 configurar o recurso webhook no Geofency. \n\n Preencha as seguintes informa\u00e7\u00f5es: \n\n - URL: `{webhook_url}` \n - M\u00e9todo: POST \n\n Veja [a documenta\u00e7\u00e3o] ({docs_url}) para mais detalhes." }, "step": { "user": { diff --git a/homeassistant/components/geofency/translations/sk.json b/homeassistant/components/geofency/translations/sk.json index 933f73976d2..7087260ba6e 100644 --- a/homeassistant/components/geofency/translations/sk.json +++ b/homeassistant/components/geofency/translations/sk.json @@ -4,6 +4,15 @@ "cloud_not_connected": "Nie je pripojen\u00e9 k Home Assistant Cloud.", "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia.", "webhook_not_internet_accessible": "Va\u0161a in\u0161tancia Home Assistant mus\u00ed by\u0165 pr\u00edstupn\u00e1 z internetu, aby ste mohli prij\u00edma\u0165 spr\u00e1vy webhooku." + }, + "create_entry": { + "default": "Ak chcete odosiela\u0165 udalosti dom\u00e1cemu asistentovi, budete musie\u0165 nastavi\u0165 funkciu webhook v Geofency. \n\nVypl\u0148te nasleduj\u00face inform\u00e1cie: \n\n - URL: `{webhook_url}`\n - Met\u00f3da: POST \n\n\u010eal\u0161ie podrobnosti n\u00e1jdete v [dokument\u00e1cii]({docs_url})." + }, + "step": { + "user": { + "description": "Naozaj chcete nastavi\u0165 Geofency Webhook?", + "title": "Nastavte Geofency Webhook" + } } } } \ No newline at end of file diff --git a/homeassistant/components/geonetnz_quakes/__init__.py b/homeassistant/components/geonetnz_quakes/__init__.py index e09b4e720e3..dfe51f3119e 100644 --- a/homeassistant/components/geonetnz_quakes/__init__.py +++ b/homeassistant/components/geonetnz_quakes/__init__.py @@ -11,8 +11,7 @@ from homeassistant.const import ( CONF_LONGITUDE, CONF_RADIUS, CONF_SCAN_INTERVAL, - LENGTH_KILOMETERS, - LENGTH_MILES, + UnitOfLength, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import aiohttp_client, config_validation as cv @@ -96,7 +95,9 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b radius = config_entry.data[CONF_RADIUS] if hass.config.units is US_CUSTOMARY_SYSTEM: - radius = DistanceConverter.convert(radius, LENGTH_MILES, LENGTH_KILOMETERS) + radius = DistanceConverter.convert( + radius, UnitOfLength.MILES, UnitOfLength.KILOMETERS + ) # Create feed entity manager for all platforms. manager = GeonetnzQuakesFeedEntityManager(hass, config_entry, radius) feeds[config_entry.entry_id] = manager diff --git a/homeassistant/components/geonetnz_quakes/geo_location.py b/homeassistant/components/geonetnz_quakes/geo_location.py index a530c2d8fdb..411a0375461 100644 --- a/homeassistant/components/geonetnz_quakes/geo_location.py +++ b/homeassistant/components/geonetnz_quakes/geo_location.py @@ -9,7 +9,7 @@ from aio_geojson_geonetnz_quakes.feed_entry import GeonetnzQuakesFeedEntry from homeassistant.components.geo_location import GeolocationEvent from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TIME, LENGTH_KILOMETERS, LENGTH_MILES +from homeassistant.const import ATTR_TIME, UnitOfLength from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import entity_registry as er from homeassistant.helpers.dispatcher import async_dispatcher_connect @@ -81,7 +81,7 @@ class GeonetnzQuakesEvent(GeolocationEvent): self._feed_manager = feed_manager self._external_id = external_id self._attr_unique_id = f"{integration_id}_{external_id}" - self._attr_unit_of_measurement = LENGTH_KILOMETERS + self._attr_unit_of_measurement = UnitOfLength.KILOMETERS self._depth = None self._locality = None self._magnitude = None @@ -94,7 +94,7 @@ class GeonetnzQuakesEvent(GeolocationEvent): async def async_added_to_hass(self) -> None: """Call when entity is added to hass.""" if self.hass.config.units is US_CUSTOMARY_SYSTEM: - self._attr_unit_of_measurement = LENGTH_MILES + self._attr_unit_of_measurement = UnitOfLength.MILES self._remove_signal_delete = async_dispatcher_connect( self.hass, f"geonetnz_quakes_delete_{self._external_id}", @@ -138,7 +138,7 @@ class GeonetnzQuakesEvent(GeolocationEvent): # Convert distance if not metric system. if self.hass.config.units is US_CUSTOMARY_SYSTEM: self._attr_distance = DistanceConverter.convert( - feed_entry.distance_to_home, LENGTH_KILOMETERS, LENGTH_MILES + feed_entry.distance_to_home, UnitOfLength.KILOMETERS, UnitOfLength.MILES ) else: self._attr_distance = feed_entry.distance_to_home diff --git a/homeassistant/components/geonetnz_volcano/__init__.py b/homeassistant/components/geonetnz_volcano/__init__.py index da081b42599..fb7770a5461 100644 --- a/homeassistant/components/geonetnz_volcano/__init__.py +++ b/homeassistant/components/geonetnz_volcano/__init__.py @@ -14,8 +14,7 @@ from homeassistant.const import ( CONF_RADIUS, CONF_SCAN_INTERVAL, CONF_UNIT_SYSTEM, - LENGTH_KILOMETERS, - LENGTH_MILES, + UnitOfLength, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import aiohttp_client, config_validation as cv @@ -92,7 +91,9 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b radius = config_entry.data[CONF_RADIUS] unit_system = config_entry.data[CONF_UNIT_SYSTEM] if unit_system == IMPERIAL_UNITS: - radius = DistanceConverter.convert(radius, LENGTH_MILES, LENGTH_KILOMETERS) + radius = DistanceConverter.convert( + radius, UnitOfLength.MILES, UnitOfLength.KILOMETERS + ) # Create feed entity manager for all platforms. manager = GeonetnzVolcanoFeedEntityManager(hass, config_entry, radius, unit_system) hass.data[DOMAIN][FEED][config_entry.entry_id] = manager diff --git a/homeassistant/components/geonetnz_volcano/sensor.py b/homeassistant/components/geonetnz_volcano/sensor.py index e78641f99e2..25e02f44308 100644 --- a/homeassistant/components/geonetnz_volcano/sensor.py +++ b/homeassistant/components/geonetnz_volcano/sensor.py @@ -5,12 +5,7 @@ import logging from homeassistant.components.sensor import SensorEntity from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - ATTR_LATITUDE, - ATTR_LONGITUDE, - LENGTH_KILOMETERS, - LENGTH_MILES, -) +from homeassistant.const import ATTR_LATITUDE, ATTR_LONGITUDE, UnitOfLength from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -115,7 +110,9 @@ class GeonetnzVolcanoSensor(SensorEntity): if self._unit_system == IMPERIAL_UNITS: self._distance = round( DistanceConverter.convert( - feed_entry.distance_to_home, LENGTH_KILOMETERS, LENGTH_MILES + feed_entry.distance_to_home, + UnitOfLength.KILOMETERS, + UnitOfLength.MILES, ), 1, ) diff --git a/homeassistant/components/gios/manifest.json b/homeassistant/components/gios/manifest.json index d5f01d0f1c3..193c07bc4a5 100644 --- a/homeassistant/components/gios/manifest.json +++ b/homeassistant/components/gios/manifest.json @@ -3,7 +3,7 @@ "name": "GIO\u015a", "documentation": "https://www.home-assistant.io/integrations/gios", "codeowners": ["@bieniu"], - "requirements": ["gios==2.1.0"], + "requirements": ["gios==2.3.0"], "config_flow": true, "quality_scale": "platinum", "iot_class": "cloud_polling", diff --git a/homeassistant/components/gios/sensor.py b/homeassistant/components/gios/sensor.py index aa528f3d1d3..feb3f90a313 100644 --- a/homeassistant/components/gios/sensor.py +++ b/homeassistant/components/gios/sensor.py @@ -72,7 +72,6 @@ SENSOR_TYPES: tuple[GiosSensorEntityDescription, ...] = ( GiosSensorEntityDescription( key=ATTR_CO, name="CO", - device_class=SensorDeviceClass.CO, native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, state_class=SensorStateClass.MEASUREMENT, ), diff --git a/homeassistant/components/gios/translations/pt.json b/homeassistant/components/gios/translations/pt.json index 47e36006adb..bf2a71cea81 100644 --- a/homeassistant/components/gios/translations/pt.json +++ b/homeassistant/components/gios/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "A localiza\u00e7\u00e3o j\u00e1 est\u00e1 configurada" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "user": { diff --git a/homeassistant/components/gios/translations/sk.json b/homeassistant/components/gios/translations/sk.json index 23559b853da..7dee255ffe5 100644 --- a/homeassistant/components/gios/translations/sk.json +++ b/homeassistant/components/gios/translations/sk.json @@ -13,8 +13,14 @@ "data": { "name": "N\u00e1zov", "station_id": "ID meracej stanice" - } + }, + "title": "GIO\u015a (Po\u013esk\u00fd hlavn\u00fd in\u0161pektor\u00e1t ochrany \u017eivotn\u00e9ho prostredia)" } } + }, + "system_health": { + "info": { + "can_reach_server": "Dosta\u0148te sa na GIO\u015a server" + } } } \ No newline at end of file diff --git a/homeassistant/components/github/__init__.py b/homeassistant/components/github/__init__.py index bc2bab49bdb..1ce5d7df0ce 100644 --- a/homeassistant/components/github/__init__.py +++ b/homeassistant/components/github/__init__.py @@ -66,7 +66,10 @@ def async_cleanup_device_registry( for item in device.identifiers: if DOMAIN == item[0] and item[1] not in entry.options[CONF_REPOSITORIES]: LOGGER.debug( - "Unlinking device %s for untracked repository %s from config entry %s", + ( + "Unlinking device %s for untracked repository %s from config" + " entry %s" + ), device.id, item[1], entry.entry_id, diff --git a/homeassistant/components/github/translations/ko.json b/homeassistant/components/github/translations/ko.json new file mode 100644 index 00000000000..e1300423811 --- /dev/null +++ b/homeassistant/components/github/translations/ko.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "\uc11c\ube44\uc2a4\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/github/translations/sk.json b/homeassistant/components/github/translations/sk.json index f04d4a327f4..d3efa0f5eac 100644 --- a/homeassistant/components/github/translations/sk.json +++ b/homeassistant/components/github/translations/sk.json @@ -1,7 +1,19 @@ { "config": { "abort": { - "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1", + "could_not_register": "Nepodarilo sa zaregistrova\u0165 integr\u00e1ciu s GitHub" + }, + "progress": { + "wait_for_device": "1. Otvorte {url} \n2. Vlo\u017ete nasleduj\u00faci k\u013e\u00fa\u010d na autoriz\u00e1ciu integr\u00e1cie: \n```\n{code}\n```\n" + }, + "step": { + "repositories": { + "data": { + "repositories": "Vyberte \u00falo\u017eisk\u00e1, ktor\u00e9 chcete sledova\u0165." + }, + "title": "Konfigur\u00e1cia \u00falo\u017e\u00edsk" + } } } } \ No newline at end of file diff --git a/homeassistant/components/glances/sensor.py b/homeassistant/components/glances/sensor.py index a479cb260de..7e9d767f20d 100644 --- a/homeassistant/components/glances/sensor.py +++ b/homeassistant/components/glances/sensor.py @@ -13,13 +13,12 @@ from homeassistant.components.sensor import ( from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONF_NAME, - DATA_GIBIBYTES, - DATA_MEBIBYTES, PERCENTAGE, REVOLUTIONS_PER_MINUTE, STATE_UNAVAILABLE, - TEMP_CELSIUS, Platform, + UnitOfInformation, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import entity_registry @@ -59,7 +58,8 @@ SENSOR_TYPES: tuple[GlancesSensorEntityDescription, ...] = ( key="disk_use", type="fs", name_suffix="used", - native_unit_of_measurement=DATA_GIBIBYTES, + native_unit_of_measurement=UnitOfInformation.GIBIBYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:harddisk", state_class=SensorStateClass.MEASUREMENT, ), @@ -67,7 +67,8 @@ SENSOR_TYPES: tuple[GlancesSensorEntityDescription, ...] = ( key="disk_free", type="fs", name_suffix="free", - native_unit_of_measurement=DATA_GIBIBYTES, + native_unit_of_measurement=UnitOfInformation.GIBIBYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:harddisk", state_class=SensorStateClass.MEASUREMENT, ), @@ -83,7 +84,8 @@ SENSOR_TYPES: tuple[GlancesSensorEntityDescription, ...] = ( key="memory_use", type="mem", name_suffix="RAM used", - native_unit_of_measurement=DATA_MEBIBYTES, + native_unit_of_measurement=UnitOfInformation.MEBIBYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:memory", state_class=SensorStateClass.MEASUREMENT, ), @@ -91,7 +93,8 @@ SENSOR_TYPES: tuple[GlancesSensorEntityDescription, ...] = ( key="memory_free", type="mem", name_suffix="RAM free", - native_unit_of_measurement=DATA_MEBIBYTES, + native_unit_of_measurement=UnitOfInformation.MEBIBYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:memory", state_class=SensorStateClass.MEASUREMENT, ), @@ -107,7 +110,8 @@ SENSOR_TYPES: tuple[GlancesSensorEntityDescription, ...] = ( key="swap_use", type="memswap", name_suffix="Swap used", - native_unit_of_measurement=DATA_GIBIBYTES, + native_unit_of_measurement=UnitOfInformation.GIBIBYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:memory", state_class=SensorStateClass.MEASUREMENT, ), @@ -115,7 +119,8 @@ SENSOR_TYPES: tuple[GlancesSensorEntityDescription, ...] = ( key="swap_free", type="memswap", name_suffix="Swap free", - native_unit_of_measurement=DATA_GIBIBYTES, + native_unit_of_measurement=UnitOfInformation.GIBIBYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:memory", state_class=SensorStateClass.MEASUREMENT, ), @@ -166,7 +171,7 @@ SENSOR_TYPES: tuple[GlancesSensorEntityDescription, ...] = ( key="temperature_core", type="sensors", name_suffix="Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), @@ -174,7 +179,7 @@ SENSOR_TYPES: tuple[GlancesSensorEntityDescription, ...] = ( key="temperature_hdd", type="sensors", name_suffix="Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), @@ -213,7 +218,8 @@ SENSOR_TYPES: tuple[GlancesSensorEntityDescription, ...] = ( key="docker_memory_use", type="docker", name_suffix="Containers RAM used", - native_unit_of_measurement=DATA_MEBIBYTES, + native_unit_of_measurement=UnitOfInformation.MEBIBYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:docker", state_class=SensorStateClass.MEASUREMENT, ), diff --git a/homeassistant/components/glances/translations/pt.json b/homeassistant/components/glances/translations/pt.json index 711e6452a8e..b36b3d1dbab 100644 --- a/homeassistant/components/glances/translations/pt.json +++ b/homeassistant/components/glances/translations/pt.json @@ -9,7 +9,7 @@ "step": { "user": { "data": { - "host": "Servidor", + "host": "Endere\u00e7o", "password": "Palavra-passe", "port": "Porta", "ssl": "Utiliza um certificado SSL", diff --git a/homeassistant/components/goalzero/sensor.py b/homeassistant/components/goalzero/sensor.py index 2538639883c..e75f21f1152 100644 --- a/homeassistant/components/goalzero/sensor.py +++ b/homeassistant/components/goalzero/sensor.py @@ -11,15 +11,14 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - ELECTRIC_CURRENT_AMPERE, - ELECTRIC_POTENTIAL_VOLT, - ENERGY_WATT_HOUR, PERCENTAGE, - POWER_WATT, SIGNAL_STRENGTH_DECIBELS, - TEMP_CELSIUS, - TIME_MINUTES, - TIME_SECONDS, + UnitOfElectricCurrent, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfPower, + UnitOfTemperature, + UnitOfTime, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory @@ -34,14 +33,14 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( key="wattsIn", name="Watts in", device_class=SensorDeviceClass.POWER, - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="ampsIn", name="Amps in", device_class=SensorDeviceClass.CURRENT, - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, state_class=SensorStateClass.MEASUREMENT, entity_registry_enabled_default=False, ), @@ -49,14 +48,14 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( key="wattsOut", name="Watts out", device_class=SensorDeviceClass.POWER, - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="ampsOut", name="Amps out", device_class=SensorDeviceClass.CURRENT, - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, state_class=SensorStateClass.MEASUREMENT, entity_registry_enabled_default=False, ), @@ -64,7 +63,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( key="whOut", name="Wh out", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, state_class=SensorStateClass.TOTAL_INCREASING, entity_registry_enabled_default=False, ), @@ -72,14 +71,14 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( key="whStored", name="Wh stored", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="volts", name="Volts", device_class=SensorDeviceClass.VOLTAGE, - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, entity_registry_enabled_default=False, ), SensorEntityDescription( @@ -92,13 +91,13 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( key="timeToEmptyFull", name="Time to empty/full", device_class=SensorDeviceClass.DURATION, - native_unit_of_measurement=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, ), SensorEntityDescription( key="temperature", name="Temperature", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, entity_category=EntityCategory.DIAGNOSTIC, ), SensorEntityDescription( @@ -112,7 +111,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="timestamp", name="Total run time", - native_unit_of_measurement=TIME_SECONDS, + native_unit_of_measurement=UnitOfTime.SECONDS, entity_registry_enabled_default=False, entity_category=EntityCategory.DIAGNOSTIC, ), diff --git a/homeassistant/components/goalzero/translations/ko.json b/homeassistant/components/goalzero/translations/ko.json index dd947530c16..29099caa00f 100644 --- a/homeassistant/components/goalzero/translations/ko.json +++ b/homeassistant/components/goalzero/translations/ko.json @@ -1,7 +1,9 @@ { "config": { "abort": { - "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "invalid_host": "\ud638\uc2a4\ud2b8 \uc774\ub984 \ub610\ub294 IP \uc8fc\uc18c\uac00 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" }, "error": { "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", diff --git a/homeassistant/components/goalzero/translations/pt.json b/homeassistant/components/goalzero/translations/pt.json index ce945ba68d2..4b43b660db0 100644 --- a/homeassistant/components/goalzero/translations/pt.json +++ b/homeassistant/components/goalzero/translations/pt.json @@ -4,14 +4,14 @@ "already_configured": "Conta j\u00e1 configurada" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", - "invalid_host": "Nome de servidor ou endere\u00e7o IP inv\u00e1lido.", + "cannot_connect": "A liga\u00e7\u00e3o falhou", + "invalid_host": "Endere\u00e7o IP ou hostname inv\u00e1lido.", "unknown": "Erro inesperado" }, "step": { "user": { "data": { - "host": "Servidor", + "host": "Endere\u00e7o", "name": "Nome" } } diff --git a/homeassistant/components/goalzero/translations/sk.json b/homeassistant/components/goalzero/translations/sk.json index dc9ffc47876..9f904bee34b 100644 --- a/homeassistant/components/goalzero/translations/sk.json +++ b/homeassistant/components/goalzero/translations/sk.json @@ -11,6 +11,9 @@ "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { + "confirm_discovery": { + "description": "Odpor\u00fa\u010da sa rezerv\u00e1cia DHCP na va\u0161om smerova\u010di. Ak nie je nastaven\u00e9, zariadenie m\u00f4\u017ee by\u0165 nedostupn\u00e9, k\u00fdm Home Assistant nezist\u00ed nov\u00fa IP adresu. Pozrite si pou\u017e\u00edvate\u013esk\u00fa pr\u00edru\u010dku k smerova\u010du." + }, "user": { "data": { "host": "Hostite\u013e", diff --git a/homeassistant/components/gogogate2/common.py b/homeassistant/components/gogogate2/common.py index bfbadf86ee2..3b72e85276c 100644 --- a/homeassistant/components/gogogate2/common.py +++ b/homeassistant/components/gogogate2/common.py @@ -4,9 +4,15 @@ from __future__ import annotations from collections.abc import Awaitable, Callable, Mapping from datetime import timedelta import logging -from typing import Any, NamedTuple +from typing import Any, NamedTuple, Union -from ismartgate import AbstractGateApi, GogoGate2Api, ISmartGateApi +from ismartgate import ( + AbstractGateApi, + GogoGate2Api, + GogoGate2InfoResponse, + ISmartGateApi, + ISmartGateInfoResponse, +) from ismartgate.common import AbstractDoor, get_door_by_id from homeassistant.config_entries import ConfigEntry @@ -39,7 +45,9 @@ class StateData(NamedTuple): door: AbstractDoor | None -class DeviceDataUpdateCoordinator(DataUpdateCoordinator): +class DeviceDataUpdateCoordinator( + DataUpdateCoordinator[Union[GogoGate2InfoResponse, ISmartGateInfoResponse]] +): """Manages polling for state changes from the device.""" def __init__( @@ -50,7 +58,10 @@ class DeviceDataUpdateCoordinator(DataUpdateCoordinator): *, name: str, update_interval: timedelta, - update_method: Callable[[], Awaitable] | None = None, + update_method: Callable[ + [], Awaitable[GogoGate2InfoResponse | ISmartGateInfoResponse] + ] + | None = None, request_refresh_debouncer: Debouncer | None = None, ) -> None: """Initialize the data update coordinator.""" @@ -131,7 +142,7 @@ def get_data_update_coordinator( if DATA_UPDATE_COORDINATOR not in config_entry_data: api = get_api(hass, config_entry.data) - async def async_update_data(): + async def async_update_data() -> GogoGate2InfoResponse | ISmartGateInfoResponse: try: return await api.async_info() except Exception as exception: diff --git a/homeassistant/components/gogogate2/sensor.py b/homeassistant/components/gogogate2/sensor.py index 1f48dde007e..378d9742da0 100644 --- a/homeassistant/components/gogogate2/sensor.py +++ b/homeassistant/components/gogogate2/sensor.py @@ -11,7 +11,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import PERCENTAGE, TEMP_CELSIUS +from homeassistant.const import PERCENTAGE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -96,7 +96,7 @@ class DoorSensorTemperature(DoorSensorEntity): _attr_device_class = SensorDeviceClass.TEMPERATURE _attr_state_class = SensorStateClass.MEASUREMENT - _attr_native_unit_of_measurement = TEMP_CELSIUS + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS def __init__( self, diff --git a/homeassistant/components/gogogate2/strings.json b/homeassistant/components/gogogate2/strings.json index 7c165f90d06..17349764642 100644 --- a/homeassistant/components/gogogate2/strings.json +++ b/homeassistant/components/gogogate2/strings.json @@ -10,7 +10,7 @@ }, "step": { "user": { - "title": "Setup Gogogate2 or ismartgate", + "title": "Set up Gogogate2 or ismartgate", "description": "Provide requisite information below.", "data": { "ip_address": "[%key:common::config_flow::data::ip%]", diff --git a/homeassistant/components/gogogate2/translations/en.json b/homeassistant/components/gogogate2/translations/en.json index 53e578526b2..cfdb4ff1514 100644 --- a/homeassistant/components/gogogate2/translations/en.json +++ b/homeassistant/components/gogogate2/translations/en.json @@ -16,7 +16,7 @@ "username": "Username" }, "description": "Provide requisite information below.", - "title": "Setup Gogogate2 or ismartgate" + "title": "Set up Gogogate2 or ismartgate" } } } diff --git a/homeassistant/components/gogogate2/translations/it.json b/homeassistant/components/gogogate2/translations/it.json index 71510b90040..4bb118fc4c4 100644 --- a/homeassistant/components/gogogate2/translations/it.json +++ b/homeassistant/components/gogogate2/translations/it.json @@ -16,7 +16,7 @@ "username": "Nome utente" }, "description": "Fornire le informazioni richieste di seguito.", - "title": "Imposta Gogogate2 o ismartgate" + "title": "Configura Gogogate2 o ismartgate" } } } diff --git a/homeassistant/components/gogogate2/translations/pt.json b/homeassistant/components/gogogate2/translations/pt.json index eb35226e8e9..6b3b54b547a 100644 --- a/homeassistant/components/gogogate2/translations/pt.json +++ b/homeassistant/components/gogogate2/translations/pt.json @@ -1,10 +1,10 @@ { "config": { "abort": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "user": { diff --git a/homeassistant/components/gogogate2/translations/sk.json b/homeassistant/components/gogogate2/translations/sk.json index b704eb5be7f..f0efef7fbe7 100644 --- a/homeassistant/components/gogogate2/translations/sk.json +++ b/homeassistant/components/gogogate2/translations/sk.json @@ -15,7 +15,8 @@ "password": "Heslo", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" }, - "description": "Ni\u017e\u0161ie uve\u010fte po\u017eadovan\u00e9 inform\u00e1cie." + "description": "Ni\u017e\u0161ie uve\u010fte po\u017eadovan\u00e9 inform\u00e1cie.", + "title": "Nastavte Gogogate2 alebo ismartgate" } } } diff --git a/homeassistant/components/goodwe/number.py b/homeassistant/components/goodwe/number.py index b8196b05252..10a825aaa3c 100644 --- a/homeassistant/components/goodwe/number.py +++ b/homeassistant/components/goodwe/number.py @@ -7,9 +7,13 @@ import logging from goodwe import Inverter, InverterError -from homeassistant.components.number import NumberEntity, NumberEntityDescription +from homeassistant.components.number import ( + NumberDeviceClass, + NumberEntity, + NumberEntityDescription, +) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import PERCENTAGE, POWER_WATT +from homeassistant.const import PERCENTAGE, UnitOfPower from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo, EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -42,7 +46,8 @@ NUMBERS = ( name="Grid export limit", icon="mdi:transmission-tower", entity_category=EntityCategory.CONFIG, - native_unit_of_measurement=POWER_WATT, + device_class=NumberDeviceClass.POWER, + native_unit_of_measurement=UnitOfPower.WATT, native_step=100, native_min_value=0, native_max_value=10000, diff --git a/homeassistant/components/goodwe/sensor.py b/homeassistant/components/goodwe/sensor.py index 22439a05491..546cc0fdb39 100644 --- a/homeassistant/components/goodwe/sensor.py +++ b/homeassistant/components/goodwe/sensor.py @@ -17,13 +17,13 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - ELECTRIC_CURRENT_AMPERE, - ELECTRIC_POTENTIAL_VOLT, - ENERGY_KILO_WATT_HOUR, - FREQUENCY_HERTZ, PERCENTAGE, - POWER_WATT, - TEMP_CELSIUS, + UnitOfElectricCurrent, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfFrequency, + UnitOfPower, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import DeviceInfo, EntityCategory @@ -86,25 +86,25 @@ _DESCRIPTIONS: dict[str, GoodweSensorEntityDescription] = { key="A", device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, ), "V": GoodweSensorEntityDescription( key="V", device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, ), "W": GoodweSensorEntityDescription( key="W", device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, ), "kWh": GoodweSensorEntityDescription( key="kWh", device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, value=lambda prev, val: prev if not val else val, available=lambda entity: entity.coordinator.data is not None, ), @@ -112,13 +112,13 @@ _DESCRIPTIONS: dict[str, GoodweSensorEntityDescription] = { key="C", device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, ), "Hz": GoodweSensorEntityDescription( key="Hz", - device_class=SensorDeviceClass.VOLTAGE, + device_class=SensorDeviceClass.FREQUENCY, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=FREQUENCY_HERTZ, + native_unit_of_measurement=UnitOfFrequency.HERTZ, ), "%": GoodweSensorEntityDescription( key="%", diff --git a/homeassistant/components/goodwe/translations/ko.json b/homeassistant/components/goodwe/translations/ko.json index 9aaa9eacf20..996cd14e564 100644 --- a/homeassistant/components/goodwe/translations/ko.json +++ b/homeassistant/components/goodwe/translations/ko.json @@ -1,7 +1,17 @@ { "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4" + }, + "error": { + "connection_error": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" + }, "step": { "user": { + "data": { + "host": "IP \uc8fc\uc18c" + }, "description": "\uc778\ubc84\ud130\uc5d0 \uc5f0\uacb0" } } diff --git a/homeassistant/components/goodwe/translations/sk.json b/homeassistant/components/goodwe/translations/sk.json index 9a7539ecc3b..6d30dee1a11 100644 --- a/homeassistant/components/goodwe/translations/sk.json +++ b/homeassistant/components/goodwe/translations/sk.json @@ -12,7 +12,8 @@ "data": { "host": "IP adresa" }, - "description": "Pripoji\u0165 k meni\u010du" + "description": "Pripoji\u0165 k meni\u010du", + "title": "Meni\u010d GoodWe" } } } diff --git a/homeassistant/components/google/__init__.py b/homeassistant/components/google/__init__.py index c4e51739efd..bc5e814b24d 100644 --- a/homeassistant/components/google/__init__.py +++ b/homeassistant/components/google/__init__.py @@ -11,7 +11,6 @@ from gcal_sync.api import GoogleCalendarService from gcal_sync.exceptions import ApiException, AuthException from gcal_sync.model import DateOrDatetime, Event import voluptuous as vol -from voluptuous.error import Error as VoluptuousError import yaml from homeassistant.config_entries import ConfigEntry @@ -143,6 +142,14 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass.data.setdefault(DOMAIN, {}) hass.data[DOMAIN][entry.entry_id] = {} + # Validate google_calendars.yaml (if present) as soon as possible to return + # helpful error messages. + try: + await hass.async_add_executor_job(load_config, hass.config.path(YAML_DEVICES)) + except vol.Invalid as err: + _LOGGER.error("Configuration error in %s: %s", YAML_DEVICES, str(err)) + return False + implementation = ( await config_entry_oauth2_flow.async_get_config_entry_implementation( hass, entry @@ -322,11 +329,7 @@ def load_config(path: str) -> dict[str, Any]: with open(path, encoding="utf8") as file: data = yaml.safe_load(file) for calendar in data: - try: - calendars.update({calendar[CONF_CAL_ID]: DEVICE_SCHEMA(calendar)}) - except VoluptuousError as exception: - # keep going - _LOGGER.warning("Calendar Invalid Data: %s", exception) + calendars[calendar[CONF_CAL_ID]] = DEVICE_SCHEMA(calendar) except FileNotFoundError as err: _LOGGER.debug("Error reading calendar configuration: %s", err) # When YAML file could not be loaded/did not contain a dict diff --git a/homeassistant/components/google/application_credentials.py b/homeassistant/components/google/application_credentials.py index 3d557630b05..60ad9b3275e 100644 --- a/homeassistant/components/google/application_credentials.py +++ b/homeassistant/components/google/application_credentials.py @@ -26,7 +26,9 @@ async def async_get_auth_implementation( async def async_get_description_placeholders(hass: HomeAssistant) -> dict[str, str]: """Return description placeholders for the credentials dialog.""" return { - "oauth_consent_url": "https://console.cloud.google.com/apis/credentials/consent", + "oauth_consent_url": ( + "https://console.cloud.google.com/apis/credentials/consent" + ), "more_info_url": "https://www.home-assistant.io/integrations/google/", "oauth_creds_url": "https://console.cloud.google.com/apis/credentials", } diff --git a/homeassistant/components/google/calendar.py b/homeassistant/components/google/calendar.py index eff26c2fbc4..702146ee052 100644 --- a/homeassistant/components/google/calendar.py +++ b/homeassistant/components/google/calendar.py @@ -6,9 +6,14 @@ import asyncio from collections.abc import Iterable from datetime import datetime, timedelta import logging -from typing import Any +from typing import Any, Union, cast -from gcal_sync.api import GoogleCalendarService, ListEventsRequest, SyncEventsRequest +from gcal_sync.api import ( + GoogleCalendarService, + ListEventsRequest, + Range, + SyncEventsRequest, +) from gcal_sync.exceptions import ApiException from gcal_sync.model import AccessRole, DateOrDatetime, Event from gcal_sync.store import ScopedCalendarStore @@ -18,7 +23,13 @@ import voluptuous as vol from homeassistant.components.calendar import ( ENTITY_ID_FORMAT, + EVENT_DESCRIPTION, + EVENT_END, + EVENT_RRULE, + EVENT_START, + EVENT_SUMMARY, CalendarEntity, + CalendarEntityFeature, CalendarEvent, extract_offset, is_offset_reached, @@ -56,7 +67,6 @@ from .api import get_feature_access from .const import ( DATA_SERVICE, DATA_STORE, - EVENT_DESCRIPTION, EVENT_END_DATE, EVENT_END_DATETIME, EVENT_IN, @@ -64,7 +74,6 @@ from .const import ( EVENT_IN_WEEKS, EVENT_START_DATE, EVENT_START_DATETIME, - EVENT_SUMMARY, EVENT_TYPES_CONF, FeatureAccess, ) @@ -82,6 +91,10 @@ SYNC_EVENT_MIN_TIME = timedelta(days=-90) # are not opaque are ignored by default. OPAQUE = "opaque" +# Google calendar prefixes recurrence rules with RRULE: which +# we need to strip when working with the frontend recurrence rule values +RRULE_PREFIX = "RRULE:" + _EVENT_IN_TYPES = vol.Schema( { vol.Exclusive(EVENT_IN_DAYS, EVENT_TYPES_CONF): cv.positive_int, @@ -162,8 +175,9 @@ async def async_setup_entry( entity_enabled = data.get(CONF_TRACK, True) if not entity_enabled: _LOGGER.warning( - "The 'track' option in google_calendars.yaml has been deprecated. The setting " - "has been imported to the UI, and should now be removed from google_calendars.yaml" + "The 'track' option in google_calendars.yaml has been deprecated." + " The setting has been imported to the UI, and should now be" + " removed from google_calendars.yaml" ) entity_name = data[CONF_DEVICE_ID] # The unique id is based on the config entry and calendar id since multiple accounts @@ -201,10 +215,13 @@ async def async_setup_entry( # Prefer calendar sync down of resources when possible. However, sync does not work # for search. Also free-busy calendars denormalize recurring events as individual # events which is not efficient for sync + support_write = ( + calendar_item.access_role.is_writer + and get_feature_access(hass, config_entry) is FeatureAccess.read_write + ) if ( search := data.get(CONF_SEARCH) - or calendar_item.access_role == AccessRole.FREE_BUSY_READER - ): + ) or calendar_item.access_role == AccessRole.FREE_BUSY_READER: coordinator = CalendarQueryUpdateCoordinator( hass, calendar_service, @@ -212,6 +229,7 @@ async def async_setup_entry( calendar_id, search, ) + support_write = False else: request_template = SyncEventsRequest( calendar_id=calendar_id, @@ -235,6 +253,7 @@ async def async_setup_entry( generate_entity_id(ENTITY_ID_FORMAT, entity_name, hass=hass), unique_id, entity_enabled, + support_write, ) ) @@ -250,7 +269,10 @@ async def async_setup_entry( await hass.async_add_executor_job(append_calendars_to_config) platform = entity_platform.async_get_current_platform() - if get_feature_access(hass, config_entry) is FeatureAccess.read_write: + if ( + any(calendar_item.access_role.is_writer for calendar_item in result.items) + and get_feature_access(hass, config_entry) is FeatureAccess.read_write + ): platform.async_register_entity_service( SERVICE_CREATE_EVENT, CREATE_EVENT_SCHEMA, @@ -369,7 +391,12 @@ class CalendarQueryUpdateCoordinator(DataUpdateCoordinator[list[Event]]): return self.data -class GoogleCalendarEntity(CoordinatorEntity, CalendarEntity): +class GoogleCalendarEntity( + CoordinatorEntity[ + Union[CalendarSyncUpdateCoordinator, CalendarQueryUpdateCoordinator] + ], + CalendarEntity, +): """A calendar event entity.""" _attr_has_entity_name = True @@ -382,10 +409,10 @@ class GoogleCalendarEntity(CoordinatorEntity, CalendarEntity): entity_id: str, unique_id: str | None, entity_enabled: bool, + supports_write: bool, ) -> None: """Create the Calendar event device.""" super().__init__(coordinator) - self.coordinator = coordinator self.calendar_id = calendar_id self._ignore_availability: bool = data.get(CONF_IGNORE_AVAILABILITY, False) self._event: CalendarEvent | None = None @@ -395,6 +422,10 @@ class GoogleCalendarEntity(CoordinatorEntity, CalendarEntity): self.entity_id = entity_id self._attr_unique_id = unique_id self._attr_entity_registry_enabled_default = entity_enabled + if supports_write: + self._attr_supported_features = ( + CalendarEntityFeature.CREATE_EVENT | CalendarEntityFeature.DELETE_EVENT + ) @property def should_poll(self) -> bool: @@ -486,10 +517,69 @@ class GoogleCalendarEntity(CoordinatorEntity, CalendarEntity): started, handled by CalendarEntity parent class. """ + async def async_create_event(self, **kwargs: Any) -> None: + """Add a new event to calendar.""" + dtstart = kwargs[EVENT_START] + dtend = kwargs[EVENT_END] + start: DateOrDatetime + end: DateOrDatetime + if isinstance(dtstart, datetime): + start = DateOrDatetime( + date_time=dt_util.as_local(dtstart), + timezone=str(dt_util.DEFAULT_TIME_ZONE), + ) + end = DateOrDatetime( + date_time=dt_util.as_local(dtend), + timezone=str(dt_util.DEFAULT_TIME_ZONE), + ) + else: + start = DateOrDatetime(date=dtstart) + end = DateOrDatetime(date=dtend) + event = Event.parse_obj( + { + EVENT_SUMMARY: kwargs[EVENT_SUMMARY], + "start": start, + "end": end, + EVENT_DESCRIPTION: kwargs.get(EVENT_DESCRIPTION), + } + ) + if rrule := kwargs.get(EVENT_RRULE): + event.recurrence = [f"{RRULE_PREFIX}{rrule}"] + + await cast( + CalendarSyncUpdateCoordinator, self.coordinator + ).sync.store_service.async_add_event(event) + await self.coordinator.async_refresh() + + async def async_delete_event( + self, + uid: str, + recurrence_id: str | None = None, + recurrence_range: str | None = None, + ) -> None: + """Delete an event on the calendar.""" + range_value: Range = Range.NONE + if recurrence_range == Range.THIS_AND_FUTURE: + range_value = Range.THIS_AND_FUTURE + await cast( + CalendarSyncUpdateCoordinator, self.coordinator + ).sync.store_service.async_delete_event( + ical_uuid=uid, + event_id=recurrence_id, + recurrence_range=range_value, + ) + await self.coordinator.async_refresh() + def _get_calendar_event(event: Event) -> CalendarEvent: """Return a CalendarEvent from an API event.""" + rrule: str | None = None + if len(event.recurrence) == 1: + rrule = event.recurrence[0].lstrip(RRULE_PREFIX) return CalendarEvent( + uid=event.ical_uuid, + recurrence_id=event.id if event.recurring_event_id else None, + rrule=rrule, summary=event.summary, start=event.start.value, end=event.end.value, @@ -537,7 +627,9 @@ async def async_create_event(entity: GoogleCalendarEntity, call: ServiceCall) -> raise ValueError("Missing required fields to set start or end date/datetime") try: - await entity.coordinator.sync.api.async_create_event( + await cast( + CalendarSyncUpdateCoordinator, entity.coordinator + ).sync.api.async_create_event( entity.calendar_id, Event( summary=call.data[EVENT_SUMMARY], diff --git a/homeassistant/components/google/manifest.json b/homeassistant/components/google/manifest.json index 542eb72206a..100e128c8e3 100644 --- a/homeassistant/components/google/manifest.json +++ b/homeassistant/components/google/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "dependencies": ["application_credentials"], "documentation": "https://www.home-assistant.io/integrations/calendar.google/", - "requirements": ["gcal-sync==4.0.4", "oauth2client==4.1.3"], + "requirements": ["gcal-sync==4.1.0", "oauth2client==4.1.3"], "codeowners": ["@allenporter"], "iot_class": "cloud_polling", "loggers": ["googleapiclient"] diff --git a/homeassistant/components/google/translations/ko.json b/homeassistant/components/google/translations/ko.json index e4fc1875308..bd537d141e3 100644 --- a/homeassistant/components/google/translations/ko.json +++ b/homeassistant/components/google/translations/ko.json @@ -1,9 +1,22 @@ { "config": { "abort": { - "missing_configuration": "\uad6c\uc131\uc694\uc18c\uac00 \uad6c\uc131\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4. \uc124\uba85\uc11c\ub97c \ucc38\uace0\ud574\uc8fc\uc138\uc694." + "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_access_token": "\uc561\uc138\uc2a4 \ud1a0\ud070\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "missing_configuration": "\uad6c\uc131\uc694\uc18c\uac00 \uad6c\uc131\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4. \uc124\uba85\uc11c\ub97c \ucc38\uace0\ud574\uc8fc\uc138\uc694.", + "oauth_error": "\uc798\ubabb\ub41c \ud1a0\ud070 \ub370\uc774\ud130\ub97c \ubc1b\uc558\uc2b5\ub2c8\ub2e4.", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4", + "timeout_connect": "\uc5f0\uacb0 \uc124\uc815 \uc2dc\uac04 \ucd08\uacfc" + }, + "create_entry": { + "default": "\uc131\uacf5\uc801\uc73c\ub85c \uc778\uc99d\ub418\uc5c8\uc2b5\ub2c8\ub2e4" }, "step": { + "pick_implementation": { + "title": "\uc778\uc99d \ubc29\ubc95 \uc120\ud0dd\ud558\uae30" + }, "reauth_confirm": { "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" } diff --git a/homeassistant/components/google/translations/nl.json b/homeassistant/components/google/translations/nl.json index 05b053497c4..017fac88730 100644 --- a/homeassistant/components/google/translations/nl.json +++ b/homeassistant/components/google/translations/nl.json @@ -1,4 +1,7 @@ { + "application_credentials": { + "description": "Volg de [instructies]({more_info_url}) voor [OAuth consent screen]({oauth_consent_url}) om Home Assistant toegang te geven tot Google Agenda. Je moet ook `Application Credentials` aanmaken gekoppeld aan je agenda:\n1. Ga naar [Credentials]({oauth_creds_url}) en klik **Create Credentials**.\n2. Selecteer **OAuth client ID** van de drop-down lijst.\n3. Selecteer **TV and Limited Input devices** als `Application Type`.\n" + }, "config": { "abort": { "already_configured": "Account is al geconfigureerd", diff --git a/homeassistant/components/google/translations/pt.json b/homeassistant/components/google/translations/pt.json index 518809302e3..616d6e59c10 100644 --- a/homeassistant/components/google/translations/pt.json +++ b/homeassistant/components/google/translations/pt.json @@ -1,8 +1,9 @@ { "config": { "abort": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", - "missing_configuration": "O componente n\u00e3o est\u00e1 configurado. Por favor, siga a documenta\u00e7\u00e3o." + "cannot_connect": "A liga\u00e7\u00e3o falhou", + "missing_configuration": "O componente n\u00e3o est\u00e1 configurado. Por favor, siga a documenta\u00e7\u00e3o.", + "timeout_connect": "Excedido o tempo limite para estabelecer liga\u00e7\u00e3o" }, "create_entry": { "default": "Autenticado com sucesso" diff --git a/homeassistant/components/google/translations/sk.json b/homeassistant/components/google/translations/sk.json index 01103448bea..95ab65271ef 100644 --- a/homeassistant/components/google/translations/sk.json +++ b/homeassistant/components/google/translations/sk.json @@ -1,9 +1,13 @@ { + "application_credentials": { + "description": "Pod\u013ea [pokynov]({more_info_url}) pre [obrazovka s\u00fahlasu s protokolom OAuth]({oauth_consent_url}) povo\u013ete Asistentovi dom\u00e1cnosti pr\u00edstup k v\u00e1\u0161mu Kalend\u00e1ru Google. Mus\u00edte tie\u017e vytvori\u0165 poverenia aplik\u00e1cie prepojen\u00e9 s va\u0161\u00edm kalend\u00e1rom:\n 1. Prejdite na [Poverenia]({oauth_creds_url}) a kliknite na **Vytvori\u0165 poverenia**.\n 1. Z rozba\u013eovacieho zoznamu vyberte **ID klienta OAuth**.\n 1. Ako typ aplik\u00e1cie vyberte **TV a zariadenia s obmedzen\u00fdm vstupom**. \n\n" + }, "config": { "abort": { "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "cannot_connect": "Nepodarilo sa pripoji\u0165", + "code_expired": "Platnos\u0165 overovacieho k\u00f3du vypr\u0161ala alebo nastavenie poveren\u00ed je neplatn\u00e9, sk\u00faste to znova.", "invalid_access_token": "Neplatn\u00fd pr\u00edstupov\u00fd token", "missing_configuration": "Komponent nie je nakonfigurovan\u00fd. Postupujte pod\u013ea dokument\u00e1cie.", "oauth_error": "Prijat\u00e9 neplatn\u00e9 \u00fadaje tokenu.", @@ -24,6 +28,7 @@ "title": "Vyberte met\u00f3du overenia" }, "reauth_confirm": { + "description": "Integr\u00e1cia Kalend\u00e1ra Google vy\u017eaduje op\u00e4tovn\u00e9 overenie v\u00e1\u0161ho \u00fa\u010dtu", "title": "Znova overi\u0165 integr\u00e1ciu" } } diff --git a/homeassistant/components/google_assistant/__init__.py b/homeassistant/components/google_assistant/__init__.py index 644179858a3..82868828531 100644 --- a/homeassistant/components/google_assistant/__init__.py +++ b/homeassistant/components/google_assistant/__init__.py @@ -153,7 +153,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: if agent_user_id is None: _LOGGER.warning( - "No agent_user_id supplied for request_sync. Call as a user or pass in user id as agent_user_id" + "No agent_user_id supplied for request_sync. Call as a user or pass in" + " user id as agent_user_id" ) return diff --git a/homeassistant/components/google_assistant/button.py b/homeassistant/components/google_assistant/button.py index 322a021053a..c77edf4520b 100644 --- a/homeassistant/components/google_assistant/button.py +++ b/homeassistant/components/google_assistant/button.py @@ -49,5 +49,6 @@ class SyncButton(ButtonEntity): result = await self._google_config.async_sync_entities(agent_user_id) if result != 200: raise HomeAssistantError( - f"Unable to sync devices with result code: {result}, check log for more info." + f"Unable to sync devices with result code: {result}, check log for more" + " info." ) diff --git a/homeassistant/components/google_assistant/helpers.py b/homeassistant/components/google_assistant/helpers.py index 3351af94648..3f9c90d40e1 100644 --- a/homeassistant/components/google_assistant/helpers.py +++ b/homeassistant/components/google_assistant/helpers.py @@ -52,7 +52,11 @@ LOCAL_SDK_MIN_VERSION = AwesomeVersion("2.1.5") @callback def _get_registry_entries( hass: HomeAssistant, entity_id: str -) -> tuple[device_registry.DeviceEntry | None, area_registry.AreaEntry | None]: +) -> tuple[ + entity_registry.RegistryEntry | None, + device_registry.DeviceEntry | None, + area_registry.AreaEntry | None, +]: """Get registry entries.""" ent_reg = entity_registry.async_get(hass) dev_reg = device_registry.async_get(hass) @@ -75,7 +79,7 @@ def _get_registry_entries( else: area_entry = None - return device_entry, area_entry + return entity_entry, device_entry, area_entry class AbstractConfig(ABC): @@ -345,7 +349,10 @@ class AbstractConfig(ABC): not version or AwesomeVersion(version) < LOCAL_SDK_MIN_VERSION ): _LOGGER.warning( - "Local SDK version is too old (%s), check documentation on how to update to the latest version", + ( + "Local SDK version is too old (%s), check documentation on how to" + " update to the latest version" + ), version, ) self._local_sdk_version_warn = True @@ -364,7 +371,10 @@ class AbstractConfig(ABC): # No agent user linked to this webhook, means that the user has somehow unregistered # removing webhook and stopping processing of this request. _LOGGER.error( - "Cannot process request for webhook %s as no linked agent user is found:\n%s\n", + ( + "Cannot process request for webhook %s as no linked agent user is" + " found:\n%s\n" + ), webhook_id, pprint.pformat(payload), ) @@ -588,7 +598,9 @@ class GoogleEntity: name = (entity_config.get(CONF_NAME) or state.name).strip() # Find entity/device/area registry entries - device_entry, area_entry = _get_registry_entries(self.hass, self.entity_id) + entity_entry, device_entry, area_entry = _get_registry_entries( + self.hass, self.entity_id + ) # Build the device info device = { @@ -603,8 +615,12 @@ class GoogleEntity: } # Add aliases - if aliases := entity_config.get(CONF_ALIASES): - device["name"]["nicknames"] = [name] + aliases + if (config_aliases := entity_config.get(CONF_ALIASES, [])) or ( + entity_entry and entity_entry.aliases + ): + device["name"]["nicknames"] = [name] + config_aliases + if entity_entry: + device["name"]["nicknames"].extend(entity_entry.aliases) # Add local SDK info if enabled if self.config.is_local_sdk_active and self.should_expose_local(): diff --git a/homeassistant/components/google_assistant/trait.py b/homeassistant/components/google_assistant/trait.py index a76b0a7d687..920a63b70f8 100644 --- a/homeassistant/components/google_assistant/trait.py +++ b/homeassistant/components/google_assistant/trait.py @@ -63,8 +63,7 @@ from homeassistant.const import ( STATE_STANDBY, STATE_UNAVAILABLE, STATE_UNKNOWN, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, + UnitOfTemperature, ) from homeassistant.core import DOMAIN as HA_DOMAIN from homeassistant.helpers.network import get_url @@ -175,7 +174,7 @@ def register_trait(trait): def _google_temp_unit(units): """Return Google temperature unit.""" - if units == TEMP_FAHRENHEIT: + if units == UnitOfTemperature.FAHRENHEIT: return "F" return "C" @@ -845,7 +844,10 @@ class TemperatureControlTrait(_Trait): current_temp = self.state.state if current_temp not in (STATE_UNKNOWN, STATE_UNAVAILABLE): temp = round( - TemperatureConverter.convert(float(current_temp), unit, TEMP_CELSIUS), 1 + TemperatureConverter.convert( + float(current_temp), unit, UnitOfTemperature.CELSIUS + ), + 1, ) response["temperatureSetpointCelsius"] = temp response["temperatureAmbientCelsius"] = temp @@ -951,7 +953,10 @@ class TemperatureSettingTrait(_Trait): current_temp = attrs.get(climate.ATTR_CURRENT_TEMPERATURE) if current_temp is not None: response["thermostatTemperatureAmbient"] = round( - TemperatureConverter.convert(current_temp, unit, TEMP_CELSIUS), 1 + TemperatureConverter.convert( + current_temp, unit, UnitOfTemperature.CELSIUS + ), + 1, ) current_humidity = attrs.get(climate.ATTR_CURRENT_HUMIDITY) @@ -962,27 +967,37 @@ class TemperatureSettingTrait(_Trait): if supported & climate.SUPPORT_TARGET_TEMPERATURE_RANGE: response["thermostatTemperatureSetpointHigh"] = round( TemperatureConverter.convert( - attrs[climate.ATTR_TARGET_TEMP_HIGH], unit, TEMP_CELSIUS + attrs[climate.ATTR_TARGET_TEMP_HIGH], + unit, + UnitOfTemperature.CELSIUS, ), 1, ) response["thermostatTemperatureSetpointLow"] = round( TemperatureConverter.convert( - attrs[climate.ATTR_TARGET_TEMP_LOW], unit, TEMP_CELSIUS + attrs[climate.ATTR_TARGET_TEMP_LOW], + unit, + UnitOfTemperature.CELSIUS, ), 1, ) else: if (target_temp := attrs.get(ATTR_TEMPERATURE)) is not None: target_temp = round( - TemperatureConverter.convert(target_temp, unit, TEMP_CELSIUS), 1 + TemperatureConverter.convert( + target_temp, unit, UnitOfTemperature.CELSIUS + ), + 1, ) response["thermostatTemperatureSetpointHigh"] = target_temp response["thermostatTemperatureSetpointLow"] = target_temp else: if (target_temp := attrs.get(ATTR_TEMPERATURE)) is not None: response["thermostatTemperatureSetpoint"] = round( - TemperatureConverter.convert(target_temp, unit, TEMP_CELSIUS), 1 + TemperatureConverter.convert( + target_temp, unit, UnitOfTemperature.CELSIUS + ), + 1, ) return response @@ -996,9 +1011,9 @@ class TemperatureSettingTrait(_Trait): if command == COMMAND_THERMOSTAT_TEMPERATURE_SETPOINT: temp = TemperatureConverter.convert( - params["thermostatTemperatureSetpoint"], TEMP_CELSIUS, unit + params["thermostatTemperatureSetpoint"], UnitOfTemperature.CELSIUS, unit ) - if unit == TEMP_FAHRENHEIT: + if unit == UnitOfTemperature.FAHRENHEIT: temp = round(temp) if temp < min_temp or temp > max_temp: @@ -1017,31 +1032,35 @@ class TemperatureSettingTrait(_Trait): elif command == COMMAND_THERMOSTAT_TEMPERATURE_SET_RANGE: temp_high = TemperatureConverter.convert( - params["thermostatTemperatureSetpointHigh"], TEMP_CELSIUS, unit + params["thermostatTemperatureSetpointHigh"], + UnitOfTemperature.CELSIUS, + unit, ) - if unit == TEMP_FAHRENHEIT: + if unit == UnitOfTemperature.FAHRENHEIT: temp_high = round(temp_high) if temp_high < min_temp or temp_high > max_temp: raise SmartHomeError( ERR_VALUE_OUT_OF_RANGE, ( - f"Upper bound for temperature range should be between " + "Upper bound for temperature range should be between " f"{min_temp} and {max_temp}" ), ) temp_low = TemperatureConverter.convert( - params["thermostatTemperatureSetpointLow"], TEMP_CELSIUS, unit + params["thermostatTemperatureSetpointLow"], + UnitOfTemperature.CELSIUS, + unit, ) - if unit == TEMP_FAHRENHEIT: + if unit == UnitOfTemperature.FAHRENHEIT: temp_low = round(temp_low) if temp_low < min_temp or temp_low > max_temp: raise SmartHomeError( ERR_VALUE_OUT_OF_RANGE, ( - f"Lower bound for temperature range should be between " + "Lower bound for temperature range should be between " f"{min_temp} and {max_temp}" ), ) diff --git a/homeassistant/components/google_assistant_sdk/__init__.py b/homeassistant/components/google_assistant_sdk/__init__.py new file mode 100644 index 00000000000..119ba9e1d27 --- /dev/null +++ b/homeassistant/components/google_assistant_sdk/__init__.py @@ -0,0 +1,92 @@ +"""Support for Google Assistant SDK.""" +from __future__ import annotations + +import aiohttp +import voluptuous as vol + +from homeassistant.config_entries import ConfigEntry, ConfigEntryState +from homeassistant.const import CONF_NAME, Platform +from homeassistant.core import HomeAssistant, ServiceCall +from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady +from homeassistant.helpers import discovery +from homeassistant.helpers.config_entry_oauth2_flow import ( + OAuth2Session, + async_get_config_entry_implementation, +) +from homeassistant.helpers.typing import ConfigType + +from .const import DOMAIN +from .helpers import async_send_text_commands + +SERVICE_SEND_TEXT_COMMAND = "send_text_command" +SERVICE_SEND_TEXT_COMMAND_FIELD_COMMAND = "command" +SERVICE_SEND_TEXT_COMMAND_SCHEMA = vol.All( + { + vol.Required(SERVICE_SEND_TEXT_COMMAND_FIELD_COMMAND): vol.All( + str, vol.Length(min=1) + ), + }, +) + + +async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: + """Set up Google Assistant SDK component.""" + hass.async_create_task( + discovery.async_load_platform( + hass, Platform.NOTIFY, DOMAIN, {CONF_NAME: DOMAIN}, config + ) + ) + + return True + + +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Set up Google Assistant SDK from a config entry.""" + implementation = await async_get_config_entry_implementation(hass, entry) + session = OAuth2Session(hass, entry, implementation) + try: + await session.async_ensure_token_valid() + except aiohttp.ClientResponseError as err: + if 400 <= err.status < 500: + raise ConfigEntryAuthFailed( + "OAuth session is not valid, reauth required" + ) from err + raise ConfigEntryNotReady from err + except aiohttp.ClientError as err: + raise ConfigEntryNotReady from err + hass.data.setdefault(DOMAIN, {})[entry.entry_id] = session + + await async_setup_service(hass) + + return True + + +async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Unload a config entry.""" + hass.data[DOMAIN].pop(entry.entry_id) + loaded_entries = [ + entry + for entry in hass.config_entries.async_entries(DOMAIN) + if entry.state == ConfigEntryState.LOADED + ] + if len(loaded_entries) == 1: + for service_name in hass.services.async_services()[DOMAIN]: + hass.services.async_remove(DOMAIN, service_name) + + return True + + +async def async_setup_service(hass: HomeAssistant) -> None: + """Add the services for Google Assistant SDK.""" + + async def send_text_command(call: ServiceCall) -> None: + """Send a text command to Google Assistant SDK.""" + command: str = call.data[SERVICE_SEND_TEXT_COMMAND_FIELD_COMMAND] + await async_send_text_commands([command], hass) + + hass.services.async_register( + DOMAIN, + SERVICE_SEND_TEXT_COMMAND, + send_text_command, + schema=SERVICE_SEND_TEXT_COMMAND_SCHEMA, + ) diff --git a/homeassistant/components/google_assistant_sdk/application_credentials.py b/homeassistant/components/google_assistant_sdk/application_credentials.py new file mode 100644 index 00000000000..74cefb14b65 --- /dev/null +++ b/homeassistant/components/google_assistant_sdk/application_credentials.py @@ -0,0 +1,26 @@ +"""application_credentials platform for Google Assistant SDK.""" +import oauth2client + +from homeassistant.components.application_credentials import AuthorizationServer +from homeassistant.core import HomeAssistant + + +async def async_get_authorization_server(hass: HomeAssistant) -> AuthorizationServer: + """Return authorization server.""" + return AuthorizationServer( + oauth2client.GOOGLE_AUTH_URI, + oauth2client.GOOGLE_TOKEN_URI, + ) + + +async def async_get_description_placeholders(hass: HomeAssistant) -> dict[str, str]: + """Return description placeholders for the credentials dialog.""" + return { + "oauth_consent_url": ( + "https://console.cloud.google.com/apis/credentials/consent" + ), + "more_info_url": ( + "https://www.home-assistant.io/integrations/google_assistant_sdk/" + ), + "oauth_creds_url": "https://console.cloud.google.com/apis/credentials", + } diff --git a/homeassistant/components/google_assistant_sdk/config_flow.py b/homeassistant/components/google_assistant_sdk/config_flow.py new file mode 100644 index 00000000000..b4f617ca029 --- /dev/null +++ b/homeassistant/components/google_assistant_sdk/config_flow.py @@ -0,0 +1,113 @@ +"""Config flow for Google Assistant SDK integration.""" +from __future__ import annotations + +from collections.abc import Mapping +import logging +from typing import Any + +import voluptuous as vol + +from homeassistant import config_entries +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import callback +from homeassistant.data_entry_flow import FlowResult +from homeassistant.helpers import config_entry_oauth2_flow + +from .const import CONF_LANGUAGE_CODE, DEFAULT_NAME, DOMAIN, SUPPORTED_LANGUAGE_CODES +from .helpers import default_language_code + +_LOGGER = logging.getLogger(__name__) + + +class OAuth2FlowHandler( + config_entry_oauth2_flow.AbstractOAuth2FlowHandler, domain=DOMAIN +): + """Config flow to handle Google Assistant SDK OAuth2 authentication.""" + + DOMAIN = DOMAIN + + reauth_entry: ConfigEntry | None = None + + @property + def logger(self) -> logging.Logger: + """Return logger.""" + return logging.getLogger(__name__) + + @property + def extra_authorize_data(self) -> dict[str, Any]: + """Extra data that needs to be appended to the authorize url.""" + return { + "scope": "https://www.googleapis.com/auth/assistant-sdk-prototype", + # Add params to ensure we get back a refresh token + "access_type": "offline", + "prompt": "consent", + } + + async def async_step_reauth(self, entry_data: Mapping[str, Any]) -> FlowResult: + """Perform reauth upon an API authentication error.""" + self.reauth_entry = self.hass.config_entries.async_get_entry( + self.context["entry_id"] + ) + return await self.async_step_reauth_confirm() + + async def async_step_reauth_confirm( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Confirm reauth dialog.""" + if user_input is None: + return self.async_show_form(step_id="reauth_confirm") + return await self.async_step_user() + + async def async_oauth_create_entry(self, data: dict[str, Any]) -> FlowResult: + """Create an entry for the flow, or update existing entry.""" + if self.reauth_entry: + self.hass.config_entries.async_update_entry(self.reauth_entry, data=data) + await self.hass.config_entries.async_reload(self.reauth_entry.entry_id) + return self.async_abort(reason="reauth_successful") + + if self._async_current_entries(): + # Config entry already exists, only one allowed. + return self.async_abort(reason="single_instance_allowed") + + return self.async_create_entry( + title=DEFAULT_NAME, + data=data, + options={ + CONF_LANGUAGE_CODE: default_language_code(self.hass), + }, + ) + + @staticmethod + @callback + def async_get_options_flow( + config_entry: config_entries.ConfigEntry, + ) -> config_entries.OptionsFlow: + """Create the options flow.""" + return OptionsFlowHandler(config_entry) + + +class OptionsFlowHandler(config_entries.OptionsFlow): + """Google Assistant SDK options flow.""" + + def __init__(self, config_entry: config_entries.ConfigEntry) -> None: + """Initialize options flow.""" + self.config_entry = config_entry + + async def async_step_init( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Manage the options.""" + if user_input is not None: + return self.async_create_entry(title="", data=user_input) + + return self.async_show_form( + step_id="init", + data_schema=vol.Schema( + { + vol.Required( + CONF_LANGUAGE_CODE, + default=self.config_entry.options.get(CONF_LANGUAGE_CODE), + ): vol.In(SUPPORTED_LANGUAGE_CODES), + } + ), + ) diff --git a/homeassistant/components/google_assistant_sdk/const.py b/homeassistant/components/google_assistant_sdk/const.py new file mode 100644 index 00000000000..acd9a405343 --- /dev/null +++ b/homeassistant/components/google_assistant_sdk/const.py @@ -0,0 +1,24 @@ +"""Constants for Google Assistant SDK integration.""" +from typing import Final + +DOMAIN: Final = "google_assistant_sdk" + +DEFAULT_NAME: Final = "Google Assistant SDK" + +CONF_LANGUAGE_CODE: Final = "language_code" + +# https://developers.google.com/assistant/sdk/reference/rpc/languages +SUPPORTED_LANGUAGE_CODES: Final = [ + "de-DE", + "en-AU", + "en-CA", + "en-GB", + "en-IN", + "en-US", + "es-ES", + "es-MX", + "fr-CA", + "fr-FR", + "it-IT", + "pt-BR", +] diff --git a/homeassistant/components/google_assistant_sdk/helpers.py b/homeassistant/components/google_assistant_sdk/helpers.py new file mode 100644 index 00000000000..15e325f10c1 --- /dev/null +++ b/homeassistant/components/google_assistant_sdk/helpers.py @@ -0,0 +1,55 @@ +"""Helper classes for Google Assistant SDK integration.""" +from __future__ import annotations + +import logging + +import aiohttp +from gassist_text import TextAssistant +from google.oauth2.credentials import Credentials + +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_ACCESS_TOKEN +from homeassistant.core import HomeAssistant +from homeassistant.helpers.config_entry_oauth2_flow import OAuth2Session + +from .const import CONF_LANGUAGE_CODE, DOMAIN, SUPPORTED_LANGUAGE_CODES + +_LOGGER = logging.getLogger(__name__) + +DEFAULT_LANGUAGE_CODES = { + "de": "de-DE", + "en": "en-US", + "es": "es-ES", + "fr": "fr-FR", + "it": "it-IT", + "pt": "pt-BR", +} + + +async def async_send_text_commands(commands: list[str], hass: HomeAssistant) -> None: + """Send text commands to Google Assistant Service.""" + # There can only be 1 entry (config_flow has single_instance_allowed) + entry: ConfigEntry = hass.config_entries.async_entries(DOMAIN)[0] + + session: OAuth2Session = hass.data[DOMAIN].get(entry.entry_id) + try: + await session.async_ensure_token_valid() + except aiohttp.ClientResponseError as err: + if 400 <= err.status < 500: + entry.async_start_reauth(hass) + raise err + + credentials = Credentials(session.token[CONF_ACCESS_TOKEN]) + language_code = entry.options.get(CONF_LANGUAGE_CODE, default_language_code(hass)) + with TextAssistant(credentials, language_code) as assistant: + for command in commands: + text_response = assistant.assist(command)[0] + _LOGGER.debug("command: %s\nresponse: %s", command, text_response) + + +def default_language_code(hass: HomeAssistant): + """Get default language code based on Home Assistant config.""" + language_code = f"{hass.config.language}-{hass.config.country}" + if language_code in SUPPORTED_LANGUAGE_CODES: + return language_code + return DEFAULT_LANGUAGE_CODES.get(hass.config.language, "en-US") diff --git a/homeassistant/components/google_assistant_sdk/manifest.json b/homeassistant/components/google_assistant_sdk/manifest.json new file mode 100644 index 00000000000..e1b390f9496 --- /dev/null +++ b/homeassistant/components/google_assistant_sdk/manifest.json @@ -0,0 +1,11 @@ +{ + "domain": "google_assistant_sdk", + "name": "Google Assistant SDK", + "config_flow": true, + "dependencies": ["application_credentials"], + "documentation": "https://www.home-assistant.io/integrations/google_assistant_sdk/", + "requirements": ["gassist-text==0.0.7"], + "codeowners": ["@tronikos"], + "iot_class": "cloud_polling", + "integration_type": "service" +} diff --git a/homeassistant/components/google_assistant_sdk/notify.py b/homeassistant/components/google_assistant_sdk/notify.py new file mode 100644 index 00000000000..3872a1df2a3 --- /dev/null +++ b/homeassistant/components/google_assistant_sdk/notify.py @@ -0,0 +1,71 @@ +"""Support for Google Assistant SDK broadcast notifications.""" +from __future__ import annotations + +from typing import Any + +from homeassistant.components.notify import ATTR_TARGET, BaseNotificationService +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType + +from .const import CONF_LANGUAGE_CODE, DOMAIN +from .helpers import async_send_text_commands, default_language_code + +# https://support.google.com/assistant/answer/9071582?hl=en +LANG_TO_BROADCAST_COMMAND = { + "en": ("broadcast", "broadcast to"), + "de": ("Nachricht an alle", "Nachricht an alle an"), + "es": ("Anuncia", "Anuncia en"), + "fr": ("Diffuse", "Diffuse dans"), + "it": ("Trasmetti", "Trasmetti in"), + "pt": ("Transmite", "Transmite para"), +} + + +def broadcast_commands(language_code: str): + """ + Get the commands for broadcasting a message for the given language code. + + Return type is a tuple where [0] is for broadcasting to your entire home, + while [1] is for broadcasting to a specific target. + """ + return LANG_TO_BROADCAST_COMMAND.get(language_code.split("-", maxsplit=1)[0]) + + +async def async_get_service( + hass: HomeAssistant, + config: ConfigType, + discovery_info: DiscoveryInfoType | None = None, +) -> BaseNotificationService: + """Get the broadcast notification service.""" + return BroadcastNotificationService(hass) + + +class BroadcastNotificationService(BaseNotificationService): + """Implement broadcast notification service.""" + + def __init__(self, hass: HomeAssistant) -> None: + """Initialize the service.""" + self.hass = hass + + async def async_send_message(self, message: str = "", **kwargs: Any) -> None: + """Send a message.""" + if not message: + return + + # There can only be 1 entry (config_flow has single_instance_allowed) + entry: ConfigEntry = self.hass.config_entries.async_entries(DOMAIN)[0] + language_code = entry.options.get( + CONF_LANGUAGE_CODE, default_language_code(self.hass) + ) + + commands = [] + targets = kwargs.get(ATTR_TARGET) + if not targets: + commands.append(f"{broadcast_commands(language_code)[0]} {message}") + else: + for target in targets: + commands.append( + f"{broadcast_commands(language_code)[1]} {target} {message}" + ) + await async_send_text_commands(commands, self.hass) diff --git a/homeassistant/components/google_assistant_sdk/services.yaml b/homeassistant/components/google_assistant_sdk/services.yaml new file mode 100644 index 00000000000..b9d4e8635de --- /dev/null +++ b/homeassistant/components/google_assistant_sdk/services.yaml @@ -0,0 +1,10 @@ +send_text_command: + name: Send text command + description: Send a command as a text query to Google Assistant. + fields: + command: + name: Command + description: Command to send to Google Assistant. + example: turn off kitchen TV + selector: + text: diff --git a/homeassistant/components/google_assistant_sdk/strings.json b/homeassistant/components/google_assistant_sdk/strings.json new file mode 100644 index 00000000000..66a2b975b5e --- /dev/null +++ b/homeassistant/components/google_assistant_sdk/strings.json @@ -0,0 +1,42 @@ +{ + "config": { + "step": { + "pick_implementation": { + "title": "[%key:common::config_flow::title::oauth2_pick_implementation%]" + }, + "auth": { + "title": "Link Google Account" + }, + "reauth_confirm": { + "title": "[%key:common::config_flow::title::reauth%]", + "description": "The Google Assistant SDK integration needs to re-authenticate your account" + } + }, + "abort": { + "already_configured": "[%key:common::config_flow::abort::already_configured_account%]", + "already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]", + "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", + "timeout_connect": "[%key:common::config_flow::error::timeout_connect%]", + "oauth_error": "[%key:common::config_flow::abort::oauth2_error%]", + "missing_configuration": "[%key:common::config_flow::abort::oauth2_missing_configuration%]", + "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]", + "invalid_access_token": "[%key:common::config_flow::error::invalid_access_token%]", + "unknown": "[%key:common::config_flow::error::unknown%]" + }, + "create_entry": { + "default": "[%key:common::config_flow::create_entry::authenticated%]" + } + }, + "options": { + "step": { + "init": { + "data": { + "language_code": "Language code" + } + } + } + }, + "application_credentials": { + "description": "Follow the [instructions]({more_info_url}) for [OAuth consent screen]({oauth_consent_url}) to give Home Assistant access to your Google Assistant SDK. You also need to create Application Credentials linked to your account:\n1. Go to [Credentials]({oauth_creds_url}) and click **Create Credentials**.\n1. From the drop-down list select **OAuth client ID**.\n1. Select **Web application** for the Application Type.\n\n" + } +} diff --git a/homeassistant/components/google_assistant_sdk/translations/bg.json b/homeassistant/components/google_assistant_sdk/translations/bg.json new file mode 100644 index 00000000000..dcd006d0e14 --- /dev/null +++ b/homeassistant/components/google_assistant_sdk/translations/bg.json @@ -0,0 +1,35 @@ +{ + "config": { + "abort": { + "already_configured": "\u0410\u043a\u0430\u0443\u043d\u0442\u044a\u0442 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", + "missing_configuration": "\u041a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044a\u0442 \u043d\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d. \u041c\u043e\u043b\u044f, \u0441\u043b\u0435\u0434\u0432\u0430\u0439\u0442\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f\u0442\u0430.", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430", + "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" + }, + "create_entry": { + "default": "\u0423\u0441\u043f\u0435\u0448\u043d\u043e \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u0446\u0438\u0440\u0430\u043d" + }, + "step": { + "auth": { + "title": "\u0421\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435 \u043d\u0430 \u0430\u043a\u0430\u0443\u043d\u0442 \u0432 Google" + }, + "pick_implementation": { + "title": "\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u043c\u0435\u0442\u043e\u0434 \u0437\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f" + }, + "reauth_confirm": { + "description": "\u0418\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f\u0442\u0430 Google Assistant SDK \u0441\u0435 \u043d\u0443\u0436\u0434\u0430\u0435 \u043e\u0442 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u043d\u0430 \u0432\u0430\u0448\u0438\u044f \u0430\u043a\u0430\u0443\u043d\u0442", + "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043da \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u043d\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f\u0442\u0430" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "language_code": "\u0415\u0437\u0438\u043a\u043e\u0432 \u043a\u043e\u0434" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google_assistant_sdk/translations/ca.json b/homeassistant/components/google_assistant_sdk/translations/ca.json new file mode 100644 index 00000000000..41c8bb575af --- /dev/null +++ b/homeassistant/components/google_assistant_sdk/translations/ca.json @@ -0,0 +1,42 @@ +{ + "application_credentials": { + "description": "Segueix les [instruccions]({more_info_url}) de [la pantalla de consentiment OAuth]({oauth_consent_url}) perqu\u00e8 Home Assistant tingui acc\u00e9s als teu Google Assistant SDK. Tamb\u00e9 has de crear les credencials d'aplicaci\u00f3 enlla\u00e7ades al teu compte:\n 1. V\u00e9s a [Credencials]({oauth_creds_url}) i fes clic a **Crear credencials**.\n 2. A la llista desplegable, selecciona **ID de client OAuth**.\n 3. Selecciona **Aplicaci\u00f3 Web** al tipus d'aplicaci\u00f3.\n \n " + }, + "config": { + "abort": { + "already_configured": "El compte ja est\u00e0 configurat", + "already_in_progress": "El flux de configuraci\u00f3 ja est\u00e0 en curs", + "cannot_connect": "Ha fallat la connexi\u00f3", + "invalid_access_token": "Token d'acc\u00e9s inv\u00e0lid", + "missing_configuration": "El component no est\u00e0 configurat. Mira'n la documentaci\u00f3.", + "oauth_error": "S'han rebut dades token inv\u00e0lides.", + "reauth_successful": "Re-autenticaci\u00f3 realitzada correctament", + "timeout_connect": "S'ha esgotat el temps m\u00e0xim d'espera per establir connexi\u00f3", + "unknown": "Error inesperat" + }, + "create_entry": { + "default": "Autenticaci\u00f3 exitosa" + }, + "step": { + "auth": { + "title": "Vinculaci\u00f3 amb compte de Google" + }, + "pick_implementation": { + "title": "Selecciona el m\u00e8tode d'autenticaci\u00f3" + }, + "reauth_confirm": { + "description": "La integraci\u00f3 Google Assistant SDK ha de tornar a autenticar-se amb el teu compte", + "title": "Reautenticaci\u00f3 de la integraci\u00f3" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "language_code": "Codi d'idioma" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google_assistant_sdk/translations/de.json b/homeassistant/components/google_assistant_sdk/translations/de.json new file mode 100644 index 00000000000..73a6b908ea2 --- /dev/null +++ b/homeassistant/components/google_assistant_sdk/translations/de.json @@ -0,0 +1,42 @@ +{ + "application_credentials": { + "description": "Befolge die [Anleitung]({more_info_url}) f\u00fcr den [OAuth-Zustimmungsbildschirm]({oauth_consent_url}), um Home Assistant Zugriff auf dein Google Assistant SDK zu gew\u00e4hren. Du musst auch mit deinem Konto verkn\u00fcpfte Anwendungsanmeldeinformationen erstellen:\n 1. Gehe zu [Credentials]({oauth_creds_url}) und klicke auf **Create Credentials**.\n 1. W\u00e4hle aus der Dropdown-Liste **OAuth-Client-ID** aus.\n 1. W\u00e4hle **Web Application** als Anwendungstyp aus." + }, + "config": { + "abort": { + "already_configured": "Konto wurde bereits konfiguriert", + "already_in_progress": "Der Konfigurationsablauf wird bereits ausgef\u00fchrt", + "cannot_connect": "Verbindung fehlgeschlagen", + "invalid_access_token": "Ung\u00fcltiger Zugriffs-Token", + "missing_configuration": "Die Komponente ist nicht konfiguriert. Bitte der Dokumentation folgen.", + "oauth_error": "Ung\u00fcltige Token-Daten empfangen.", + "reauth_successful": "Die erneute Authentifizierung war erfolgreich", + "timeout_connect": "Zeit\u00fcberschreitung beim Verbindungsaufbau", + "unknown": "Unerwarteter Fehler" + }, + "create_entry": { + "default": "Erfolgreich authentifiziert" + }, + "step": { + "auth": { + "title": "Google-Konto verkn\u00fcpfen" + }, + "pick_implementation": { + "title": "W\u00e4hle die Authentifizierungsmethode" + }, + "reauth_confirm": { + "description": "Die Google Assistant SDK-Integration muss dein Konto erneut authentifizieren", + "title": "Integration erneut authentifizieren" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "language_code": "Sprachcode" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google_assistant_sdk/translations/el.json b/homeassistant/components/google_assistant_sdk/translations/el.json new file mode 100644 index 00000000000..a735860a746 --- /dev/null +++ b/homeassistant/components/google_assistant_sdk/translations/el.json @@ -0,0 +1,42 @@ +{ + "application_credentials": { + "description": "\u0391\u03ba\u03bf\u03bb\u03bf\u03c5\u03b8\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b9\u03c2 [\u03bf\u03b4\u03b7\u03b3\u03af\u03b5\u03c2]( {more_info_url} ) \u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd [\u03bf\u03b8\u03cc\u03bd\u03b7 \u03c3\u03c5\u03bd\u03b1\u03af\u03bd\u03b5\u03c3\u03b7\u03c2 OAuth]( {oauth_consent_url} ) \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03ce\u03c3\u03b5\u03c4\u03b5 \u03c3\u03c4\u03bf\u03bd \u0392\u03bf\u03b7\u03b8\u03cc Home \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7 \u03c3\u03c4\u03bf SDK \u03c4\u03bf\u03c5 \u0392\u03bf\u03b7\u03b8\u03bf\u03cd Google. \u03a0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03b5\u03c0\u03af\u03c3\u03b7\u03c2 \u03bd\u03b1 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03ae\u03c3\u03b5\u03c4\u03b5 \u03b4\u03b9\u03b1\u03c0\u03b9\u03c3\u03c4\u03b5\u03c5\u03c4\u03ae\u03c1\u03b9\u03b1 \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae\u03c2 \u03c3\u03c5\u03bd\u03b4\u03b5\u03b4\u03b5\u03bc\u03ad\u03bd\u03b1 \u03bc\u03b5 \u03c4\u03bf\u03bd \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc \u03c3\u03b1\u03c2:\n 1. \u039c\u03b5\u03c4\u03b1\u03b2\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03b1 [\u0394\u03b9\u03b1\u03c0\u03b9\u03c3\u03c4\u03b5\u03c5\u03c4\u03ae\u03c1\u03b9\u03b1] ( {oauth_creds_url} ) \u03ba\u03b1\u03b9 \u03ba\u03ac\u03bd\u03c4\u03b5 \u03ba\u03bb\u03b9\u03ba \u03c3\u03c4\u03bf **\u0394\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03b4\u03b9\u03b1\u03c0\u03b9\u03c3\u03c4\u03b5\u03c5\u03c4\u03b7\u03c1\u03af\u03c9\u03bd**.\n 1. \u0391\u03c0\u03cc \u03c4\u03b7\u03bd \u03b1\u03bd\u03b1\u03c0\u03c4\u03c5\u03c3\u03c3\u03cc\u03bc\u03b5\u03bd\u03b7 \u03bb\u03af\u03c3\u03c4\u03b1 \u03b5\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 **OAuth \u0391\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c0\u03b5\u03bb\u03ac\u03c4\u03b7**.\n 1. \u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 **\u0395\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae \u0399\u03c3\u03c4\u03bf\u03cd** \u03b3\u03b9\u03b1 \u03c4\u03bf\u03bd \u03a4\u03cd\u03c0\u03bf \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae\u03c2. \n\n" + }, + "config": { + "abort": { + "already_configured": "\u039f \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc\u03c2 \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", + "already_in_progress": "\u0397 \u03c1\u03bf\u03ae \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7\u03c2 \u03b2\u03c1\u03af\u03c3\u03ba\u03b5\u03c4\u03b1\u03b9 \u03ae\u03b4\u03b7 \u03c3\u03b5 \u03b5\u03be\u03ad\u03bb\u03b9\u03be\u03b7", + "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", + "invalid_access_token": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03b4\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", + "missing_configuration": "\u03a4\u03bf \u03c3\u03c4\u03bf\u03b9\u03c7\u03b5\u03af\u03bf \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af. \u03a0\u03b1\u03c1\u03b1\u03ba\u03b1\u03bb\u03bf\u03cd\u03bc\u03b5 \u03b1\u03ba\u03bf\u03bb\u03bf\u03c5\u03b8\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03c4\u03b5\u03ba\u03bc\u03b7\u03c1\u03af\u03c9\u03c3\u03b7.", + "oauth_error": "\u039b\u03ae\u03c6\u03b8\u03b7\u03ba\u03b1\u03bd \u03bc\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03b1 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03b1 \u03b4\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03bf\u03cd.", + "reauth_successful": "\u039f \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ae\u03c4\u03b1\u03bd \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2", + "timeout_connect": "\u03a7\u03c1\u03bf\u03bd\u03b9\u03ba\u03cc \u03cc\u03c1\u03b9\u03bf \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03af\u03b1\u03c2 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", + "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" + }, + "create_entry": { + "default": "\u0395\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2" + }, + "step": { + "auth": { + "title": "\u03a3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03bf\u03cd Google" + }, + "pick_implementation": { + "title": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u03bc\u03b5\u03b8\u03cc\u03b4\u03bf\u03c5 \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2" + }, + "reauth_confirm": { + "description": "\u0397 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 Google Assistant SDK \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b5\u03bb\u03ad\u03b3\u03be\u03b5\u03b9 \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03c4\u03b7\u03bd \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1 \u03c4\u03bf\u03c5 \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03bf\u03cd \u03c3\u03b1\u03c2", + "title": "\u0395\u03c0\u03b1\u03bd\u03b1\u03bb\u03b7\u03c0\u03c4\u03b9\u03ba\u03cc\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "language_code": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03b3\u03bb\u03ce\u03c3\u03c3\u03b1\u03c2" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google_assistant_sdk/translations/en.json b/homeassistant/components/google_assistant_sdk/translations/en.json new file mode 100644 index 00000000000..36d28427ca2 --- /dev/null +++ b/homeassistant/components/google_assistant_sdk/translations/en.json @@ -0,0 +1,42 @@ +{ + "application_credentials": { + "description": "Follow the [instructions]({more_info_url}) for [OAuth consent screen]({oauth_consent_url}) to give Home Assistant access to your Google Assistant SDK. You also need to create Application Credentials linked to your account:\n1. Go to [Credentials]({oauth_creds_url}) and click **Create Credentials**.\n1. From the drop-down list select **OAuth client ID**.\n1. Select **Web application** for the Application Type.\n\n" + }, + "config": { + "abort": { + "already_configured": "Account is already configured", + "already_in_progress": "Configuration flow is already in progress", + "cannot_connect": "Failed to connect", + "invalid_access_token": "Invalid access token", + "missing_configuration": "The component is not configured. Please follow the documentation.", + "oauth_error": "Received invalid token data.", + "reauth_successful": "Re-authentication was successful", + "timeout_connect": "Timeout establishing connection", + "unknown": "Unexpected error" + }, + "create_entry": { + "default": "Successfully authenticated" + }, + "step": { + "auth": { + "title": "Link Google Account" + }, + "pick_implementation": { + "title": "Pick Authentication Method" + }, + "reauth_confirm": { + "description": "The Google Assistant SDK integration needs to re-authenticate your account", + "title": "Reauthenticate Integration" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "language_code": "Language code" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google_assistant_sdk/translations/es.json b/homeassistant/components/google_assistant_sdk/translations/es.json new file mode 100644 index 00000000000..18513dec620 --- /dev/null +++ b/homeassistant/components/google_assistant_sdk/translations/es.json @@ -0,0 +1,42 @@ +{ + "application_credentials": { + "description": "Sigue las [instrucciones]({more_info_url}) para la [pantalla de consentimiento de OAuth]({oauth_consent_url}) para otorgarle a Home Assistant acceso a tu SDK de Google Assistant. Tambi\u00e9n debes crear Credenciales de aplicaci\u00f3n vinculadas a tu cuenta:\n1. Ve a [Credenciales]({oauth_creds_url}) y haz clic en **Crear credenciales**.\n1. En la lista desplegable, selecciona **ID de cliente de OAuth**.\n1. Selecciona **Aplicaci\u00f3n web** para el Tipo de aplicaci\u00f3n. \n\n" + }, + "config": { + "abort": { + "already_configured": "La cuenta ya est\u00e1 configurada", + "already_in_progress": "El flujo de configuraci\u00f3n ya est\u00e1 en curso", + "cannot_connect": "No se pudo conectar", + "invalid_access_token": "Token de acceso no v\u00e1lido", + "missing_configuration": "El componente no est\u00e1 configurado. Por favor, sigue la documentaci\u00f3n.", + "oauth_error": "Se han recibido datos de token no v\u00e1lidos.", + "reauth_successful": "La autenticaci\u00f3n se volvi\u00f3 a realizar correctamente", + "timeout_connect": "Tiempo de espera agotado para establecer la conexi\u00f3n", + "unknown": "Error inesperado" + }, + "create_entry": { + "default": "Autenticado correctamente" + }, + "step": { + "auth": { + "title": "Vincular cuenta de Google" + }, + "pick_implementation": { + "title": "Selecciona el m\u00e9todo de autenticaci\u00f3n" + }, + "reauth_confirm": { + "description": "La integraci\u00f3n del SDK del Asistente de Google necesita volver a autenticar tu cuenta", + "title": "Volver a autenticar la integraci\u00f3n" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "language_code": "C\u00f3digo de idioma" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google_assistant_sdk/translations/et.json b/homeassistant/components/google_assistant_sdk/translations/et.json new file mode 100644 index 00000000000..b060d69f795 --- /dev/null +++ b/homeassistant/components/google_assistant_sdk/translations/et.json @@ -0,0 +1,42 @@ +{ + "application_credentials": { + "description": "J\u00e4rgi [juhiseid]( {more_info_url} ) [OAuthi n\u00f5usoleku ekraanil]( {oauth_consent_url} ), et anda Home Assistantile juurdep\u00e4\u00e4s Google'i assistendi SDK-le. Samuti pead looma oma kontoga lingitud rakenduse mandaadid:\n 1. Ava [Mandaat] ( {oauth_creds_url} ) ja kl\u00f5psa **Loo mandaadid**.\n 1. Vali ripploendist **OAuthi kliendi ID**.\n 1. Vali rakenduse t\u00fc\u00fcbiks **Veebirakendus**. \n\n" + }, + "config": { + "abort": { + "already_configured": "Konto on juba h\u00e4\u00e4lestatud", + "already_in_progress": "Seadistamine juba k\u00e4ib", + "cannot_connect": "\u00dchendamine nurjus", + "invalid_access_token": "Sobimatud loaandmed", + "missing_configuration": "Komponent pole h\u00e4\u00e4lestatud. J\u00e4rgi juhiseid.", + "oauth_error": "Saadi sobimatud loaandmed.", + "reauth_successful": "Taastuvastamine \u00f5nnestus", + "timeout_connect": "\u00dchenduse loomise ajal\u00f5pp", + "unknown": "Ootamatu t\u00f5rge" + }, + "create_entry": { + "default": "Tuvastamine \u00f5nnestus" + }, + "step": { + "auth": { + "title": "Google'i konto linkimine" + }, + "pick_implementation": { + "title": "Vali tuvastusmeetod" + }, + "reauth_confirm": { + "description": "Google'i assistendi SDK sidumine peab konto uuesti autentima", + "title": "Taastuvasta sidumine" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "language_code": "Keele kood" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google_assistant_sdk/translations/he.json b/homeassistant/components/google_assistant_sdk/translations/he.json new file mode 100644 index 00000000000..14b08d841e5 --- /dev/null +++ b/homeassistant/components/google_assistant_sdk/translations/he.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d7\u05e9\u05d1\u05d5\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", + "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", + "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", + "invalid_access_token": "\u05d0\u05e1\u05d9\u05de\u05d5\u05df \u05d2\u05d9\u05e9\u05d4 \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9", + "missing_configuration": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05e8\u05db\u05d9\u05d1 \u05dc\u05d0 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e0\u05d0 \u05e2\u05e7\u05d5\u05d1 \u05d0\u05d7\u05e8 \u05d4\u05ea\u05d9\u05e2\u05d5\u05d3.", + "oauth_error": "\u05d4\u05ea\u05e7\u05d1\u05dc\u05d5 \u05e0\u05ea\u05d5\u05e0\u05d9 \u05d0\u05e1\u05d9\u05de\u05d5\u05df \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9\u05d9\u05dd.", + "reauth_successful": "\u05d4\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05d4\u05e6\u05dc\u05d9\u05d7", + "timeout_connect": "\u05e4\u05e1\u05e7 \u05d6\u05de\u05df \u05dc\u05d9\u05e6\u05d9\u05e8\u05ea \u05d7\u05d9\u05d1\u05d5\u05e8", + "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" + }, + "create_entry": { + "default": "\u05d0\u05d5\u05de\u05ea \u05d1\u05d4\u05e6\u05dc\u05d7\u05d4" + }, + "step": { + "pick_implementation": { + "title": "\u05d1\u05d7\u05e8 \u05e9\u05d9\u05d8\u05ea \u05d0\u05d9\u05de\u05d5\u05ea" + }, + "reauth_confirm": { + "title": "\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05e9\u05dc \u05e9\u05d9\u05dc\u05d5\u05d1" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google_assistant_sdk/translations/hu.json b/homeassistant/components/google_assistant_sdk/translations/hu.json new file mode 100644 index 00000000000..e39c135827d --- /dev/null +++ b/homeassistant/components/google_assistant_sdk/translations/hu.json @@ -0,0 +1,42 @@ +{ + "application_credentials": { + "description": "K\u00f6vesse az [utas\u00edt\u00e1sokat]({more_info_url}) az [OAuth hozz\u00e1j\u00e1rul\u00e1si k\u00e9perny\u0151n]({oauth_consent_url}), hogy hozz\u00e1f\u00e9r\u00e9st adjon Home Assistantnak a Google Assistant SDK-hoz. A fi\u00f3kj\u00e1hoz kapcsol\u00f3d\u00f3 Alkalmaz\u00e1si hiteles\u00edt\u0151 adatokat is l\u00e9tre kell hoznia:\n1. Menjen a [Hiteles\u00edt\u00e9si adatok]({oauth_creds_url}) men\u00fcpontra, \u00e9s kattintson a **Hiteles\u00edt\u00e9si adatok l\u00e9trehoz\u00e1sa** gombra.\n1. A leg\u00f6rd\u00fcl\u0151 list\u00e1b\u00f3l v\u00e1lassza ki az **OAuth \u00fcgyf\u00e9l azonos\u00edt\u00f3t**.\n1. Az Alkalmaz\u00e1s t\u00edpus\u00e1hoz v\u00e1lassza a **Webalkalmaz\u00e1s** lehet\u0151s\u00e9get.\n" + }, + "config": { + "abort": { + "already_configured": "A fi\u00f3k m\u00e1r konfigur\u00e1lva van", + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", + "cannot_connect": "Sikertelen csatlakoz\u00e1s", + "invalid_access_token": "\u00c9rv\u00e9nytelen hozz\u00e1f\u00e9r\u00e9si token", + "missing_configuration": "A komponens nincs konfigur\u00e1lva. K\u00e9rem, k\u00f6vesse a dokument\u00e1ci\u00f3t.", + "oauth_error": "\u00c9rv\u00e9nytelen token adatok \u00e9rkeztek.", + "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt.", + "timeout_connect": "Id\u0151t\u00fall\u00e9p\u00e9s a kapcsolat l\u00e9trehoz\u00e1sa sor\u00e1n", + "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" + }, + "create_entry": { + "default": "Sikeres hiteles\u00edt\u00e9s" + }, + "step": { + "auth": { + "title": "Google-fi\u00f3k \u00f6sszekapcsol\u00e1sa" + }, + "pick_implementation": { + "title": "V\u00e1lasszon egy hiteles\u00edt\u00e9si m\u00f3dszert" + }, + "reauth_confirm": { + "description": "A Google Assistant SDK integr\u00e1ci\u00f3nak \u00fajra kell hiteles\u00edtenie fi\u00f3kj\u00e1t", + "title": "Integr\u00e1ci\u00f3 \u00fajrahiteles\u00edt\u00e9se" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "language_code": "Nyelvi k\u00f3d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google_assistant_sdk/translations/id.json b/homeassistant/components/google_assistant_sdk/translations/id.json new file mode 100644 index 00000000000..0ab64cd9529 --- /dev/null +++ b/homeassistant/components/google_assistant_sdk/translations/id.json @@ -0,0 +1,42 @@ +{ + "application_credentials": { + "description": "Ikuti [instruksi]({more_info_url}) untuk [layar persetujuan OAuth]({oauth_consent_url}) untuk memberikan akses Home Assistant ke SDK Google Asisten Anda. Anda juga perlu membuat Kredensial Aplikasi yang ditautkan ke akun Anda:\n1. Buka [Kredensial]({oauth_creds_url}) dan klik **Buat Kredensial**.\n1. Dari daftar drop-down pilih **OAuth client ID**.\n1. Pilih **Aplikasi web** untuk Jenis Aplikasi.\n\n" + }, + "config": { + "abort": { + "already_configured": "Akun sudah dikonfigurasi", + "already_in_progress": "Alur konfigurasi sedang berlangsung", + "cannot_connect": "Gagal terhubung", + "invalid_access_token": "Token akses tidak valid", + "missing_configuration": "Komponen tidak dikonfigurasi. Ikuti petunjuk dalam dokumentasi.", + "oauth_error": "Menerima respons token yang tidak valid.", + "reauth_successful": "Autentikasi ulang berhasil", + "timeout_connect": "Tenggang waktu membuat koneksi habis", + "unknown": "Kesalahan yang tidak diharapkan" + }, + "create_entry": { + "default": "Berhasil diautentikasi" + }, + "step": { + "auth": { + "title": "Tautkan Akun Google" + }, + "pick_implementation": { + "title": "Pilih Metode Autentikasi" + }, + "reauth_confirm": { + "description": "Integrasi SDK Google Asisten perlu mengautentikasi ulang akun Anda", + "title": "Autentikasi Ulang Integrasi" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "language_code": "Kode bahasa" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google_assistant_sdk/translations/it.json b/homeassistant/components/google_assistant_sdk/translations/it.json new file mode 100644 index 00000000000..03e2421fc86 --- /dev/null +++ b/homeassistant/components/google_assistant_sdk/translations/it.json @@ -0,0 +1,42 @@ +{ + "application_credentials": { + "description": "Segui le [istruzioni]({more_info_url}) per la [schermata di consenso OAuth]({oauth_consent_url}) per concedere Home Assistant l'accesso al tuo SDK dell'Assistente Google. Devi anche creare le credenziali dell'applicazione collegate al tuo account:\n 1. Vai a [Credenziali]({oauth_creds_url}) e fai clic su **Crea credenziali**.\n 1. Dall'elenco a discesa selezionare **ID client OAuth**.\n 1. Selezionare **Applicazione web** per Tipo di applicazione. \n\n" + }, + "config": { + "abort": { + "already_configured": "L'account \u00e8 gi\u00e0 configurato", + "already_in_progress": "Il flusso di configurazione \u00e8 gi\u00e0 in corso", + "cannot_connect": "Impossibile connettersi", + "invalid_access_token": "Token di accesso non valido", + "missing_configuration": "Il componente non \u00e8 configurato. Segui la documentazione.", + "oauth_error": "Ricevuti dati token non validi.", + "reauth_successful": "La nuova autenticazione \u00e8 stata eseguita correttamente", + "timeout_connect": "Tempo scaduto per stabile la connessione.", + "unknown": "Errore imprevisto" + }, + "create_entry": { + "default": "Autenticazione riuscita" + }, + "step": { + "auth": { + "title": "Collega l'account Google" + }, + "pick_implementation": { + "title": "Scegli il metodo di autenticazione" + }, + "reauth_confirm": { + "description": "L'integrazione dell'SDK dell'Assistente Google deve autenticare nuovamente il tuo account", + "title": "Autentica nuovamente l'integrazione" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "language_code": "Codice lingua" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google_assistant_sdk/translations/ja.json b/homeassistant/components/google_assistant_sdk/translations/ja.json new file mode 100644 index 00000000000..1e0937a818b --- /dev/null +++ b/homeassistant/components/google_assistant_sdk/translations/ja.json @@ -0,0 +1,29 @@ +{ + "config": { + "abort": { + "already_configured": "\u30a2\u30ab\u30a6\u30f3\u30c8\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059", + "already_in_progress": "\u69cb\u6210\u30d5\u30ed\u30fc\u306f\u3059\u3067\u306b\u9032\u884c\u4e2d\u3067\u3059", + "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", + "invalid_access_token": "\u7121\u52b9\u306a\u30a2\u30af\u30bb\u30b9\u30c8\u30fc\u30af\u30f3", + "missing_configuration": "\u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8\u304c\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u306b\u5f93\u3063\u3066\u304f\u3060\u3055\u3044\u3002", + "oauth_error": "\u7121\u52b9\u306a\u30c8\u30fc\u30af\u30f3\u30c7\u30fc\u30bf\u3092\u53d7\u4fe1\u3057\u307e\u3057\u305f\u3002", + "reauth_successful": "\u518d\u8a8d\u8a3c\u306b\u6210\u529f\u3057\u307e\u3057\u305f", + "timeout_connect": "\u63a5\u7d9a\u78ba\u7acb\u6642\u306b\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8", + "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" + }, + "create_entry": { + "default": "\u6b63\u5e38\u306b\u8a8d\u8a3c\u3055\u308c\u307e\u3057\u305f" + }, + "step": { + "auth": { + "title": "Google\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u30ea\u30f3\u30af\u3059\u308b" + }, + "pick_implementation": { + "title": "\u8a8d\u8a3c\u65b9\u6cd5\u306e\u9078\u629e" + }, + "reauth_confirm": { + "title": "\u7d71\u5408\u306e\u518d\u8a8d\u8a3c" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google_assistant_sdk/translations/nl.json b/homeassistant/components/google_assistant_sdk/translations/nl.json new file mode 100644 index 00000000000..66849b98b82 --- /dev/null +++ b/homeassistant/components/google_assistant_sdk/translations/nl.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "already_configured": "Account is al geconfigureerd", + "already_in_progress": "De configuratie is momenteel al bezig", + "cannot_connect": "Kan geen verbinding maken", + "invalid_access_token": "Ongeldig toegangstoken", + "missing_configuration": "Integratie niet geconfigureerd. Raadpleeg de documentatie.", + "oauth_error": "Ongeldige tokengegevens ontvangen.", + "reauth_successful": "Herauthenticatie geslaagd", + "timeout_connect": "Time-out bij het maken van verbinding", + "unknown": "Onverwachte fout" + }, + "create_entry": { + "default": "Authenticatie geslaagd" + }, + "step": { + "pick_implementation": { + "title": "Kies een authenticatie methode" + }, + "reauth_confirm": { + "title": "Integratie herauthenticeren" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google_assistant_sdk/translations/no.json b/homeassistant/components/google_assistant_sdk/translations/no.json new file mode 100644 index 00000000000..0a4a81a8b30 --- /dev/null +++ b/homeassistant/components/google_assistant_sdk/translations/no.json @@ -0,0 +1,42 @@ +{ + "application_credentials": { + "description": "F\u00f8lg [instruksjonene]( {more_info_url} ) for [OAuth-samtykkeskjermen]( {oauth_consent_url} ) for \u00e5 gi Home Assistant tilgang til Google Assistant SDK. Du m\u00e5 ogs\u00e5 opprette applikasjonslegitimasjon knyttet til kontoen din:\n 1. G\u00e5 til [Credentials]( {oauth_creds_url} ) og klikk p\u00e5 **Create Credentials**.\n 1. Velg **OAuth-klient-ID** fra rullegardinlisten.\n 1. Velg **Webapplikasjon** for applikasjonstype. \n\n" + }, + "config": { + "abort": { + "already_configured": "Kontoen er allerede konfigurert", + "already_in_progress": "Konfigurasjonsflyten p\u00e5g\u00e5r allerede", + "cannot_connect": "Tilkobling mislyktes", + "invalid_access_token": "Ugyldig tilgangstoken", + "missing_configuration": "Komponenten er ikke konfigurert, vennligst f\u00f8lg dokumentasjonen", + "oauth_error": "Mottatt ugyldige token data.", + "reauth_successful": "Re-autentisering var vellykket", + "timeout_connect": "Tidsavbrudd oppretter forbindelse", + "unknown": "Uventet feil" + }, + "create_entry": { + "default": "Vellykket godkjenning" + }, + "step": { + "auth": { + "title": "Koble til Google-kontoen" + }, + "pick_implementation": { + "title": "Velg godkjenningsmetode" + }, + "reauth_confirm": { + "description": "Google Assistant SDK-integrasjonen m\u00e5 autentisere kontoen din p\u00e5 nytt", + "title": "Godkjenne integrering p\u00e5 nytt" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "language_code": "Spr\u00e5kkode" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google_assistant_sdk/translations/pl.json b/homeassistant/components/google_assistant_sdk/translations/pl.json new file mode 100644 index 00000000000..0ad35200fe6 --- /dev/null +++ b/homeassistant/components/google_assistant_sdk/translations/pl.json @@ -0,0 +1,42 @@ +{ + "application_credentials": { + "description": "Post\u0119puj zgodnie z [instrukcjami]({more_info_url}) na [ekran akceptacji OAuth]({oauth_consent_url}), aby przyzna\u0107 Home Assistantowi dost\u0119p do Google Assistant SDK. Musisz r\u00f3wnie\u017c utworzy\u0107 po\u015bwiadczenia aplikacji powi\u0105zane z Twoim kontem:\n1. Przejd\u017a do [Po\u015bwiadczenia]({oauth_creds_url}) i kliknij **Utw\u00f3rz po\u015bwiadczenia**.\n2. Z listy rozwijanej wybierz **Identyfikator klienta OAuth**.\n3. Wybierz **Aplikacja internetowa** jako Typ aplikacji. \n\n" + }, + "config": { + "abort": { + "already_configured": "Konto jest ju\u017c skonfigurowane", + "already_in_progress": "Konfiguracja jest ju\u017c w toku", + "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", + "invalid_access_token": "Niepoprawny token dost\u0119pu", + "missing_configuration": "Komponent nie jest skonfigurowany. Post\u0119puj zgodnie z dokumentacj\u0105.", + "oauth_error": "Otrzymano nieprawid\u0142owe dane tokena.", + "reauth_successful": "Ponowne uwierzytelnienie powiod\u0142o si\u0119", + "timeout_connect": "Up\u0142yn\u0105\u0142 limit czasu po\u0142\u0105czenia", + "unknown": "Nieoczekiwany b\u0142\u0105d" + }, + "create_entry": { + "default": "Pomy\u015blnie uwierzytelniono" + }, + "step": { + "auth": { + "title": "Po\u0142\u0105czenie z kontem Google" + }, + "pick_implementation": { + "title": "Wybierz metod\u0119 uwierzytelniania" + }, + "reauth_confirm": { + "description": "Integracja Google Assistant SDK wymaga ponownego uwierzytelnienia Twojego konta", + "title": "Ponownie uwierzytelnij integracj\u0119" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "language_code": "Kod j\u0119zyka" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google_assistant_sdk/translations/pt-BR.json b/homeassistant/components/google_assistant_sdk/translations/pt-BR.json new file mode 100644 index 00000000000..9864778e52d --- /dev/null +++ b/homeassistant/components/google_assistant_sdk/translations/pt-BR.json @@ -0,0 +1,42 @@ +{ + "application_credentials": { + "description": "Siga as [instru\u00e7\u00f5es]({more_info_url}) para a [tela de consentimento OAuth]({oauth_consent_url}) para dar ao Home Assistant acesso ao seu Google Assistant SDK. Voc\u00ea tamb\u00e9m precisa criar credenciais de aplicativo vinculadas \u00e0 sua conta:\n 1. Acesse [Credenciais]({oauth_creds_url}) e clique em **Criar credenciais**.\n 1. Na lista suspensa, selecione **OAuth client ID**.\n 1. Selecione **Aplicativo da Web** para o Tipo de aplicativo." + }, + "config": { + "abort": { + "already_configured": "A conta j\u00e1 foi configurada", + "already_in_progress": "O fluxo de configura\u00e7\u00e3o j\u00e1 est\u00e1 em andamento", + "cannot_connect": "Falha ao conectar", + "invalid_access_token": "Token de acesso inv\u00e1lido", + "missing_configuration": "O componente n\u00e3o est\u00e1 configurado. Por favor, siga a documenta\u00e7\u00e3o.", + "oauth_error": "Dados de token recebidos inv\u00e1lidos.", + "reauth_successful": "A reautentica\u00e7\u00e3o foi bem-sucedida", + "timeout_connect": "Tempo limite para estabelecer conex\u00e3o atingido", + "unknown": "Erro inesperado" + }, + "create_entry": { + "default": "Autenticado com sucesso" + }, + "step": { + "auth": { + "title": "Vincular conta do Google" + }, + "pick_implementation": { + "title": "Escolha o m\u00e9todo de autentica\u00e7\u00e3o" + }, + "reauth_confirm": { + "description": "A integra\u00e7\u00e3o do Google Assistant SDK precisa autenticar novamente sua conta", + "title": "Reautenticar Integra\u00e7\u00e3o" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "language_code": "C\u00f3digo do idioma" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google_assistant_sdk/translations/ru.json b/homeassistant/components/google_assistant_sdk/translations/ru.json new file mode 100644 index 00000000000..493185dc2d0 --- /dev/null +++ b/homeassistant/components/google_assistant_sdk/translations/ru.json @@ -0,0 +1,42 @@ +{ + "application_credentials": { + "description": "\u0421\u043b\u0435\u0434\u0443\u0439\u0442\u0435 [\u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f\u043c]({more_info_url}) \u043d\u0430 [\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 OAuth]({oauth_consent_url}), \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c Home Assistant \u0434\u043e\u0441\u0442\u0443\u043f \u043a Google Assistant SDK. \u0422\u0430\u043a\u0436\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0443\u0447\u0435\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0435 \u0441 \u0412\u0430\u0448\u0438\u043c \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u043e\u043c:\n1. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u0432 [\u0423\u0447\u0435\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439]({oauth_creds_url}) \u0438 \u043d\u0430\u0436\u043c\u0438\u0442\u0435 **\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0443\u0447\u0435\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f**.\n2. \u0412 \u0432\u044b\u043f\u0430\u0434\u0430\u044e\u0449\u0435\u043c \u0441\u043f\u0438\u0441\u043a\u0435 \u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435 **\u0418\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 OAuth**.\n3. \u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 **\u0412\u0435\u0431 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435** \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0422\u0438\u043f\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f." + }, + "config": { + "abort": { + "already_configured": "\u042d\u0442\u0430 \u0443\u0447\u0451\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 Home Assistant.", + "already_in_progress": "\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f.", + "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", + "invalid_access_token": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430.", + "missing_configuration": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439.", + "oauth_error": "\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u044b \u043d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0442\u043e\u043a\u0435\u043d\u0430.", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e.", + "timeout_connect": "\u0418\u0441\u0442\u0435\u043a\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f.", + "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." + }, + "create_entry": { + "default": "\u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u043f\u0440\u043e\u0439\u0434\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e." + }, + "step": { + "auth": { + "title": "\u0423\u0447\u0435\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c Google" + }, + "pick_implementation": { + "title": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u043f\u043e\u0441\u043e\u0431 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438" + }, + "reauth_confirm": { + "description": "\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0443\u0447\u0435\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438 Google.", + "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "language_code": "\u041a\u043e\u0434 \u044f\u0437\u044b\u043a\u0430" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google_assistant_sdk/translations/sk.json b/homeassistant/components/google_assistant_sdk/translations/sk.json new file mode 100644 index 00000000000..2c710d13a70 --- /dev/null +++ b/homeassistant/components/google_assistant_sdk/translations/sk.json @@ -0,0 +1,42 @@ +{ + "application_credentials": { + "description": "Pod\u013ea [pokynov]({more_info_url}) pre [obrazovka s\u00fahlasu s protokolom OAuth]({oauth_consent_url}) povo\u013ete dom\u00e1cemu Asistentovi pr\u00edstup k s\u00faprave Google Assistant SDK. Mus\u00edte tie\u017e vytvori\u0165 poverenia aplik\u00e1cie prepojen\u00e9 s va\u0161\u00edm \u00fa\u010dtom:\n 1. Prejdite na [Poverenia]({oauth_creds_url}) a kliknite na **Vytvori\u0165 poverenia**.\n 1. Z rozba\u013eovacieho zoznamu vyberte **ID klienta OAuth**.\n 1. Ako Typ aplik\u00e1cie vyberte **Webov\u00e1 aplik\u00e1cia**. \n\n" + }, + "config": { + "abort": { + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_access_token": "Neplatn\u00fd pr\u00edstupov\u00fd token", + "missing_configuration": "Komponent nie je nakonfigurovan\u00fd. Postupujte pod\u013ea dokument\u00e1cie.", + "oauth_error": "Prijat\u00e9 neplatn\u00e9 \u00fadaje tokenu.", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", + "timeout_connect": "\u010casov\u00fd limit na nadviazanie spojenia", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "create_entry": { + "default": "\u00daspe\u0161ne overen\u00e9" + }, + "step": { + "auth": { + "title": "Prepoji\u0165 \u00fa\u010det Google" + }, + "pick_implementation": { + "title": "Vyberte met\u00f3du overenia" + }, + "reauth_confirm": { + "description": "Integr\u00e1cia s\u00fapravy Google Assistant SDK vy\u017eaduje op\u00e4tovn\u00e9 overenie v\u00e1\u0161ho \u00fa\u010dtu", + "title": "Znova overi\u0165 integr\u00e1ciu" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "language_code": "K\u00f3d jazyka" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google_assistant_sdk/translations/zh-Hant.json b/homeassistant/components/google_assistant_sdk/translations/zh-Hant.json new file mode 100644 index 00000000000..3212505e74e --- /dev/null +++ b/homeassistant/components/google_assistant_sdk/translations/zh-Hant.json @@ -0,0 +1,42 @@ +{ + "application_credentials": { + "description": "\u8ddf\u96a8[\u8aaa\u660e]({more_info_url})\u4ee5\u8a2d\u5b9a\u81f3 [OAuth \u540c\u610f\u756b\u9762]({oauth_consent_url})\u3001\u4f9b Home Assistant \u5b58\u53d6\u60a8\u7684 Google Assistant SDK\u3002\u540c\u6642\u9700\u8981\u65b0\u589e\u9023\u7d50\u81f3\u5e33\u865f\u7684\u61c9\u7528\u7a0b\u5f0f\u6191\u8b49\uff1a\n1. \u700f\u89bd\u81f3 [\u6191\u8b49]({oauth_creds_url}) \u9801\u9762\u4e26\u9ede\u9078 **\u5efa\u7acb\u6191\u8b49**\u3002\n1. \u7531\u4e0b\u62c9\u9078\u55ae\u4e2d\u9078\u64c7 **OAuth \u7528\u6236\u7aef ID**\u3002\n1. \u61c9\u7528\u7a0b\u5f0f\u985e\u578b\u5247\u9078\u64c7 **Web \u61c9\u7528\u7a0b\u5f0f**\u3002\n\n" + }, + "config": { + "abort": { + "already_configured": "\u5e33\u865f\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", + "already_in_progress": "\u8a2d\u5b9a\u5df2\u7d93\u9032\u884c\u4e2d", + "cannot_connect": "\u9023\u7dda\u5931\u6557", + "invalid_access_token": "\u5b58\u53d6\u6b0a\u6756\u7121\u6548", + "missing_configuration": "\u5143\u4ef6\u5c1a\u672a\u8a2d\u7f6e\uff0c\u8acb\u53c3\u95b1\u6587\u4ef6\u8aaa\u660e\u3002", + "oauth_error": "\u6536\u5230\u7121\u6548\u7684\u6b0a\u6756\u8cc7\u6599\u3002", + "reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f", + "timeout_connect": "\u5efa\u7acb\u9023\u7dda\u903e\u6642", + "unknown": "\u672a\u9810\u671f\u932f\u8aa4" + }, + "create_entry": { + "default": "\u5df2\u6210\u529f\u8a8d\u8b49" + }, + "step": { + "auth": { + "title": "\u9023\u7d50 Google \u5e33\u865f" + }, + "pick_implementation": { + "title": "\u9078\u64c7\u9a57\u8b49\u6a21\u5f0f" + }, + "reauth_confirm": { + "description": "Google Assistant SDK \u6574\u5408\u9700\u8981\u91cd\u65b0\u8a8d\u8b49\u60a8\u7684\u5e33\u865f", + "title": "\u91cd\u65b0\u8a8d\u8b49\u6574\u5408" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "language_code": "\u8a9e\u8a00\u4ee3\u78bc" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google_maps/device_tracker.py b/homeassistant/components/google_maps/device_tracker.py index 8f15278e214..e1927aa81c5 100644 --- a/homeassistant/components/google_maps/device_tracker.py +++ b/homeassistant/components/google_maps/device_tracker.py @@ -80,7 +80,8 @@ class GoogleMapsScanner: except InvalidCookies: _LOGGER.error( - "The cookie file provided does not provide a valid session. Please create another one and try again" + "The cookie file provided does not provide a valid session. Please" + " create another one and try again" ) self.success_init = False @@ -97,8 +98,10 @@ class GoogleMapsScanner: and person.accuracy > self.max_gps_accuracy ): _LOGGER.info( - "Ignoring %s update because expected GPS " - "accuracy %s is not met: %s", + ( + "Ignoring %s update because expected GPS " + "accuracy %s is not met: %s" + ), person.nickname, self.max_gps_accuracy, person.accuracy, @@ -108,8 +111,7 @@ class GoogleMapsScanner: last_seen = dt_util.as_utc(person.datetime) if last_seen < self._prev_seen.get(dev_id, last_seen): _LOGGER.warning( - "Ignoring %s update because timestamp " - "is older than last timestamp", + "Ignoring %s update because timestamp is older than last timestamp", person.nickname, ) _LOGGER.debug("%s < %s", last_seen, self._prev_seen[dev_id]) diff --git a/homeassistant/components/google_pubsub/manifest.json b/homeassistant/components/google_pubsub/manifest.json index d7318e597e1..c87e79c34d5 100644 --- a/homeassistant/components/google_pubsub/manifest.json +++ b/homeassistant/components/google_pubsub/manifest.json @@ -2,7 +2,7 @@ "domain": "google_pubsub", "name": "Google Pub/Sub", "documentation": "https://www.home-assistant.io/integrations/google_pubsub", - "requirements": ["google-cloud-pubsub==2.13.10"], + "requirements": ["google-cloud-pubsub==2.13.11"], "codeowners": [], "iot_class": "cloud_push" } diff --git a/homeassistant/components/google_sheets/__init__.py b/homeassistant/components/google_sheets/__init__.py index 19f5ce81f5c..803b737283b 100644 --- a/homeassistant/components/google_sheets/__init__.py +++ b/homeassistant/components/google_sheets/__init__.py @@ -21,9 +21,10 @@ from homeassistant.helpers.config_entry_oauth2_flow import ( import homeassistant.helpers.config_validation as cv from homeassistant.helpers.selector import ConfigEntrySelector -from .const import DATA_CONFIG_ENTRY, DEFAULT_ACCESS, DOMAIN +from .const import DEFAULT_ACCESS, DOMAIN DATA = "data" +DATA_CONFIG_ENTRY = "config_entry" WORKSHEET = "worksheet" SERVICE_APPEND_SHEET = "append_sheet" diff --git a/homeassistant/components/google_sheets/application_credentials.py b/homeassistant/components/google_sheets/application_credentials.py index c54356b659e..415ab5947bf 100644 --- a/homeassistant/components/google_sheets/application_credentials.py +++ b/homeassistant/components/google_sheets/application_credentials.py @@ -1,14 +1,9 @@ """application_credentials platform for Google Sheets.""" - import oauth2client from homeassistant.components.application_credentials import AuthorizationServer from homeassistant.core import HomeAssistant -AUTHORIZATION_SERVER = AuthorizationServer( - oauth2client.GOOGLE_AUTH_URI, oauth2client.GOOGLE_TOKEN_URI -) - async def async_get_authorization_server(hass: HomeAssistant) -> AuthorizationServer: """Return authorization server.""" @@ -21,7 +16,9 @@ async def async_get_authorization_server(hass: HomeAssistant) -> AuthorizationSe async def async_get_description_placeholders(hass: HomeAssistant) -> dict[str, str]: """Return description placeholders for the credentials dialog.""" return { - "oauth_consent_url": "https://console.cloud.google.com/apis/credentials/consent", + "oauth_consent_url": ( + "https://console.cloud.google.com/apis/credentials/consent" + ), "more_info_url": "https://www.home-assistant.io/integrations/google_sheets/", "oauth_creds_url": "https://console.cloud.google.com/apis/credentials", } diff --git a/homeassistant/components/google_sheets/const.py b/homeassistant/components/google_sheets/const.py index f8f065972f9..71ef8f4a4f4 100644 --- a/homeassistant/components/google_sheets/const.py +++ b/homeassistant/components/google_sheets/const.py @@ -1,10 +1,7 @@ """Constants for Google Sheets integration.""" from __future__ import annotations -from typing import Final - DOMAIN = "google_sheets" -DATA_CONFIG_ENTRY: Final = "config_entry" DEFAULT_NAME = "Google Sheets" DEFAULT_ACCESS = "https://www.googleapis.com/auth/drive.file" diff --git a/homeassistant/components/google_sheets/strings.json b/homeassistant/components/google_sheets/strings.json index 33230038cdf..602301758f8 100644 --- a/homeassistant/components/google_sheets/strings.json +++ b/homeassistant/components/google_sheets/strings.json @@ -4,10 +4,6 @@ "pick_implementation": { "title": "[%key:common::config_flow::title::oauth2_pick_implementation%]" }, - "reauth_confirm": { - "title": "[%key:common::config_flow::title::reauth%]", - "description": "The Google Sheets integration needs to re-authenticate your account" - }, "auth": { "title": "Link Google Account" }, diff --git a/homeassistant/components/google_sheets/translations/ko.json b/homeassistant/components/google_sheets/translations/ko.json new file mode 100644 index 00000000000..ea33c206edd --- /dev/null +++ b/homeassistant/components/google_sheets/translations/ko.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_access_token": "\uc561\uc138\uc2a4 \ud1a0\ud070\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "missing_configuration": "\uad6c\uc131\uc694\uc18c\uac00 \uad6c\uc131\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4. \uc124\uba85\uc11c\ub97c \ucc38\uace0\ud574\uc8fc\uc138\uc694.", + "oauth_error": "\uc798\ubabb\ub41c \ud1a0\ud070 \ub370\uc774\ud130\ub97c \ubc1b\uc558\uc2b5\ub2c8\ub2e4.", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4", + "timeout_connect": "\uc5f0\uacb0 \uc124\uc815 \uc2dc\uac04 \ucd08\uacfc", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "pick_implementation": { + "title": "\uc778\uc99d \ubc29\ubc95 \uc120\ud0dd\ud558\uae30" + }, + "reauth_confirm": { + "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google_sheets/translations/pt.json b/homeassistant/components/google_sheets/translations/pt.json index 60c19d18dc2..80c52ab4593 100644 --- a/homeassistant/components/google_sheets/translations/pt.json +++ b/homeassistant/components/google_sheets/translations/pt.json @@ -1,7 +1,16 @@ { "config": { "abort": { - "open_spreadsheet_failure": "Erro ao abrir a planilha, veja o log de erros para detalhes" + "already_configured": "Conta j\u00e1 configurada", + "already_in_progress": "O processo de configura\u00e7\u00e3o j\u00e1 est\u00e1 a decorrer", + "cannot_connect": "A liga\u00e7\u00e3o falhou", + "invalid_access_token": "Token de acesso inv\u00e1lido", + "missing_configuration": "O componente n\u00e3o est\u00e1 configurado. Por favor, siga a documenta\u00e7\u00e3o.", + "oauth_error": "Recebido token de autentica\u00e7\u00e3o inv\u00e1lido.", + "open_spreadsheet_failure": "Erro ao abrir a planilha, veja o log de erros para detalhes", + "reauth_successful": "Reautentica\u00e7\u00e3o bem sucedida", + "timeout_connect": "Excedido o tempo limite para estabelecer liga\u00e7\u00e3o", + "unknown": "Erro inesperado" }, "create_entry": { "default": "Autentica\u00e7\u00e3o com sucesso e planilha criada em: {url}" @@ -9,6 +18,12 @@ "step": { "auth": { "title": "Vincular Conta do Google" + }, + "pick_implementation": { + "title": "Escolha o m\u00e9todo de autentica\u00e7\u00e3o" + }, + "reauth_confirm": { + "title": "Reautenticar integra\u00e7\u00e3o" } } } diff --git a/homeassistant/components/google_sheets/translations/sk.json b/homeassistant/components/google_sheets/translations/sk.json index 37e824e8df9..b902d2f39d1 100644 --- a/homeassistant/components/google_sheets/translations/sk.json +++ b/homeassistant/components/google_sheets/translations/sk.json @@ -1,4 +1,7 @@ { + "application_credentials": { + "description": "Pod\u013ea [pokynov]({more_info_url}) pre [obrazovka s\u00fahlasu s protokolom OAuth]({oauth_consent_url}) povo\u013ete Asistentovi dom\u00e1cnosti pr\u00edstup k svojim Tabu\u013ek\u00e1m Google. Mus\u00edte tie\u017e vytvori\u0165 poverenia aplik\u00e1cie prepojen\u00e9 s va\u0161\u00edm \u00fa\u010dtom:\n 1. Prejdite na [Poverenia]({oauth_creds_url}) a kliknite na **Vytvori\u0165 poverenia**.\n 1. Z rozba\u013eovacieho zoznamu vyberte **ID klienta OAuth**.\n 1. Ako Typ aplik\u00e1cie vyberte **Webov\u00e1 aplik\u00e1cia**. \n\n" + }, "config": { "abort": { "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", diff --git a/homeassistant/components/google_translate/const.py b/homeassistant/components/google_translate/const.py new file mode 100644 index 00000000000..e6361c1025e --- /dev/null +++ b/homeassistant/components/google_translate/const.py @@ -0,0 +1,306 @@ +"""Constant for google_translate integration.""" +from dataclasses import dataclass + +SUPPORT_LANGUAGES = [ + "af", + "ar", + "bg", + "bn", + "bs", + "ca", + "cs", + "cy", + "da", + "de", + "el", + "en", + "eo", + "es", + "et", + "fi", + "fr", + "gu", + "hi", + "hr", + "hu", + "hy", + "id", + "is", + "it", + "iw", + "ja", + "jw", + "km", + "kn", + "ko", + "la", + "lv", + "mk", + "ml", + "mr", + "my", + "ne", + "nl", + "no", + "pl", + "pt", + "ro", + "ru", + "si", + "sk", + "sq", + "sr", + "su", + "sv", + "sw", + "ta", + "te", + "th", + "tl", + "tr", + "uk", + "ur", + "vi", + # dialects + "zh-CN", + "zh-cn", + "zh-tw", + "en-us", + "en-ca", + "en-uk", + "en-gb", + "en-au", + "en-gh", + "en-in", + "en-ie", + "en-nz", + "en-ng", + "en-ph", + "en-za", + "en-tz", + "fr-ca", + "fr-fr", + "pt-br", + "pt-pt", + "es-es", + "es-us", +] + +SUPPORT_TLD = [ + "com", + "ad", + "ae", + "com.af", + "com.ag", + "com.ai", + "al", + "am", + "co.ao", + "com.ar", + "as", + "at", + "com.au", + "az", + "ba", + "com.bd", + "be", + "bf", + "bg", + "com.bh", + "bi", + "bj", + "com.bn", + "com.bo", + "com.br", + "bs", + "bt", + "co.bw", + "by", + "com.bz", + "ca", + "cd", + "cf", + "cg", + "ch", + "ci", + "co.ck", + "cl", + "cm", + "cn", + "com.co", + "co.cr", + "com.cu", + "cv", + "com.cy", + "cz", + "de", + "dj", + "dk", + "dm", + "com.do", + "dz", + "com.ec", + "ee", + "com.eg", + "es", + "com.et", + "fi", + "com.fj", + "fm", + "fr", + "ga", + "ge", + "gg", + "com.gh", + "com.gi", + "gl", + "gm", + "gr", + "com.gt", + "gy", + "com.hk", + "hn", + "hr", + "ht", + "hu", + "co.id", + "ie", + "co.il", + "im", + "co.in", + "iq", + "is", + "it", + "je", + "com.jm", + "jo", + "co.jp", + "co.ke", + "com.kh", + "ki", + "kg", + "co.kr", + "com.kw", + "kz", + "la", + "com.lb", + "li", + "lk", + "co.ls", + "lt", + "lu", + "lv", + "com.ly", + "co.ma", + "md", + "me", + "mg", + "mk", + "ml", + "com.mm", + "mn", + "ms", + "com.mt", + "mu", + "mv", + "mw", + "com.mx", + "com.my", + "co.mz", + "com.na", + "com.ng", + "com.ni", + "ne", + "nl", + "no", + "com.np", + "nr", + "nu", + "co.nz", + "com.om", + "com.pa", + "com.pe", + "com.pg", + "com.ph", + "com.pk", + "pl", + "pn", + "com.pr", + "ps", + "pt", + "com.py", + "com.qa", + "ro", + "ru", + "rw", + "com.sa", + "com.sb", + "sc", + "se", + "com.sg", + "sh", + "si", + "sk", + "com.sl", + "sn", + "so", + "sm", + "sr", + "st", + "com.sv", + "td", + "tg", + "co.th", + "com.tj", + "tl", + "tm", + "tn", + "to", + "com.tr", + "tt", + "com.tw", + "co.tz", + "com.ua", + "co.ug", + "co.uk", + "com.uy", + "co.uz", + "com.vc", + "co.ve", + "vg", + "co.vi", + "com.vn", + "vu", + "ws", + "rs", + "co.za", + "co.zm", + "co.zw", + "cat", +] + + +@dataclass +class Dialect: + """Language and TLD for a dialect supported by Google Translate.""" + + lang: str + tld: str + + +MAP_LANG_TLD: dict[str, Dialect] = { + "en-us": Dialect("en", "com"), + "en-gb": Dialect("en", "co.uk"), + "en-uk": Dialect("en", "co.uk"), + "en-au": Dialect("en", "com.au"), + "en-ca": Dialect("en", "ca"), + "en-in": Dialect("en", "co.in"), + "en-ie": Dialect("en", "ie"), + "en-za": Dialect("en", "co.za"), + "fr-ca": Dialect("fr", "ca"), + "fr-fr": Dialect("fr", "fr"), + "pt-br": Dialect("pt", "com.br"), + "pt-pt": Dialect("pt", "pt"), + "es-es": Dialect("es", "es"), + "es-us": Dialect("es", "com"), +} diff --git a/homeassistant/components/google_translate/tts.py b/homeassistant/components/google_translate/tts.py index eb6faf22bbc..c698a16baae 100644 --- a/homeassistant/components/google_translate/tts.py +++ b/homeassistant/components/google_translate/tts.py @@ -7,112 +7,41 @@ import voluptuous as vol from homeassistant.components.tts import CONF_LANG, PLATFORM_SCHEMA, Provider -_LOGGER = logging.getLogger(__name__) +from .const import MAP_LANG_TLD, SUPPORT_LANGUAGES, SUPPORT_TLD -SUPPORT_LANGUAGES = [ - "af", - "ar", - "bg", - "bn", - "bs", - "ca", - "cs", - "cy", - "da", - "de", - "el", - "en", - "eo", - "es", - "et", - "fi", - "fr", - "gu", - "hi", - "hr", - "hu", - "hy", - "id", - "is", - "it", - "iw", - "ja", - "jw", - "km", - "kn", - "ko", - "la", - "lv", - "mk", - "ml", - "mr", - "my", - "ne", - "nl", - "no", - "pl", - "pt", - "ro", - "ru", - "si", - "sk", - "sq", - "sr", - "su", - "sv", - "sw", - "ta", - "te", - "th", - "tl", - "tr", - "uk", - "ur", - "vi", - # dialects - "zh-CN", - "zh-cn", - "zh-tw", - "en-us", - "en-ca", - "en-uk", - "en-gb", - "en-au", - "en-gh", - "en-in", - "en-ie", - "en-nz", - "en-ng", - "en-ph", - "en-za", - "en-tz", - "fr-ca", - "fr-fr", - "pt-br", - "pt-pt", - "es-es", - "es-us", -] +_LOGGER = logging.getLogger(__name__) DEFAULT_LANG = "en" +SUPPORT_OPTIONS = ["tld"] + +DEFAULT_TLD = "com" + PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( - {vol.Optional(CONF_LANG, default=DEFAULT_LANG): vol.In(SUPPORT_LANGUAGES)} + { + vol.Optional(CONF_LANG, default=DEFAULT_LANG): vol.In(SUPPORT_LANGUAGES), + vol.Optional("tld", default=DEFAULT_TLD): vol.In(SUPPORT_TLD), + } ) async def async_get_engine(hass, config, discovery_info=None): """Set up Google speech component.""" - return GoogleProvider(hass, config[CONF_LANG]) + return GoogleProvider(hass, config[CONF_LANG], config["tld"]) class GoogleProvider(Provider): """The Google speech API provider.""" - def __init__(self, hass, lang): + def __init__(self, hass, lang, tld): """Init Google TTS service.""" self.hass = hass - self._lang = lang + if lang in MAP_LANG_TLD: + self._lang = MAP_LANG_TLD[lang].lang + self._tld = MAP_LANG_TLD[lang].tld + else: + self._lang = lang + self._tld = tld self.name = "Google" @property @@ -125,9 +54,20 @@ class GoogleProvider(Provider): """Return list of supported languages.""" return SUPPORT_LANGUAGES + @property + def supported_options(self): + """Return a list of supported options.""" + return SUPPORT_OPTIONS + def get_tts_audio(self, message, language, options=None): """Load TTS from google.""" - tts = gTTS(text=message, lang=language) + tld = self._tld + if language in MAP_LANG_TLD: + tld = MAP_LANG_TLD[language].tld + language = MAP_LANG_TLD[language].lang + if options is not None and "tld" in options.keys(): + tld = options["tld"] + tts = gTTS(text=message, lang=language, tld=tld) mp3_data = BytesIO() try: diff --git a/homeassistant/components/google_travel_time/manifest.json b/homeassistant/components/google_travel_time/manifest.json index 4b2693374f6..a21714218a9 100644 --- a/homeassistant/components/google_travel_time/manifest.json +++ b/homeassistant/components/google_travel_time/manifest.json @@ -6,5 +6,5 @@ "codeowners": ["@eifinger"], "config_flow": true, "iot_class": "cloud_polling", - "loggers": ["googlemaps"] + "loggers": ["googlemaps", "homeassistant.helpers.location"] } diff --git a/homeassistant/components/google_travel_time/sensor.py b/homeassistant/components/google_travel_time/sensor.py index e75db1a29e9..ffbc4ff3cfd 100644 --- a/homeassistant/components/google_travel_time/sensor.py +++ b/homeassistant/components/google_travel_time/sensor.py @@ -13,7 +13,7 @@ from homeassistant.const import ( CONF_API_KEY, CONF_NAME, EVENT_HOMEASSISTANT_STARTED, - TIME_MINUTES, + UnitOfTime, ) from homeassistant.core import CoreState, HomeAssistant from homeassistant.helpers.device_registry import DeviceEntryType @@ -76,7 +76,7 @@ class GoogleTravelTimeSensor(SensorEntity): """Initialize the sensor.""" self._name = name self._config_entry = config_entry - self._unit_of_measurement = TIME_MINUTES + self._unit_of_measurement = UnitOfTime.MINUTES self._matrix = None self._api_key = api_key self._unique_id = config_entry.entry_id diff --git a/homeassistant/components/google_travel_time/translations/ko.json b/homeassistant/components/google_travel_time/translations/ko.json index 41873626ea5..c7977136b4a 100644 --- a/homeassistant/components/google_travel_time/translations/ko.json +++ b/homeassistant/components/google_travel_time/translations/ko.json @@ -4,13 +4,15 @@ "already_configured": "\uc704\uce58\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" }, "error": { - "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" }, "step": { "user": { "data": { "api_key": "API \ud0a4", "destination": "\ubaa9\uc801\uc9c0", + "name": "\uc774\ub984", "origin": "\ucd9c\ubc1c\uc9c0" }, "description": "\ucd9c\ubc1c\uc9c0\uc640 \ubaa9\uc801\uc9c0\ub97c \uc9c0\uc815\ud560 \ub54c \uc8fc\uc18c, \uc704\ub3c4/\uacbd\ub3c4 \uc88c\ud45c \ub610\ub294 Google Place ID \ud615\uc2dd\uc73c\ub85c \ud30c\uc774\ud504 \ubb38\uc790(|)\ub85c \uad6c\ubd84\ub41c \ud558\ub098 \uc774\uc0c1\uc758 \uc704\uce58\ub97c \uc785\ub825\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4. Google Place ID\ub97c \uc0ac\uc6a9\ud558\uc5ec \uc704\uce58\ub97c \uc9c0\uc815\ud560 \ub54c\ub294 ID \uc55e\uc5d0 `place_id:`\ub97c \ubd99\uc5ec\uc57c \ud569\ub2c8\ub2e4." diff --git a/homeassistant/components/google_travel_time/translations/sk.json b/homeassistant/components/google_travel_time/translations/sk.json index 233c48e1b42..d5b4a578e52 100644 --- a/homeassistant/components/google_travel_time/translations/sk.json +++ b/homeassistant/components/google_travel_time/translations/sk.json @@ -11,8 +11,11 @@ "user": { "data": { "api_key": "API k\u013e\u00fa\u010d", - "name": "N\u00e1zov" - } + "destination": "Cie\u013e", + "name": "N\u00e1zov", + "origin": "V\u00fdchodzie miesto" + }, + "description": "Pri zad\u00e1van\u00ed miesta p\u00f4vodu a cie\u013ea m\u00f4\u017eete zada\u0165 jedno alebo viac miest oddelen\u00fdch \u010diarou vo forme adresy, s\u00faradn\u00edc zemepisnej \u0161\u00edrky a d\u013a\u017eky alebo identifik\u00e1tora miesta Google. Pri zad\u00e1van\u00ed miesta pomocou identifik\u00e1tora miesta Google mus\u00ed by\u0165 identifik\u00e1tor predponou `place_id:`." } } }, @@ -20,11 +23,19 @@ "step": { "init": { "data": { + "avoid": "Vyhn\u00fa\u0165 sa", "language": "Jazyk", + "mode": "Re\u017eim cestovania", "time": "\u010cas", + "time_type": "Typ \u010dasu", + "traffic_mode": "Re\u017eim prem\u00e1vky", + "transit_mode": "Re\u017eim verejnej dopravy", + "transit_routing_preference": "Predvo\u013eba smerovania verejnej dopravy", "units": "Jednotky" - } + }, + "description": "Volite\u013ene m\u00f4\u017eete zada\u0165 \u010das odchodu alebo \u010das pr\u00edchodu. Ak zad\u00e1vate \u010das odchodu, m\u00f4\u017eete zada\u0165 \u201eteraz\u201c, \u010dasov\u00fa pe\u010diatku syst\u00e9mu Unix alebo 24-hodinov\u00fd \u010dasov\u00fd re\u0165azec, napr\u00edklad \u201e08:00:00\u201c. Ak zad\u00e1vate \u010das pr\u00edchodu, m\u00f4\u017eete pou\u017ei\u0165 \u010dasov\u00fa pe\u010diatku Unixu alebo 24-hodinov\u00fd \u010dasov\u00fd re\u0165azec, napr\u00edklad `08:00:00`" } } - } + }, + "title": "\u010cas cesty v Map\u00e1ch Google" } \ No newline at end of file diff --git a/homeassistant/components/google_wifi/sensor.py b/homeassistant/components/google_wifi/sensor.py index 2ebbd44b81b..d7be81a8ea5 100644 --- a/homeassistant/components/google_wifi/sensor.py +++ b/homeassistant/components/google_wifi/sensor.py @@ -18,7 +18,7 @@ from homeassistant.const import ( CONF_MONITORED_CONDITIONS, CONF_NAME, STATE_UNKNOWN, - TIME_DAYS, + UnitOfTime, ) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv @@ -75,7 +75,7 @@ SENSOR_TYPES: tuple[GoogleWifiSensorEntityDescription, ...] = ( key=ATTR_UPTIME, primary_key="system", sensor_key="uptime", - native_unit_of_measurement=TIME_DAYS, + native_unit_of_measurement=UnitOfTime.DAYS, icon="mdi:timelapse", ), GoogleWifiSensorEntityDescription( @@ -228,8 +228,10 @@ class GoogleWifiAPI: self.data[attr_key] = sensor_value except KeyError: _LOGGER.error( - "Router does not support %s field. " - "Please remove %s from monitored_conditions", + ( + "Router does not support %s field. " + "Please remove %s from monitored_conditions" + ), description.sensor_key, attr_key, ) diff --git a/homeassistant/components/govee_ble/manifest.json b/homeassistant/components/govee_ble/manifest.json index 8272ab5d20e..ad54aa4bc43 100644 --- a/homeassistant/components/govee_ble/manifest.json +++ b/homeassistant/components/govee_ble/manifest.json @@ -73,8 +73,8 @@ "connectable": false } ], - "requirements": ["govee-ble==0.19.3"], + "requirements": ["govee-ble==0.21.0"], "dependencies": ["bluetooth"], - "codeowners": ["@bdraco"], + "codeowners": ["@bdraco", "@PierreAronnax"], "iot_class": "local_push" } diff --git a/homeassistant/components/govee_ble/sensor.py b/homeassistant/components/govee_ble/sensor.py index 1d193287419..ce603b5b5a6 100644 --- a/homeassistant/components/govee_ble/sensor.py +++ b/homeassistant/components/govee_ble/sensor.py @@ -4,6 +4,7 @@ from __future__ import annotations from typing import Optional, Union from govee_ble import DeviceClass, DeviceKey, SensorUpdate, Units +from govee_ble.parser import ERROR from homeassistant import config_entries from homeassistant.components.bluetooth.passive_update_processor import ( @@ -22,7 +23,7 @@ from homeassistant.components.sensor import ( from homeassistant.const import ( PERCENTAGE, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -34,7 +35,7 @@ SENSOR_DESCRIPTIONS = { (DeviceClass.TEMPERATURE, Units.TEMP_CELSIUS): SensorEntityDescription( key=f"{DeviceClass.TEMPERATURE}_{Units.TEMP_CELSIUS}", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, ), (DeviceClass.HUMIDITY, Units.PERCENTAGE): SensorEntityDescription( @@ -116,13 +117,21 @@ async def async_setup_entry( class GoveeBluetoothSensorEntity( PassiveBluetoothProcessorEntity[ - PassiveBluetoothDataProcessor[Optional[Union[float, int]]] + PassiveBluetoothDataProcessor[Optional[Union[float, int, str]]] ], SensorEntity, ): """Representation of a govee ble sensor.""" @property - def native_value(self) -> int | float | None: + def available(self) -> bool: + """Return False if sensor is in error.""" + return ( + self.processor.entity_data.get(self.entity_key) != ERROR + and super().available + ) + + @property + def native_value(self) -> float | int | str | None: """Return the native value.""" return self.processor.entity_data.get(self.entity_key) diff --git a/homeassistant/components/govee_ble/translations/en.json b/homeassistant/components/govee_ble/translations/en.json index d24df64f135..f99ec0bbe63 100644 --- a/homeassistant/components/govee_ble/translations/en.json +++ b/homeassistant/components/govee_ble/translations/en.json @@ -8,13 +8,13 @@ "flow_title": "{name}", "step": { "bluetooth_confirm": { - "description": "Do you want to setup {name}?" + "description": "Do you want to set up {name}?" }, "user": { "data": { "address": "Device" }, - "description": "Choose a device to setup" + "description": "Choose a device to set up" } } } diff --git a/homeassistant/components/govee_ble/translations/it.json b/homeassistant/components/govee_ble/translations/it.json index 501b5095826..3ede7709c00 100644 --- a/homeassistant/components/govee_ble/translations/it.json +++ b/homeassistant/components/govee_ble/translations/it.json @@ -14,7 +14,7 @@ "data": { "address": "Dispositivo" }, - "description": "Seleziona un dispositivo da configurare" + "description": "Scegli un dispositivo da configurare" } } } diff --git a/homeassistant/components/govee_ble/translations/ko.json b/homeassistant/components/govee_ble/translations/ko.json new file mode 100644 index 00000000000..1a59c05b30c --- /dev/null +++ b/homeassistant/components/govee_ble/translations/ko.json @@ -0,0 +1,9 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", + "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/govee_ble/translations/no.json b/homeassistant/components/govee_ble/translations/no.json index 28ec4582177..0a44ef08d80 100644 --- a/homeassistant/components/govee_ble/translations/no.json +++ b/homeassistant/components/govee_ble/translations/no.json @@ -8,7 +8,7 @@ "flow_title": "{name}", "step": { "bluetooth_confirm": { - "description": "Vil du konfigurere {name}?" + "description": "Vil du sette opp {name} ?" }, "user": { "data": { diff --git a/homeassistant/components/govee_ble/translations/pt.json b/homeassistant/components/govee_ble/translations/pt.json new file mode 100644 index 00000000000..c6a15010072 --- /dev/null +++ b/homeassistant/components/govee_ble/translations/pt.json @@ -0,0 +1,9 @@ +{ + "config": { + "abort": { + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", + "already_in_progress": "O processo de configura\u00e7\u00e3o j\u00e1 est\u00e1 a decorrer", + "no_devices_found": "Nenhum dispositivo encontrado na rede" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/gpslogger/strings.json b/homeassistant/components/gpslogger/strings.json index d75272fe33c..a946574f8b8 100644 --- a/homeassistant/components/gpslogger/strings.json +++ b/homeassistant/components/gpslogger/strings.json @@ -12,7 +12,7 @@ "webhook_not_internet_accessible": "[%key:common::config_flow::abort::webhook_not_internet_accessible%]" }, "create_entry": { - "default": "To send events to Home Assistant, you will need to setup the webhook feature in GPSLogger.\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n\nSee [the documentation]({docs_url}) for further details." + "default": "To send events to Home Assistant, you will need to set up the webhook feature in GPSLogger.\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n\nSee [the documentation]({docs_url}) for further details." } } } diff --git a/homeassistant/components/gpslogger/translations/ca.json b/homeassistant/components/gpslogger/translations/ca.json index e095a8bdb14..b5ac4d4ce20 100644 --- a/homeassistant/components/gpslogger/translations/ca.json +++ b/homeassistant/components/gpslogger/translations/ca.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "La teva inst\u00e0ncia de Home Assistant ha de ser accessible des d'Internet per poder rebre missatges webhook." }, "create_entry": { - "default": "Per enviar esdeveniments a Home Assistant, haur\u00e0s de configurar l'opci\u00f3 webhook de GPSLogger.\n\nCompleta la seg\u00fcent informaci\u00f3:\n\n- URL: `{webhook_url}` \n- M\u00e8tode: POST \n\nConsulta la [documentaci\u00f3]({docs_url}) per a m\u00e9s detalls." + "default": "Per enviar esdeveniments a Home Assistant, has de configurar l'opci\u00f3 webhook de GPSLogger.\n\nCompleta la seg\u00fcent informaci\u00f3:\n\n- URL: `{webhook_url}` \n- M\u00e8tode: POST \n\nConsulta la [documentaci\u00f3]({docs_url}) per a m\u00e9s detalls." }, "step": { "user": { diff --git a/homeassistant/components/gpslogger/translations/de.json b/homeassistant/components/gpslogger/translations/de.json index a6ac2e228e2..10665bb768d 100644 --- a/homeassistant/components/gpslogger/translations/de.json +++ b/homeassistant/components/gpslogger/translations/de.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "Deine Home Assistant-Instanz muss \u00fcber das Internet erreichbar sein, um Webhook-Nachrichten empfangen zu k\u00f6nnen." }, "create_entry": { - "default": "Um Ereignisse an Home Assistant zu senden, muss das Webhook Feature in der GPSLogger konfiguriert werden.\n\n F\u00fcge die folgenden Informationen ein: \n\n - URL: ` {webhook_url} ` \n - Methode: POST \n \n Weitere Informationen finden sich in der [Dokumentation]({docs_url})." + "default": "Um Ereignisse an Home Assistant zu senden, muss das Webhook Feature in GPSLogger konfiguriert werden.\n\nF\u00fcge die folgenden Informationen ein: \n\n- URL: `{webhook_url}` \n- Methode: POST \n \nWeitere Informationen finden sich in der [Dokumentation]({docs_url})." }, "step": { "user": { diff --git a/homeassistant/components/gpslogger/translations/en.json b/homeassistant/components/gpslogger/translations/en.json index 24949d0933a..b800c8444ab 100644 --- a/homeassistant/components/gpslogger/translations/en.json +++ b/homeassistant/components/gpslogger/translations/en.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "Your Home Assistant instance needs to be accessible from the internet to receive webhook messages." }, "create_entry": { - "default": "To send events to Home Assistant, you will need to setup the webhook feature in GPSLogger.\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n\nSee [the documentation]({docs_url}) for further details." + "default": "To send events to Home Assistant, you will need to set up the webhook feature in GPSLogger.\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n\nSee [the documentation]({docs_url}) for further details." }, "step": { "user": { diff --git a/homeassistant/components/gpslogger/translations/es.json b/homeassistant/components/gpslogger/translations/es.json index bf6f3052e23..72b5d960f7a 100644 --- a/homeassistant/components/gpslogger/translations/es.json +++ b/homeassistant/components/gpslogger/translations/es.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "Tu instancia de Home Assistant debe estar accesible desde Internet para recibir mensajes webhook." }, "create_entry": { - "default": "Para enviar eventos a Home Assistant, necesitar\u00e1s configurar la funci\u00f3n de webhook en GPSLogger.\n\nCompleta la siguiente informaci\u00f3n:\n\n- URL: `{webhook_url}`\n- Method: POST\n\nConsulta [la documentaci\u00f3n]({docs_url}) para m\u00e1s detalles." + "default": "Para enviar eventos a Home Assistant, necesitar\u00e1s configurar la funci\u00f3n de webhook en GPSLogger.\n\nCompleta la siguiente informaci\u00f3n:\n\n- URL: `{webhook_url}`\n- M\u00e9todo: POST\n\nConsulta [la documentaci\u00f3n]({docs_url}) para obtener m\u00e1s detalles." }, "step": { "user": { diff --git a/homeassistant/components/gpslogger/translations/it.json b/homeassistant/components/gpslogger/translations/it.json index 235a66c8993..babd16bcd45 100644 --- a/homeassistant/components/gpslogger/translations/it.json +++ b/homeassistant/components/gpslogger/translations/it.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "L'istanza di Home Assistant deve essere accessibile da Internet per ricevere messaggi webhook." }, "create_entry": { - "default": "Per inviare eventi a Home Assistant, dovrai configurare la funzionalit\u00e0 webhook in GPSLogger.\n\n Compila le seguenti informazioni: \n\n - URL: `{webhook_url}` \n - Metodo: POST \n\n Vedi [la documentazione]({docs_url}) per ulteriori dettagli." + "default": "Per inviare eventi a Home Assistant, dovrai configurare la funzione webhook in GPSLogger. \n\nInserisci le seguenti informazioni: \n\n - URL: `{webhook_url}`\n - Metodo: POST \n\n Consulta [la documentazione]({docs_url}) per ulteriori dettagli." }, "step": { "user": { diff --git a/homeassistant/components/gpslogger/translations/ko.json b/homeassistant/components/gpslogger/translations/ko.json index 3d32cb44736..35f0ea91aea 100644 --- a/homeassistant/components/gpslogger/translations/ko.json +++ b/homeassistant/components/gpslogger/translations/ko.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "cloud_not_connected": "Home Assistant \ud074\ub77c\uc6b0\ub4dc\uc5d0 \uc5f0\uacb0\ub418\uc5b4 \uc788\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.", "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.", "webhook_not_internet_accessible": "\uc6f9 \ud6c5 \uba54\uc2dc\uc9c0\ub97c \ubc1b\uc73c\ub824\uba74 \uc778\ud130\ub137\uc5d0\uc11c Home Assistant \uc778\uc2a4\ud134\uc2a4\uc5d0 \uc811\uadfc\ud560 \uc218 \uc788\uc5b4\uc57c \ud569\ub2c8\ub2e4." }, diff --git a/homeassistant/components/gpslogger/translations/no.json b/homeassistant/components/gpslogger/translations/no.json index a46042abe87..10a17e60ad9 100644 --- a/homeassistant/components/gpslogger/translations/no.json +++ b/homeassistant/components/gpslogger/translations/no.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "Home Assistant forekomsten din m\u00e5 v\u00e6re tilgjengelig fra internett for \u00e5 kunne motta webhook meldinger" }, "create_entry": { - "default": "For \u00e5 sende hendelser til Home Assistant, m\u00e5 du sette opp webhook-funksjonen i GPSLogger. \n\n Fyll ut f\u00f8lgende informasjon: \n\n - URL: `{webhook_url}` \n - Metode: POST \n\n Se [dokumentasjonen]({docs_url}) for ytterligere detaljer." + "default": "For \u00e5 sende hendelser til Home Assistant, m\u00e5 du sette opp webhook-funksjonen i GPSLogger. \n\n Fyll inn f\u00f8lgende informasjon: \n\n - URL: ` {webhook_url} `\n - Metode: POST \n\n Se [dokumentasjonen]( {docs_url} ) for ytterligere detaljer." }, "step": { "user": { diff --git a/homeassistant/components/gpslogger/translations/pt-BR.json b/homeassistant/components/gpslogger/translations/pt-BR.json index a3204d7e554..2c21d69b0f9 100644 --- a/homeassistant/components/gpslogger/translations/pt-BR.json +++ b/homeassistant/components/gpslogger/translations/pt-BR.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "Sua inst\u00e2ncia do Home Assistant precisa estar acess\u00edvel pela Internet para receber mensagens de webhook." }, "create_entry": { - "default": "Para enviar eventos para o Home Assistant, voc\u00ea precisar\u00e1 configurar o recurso webhook no GPSLogger. \n\n Preencha as seguintes informa\u00e7\u00f5es: \n\n - URL: ` {webhook_url} ` \n - M\u00e9todo: POST \n\n Veja [a documenta\u00e7\u00e3o] ( {docs_url} ) para mais detalhes." + "default": "Para enviar eventos para o Home Assistant, voc\u00ea precisar\u00e1 configurar o recurso webhook no GPSLogger. \n\n Preencha as seguintes informa\u00e7\u00f5es: \n\n - URL: `{webhook_url}` \n - M\u00e9todo: POST \n\n Veja [a documenta\u00e7\u00e3o] ({docs_url}) para mais detalhes." }, "step": { "user": { diff --git a/homeassistant/components/gpslogger/translations/sk.json b/homeassistant/components/gpslogger/translations/sk.json index 933f73976d2..aa84c83f4d2 100644 --- a/homeassistant/components/gpslogger/translations/sk.json +++ b/homeassistant/components/gpslogger/translations/sk.json @@ -4,6 +4,15 @@ "cloud_not_connected": "Nie je pripojen\u00e9 k Home Assistant Cloud.", "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia.", "webhook_not_internet_accessible": "Va\u0161a in\u0161tancia Home Assistant mus\u00ed by\u0165 pr\u00edstupn\u00e1 z internetu, aby ste mohli prij\u00edma\u0165 spr\u00e1vy webhooku." + }, + "create_entry": { + "default": "Ak chcete odosiela\u0165 udalosti dom\u00e1cemu asistentovi, budete musie\u0165 nastavi\u0165 funkciu webhooku v GPSLogger. \n\nVypl\u0148te nasleduj\u00face inform\u00e1cie: \n\n - URL: `{webhook_url}`\n - Met\u00f3da: POST \n\n\u010eal\u0161ie podrobnosti n\u00e1jdete v [dokument\u00e1cii]({docs_url})." + }, + "step": { + "user": { + "description": "Naozaj chcete nastavi\u0165 GPSLogger Webhook?", + "title": "Nastavte webhook GPSLogger" + } } } } \ No newline at end of file diff --git a/homeassistant/components/gree/climate.py b/homeassistant/components/gree/climate.py index 976cc31761d..8a53e3b3229 100644 --- a/homeassistant/components/gree/climate.py +++ b/homeassistant/components/gree/climate.py @@ -35,12 +35,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - ATTR_TEMPERATURE, - PRECISION_WHOLE, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, -) +from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, UnitOfTemperature from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC from homeassistant.helpers.dispatcher import async_dispatcher_connect @@ -48,6 +43,7 @@ from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import CoordinatorEntity +from .bridge import DeviceDataUpdateCoordinator from .const import ( COORDINATORS, DISPATCH_DEVICE_DISCOVERED, @@ -110,7 +106,7 @@ async def async_setup_entry( ) -class GreeClimateEntity(CoordinatorEntity, ClimateEntity): +class GreeClimateEntity(CoordinatorEntity[DeviceDataUpdateCoordinator], ClimateEntity): """Representation of a Gree HVAC device.""" _attr_precision = PRECISION_WHOLE @@ -121,7 +117,7 @@ class GreeClimateEntity(CoordinatorEntity, ClimateEntity): | ClimateEntityFeature.SWING_MODE ) - def __init__(self, coordinator): + def __init__(self, coordinator: DeviceDataUpdateCoordinator) -> None: """Initialize the Gree device.""" super().__init__(coordinator) self._name = coordinator.device.device_info.name @@ -151,7 +147,9 @@ class GreeClimateEntity(CoordinatorEntity, ClimateEntity): def temperature_unit(self) -> str: """Return the temperature units for the device.""" units = self.coordinator.device.temperature_units - return TEMP_CELSIUS if units == TemperatureUnits.C else TEMP_FAHRENHEIT + if units == TemperatureUnits.C: + return UnitOfTemperature.CELSIUS + return UnitOfTemperature.FAHRENHEIT @property def current_temperature(self) -> float: @@ -182,12 +180,16 @@ class GreeClimateEntity(CoordinatorEntity, ClimateEntity): @property def min_temp(self) -> float: """Return the minimum temperature supported by the device.""" - return TEMP_MIN if self.temperature_unit == TEMP_CELSIUS else TEMP_MIN_F + if self.temperature_unit == UnitOfTemperature.CELSIUS: + return TEMP_MIN + return TEMP_MIN_F @property def max_temp(self) -> float: """Return the maximum temperature supported by the device.""" - return TEMP_MAX if self.temperature_unit == TEMP_CELSIUS else TEMP_MAX_F + if self.temperature_unit == UnitOfTemperature.CELSIUS: + return TEMP_MAX + return TEMP_MAX_F @property def target_temperature_step(self) -> float: diff --git a/homeassistant/components/gree/translations/en.json b/homeassistant/components/gree/translations/en.json index f05becffed3..1f858b1dfb5 100644 --- a/homeassistant/components/gree/translations/en.json +++ b/homeassistant/components/gree/translations/en.json @@ -6,7 +6,7 @@ }, "step": { "confirm": { - "description": "Do you want to start set up?" + "description": "Do you want to start setup?" } } } diff --git a/homeassistant/components/gree/translations/it.json b/homeassistant/components/gree/translations/it.json index 0278fe07bfe..278f85c5cff 100644 --- a/homeassistant/components/gree/translations/it.json +++ b/homeassistant/components/gree/translations/it.json @@ -6,7 +6,7 @@ }, "step": { "confirm": { - "description": "Vuoi iniziare la configurazione?" + "description": "Vuoi avviare la configurazione?" } } } diff --git a/homeassistant/components/gree/translations/pt.json b/homeassistant/components/gree/translations/pt.json index e25888655a9..02b15520b4f 100644 --- a/homeassistant/components/gree/translations/pt.json +++ b/homeassistant/components/gree/translations/pt.json @@ -6,7 +6,7 @@ }, "step": { "confirm": { - "description": "Quer dar inicio \u00e0 configura\u00e7\u00e3o?" + "description": "Quer dar in\u00edcio \u00e0 configura\u00e7\u00e3o?" } } } diff --git a/homeassistant/components/greeneye_monitor/__init__.py b/homeassistant/components/greeneye_monitor/__init__.py index 3b9532b4b73..e4090787e65 100644 --- a/homeassistant/components/greeneye_monitor/__init__.py +++ b/homeassistant/components/greeneye_monitor/__init__.py @@ -12,10 +12,8 @@ from homeassistant.const import ( CONF_SENSORS, CONF_TEMPERATURE_UNIT, EVENT_HOMEASSISTANT_STOP, - TIME_HOURS, - TIME_MINUTES, - TIME_SECONDS, Platform, + UnitOfTime, ) from homeassistant.core import Event, HomeAssistant import homeassistant.helpers.config_validation as cv @@ -66,8 +64,8 @@ PULSE_COUNTER_SCHEMA = vol.Schema( vol.Required(CONF_NAME): cv.string, vol.Required(CONF_COUNTED_QUANTITY): cv.string, vol.Optional(CONF_COUNTED_QUANTITY_PER_PULSE, default=1.0): vol.Coerce(float), - vol.Optional(CONF_TIME_UNIT, default=TIME_SECONDS): vol.Any( - TIME_SECONDS, TIME_MINUTES, TIME_HOURS + vol.Optional(CONF_TIME_UNIT, default=UnitOfTime.SECONDS): vol.Any( + UnitOfTime.SECONDS.value, UnitOfTime.MINUTES.value, UnitOfTime.HOURS.value ), } ) @@ -91,8 +89,10 @@ MONITOR_SCHEMA = vol.Schema( vol.Length( min=8, max=8, - msg="GEM serial number must be specified as an 8-character " - "string (including leading zeroes).", + msg=( + "GEM serial number must be specified as an 8-character " + "string (including leading zeroes)." + ), ), vol.Coerce(int), ), diff --git a/homeassistant/components/greeneye_monitor/sensor.py b/homeassistant/components/greeneye_monitor/sensor.py index d6b7185a7cf..55318ad6018 100644 --- a/homeassistant/components/greeneye_monitor/sensor.py +++ b/homeassistant/components/greeneye_monitor/sensor.py @@ -10,11 +10,9 @@ from homeassistant.const import ( CONF_NAME, CONF_SENSORS, CONF_TEMPERATURE_UNIT, - ELECTRIC_POTENTIAL_VOLT, - POWER_WATT, - TIME_HOURS, - TIME_MINUTES, - TIME_SECONDS, + UnitOfElectricPotential, + UnitOfPower, + UnitOfTime, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -38,8 +36,6 @@ from .const import ( DATA_PULSES = "pulses" DATA_WATT_SECONDS = "watt_seconds" -UNIT_WATTS = POWER_WATT - COUNTER_ICON = "mdi:counter" @@ -165,7 +161,7 @@ class GEMSensor(SensorEntity): class CurrentSensor(GEMSensor): """Entity showing power usage on one channel of the monitor.""" - _attr_native_unit_of_measurement = UNIT_WATTS + _attr_native_unit_of_measurement = UnitOfPower.WATT _attr_device_class = SensorDeviceClass.POWER def __init__( @@ -235,16 +231,17 @@ class PulseCounter(GEMSensor): @property def _seconds_per_time_unit(self) -> int: """Return the number of seconds in the given display time unit.""" - if self._time_unit == TIME_SECONDS: + if self._time_unit == UnitOfTime.SECONDS: return 1 - if self._time_unit == TIME_MINUTES: + if self._time_unit == UnitOfTime.MINUTES: return 60 - if self._time_unit == TIME_HOURS: + if self._time_unit == UnitOfTime.HOURS: return 3600 # Config schema should have ensured it is one of the above values raise Exception( - f"Invalid value for time unit: {self._time_unit}. Expected one of {TIME_SECONDS}, {TIME_MINUTES}, or {TIME_HOURS}" + f"Invalid value for time unit: {self._time_unit}. Expected one of" + f" {UnitOfTime.SECONDS}, {UnitOfTime.MINUTES}, or {UnitOfTime.HOURS}" ) @property @@ -277,7 +274,7 @@ class TemperatureSensor(GEMSensor): class VoltageSensor(GEMSensor): """Entity showing voltage.""" - _attr_native_unit_of_measurement = ELECTRIC_POTENTIAL_VOLT + _attr_native_unit_of_measurement = UnitOfElectricPotential.VOLT _attr_device_class = SensorDeviceClass.VOLTAGE def __init__( diff --git a/homeassistant/components/group/__init__.py b/homeassistant/components/group/__init__.py index bdf295e35ae..0ed293ea777 100644 --- a/homeassistant/components/group/__init__.py +++ b/homeassistant/components/group/__init__.py @@ -767,7 +767,7 @@ class Group(Entity): self._see_state(state) def _see_state(self, new_state: State) -> None: - """Keep track of the the state.""" + """Keep track of the state.""" entity_id = new_state.entity_id domain = new_state.domain state = new_state.state diff --git a/homeassistant/components/group/translations/sk.json b/homeassistant/components/group/translations/sk.json index 5bca7cea930..7b689aeab28 100644 --- a/homeassistant/components/group/translations/sk.json +++ b/homeassistant/components/group/translations/sk.json @@ -8,6 +8,7 @@ "hide_members": "Skry\u0165 \u010dlenov", "name": "N\u00e1zov" }, + "description": "Ak je povolen\u00e9 \u201ev\u0161etky entity\u201c, stav skupiny je zapnut\u00fd, iba ak s\u00fa zapnut\u00e9 v\u0161etci \u010dlenovia. Ak je mo\u017enos\u0165 \u201ev\u0161etky entity\u201c vypnut\u00e1, stav skupiny je zapnut\u00fd, ak je zapnut\u00fd niektor\u00fd \u010dlen.", "title": "Prida\u0165 skupinu" }, "cover": { @@ -59,8 +60,10 @@ "title": "Prida\u0165 skupinu" }, "user": { + "description": "Skupiny v\u00e1m umo\u017e\u0148uj\u00fa vytvori\u0165 nov\u00fa entitu, ktor\u00e1 predstavuje viacero ent\u00edt rovnak\u00e9ho typu.", "menu_options": { "binary_sensor": "Skupina sn\u00edma\u010d", + "cover": "Skupina roliet", "fan": "Skupina ventil\u00e1tor", "light": "Skupina osvetlenie", "lock": "Skupina z\u00e1mok", @@ -78,7 +81,8 @@ "all": "V\u0161etky entity", "entities": "\u010clenovia", "hide_members": "Skry\u0165 \u010dlenov" - } + }, + "description": "Ak je povolen\u00e9 \u201ev\u0161etky entity\u201c, stav skupiny je zapnut\u00fd, iba ak s\u00fa zapnut\u00e9 v\u0161etci \u010dlenovia. Ak je mo\u017enos\u0165 \u201ev\u0161etky entity\u201c vypnut\u00e1, stav skupiny je zapnut\u00fd, ak je zapnut\u00fd niektor\u00fd \u010dlen." }, "cover": { "data": { @@ -97,7 +101,8 @@ "all": "V\u0161etky entity", "entities": "\u010clenovia", "hide_members": "Skry\u0165 \u010dlenov" - } + }, + "description": "Ak je povolen\u00e9 \u201ev\u0161etky entity\u201c, stav skupiny je zapnut\u00fd, iba ak s\u00fa zapnut\u00e9 v\u0161etci \u010dlenovia. Ak je mo\u017enos\u0165 \u201ev\u0161etky entity\u201c vypnut\u00e1, stav skupiny je zapnut\u00fd, ak je zapnut\u00fd niektor\u00fd \u010dlen." }, "lock": { "data": { @@ -116,7 +121,8 @@ "all": "V\u0161etky entity", "entities": "\u010clenovia", "hide_members": "Skry\u0165 \u010dlenov" - } + }, + "description": "Ak je povolen\u00e9 \u201ev\u0161etky entity\u201c, stav skupiny je zapnut\u00fd, iba ak s\u00fa zapnut\u00e9 v\u0161etci \u010dlenovia. Ak je mo\u017enos\u0165 \u201ev\u0161etky entity\u201c vypnut\u00e1, stav skupiny je zapnut\u00fd, ak je zapnut\u00fd niektor\u00fd \u010dlen." } } }, diff --git a/homeassistant/components/growatt_server/config_flow.py b/homeassistant/components/growatt_server/config_flow.py index 11f082f1eab..a4dcd25173f 100644 --- a/homeassistant/components/growatt_server/config_flow.py +++ b/homeassistant/components/growatt_server/config_flow.py @@ -22,7 +22,7 @@ class GrowattServerConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): def __init__(self): """Initialise growatt server flow.""" - self.api = growattServer.GrowattApi() + self.api = None self.user_id = None self.data = {} @@ -46,6 +46,10 @@ class GrowattServerConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): if not user_input: return self._async_show_user_form() + # Initialise the library with the username & a random id each time it is started + self.api = growattServer.GrowattApi( + add_random_user_id=True, agent_identifier=user_input[CONF_USERNAME] + ) self.api.server_url = user_input[CONF_URL] login_response = await self.hass.async_add_executor_job( self.api.login, user_input[CONF_USERNAME], user_input[CONF_PASSWORD] diff --git a/homeassistant/components/growatt_server/sensor.py b/homeassistant/components/growatt_server/sensor.py index b22664897fd..1dea3b3510c 100644 --- a/homeassistant/components/growatt_server/sensor.py +++ b/homeassistant/components/growatt_server/sensor.py @@ -80,12 +80,8 @@ async def async_setup_entry( config[CONF_URL] = url hass.config_entries.async_update_entry(config_entry, data=config) - # Initialise the library with a random user id each time it is started, - # also extend the library's default identifier to include 'home-assistant' - api = growattServer.GrowattApi( - add_random_user_id=True, - agent_identifier=f"{growattServer.GrowattApi.agent_identifier} - home-assistant", - ) + # Initialise the library with the username & a random id each time it is started + api = growattServer.GrowattApi(add_random_user_id=True, agent_identifier=username) api.server_url = url devices, plant_id = await hass.async_add_executor_job(get_device_list, api, config) @@ -290,7 +286,10 @@ class GrowattData: and api_value is not None ): _LOGGER.debug( - "%s - Drop threshold specified (%s), checking for drop... API Value: %s, Previous Value: %s", + ( + "%s - Drop threshold specified (%s), checking for drop... API" + " Value: %s, Previous Value: %s" + ), entity_description.name, entity_description.previous_value_drop_threshold, api_value, @@ -304,8 +303,11 @@ class GrowattData: # however if the value is low e.g. 0.2 and drops by 0.1 it classes as a reset. if -(entity_description.previous_value_drop_threshold) <= diff < 0: _LOGGER.debug( - "Diff is negative, but only by a small amount therefore not a nightly reset, " - "using previous value (%s) instead of api value (%s)", + ( + "Diff is negative, but only by a small amount therefore not a" + " nightly reset, using previous value (%s) instead of api value" + " (%s)" + ), previous_value, api_value, ) @@ -329,7 +331,10 @@ class GrowattData: # value of the entity from the recorder if entity_description.never_resets and api_value == 0 and previous_value: _LOGGER.debug( - "API value is 0, but this value should never reset, returning previous value (%s) instead", + ( + "API value is 0, but this value should never reset, returning" + " previous value (%s) instead" + ), previous_value, ) return_value = previous_value diff --git a/homeassistant/components/growatt_server/sensor_types/inverter.py b/homeassistant/components/growatt_server/sensor_types/inverter.py index eb9315c0777..746e4880cef 100644 --- a/homeassistant/components/growatt_server/sensor_types/inverter.py +++ b/homeassistant/components/growatt_server/sensor_types/inverter.py @@ -3,12 +3,12 @@ from __future__ import annotations from homeassistant.components.sensor import SensorDeviceClass, SensorStateClass from homeassistant.const import ( - ELECTRIC_CURRENT_AMPERE, - ELECTRIC_POTENTIAL_VOLT, - ENERGY_KILO_WATT_HOUR, - FREQUENCY_HERTZ, - POWER_WATT, - TEMP_CELSIUS, + UnitOfElectricCurrent, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfFrequency, + UnitOfPower, + UnitOfTemperature, ) from .sensor_entity_description import GrowattSensorEntityDescription @@ -18,7 +18,7 @@ INVERTER_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="inverter_energy_today", name="Energy today", api_key="powerToday", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, precision=1, ), @@ -26,7 +26,7 @@ INVERTER_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="inverter_energy_total", name="Lifetime energy output", api_key="powerTotal", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, precision=1, state_class=SensorStateClass.TOTAL, @@ -35,7 +35,7 @@ INVERTER_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="inverter_voltage_input_1", name="Input 1 voltage", api_key="vpv1", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, precision=2, ), @@ -43,7 +43,7 @@ INVERTER_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="inverter_amperage_input_1", name="Input 1 Amperage", api_key="ipv1", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, device_class=SensorDeviceClass.CURRENT, precision=1, ), @@ -51,7 +51,7 @@ INVERTER_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="inverter_wattage_input_1", name="Input 1 Wattage", api_key="ppv1", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, precision=1, ), @@ -59,7 +59,7 @@ INVERTER_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="inverter_voltage_input_2", name="Input 2 voltage", api_key="vpv2", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, precision=1, ), @@ -67,7 +67,7 @@ INVERTER_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="inverter_amperage_input_2", name="Input 2 Amperage", api_key="ipv2", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, device_class=SensorDeviceClass.CURRENT, precision=1, ), @@ -75,7 +75,7 @@ INVERTER_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="inverter_wattage_input_2", name="Input 2 Wattage", api_key="ppv2", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, precision=1, ), @@ -83,7 +83,7 @@ INVERTER_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="inverter_voltage_input_3", name="Input 3 voltage", api_key="vpv3", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, precision=1, ), @@ -91,7 +91,7 @@ INVERTER_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="inverter_amperage_input_3", name="Input 3 Amperage", api_key="ipv3", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, device_class=SensorDeviceClass.CURRENT, precision=1, ), @@ -99,7 +99,7 @@ INVERTER_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="inverter_wattage_input_3", name="Input 3 Wattage", api_key="ppv3", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, precision=1, ), @@ -107,7 +107,7 @@ INVERTER_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="inverter_internal_wattage", name="Internal wattage", api_key="ppv", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, precision=1, ), @@ -115,7 +115,7 @@ INVERTER_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="inverter_reactive_voltage", name="Reactive voltage", api_key="vacr", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, precision=1, ), @@ -123,7 +123,7 @@ INVERTER_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="inverter_inverter_reactive_amperage", name="Reactive amperage", api_key="iacr", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, device_class=SensorDeviceClass.CURRENT, precision=1, ), @@ -131,14 +131,15 @@ INVERTER_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="inverter_frequency", name="AC frequency", api_key="fac", - native_unit_of_measurement=FREQUENCY_HERTZ, + native_unit_of_measurement=UnitOfFrequency.HERTZ, + device_class=SensorDeviceClass.FREQUENCY, precision=1, ), GrowattSensorEntityDescription( key="inverter_current_wattage", name="Output power", api_key="pac", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, precision=1, ), @@ -146,7 +147,7 @@ INVERTER_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="inverter_current_reactive_wattage", name="Reactive wattage", api_key="pacr", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, precision=1, ), @@ -154,7 +155,7 @@ INVERTER_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="inverter_ipm_temperature", name="Intelligent Power Management temperature", api_key="ipmTemperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, precision=1, ), @@ -162,7 +163,7 @@ INVERTER_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="inverter_temperature", name="Temperature", api_key="temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, precision=1, ), diff --git a/homeassistant/components/growatt_server/sensor_types/mix.py b/homeassistant/components/growatt_server/sensor_types/mix.py index cfb47e81519..76d37f4d193 100644 --- a/homeassistant/components/growatt_server/sensor_types/mix.py +++ b/homeassistant/components/growatt_server/sensor_types/mix.py @@ -3,11 +3,10 @@ from __future__ import annotations from homeassistant.components.sensor import SensorDeviceClass, SensorStateClass from homeassistant.const import ( - ELECTRIC_POTENTIAL_VOLT, - ENERGY_KILO_WATT_HOUR, PERCENTAGE, - POWER_KILO_WATT, - POWER_WATT, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfPower, ) from .sensor_entity_description import GrowattSensorEntityDescription @@ -25,14 +24,14 @@ MIX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="mix_battery_charge_today", name="Battery charged today", api_key="eBatChargeToday", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, ), GrowattSensorEntityDescription( key="mix_battery_charge_lifetime", name="Lifetime battery charged", api_key="eBatChargeTotal", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL, ), @@ -40,14 +39,14 @@ MIX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="mix_battery_discharge_today", name="Battery discharged today", api_key="eBatDisChargeToday", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, ), GrowattSensorEntityDescription( key="mix_battery_discharge_lifetime", name="Lifetime battery discharged", api_key="eBatDisChargeTotal", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL, ), @@ -55,14 +54,14 @@ MIX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="mix_solar_generation_today", name="Solar energy today", api_key="epvToday", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, ), GrowattSensorEntityDescription( key="mix_solar_generation_lifetime", name="Lifetime solar energy", api_key="epvTotal", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL, ), @@ -70,28 +69,28 @@ MIX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="mix_battery_discharge_w", name="Battery discharging W", api_key="pDischarge1", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, ), GrowattSensorEntityDescription( key="mix_battery_voltage", name="Battery voltage", api_key="vbat", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, ), GrowattSensorEntityDescription( key="mix_pv1_voltage", name="PV1 voltage", api_key="vpv1", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, ), GrowattSensorEntityDescription( key="mix_pv2_voltage", name="PV2 voltage", api_key="vpv2", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, ), # Values from 'mix_totals' API call @@ -99,14 +98,14 @@ MIX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="mix_load_consumption_today", name="Load consumption today", api_key="elocalLoadToday", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, ), GrowattSensorEntityDescription( key="mix_load_consumption_lifetime", name="Lifetime load consumption", api_key="elocalLoadTotal", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL, ), @@ -114,14 +113,14 @@ MIX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="mix_export_to_grid_today", name="Export to grid today", api_key="etoGridToday", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, ), GrowattSensorEntityDescription( key="mix_export_to_grid_lifetime", name="Lifetime export to grid", api_key="etogridTotal", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL, ), @@ -130,63 +129,63 @@ MIX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="mix_battery_charge", name="Battery charging", api_key="chargePower", - native_unit_of_measurement=POWER_KILO_WATT, + native_unit_of_measurement=UnitOfPower.KILO_WATT, device_class=SensorDeviceClass.POWER, ), GrowattSensorEntityDescription( key="mix_load_consumption", name="Load consumption", api_key="pLocalLoad", - native_unit_of_measurement=POWER_KILO_WATT, + native_unit_of_measurement=UnitOfPower.KILO_WATT, device_class=SensorDeviceClass.POWER, ), GrowattSensorEntityDescription( key="mix_wattage_pv_1", name="PV1 Wattage", api_key="pPv1", - native_unit_of_measurement=POWER_KILO_WATT, + native_unit_of_measurement=UnitOfPower.KILO_WATT, device_class=SensorDeviceClass.POWER, ), GrowattSensorEntityDescription( key="mix_wattage_pv_2", name="PV2 Wattage", api_key="pPv2", - native_unit_of_measurement=POWER_KILO_WATT, + native_unit_of_measurement=UnitOfPower.KILO_WATT, device_class=SensorDeviceClass.POWER, ), GrowattSensorEntityDescription( key="mix_wattage_pv_all", name="All PV Wattage", api_key="ppv", - native_unit_of_measurement=POWER_KILO_WATT, + native_unit_of_measurement=UnitOfPower.KILO_WATT, device_class=SensorDeviceClass.POWER, ), GrowattSensorEntityDescription( key="mix_export_to_grid", name="Export to grid", api_key="pactogrid", - native_unit_of_measurement=POWER_KILO_WATT, + native_unit_of_measurement=UnitOfPower.KILO_WATT, device_class=SensorDeviceClass.POWER, ), GrowattSensorEntityDescription( key="mix_import_from_grid", name="Import from grid", api_key="pactouser", - native_unit_of_measurement=POWER_KILO_WATT, + native_unit_of_measurement=UnitOfPower.KILO_WATT, device_class=SensorDeviceClass.POWER, ), GrowattSensorEntityDescription( key="mix_battery_discharge_kw", name="Battery discharging kW", api_key="pdisCharge1", - native_unit_of_measurement=POWER_KILO_WATT, + native_unit_of_measurement=UnitOfPower.KILO_WATT, device_class=SensorDeviceClass.POWER, ), GrowattSensorEntityDescription( key="mix_grid_voltage", name="Grid voltage", api_key="vAc1", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, ), # Values from 'mix_detail' API call @@ -194,35 +193,35 @@ MIX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="mix_system_production_today", name="System production today (self-consumption + export)", api_key="eCharge", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, ), GrowattSensorEntityDescription( key="mix_load_consumption_solar_today", name="Load consumption today (solar)", api_key="eChargeToday", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, ), GrowattSensorEntityDescription( key="mix_self_consumption_today", name="Self consumption today (solar + battery)", api_key="eChargeToday1", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, ), GrowattSensorEntityDescription( key="mix_load_consumption_battery_today", name="Load consumption today (battery)", api_key="echarge1", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, ), GrowattSensorEntityDescription( key="mix_import_from_grid_today", name="Import from grid today (load)", api_key="etouser", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, ), # This sensor is manually created using the most recent X-Axis value from the chartData @@ -237,7 +236,7 @@ MIX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="mix_import_from_grid_today_combined", name="Import from grid today (load + charging)", api_key="etouser_combined", # This id is not present in the raw API data, it is added by the sensor - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, previous_value_drop_threshold=0.2, diff --git a/homeassistant/components/growatt_server/sensor_types/storage.py b/homeassistant/components/growatt_server/sensor_types/storage.py index 11e64274686..d1305aa879d 100644 --- a/homeassistant/components/growatt_server/sensor_types/storage.py +++ b/homeassistant/components/growatt_server/sensor_types/storage.py @@ -3,12 +3,12 @@ from __future__ import annotations from homeassistant.components.sensor import SensorDeviceClass, SensorStateClass from homeassistant.const import ( - ELECTRIC_CURRENT_AMPERE, - ELECTRIC_POTENTIAL_VOLT, - ENERGY_KILO_WATT_HOUR, - FREQUENCY_HERTZ, PERCENTAGE, - POWER_WATT, + UnitOfElectricCurrent, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfFrequency, + UnitOfPower, ) from .sensor_entity_description import GrowattSensorEntityDescription @@ -18,14 +18,14 @@ STORAGE_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="storage_storage_production_today", name="Storage production today", api_key="eBatDisChargeToday", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, ), GrowattSensorEntityDescription( key="storage_storage_production_lifetime", name="Lifetime Storage production", api_key="eBatDisChargeTotal", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL, ), @@ -33,21 +33,21 @@ STORAGE_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="storage_grid_discharge_today", name="Grid discharged today", api_key="eacDisChargeToday", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, ), GrowattSensorEntityDescription( key="storage_load_consumption_today", name="Load consumption today", api_key="eopDischrToday", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, ), GrowattSensorEntityDescription( key="storage_load_consumption_lifetime", name="Lifetime load consumption", api_key="eopDischrTotal", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL, ), @@ -55,14 +55,14 @@ STORAGE_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="storage_grid_charged_today", name="Grid charged today", api_key="eacChargeToday", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, ), GrowattSensorEntityDescription( key="storage_charge_storage_lifetime", name="Lifetime storaged charged", api_key="eChargeTotal", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL, ), @@ -70,7 +70,7 @@ STORAGE_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="storage_solar_production", name="Solar power production", api_key="ppv", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, ), GrowattSensorEntityDescription( @@ -84,7 +84,7 @@ STORAGE_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="storage_power_flow", name="Storage charging/ discharging(-ve)", api_key="pCharge", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, ), GrowattSensorEntityDescription( @@ -97,28 +97,28 @@ STORAGE_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="storage_charge_today", name="Charge today", api_key="eChargeToday", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, ), GrowattSensorEntityDescription( key="storage_import_from_grid", name="Import from grid", api_key="pAcInPut", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, ), GrowattSensorEntityDescription( key="storage_import_from_grid_today", name="Import from grid today", api_key="eToUserToday", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, ), GrowattSensorEntityDescription( key="storage_import_from_grid_total", name="Import from grid total", api_key="eToUserTotal", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL, ), @@ -126,14 +126,14 @@ STORAGE_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="storage_load_consumption", name="Load consumption", api_key="outPutPower", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, ), GrowattSensorEntityDescription( key="storage_grid_voltage", name="AC input voltage", api_key="vGrid", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, precision=2, ), @@ -141,7 +141,7 @@ STORAGE_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="storage_pv_charging_voltage", name="PV charging voltage", api_key="vpv", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, precision=2, ), @@ -149,14 +149,15 @@ STORAGE_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="storage_ac_input_frequency_out", name="AC input frequency", api_key="freqOutPut", - native_unit_of_measurement=FREQUENCY_HERTZ, + native_unit_of_measurement=UnitOfFrequency.HERTZ, + device_class=SensorDeviceClass.FREQUENCY, precision=2, ), GrowattSensorEntityDescription( key="storage_output_voltage", name="Output voltage", api_key="outPutVolt", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, precision=2, ), @@ -164,14 +165,15 @@ STORAGE_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="storage_ac_output_frequency", name="Ac output frequency", api_key="freqGrid", - native_unit_of_measurement=FREQUENCY_HERTZ, + native_unit_of_measurement=UnitOfFrequency.HERTZ, + device_class=SensorDeviceClass.FREQUENCY, precision=2, ), GrowattSensorEntityDescription( key="storage_current_PV", name="Solar charge current", api_key="iAcCharge", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, device_class=SensorDeviceClass.CURRENT, precision=2, ), @@ -179,7 +181,7 @@ STORAGE_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="storage_current_1", name="Solar current to storage", api_key="iChargePV1", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, device_class=SensorDeviceClass.CURRENT, precision=2, ), @@ -187,7 +189,7 @@ STORAGE_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="storage_grid_amperage_input", name="Grid charge current", api_key="chgCurr", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, device_class=SensorDeviceClass.CURRENT, precision=2, ), @@ -195,7 +197,7 @@ STORAGE_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="storage_grid_out_current", name="Grid out current", api_key="outPutCurrent", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, device_class=SensorDeviceClass.CURRENT, precision=2, ), @@ -203,7 +205,7 @@ STORAGE_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="storage_battery_voltage", name="Battery voltage", api_key="vBat", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, precision=2, ), diff --git a/homeassistant/components/growatt_server/sensor_types/tlx.py b/homeassistant/components/growatt_server/sensor_types/tlx.py index 2a6e76a55a7..4703c39a8f9 100644 --- a/homeassistant/components/growatt_server/sensor_types/tlx.py +++ b/homeassistant/components/growatt_server/sensor_types/tlx.py @@ -7,13 +7,13 @@ from __future__ import annotations from homeassistant.components.sensor import SensorDeviceClass, SensorStateClass from homeassistant.const import ( - ELECTRIC_CURRENT_AMPERE, - ELECTRIC_POTENTIAL_VOLT, - ENERGY_KILO_WATT_HOUR, - FREQUENCY_HERTZ, PERCENTAGE, - POWER_WATT, - TEMP_CELSIUS, + UnitOfElectricCurrent, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfFrequency, + UnitOfPower, + UnitOfTemperature, ) from .sensor_entity_description import GrowattSensorEntityDescription @@ -23,7 +23,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_energy_today", name="Energy today", api_key="eacToday", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, precision=1, @@ -32,7 +32,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_energy_total", name="Lifetime energy output", api_key="eacTotal", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, precision=1, @@ -42,7 +42,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_energy_total_input_1", name="Lifetime total energy input 1", api_key="epv1Total", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, precision=1, @@ -52,7 +52,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_energy_today_input_1", name="Energy Today Input 1", api_key="epv1Today", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, precision=1, @@ -61,7 +61,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_voltage_input_1", name="Input 1 voltage", api_key="vpv1", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, precision=1, ), @@ -69,7 +69,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_amperage_input_1", name="Input 1 Amperage", api_key="ipv1", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, device_class=SensorDeviceClass.CURRENT, precision=1, ), @@ -77,7 +77,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_wattage_input_1", name="Input 1 Wattage", api_key="ppv1", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, precision=1, ), @@ -85,7 +85,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_energy_total_input_2", name="Lifetime total energy input 2", api_key="epv2Total", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, precision=1, @@ -95,7 +95,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_energy_today_input_2", name="Energy Today Input 2", api_key="epv2Today", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, precision=1, @@ -104,7 +104,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_voltage_input_2", name="Input 2 voltage", api_key="vpv2", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, precision=1, ), @@ -112,7 +112,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_amperage_input_2", name="Input 2 Amperage", api_key="ipv2", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, device_class=SensorDeviceClass.CURRENT, precision=1, ), @@ -120,7 +120,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_wattage_input_2", name="Input 2 Wattage", api_key="ppv2", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, precision=1, ), @@ -128,7 +128,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_energy_total_input_3", name="Lifetime total energy input 3", api_key="epv3Total", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, precision=1, @@ -138,7 +138,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_energy_today_input_3", name="Energy Today Input 3", api_key="epv3Today", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, precision=1, @@ -147,7 +147,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_voltage_input_3", name="Input 3 voltage", api_key="vpv3", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, precision=1, ), @@ -155,7 +155,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_amperage_input_3", name="Input 3 Amperage", api_key="ipv3", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, device_class=SensorDeviceClass.CURRENT, precision=1, ), @@ -163,7 +163,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_wattage_input_3", name="Input 3 Wattage", api_key="ppv3", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, precision=1, ), @@ -171,7 +171,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_energy_total_input_4", name="Lifetime total energy input 4", api_key="epv4Total", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, precision=1, @@ -181,7 +181,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_energy_today_input_4", name="Energy Today Input 4", api_key="epv4Today", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, precision=1, @@ -190,7 +190,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_voltage_input_4", name="Input 4 voltage", api_key="vpv4", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, precision=1, ), @@ -198,7 +198,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_amperage_input_4", name="Input 4 Amperage", api_key="ipv4", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, device_class=SensorDeviceClass.CURRENT, precision=1, ), @@ -206,7 +206,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_wattage_input_4", name="Input 4 Wattage", api_key="ppv4", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, precision=1, ), @@ -214,7 +214,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_solar_generation_total", name="Lifetime total solar energy", api_key="epvTotal", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, never_resets=True, @@ -223,7 +223,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_internal_wattage", name="Internal wattage", api_key="ppv", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, precision=1, ), @@ -231,7 +231,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_reactive_voltage", name="Reactive voltage", api_key="vacrs", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, precision=1, ), @@ -239,14 +239,15 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_frequency", name="AC frequency", api_key="fac", - native_unit_of_measurement=FREQUENCY_HERTZ, + native_unit_of_measurement=UnitOfFrequency.HERTZ, + device_class=SensorDeviceClass.FREQUENCY, precision=1, ), GrowattSensorEntityDescription( key="tlx_current_wattage", name="Output power", api_key="pac", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, precision=1, ), @@ -254,7 +255,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_temperature_1", name="Temperature 1", api_key="temp1", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, precision=1, ), @@ -262,7 +263,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_temperature_2", name="Temperature 2", api_key="temp2", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, precision=1, ), @@ -270,7 +271,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_temperature_3", name="Temperature 3", api_key="temp3", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, precision=1, ), @@ -278,7 +279,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_temperature_4", name="Temperature 4", api_key="temp4", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, precision=1, ), @@ -286,7 +287,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_temperature_5", name="Temperature 5", api_key="temp5", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, precision=1, ), @@ -294,7 +295,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_all_batteries_discharge_today", name="All batteries discharged today", api_key="edischargeToday", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), @@ -302,7 +303,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_all_batteries_discharge_total", name="Lifetime total all batteries discharged", api_key="edischargeTotal", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, never_resets=True, @@ -311,14 +312,14 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_battery_1_discharge_w", name="Battery 1 discharging W", api_key="bdc1DischargePower", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, ), GrowattSensorEntityDescription( key="tlx_battery_1_discharge_total", name="Lifetime total battery 1 discharged", api_key="bdc1DischargeTotal", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, never_resets=True, @@ -327,14 +328,14 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_battery_2_discharge_w", name="Battery 2 discharging W", api_key="bdc1DischargePower", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, ), GrowattSensorEntityDescription( key="tlx_battery_2_discharge_total", name="Lifetime total battery 2 discharged", api_key="bdc1DischargeTotal", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, never_resets=True, @@ -343,7 +344,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_all_batteries_charge_today", name="All batteries charged today", api_key="echargeToday", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), @@ -351,7 +352,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_all_batteries_charge_total", name="Lifetime total all batteries charged", api_key="echargeTotal", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, never_resets=True, @@ -360,14 +361,14 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_battery_1_charge_w", name="Battery 1 charging W", api_key="bdc1ChargePower", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, ), GrowattSensorEntityDescription( key="tlx_battery_1_charge_total", name="Lifetime total battery 1 charged", api_key="bdc1ChargeTotal", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, never_resets=True, @@ -376,14 +377,14 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_battery_2_charge_w", name="Battery 2 charging W", api_key="bdc1ChargePower", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, ), GrowattSensorEntityDescription( key="tlx_battery_2_charge_total", name="Lifetime total battery 2 charged", api_key="bdc1ChargeTotal", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, never_resets=True, @@ -392,7 +393,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_export_to_grid_today", name="Export to grid today", api_key="etoGridToday", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), @@ -400,7 +401,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_export_to_grid_total", name="Lifetime total export to grid", api_key="etoGridTotal", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, never_resets=True, @@ -409,7 +410,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="tlx_load_consumption_today", name="Load consumption today", api_key="elocalLoadToday", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), @@ -417,7 +418,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="mix_load_consumption_total", name="Lifetime total load consumption", api_key="elocalLoadTotal", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, never_resets=True, diff --git a/homeassistant/components/growatt_server/sensor_types/total.py b/homeassistant/components/growatt_server/sensor_types/total.py index f3d48cf8027..2056443af3d 100644 --- a/homeassistant/components/growatt_server/sensor_types/total.py +++ b/homeassistant/components/growatt_server/sensor_types/total.py @@ -2,7 +2,7 @@ from __future__ import annotations from homeassistant.components.sensor import SensorDeviceClass, SensorStateClass -from homeassistant.const import ENERGY_KILO_WATT_HOUR, POWER_WATT +from homeassistant.const import UnitOfEnergy, UnitOfPower from .sensor_entity_description import GrowattSensorEntityDescription @@ -23,21 +23,21 @@ TOTAL_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="total_energy_today", name="Energy Today", api_key="todayEnergy", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, ), GrowattSensorEntityDescription( key="total_output_power", name="Output Power", api_key="invTodayPpv", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, ), GrowattSensorEntityDescription( key="total_energy_output", name="Lifetime energy output", api_key="totalEnergy", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL, ), @@ -45,7 +45,7 @@ TOTAL_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = ( key="total_maximum_output", name="Maximum power", api_key="nominalPower", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, ), ) diff --git a/homeassistant/components/growatt_server/translations/ko.json b/homeassistant/components/growatt_server/translations/ko.json index 676542812e7..9a7a8777ec3 100644 --- a/homeassistant/components/growatt_server/translations/ko.json +++ b/homeassistant/components/growatt_server/translations/ko.json @@ -1,3 +1,18 @@ { + "config": { + "error": { + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "name": "\uc774\ub984", + "password": "\ube44\ubc00\ubc88\ud638", + "url": "URL \uc8fc\uc18c", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + } + } + } + }, "title": "Growatt \uc11c\ubc84" } \ No newline at end of file diff --git a/homeassistant/components/growatt_server/translations/no.json b/homeassistant/components/growatt_server/translations/no.json index 8977a7e86a3..8d8b985d025 100644 --- a/homeassistant/components/growatt_server/translations/no.json +++ b/homeassistant/components/growatt_server/translations/no.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "no_plants": "Ingen planter er funnet p\u00e5 denne kontoen" + "no_plants": "Ingen anlegg funnet p\u00e5 denne kontoen" }, "error": { "invalid_auth": "Ugyldig godkjenning" @@ -9,9 +9,9 @@ "step": { "plant": { "data": { - "plant_id": "Plante" + "plant_id": "Anlegg" }, - "title": "Velg din plante" + "title": "Velg ditt anlegg" }, "user": { "data": { diff --git a/homeassistant/components/growatt_server/translations/sk.json b/homeassistant/components/growatt_server/translations/sk.json index 256fec69c1a..3bb4dfa2c16 100644 --- a/homeassistant/components/growatt_server/translations/sk.json +++ b/homeassistant/components/growatt_server/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "no_plants": "Na tomto \u00fa\u010dte neboli n\u00e1jden\u00e9 \u017eiadne rastliny" + }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, @@ -7,7 +10,8 @@ "plant": { "data": { "plant_id": "Rastlina" - } + }, + "title": "Vyberte svoju rastlinu" }, "user": { "data": { diff --git a/homeassistant/components/gtfs/sensor.py b/homeassistant/components/gtfs/sensor.py index 9b733d41502..2bce3cb21c1 100644 --- a/homeassistant/components/gtfs/sensor.py +++ b/homeassistant/components/gtfs/sensor.py @@ -427,16 +427,14 @@ def get_next_departure( if item["dest_arrival_time"] < item["origin_depart_time"]: dest_arrival += datetime.timedelta(days=1) dest_arrival_time = ( - f"{dest_arrival.strftime(dt_util.DATE_STR_FORMAT)} " - f"{item['dest_arrival_time']}" + f"{dest_arrival.strftime(dt_util.DATE_STR_FORMAT)} {item['dest_arrival_time']}" ) dest_depart = dest_arrival if item["dest_depart_time"] < item["dest_arrival_time"]: dest_depart += datetime.timedelta(days=1) dest_depart_time = ( - f"{dest_depart.strftime(dt_util.DATE_STR_FORMAT)} " - f"{item['dest_depart_time']}" + f"{dest_depart.strftime(dt_util.DATE_STR_FORMAT)} {item['dest_depart_time']}" ) depart_time = dt_util.parse_datetime(origin_depart_time) @@ -638,9 +636,11 @@ class GTFSDepartureSensor(SensorEntity): self._agency = self._pygtfs.agencies_by_id(self._route.agency_id)[0] except IndexError: _LOGGER.warning( - "Agency ID '%s' was not found in agency table, " - "you may want to update the routes database table " - "to fix this missing reference", + ( + "Agency ID '%s' was not found in agency table, " + "you may want to update the routes database table " + "to fix this missing reference" + ), self._route.agency_id, ) self._agency = False diff --git a/homeassistant/components/guardian/sensor.py b/homeassistant/components/guardian/sensor.py index 05de437b10a..4849666736e 100644 --- a/homeassistant/components/guardian/sensor.py +++ b/homeassistant/components/guardian/sensor.py @@ -10,7 +10,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ELECTRIC_POTENTIAL_VOLT, TEMP_FAHRENHEIT, TIME_MINUTES +from homeassistant.const import UnitOfElectricPotential, UnitOfTemperature, UnitOfTime from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import EntityCategory @@ -48,13 +48,13 @@ PAIRED_SENSOR_DESCRIPTIONS = ( name="Battery", device_class=SensorDeviceClass.VOLTAGE, entity_category=EntityCategory.DIAGNOSTIC, - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, ), SensorEntityDescription( key=SENSOR_KIND_TEMPERATURE, name="Temperature", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_FAHRENHEIT, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, state_class=SensorStateClass.MEASUREMENT, ), ) @@ -63,7 +63,7 @@ VALVE_CONTROLLER_DESCRIPTIONS = ( key=SENSOR_KIND_TEMPERATURE, name="Temperature", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_FAHRENHEIT, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, state_class=SensorStateClass.MEASUREMENT, api_category=API_SYSTEM_ONBOARD_SENSOR_STATUS, ), @@ -72,7 +72,7 @@ VALVE_CONTROLLER_DESCRIPTIONS = ( name="Uptime", icon="mdi:timer", entity_category=EntityCategory.DIAGNOSTIC, - native_unit_of_measurement=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, api_category=API_SYSTEM_DIAGNOSTICS, ), ) diff --git a/homeassistant/components/guardian/translations/pt.json b/homeassistant/components/guardian/translations/pt.json index 04a5519b895..7ca84302d8f 100644 --- a/homeassistant/components/guardian/translations/pt.json +++ b/homeassistant/components/guardian/translations/pt.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", "already_in_progress": "O processo de configura\u00e7\u00e3o j\u00e1 est\u00e1 a decorrer", - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "user": { diff --git a/homeassistant/components/guardian/translations/sk.json b/homeassistant/components/guardian/translations/sk.json index 1e35b169d86..cb2bc562b8c 100644 --- a/homeassistant/components/guardian/translations/sk.json +++ b/homeassistant/components/guardian/translations/sk.json @@ -6,11 +6,15 @@ "cannot_connect": "Nepodarilo sa pripoji\u0165" }, "step": { + "discovery_confirm": { + "description": "Chcete nastavi\u0165 toto zariadenie Guardian?" + }, "user": { "data": { "ip_address": "IP adresa", "port": "Port" - } + }, + "description": "Nakonfigurujte miestne zariadenie Elexa Guardian." } } }, diff --git a/homeassistant/components/habitica/sensor.py b/homeassistant/components/habitica/sensor.py index 5a7109df0b9..e085167301f 100644 --- a/homeassistant/components/habitica/sensor.py +++ b/homeassistant/components/habitica/sensor.py @@ -114,8 +114,10 @@ class HabitipyData: except ClientResponseError as error: if error.status == HTTPStatus.TOO_MANY_REQUESTS: _LOGGER.warning( - "Sensor data update for %s has too many API requests;" - " Skipping the update", + ( + "Sensor data update for %s has too many API requests;" + " Skipping the update" + ), DOMAIN, ) else: @@ -131,8 +133,10 @@ class HabitipyData: except ClientResponseError as error: if error.status == HTTPStatus.TOO_MANY_REQUESTS: _LOGGER.warning( - "Sensor data update for %s has too many API requests;" - " Skipping the update", + ( + "Sensor data update for %s has too many API requests;" + " Skipping the update" + ), DOMAIN, ) else: diff --git a/homeassistant/components/habitica/translations/de.json b/homeassistant/components/habitica/translations/de.json index 6eac60a55d3..cab6b3250ec 100644 --- a/homeassistant/components/habitica/translations/de.json +++ b/homeassistant/components/habitica/translations/de.json @@ -9,10 +9,10 @@ "data": { "api_key": "API-Schl\u00fcssel", "api_user": "Habitica API-Benutzer-ID", - "name": "Override f\u00fcr den Benutzernamen von Habitica. Wird f\u00fcr Serviceaufrufe verwendet", + "name": "\u00dcbersteuerung f\u00fcr den Benutzernamen von Habitica. Wird f\u00fcr Serviceaufrufe verwendet", "url": "URL" }, - "description": "Verbinde dein Habitica-Profil, um die \u00dcberwachung des Profils und der Aufgaben deines Benutzers zu erm\u00f6glichen. Beachte, dass api_id und api_key von https://habitica.com/user/settings/api bezogen werden m\u00fcssen." + "description": "Verbinde dein Habitica Profil, um die \u00dcberwachung des Profils und der Aufgaben deines Benutzers zu erm\u00f6glichen. Beachte, dass api_id und api_key von https://habitica.com/user/settings/api bezogen werden m\u00fcssen." } } } diff --git a/homeassistant/components/habitica/translations/sk.json b/homeassistant/components/habitica/translations/sk.json index 25aa532742a..0df35987566 100644 --- a/homeassistant/components/habitica/translations/sk.json +++ b/homeassistant/components/habitica/translations/sk.json @@ -8,9 +8,11 @@ "user": { "data": { "api_key": "API k\u013e\u00fa\u010d", + "api_user": "ID pou\u017e\u00edvate\u013ea API spolo\u010dnosti Habitica", "name": "Prep\u00edsa\u0165 pou\u017e\u00edvate\u013esk\u00e9 meno Habitica. Bude sa pou\u017e\u00edva\u0165 na servisn\u00e9 hovory", "url": "URL" - } + }, + "description": "Pripojte svoj profil Habitica, aby ste umo\u017enili sledovanie profilu a \u00faloh v\u00e1\u0161ho pou\u017e\u00edvate\u013ea. Upozor\u0148ujeme, \u017ee api_id a api_key je potrebn\u00e9 z\u00edska\u0165 z https://habitica.com/user/settings/api" } } } diff --git a/homeassistant/components/hardkernel/__init__.py b/homeassistant/components/hardkernel/__init__.py index 6dfe30b9e75..c81ad7860be 100644 --- a/homeassistant/components/hardkernel/__init__.py +++ b/homeassistant/components/hardkernel/__init__.py @@ -13,7 +13,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: # The hassio integration has not yet fetched data from the supervisor raise ConfigEntryNotReady - board: str + board: str | None if (board := os_info.get("board")) is None or not board.startswith("odroid"): # Not running on a Hardkernel board, Home Assistant may have been migrated hass.async_create_task(hass.config_entries.async_remove(entry.entry_id)) diff --git a/homeassistant/components/hardkernel/hardware.py b/homeassistant/components/hardkernel/hardware.py index eca599960f8..cd83f684eac 100644 --- a/homeassistant/components/hardkernel/hardware.py +++ b/homeassistant/components/hardkernel/hardware.py @@ -21,7 +21,7 @@ def async_info(hass: HomeAssistant) -> list[HardwareInfo]: """Return board info.""" if (os_info := get_os_info(hass)) is None: raise HomeAssistantError - board: str + board: str | None if (board := os_info.get("board")) is None: raise HomeAssistantError if not board.startswith("odroid"): diff --git a/homeassistant/components/hardware/hardware.py b/homeassistant/components/hardware/hardware.py index e07f70022f4..cc904fbf131 100644 --- a/homeassistant/components/hardware/hardware.py +++ b/homeassistant/components/hardware/hardware.py @@ -11,18 +11,16 @@ from .const import DOMAIN from .models import HardwareProtocol -async def async_process_hardware_platforms(hass: HomeAssistant): +async def async_process_hardware_platforms(hass: HomeAssistant) -> None: """Start processing hardware platforms.""" hass.data[DOMAIN]["hardware_platform"] = {} await async_process_integration_platforms(hass, DOMAIN, _register_hardware_platform) - return True - async def _register_hardware_platform( hass: HomeAssistant, integration_domain: str, platform: HardwareProtocol -): +) -> None: """Register a hardware platform.""" if integration_domain == DOMAIN: return diff --git a/homeassistant/components/hardware/websocket_api.py b/homeassistant/components/hardware/websocket_api.py index d8dc5f28fdd..66b90edc893 100644 --- a/homeassistant/components/hardware/websocket_api.py +++ b/homeassistant/components/hardware/websocket_api.py @@ -74,7 +74,7 @@ async def ws_info( @websocket_api.async_response async def ws_subscribe_system_status( hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any] -): +) -> None: """Subscribe to system status updates.""" system_status: SystemStatus = hass.data[DOMAIN]["system_status"] diff --git a/homeassistant/components/harmony/data.py b/homeassistant/components/harmony/data.py index fbbbbd38e3a..33c0e3c5b5f 100644 --- a/homeassistant/components/harmony/data.py +++ b/homeassistant/components/harmony/data.py @@ -126,7 +126,8 @@ class HarmonyData(HarmonySubscriberMixin): except (ValueError, AttributeError) as err: await self._client.close() raise ConfigEntryNotReady( - f"{self._name}: Error {err} while connected HUB at: {self._address}:8088" + f"{self._name}: Error {err} while connected HUB at:" + f" {self._address}:8088" ) from err if not connected: await self._client.close() @@ -219,8 +220,10 @@ class HarmonyData(HarmonySubscriberMixin): return _LOGGER.debug( - "Sending commands to device %s holding for %s seconds " - "with a delay of %s seconds", + ( + "Sending commands to device %s holding for %s seconds " + "with a delay of %s seconds" + ), device, hold_secs, delay_secs, diff --git a/homeassistant/components/harmony/select.py b/homeassistant/components/harmony/select.py index a3f059d2c00..0ed3f0ca275 100644 --- a/homeassistant/components/harmony/select.py +++ b/homeassistant/components/harmony/select.py @@ -33,7 +33,7 @@ async def async_setup_entry( class HarmonyActivitySelect(HarmonyEntity, SelectEntity): """Select representation of a Harmony activities.""" - _attr_device_class = f"{DOMAIN}__activities" + _attr_translation_key = "activities" def __init__(self, name: str, data: HarmonyData) -> None: """Initialize HarmonyActivitySelect class.""" @@ -67,6 +67,7 @@ class HarmonyActivitySelect(HarmonyEntity, SelectEntity): """Change the current activity.""" if option == TRANSLATABLE_POWER_OFF: await self._data.async_start_activity(ACTIVITY_POWER_OFF) + return await self._data.async_start_activity(option) async def async_added_to_hass(self) -> None: diff --git a/homeassistant/components/harmony/strings.json b/homeassistant/components/harmony/strings.json index 1a156ca863c..62de202372b 100644 --- a/homeassistant/components/harmony/strings.json +++ b/homeassistant/components/harmony/strings.json @@ -3,15 +3,15 @@ "flow_title": "{name}", "step": { "user": { - "title": "Setup Logitech Harmony Hub", + "title": "Set up Logitech Harmony Hub", "data": { "host": "[%key:common::config_flow::data::host%]", "name": "Hub Name" } }, "link": { - "title": "Setup Logitech Harmony Hub", - "description": "Do you want to setup {name} ({host})?" + "title": "Set up Logitech Harmony Hub", + "description": "Do you want to set up {name} ({host})?" } }, "error": { @@ -32,5 +32,14 @@ } } } + }, + "entity": { + "select": { + "activities": { + "state": { + "power_off": "Power Off" + } + } + } } } diff --git a/homeassistant/components/harmony/strings.select.json b/homeassistant/components/harmony/strings.select.json deleted file mode 100644 index 5dbdf1a1c3d..00000000000 --- a/homeassistant/components/harmony/strings.select.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "state": { - "harmony__activities": { - "power_off": "Power Off" - } - } -} diff --git a/homeassistant/components/harmony/translations/ca.json b/homeassistant/components/harmony/translations/ca.json index f5648d99dba..af7aa9421de 100644 --- a/homeassistant/components/harmony/translations/ca.json +++ b/homeassistant/components/harmony/translations/ca.json @@ -22,6 +22,15 @@ } } }, + "entity": { + "select": { + "activities": { + "state": { + "power_off": "Apaga" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/harmony/translations/de.json b/homeassistant/components/harmony/translations/de.json index 24cdc51cd65..3e81b745b16 100644 --- a/homeassistant/components/harmony/translations/de.json +++ b/homeassistant/components/harmony/translations/de.json @@ -22,6 +22,15 @@ } } }, + "entity": { + "select": { + "activities": { + "state": { + "power_off": "Ausschalten" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/harmony/translations/el.json b/homeassistant/components/harmony/translations/el.json index c8483acd34b..b3555ff1244 100644 --- a/homeassistant/components/harmony/translations/el.json +++ b/homeassistant/components/harmony/translations/el.json @@ -22,6 +22,15 @@ } } }, + "entity": { + "select": { + "activities": { + "state": { + "power_off": "\u0391\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/harmony/translations/en.json b/homeassistant/components/harmony/translations/en.json index dbc0dee16c5..4dcaf4f1392 100644 --- a/homeassistant/components/harmony/translations/en.json +++ b/homeassistant/components/harmony/translations/en.json @@ -10,15 +10,24 @@ "flow_title": "{name}", "step": { "link": { - "description": "Do you want to setup {name} ({host})?", - "title": "Setup Logitech Harmony Hub" + "description": "Do you want to set up {name} ({host})?", + "title": "Set up Logitech Harmony Hub" }, "user": { "data": { "host": "Host", "name": "Hub Name" }, - "title": "Setup Logitech Harmony Hub" + "title": "Set up Logitech Harmony Hub" + } + } + }, + "entity": { + "select": { + "activities": { + "state": { + "power_off": "Power Off" + } } } }, diff --git a/homeassistant/components/harmony/translations/es.json b/homeassistant/components/harmony/translations/es.json index 3acc9abb07e..fa05e771940 100644 --- a/homeassistant/components/harmony/translations/es.json +++ b/homeassistant/components/harmony/translations/es.json @@ -22,6 +22,15 @@ } } }, + "entity": { + "select": { + "activities": { + "state": { + "power_off": "Apagar" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/harmony/translations/et.json b/homeassistant/components/harmony/translations/et.json index ef4c0c5ce69..f535d2ffc9e 100644 --- a/homeassistant/components/harmony/translations/et.json +++ b/homeassistant/components/harmony/translations/et.json @@ -22,6 +22,15 @@ } } }, + "entity": { + "select": { + "activities": { + "state": { + "power_off": "Toide v\u00e4lja" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/harmony/translations/hu.json b/homeassistant/components/harmony/translations/hu.json index 900cd243247..45bf73f2bd3 100644 --- a/homeassistant/components/harmony/translations/hu.json +++ b/homeassistant/components/harmony/translations/hu.json @@ -22,6 +22,15 @@ } } }, + "entity": { + "select": { + "activities": { + "state": { + "power_off": "Kikapcsol\u00e1s" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/harmony/translations/id.json b/homeassistant/components/harmony/translations/id.json index 86ab0be3274..ba1dbd19d28 100644 --- a/homeassistant/components/harmony/translations/id.json +++ b/homeassistant/components/harmony/translations/id.json @@ -22,6 +22,15 @@ } } }, + "entity": { + "select": { + "activities": { + "state": { + "power_off": "Daya Mati" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/harmony/translations/it.json b/homeassistant/components/harmony/translations/it.json index 9f90460f785..ed85856d28b 100644 --- a/homeassistant/components/harmony/translations/it.json +++ b/homeassistant/components/harmony/translations/it.json @@ -10,8 +10,8 @@ "flow_title": "{name}", "step": { "link": { - "description": "Vuoi impostare {name} ({host})?", - "title": "Impostazione di Logitech Harmony Hub" + "description": "Vuoi configurare {name} ({host})?", + "title": "Configura Logitech Harmony Hub" }, "user": { "data": { @@ -22,6 +22,15 @@ } } }, + "entity": { + "select": { + "activities": { + "state": { + "power_off": "Spegnimento" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/harmony/translations/no.json b/homeassistant/components/harmony/translations/no.json index 072837d600e..197473053ee 100644 --- a/homeassistant/components/harmony/translations/no.json +++ b/homeassistant/components/harmony/translations/no.json @@ -10,15 +10,24 @@ "flow_title": "{name}", "step": { "link": { - "description": "Vil du konfigurere {name} ({host})?", - "title": "Sett opp Logitech Harmony Hub" + "description": "Vil du sette opp {name} ( {host} )?", + "title": "Konfigurer Logitech Harmony Hub" }, "user": { "data": { "host": "Vert", "name": "Navn p\u00e5 hub" }, - "title": "Sett opp Logitech Harmony Hub" + "title": "Konfigurer Logitech Harmony Hub" + } + } + }, + "entity": { + "select": { + "activities": { + "state": { + "power_off": "Sl\u00e5 av" + } } } }, diff --git a/homeassistant/components/harmony/translations/pl.json b/homeassistant/components/harmony/translations/pl.json index 7e29aa8ac9a..8284311bf6d 100644 --- a/homeassistant/components/harmony/translations/pl.json +++ b/homeassistant/components/harmony/translations/pl.json @@ -22,6 +22,15 @@ } } }, + "entity": { + "select": { + "activities": { + "state": { + "power_off": "wy\u0142\u0105czony" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/harmony/translations/pt-BR.json b/homeassistant/components/harmony/translations/pt-BR.json index 4b3f264ddeb..aa4bb271c82 100644 --- a/homeassistant/components/harmony/translations/pt-BR.json +++ b/homeassistant/components/harmony/translations/pt-BR.json @@ -22,6 +22,15 @@ } } }, + "entity": { + "select": { + "activities": { + "state": { + "power_off": "Desligar" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/harmony/translations/pt.json b/homeassistant/components/harmony/translations/pt.json index 3b58778917d..a8daca59301 100644 --- a/homeassistant/components/harmony/translations/pt.json +++ b/homeassistant/components/harmony/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "unknown": "Erro inesperado" }, "step": { diff --git a/homeassistant/components/harmony/translations/ru.json b/homeassistant/components/harmony/translations/ru.json index 8bf00fe8637..069064c3cf1 100644 --- a/homeassistant/components/harmony/translations/ru.json +++ b/homeassistant/components/harmony/translations/ru.json @@ -22,6 +22,15 @@ } } }, + "entity": { + "select": { + "activities": { + "state": { + "power_off": "\u0412\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043f\u0438\u0442\u0430\u043d\u0438\u044f" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/harmony/translations/select.hu.json b/homeassistant/components/harmony/translations/select.hu.json new file mode 100644 index 00000000000..ac813756a6a --- /dev/null +++ b/homeassistant/components/harmony/translations/select.hu.json @@ -0,0 +1,7 @@ +{ + "state": { + "harmony__activities": { + "power_off": "Kikapcsol\u00e1s" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/harmony/translations/select.ko.json b/homeassistant/components/harmony/translations/select.ko.json new file mode 100644 index 00000000000..fd933c19fac --- /dev/null +++ b/homeassistant/components/harmony/translations/select.ko.json @@ -0,0 +1,7 @@ +{ + "state": { + "harmony__activities": { + "power_off": "\uc804\uc6d0 \ub044\uae30" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/harmony/translations/sk.json b/homeassistant/components/harmony/translations/sk.json index 78cba980f6a..8520719a88d 100644 --- a/homeassistant/components/harmony/translations/sk.json +++ b/homeassistant/components/harmony/translations/sk.json @@ -10,11 +10,23 @@ "flow_title": "{name}", "step": { "link": { - "description": "Chcete nastavi\u0165 {name} ({host})?" + "description": "Chcete nastavi\u0165 {name} ({host})?", + "title": "Nastavte Logitech Harmony Hub" }, "user": { "data": { - "host": "Hostite\u013e" + "host": "Hostite\u013e", + "name": "N\u00e1zov hubu" + }, + "title": "Nastavte Logitech Harmony Hub" + } + } + }, + "entity": { + "select": { + "activities": { + "state": { + "power_off": "Vypn\u00fa\u0165" } } } @@ -23,8 +35,10 @@ "step": { "init": { "data": { - "activity": "Predvolen\u00e1 aktivita, ktor\u00e1 sa m\u00e1 vykona\u0165, ke\u010f nie je zadan\u00e1 \u017eiadna." - } + "activity": "Predvolen\u00e1 aktivita, ktor\u00e1 sa m\u00e1 vykona\u0165, ke\u010f nie je zadan\u00e1 \u017eiadna.", + "delay_secs": "Oneskorenie medzi odoslan\u00edm pr\u00edkazov." + }, + "description": "Upravte mo\u017enosti Harmony Hub" } } } diff --git a/homeassistant/components/harmony/translations/zh-Hant.json b/homeassistant/components/harmony/translations/zh-Hant.json index 0ea95aecd67..517475fd46d 100644 --- a/homeassistant/components/harmony/translations/zh-Hant.json +++ b/homeassistant/components/harmony/translations/zh-Hant.json @@ -22,6 +22,15 @@ } } }, + "entity": { + "select": { + "activities": { + "state": { + "power_off": "\u95dc\u6a5f" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/hassio/__init__.py b/homeassistant/components/hassio/__init__.py index 581ed0e3292..0d159915013 100644 --- a/homeassistant/components/hassio/__init__.py +++ b/homeassistant/components/hassio/__init__.py @@ -241,7 +241,7 @@ HARDWARE_INTEGRATIONS = { @callback @bind_hass -def get_info(hass): +def get_info(hass: HomeAssistant) -> dict[str, Any] | None: """Return generic information from Supervisor. Async friendly. @@ -251,7 +251,7 @@ def get_info(hass): @callback @bind_hass -def get_host_info(hass): +def get_host_info(hass: HomeAssistant) -> dict[str, Any] | None: """Return generic host information. Async friendly. @@ -261,7 +261,7 @@ def get_host_info(hass): @callback @bind_hass -def get_store(hass): +def get_store(hass: HomeAssistant) -> dict[str, Any] | None: """Return store information. Async friendly. @@ -311,7 +311,7 @@ def get_addons_changelogs(hass): @callback @bind_hass -def get_os_info(hass): +def get_os_info(hass: HomeAssistant) -> dict[str, Any] | None: """Return OS information. Async friendly. @@ -321,7 +321,7 @@ def get_os_info(hass): @callback @bind_hass -def get_core_info(hass): +def get_core_info(hass: HomeAssistant) -> dict[str, Any] | None: """Return Home Assistant Core information from Supervisor. Async friendly. @@ -723,7 +723,7 @@ class HassioDataUpdateCoordinator(DataUpdateCoordinator): addons_info = get_addons_info(self.hass) addons_stats = get_addons_stats(self.hass) addons_changelogs = get_addons_changelogs(self.hass) - store_data = get_store(self.hass) + store_data = get_store(self.hass) or {} repositories = { repo[ATTR_SLUG]: repo[ATTR_NAME] diff --git a/homeassistant/components/hassio/handler.py b/homeassistant/components/hassio/handler.py index 4f300ef16db..2806c08ee54 100644 --- a/homeassistant/components/hassio/handler.py +++ b/homeassistant/components/hassio/handler.py @@ -414,7 +414,8 @@ class HassIO: if http_config.get(CONF_SERVER_HOST) is not None: options["watchdog"] = False _LOGGER.warning( - "Found incompatible HTTP option 'server_host'. Watchdog feature disabled" + "Found incompatible HTTP option 'server_host'. Watchdog feature" + " disabled" ) return await self.send_command("/homeassistant/options", payload=options) @@ -455,7 +456,9 @@ class HassIO: f"http://{self._ip}{command}", json=payload, headers={ - aiohttp.hdrs.AUTHORIZATION: f"Bearer {os.environ.get('SUPERVISOR_TOKEN', '')}" + aiohttp.hdrs.AUTHORIZATION: ( + f"Bearer {os.environ.get('SUPERVISOR_TOKEN', '')}" + ) }, timeout=aiohttp.ClientTimeout(total=timeout), ) diff --git a/homeassistant/components/hassio/system_health.py b/homeassistant/components/hassio/system_health.py index 795d1e325fb..74437186ff2 100644 --- a/homeassistant/components/hassio/system_health.py +++ b/homeassistant/components/hassio/system_health.py @@ -22,8 +22,8 @@ def async_register( async def system_health_info(hass: HomeAssistant): """Get info for the info page.""" - info = get_info(hass) - host_info = get_host_info(hass) + info = get_info(hass) or {} + host_info = get_host_info(hass) or {} supervisor_info = get_supervisor_info(hass) healthy: bool | dict[str, str] @@ -57,7 +57,7 @@ async def system_health_info(hass: HomeAssistant): } if info.get("hassos") is not None: - os_info = get_os_info(hass) + os_info = get_os_info(hass) or {} information["board"] = os_info.get("board") information["supervisor_api"] = system_health.async_check_can_reach_url( diff --git a/homeassistant/components/hassio/translations/ko.json b/homeassistant/components/hassio/translations/ko.json index 23280115649..315812ed675 100644 --- a/homeassistant/components/hassio/translations/ko.json +++ b/homeassistant/components/hassio/translations/ko.json @@ -1,4 +1,61 @@ { + "issues": { + "unsupported_docker_configuration": { + "title": "\uc9c0\uc6d0\ub418\uc9c0 \uc54a\ub294 \uc2dc\uc2a4\ud15c - Docker\uac00 \uc798\ubabb \uad6c\uc131\ub428" + }, + "unsupported_docker_version": { + "description": "\uc798\ubabb\ub41c \ubc84\uc804\uc758 Docker\ub97c \uc0ac\uc6a9 \uc911\uc774\ubbc0\ub85c \uc2dc\uc2a4\ud15c\uc774 \uc9c0\uc6d0\ub418\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. \ub9c1\ud06c\ub97c \ud074\ub9ad\ud558\uc5ec \uc62c\ubc14\ub978 \ubc84\uc804\uacfc \uc774 \ubb38\uc81c\ub97c \ud574\uacb0\ud558\ub294 \ubc29\ubc95\uc744 \uc54c\uc544\ubcf4\uc138\uc694.", + "title": "\uc9c0\uc6d0\ub418\uc9c0 \uc54a\ub294 \uc2dc\uc2a4\ud15c - Docker \ubc84\uc804" + }, + "unsupported_job_conditions": { + "description": "\uc608\uae30\uce58 \uc54a\uc740 \uc624\ub958 \ubc0f \uc190\uc0c1\uc73c\ub85c\ubd80\ud130 \ubcf4\ud638\ud558\ub294 \ud558\ub098 \uc774\uc0c1\uc758 \uc791\uc5c5 \uc870\uac74\uc774 \ube44\ud65c\uc131\ud654\ub418\uc5b4 \uc2dc\uc2a4\ud15c\uc774 \uc9c0\uc6d0\ub418\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. \ub9c1\ud06c\ub97c \ud074\ub9ad\ud558\uc5ec \uc790\uc138\ud55c \ub0b4\uc6a9\uacfc \uc774 \ubb38\uc81c\ub97c \ud574\uacb0\ud558\ub294 \ubc29\ubc95\uc744 \uc54c\uc544\ubcf4\uc138\uc694.", + "title": "\uc9c0\uc6d0\ub418\uc9c0 \uc54a\ub294 \uc2dc\uc2a4\ud15c - \ubcf4\ud638 \uae30\ub2a5 \ube44\ud65c\uc131\ud654" + }, + "unsupported_lxc": { + "description": "\uc2dc\uc2a4\ud15c\uc774 LXC \uac00\uc0c1 \uba38\uc2e0\uc5d0\uc11c \uc2e4\ud589 \uc911\uc774\uae30 \ub54c\ubb38\uc5d0 \uc9c0\uc6d0\ub418\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. \ub9c1\ud06c\ub97c \ud074\ub9ad\ud558\uc5ec \uc790\uc138\ud55c \ub0b4\uc6a9\uacfc \uc774 \ubb38\uc81c\ub97c \ud574\uacb0\ud558\ub294 \ubc29\ubc95\uc744 \uc54c\uc544\ubcf4\uc138\uc694.", + "title": "\uc9c0\uc6d0\ub418\uc9c0 \uc54a\ub294 \uc2dc\uc2a4\ud15c - LXC \uac10\uc9c0\ub428" + }, + "unsupported_network_manager": { + "description": "\ub124\ud2b8\uc6cc\ud06c \uad00\ub9ac\uc790\uac00 \uc5c6\uac70\ub098 \ube44\ud65c\uc131 \uc0c1\ud0dc\uc774\uac70\ub098 \uc798\ubabb \uad6c\uc131\ub418\uc5c8\uae30 \ub54c\ubb38\uc5d0 \uc2dc\uc2a4\ud15c\uc774 \uc9c0\uc6d0\ub418\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. \ub9c1\ud06c\ub97c \ud074\ub9ad\ud558\uc5ec \uc790\uc138\ud788 \uc54c\uc544\ubcf4\uace0 \uc774 \ubb38\uc81c\ub97c \ud574\uacb0\ud558\ub294 \ubc29\ubc95\uc744 \uc54c\uc544\ubcf4\uc138\uc694.", + "title": "\uc9c0\uc6d0\ub418\uc9c0 \uc54a\ub294 \uc2dc\uc2a4\ud15c - \ub124\ud2b8\uc6cc\ud06c \uad00\ub9ac\uc790 \ubb38\uc81c" + }, + "unsupported_os": { + "description": "\uc0ac\uc6a9 \uc911\uc778 \uc6b4\uc601 \uccb4\uc81c\uac00 Supervisor\uc640 \ud568\uaed8 \uc0ac\uc6a9\ud558\ub3c4\ub85d \ud14c\uc2a4\ud2b8 \ub610\ub294 \uc720\uc9c0 \uad00\ub9ac\ub418\uc9c0 \uc54a\uc558\uae30 \ub54c\ubb38\uc5d0 \uc2dc\uc2a4\ud15c\uc774 \uc9c0\uc6d0\ub418\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. \uc9c0\uc6d0\ub418\ub294 \uc6b4\uc601 \uccb4\uc81c \ubc0f \uc774\ub97c \uc218\uc815\ud558\ub294 \ubc29\ubc95\uc5d0 \ub300\ud574 \ub9c1\ud06c\ub97c \ud074\ub9ad\ud574\uc11c \ucc38\uace0\ud558\uc2ed\uc2dc\uc624.", + "title": "\uc9c0\uc6d0\ub418\uc9c0 \uc54a\ub294 \uc2dc\uc2a4\ud15c - \uc6b4\uc601 \uccb4\uc81c" + }, + "unsupported_os_agent": { + "description": "OS-Agent\uac00 \uc5c6\uac70\ub098 \ube44\ud65c\uc131\uc774\uac70\ub098 \uc798\ubabb \uad6c\uc131\ub418\uc5b4 \uc2dc\uc2a4\ud15c\uc774 \uc9c0\uc6d0\ub418\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. \ub9c1\ud06c\ub97c \ud074\ub9ad\ud558\uc5ec \uc790\uc138\ud55c \ub0b4\uc6a9\uacfc \uc774 \ubb38\uc81c\ub97c \ud574\uacb0\ud558\ub294 \ubc29\ubc95\uc744 \uc54c\uc544\ubcf4\uc138\uc694.", + "title": "\uc9c0\uc6d0\ub418\uc9c0 \uc54a\ub294 \uc2dc\uc2a4\ud15c - OS \uc5d0\uc774\uc804\ud2b8 \ubb38\uc81c" + }, + "unsupported_restart_policy": { + "description": "Docker \ucee8\ud14c\uc774\ub108\uc5d0 \uc2dc\uc791 \uc2dc \ubb38\uc81c\ub97c \uc77c\uc73c\ud0ac \uc218 \uc788\ub294 \uc7ac\uc2dc\uc791 \uc815\ucc45 \uc138\ud2b8\uac00 \uc788\uc73c\ubbc0\ub85c \uc2dc\uc2a4\ud15c\uc774 \uc9c0\uc6d0\ub418\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. \ub9c1\ud06c\ub97c \ud074\ub9ad\ud558\uc5ec \uc790\uc138\ud55c \ub0b4\uc6a9\uacfc \uc774 \ubb38\uc81c\ub97c \ud574\uacb0\ud558\ub294 \ubc29\ubc95\uc744 \uc54c\uc544\ubcf4\uc138\uc694.", + "title": "\uc9c0\uc6d0\ub418\uc9c0 \uc54a\ub294 \uc2dc\uc2a4\ud15c - \ucee8\ud14c\uc774\ub108 \uc7ac\uc2dc\uc791 \uc815\ucc45" + }, + "unsupported_software": { + "description": "Home Assistant \uc0dd\ud0dc\uacc4 \uc678\ubd80\uc758 \ucd94\uac00 \uc18c\ud504\ud2b8\uc6e8\uc5b4\uac00 \uac10\uc9c0\ub418\uc5b4 \uc2dc\uc2a4\ud15c\uc774 \uc9c0\uc6d0\ub418\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. \ub9c1\ud06c\ub97c \ud074\ub9ad\ud558\uc5ec \uc790\uc138\ud55c \ub0b4\uc6a9\uacfc \uc774 \ubb38\uc81c\ub97c \ud574\uacb0\ud558\ub294 \ubc29\ubc95\uc744 \uc54c\uc544\ubcf4\uc138\uc694.", + "title": "\uc9c0\uc6d0\ub418\uc9c0 \uc54a\ub294 \uc2dc\uc2a4\ud15c - \uc9c0\uc6d0\ub418\uc9c0 \uc54a\ub294 \uc18c\ud504\ud2b8\uc6e8\uc5b4" + }, + "unsupported_source_mods": { + "description": "Supervisor \uc18c\uc2a4 \ucf54\ub4dc\uac00 \uc218\uc815\ub418\uc5b4 \uc2dc\uc2a4\ud15c\uc774 \uc9c0\uc6d0\ub418\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. \ub9c1\ud06c\ub97c \ud074\ub9ad\ud558\uc5ec \uc790\uc138\ud55c \ub0b4\uc6a9\uacfc \uc774 \ubb38\uc81c\ub97c \ud574\uacb0\ud558\ub294 \ubc29\ubc95\uc744 \uc54c\uc544\ubcf4\uc138\uc694.", + "title": "\uc9c0\uc6d0\ub418\uc9c0 \uc54a\ub294 \uc2dc\uc2a4\ud15c - Supervisor \uc18c\uc2a4 \uc218\uc815" + }, + "unsupported_supervisor_version": { + "description": "\uc624\ub798\ub41c \ubc84\uc804\uc758 Supervisor\uac00 \uc0ac\uc6a9 \uc911\uc774\uace0 \uc790\ub3d9 \uc5c5\ub370\uc774\ud2b8\uac00 \ube44\ud65c\uc131\ud654\ub418\uc5c8\uae30 \ub54c\ubb38\uc5d0 \uc2dc\uc2a4\ud15c\uc774 \uc9c0\uc6d0\ub418\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. \ub9c1\ud06c\ub97c \ud074\ub9ad\ud558\uc5ec \uc790\uc138\ud55c \ub0b4\uc6a9\uacfc \uc774 \ubb38\uc81c\ub97c \ud574\uacb0\ud558\ub294 \ubc29\ubc95\uc744 \uc54c\uc544\ubcf4\uc138\uc694.", + "title": "\uc9c0\uc6d0\ub418\uc9c0 \uc54a\ub294 \uc2dc\uc2a4\ud15c - Supervisor \ubc84\uc804" + }, + "unsupported_systemd": { + "description": "Systemd\uac00 \uc5c6\uac70\ub098 \ube44\ud65c\uc131\uc774\uac70\ub098 \uc798\ubabb \uad6c\uc131\ub418\uc5b4 \uc2dc\uc2a4\ud15c\uc774 \uc9c0\uc6d0\ub418\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. \ub9c1\ud06c\ub97c \ud074\ub9ad\ud558\uc5ec \uc790\uc138\ud55c \ub0b4\uc6a9\uacfc \uc774 \ubb38\uc81c\ub97c \ud574\uacb0\ud558\ub294 \ubc29\ubc95\uc744 \uc54c\uc544\ubcf4\uc138\uc694.", + "title": "\uc9c0\uc6d0\ub418\uc9c0 \uc54a\ub294 \uc2dc\uc2a4\ud15c - Systemd \ubb38\uc81c" + }, + "unsupported_systemd_journal": { + "description": "Systemd Journal \ubc0f/\ub610\ub294 \uac8c\uc774\ud2b8\uc6e8\uc774 \uc11c\ube44\uc2a4\uac00 \ub204\ub77d, \ube44\ud65c\uc131 \ub610\ub294 \uc798\ubabb \uad6c\uc131\ub418\uc5b4 \uc2dc\uc2a4\ud15c\uc774 \uc9c0\uc6d0\ub418\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. \ub9c1\ud06c\ub97c \ud074\ub9ad\ud558\uc5ec \uc790\uc138\ud55c \ub0b4\uc6a9\uacfc \uc774 \ubb38\uc81c\ub97c \ud574\uacb0\ud558\ub294 \ubc29\ubc95\uc744 \uc54c\uc544\ubcf4\uc138\uc694.", + "title": "\uc9c0\uc6d0\ub418\uc9c0 \uc54a\ub294 \uc2dc\uc2a4\ud15c - Systemd Journal \ubb38\uc81c" + }, + "unsupported_systemd_resolved": { + "description": "Systemd Resolved\uac00 \ub204\ub77d, \ube44\ud65c\uc131 \ub610\ub294 \uc798\ubabb \uad6c\uc131\ub418\uc5b4 \uc2dc\uc2a4\ud15c\uc774 \uc9c0\uc6d0\ub418\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. \ub9c1\ud06c\ub97c \ud074\ub9ad\ud558\uc5ec \uc790\uc138\ud55c \ub0b4\uc6a9\uacfc \uc774 \ubb38\uc81c\ub97c \ud574\uacb0\ud558\ub294 \ubc29\ubc95\uc744 \uc54c\uc544\ubcf4\uc138\uc694.", + "title": "\uc9c0\uc6d0\ub418\uc9c0 \uc54a\ub294 \uc2dc\uc2a4\ud15c - Systemd-Resolved \ubb38\uc81c" + } + }, "system_health": { "info": { "board": "\ubcf4\ub4dc \uc720\ud615", diff --git a/homeassistant/components/hassio/translations/pl.json b/homeassistant/components/hassio/translations/pl.json index 7ee470bc9bd..dec3db4de51 100644 --- a/homeassistant/components/hassio/translations/pl.json +++ b/homeassistant/components/hassio/translations/pl.json @@ -25,7 +25,7 @@ "title": "\"Niezdrowy\" system \u2014 niezaufany kod" }, "unsupported": { - "description": "System nie jest obs\u0142ugiwany z powodu \u201e{pow\u00f3d}\u201d. Skorzystaj z linku, aby dowiedzie\u0107 si\u0119 wi\u0119cej i jak to naprawi\u0107.", + "description": "System nie jest obs\u0142ugiwany z powodu \u201e{reason}\u201d. Skorzystaj z linku, aby dowiedzie\u0107 si\u0119 wi\u0119cej i jak to naprawi\u0107.", "title": "Nieobs\u0142ugiwany system \u2013 {reason}" }, "unsupported_apparmor": { diff --git a/homeassistant/components/hassio/translations/sk.json b/homeassistant/components/hassio/translations/sk.json index c8c1d58b4c9..0e04c7e2d96 100644 --- a/homeassistant/components/hassio/translations/sk.json +++ b/homeassistant/components/hassio/translations/sk.json @@ -1,40 +1,126 @@ { "issues": { + "unhealthy": { + "description": "Syst\u00e9m moment\u00e1lne nie je v poriadku z {reason}. Ak sa chcete dozvedie\u0165 viac a ako to opravi\u0165, pou\u017eite odkaz.", + "title": "Syst\u00e9m nie je v poriadku - {reason}" + }, + "unhealthy_docker": { + "description": "Syst\u00e9m moment\u00e1lne nie je v poriadku, preto\u017ee Docker je nespr\u00e1vne nakonfigurovan\u00fd. Ak sa chcete dozvedie\u0165 viac a ako to opravi\u0165, pou\u017eite odkaz.", + "title": "Syst\u00e9m nie je v poriadku \u2013 Docker je nespr\u00e1vne nakonfigurovan\u00fd" + }, + "unhealthy_privileged": { + "description": "Syst\u00e9m moment\u00e1lne nie je v poriadku, preto\u017ee nem\u00e1 privilegovan\u00fd pr\u00edstup k runtime dockeru. Ak sa chcete dozvedie\u0165 viac a ako to opravi\u0165, pou\u017eite odkaz.", + "title": "Syst\u00e9m nie je v poriadku - Nie je privilegovan\u00fd" + }, + "unhealthy_setup": { + "description": "Syst\u00e9m moment\u00e1lne nie je v poriadku, preto\u017ee sa nepodarilo dokon\u010di\u0165 nastavenie. Existuje nieko\u013eko d\u00f4vodov, pre\u010do sa to m\u00f4\u017ee vyskytn\u00fa\u0165, pomocou odkazu sa dozviete viac a ako to opravi\u0165.", + "title": "Syst\u00e9m nie je v poriadku \u2013 nastavenie zlyhalo" + }, + "unhealthy_supervisor": { + "description": "Syst\u00e9m moment\u00e1lne nie je v poriadku, preto\u017ee pokus o aktualiz\u00e1ciu spr\u00e1vcu na najnov\u0161iu verziu zlyhal. Ak sa chcete dozvedie\u0165 viac a ako to opravi\u0165, pou\u017eite odkaz.", + "title": "Syst\u00e9m nie je v poriadku \u2013 aktualiz\u00e1cia spr\u00e1vcu zlyhala" + }, + "unhealthy_untrusted": { + "description": "Syst\u00e9m je moment\u00e1lne nezdrav\u00fd, preto\u017ee zistil, \u017ee sa pou\u017e\u00edva ned\u00f4veryhodn\u00fd k\u00f3d alebo obr\u00e1zky. Ak sa chcete dozvedie\u0165 viac a ako to opravi\u0165, pou\u017eite odkaz.", + "title": "Syst\u00e9m nie je v poriadku \u2013 Ned\u00f4veryhodn\u00fd k\u00f3d" + }, "unsupported": { "description": "Syst\u00e9m nie je podporovan\u00fd z {reason}. Ak sa chcete dozvedie\u0165 viac a ako to opravi\u0165, pou\u017eite odkaz.", "title": "Nepodporovan\u00fd syst\u00e9m \u2013 {reason}" }, + "unsupported_apparmor": { + "description": "Syst\u00e9m nie je podporovan\u00fd, preto\u017ee AppArmor nefunguje spr\u00e1vne a doplnky s\u00fa spusten\u00e9 nechr\u00e1nen\u00fdm a nezabezpe\u010den\u00fdm sp\u00f4sobom. Ak sa chcete dozvedie\u0165 viac a ako to opravi\u0165, pou\u017eite odkaz.", + "title": "Nepodporovan\u00fd syst\u00e9m \u2013 probl\u00e9my s AppArmor" + }, "unsupported_cgroup_version": { + "description": "Syst\u00e9m nie je podporovan\u00fd, preto\u017ee sa pou\u017e\u00edva nespr\u00e1vna verzia Docker CGroup. Ak chcete zisti\u0165 spr\u00e1vnu verziu a ako to opravi\u0165, pou\u017eite odkaz.", "title": "Nepodporovan\u00fd syst\u00e9m \u2013 verzia CGroup" }, "unsupported_connectivity_check": { + "description": "Syst\u00e9m nie je podporovan\u00fd, preto\u017ee Home Assistant nedok\u00e1\u017ee ur\u010di\u0165, kedy je dostupn\u00e9 internetov\u00e9 pripojenie. Ak sa chcete dozvedie\u0165 viac a ako to opravi\u0165, pou\u017eite odkaz.", "title": "Nepodporovan\u00fd syst\u00e9m \u2013 Kontrola pripojenia je vypnut\u00e1" }, + "unsupported_content_trust": { + "description": "Syst\u00e9m nie je podporovan\u00fd, preto\u017ee Home Assistant nem\u00f4\u017ee overi\u0165, \u010di spusten\u00fd obsah je d\u00f4veryhodn\u00fd a \u00fato\u010dn\u00edci ho neupravuj\u00fa. Ak sa chcete dozvedie\u0165 viac a ako to opravi\u0165, pou\u017eite odkaz.", + "title": "Nepodporovan\u00fd syst\u00e9m \u2013 Kontrola d\u00f4veryhodnosti obsahu je vypnut\u00e1" + }, "unsupported_dbus": { + "description": "Syst\u00e9m nie je podporovan\u00fd, preto\u017ee D-Bus nefunguje spr\u00e1vne. Bez toho ve\u013ea vec\u00ed zlyh\u00e1, preto\u017ee superv\u00edzor nem\u00f4\u017ee komunikova\u0165 s hostite\u013eom. Ak sa chcete dozvedie\u0165 viac a ako to opravi\u0165, pou\u017eite odkaz.", "title": "Nepodporovan\u00fd syst\u00e9m - probl\u00e9my s D-Bus" }, "unsupported_dns_server": { + "description": "Syst\u00e9m nie je podporovan\u00fd, preto\u017ee poskytnut\u00fd server DNS nefunguje spr\u00e1vne a z\u00e1lo\u017en\u00e1 mo\u017enos\u0165 DNS bola zak\u00e1zan\u00e1. Ak sa chcete dozvedie\u0165 viac a ako to opravi\u0165, pou\u017eite odkaz.", "title": "Nepodporovan\u00fd syst\u00e9m \u2013 probl\u00e9my so serverom DNS" }, "unsupported_docker_configuration": { + "description": "Syst\u00e9m nie je podporovan\u00fd, preto\u017ee d\u00e9mon Docker be\u017e\u00ed neo\u010dak\u00e1van\u00fdm sp\u00f4sobom. Ak sa chcete dozvedie\u0165 viac a ako to opravi\u0165, pou\u017eite odkaz.", "title": "Nepodporovan\u00fd syst\u00e9m \u2013 Docker je nespr\u00e1vne nakonfigurovan\u00fd" }, "unsupported_docker_version": { + "description": "Syst\u00e9m nie je podporovan\u00fd, preto\u017ee sa pou\u017e\u00edva nespr\u00e1vna verzia Dockeru. Ak chcete zisti\u0165 spr\u00e1vnu verziu a ako to opravi\u0165, pou\u017eite odkaz.", "title": "Nepodporovan\u00fd syst\u00e9m \u2013 verzia Docker" }, + "unsupported_job_conditions": { + "description": "Syst\u00e9m nie je podporovan\u00fd, preto\u017ee bola zak\u00e1zan\u00e1 jedna alebo viac pracovn\u00fdch podmienok, ktor\u00e9 chr\u00e1nia pred neo\u010dak\u00e1van\u00fdmi zlyhaniami a poruchami. Ak sa chcete dozvedie\u0165 viac a ako to opravi\u0165, pou\u017eite odkaz.", + "title": "Nepodporovan\u00fd syst\u00e9m \u2013 Ochrana je vypnut\u00e1" + }, "unsupported_lxc": { + "description": "Syst\u00e9m nie je podporovan\u00fd, preto\u017ee je spusten\u00fd na virtu\u00e1lnom stroji LXC. Ak sa chcete dozvedie\u0165 viac a ako to opravi\u0165, pou\u017eite odkaz.", "title": "Nepodporovan\u00fd syst\u00e9m - zisten\u00e9 LXC" }, + "unsupported_network_manager": { + "description": "Syst\u00e9m nie je podporovan\u00fd, preto\u017ee ch\u00fdba spr\u00e1vca siete, je neakt\u00edvny alebo je nespr\u00e1vne nakonfigurovan\u00fd. Ak sa chcete dozvedie\u0165 viac a ako to opravi\u0165, pou\u017eite odkaz.", + "title": "Nepodporovan\u00fd syst\u00e9m \u2013 probl\u00e9my so spr\u00e1vcom siete" + }, + "unsupported_os": { + "description": "Syst\u00e9m nie je podporovan\u00fd, preto\u017ee pou\u017e\u00edvan\u00fd opera\u010dn\u00fd syst\u00e9m nie je testovan\u00fd ani udr\u017eiavan\u00fd na pou\u017eitie so superv\u00edzorom. Pou\u017eite odkaz, ktor\u00e9 opera\u010dn\u00e9 syst\u00e9my s\u00fa podporovan\u00e9 a ako to opravi\u0165.", + "title": "Nepodporovan\u00fd syst\u00e9m - Opera\u010dn\u00fd syst\u00e9m" + }, + "unsupported_os_agent": { + "description": "Syst\u00e9m nie je podporovan\u00fd, preto\u017ee OS-Agent ch\u00fdba, je neakt\u00edvny alebo je nespr\u00e1vne nakonfigurovan\u00fd. Ak sa chcete dozvedie\u0165 viac a ako to opravi\u0165, pou\u017eite odkaz.", + "title": "Nepodporovan\u00fd syst\u00e9m \u2013 probl\u00e9my s OS-Agentom" + }, + "unsupported_restart_policy": { + "description": "Syst\u00e9m nie je podporovan\u00fd, preto\u017ee kontajner Docker m\u00e1 nastaven\u00fa politiku re\u0161tartovania, ktor\u00e1 by mohla sp\u00f4sobi\u0165 probl\u00e9my pri spusten\u00ed. Ak sa chcete dozvedie\u0165 viac a ako to opravi\u0165, pou\u017eite odkaz.", + "title": "Nepodporovan\u00fd syst\u00e9m - Z\u00e1sady re\u0161tartu kontajnera" + }, "unsupported_software": { + "description": "Syst\u00e9m nie je podporovan\u00fd, preto\u017ee bol zisten\u00fd \u010fal\u0161\u00ed softv\u00e9r mimo ekosyst\u00e9mu Home Assistant. Ak sa chcete dozvedie\u0165 viac a ako to opravi\u0165, pou\u017eite odkaz.", "title": "Nepodporovan\u00fd syst\u00e9m \u2013 Nepodporovan\u00fd softv\u00e9r" + }, + "unsupported_source_mods": { + "description": "Syst\u00e9m nie je podporovan\u00fd, preto\u017ee zdrojov\u00fd k\u00f3d spr\u00e1vcu bol upraven\u00fd. Ak sa chcete dozvedie\u0165 viac a ako to opravi\u0165, pou\u017eite odkaz.", + "title": "Nepodporovan\u00fd syst\u00e9m \u2013 \u00fapravy zdroja superv\u00edzora" + }, + "unsupported_supervisor_version": { + "description": "Syst\u00e9m nie je podporovan\u00fd, preto\u017ee sa pou\u017e\u00edva zastaran\u00e1 verzia n\u00e1stroja Supervisor a automatick\u00e1 aktualiz\u00e1cia bola zak\u00e1zan\u00e1. Ak sa chcete dozvedie\u0165 viac a ako to opravi\u0165, pou\u017eite odkaz.", + "title": "Nepodporovan\u00fd syst\u00e9m - Verzia spr\u00e1vcu" + }, + "unsupported_systemd": { + "description": "Syst\u00e9m nie je podporovan\u00fd, preto\u017ee Systemd ch\u00fdba, je neakt\u00edvny alebo je nespr\u00e1vne nakonfigurovan\u00fd. Ak sa chcete dozvedie\u0165 viac a ako to opravi\u0165, pou\u017eite odkaz.", + "title": "Nepodporovan\u00fd syst\u00e9m \u2013 probl\u00e9my so syst\u00e9mom Systemd" + }, + "unsupported_systemd_journal": { + "description": "Syst\u00e9m nie je podporovan\u00fd, preto\u017ee Systemd Journal a/alebo slu\u017eba br\u00e1ny ch\u00fdba, je neakt\u00edvna alebo je nespr\u00e1vne nakonfigurovan\u00e1. Ak sa chcete dozvedie\u0165 viac a ako to opravi\u0165, pou\u017eite odkaz.", + "title": "Nepodporovan\u00fd syst\u00e9m \u2013 probl\u00e9my so Systemd Journal" + }, + "unsupported_systemd_resolved": { + "description": "Syst\u00e9m nie je podporovan\u00fd, preto\u017ee Systemd Resolved ch\u00fdba, je neakt\u00edvny alebo je nespr\u00e1vne nakonfigurovan\u00fd. Ak sa chcete dozvedie\u0165 viac a ako to opravi\u0165, pou\u017eite odkaz.", + "title": "Nepodporovan\u00fd syst\u00e9m \u2013 Probl\u00e9my vyrie\u0161en\u00e9 syst\u00e9mom Systemd" } }, "system_health": { "info": { "agent_version": "Verzia agenta", + "board": "Board", + "disk_total": "Disk celkovo", + "disk_used": "Disk pou\u017eit\u00fd", "docker_version": "Verzia Dockera", + "healthy": "V poriadku", "host_os": "Hostite\u013esk\u00fd opera\u010dn\u00fd syst\u00e9m", "installed_addons": "Nain\u0161talovan\u00e9 doplnky", + "supervisor_api": "Supervisor API", + "supervisor_version": "Supervisor verzia", "supported": "Podporovan\u00e9", "update_channel": "Aktualizova\u0165 kan\u00e1l", "version_api": "Verzia API" diff --git a/homeassistant/components/haveibeenpwned/sensor.py b/homeassistant/components/haveibeenpwned/sensor.py index 199035b2713..7caf9690bd8 100644 --- a/homeassistant/components/haveibeenpwned/sensor.py +++ b/homeassistant/components/haveibeenpwned/sensor.py @@ -5,7 +5,6 @@ from datetime import timedelta from http import HTTPStatus import logging -from aiohttp.hdrs import USER_AGENT import requests import voluptuous as vol @@ -160,7 +159,7 @@ class HaveIBeenPwnedData: """Get the latest data for current email from REST service.""" try: url = f"{URL}{self._email}?truncateResponse=false" - header = {USER_AGENT: HA_USER_AGENT, "hibp-api-key": self._api_key} + header = {"User-Agent": HA_USER_AGENT, "hibp-api-key": self._api_key} _LOGGER.debug("Checking for breaches for email: %s", self._email) req = requests.get(url, headers=header, allow_redirects=True, timeout=5) diff --git a/homeassistant/components/hddtemp/sensor.py b/homeassistant/components/hddtemp/sensor.py index 7ff8de90509..117de2116a4 100644 --- a/homeassistant/components/hddtemp/sensor.py +++ b/homeassistant/components/hddtemp/sensor.py @@ -18,8 +18,7 @@ from homeassistant.const import ( CONF_HOST, CONF_NAME, CONF_PORT, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv @@ -99,9 +98,9 @@ class HddTempSensor(SensorEntity): self._details = self.hddtemp.data[self.disk].split("|") self._attr_native_value = self._details[2] if self._details is not None and self._details[3] == "F": - self._attr_native_unit_of_measurement = TEMP_FAHRENHEIT + self._attr_native_unit_of_measurement = UnitOfTemperature.FAHRENHEIT else: - self._attr_native_unit_of_measurement = TEMP_CELSIUS + self._attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS else: self._attr_native_value = None diff --git a/homeassistant/components/heatmiser/climate.py b/homeassistant/components/heatmiser/climate.py index 7f6bd0ccf9c..24a0c88b45a 100644 --- a/homeassistant/components/heatmiser/climate.py +++ b/homeassistant/components/heatmiser/climate.py @@ -19,8 +19,7 @@ from homeassistant.const import ( CONF_ID, CONF_NAME, CONF_PORT, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv @@ -120,9 +119,9 @@ class HeatmiserV3Thermostat(ClimateEntity): return self.dcb = self.therm.read_dcb() self._attr_temperature_unit = ( - TEMP_CELSIUS + UnitOfTemperature.CELSIUS if (self.therm.get_temperature_format() == "C") - else TEMP_FAHRENHEIT + else UnitOfTemperature.FAHRENHEIT ) self._current_temperature = int(self.therm.get_floor_temp()) self._target_temperature = int(self.therm.get_target_temp()) diff --git a/homeassistant/components/heos/__init__.py b/homeassistant/components/heos/__init__.py index a7f56e91368..2df7c37a51c 100644 --- a/homeassistant/components/heos/__init__.py +++ b/homeassistant/components/heos/__init__.py @@ -108,8 +108,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: favorites = await controller.get_favorites() else: _LOGGER.warning( - "%s is not logged in to a HEOS account and will be unable to retrieve " - "HEOS favorites: Use the 'heos.sign_in' service to sign-in to a HEOS account", + ( + "%s is not logged in to a HEOS account and will be unable to" + " retrieve HEOS favorites: Use the 'heos.sign_in' service to" + " sign-in to a HEOS account" + ), host, ) inputs = await controller.get_input_sources() @@ -289,7 +292,8 @@ class GroupManager: leader_id = entity_id_to_player_id_map.get(leader_entity_id) if not leader_id: raise HomeAssistantError( - f"The group leader {leader_entity_id} could not be resolved to a HEOS player." + f"The group leader {leader_entity_id} could not be resolved to a HEOS" + " player." ) member_ids = [ entity_id_to_player_id_map[member] diff --git a/homeassistant/components/heos/translations/pt.json b/homeassistant/components/heos/translations/pt.json index a8931048295..a28999a806b 100644 --- a/homeassistant/components/heos/translations/pt.json +++ b/homeassistant/components/heos/translations/pt.json @@ -4,7 +4,7 @@ "single_instance_allowed": "J\u00e1 configurado. Apenas uma \u00fanica configura\u00e7\u00e3o \u00e9 poss\u00edvel." }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "user": { diff --git a/homeassistant/components/heos/translations/sk.json b/homeassistant/components/heos/translations/sk.json index d57cf8b2883..24e570f91b9 100644 --- a/homeassistant/components/heos/translations/sk.json +++ b/homeassistant/components/heos/translations/sk.json @@ -11,6 +11,7 @@ "data": { "host": "Hostite\u013e" }, + "description": "Zadajte n\u00e1zov hostite\u013ea alebo IP adresu zariadenia Heos (najlep\u0161ie pripojen\u00e9ho k\u00e1blom k sieti).", "title": "Pripoji\u0165 sa k Heos" } } diff --git a/homeassistant/components/here_travel_time/__init__.py b/homeassistant/components/here_travel_time/__init__.py index 57ad77e0654..2ce91946f86 100644 --- a/homeassistant/components/here_travel_time/__init__.py +++ b/homeassistant/components/here_travel_time/__init__.py @@ -1,8 +1,6 @@ """The HERE Travel Time integration.""" from __future__ import annotations -import logging - from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_API_KEY, CONF_MODE, Platform from homeassistant.core import HomeAssistant @@ -28,23 +26,14 @@ from .coordinator import ( from .model import HERETravelTimeConfig PLATFORMS = [Platform.SENSOR] -_LOGGER = logging.getLogger(__name__) async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: """Set up HERE Travel Time from a config entry.""" api_key = config_entry.data[CONF_API_KEY] - arrival = ( - dt.parse_time(config_entry.options[CONF_ARRIVAL_TIME]) - if config_entry.options[CONF_ARRIVAL_TIME] is not None - else None - ) - departure = ( - dt.parse_time(config_entry.options[CONF_DEPARTURE_TIME]) - if config_entry.options[CONF_DEPARTURE_TIME] is not None - else None - ) + arrival = dt.parse_time(config_entry.options.get(CONF_ARRIVAL_TIME, "")) + departure = dt.parse_time(config_entry.options.get(CONF_DEPARTURE_TIME, "")) here_travel_time_config = HERETravelTimeConfig( destination_latitude=config_entry.data.get(CONF_DESTINATION_LATITUDE), diff --git a/homeassistant/components/here_travel_time/coordinator.py b/homeassistant/components/here_travel_time/coordinator.py index 97759510d36..56430d79a22 100644 --- a/homeassistant/components/here_travel_time/coordinator.py +++ b/homeassistant/components/here_travel_time/coordinator.py @@ -3,14 +3,28 @@ from __future__ import annotations from datetime import datetime, time, timedelta import logging +from typing import Any import here_routing -from here_routing import HERERoutingApi, Return, RoutingMode, Spans, TransportMode +from here_routing import ( + HERERoutingApi, + HERERoutingTooManyRequestsError, + Return, + RoutingMode, + Spans, + TransportMode, +) import here_transit -from here_transit import HERETransitApi +from here_transit import ( + HERETransitApi, + HERETransitConnectionError, + HERETransitDepartureArrivalTooCloseError, + HERETransitNoRouteFoundError, + HERETransitTooManyRequestsError, +) import voluptuous as vol -from homeassistant.const import ATTR_ATTRIBUTION, UnitOfLength +from homeassistant.const import UnitOfLength from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv from homeassistant.helpers.location import find_coordinates @@ -18,20 +32,11 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda from homeassistant.util import dt from homeassistant.util.unit_conversion import DistanceConverter -from .const import ( - ATTR_DESTINATION, - ATTR_DESTINATION_NAME, - ATTR_DISTANCE, - ATTR_DURATION, - ATTR_DURATION_IN_TRAFFIC, - ATTR_ORIGIN, - ATTR_ORIGIN_NAME, - DEFAULT_SCAN_INTERVAL, - DOMAIN, - ROUTE_MODE_FASTEST, -) +from .const import DEFAULT_SCAN_INTERVAL, DOMAIN, ROUTE_MODE_FASTEST from .model import HERETravelTimeConfig, HERETravelTimeData +BACKOFF_MULTIPLIER = 1.1 + _LOGGER = logging.getLogger(__name__) @@ -67,7 +72,10 @@ class HERERoutingDataUpdateCoordinator(DataUpdateCoordinator): ) _LOGGER.debug( - "Requesting route for origin: %s, destination: %s, route_mode: %s, mode: %s, arrival: %s, departure: %s", + ( + "Requesting route for origin: %s, destination: %s, route_mode: %s," + " mode: %s, arrival: %s, departure: %s" + ), origin, destination, route_mode, @@ -76,25 +84,41 @@ class HERERoutingDataUpdateCoordinator(DataUpdateCoordinator): departure, ) - response = await self._api.route( - transport_mode=TransportMode(self.config.travel_mode), - origin=here_routing.Place(origin[0], origin[1]), - destination=here_routing.Place(destination[0], destination[1]), - routing_mode=route_mode, - arrival_time=arrival, - departure_time=departure, - return_values=[Return.POLYINE, Return.SUMMARY], - spans=[Spans.NAMES], - ) - + try: + response = await self._api.route( + transport_mode=TransportMode(self.config.travel_mode), + origin=here_routing.Place(origin[0], origin[1]), + destination=here_routing.Place(destination[0], destination[1]), + routing_mode=route_mode, + arrival_time=arrival, + departure_time=departure, + return_values=[Return.POLYINE, Return.SUMMARY], + spans=[Spans.NAMES], + ) + except HERERoutingTooManyRequestsError as error: + assert self.update_interval is not None + _LOGGER.debug( + "Rate limit has been reached. Increasing update interval to %s", + self.update_interval.total_seconds() * BACKOFF_MULTIPLIER, + ) + self.update_interval = timedelta( + seconds=self.update_interval.total_seconds() * BACKOFF_MULTIPLIER + ) + raise UpdateFailed("Rate limit has been reached") from error _LOGGER.debug("Raw response is: %s", response) + if self.update_interval != timedelta(seconds=DEFAULT_SCAN_INTERVAL): + _LOGGER.debug( + "Resetting update interval to %s", + DEFAULT_SCAN_INTERVAL, + ) + self.update_interval = timedelta(seconds=DEFAULT_SCAN_INTERVAL) return self._parse_routing_response(response) - def _parse_routing_response(self, response) -> HERETravelTimeData: + def _parse_routing_response(self, response: dict[str, Any]) -> HERETravelTimeData: """Parse the routing response dict to a HERETravelTimeData.""" - section: dict = response["routes"][0]["sections"][0] - summary: dict = section["summary"] + section: dict[str, Any] = response["routes"][0]["sections"][0] + summary: dict[str, int] = section["summary"] mapped_origin_lat: float = section["departure"]["place"]["location"]["lat"] mapped_origin_lon: float = section["departure"]["place"]["location"]["lng"] mapped_destination_lat: float = section["arrival"]["place"]["location"]["lat"] @@ -109,16 +133,14 @@ class HERERoutingDataUpdateCoordinator(DataUpdateCoordinator): if (names := section["spans"][-1].get("names")) is not None: destination_name = names[0]["value"] return HERETravelTimeData( - { - ATTR_ATTRIBUTION: None, - ATTR_DURATION: round(summary["baseDuration"] / 60), # type: ignore[misc] - ATTR_DURATION_IN_TRAFFIC: round(summary["duration"] / 60), - ATTR_DISTANCE: distance, - ATTR_ORIGIN: f"{mapped_origin_lat},{mapped_origin_lon}", - ATTR_DESTINATION: f"{mapped_destination_lat},{mapped_destination_lon}", - ATTR_ORIGIN_NAME: origin_name, - ATTR_DESTINATION_NAME: destination_name, - } + attribution=None, + duration=round(summary["baseDuration"] / 60), + duration_in_traffic=round(summary["duration"] / 60), + distance=distance, + origin=f"{mapped_origin_lat},{mapped_origin_lon}", + destination=f"{mapped_destination_lat},{mapped_destination_lon}", + origin_name=origin_name, + destination_name=destination_name, ) @@ -148,33 +170,56 @@ class HERETransitDataUpdateCoordinator(DataUpdateCoordinator): ) _LOGGER.debug( - "Requesting transit route for origin: %s, destination: %s, arrival: %s, departure: %s", + ( + "Requesting transit route for origin: %s, destination: %s, arrival: %s," + " departure: %s" + ), origin, destination, arrival, departure, ) - - response = await self._api.route( - origin=here_transit.Place(latitude=origin[0], longitude=origin[1]), - destination=here_transit.Place( - latitude=destination[0], longitude=destination[1] - ), - arrival_time=arrival, - departure_time=departure, - return_values=[ - here_transit.Return.POLYLINE, - here_transit.Return.TRAVEL_SUMMARY, - ], - ) + try: + response = await self._api.route( + origin=here_transit.Place(latitude=origin[0], longitude=origin[1]), + destination=here_transit.Place( + latitude=destination[0], longitude=destination[1] + ), + arrival_time=arrival, + departure_time=departure, + return_values=[ + here_transit.Return.POLYLINE, + here_transit.Return.TRAVEL_SUMMARY, + ], + ) + except HERETransitTooManyRequestsError as error: + assert self.update_interval is not None + _LOGGER.debug( + "Rate limit has been reached. Increasing update interval to %s", + self.update_interval.total_seconds() * BACKOFF_MULTIPLIER, + ) + self.update_interval = timedelta( + seconds=self.update_interval.total_seconds() * BACKOFF_MULTIPLIER + ) + raise UpdateFailed("Rate limit has been reached") from error + except HERETransitDepartureArrivalTooCloseError: + _LOGGER.debug("Ignoring HERETransitDepartureArrivalTooCloseError") + return None + except (HERETransitConnectionError, HERETransitNoRouteFoundError) as error: + raise UpdateFailed from error _LOGGER.debug("Raw response is: %s", response) - + if self.update_interval != timedelta(seconds=DEFAULT_SCAN_INTERVAL): + _LOGGER.debug( + "Resetting update interval to %s", + DEFAULT_SCAN_INTERVAL, + ) + self.update_interval = timedelta(seconds=DEFAULT_SCAN_INTERVAL) return self._parse_transit_response(response) - def _parse_transit_response(self, response) -> HERETravelTimeData: + def _parse_transit_response(self, response: dict[str, Any]) -> HERETravelTimeData: """Parse the transit response dict to a HERETravelTimeData.""" - sections: dict = response["routes"][0]["sections"] + sections: list[dict[str, Any]] = response["routes"][0]["sections"] attribution: str | None = build_hass_attribution(sections) mapped_origin_lat: float = sections[0]["departure"]["place"]["location"]["lat"] mapped_origin_lon: float = sections[0]["departure"]["place"]["location"]["lng"] @@ -193,16 +238,14 @@ class HERETransitDataUpdateCoordinator(DataUpdateCoordinator): section["travelSummary"]["duration"] for section in sections ) return HERETravelTimeData( - { - ATTR_ATTRIBUTION: attribution, - ATTR_DURATION: round(duration / 60), # type: ignore[misc] - ATTR_DURATION_IN_TRAFFIC: round(duration / 60), - ATTR_DISTANCE: distance, - ATTR_ORIGIN: f"{mapped_origin_lat},{mapped_origin_lon}", - ATTR_DESTINATION: f"{mapped_destination_lat},{mapped_destination_lon}", - ATTR_ORIGIN_NAME: sections[0]["departure"]["place"].get("name"), - ATTR_DESTINATION_NAME: sections[-1]["arrival"]["place"].get("name"), - } + attribution=attribution, + duration=round(duration / 60), + duration_in_traffic=round(duration / 60), + distance=distance, + origin=f"{mapped_origin_lat},{mapped_origin_lon}", + destination=f"{mapped_destination_lat},{mapped_destination_lon}", + origin_name=sections[0]["departure"]["place"].get("name"), + destination_name=sections[-1]["arrival"]["place"].get("name"), ) @@ -256,7 +299,7 @@ def prepare_parameters( return (origin, destination, arrival, departure) -def build_hass_attribution(sections: dict) -> str | None: +def build_hass_attribution(sections: list[dict[str, Any]]) -> str | None: """Build a hass frontend ready string out of the attributions.""" relevant_attributions = [] for section in sections: diff --git a/homeassistant/components/here_travel_time/manifest.json b/homeassistant/components/here_travel_time/manifest.json index 8efcf29b6b0..5f46c344af8 100644 --- a/homeassistant/components/here_travel_time/manifest.json +++ b/homeassistant/components/here_travel_time/manifest.json @@ -3,8 +3,8 @@ "name": "HERE Travel Time", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/here_travel_time", - "requirements": ["here_routing==0.1.1", "here_transit==1.0.0"], + "requirements": ["here_routing==0.2.0", "here_transit==1.2.0"], "codeowners": ["@eifinger"], "iot_class": "cloud_polling", - "loggers": ["here_routing", "here_transit"] + "loggers": ["here_routing", "here_transit", "homeassistant.helpers.location"] } diff --git a/homeassistant/components/here_travel_time/model.py b/homeassistant/components/here_travel_time/model.py index 6d8d4826007..433653cbf56 100644 --- a/homeassistant/components/here_travel_time/model.py +++ b/homeassistant/components/here_travel_time/model.py @@ -9,14 +9,14 @@ from typing import TypedDict class HERETravelTimeData(TypedDict): """Routing information.""" - ATTR_ATTRIBUTION: str | None - ATTR_DURATION: float - ATTR_DURATION_IN_TRAFFIC: float - ATTR_DISTANCE: float - ATTR_ORIGIN: str - ATTR_DESTINATION: str - ATTR_ORIGIN_NAME: str - ATTR_DESTINATION_NAME: str + attribution: str | None + duration: float + duration_in_traffic: float + distance: float + origin: str + destination: str + origin_name: str | None + destination_name: str | None @dataclass diff --git a/homeassistant/components/here_travel_time/sensor.py b/homeassistant/components/here_travel_time/sensor.py index 7e1c93bcfb5..445efb92dcf 100644 --- a/homeassistant/components/here_travel_time/sensor.py +++ b/homeassistant/components/here_travel_time/sensor.py @@ -18,8 +18,8 @@ from homeassistant.const import ( ATTR_LONGITUDE, CONF_MODE, CONF_NAME, - TIME_MINUTES, UnitOfLength, + UnitOfTime, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.device_registry import DeviceEntryType @@ -40,7 +40,10 @@ from .const import ( ICON_CAR, ICONS, ) -from .coordinator import HERERoutingDataUpdateCoordinator +from .coordinator import ( + HERERoutingDataUpdateCoordinator, + HERETransitDataUpdateCoordinator, +) SCAN_INTERVAL = timedelta(minutes=5) @@ -53,14 +56,14 @@ def sensor_descriptions(travel_mode: str) -> tuple[SensorEntityDescription, ...] icon=ICONS.get(travel_mode, ICON_CAR), key=ATTR_DURATION, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, ), SensorEntityDescription( name="Duration in traffic", icon=ICONS.get(travel_mode, ICON_CAR), key=ATTR_DURATION_IN_TRAFFIC, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, ), SensorEntityDescription( name="Distance", @@ -107,7 +110,8 @@ class HERETravelTimeSensor(CoordinatorEntity, RestoreSensor): unique_id_prefix: str, name: str, sensor_description: SensorEntityDescription, - coordinator: HERERoutingDataUpdateCoordinator, + coordinator: HERERoutingDataUpdateCoordinator + | HERETransitDataUpdateCoordinator, ) -> None: """Initialize the sensor.""" super().__init__(coordinator) @@ -131,7 +135,7 @@ class HERETravelTimeSensor(CoordinatorEntity, RestoreSensor): await self._async_restore_state() await super().async_added_to_hass() - async def _update_at_start(_): + async def _update_at_start(_: HomeAssistant) -> None: await self.async_update() self.async_on_remove(async_at_started(self.hass, _update_at_start)) @@ -149,7 +153,8 @@ class HERETravelTimeSensor(CoordinatorEntity, RestoreSensor): def attribution(self) -> str | None: """Return the attribution.""" if self.coordinator.data is not None: - return self.coordinator.data.get(ATTR_ATTRIBUTION) + if (attribution := self.coordinator.data.get(ATTR_ATTRIBUTION)) is not None: + return str(attribution) return None diff --git a/homeassistant/components/here_travel_time/translations/ko.json b/homeassistant/components/here_travel_time/translations/ko.json new file mode 100644 index 00000000000..40ed872d4ef --- /dev/null +++ b/homeassistant/components/here_travel_time/translations/ko.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "api_key": "API \ud0a4", + "name": "\uc774\ub984" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/here_travel_time/translations/sk.json b/homeassistant/components/here_travel_time/translations/sk.json index 7bb5d95a7e5..287fccd62d4 100644 --- a/homeassistant/components/here_travel_time/translations/sk.json +++ b/homeassistant/components/here_travel_time/translations/sk.json @@ -8,12 +8,48 @@ "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { + "destination_coordinates": { + "data": { + "destination": "Cie\u013e ako s\u00faradnice GPS" + }, + "title": "Vyberte umiestnenie" + }, "destination_entity_id": { + "data": { + "destination_entity_id": "Cie\u013e pomocou entity" + }, "title": "Vyberte cie\u013e" }, + "destination_menu": { + "menu_options": { + "destination_coordinates": "Pou\u017eitie polohy na mape", + "destination_entity": "Pou\u017eitie entity" + }, + "title": "Vyberte umiestnenie" + }, + "origin_coordinates": { + "data": { + "origin": "P\u00f4vod ako s\u00faradnice GPS" + }, + "title": "Vyberte v\u00fdchodzie miesto" + }, + "origin_entity_id": { + "data": { + "origin_entity_id": "V\u00fdchodzie miesto pomocou entity" + }, + "title": "Vyberte v\u00fdchodzie miesto" + }, + "origin_menu": { + "menu_options": { + "origin_coordinates": "Pou\u017eitie polohy na mape", + "origin_entity": "Pou\u017eitie entity" + }, + "title": "Vyberte v\u00fdchodzie miesto" + }, "user": { "data": { "api_key": "API k\u013e\u00fa\u010d", + "mode": "Re\u017eim cestovania", "name": "N\u00e1zov" } } @@ -33,10 +69,20 @@ }, "title": "V\u00fdber \u010dasu odchodu" }, + "init": { + "data": { + "route_mode": "Re\u017eim trasy", + "traffic_mode": "Re\u017eim prem\u00e1vky", + "unit_system": "Jednotky" + } + }, "time_menu": { "menu_options": { + "arrival_time": "Nakonfigurujte \u010das pr\u00edchodu", + "departure_time": "Nakonfigurujte \u010das odchodu", "no_time": "Nekonfigurujte \u010das" - } + }, + "title": "V\u00fdber typu \u010dasu" } } } diff --git a/homeassistant/components/hisense_aehw4a1/climate.py b/homeassistant/components/hisense_aehw4a1/climate.py index fc956ec8760..113a0c622b9 100644 --- a/homeassistant/components/hisense_aehw4a1/climate.py +++ b/homeassistant/components/hisense_aehw4a1/climate.py @@ -25,12 +25,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - ATTR_TEMPERATURE, - PRECISION_WHOLE, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, -) +from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -184,9 +179,9 @@ class ClimateAehW4a1(ClimateEntity): self._on = status["run_status"] if status["temperature_Fahrenheit"] == "0": - self._attr_temperature_unit = TEMP_CELSIUS + self._attr_temperature_unit = UnitOfTemperature.CELSIUS else: - self._attr_temperature_unit = TEMP_FAHRENHEIT + self._attr_temperature_unit = UnitOfTemperature.FAHRENHEIT self._current_temperature = int(status["indoor_temperature_status"], 2) @@ -274,14 +269,14 @@ class ClimateAehW4a1(ClimateEntity): @property def min_temp(self): """Return the minimum temperature.""" - if self.temperature_unit == TEMP_CELSIUS: + if self.temperature_unit == UnitOfTemperature.CELSIUS: return MIN_TEMP_C return MIN_TEMP_F @property def max_temp(self): """Return the maximum temperature.""" - if self.temperature_unit == TEMP_CELSIUS: + if self.temperature_unit == UnitOfTemperature.CELSIUS: return MAX_TEMP_C return MAX_TEMP_F @@ -301,7 +296,7 @@ class ClimateAehW4a1(ClimateEntity): _LOGGER.debug("Setting temp of %s to %s", self._unique_id, temp) if self._preset_mode != PRESET_NONE: await self.async_set_preset_mode(PRESET_NONE) - if self.temperature_unit == TEMP_CELSIUS: + if self.temperature_unit == UnitOfTemperature.CELSIUS: await self._device.command(f"temp_{int(temp)}_C") else: await self._device.command(f"temp_{int(temp)}_F") diff --git a/homeassistant/components/hisense_aehw4a1/translations/sk.json b/homeassistant/components/hisense_aehw4a1/translations/sk.json index 99798036ffd..96e68d5ef50 100644 --- a/homeassistant/components/hisense_aehw4a1/translations/sk.json +++ b/homeassistant/components/hisense_aehw4a1/translations/sk.json @@ -3,6 +3,11 @@ "abort": { "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, + "step": { + "confirm": { + "description": "Chcete nastavi\u0165 Hisense AEH-W4A1?" + } } } } \ No newline at end of file diff --git a/homeassistant/components/history_stats/sensor.py b/homeassistant/components/history_stats/sensor.py index 642c327e29d..fc3aedfde24 100644 --- a/homeassistant/components/history_stats/sensor.py +++ b/homeassistant/components/history_stats/sensor.py @@ -18,7 +18,7 @@ from homeassistant.const import ( CONF_STATE, CONF_TYPE, PERCENTAGE, - TIME_HOURS, + UnitOfTime, ) from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import PlatformNotReady @@ -46,7 +46,7 @@ CONF_TYPE_KEYS = [CONF_TYPE_TIME, CONF_TYPE_RATIO, CONF_TYPE_COUNT] DEFAULT_NAME = "unnamed statistics" UNITS: dict[str, str] = { - CONF_TYPE_TIME: TIME_HOURS, + CONF_TYPE_TIME: UnitOfTime.HOURS, CONF_TYPE_RATIO: PERCENTAGE, CONF_TYPE_COUNT: "", } diff --git a/homeassistant/components/hive/climate.py b/homeassistant/components/hive/climate.py index 620d679fe1c..95304371e79 100644 --- a/homeassistant/components/hive/climate.py +++ b/homeassistant/components/hive/climate.py @@ -14,7 +14,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS, TEMP_FAHRENHEIT +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers import config_validation as cv, entity_platform from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -45,7 +45,7 @@ HIVE_TO_HASS_HVAC_ACTION = { True: HVACAction.HEATING, } -TEMP_UNIT = {"C": TEMP_CELSIUS, "F": TEMP_FAHRENHEIT} +TEMP_UNIT = {"C": UnitOfTemperature.CELSIUS, "F": UnitOfTemperature.FAHRENHEIT} PARALLEL_UPDATES = 0 SCAN_INTERVAL = timedelta(seconds=15) _LOGGER = logging.getLogger() @@ -140,7 +140,8 @@ class HiveClimateEntity(HiveEntity, ClimateEntity): async def async_heating_boost(self, time_period, temperature): """Handle boost heating service call.""" _LOGGER.warning( - "Hive Service heating_boost will be removed in 2021.7.0, please update to heating_boost_on" + "Hive Service heating_boost will be removed in 2021.7.0, please update to" + " heating_boost_on" ) await self.async_heating_boost_on(time_period, temperature) diff --git a/homeassistant/components/hive/sensor.py b/homeassistant/components/hive/sensor.py index 0cc222c27c3..ed742e4bdad 100644 --- a/homeassistant/components/hive/sensor.py +++ b/homeassistant/components/hive/sensor.py @@ -8,7 +8,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import PERCENTAGE, POWER_WATT +from homeassistant.const import PERCENTAGE, UnitOfPower from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -28,7 +28,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( ), SensorEntityDescription( key="Power", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, state_class=SensorStateClass.MEASUREMENT, device_class=SensorDeviceClass.POWER, entity_category=EntityCategory.DIAGNOSTIC, diff --git a/homeassistant/components/hive/translations/sk.json b/homeassistant/components/hive/translations/sk.json index 18d0c4e60db..be7f7c7badf 100644 --- a/homeassistant/components/hive/translations/sk.json +++ b/homeassistant/components/hive/translations/sk.json @@ -6,6 +6,8 @@ "unknown_entry": "Nie je mo\u017en\u00e9 n\u00e1js\u0165 existuj\u00faci z\u00e1znam." }, "error": { + "invalid_code": "Nepodarilo sa prihl\u00e1si\u0165 do Hive. V\u00e1\u0161 dvojfaktorov\u00fd overovac\u00ed k\u00f3d bol nespr\u00e1vny.", + "invalid_password": "Nepodarilo sa prihl\u00e1si\u0165 do Hive. Nespr\u00e1vne heslo, sk\u00faste to znova.", "invalid_username": "Nepodarilo sa prihl\u00e1si\u0165 do syst\u00e9mu Hive. Va\u0161a e-mailov\u00e1 adresa nie je rozpoznan\u00e1.", "no_internet_available": "Na pripojenie k Hive je potrebn\u00e9 internetov\u00e9 pripojenie.", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" @@ -50,6 +52,7 @@ "data": { "scan_interval": "Interval skenovania (sekundy)" }, + "description": "Aktualizujte interval skenovania, aby sa \u00fadaje vyh\u013ead\u00e1vali \u010dastej\u0161ie.", "title": "Mo\u017enosti pre Hive" } } diff --git a/homeassistant/components/hive/water_heater.py b/homeassistant/components/hive/water_heater.py index 1860d7b092e..2dbb6cb0230 100644 --- a/homeassistant/components/hive/water_heater.py +++ b/homeassistant/components/hive/water_heater.py @@ -9,7 +9,7 @@ from homeassistant.components.water_heater import ( WaterHeaterEntityFeature, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import STATE_OFF, STATE_ON, TEMP_CELSIUS +from homeassistant.const import STATE_OFF, STATE_ON, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers import config_validation as cv, entity_platform from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -74,7 +74,7 @@ class HiveWaterHeater(HiveEntity, WaterHeaterEntity): """Hive Water Heater Device.""" _attr_supported_features = WaterHeaterEntityFeature.OPERATION_MODE - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS _attr_operation_list = SUPPORT_WATER_HEATER @refresh_system diff --git a/homeassistant/components/hlk_sw16/translations/pt.json b/homeassistant/components/hlk_sw16/translations/pt.json index 561c8d77287..7e727215891 100644 --- a/homeassistant/components/hlk_sw16/translations/pt.json +++ b/homeassistant/components/hlk_sw16/translations/pt.json @@ -4,14 +4,14 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, "step": { "user": { "data": { - "host": "Servidor", + "host": "Endere\u00e7o", "password": "Palavra-passe", "username": "Nome de Utilizador" } diff --git a/homeassistant/components/home_connect/api.py b/homeassistant/components/home_connect/api.py index f3c98e618b8..f50ab711550 100644 --- a/homeassistant/components/home_connect/api.py +++ b/homeassistant/components/home_connect/api.py @@ -15,7 +15,7 @@ from homeassistant.const import ( CONF_DEVICE, CONF_ENTITIES, PERCENTAGE, - TIME_SECONDS, + UnitOfTime, ) from homeassistant.helpers import config_entry_oauth2_flow from homeassistant.helpers.dispatcher import dispatcher_send @@ -167,7 +167,7 @@ class DeviceWithPrograms(HomeConnectDevice): """ sensors = { "Remaining Program Time": (None, None, SensorDeviceClass.TIMESTAMP, 1), - "Duration": (TIME_SECONDS, "mdi:update", None, 1), + "Duration": (UnitOfTime.SECONDS, "mdi:update", None, 1), "Program Progress": (PERCENTAGE, "mdi:progress-clock", None, 1), } return [ diff --git a/homeassistant/components/home_connect/translations/pt.json b/homeassistant/components/home_connect/translations/pt.json index eb27f259531..258d455f92d 100644 --- a/homeassistant/components/home_connect/translations/pt.json +++ b/homeassistant/components/home_connect/translations/pt.json @@ -2,7 +2,7 @@ "config": { "abort": { "missing_configuration": "O componente n\u00e3o est\u00e1 configurado. Por favor, siga a documenta\u00e7\u00e3o.", - "no_url_available": "Nenhum URL dispon\u00edvel. Para obter informa\u00e7\u00f5es sobre esse erro, [verifique a sec\u00e7\u00e3o de ajuda]({docs_url})" + "no_url_available": "Nenhum URL est\u00e1 dispon\u00edvel. Para obter informa\u00e7\u00f5es sobre esse erro, [consulte a sec\u00e7\u00e3o de ajuda]({docs_url})" }, "create_entry": { "default": "Autenticado com sucesso" diff --git a/homeassistant/components/home_plus_control/translations/en_GB.json b/homeassistant/components/home_plus_control/translations/en-GB.json similarity index 100% rename from homeassistant/components/home_plus_control/translations/en_GB.json rename to homeassistant/components/home_plus_control/translations/en-GB.json diff --git a/homeassistant/components/home_plus_control/translations/pt.json b/homeassistant/components/home_plus_control/translations/pt.json index 2a1a4a7174d..79054bc5a10 100644 --- a/homeassistant/components/home_plus_control/translations/pt.json +++ b/homeassistant/components/home_plus_control/translations/pt.json @@ -3,9 +3,9 @@ "abort": { "already_configured": "Conta j\u00e1 configurada", "already_in_progress": "O processo de configura\u00e7\u00e3o j\u00e1 est\u00e1 a decorrer", - "authorize_url_timeout": "Tempo excedido a gerar um URL de autoriza\u00e7\u00e3o", + "authorize_url_timeout": "Excedido o tempo limite para gerar um URL de autoriza\u00e7\u00e3o", "missing_configuration": "O componente n\u00e3o est\u00e1 configurado. Por favor, siga a documenta\u00e7\u00e3o.", - "no_url_available": "Nenhum URL dispon\u00edvel. Para obter informa\u00e7\u00f5es sobre esse erro, [verifique a sec\u00e7\u00e3o de ajuda]({docs_url})", + "no_url_available": "Nenhum URL est\u00e1 dispon\u00edvel. Para obter informa\u00e7\u00f5es sobre esse erro, [consulte a sec\u00e7\u00e3o de ajuda]({docs_url})", "single_instance_allowed": "J\u00e1 configurado. Apenas uma \u00fanica configura\u00e7\u00e3o \u00e9 poss\u00edvel." }, "create_entry": { diff --git a/homeassistant/components/homeassistant/translations/ca.json b/homeassistant/components/homeassistant/translations/ca.json index 3272c2fcbad..88969df99ba 100644 --- a/homeassistant/components/homeassistant/translations/ca.json +++ b/homeassistant/components/homeassistant/translations/ca.json @@ -9,6 +9,7 @@ "title": "El valor de moneda configurat ja no s'utilitza" }, "python_version": { + "description": "La versi\u00f3 actual de Python {current_python_version} est\u00e0 obsoleta per funcionar amb Home Assistant i s'eliminar\u00e0 a Home Assistant {breaks_in_ha_version}. Si us plau, actualitza Python a la versi\u00f3 {required_python_version} per evitar que la teva inst\u00e0ncia de Home Assistant s'espatlli.", "title": "S'est\u00e0 eliminant la compatibilitat amb Python {current_python_version}" } }, diff --git a/homeassistant/components/homeassistant/translations/el.json b/homeassistant/components/homeassistant/translations/el.json index 7fa0d674233..a4dd672caf3 100644 --- a/homeassistant/components/homeassistant/translations/el.json +++ b/homeassistant/components/homeassistant/translations/el.json @@ -1,5 +1,9 @@ { "issues": { + "country_not_configured": { + "description": "\u0394\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af \u03ba\u03b1\u03bc\u03af\u03b1 \u03c7\u03ce\u03c1\u03b1, \u03b5\u03bd\u03b7\u03bc\u03b5\u03c1\u03ce\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03ba\u03ac\u03bd\u03bf\u03bd\u03c4\u03b1\u03c2 \u03ba\u03bb\u03b9\u03ba \u03c3\u03c4\u03bf \u03ba\u03bf\u03c5\u03bc\u03c0\u03af \"\u03bc\u03ac\u03b8\u03b5\u03c4\u03b5 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b1\" \u03c0\u03b1\u03c1\u03b1\u03ba\u03ac\u03c4\u03c9.", + "title": "\u0397 \u03c7\u03ce\u03c1\u03b1 \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af" + }, "historic_currency": { "description": "\u03a4\u03bf \u03bd\u03cc\u03bc\u03b9\u03c3\u03bc\u03b1 {currency} \u03b4\u03b5\u03bd \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03c0\u03bb\u03ad\u03bf\u03bd, \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03ce\u03c3\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03bd\u03bf\u03bc\u03af\u03c3\u03bc\u03b1\u03c4\u03bf\u03c2.", "title": "\u03a4\u03bf \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03bc\u03ad\u03bd\u03bf \u03bd\u03cc\u03bc\u03b9\u03c3\u03bc\u03b1 \u03b4\u03b5\u03bd \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03c0\u03bb\u03ad\u03bf\u03bd" diff --git a/homeassistant/components/homeassistant/translations/es.json b/homeassistant/components/homeassistant/translations/es.json index e7fe09ac9b9..e47fedf0876 100644 --- a/homeassistant/components/homeassistant/translations/es.json +++ b/homeassistant/components/homeassistant/translations/es.json @@ -1,7 +1,7 @@ { "issues": { "country_not_configured": { - "description": "No se ha configurado ning\u00fan pa\u00eds, por favor, actualiza la configuraci\u00f3n haciendo clic en el bot\u00f3n \"m\u00e1s informaci\u00f3n\" a continuaci\u00f3n.", + "description": "No se ha configurado ning\u00fan pa\u00eds, por favor, actualiza la configuraci\u00f3n haciendo clic en el bot\u00f3n \"Saber m\u00e1s\" a continuaci\u00f3n.", "title": "El pa\u00eds no ha sido configurado" }, "historic_currency": { diff --git a/homeassistant/components/homeassistant/translations/he.json b/homeassistant/components/homeassistant/translations/he.json index 75f487a964e..a5247af9811 100644 --- a/homeassistant/components/homeassistant/translations/he.json +++ b/homeassistant/components/homeassistant/translations/he.json @@ -1,4 +1,18 @@ { + "issues": { + "country_not_configured": { + "description": "\u05d0\u05e3 \u05de\u05d3\u05d9\u05e0\u05d4 \u05dc\u05d0 \u05d4\u05d5\u05d2\u05d3\u05e8\u05d4, \u05e0\u05d0 \u05dc\u05e2\u05d3\u05db\u05df \u05d0\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05e2\u05dc \u05d9\u05d3\u05d9 \u05dc\u05d7\u05d9\u05e6\u05d4 \u05e2\u05dc \u05db\u05e4\u05ea\u05d5\u05e8 \"\u05dc\u05de\u05d3 \u05e2\u05d5\u05d3\" \u05dc\u05de\u05d8\u05d4.", + "title": "\u05d4\u05de\u05d3\u05d9\u05e0\u05d4 \u05dc\u05d0 \u05d4\u05d5\u05d2\u05d3\u05e8\u05d4" + }, + "historic_currency": { + "description": "\u05d4\u05de\u05d8\u05d1\u05e2 {currency} \u05d0\u05d9\u05e0\u05d5 \u05d1\u05e9\u05d9\u05de\u05d5\u05e9 \u05e2\u05d5\u05d3, \u05e0\u05d0 \u05dc\u05d4\u05d2\u05d3\u05d9\u05e8 \u05de\u05d7\u05d3\u05e9 \u05d0\u05ea \u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05de\u05d8\u05d1\u05e2.", + "title": "\u05d4\u05de\u05d8\u05d1\u05e2 \u05e9\u05ea\u05e6\u05d5\u05e8\u05ea\u05d5 \u05e0\u05e7\u05d1\u05e2\u05d4 \u05d0\u05d9\u05e0\u05d5 \u05d1\u05e9\u05d9\u05de\u05d5\u05e9 \u05e2\u05d5\u05d3" + }, + "python_version": { + "description": "\u05d4\u05ea\u05de\u05d9\u05db\u05d4 \u05d1\u05d4\u05e4\u05e2\u05dc\u05ea Home Assistant \u05d1\u05d2\u05d9\u05e8\u05e1\u05ea Python \u05d4\u05e0\u05d5\u05db\u05d7\u05d9\u05ea \u05d4\u05e0\u05de\u05e6\u05d0\u05ea \u05d1\u05e9\u05d9\u05de\u05d5\u05e9 {current_python_version} \u05d4\u05d5\u05e6\u05d0\u05d4 \u05de\u05e9\u05d9\u05de\u05d5\u05e9 \u05d5\u05ea\u05d5\u05e1\u05e8 \u05d1-Home Assistant {breaks_in_ha_version}. \u05d9\u05e9 \u05dc\u05e9\u05d3\u05e8\u05d2 \u05d0\u05ea Python \u05dc- {required_python_version} \u05db\u05d3\u05d9 \u05dc\u05de\u05e0\u05d5\u05e2 \u05de\u05de\u05d5\u05e4\u05e2 \u05d4-Home Assistant \u05e9\u05dc\u05da \u05dc\u05d4\u05d9\u05e9\u05d1\u05e8.", + "title": "\u05d4\u05ea\u05de\u05d9\u05db\u05d4 \u05e2\u05d1\u05d5\u05e8 Python {current_python_version} \u05de\u05d5\u05e1\u05e8\u05ea" + } + }, "system_health": { "info": { "arch": "\u05d0\u05e8\u05db\u05d9\u05d8\u05e7\u05d8\u05d5\u05e8\u05ea \u05de\u05e2\u05d1\u05d3", diff --git a/homeassistant/components/homeassistant/translations/hu.json b/homeassistant/components/homeassistant/translations/hu.json index 1e290293009..3b89dc26fbc 100644 --- a/homeassistant/components/homeassistant/translations/hu.json +++ b/homeassistant/components/homeassistant/translations/hu.json @@ -1,5 +1,9 @@ { "issues": { + "country_not_configured": { + "description": "Az orsz\u00e1g nincs be\u00e1ll\u00edtva. K\u00e9rem, friss\u00edtse a konfigur\u00e1ci\u00f3t az al\u00e1bbi gombra kattintva.", + "title": "Az orsz\u00e1g nincs konfigur\u00e1lva" + }, "historic_currency": { "description": "{currency} p\u00e9nznem m\u00e1r nincs haszn\u00e1latban, k\u00e9rj\u00fck, konfigur\u00e1lja \u00fajra a p\u00e9nznemet.", "title": "A be\u00e1ll\u00edtott p\u00e9nznem m\u00e1r nincs haszn\u00e1latban" diff --git a/homeassistant/components/homeassistant/translations/it.json b/homeassistant/components/homeassistant/translations/it.json index 164f7e90c95..e330a49803f 100644 --- a/homeassistant/components/homeassistant/translations/it.json +++ b/homeassistant/components/homeassistant/translations/it.json @@ -1,5 +1,9 @@ { "issues": { + "country_not_configured": { + "description": "Nessuna nazione \u00e8 stata configurata, aggiornare la configurazione facendo clic sul pulsante \"Ulteriori informazioni\" in basso.", + "title": "La nazione non \u00e8 stata configurata" + }, "historic_currency": { "description": "La valuta {currency} non \u00e8 pi\u00f9 in uso, riconfigura la configurazione della valuta.", "title": "La valuta configurata non \u00e8 pi\u00f9 in uso" diff --git a/homeassistant/components/homeassistant/translations/ko.json b/homeassistant/components/homeassistant/translations/ko.json index 1a9b1aef5c8..4a166013cd8 100644 --- a/homeassistant/components/homeassistant/translations/ko.json +++ b/homeassistant/components/homeassistant/translations/ko.json @@ -1,4 +1,14 @@ { + "issues": { + "country_not_configured": { + "description": "\uc124\uc815\ub41c \uad6d\uac00\uac00 \uc5c6\uc2b5\ub2c8\ub2e4. \uc544\ub798\uc758 \"\uc790\uc138\ud788 \uc54c\uc544\ubcf4\uae30\" \ubc84\ud2bc\uc744 \ud074\ub9ad\ud558\uc5ec \uc124\uc815\uc744 \uc5c5\ub370\uc774\ud2b8\ud558\uc2ed\uc2dc\uc624.", + "title": "\uad6d\uac00\uac00 \uc124\uc815\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4." + }, + "python_version": { + "description": "\ud604\uc7ac \uc0ac\uc6a9\ub418\ub294 Python {current_python_version} \uc740 Home Assistant {breaks_in_ha_version} \uc5d0\uc11c \uc9c0\uc6d0\uc774 \uc911\ub2e8\ub429\ub2c8\ub2e4. Home Assistant \uc778\uc2a4\ud134\uc2a4\uac00 \uc911\ub2e8\ub418\uc9c0 \uc54a\ub3c4\ub85d Python {required_python_version} \uc73c\ub85c \uc5c5\uadf8\ub808\uc774\ub4dc\ud558\uc2ed\uc2dc\uc624.", + "title": "Python {current_python_version} \uc744 \ub354\uc774\uc0c1 \uc9c0\uc6d0\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4." + } + }, "system_health": { "info": { "arch": "CPU \uc544\ud0a4\ud14d\ucc98", diff --git a/homeassistant/components/homeassistant/translations/lv.json b/homeassistant/components/homeassistant/translations/lv.json new file mode 100644 index 00000000000..05e0afa3ca1 --- /dev/null +++ b/homeassistant/components/homeassistant/translations/lv.json @@ -0,0 +1,16 @@ +{ + "issues": { + "country_not_configured": { + "description": "Valsts nav konfigur\u0113ta. L\u016bdzu, atjauniniet konfigur\u0101ciju, noklik\u0161\u0137inot uz \"Uzzin\u0101t vair\u0101k\" pogas zem\u0101k.", + "title": "Valsts nav konfigur\u0113ta" + }, + "historic_currency": { + "description": "Val\u016bta {currency} vairs netiek lietota. L\u016bdzu, atjauniniet val\u016btas konfigur\u0101ciju.", + "title": "Konfigur\u0113t\u0101 val\u016bta vairs netiek izmantota" + }, + "python_version": { + "description": "Atbalsts Home Assistant darbin\u0101\u0161anai ar pa\u0161reiz izmantoto Python versiju {current_python_version} ir novecojis un tiks no\u0146emts Home Assistant {breaks_in_ha_version}. L\u016bdzu, atjaunojiet Python uz {required_python_version}, lai nov\u0113rstu probl\u0113mas Home Assistant darb\u012bb\u0101.", + "title": "Python {current_python_version} atbalsts tiek no\u0146emts" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant/translations/pl.json b/homeassistant/components/homeassistant/translations/pl.json index 02383146b53..b9d66d282bf 100644 --- a/homeassistant/components/homeassistant/translations/pl.json +++ b/homeassistant/components/homeassistant/translations/pl.json @@ -1,5 +1,9 @@ { "issues": { + "country_not_configured": { + "description": "\u017baden kraj nie zosta\u0142 skonfigurowany, zaktualizuj konfiguracj\u0119, klikaj\u0105c przycisk \u201eDowiedz si\u0119 wi\u0119cej\u201d poni\u017cej.", + "title": "Kraj nie zosta\u0142 skonfigurowany" + }, "historic_currency": { "description": "Waluta {currency} nie jest ju\u017c u\u017cywana. Zmie\u0144 konfiguracj\u0119 waluty.", "title": "Skonfigurowana waluta nie jest ju\u017c u\u017cywana" diff --git a/homeassistant/components/homeassistant/translations/ru.json b/homeassistant/components/homeassistant/translations/ru.json index d368a980a56..c8d879a0851 100644 --- a/homeassistant/components/homeassistant/translations/ru.json +++ b/homeassistant/components/homeassistant/translations/ru.json @@ -1,7 +1,7 @@ { "issues": { "country_not_configured": { - "description": "\u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u0441\u0442\u0440\u0430\u043d\u0435 \u043d\u0435 \u0443\u043a\u0430\u0437\u0430\u043d\u0430. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e, \u043d\u0430\u0436\u0430\u0432 \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 \"\u0423\u0437\u043d\u0430\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u0435\".", + "description": "\u041d\u0430\u0436\u043c\u0438\u0442\u0435 \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 \"\u0423\u0437\u043d\u0430\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u0435\", \u043d\u0430 \u043e\u0442\u043a\u0440\u044b\u0432\u0448\u0435\u0439\u0441\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u0442\u0440\u0430\u043d\u0443 \u0438\u0437 \u0441\u043f\u0438\u0441\u043a\u0430.", "title": "\u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u0441\u0442\u0440\u0430\u043d\u0435 \u043d\u0435 \u0443\u043a\u0430\u0437\u0430\u043d\u0430" }, "historic_currency": { diff --git a/homeassistant/components/homeassistant/translations/sk.json b/homeassistant/components/homeassistant/translations/sk.json index 59d0db98bb1..05810dcf8f2 100644 --- a/homeassistant/components/homeassistant/translations/sk.json +++ b/homeassistant/components/homeassistant/translations/sk.json @@ -5,9 +5,11 @@ "title": "Krajina nebola nakonfigurovan\u00e1" }, "historic_currency": { + "description": "Mena {currency} sa u\u017e nepou\u017e\u00edva. Zme\u0148te konfigur\u00e1ciu meny.", "title": "Konfigurovan\u00e1 mena sa u\u017e nepou\u017e\u00edva" }, "python_version": { + "description": "Podpora pre spustenie Home Assistant v aktu\u00e1lne pou\u017e\u00edvanej verzii Pythonu {current_python_version} je zastaran\u00e1 a bude odstr\u00e1nen\u00e1 v Home Assistant {breaks_in_ha_version}. Inovujte Python na {required_python_version}, aby ste zabr\u00e1nili naru\u0161eniu va\u0161ej in\u0161tancie Home Assistant.", "title": "Podpora pre Python {current_python_version} sa odstra\u0148uje" } }, @@ -15,14 +17,17 @@ "info": { "arch": "Architekt\u00fara CPU", "config_dir": "Adres\u00e1r konfigur\u00e1cie", + "dev": "V\u00fdvoj", "docker": "Docker", + "hassio": "Supervisor", "installation_type": "Typ in\u0161tal\u00e1cie", "os_name": "Rodina opera\u010dn\u00fdch syst\u00e9mov", "os_version": "Verzia opera\u010dn\u00e9ho syst\u00e9mu", "python_version": "Verzia Pythonu", "timezone": "\u010casov\u00e9 p\u00e1smo", "user": "Pou\u017e\u00edvate\u013e", - "version": "Verzia" + "version": "Verzia", + "virtualenv": "Virtu\u00e1lne prostredie" } } } \ No newline at end of file diff --git a/homeassistant/components/homeassistant/triggers/numeric_state.py b/homeassistant/components/homeassistant/triggers/numeric_state.py index b8f548adb16..53d3fb1217f 100644 --- a/homeassistant/components/homeassistant/triggers/numeric_state.py +++ b/homeassistant/components/homeassistant/triggers/numeric_state.py @@ -41,7 +41,10 @@ def validate_above_below(value): if above > below: raise vol.Invalid( - f"A value can never be above {above} and below {below} at the same time. You probably want two different triggers.", + ( + f"A value can never be above {above} and below {below} at the same" + " time. You probably want two different triggers." + ), ) return value diff --git a/homeassistant/components/homeassistant/triggers/time.py b/homeassistant/components/homeassistant/triggers/time.py index a51eff004e5..f5473d66a5b 100644 --- a/homeassistant/components/homeassistant/triggers/time.py +++ b/homeassistant/components/homeassistant/triggers/time.py @@ -26,7 +26,9 @@ import homeassistant.util.dt as dt_util _TIME_TRIGGER_SCHEMA = vol.Any( cv.time, vol.All(str, cv.entity_domain(["input_datetime", "sensor"])), - msg="Expected HH:MM, HH:MM:SS or Entity ID with domain 'input_datetime' or 'sensor'", + msg=( + "Expected HH:MM, HH:MM:SS or Entity ID with domain 'input_datetime' or 'sensor'" + ), ) TRIGGER_SCHEMA = cv.TRIGGER_BASE_SCHEMA.extend( diff --git a/homeassistant/components/homeassistant_alerts/__init__.py b/homeassistant/components/homeassistant_alerts/__init__.py index 82631d58ec5..7012111ed61 100644 --- a/homeassistant/components/homeassistant_alerts/__init__.py +++ b/homeassistant/components/homeassistant_alerts/__init__.py @@ -21,7 +21,6 @@ from homeassistant.helpers.issue_registry import ( from homeassistant.helpers.start import async_at_start from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.update_coordinator import DataUpdateCoordinator -from homeassistant.util.yaml import parse_yaml DOMAIN = "homeassistant_alerts" UPDATE_INTERVAL = timedelta(hours=3) @@ -46,35 +45,14 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # Fetch alert to get title + description try: response = await async_get_clientsession(hass).get( - f"https://alerts.home-assistant.io/alerts/{alert.filename}", + f"https://alerts.home-assistant.io/alerts/{alert.alert_id}.json", timeout=aiohttp.ClientTimeout(total=10), ) except asyncio.TimeoutError: _LOGGER.warning("Error fetching %s: timeout", alert.filename) continue - alert_content = await response.text() - alert_parts = alert_content.split("---") - - if len(alert_parts) != 3: - _LOGGER.warning( - "Error parsing %s: unexpected metadata format", alert.filename - ) - continue - - try: - alert_info = parse_yaml(alert_parts[1]) - except ValueError as err: - _LOGGER.warning("Error parsing %s metadata: %s", alert.filename, err) - continue - - if not isinstance(alert_info, dict) or "title" not in alert_info: - _LOGGER.warning("Error in %s metadata: title not found", alert.filename) - continue - - alert_title = alert_info["title"] - alert_content = alert_parts[2].strip() - + alert_content = await response.json() async_create_issue( hass, DOMAIN, @@ -84,8 +62,8 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: severity=IssueSeverity.WARNING, translation_key="alert", translation_placeholders={ - "title": alert_title, - "description": alert_content, + "title": alert_content["title"], + "description": alert_content["content"], }, ) active_alerts[issue_id] = alert.date_updated @@ -118,6 +96,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: class IntegrationAlert: """Issue Registry Entry.""" + alert_id: str integration: str filename: str date_updated: str | None @@ -197,9 +176,10 @@ class AlertUpdateCoordinator(DataUpdateCoordinator[dict[str, IntegrationAlert]]) continue integration_alert = IntegrationAlert( + alert_id=alert["id"], integration=integration["package"], filename=alert["filename"], - date_updated=alert.get("date_updated"), + date_updated=alert.get("updated"), ) result[integration_alert.issue_id] = integration_alert diff --git a/homeassistant/components/homeassistant_hardware/silabs_multiprotocol_addon.py b/homeassistant/components/homeassistant_hardware/silabs_multiprotocol_addon.py index 41dbcabae43..921521c182a 100644 --- a/homeassistant/components/homeassistant_hardware/silabs_multiprotocol_addon.py +++ b/homeassistant/components/homeassistant_hardware/silabs_multiprotocol_addon.py @@ -1,7 +1,7 @@ """Manage the Silicon Labs Multiprotocol add-on.""" from __future__ import annotations -from abc import abstractmethod +from abc import ABC, abstractmethod import asyncio import dataclasses import logging @@ -63,7 +63,7 @@ class SerialPortSettings: flow_control: bool -def get_zigbee_socket(hass, addon_info: AddonInfo) -> str: +def get_zigbee_socket(hass: HomeAssistant, addon_info: AddonInfo) -> str: """Return the zigbee socket. Raises AddonError on error @@ -71,7 +71,7 @@ def get_zigbee_socket(hass, addon_info: AddonInfo) -> str: return f"socket://{addon_info.hostname}:9999" -class BaseMultiPanFlow(FlowHandler): +class BaseMultiPanFlow(FlowHandler, ABC): """Support configuring the Silicon Labs Multiprotocol add-on.""" def __init__(self) -> None: @@ -245,7 +245,7 @@ class OptionsFlowHandler(BaseMultiPanFlow, config_entries.OptionsFlow): # pylint: disable=unreachable - return await self.async_step_on_supervisor() + return await self.async_step_on_supervisor() # type: ignore[unreachable] async def async_step_on_supervisor( self, user_input: dict[str, Any] | None = None diff --git a/homeassistant/components/homeassistant_hardware/translations/bg.json b/homeassistant/components/homeassistant_hardware/translations/bg.json new file mode 100644 index 00000000000..1101f60495d --- /dev/null +++ b/homeassistant/components/homeassistant_hardware/translations/bg.json @@ -0,0 +1,13 @@ +{ + "silabs_multiprotocol_hardware": { + "options": { + "abort": { + "not_hassio": "\u0425\u0430\u0440\u0434\u0443\u0435\u0440\u043d\u0438\u0442\u0435 \u043e\u043f\u0446\u0438\u0438 \u043c\u043e\u0433\u0430\u0442 \u0434\u0430 \u0431\u044a\u0434\u0430\u0442 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0438 \u0441\u0430\u043c\u043e \u043f\u0440\u0438 \u0438\u043d\u0441\u0442\u0430\u043b\u0430\u0446\u0438\u0438 \u043d\u0430 HassOS.", + "zha_migration_failed": "\u041c\u0438\u0433\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u043d\u0430 ZHA \u043d \u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430." + }, + "error": { + "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_hardware/translations/ca.json b/homeassistant/components/homeassistant_hardware/translations/ca.json new file mode 100644 index 00000000000..b633d1a77b5 --- /dev/null +++ b/homeassistant/components/homeassistant_hardware/translations/ca.json @@ -0,0 +1,44 @@ +{ + "silabs_multiprotocol_hardware": { + "options": { + "abort": { + "addon_info_failed": "No s'ha pogut obtenir la informaci\u00f3 del complement Silicon Labs Multiprotocol.", + "addon_install_failed": "No s'ha pogut instal\u00b7lar el complement Silicon Labs Multiprotocol-", + "addon_set_config_failed": "No s'ha pogut establir la configuraci\u00f3 de Silicon Labs Multiprotocol.", + "addon_start_failed": "No s'ha pogut iniciar el complement Silicon Labs Multiprotocol.", + "disabled_due_to_bug": "Les opcions de maquinari s'han desactivat temporalment mentre es corregeix un error. [M\u00e9s informaci\u00f3]({url})", + "not_hassio": "Les opcions de maquinari nom\u00e9s es poden configurar a les instal\u00b7lacions HassOS.", + "zha_migration_failed": "La migraci\u00f3 ZHA no ha tingut \u00e8xit." + }, + "error": { + "unknown": "Error inesperat" + }, + "progress": { + "install_addon": "Espera mentre finalitza la instal\u00b7laci\u00f3 del complement Silicon Labs Multiprotocol. Pot tardar uns minuts.", + "start_addon": "Espera mentre es completa la inicialitzaci\u00f3 del complement Silicon Labs Multiprotocol. Pot tardar uns segons." + }, + "step": { + "addon_installed_other_device": { + "title": "El suport multiprotocol ja est\u00e0 activat per a un altre dispositiu" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "Activa el suport multiprotocol" + }, + "description": "Quan el suport multiprotocol est\u00e0 activat, la r\u00e0dio IEEE 802.15.4 de {hardware_name} es pot utilitzar tant per a Zigbee com per a Thread alhora (utilitzat per Matter). Si la r\u00e0dio ja utilitza la integraci\u00f3 ZHA Zigbee, ZHA es reconfigurar\u00e0 per utilitzar el microprogramari multiprotocol. \n\nNota: aquesta \u00e9s una caracter\u00edstica experimental.", + "title": "Activa el suport multiprotocol per l'est\u00e0ndard de r\u00e0dio IEEE 802.15.4" + }, + "install_addon": { + "title": "Ha comen\u00e7at la instal\u00b7laci\u00f3 del complement Silicon Labs Multiprotocol" + }, + "show_revert_guide": { + "description": "Si vols canviar a nom\u00e9s microprogramari Zigbee, completa els passos manuals seg\u00fcents: \n\n * Elimina el complement Silicon Labs Multiprotocol\n\n * Carrega el microprogramari (firmware) de Zigbee, segueix la guia a https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually. \n\n * Torna a configurar ZHA per migrar la configuraci\u00f3 al dispositiu r\u00e0dio actualitzat", + "title": "El suport multiprotocol est\u00e0 activat per a aquest dispositiu" + }, + "start_addon": { + "title": "El complement Silicon Labs Multiprotocol s'est\u00e0 iniciant." + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_hardware/translations/cs.json b/homeassistant/components/homeassistant_hardware/translations/cs.json new file mode 100644 index 00000000000..d9e1a86e2c6 --- /dev/null +++ b/homeassistant/components/homeassistant_hardware/translations/cs.json @@ -0,0 +1,9 @@ +{ + "silabs_multiprotocol_hardware": { + "options": { + "error": { + "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_hardware/translations/de.json b/homeassistant/components/homeassistant_hardware/translations/de.json new file mode 100644 index 00000000000..688dd7a3cca --- /dev/null +++ b/homeassistant/components/homeassistant_hardware/translations/de.json @@ -0,0 +1,44 @@ +{ + "silabs_multiprotocol_hardware": { + "options": { + "abort": { + "addon_info_failed": "Silicon Labs Multiprotokoll-Zusatzinformationen konnten nicht abgerufen werden.", + "addon_install_failed": "Die Installation des Silicon Labs Multiprotokoll Add-Ons ist fehlgeschlagen.", + "addon_set_config_failed": "Die Silicon Labs Multiprotokoll Konfiguration konnte nicht eingestellt werden.", + "addon_start_failed": "Das Silicon Labs Multiprotokoll Add-on konnte nicht gestartet werden.", + "disabled_due_to_bug": "Die Hardwareoptionen sind vor\u00fcbergehend deaktiviert, w\u00e4hrend wir einen Fehler beheben. [Weitere Informationen]({url})", + "not_hassio": "Die Hardwareoptionen k\u00f6nnen nur auf HassOS-Installationen konfiguriert werden.", + "zha_migration_failed": "Die ZHA Migration war nicht erfolgreich." + }, + "error": { + "unknown": "Unerwarteter Fehler" + }, + "progress": { + "install_addon": "Bitte warte, bis die Installation des Silicon Labs Multiprotokoll Add-ons abgeschlossen ist. Dies kann einige Minuten dauern.", + "start_addon": "Bitte warte, bis der Start des Silicon Labs Multiprotokoll Add-Ons abgeschlossen ist. Dies kann einige Sekunden dauern." + }, + "step": { + "addon_installed_other_device": { + "title": "Die Multiprotokoll Unterst\u00fctzung ist bereits f\u00fcr ein anderes Ger\u00e4t aktiviert" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "Multiprotokoll Unterst\u00fctzung aktivieren" + }, + "description": "Wenn die Multiprotokoll Unterst\u00fctzung aktiviert ist, kann das IEEE 802.15.4-Funkger\u00e4t von {hardware_name} gleichzeitig f\u00fcr Zigbee und Thread (verwendet von Matter) verwendet werden. Wenn das Funkger\u00e4t bereits von der Zigbee-Integration des ZHA verwendet wird, wird der ZHA neu konfiguriert, um die Multiprotokoll-Firmware zu verwenden.\n\nHinweis: Dies ist eine experimentelle Funktion.", + "title": "Aktiviere die Multiprotokoll Unterst\u00fctzung auf dem IEEE 802.15.4-Funkger\u00e4t" + }, + "install_addon": { + "title": "Die Installation des Silicon Labs Multiprotokoll Add-Ons hat begonnen" + }, + "show_revert_guide": { + "description": "Wenn du zu einer reinen Zigbee-Firmware wechseln m\u00f6chtest, f\u00fchre bitte die folgenden manuellen Schritte aus:\n\n * Entferne das Silicon Labs Multiprotokoll-Add-On\n\n * Flashe die reine Zigbee-Firmware, folge der Anleitung unter https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually.\n\n * Rekonfiguriere ZHA, um die Einstellungen auf das neu geflashte Funkger\u00e4t zu migrieren.", + "title": "Multiprotokoll Unterst\u00fctzung ist f\u00fcr dieses Ger\u00e4t aktiviert" + }, + "start_addon": { + "title": "Das Silicon Labs Multiprotokoll Add-on wird gestartet." + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_hardware/translations/el.json b/homeassistant/components/homeassistant_hardware/translations/el.json new file mode 100644 index 00000000000..7c4933a253a --- /dev/null +++ b/homeassistant/components/homeassistant_hardware/translations/el.json @@ -0,0 +1,44 @@ +{ + "silabs_multiprotocol_hardware": { + "options": { + "abort": { + "addon_info_failed": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03bb\u03ae\u03c8\u03b7\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03b9\u03ce\u03bd \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf\u03c5 Silicon Labs Multiprotocol.", + "addon_install_failed": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03b5\u03b3\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf\u03c5 Silicon Labs Multiprotocol.", + "addon_set_config_failed": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7\u03c2 \u03c0\u03b1\u03c1\u03b1\u03bc\u03ad\u03c4\u03c1\u03c9\u03bd \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ce\u03bd \u03c0\u03c1\u03c9\u03c4\u03bf\u03ba\u03cc\u03bb\u03bb\u03c9\u03bd Silicon Labs.", + "addon_start_failed": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03b5\u03ba\u03ba\u03af\u03bd\u03b7\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf\u03c5 Silicon Labs Multiprotocol.", + "disabled_due_to_bug": "\u039f\u03b9 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03c5\u03bb\u03b9\u03ba\u03bf\u03cd \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03bf\u03cd\u03bd\u03c4\u03b1\u03b9 \u03c0\u03c1\u03bf\u03c3\u03c9\u03c1\u03b9\u03bd\u03ac \u03b5\u03bd\u03ce \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03bd\u03bf\u03c5\u03bc\u03b5 \u03ad\u03bd\u03b1 \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1. [\u039c\u03ac\u03b8\u03b5\u03c4\u03b5 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b1]({url})", + "not_hassio": "\u039f\u03b9 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03c5\u03bb\u03b9\u03ba\u03bf\u03cd \u03bc\u03c0\u03bf\u03c1\u03bf\u03cd\u03bd \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03bf\u03cd\u03bd \u03bc\u03cc\u03bd\u03bf \u03c3\u03b5 \u03b5\u03b3\u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03ac\u03c3\u03b5\u03b9\u03c2 HassOS.", + "zha_migration_failed": "\u0397 \u03bc\u03b5\u03c4\u03ac\u03b2\u03b1\u03c3\u03b7 ZHA \u03b4\u03b5\u03bd \u03c0\u03ad\u03c4\u03c5\u03c7\u03b5." + }, + "error": { + "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" + }, + "progress": { + "install_addon": "\u03a0\u03b5\u03c1\u03b9\u03bc\u03ad\u03bd\u03b5\u03c4\u03b5 \u03bc\u03ad\u03c7\u03c1\u03b9 \u03bd\u03b1 \u03bf\u03bb\u03bf\u03ba\u03bb\u03b7\u03c1\u03c9\u03b8\u03b5\u03af \u03b7 \u03b5\u03b3\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf\u03c5 Silicon Labs Multiprotocol. \u0391\u03c5\u03c4\u03cc \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03b4\u03b9\u03b1\u03c1\u03ba\u03ad\u03c3\u03b5\u03b9 \u03b1\u03c1\u03ba\u03b5\u03c4\u03ac \u03bb\u03b5\u03c0\u03c4\u03ac.", + "start_addon": "\u03a0\u03b5\u03c1\u03b9\u03bc\u03ad\u03bd\u03b5\u03c4\u03b5 \u03bc\u03ad\u03c7\u03c1\u03b9 \u03bd\u03b1 \u03bf\u03bb\u03bf\u03ba\u03bb\u03b7\u03c1\u03c9\u03b8\u03b5\u03af \u03b7 \u03ad\u03bd\u03b1\u03c1\u03be\u03b7 \u03c4\u03bf\u03c5 \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf\u03c5 Silicon Labs Multiprotocol. \u0391\u03c5\u03c4\u03cc \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03b4\u03b9\u03b1\u03c1\u03ba\u03ad\u03c3\u03b5\u03b9 \u03bc\u03b5\u03c1\u03b9\u03ba\u03ac \u03b4\u03b5\u03c5\u03c4\u03b5\u03c1\u03cc\u03bb\u03b5\u03c0\u03c4\u03b1." + }, + "step": { + "addon_installed_other_device": { + "title": "\u0397 \u03c5\u03c0\u03bf\u03c3\u03c4\u03ae\u03c1\u03b9\u03be\u03b7 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ce\u03bd \u03c0\u03c1\u03c9\u03c4\u03bf\u03ba\u03cc\u03bb\u03bb\u03c9\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03ae\u03b4\u03b7 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7 \u03b3\u03b9\u03b1 \u03ac\u03bb\u03bb\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "\u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03ae\u03c1\u03b9\u03be\u03b7 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ce\u03bd \u03c0\u03c1\u03c9\u03c4\u03bf\u03ba\u03cc\u03bb\u03bb\u03c9\u03bd" + }, + "description": "\u038c\u03c4\u03b1\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7 \u03b7 \u03c5\u03c0\u03bf\u03c3\u03c4\u03ae\u03c1\u03b9\u03be\u03b7 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ce\u03bd \u03c0\u03c1\u03c9\u03c4\u03bf\u03ba\u03cc\u03bb\u03bb\u03c9\u03bd, \u03bf \u03c0\u03bf\u03bc\u03c0\u03bf\u03b4\u03ad\u03ba\u03c4\u03b7\u03c2 IEEE 802.15.4 \u03c4\u03bf\u03c5 {hardware_name} \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b7\u03b8\u03b5\u03af \u03c4\u03b1\u03c5\u03c4\u03cc\u03c7\u03c1\u03bf\u03bd\u03b1 \u03ba\u03b1\u03b9 \u03b3\u03b9\u03b1 \u03c4\u03bf Zigbee \u03ba\u03b1\u03b9 \u03b3\u03b9\u03b1 \u03c4\u03bf Thread (\u03c0\u03bf\u03c5 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03bf Matter). \u0395\u03ac\u03bd \u03bf \u03c0\u03bf\u03bc\u03c0\u03bf\u03b4\u03ad\u03ba\u03c4\u03b7\u03c2 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03ae\u03b4\u03b7 \u03b1\u03c0\u03cc \u03c4\u03b7\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 ZHA Zigbee, \u03c4\u03bf ZHA \u03b8\u03b1 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03ce\u03c3\u03c4\u03b5 \u03bd\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af \u03c4\u03bf \u03c5\u03bb\u03b9\u03ba\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03bc\u03b9\u03ba\u03cc \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ce\u03bd \u03c0\u03c1\u03c9\u03c4\u03bf\u03ba\u03cc\u03bb\u03bb\u03c9\u03bd. \n\n \u03a3\u03b7\u03bc\u03b5\u03af\u03c9\u03c3\u03b7: \u0391\u03c5\u03c4\u03cc \u03b5\u03af\u03bd\u03b1\u03b9 \u03ad\u03bd\u03b1 \u03c0\u03b5\u03b9\u03c1\u03b1\u03bc\u03b1\u03c4\u03b9\u03ba\u03cc \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03b7\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc.", + "title": "\u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03ae\u03c1\u03b9\u03be\u03b7 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ce\u03bd \u03c0\u03c1\u03c9\u03c4\u03bf\u03ba\u03cc\u03bb\u03bb\u03c9\u03bd \u03c3\u03c4\u03bd \u03c0\u03bf\u03bc\u03c0\u03bf\u03b4\u03ad\u03ba\u03c4\u03b7 IEEE 802.15.4" + }, + "install_addon": { + "title": "\u0397 \u03b5\u03b3\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf\u03c5 Silicon Labs Multiprotocol \u03ad\u03c7\u03b5\u03b9 \u03be\u03b5\u03ba\u03b9\u03bd\u03ae\u03c3\u03b5\u03b9" + }, + "show_revert_guide": { + "description": "\u0395\u03ac\u03bd \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03b1\u03bb\u03bb\u03ac\u03be\u03b5\u03c4\u03b5 \u03c3\u03b5 \u03c5\u03bb\u03b9\u03ba\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03bc\u03b9\u03ba\u03cc \u03bc\u03cc\u03bd\u03bf Zigbee, \u03bf\u03bb\u03bf\u03ba\u03bb\u03b7\u03c1\u03ce\u03c3\u03c4\u03b5 \u03c4\u03b1 \u03c0\u03b1\u03c1\u03b1\u03ba\u03ac\u03c4\u03c9 \u03bc\u03b7 \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b1 \u03b2\u03ae\u03bc\u03b1\u03c4\u03b1: \n\n * \u039a\u03b1\u03c4\u03b1\u03c1\u03b3\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf Silicon Labs Multiprotocol \n\n * \u0391\u03bd\u03b1\u03b2\u03bf\u03c3\u03b2\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf \u03c5\u03bb\u03b9\u03ba\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03bc\u03b9\u03ba\u03cc \u03bc\u03cc\u03bd\u03bf Zigbee, \u03b1\u03ba\u03bf\u03bb\u03bf\u03c5\u03b8\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd \u03bf\u03b4\u03b7\u03b3\u03cc \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manual. \n\n * \u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac \u03c4\u03bf ZHA \u03b3\u03b9\u03b1 \u03bc\u03b5\u03c4\u03b5\u03b3\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7 \u03c4\u03c9\u03bd \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c9\u03bd \u03c3\u03c4\u03bf\u03bd \u03b1\u03bd\u03b1\u03bd\u03b5\u03c9\u03bc\u03ad\u03bd\u03bf \u03c0\u03bf\u03bc\u03c0\u03bf\u03b4\u03ad\u03ba\u03c4\u03b7", + "title": "\u0397 \u03c5\u03c0\u03bf\u03c3\u03c4\u03ae\u03c1\u03b9\u03be\u03b7 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ce\u03bd \u03c0\u03c1\u03c9\u03c4\u03bf\u03ba\u03cc\u03bb\u03bb\u03c9\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7 \u03b3\u03b9\u03b1 \u03b1\u03c5\u03c4\u03ae\u03bd \u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae" + }, + "start_addon": { + "title": "\u039e\u03b5\u03ba\u03b9\u03bd\u03ac \u03c4\u03bf \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf Silicon Labs Multiprotocol." + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_hardware/translations/es.json b/homeassistant/components/homeassistant_hardware/translations/es.json new file mode 100644 index 00000000000..52391afd20a --- /dev/null +++ b/homeassistant/components/homeassistant_hardware/translations/es.json @@ -0,0 +1,44 @@ +{ + "silabs_multiprotocol_hardware": { + "options": { + "abort": { + "addon_info_failed": "No se pudo obtener la informaci\u00f3n del complemento Silicon Labs Multiprotocol.", + "addon_install_failed": "No se pudo instalar el complemento Silicon Labs Multiprotocol.", + "addon_set_config_failed": "No se pudo establecer la configuraci\u00f3n de Silicon Labs Multiprotocol.", + "addon_start_failed": "No se pudo iniciar el complemento Silicon Labs Multiprotocol.", + "disabled_due_to_bug": "Las opciones de hardware est\u00e1n deshabilitadas temporalmente mientras solucionamos un error. [M\u00e1s informaci\u00f3n]({url})", + "not_hassio": "Las opciones de hardware solo se pueden configurar en instalaciones de HassOS.", + "zha_migration_failed": "La migraci\u00f3n de ZHA no tuvo \u00e9xito." + }, + "error": { + "unknown": "Error inesperado" + }, + "progress": { + "install_addon": "Por favor, espera mientras finaliza la instalaci\u00f3n del complemento Silicon Labs Multiprotocol. Esto puede tardar varios minutos.", + "start_addon": "Por favor, espera mientras se completa el inicio del complemento Silicon Labs Multiprotocol. Esto puede tardar unos segundos." + }, + "step": { + "addon_installed_other_device": { + "title": "El soporte multiprotocolo ya est\u00e1 habilitado para otro dispositivo" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "Habilitar soporte multiprotocolo" + }, + "description": "Cuando el soporte multiprotocolo est\u00e1 habilitado, la radio IEEE 802.15.4 de {hardware_name} se puede usar tanto para Zigbee como para Thread (usado por Matter) al mismo tiempo. Si la integraci\u00f3n ZHA Zigbee ya usa la radio, ZHA se volver\u00e1 a configurar para usar el firmware multiprotocolo. \n\nNota: Esta es una caracter\u00edstica experimental.", + "title": "Habilitar el soporte multiprotocolo en la radio IEEE 802.15.4" + }, + "install_addon": { + "title": "La instalaci\u00f3n del complemento Silicon Labs Multiprotocol ha comenzado" + }, + "show_revert_guide": { + "description": "Si quieres cambiar el firmware a solo Zigbee, completa los siguientes pasos manuales: \n\n * Elimina el complemento Silicon Labs Multiprotocol \n\n * Actualiza el firmware solo de Zigbee, sigue la gu\u00eda en https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually. \n\n * Vuelve a configurar ZHA para migrar la configuraci\u00f3n a la radio actualizada", + "title": "El soporte multiprotocolo est\u00e1 habilitado para este dispositivo" + }, + "start_addon": { + "title": "El complemento Silicon Labs Multiprotocol se est\u00e1 iniciando." + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_hardware/translations/et.json b/homeassistant/components/homeassistant_hardware/translations/et.json new file mode 100644 index 00000000000..413070f98f3 --- /dev/null +++ b/homeassistant/components/homeassistant_hardware/translations/et.json @@ -0,0 +1,44 @@ +{ + "silabs_multiprotocol_hardware": { + "options": { + "abort": { + "addon_info_failed": "Silicon Labs Multiprotocol add-on info saamine eba\u00f5nnestus.", + "addon_install_failed": "Silicon Labs Multiprotocol add-on'i paigaldamine nurjus.", + "addon_set_config_failed": "Silicon Labs Multiprotocol konfiguratsiooni seadistamine eba\u00f5nnestus.", + "addon_start_failed": "Silicon Labsi mitmeprotokolli lisandmooduli k\u00e4ivitamine nurjus.", + "disabled_due_to_bug": "Riistvaravalikud on vea parandamise ajal ajutiselt keelatud. [Lisateave] ({url})", + "not_hassio": "Riistvaravalikuid saab konfigureerida ainult HassOS-i paigaldustes.", + "zha_migration_failed": "ZHA sidumise siirdamine nurjus." + }, + "error": { + "unknown": "Ootamatu t\u00f5rge" + }, + "progress": { + "install_addon": "Oota kuni Silicon Labsi mitmeprotokolli lisandmooduli installimine l\u00f5peb. Selleks v\u00f5ib kuluda mitu minutit.", + "start_addon": "Oota kuni Silicon Labsi mitmeprotokolli lisandmooduli k\u00e4ivitamine on l\u00f5pule viidud. See v\u00f5ib v\u00f5tta m\u00f5ne sekundi." + }, + "step": { + "addon_installed_other_device": { + "title": "Mitmeprotokolli tugi on m\u00f5ne teise seadme jaoks juba lubatud" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "Luba multiprotokolli tugi" + }, + "description": "Kui mitme protokolli tugi on lubatud, saab seadme {hardware_name} IEEE 802.15.4 raadiot kasutada samaaegselt nii Zigbee kui ka Threadi jaoks (kasutab Matter). Kui ZHA Zigbee kasutab juba kasutab raadiot, konfigureeritakse ZHA uuesti kasutama mitmeprotokollilist p\u00fcsivara. \n\n M\u00e4rkus. See on eksperimentaalne funktsioon.", + "title": "Multiprotokollide toe lubamine IEEE 802.15.4 raadiosides" + }, + "install_addon": { + "title": "Silicon Labs Multiprotocol add-on paigaldus on alanud" + }, + "show_revert_guide": { + "description": "Kui soovid muuta ainult Zigbee p\u00fcsivara, tee j\u00e4rgmised sammud: \n\n * Eemalda Silicon Labsi mitmeprotokolli lisand \n\n * V\u00e4rskenda ainult Zigbee p\u00fcsivara, j\u00e4rgi juhendit aadressil https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually. \n\n * Seadistuste \u00fcleviimiseks v\u00e4rskendatud raadiosse seadista ZHA uuesti", + "title": "Multiprotokollide tugi on selle seadme puhul lubatud." + }, + "start_addon": { + "title": "Silicon Labs Multiprotocol lisandmoodul k\u00e4ivitub." + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_hardware/translations/he.json b/homeassistant/components/homeassistant_hardware/translations/he.json new file mode 100644 index 00000000000..76dfdbf24e7 --- /dev/null +++ b/homeassistant/components/homeassistant_hardware/translations/he.json @@ -0,0 +1,44 @@ +{ + "silabs_multiprotocol_hardware": { + "options": { + "abort": { + "addon_info_failed": "\u05e0\u05db\u05e9\u05dc\u05d4 \u05e7\u05d1\u05dc\u05ea \u05de\u05d9\u05d3\u05e2 \u05e2\u05dc \u05d4\u05d4\u05e8\u05d7\u05d1\u05d4 \u05e8\u05d9\u05d1\u05d5\u05d9 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc\u05d9\u05dd \u05e9\u05dc \u05de\u05e2\u05d1\u05d3\u05d5\u05ea \u05d4\u05e1\u05d9\u05dc\u05d9\u05e7\u05d5\u05df.", + "addon_install_failed": "\u05d4\u05ea\u05e7\u05e0\u05ea \u05d4\u05d4\u05e8\u05d7\u05d1\u05d4 \u05e9\u05dc \u05e8\u05d9\u05d1\u05d5\u05d9 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05dc\u05d9\u05dd \u05e9\u05dc \u05de\u05e2\u05d1\u05d3\u05d5\u05ea \u05d4\u05e1\u05d9\u05dc\u05d9\u05e7\u05d5\u05df \u05e0\u05db\u05e9\u05dc\u05d4.", + "addon_set_config_failed": "\u05d4\u05d2\u05d3\u05e8\u05ea \u05ea\u05e6\u05d5\u05e8\u05ea \u05e8\u05d9\u05d1\u05d5\u05d9 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05dc\u05d9\u05dd \u05e9\u05dc \u05de\u05e2\u05d1\u05d3\u05d5\u05ea \u05d4\u05e1\u05d9\u05dc\u05d9\u05e7\u05d5\u05df \u05e0\u05db\u05e9\u05dc\u05d4.", + "addon_start_failed": "\u05d4\u05e4\u05e2\u05dc\u05ea \u05d4\u05d4\u05e8\u05d7\u05d1\u05d4 \u05e8\u05d9\u05d1\u05d5\u05d9 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05dc\u05d9\u05dd \u05e9\u05dc \u05de\u05e2\u05d1\u05d3\u05d5\u05ea \u05d4\u05e1\u05d9\u05dc\u05d9\u05e7\u05d5\u05df \u05e0\u05db\u05e9\u05dc\u05d4.", + "disabled_due_to_bug": "\u05d0\u05e4\u05e9\u05e8\u05d5\u05d9\u05d5\u05ea \u05d4\u05d7\u05d5\u05de\u05e8\u05d4 \u05de\u05d5\u05e9\u05d1\u05ea\u05d5\u05ea \u05d1\u05d0\u05d5\u05e4\u05df \u05d6\u05de\u05e0\u05d9 \u05d1\u05d6\u05de\u05df \u05e9\u05d0\u05e0\u05d5 \u05de\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05d0\u05d2. [\u05dc\u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3] ({url})", + "not_hassio": "\u05e0\u05d9\u05ea\u05df \u05dc\u05d4\u05d2\u05d3\u05d9\u05e8 \u05d0\u05ea \u05d0\u05e4\u05e9\u05e8\u05d5\u05d9\u05d5\u05ea \u05d4\u05d7\u05d5\u05de\u05e8\u05d4 \u05e8\u05e7 \u05d1\u05d4\u05ea\u05e7\u05e0\u05d5\u05ea HassOS.", + "zha_migration_failed": "\u05e0\u05d3\u05d9\u05d3\u05ea \u05d4-ZHA \u05dc\u05d0 \u05d4\u05e6\u05dc\u05d9\u05d7\u05d4." + }, + "error": { + "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" + }, + "progress": { + "install_addon": "\u05e0\u05d0 \u05dc\u05d4\u05de\u05ea\u05d9\u05df \u05e2\u05d3 \u05dc\u05e1\u05d9\u05d5\u05dd \u05d4\u05d4\u05ea\u05e7\u05e0\u05d4 \u05e9\u05dc \u05ea\u05d5\u05e1\u05e3 \u05d4-Silicon Labs Multiprotocol. \u05e4\u05e2\u05d5\u05dc\u05d4 \u05d6\u05d5 \u05e2\u05e9\u05d5\u05d9\u05d4 \u05dc\u05d4\u05d9\u05de\u05e9\u05da \u05de\u05e1\u05e4\u05e8 \u05d3\u05e7\u05d5\u05ea.", + "start_addon": "\u05e0\u05d0 \u05dc\u05d4\u05de\u05ea\u05d9\u05df \u05e2\u05d3 \u05dc\u05d4\u05e9\u05dc\u05de\u05ea \u05d4\u05ea\u05d5\u05e1\u05e3 \u05e8\u05d9\u05d1\u05d5\u05d9 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc\u05d9\u05dd \u05e9\u05dc \u05de\u05e2\u05d1\u05d3\u05d5\u05ea \u05d4\u05e1\u05d9\u05dc\u05d9\u05e7\u05d5\u05df. \u05e4\u05e2\u05d5\u05dc\u05d4 \u05d6\u05d5 \u05e2\u05e9\u05d5\u05d9\u05d4 \u05dc\u05d4\u05d9\u05de\u05e9\u05da \u05de\u05e1\u05e4\u05e8 \u05e9\u05e0\u05d9\u05d5\u05ea." + }, + "step": { + "addon_installed_other_device": { + "title": "\u05ea\u05de\u05d9\u05db\u05d4 \u05d1\u05e8\u05d9\u05d1\u05d5\u05d9 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc\u05d9\u05dd \u05db\u05d1\u05e8 \u05d6\u05de\u05d9\u05e0\u05d4 \u05e2\u05d1\u05d5\u05e8 \u05d4\u05ea\u05e7\u05df \u05d0\u05d7\u05e8" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "\u05d0\u05e4\u05e9\u05e8 \u05ea\u05de\u05d9\u05db\u05d4 \u05d1\u05e8\u05d9\u05d1\u05d5\u05d9 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc\u05d9\u05dd" + }, + "description": "\u05db\u05d0\u05e9\u05e8 \u05ea\u05de\u05d9\u05db\u05d4 \u05d1\u05e8\u05d9\u05d1\u05d5\u05d9 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc\u05d9\u05dd \u05de\u05d5\u05e4\u05e2\u05dc\u05ea, \u05e0\u05d9\u05ea\u05df \u05dc\u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05e8\u05d3\u05d9\u05d5 IEEE 802.15.4 \u05e9\u05dc {hardware_name} \u05d4\u05df \u05e2\u05d1\u05d5\u05e8 Zigbee \u05d5\u05d4\u05df \u05e2\u05d1\u05d5\u05e8 Thread (\u05d1\u05e9\u05d9\u05de\u05d5\u05e9 \u05e2\u05dc-\u05d9\u05d3\u05d9 Matter) \u05d1\u05d5-\u05d6\u05de\u05e0\u05d9\u05ea. \u05d0\u05dd \u05d4\u05e8\u05d3\u05d9\u05d5 \u05db\u05d1\u05e8 \u05e0\u05de\u05e6\u05d0 \u05d1\u05e9\u05d9\u05de\u05d5\u05e9 \u05e2\u05dc-\u05d9\u05d3\u05d9 \u05e9\u05d9\u05dc\u05d5\u05d1 ZHA Zigbee, ZHA \u05d9\u05d5\u05d2\u05d3\u05e8 \u05de\u05d7\u05d3\u05e9 \u05dc\u05e9\u05d9\u05de\u05d5\u05e9 \u05d1\u05e7\u05d5\u05e9\u05d7\u05d4 \u05de\u05e8\u05d5\u05d1\u05ea \u05d4\u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc\u05d9\u05dd.\n\n\u05d4\u05e2\u05e8\u05d4: \u05d6\u05d5\u05d4\u05d9 \u05ea\u05db\u05d5\u05e0\u05d4 \u05e0\u05d9\u05e1\u05d9\u05d5\u05e0\u05d9\u05ea.", + "title": "\u05d0\u05e4\u05e9\u05e8 \u05ea\u05de\u05d9\u05db\u05d4 \u05d1\u05e8\u05d9\u05d1\u05d5\u05d9 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc\u05d9\u05dd \u05d1\u05e8\u05d3\u05d9\u05d5 IEEE 802.15.4" + }, + "install_addon": { + "title": "\u05d4\u05ea\u05e7\u05e0\u05ea \u05d4\u05d4\u05e8\u05d7\u05d1\u05d4 \u05e8\u05d9\u05d1\u05d5\u05d9 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc\u05d9\u05dd \u05e9\u05dc \u05de\u05e2\u05d1\u05d3\u05d5\u05ea \u05d4\u05e1\u05d9\u05dc\u05d9\u05e7\u05d5\u05df \u05d4\u05d7\u05dc\u05d4" + }, + "show_revert_guide": { + "description": "\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05e9\u05e0\u05d5\u05ea \u05dc\u05e7\u05d5\u05e9\u05d7\u05d4 \u05e9\u05dc Zigbee \u05d1\u05dc\u05d1\u05d3, \u05d9\u05e9 \u05dc\u05d1\u05e6\u05e2 \u05d0\u05ea \u05d4\u05e9\u05dc\u05d1\u05d9\u05dd \u05d4\u05d9\u05d3\u05e0\u05d9\u05d9\u05dd \u05d4\u05d1\u05d0\u05d9\u05dd:\n\n* \u05d4\u05e1\u05e8\u05ea \u05d4\u05d4\u05e8\u05d7\u05d1\u05d4 \u05e8\u05d9\u05d1\u05d5\u05d9 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc\u05d9\u05dd \u05e9\u05dc \u05de\u05e2\u05d1\u05d3\u05d5\u05ea \u05d4\u05e1\u05d9\u05dc\u05d9\u05e7\u05d5\u05df\n\n* \u05e6\u05e8\u05d9\u05d1\u05ea \u05d4\u05e7\u05d5\u05e9\u05d7\u05d4 Zigbee \u05d1\u05dc\u05d1\u05d3, \u05dc\u05e2\u05e7\u05d5\u05d1 \u05d0\u05d7\u05e8 \u05d4\u05de\u05d3\u05e8\u05d9\u05da https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually.\n\n* \u05dc\u05d4\u05d2\u05d3\u05d9\u05e8 \u05de\u05d7\u05d3\u05e9 \u05d0\u05ea ZHA \u05db\u05d3\u05d9 \u05dc\u05d4\u05e2\u05d1\u05d9\u05e8 \u05d4\u05d2\u05d3\u05e8\u05d5\u05ea \u05dc\u05e8\u05d3\u05d9\u05d5 \u05d4\u05de\u05d7\u05d5\u05d3\u05e9", + "title": "\u05ea\u05de\u05d9\u05db\u05d4 \u05d1\u05e8\u05d9\u05d1\u05d5\u05d9 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc\u05d9\u05dd \u05d6\u05de\u05d9\u05e0\u05d4 \u05e2\u05d1\u05d5\u05e8 \u05d4\u05ea\u05e7\u05df \u05d6\u05d4" + }, + "start_addon": { + "title": "\u05d4\u05d4\u05e8\u05d7\u05d1\u05d4 \u05e8\u05d9\u05d1\u05d5\u05d9 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc\u05d9\u05dd \u05e9\u05dc \u05de\u05e2\u05d1\u05d3\u05d5\u05ea \u05d4\u05e1\u05d9\u05dc\u05d9\u05e7\u05d5\u05df \u05d4\u05d7\u05dc\u05d4." + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_hardware/translations/hu.json b/homeassistant/components/homeassistant_hardware/translations/hu.json new file mode 100644 index 00000000000..5479ff34a1e --- /dev/null +++ b/homeassistant/components/homeassistant_hardware/translations/hu.json @@ -0,0 +1,44 @@ +{ + "silabs_multiprotocol_hardware": { + "options": { + "abort": { + "addon_info_failed": "Nem siker\u00fclt lek\u00e9rni a Silicon Labs Multiprotocol kieg\u00e9sz\u00edt\u0151 adatait.", + "addon_install_failed": "Nem siker\u00fclt telep\u00edteni a Silicon Labs Multiprotocol b\u0151v\u00edtm\u00e9nyt.", + "addon_set_config_failed": "A Silicon Labs Multiprotokoll konfigur\u00e1ci\u00f3 be\u00e1ll\u00edt\u00e1sa sikertelen volt.", + "addon_start_failed": "Nem siker\u00fclt elind\u00edtani a Silicon Labs Multiprotocol b\u0151v\u00edtm\u00e9nyt.", + "disabled_due_to_bug": "A hardver opci\u00f3k \u00e1tmenetileg le vannak tiltva, am\u00edg kijav\u00edtunk egy hib\u00e1t. [Tov\u00e1bbi inform\u00e1ci\u00f3]({url})", + "not_hassio": "A hardverbe\u00e1ll\u00edt\u00e1sok csak HassOS telep\u00edt\u00e9sekn\u00e9l konfigur\u00e1lhat\u00f3k.", + "zha_migration_failed": "A ZHA migr\u00e1ci\u00f3 nem siker\u00fclt." + }, + "error": { + "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" + }, + "progress": { + "install_addon": "K\u00e9rj\u00fck, v\u00e1rjon, am\u00edg a Silicon Labs Multiprotocol b\u0151v\u00edtm\u00e9ny telep\u00edt\u00e9se befejez\u0151dik. Ez t\u00f6bb percig is eltarthat.", + "start_addon": "K\u00e9rj\u00fck, v\u00e1rjon, am\u00edg a Silicon Labs Multiprotocol b\u0151v\u00edtm\u00e9ny elindul. Ez eltarthat n\u00e9h\u00e1ny m\u00e1sodpercig." + }, + "step": { + "addon_installed_other_device": { + "title": "A multiprotokoll-t\u00e1mogat\u00e1s m\u00e1r enged\u00e9lyezve van egy m\u00e1sik eszk\u00f6z sz\u00e1m\u00e1ra" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "Multiprotokoll-t\u00e1mogat\u00e1s enged\u00e9lyez\u00e9se" + }, + "description": "Ha a multiprotokoll-t\u00e1mogat\u00e1s enged\u00e9lyezve van, a {hardware_name} IEEE 802.15.4 r\u00e1di\u00f3ja egyszerre haszn\u00e1lhat\u00f3 a Zigbee \u00e9s a Thread (a Matter \u00e1ltal haszn\u00e1lt) funkcionalit\u00e1shoz. Ha a r\u00e1di\u00f3t m\u00e1r haszn\u00e1lja a ZHA Zigbee integr\u00e1ci\u00f3, a ZHA-t \u00e1t kell konfigur\u00e1lni a multiprotokoll firmware haszn\u00e1lat\u00e1ra.\n\nMegjegyz\u00e9s: Ez egy k\u00eds\u00e9rleti funkci\u00f3.", + "title": "Multiprotokoll t\u00e1mogat\u00e1s\u00e1nak enged\u00e9lyez\u00e9se az IEEE 802.15.4 r\u00e1di\u00f3ban" + }, + "install_addon": { + "title": "A Silicon Labs Multiprotocol b\u0151v\u00edtm\u00e9ny telep\u00edt\u00e9se folyamatban van" + }, + "show_revert_guide": { + "description": "Ha csak Zigbee firmware-re szeretne v\u00e1ltani, k\u00e9rj\u00fck, hajtsa v\u00e9gre a k\u00f6vetkez\u0151 manu\u00e1lis l\u00e9p\u00e9seket:\n\n * T\u00e1vol\u00edtsa el a Silicon Labs Multiprotocol b\u0151v\u00edtm\u00e9nyt.\n\n * Flashelje a csak Zigbee firmware-t, k\u00f6vesse a https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually oldalon tal\u00e1lhat\u00f3 \u00fatmutat\u00f3t.\n\n * Konfigur\u00e1lja \u00fajra a ZHA-t, hogy a be\u00e1ll\u00edt\u00e1sokat \u00e1tvigye az \u00fajraflashelt r\u00e1di\u00f3ra.", + "title": "A multiprotokoll-t\u00e1mogat\u00e1s enged\u00e9lyezve van az eszk\u00f6z\u00f6n." + }, + "start_addon": { + "title": "A Silicon Labs Multiprotocol b\u0151v\u00edtm\u00e9ny elind\u00edt\u00e1sa folyamatban van" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_hardware/translations/id.json b/homeassistant/components/homeassistant_hardware/translations/id.json new file mode 100644 index 00000000000..a04a00e2b4f --- /dev/null +++ b/homeassistant/components/homeassistant_hardware/translations/id.json @@ -0,0 +1,44 @@ +{ + "silabs_multiprotocol_hardware": { + "options": { + "abort": { + "addon_info_failed": "Gagal mendapatkan info add-on Silicon Labs Multiprotocol.", + "addon_install_failed": "Gagal menginstal add-on Silicon Labs Multiprotocol.", + "addon_set_config_failed": "Gagal mengatur konfigurasi Silicon Labs Multiprotocol.", + "addon_start_failed": "Gagal memulai add-on Silicon Labs Multiprotocol.", + "disabled_due_to_bug": "Opsi perangkat keras untuk sementara ini dinonaktifkan karena kami sedang memperbaiki bugnya. [Pelajari lebih lanjut]({url})", + "not_hassio": "Opsi perangkat keras hanya bisa dikonfigurasi pada instalasi HassOS.", + "zha_migration_failed": "Migrasi ZHA tidak berhasil." + }, + "error": { + "unknown": "Kesalahan yang tidak diharapkan" + }, + "progress": { + "install_addon": "Harap tunggu hingga penginstalan add-on Silicon Labs Multiprotocol selesai. Ini bisa memakan waktu beberapa saat.", + "start_addon": "Harap tunggu hingga add-on Silicon Labs Multiprotocol selesai. Ini mungkin perlu waktu beberapa saat." + }, + "step": { + "addon_installed_other_device": { + "title": "Dukungan multiprotokol sudah diaktifkan untuk perangkat lain" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "Aktifkan dukungan multiprotokol" + }, + "description": "Jika dukungan multiprotocol diaktifkan, radio IEEE 802.15.4 {hardware_name} dapat digunakan untuk Zigbee dan Thread (digunakan oleh Matter) secara bersamaan. Catatan: Ini adalah fitur eksperimental. Jika komponen radio telah digunakan oleh integrasi ZHA Zigbee, ZHA akan dikonfigurasi ulang untuk menggunakan firmware multiprotokol.\n\nCatatan: Fitur ini bersifat eksperimental.", + "title": "Aktifkan dukungan multiprotokol pada radio IEEE 802.15.4" + }, + "install_addon": { + "title": "Penginstalan add-on Multiprotocol Silicon Labs telah dimulai" + }, + "show_revert_guide": { + "description": "Jika Anda ingin mengubah ke firmware Zigbee saja, selesaikan langkah-langkah manual berikut ini:\n\n * Hapus add-on Multiprotocol Silicon Labs\n\n * Flash firmware khusus Zigbee, ikuti panduan di https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually.\n\n * Konfigurasikan ulang ZHA untuk memigrasikan pengaturan ke radio yang diflash ulang", + "title": "Dukungan multiprotokol diaktifkan untuk perangkat ini" + }, + "start_addon": { + "title": "Add-on Multiprotokol Silicon Labs sedang dimulai." + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_hardware/translations/it.json b/homeassistant/components/homeassistant_hardware/translations/it.json new file mode 100644 index 00000000000..630bad51a27 --- /dev/null +++ b/homeassistant/components/homeassistant_hardware/translations/it.json @@ -0,0 +1,44 @@ +{ + "silabs_multiprotocol_hardware": { + "options": { + "abort": { + "addon_info_failed": "Impossibile ottenere informazioni sul componente aggiuntivo Silicon Labs Multiprotocol.", + "addon_install_failed": "Impossibile installare il componente aggiuntivo Silicon Labs Multiprotocol.", + "addon_set_config_failed": "Impossibile impostare la configurazione di Silicon Labs Multiprotocol.", + "addon_start_failed": "Impossibile avviare il componente aggiuntivo Silicon Labs Multiprotocol.", + "disabled_due_to_bug": "Le opzioni hardware sono temporaneamente disabilitate mentre correggiamo un bug. [Ulteriori informazioni]({url})", + "not_hassio": "Le opzioni hardware possono essere configurate solo su installazioni HassOS.", + "zha_migration_failed": "La migrazione ZHA non \u00e8 riuscita." + }, + "error": { + "unknown": "Errore imprevisto" + }, + "progress": { + "install_addon": "Attendi il completamento dell'installazione del componente aggiuntivo Silicon Labs Multiprotocol. Questo pu\u00f2 richiedere alcuni minuti.", + "start_addon": "Attendi il completamento dell'avvio del componente aggiuntivo Silicon Labs Multiprotocol. Questo potrebbe richiedere alcuni secondi." + }, + "step": { + "addon_installed_other_device": { + "title": "Il supporto multiprotocollo \u00e8 gi\u00e0 abilitato per un altro dispositivo" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "Abilita il supporto multiprotocollo" + }, + "description": "Quando il supporto multiprotocollo \u00e8 abilitato, la radio IEEE 802.15.4 di {hardware_name} pu\u00f2 essere utilizzata contemporaneamente sia per Zigbee che per Thread (utilizzato da Matter). Se la radio \u00e8 gi\u00e0 utilizzata dall'integrazione ZHA Zigbee, ZHA verr\u00e0 riconfigurata per utilizzare il firmware multiprotocollo. \n\nNota: questa \u00e8 una funzione sperimentale.", + "title": "Abilita il supporto multiprotocollo sulla radio IEEE 802.15.4" + }, + "install_addon": { + "title": "L'installazione del componente aggiuntivo Silicon Labs Multiprotocol \u00e8 iniziata" + }, + "show_revert_guide": { + "description": "Se desideri passare al solo firmware Zigbee, completa i seguenti passaggi manuali: \n\n * Rimuovi il componente aggiuntivo Silicon Labs Multiprotocol \n\n * Caricare solo il firmware Zigbee, segui la guida su https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually. \n\n * Riconfigurare ZHA per migrare le impostazioni alla radio con firmware ricaricato", + "title": "Il supporto multiprotocollo \u00e8 abilitato per questo dispositivo" + }, + "start_addon": { + "title": "Il componente aggiuntivo Silicon Labs Multiprotocol \u00e8 in fase di avvio." + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_hardware/translations/ja.json b/homeassistant/components/homeassistant_hardware/translations/ja.json new file mode 100644 index 00000000000..7b99092039e --- /dev/null +++ b/homeassistant/components/homeassistant_hardware/translations/ja.json @@ -0,0 +1,19 @@ +{ + "silabs_multiprotocol_hardware": { + "options": { + "abort": { + "disabled_due_to_bug": "\u30d0\u30b0\u3092\u4fee\u6b63\u3059\u308b\u9593\u3001\u30cf\u30fc\u30c9\u30a6\u30a7\u30a2\u30aa\u30d7\u30b7\u30e7\u30f3\u306f\u4e00\u6642\u7684\u306b\u7121\u52b9\u306b\u306a\u308a\u307e\u3059\u3002 [\u8a73\u7d30]({url})", + "zha_migration_failed": "ZHA\u3078\u306e\u79fb\u884c\u306f\u6210\u529f\u3057\u307e\u305b\u3093\u3067\u3057\u305f\u3002" + }, + "step": { + "addon_not_installed": { + "description": "\u30de\u30eb\u30c1\u30d7\u30ed\u30c8\u30b3\u30eb\u306e\u30b5\u30dd\u30fc\u30c8\u304c\u6709\u52b9\u306a\u5834\u5408\u3001 {hardware_name} \u306eIEEE 802.15.4\u7121\u7dda\u306f\u3001Zigbee\u3068Thread(Matter\u3067\u4f7f\u7528)\u306e\u4e21\u65b9\u3067\u540c\u6642\u306b\u4f7f\u7528\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002\u3053\u306e\u7121\u7dda\u304c\u3059\u3067\u306bZHA Zigbee\u7d71\u5408\u306b\u3088\u3063\u3066\u4f7f\u7528\u3055\u308c\u3066\u3044\u308b\u5834\u5408\u3001ZHA\u306f\u30de\u30eb\u30c1\u30d7\u30ed\u30c8\u30b3\u30eb\u306e\u30d5\u30a1\u30fc\u30e0\u30a6\u30a7\u30a2\u3092\u4f7f\u7528\u3059\u308b\u3088\u3046\u306b\u518d\u8a2d\u5b9a\u3055\u308c\u307e\u3059\u3002\n\n\u6ce8: \u3053\u308c\u306f\u5b9f\u9a13\u7684\u306a\u6a5f\u80fd\u3067\u3059\u3002", + "title": "IEEE 802.15.4\u7121\u7dda\u306b\u304a\u3051\u308b\u30de\u30eb\u30c1\u30d7\u30ed\u30c8\u30b3\u30eb\u306e\u30b5\u30dd\u30fc\u30c8\u3092\u6709\u52b9\u5316\u3059\u308b" + }, + "show_revert_guide": { + "description": "Zigbee\u5c02\u7528\u30d5\u30a1\u30fc\u30e0\u30a6\u30a7\u30a2\u306b\u5909\u66f4\u3059\u308b\u5834\u5408\u306f\u3001\u4ee5\u4e0b\u306e\u624b\u9806\u3092\u624b\u52d5\u3067\u884c\u3063\u3066\u304f\u3060\u3055\u3044\u3002\n\n * Silicon Labs Multiprotocol\u30a2\u30c9\u30aa\u30f3\u3092\u524a\u9664\u3057\u307e\u3059\u3002\n\n * Zigbee\u5c02\u7528\u30d5\u30a1\u30fc\u30e0\u30a6\u30a7\u30a2\u306e\u30d5\u30e9\u30c3\u30b7\u30e5\u306f\u3001https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually \u306e\u30ac\u30a4\u30c9\u306b\u5f93\u3063\u3066\u304f\u3060\u3055\u3044\u3002\n\n * \u518d\u30d5\u30e9\u30c3\u30b7\u30e5\u3055\u308c\u305f\u7121\u7dda\u6a5f\u306b\u8a2d\u5b9a\u3092\u79fb\u884c\u3059\u308b\u305f\u3081\u306bZHA\u3092\u518d\u8a2d\u5b9a\u3057\u307e\u3059\u3002" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_hardware/translations/ko.json b/homeassistant/components/homeassistant_hardware/translations/ko.json new file mode 100644 index 00000000000..5d8a9a2f040 --- /dev/null +++ b/homeassistant/components/homeassistant_hardware/translations/ko.json @@ -0,0 +1,41 @@ +{ + "silabs_multiprotocol_hardware": { + "options": { + "abort": { + "addon_set_config_failed": "Silicon Labs \ub2e4\uc911\ud504\ub85c\ud1a0\ucf5c \uad6c\uc131\uc744 \uc124\uc815\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4.", + "addon_start_failed": "Silicon Labs \ub2e4\uc911\ud504\ub85c\ud1a0\ucf5c \uc560\ub4dc\uc628\uc744 \uc2dc\uc791\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4.", + "not_hassio": "\ud558\ub4dc\uc6e8\uc5b4 \uc635\uc158\uc740 HassOS \uc124\uce58\uc5d0\uc11c\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.", + "zha_migration_failed": "ZHA \ub9c8\uc774\uadf8\ub808\uc774\uc158\uc5d0 \uc2e4\ud328\ud588\uc2b5\ub2c8\ub2e4." + }, + "error": { + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "progress": { + "install_addon": "Silicon Labs \ub2e4\uc911\ud504\ub85c\ud1a0\ucf5c \uc560\ub4dc\uc628 \uc124\uce58\uac00 \uc644\ub8cc\ub418\ub294 \ub3d9\uc548 \uae30\ub2e4\ub824 \uc8fc\uc2ed\uc2dc\uc624. \uba87 \ubd84\uc774 \uac78\ub9b4 \uc218 \uc788\uc2b5\ub2c8\ub2e4.", + "start_addon": "Silicon Labs \ub2e4\uc911\ud504\ub85c\ud1a0\ucf5c \uc560\ub4dc\uc628 \uc2dc\uc791\uc774 \uc644\ub8cc\ub420 \ub54c\uae4c\uc9c0 \uae30\ub2e4\ub9ac\uc2ed\uc2dc\uc624. \uba87 \ucd08 \uc815\ub3c4 \uac78\ub9b4 \uc218 \uc788\uc2b5\ub2c8\ub2e4." + }, + "step": { + "addon_installed_other_device": { + "title": "\ub2e4\ub978 \uae30\uae30\uc5d0 \ub300\ud574 \ub2e4\uc911 \ud504\ub85c\ud1a0\ucf5c \uc9c0\uc6d0\uc774 \uc774\ubbf8 \ud65c\uc131\ud654\ub418\uc5b4 \uc788\uc2b5\ub2c8\ub2e4." + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "\ub2e4\uc911 \ud504\ub85c\ud1a0\ucf5c \uc9c0\uc6d0 \ud65c\uc131\ud654" + }, + "description": "\ub2e4\uc911 \ud504\ub85c\ud1a0\ucf5c \uc9c0\uc6d0\uc774 \ud65c\uc131\ud654\ub418\uba74 {hardware_name} \uc758 IEEE 802.15.4 \ud1b5\uc2e0\ubc29\ubc95\uc73c\ub85c Zigbee\uc640 Thread(Matter\uc5d0\uc11c \uc0ac\uc6a9)\ub97c \ub3d9\uc2dc\uc5d0 \uc0ac\uc6a9\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4. \ud504\ub85c\ud1a0\ucf5c\uc774 ZHA Zigbee \ud1b5\ud569\uad6c\uc131\uc694\uc18c\uc5d0\uc11c \uc774\ubbf8 \uc0ac\uc6a9 \uc911\uc778 \uacbd\uc6b0 ZHA\ub294 \ub2e4\uc911 \ud504\ub85c\ud1a0\ucf5c \ud38c\uc6e8\uc5b4\ub97c \uc0ac\uc6a9\ud558\ub3c4\ub85d \uc7ac\uad6c\uc131\ub429\ub2c8\ub2e4. \n\n \ucc38\uace0: \uc774\uac83\uc740 \uc2e4\ud5d8\uc801\uc778 \uae30\ub2a5\uc785\ub2c8\ub2e4.", + "title": "IEEE 802.15.4 \ud1b5\uc2e0\ubc29\ubc95\uc5d0\uc11c \ub2e4\uc911\ud504\ub85c\ud1a0\ucf5c \uc9c0\uc6d0 \ud65c\uc131\ud654" + }, + "install_addon": { + "title": "Silicon Labs \ub2e4\uc911\ud504\ub85c\ud1a0\ucf5c \uc560\ub4dc\uc628\uc744 \uc124\uce58 \uc911\uc785\ub2c8\ub2e4" + }, + "show_revert_guide": { + "description": "Zigbee \uc804\uc6a9 \ud38c\uc6e8\uc5b4\ub85c \ubcc0\uacbd\ud558\ub824\uba74 \ub2e4\uc74c \uc548\ub0b4\ub97c \ub530\ub974\uc138\uc694. \n\n * Silicon Labs \ub2e4\uc911\ud504\ub85c\ud1a0\ucf5c \uc560\ub4dc\uc628 \uc81c\uac70 \n\n * Zigbee \uc804\uc6a9 \ud38c\uc6e8\uc5b4\ub97c \ud50c\ub798\uc2dc. \ub2e4\uc74c \uc548\ub0b4\ub97c \ub530\ub974\uc138\uc694. (https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually)\n\n * ZHA\ub97c \uc7ac\uad6c\uc131\ud558\uc5ec \ubc14\ub010 \ud1b5\uc2e0\ubc29\ubc95\uc744 \uc801\uc6a9", + "title": "\uc774 \uae30\uae30\uc5d0 \ub2e4\uc911\ud504\ub85c\ud1a0\ucf5c \uc774 \uc0ac\uc6a9\ub418\ub3c4\ub85d \uc124\uc815\ub418\uc5c8\uc2b5\ub2c8\ub2e4." + }, + "start_addon": { + "title": "Silicon Labs \ub2e4\uc911\ud504\ub85c\ud1a0\ucf5c \uc560\ub4dc\uc628\uc774 \uc2dc\uc791\ub429\ub2c8\ub2e4." + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_hardware/translations/nl.json b/homeassistant/components/homeassistant_hardware/translations/nl.json new file mode 100644 index 00000000000..a3049ab3602 --- /dev/null +++ b/homeassistant/components/homeassistant_hardware/translations/nl.json @@ -0,0 +1,20 @@ +{ + "silabs_multiprotocol_hardware": { + "options": { + "abort": { + "zha_migration_failed": "De ZHA migratie is mislukt." + }, + "error": { + "unknown": "Onverwachte fout" + }, + "step": { + "addon_not_installed": { + "description": "Wanneer multi-protocol ondersteuning is ingeschakeld, kan de IEEE 802.15.4 toegangspunt van {hardware_name} gelijktijdig voor zowel Zigbee als Thread (gebruikt door Matter) worden ingezet. Als het toegangspunt al in gebruik is door de ZHA Zigbee integratie, dan zal ZHA worden geconfigureerd om de multi-protocol firmware te gebruiken.\n\nLet op: Dit is een experimentele functionaliteit." + }, + "show_revert_guide": { + "description": "Als je wilt overschakelen naar Zigbee-only firmware, volg dan de volgende stappen:\n\n* Verwijder de Silicon Laps multi-protocol add-on\n \n* Flash de Zigbee-only firmware, volg hiervoor de aanwijzingen op https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually\n\n* Herconfigureer ZHA om de instellingen te migreren naar het ge-flash-te apparaat" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_hardware/translations/no.json b/homeassistant/components/homeassistant_hardware/translations/no.json new file mode 100644 index 00000000000..0c1ea31d02a --- /dev/null +++ b/homeassistant/components/homeassistant_hardware/translations/no.json @@ -0,0 +1,44 @@ +{ + "silabs_multiprotocol_hardware": { + "options": { + "abort": { + "addon_info_failed": "Kunne ikke hente informasjon om tilleggsprogrammet for Silicon Labs Multiprotocol.", + "addon_install_failed": "Kunne ikke installere Silicon Labs Multiprotocol-tillegget.", + "addon_set_config_failed": "Kunne ikke angi Silicon Labs Multiprotocol-konfigurasjon.", + "addon_start_failed": "Kunne ikke starte Silicon Labs Multiprotocol-tillegget.", + "disabled_due_to_bug": "Maskinvarealternativene er midlertidig deaktivert mens vi fikser en feil. [Finn ut mer]( {url} )", + "not_hassio": "Maskinvarealternativene kan bare konfigureres p\u00e5 HassOS-installasjoner.", + "zha_migration_failed": "ZHA-migreringen lyktes ikke." + }, + "error": { + "unknown": "Uventet feil" + }, + "progress": { + "install_addon": "Vennligst vent mens installasjonen av Silicon Labs Multiprotocol-tillegget fullf\u00f8res. Dette kan ta flere minutter.", + "start_addon": "Vennligst vent mens oppstarten av Silicon Labs Multiprotocol-tillegget fullf\u00f8res. Dette kan ta noen sekunder." + }, + "step": { + "addon_installed_other_device": { + "title": "Multiprotokollst\u00f8tte er allerede aktivert for en annen enhet" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "Aktiver st\u00f8tte for multiprotokoll" + }, + "description": "N\u00e5r multiprotokollst\u00f8tte er aktivert, kan {hardware_name} sin IEEE 802.15.4-radio brukes for b\u00e5de Zigbee og Thread (brukt av Matter) samtidig. Hvis radioen allerede brukes av ZHA Zigbee-integrasjonen, vil ZHA bli rekonfigurert til \u00e5 bruke multiprotokoll-fastvaren. \n\n Merk: Dette er en eksperimentell funksjon.", + "title": "Aktiver st\u00f8tte for multiprotokoll p\u00e5 IEEE 802.15.4-radioen" + }, + "install_addon": { + "title": "Silicon Labs Multiprotocol-tilleggsinstallasjonen har startet" + }, + "show_revert_guide": { + "description": "Hvis du vil bytte til kun Zigbee-firmware, m\u00e5 du fullf\u00f8re f\u00f8lgende manuelle trinn: \n\n * Fjern Silicon Labs Multiprotocol-tillegget \n\n * Flash den eneste Zigbee-fastvaren, f\u00f8lg veiledningen p\u00e5 https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually. \n\n * Konfigurer ZHA p\u00e5 nytt for \u00e5 migrere innstillinger til radioen med oppdatering", + "title": "Multiprotokollst\u00f8tte er aktivert for denne enheten" + }, + "start_addon": { + "title": "Silicon Labs Multiprotocol-tillegget starter." + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_hardware/translations/pl.json b/homeassistant/components/homeassistant_hardware/translations/pl.json new file mode 100644 index 00000000000..727350d066a --- /dev/null +++ b/homeassistant/components/homeassistant_hardware/translations/pl.json @@ -0,0 +1,44 @@ +{ + "silabs_multiprotocol_hardware": { + "options": { + "abort": { + "addon_info_failed": "Nie uda\u0142o si\u0119 pobra\u0107 informacji o dodatku Silicon Labs Multiprotocol.", + "addon_install_failed": "Nie uda\u0142o si\u0119 zainstalowa\u0107 dodatku Silicon Labs Multiprotocol.", + "addon_set_config_failed": "Nie uda\u0142o si\u0119 ustawi\u0107 konfiguracji Silicon Labs Multiprotocol.", + "addon_start_failed": "Nie uda\u0142o si\u0119 uruchomi\u0107 dodatku Silicon Labs Multiprotocol.", + "disabled_due_to_bug": "Opcje sprz\u0119towe s\u0105 tymczasowo wy\u0142\u0105czone na czas naprawiania przez nas b\u0142\u0119du. [Dowiedz si\u0119 wi\u0119cej]({url})", + "not_hassio": "Opcje sprz\u0119towe mo\u017cna skonfigurowa\u0107 tylko w instalacjach HassOS.", + "zha_migration_failed": "Migracja ZHA nie powiod\u0142a si\u0119." + }, + "error": { + "unknown": "Nieoczekiwany b\u0142\u0105d" + }, + "progress": { + "install_addon": "Poczekaj, a\u017c zako\u0144czy si\u0119 instalacja dodatku Silicon Labs Multiprotocol. Mo\u017ce to potrwa\u0107 kilka minut.", + "start_addon": "Poczekaj, a\u017c zako\u0144czy si\u0119 uruchamianie dodatku Silicon Labs Multiprotocol. Mo\u017ce to potrwa\u0107 kilka sekund." + }, + "step": { + "addon_installed_other_device": { + "title": "Obs\u0142uga Multiprotocol jest ju\u017c w\u0142\u0105czona dla innego urz\u0105dzenia" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "W\u0142\u0105cz obs\u0142ug\u0119 Multiprotocol" + }, + "description": "Gdy w\u0142\u0105czona jest obs\u0142uga Multiprotocol, radio IEEE 802.15.4 dla {hardware_name} mo\u017ce by\u0107 u\u017cywane jednocze\u015bnie dla Zigbee i Thread (u\u017cywane przez Matter). Uwaga: jest to funkcja eksperymentalna. Je\u015bli radio jest ju\u017c u\u017cywane przez integracj\u0119 ZHA Zigbee, ZHA zostanie ponownie skonfigurowane, aby korzysta\u0107 z oprogramowania multiprotocol.\n\nUwaga: jest to funkcja eksperymentalna.", + "title": "W\u0142\u0105cz obs\u0142ug\u0119 multiprotocol w radiu IEEE 802.15.4" + }, + "install_addon": { + "title": "Rozpocz\u0119\u0142a si\u0119 instalacja dodatku Silicon Labs Multiprotocol" + }, + "show_revert_guide": { + "description": "Je\u015bli chcesz zmieni\u0107 oprogramowanie obs\u0142uguj\u0105ce tylko Zigbee, wykonaj nast\u0119puj\u0105ce czynno\u015bci r\u0119czne: \n\n* Usu\u0144 dodatek Silicon Labs Multiprotocol \n\n* Wgraj oprogramowanie tylko dla Zigbee, post\u0119puj zgodnie z instrukcjami na stronie https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually. \n\n* Ponownie skonfiguruj ZHA, aby przeprowadzi\u0107 migracj\u0119 ustawie\u0144 do przeprogramowanego radia", + "title": "Obs\u0142uga multiprotocol jest w\u0142\u0105czona dla tego urz\u0105dzenia" + }, + "start_addon": { + "title": "Uruchamianie dodatku Silicon Labs Multiprotocol." + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_hardware/translations/pt-BR.json b/homeassistant/components/homeassistant_hardware/translations/pt-BR.json new file mode 100644 index 00000000000..2f6bad4a651 --- /dev/null +++ b/homeassistant/components/homeassistant_hardware/translations/pt-BR.json @@ -0,0 +1,44 @@ +{ + "silabs_multiprotocol_hardware": { + "options": { + "abort": { + "addon_info_failed": "Falha ao obter informa\u00e7\u00f5es do add-on Silicon Labs Multiprotocol.", + "addon_install_failed": "Falha ao instalar o add-on Silicon Labs Multiprotocol.", + "addon_set_config_failed": "Falha ao definir a configura\u00e7\u00e3o multiprotocolo da Silicon Labs.", + "addon_start_failed": "Falha ao iniciar o add-on Silicon Labs Multiprotocol.", + "disabled_due_to_bug": "As op\u00e7\u00f5es de hardware est\u00e3o temporariamente desativadas enquanto corrigimos um bug. [Saiba mais]({url})", + "not_hassio": "As op\u00e7\u00f5es de hardware s\u00f3 podem ser configuradas em instala\u00e7\u00f5es HassOS.", + "zha_migration_failed": "A migra\u00e7\u00e3o ZHA n\u00e3o foi bem-sucedida." + }, + "error": { + "unknown": "Erro inesperado" + }, + "progress": { + "install_addon": "Aguarde enquanto a instala\u00e7\u00e3o do add-on Silicon Labs Multiprotocol \u00e9 conclu\u00edda. Isso pode levar v\u00e1rios minutos.", + "start_addon": "Aguarde enquanto a inicializa\u00e7\u00e3o do add-on Silicon Labs Multiprotocol \u00e9 conclu\u00edda. Isso pode levar alguns segundos." + }, + "step": { + "addon_installed_other_device": { + "title": "O suporte multiprotocolo j\u00e1 est\u00e1 ativado para outro dispositivo" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "Ativar suporte multiprotocolo" + }, + "description": "Quando o suporte multiprotocolo est\u00e1 ativado, o r\u00e1dio IEEE 802.15.4 do {hardware_name} pode ser usado para Zigbee e Thread (usado por Matter) ao mesmo tempo. Se o r\u00e1dio j\u00e1 estiver sendo usado pela integra\u00e7\u00e3o ZHA Zigbee, o ZHA ser\u00e1 reconfigurado para usar o firmware multiprotocolo. \n\n Nota: Esse \u00e9 um recurso experimental.", + "title": "Habilite o suporte multiprotocolo no r\u00e1dio IEEE 802.15.4" + }, + "install_addon": { + "title": "A instala\u00e7\u00e3o do add-on Silicon Labs Multiprotocol foi iniciada" + }, + "show_revert_guide": { + "description": "Se voc\u00ea deseja alterar para o firmware somente Zigbee, conclua as seguintes etapas manuais: \n\n * Remova o add-on Silicon Labs Multiprotocol \n\n * Atualize apenas o firmware Zigbee, siga o guia em https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually. \n\n * Reconfigure o ZHA para migrar as configura\u00e7\u00f5es para o r\u00e1dio reflashed", + "title": "O suporte multiprotocolo est\u00e1 ativado para este dispositivo" + }, + "start_addon": { + "title": "O add-on Silicon Labs Multiprotocol est\u00e1 iniciando." + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_hardware/translations/pt.json b/homeassistant/components/homeassistant_hardware/translations/pt.json new file mode 100644 index 00000000000..9d0117dca04 --- /dev/null +++ b/homeassistant/components/homeassistant_hardware/translations/pt.json @@ -0,0 +1,9 @@ +{ + "silabs_multiprotocol_hardware": { + "options": { + "error": { + "unknown": "Erro inesperado" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_hardware/translations/ru.json b/homeassistant/components/homeassistant_hardware/translations/ru.json new file mode 100644 index 00000000000..e7ec2d66eb2 --- /dev/null +++ b/homeassistant/components/homeassistant_hardware/translations/ru.json @@ -0,0 +1,44 @@ +{ + "silabs_multiprotocol_hardware": { + "options": { + "abort": { + "addon_info_failed": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0438 \"Silicon Labs Multiprotocol\".", + "addon_install_failed": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \"Silicon Labs Multiprotocol\".", + "addon_set_config_failed": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \"Silicon Labs Multiprotocol\".", + "addon_start_failed": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \"Silicon Labs Multiprotocol\".", + "disabled_due_to_bug": "\u0410\u043f\u043f\u0430\u0440\u0430\u0442\u043d\u044b\u0435 \u043e\u043f\u0446\u0438\u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u044b, \u043f\u043e\u043a\u0430 \u043c\u044b \u0438\u0441\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u043e\u0448\u0438\u0431\u043a\u0443. [\u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435]({url})", + "not_hassio": "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u043e\u0431\u043e\u0440\u0443\u0434\u043e\u0432\u0430\u043d\u0438\u044f \u043c\u043e\u0436\u043d\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0445 \u0441\u0438\u0441\u0442\u0435\u043c\u0430\u0445 HassOS.", + "zha_migration_failed": "\u041c\u0438\u0433\u0440\u0430\u0446\u0438\u044f ZHA \u043d\u0435 \u0443\u0434\u0430\u043b\u0430\u0441\u044c." + }, + "error": { + "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." + }, + "progress": { + "install_addon": "\u041f\u043e\u0434\u043e\u0436\u0434\u0438\u0442\u0435, \u043f\u043e\u043a\u0430 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u0441\u044f \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \"Silicon Labs Multiprotocol\". \u042d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u043d\u044f\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043c\u0438\u043d\u0443\u0442.", + "start_addon": "\u041f\u043e\u0434\u043e\u0436\u0434\u0438\u0442\u0435, \u043f\u043e\u043a\u0430 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u0441\u044f \u0437\u0430\u043f\u0443\u0441\u043a \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \"Silicon Labs Multiprotocol\". \u042d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u043d\u044f\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u0435\u043a\u0443\u043d\u0434." + }, + "step": { + "addon_installed_other_device": { + "title": "\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u0432 \u0443\u0436\u0435 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0430 \u0434\u043b\u044f \u0434\u0440\u0443\u0433\u043e\u0433\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u0432" + }, + "description": "\u041a\u043e\u0433\u0434\u0430 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0430 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u0432, \u0440\u0430\u0434\u0438\u043e\u043c\u043e\u0434\u0443\u043b\u044c {hardware_name} \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u0430 IEEE 802.15.4 \u043c\u043e\u0436\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u043a\u0430\u043a \u0434\u043b\u044f Zigbee, \u0442\u0430\u043a \u0438 \u0434\u043b\u044f Thread (\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0432 Matter). \u0415\u0441\u043b\u0438 \u0440\u0430\u0434\u0438\u043e\u043c\u043e\u0434\u0443\u043b\u044c \u0443\u0436\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d \u0432 ZHA \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u0430 Zigbee, ZHA \u0431\u0443\u0434\u0435\u0442 \u043f\u0435\u0440\u0435\u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u0430 \u0434\u043b\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u0432.\n\n\u041f\u0440\u0438\u043c\u0435\u0447\u0430\u043d\u0438\u0435: \u044d\u0442\u043e \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u0430\u043b\u044c\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f.", + "title": "\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u0432 \u043d\u0430 \u0440\u0430\u0434\u0438\u043e\u043c\u043e\u0434\u0443\u043b\u0435 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u0430 IEEE 802.15.4." + }, + "install_addon": { + "title": "\u041d\u0430\u0447\u0430\u043b\u0430\u0441\u044c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \"Silicon Labs Multiprotocol\"" + }, + "show_revert_guide": { + "description": "\u0415\u0441\u043b\u0438 \u0412\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u043f\u0435\u0440\u0435\u0439\u0442\u0438 \u043d\u0430 \u043f\u0440\u043e\u0448\u0438\u0432\u043a\u0443, \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044e\u0449\u0443\u044e \u0442\u043e\u043b\u044c\u043a\u043e Zigbee, \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f:\n\n* \u0423\u0434\u0430\u043b\u0438\u0442\u0435 \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \"Silicon Labs Multiprotocol\".\n\n* \u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u0435 \u043f\u0440\u043e\u0448\u0438\u0432\u043a\u0443 Zigbee, \u0441\u043b\u0435\u0434\u0443\u044f \u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432\u0443 \u043d\u0430 \u0441\u0430\u0439\u0442\u0435 https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually.\n\n* \u041f\u0435\u0440\u0435\u043d\u0430\u0441\u0442\u0440\u043e\u0439\u0442\u0435 ZHA \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u043d\u043e\u0441\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u043d\u0430 \u0437\u0430\u043d\u043e\u0432\u043e \u043f\u0440\u043e\u0448\u0438\u0442\u044b\u0439 \u0440\u0430\u0434\u0438\u043e\u043c\u043e\u0434\u0443\u043b\u044c.", + "title": "\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0430 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u0432" + }, + "start_addon": { + "title": "\u0414\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 Silicon Labs Multiprotocol \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f." + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_hardware/translations/sk.json b/homeassistant/components/homeassistant_hardware/translations/sk.json new file mode 100644 index 00000000000..7f8c0197086 --- /dev/null +++ b/homeassistant/components/homeassistant_hardware/translations/sk.json @@ -0,0 +1,44 @@ +{ + "silabs_multiprotocol_hardware": { + "options": { + "abort": { + "addon_info_failed": "Nepodarilo sa z\u00edska\u0165 inform\u00e1cie o doplnku Silicon Labs Multiprotocol.", + "addon_install_failed": "Nepodarilo sa nain\u0161talova\u0165 doplnok Silicon Labs Multiprotocol.", + "addon_set_config_failed": "Nepodarilo sa nastavi\u0165 konfigur\u00e1ciu Silicon Labs Multiprotocol.", + "addon_start_failed": "Nepodarilo sa spusti\u0165 doplnok Silicon Labs Multiprotocol.", + "disabled_due_to_bug": "Hardv\u00e9rov\u00e9 mo\u017enosti s\u00fa do\u010dasne deaktivovan\u00e9, k\u00fdm oprav\u00edme chybu. [Viac inform\u00e1ci\u00ed]({url})", + "not_hassio": "Hardv\u00e9rov\u00e9 mo\u017enosti je mo\u017en\u00e9 nakonfigurova\u0165 iba v in\u0161tal\u00e1ci\u00e1ch HassOS.", + "zha_migration_failed": "Migr\u00e1cia ZHA nebola \u00faspe\u0161n\u00e1." + }, + "error": { + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "progress": { + "install_addon": "Po\u010dkajte, k\u00fdm sa dokon\u010d\u00ed in\u0161tal\u00e1cia doplnku Silicon Labs Multiprotocol. M\u00f4\u017ee to trva\u0165 nieko\u013eko min\u00fat.", + "start_addon": "Po\u010dkajte, k\u00fdm sa dokon\u010d\u00ed spustenie doplnku Silicon Labs Multiprotocol. M\u00f4\u017ee to trva\u0165 nieko\u013eko sek\u00fand." + }, + "step": { + "addon_installed_other_device": { + "title": "Podpora multiprotocol je u\u017e povolen\u00e1 pre in\u00e9 zariadenie" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "Povoli\u0165 podporu multiprotocol" + }, + "description": "Ke\u010f je povolen\u00e1 podpora viacer\u00fdch protokolov, r\u00e1dio {hardware_name} IEEE 802.15.4 mo\u017eno pou\u017ei\u0165 s\u00fa\u010dasne pre Zigbee aj vl\u00e1kno (pou\u017e\u00edvan\u00e9 spolo\u010dnos\u0165ou Matter). Ak u\u017e r\u00e1dio pou\u017e\u00edva integr\u00e1cia ZHA Zigbee, ZHA sa prekonfiguruje na pou\u017e\u00edvanie multiprotokolov\u00e9ho firmv\u00e9ru. \n\n Pozn\u00e1mka: Toto je experiment\u00e1lna funkcia.", + "title": "Povo\u013ete podporu viacer\u00fdch protokolov na r\u00e1diu IEEE 802.15.4" + }, + "install_addon": { + "title": "In\u0161tal\u00e1cia doplnku Silicon Labs Multiprotocol sa za\u010dala" + }, + "show_revert_guide": { + "description": "Ak chcete zmeni\u0165 firmv\u00e9r iba na Zigbee, vykonajte nasleduj\u00face manu\u00e1lne kroky: \n\n * Odstr\u00e1\u0148te doplnok Silicon Labs Multiprotocol \n\n * Flashujte iba firmv\u00e9r Zigbee, postupujte pod\u013ea n\u00e1vodu na https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually. \n\n * Prekonfigurujte ZHA na migr\u00e1ciu nastaven\u00ed do preflashovan\u00e9ho r\u00e1dia", + "title": "Pre toto zariadenie je povolen\u00e1 podpora multiprotocol" + }, + "start_addon": { + "title": "Sp\u00fa\u0161\u0165a sa doplnok Silicon Labs Multiprotocol." + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_hardware/translations/zh-Hant.json b/homeassistant/components/homeassistant_hardware/translations/zh-Hant.json new file mode 100644 index 00000000000..6bcee25b143 --- /dev/null +++ b/homeassistant/components/homeassistant_hardware/translations/zh-Hant.json @@ -0,0 +1,44 @@ +{ + "silabs_multiprotocol_hardware": { + "options": { + "abort": { + "addon_info_failed": "\u53d6\u5f97 Silicon Labs Multiprotocol \u9644\u52a0\u5143\u4ef6\u8cc7\u8a0a\u5931\u6557\u3002", + "addon_install_failed": "\u5b89\u88dd Silicon Labs Multiprotocol \u9644\u52a0\u5143\u4ef6\u5931\u6557\u3002", + "addon_set_config_failed": "\u8a2d\u5b9a Silicon Labs Multiprotocol \u9644\u52a0\u5143\u4ef6\u5931\u6557\u3002", + "addon_start_failed": "\u555f\u52d5 Silicon Labs Multiprotocol \u9644\u52a0\u5143\u4ef6\u5931\u6557\u3002", + "disabled_due_to_bug": "\u7531\u65bc\u6b63\u5728\u4fee\u6b63\u932f\u8aa4\u3001\u786c\u9ad4\u9078\u9805\u66ab\u6642\u95dc\u9589\u3002 [\u4e86\u89e3\u66f4\u591a]({url})", + "not_hassio": "\u786c\u9ad4\u9078\u9805\u50c5\u80fd\u65bc HassOS \u5b89\u88dd\u6a21\u5f0f\u9032\u884c\u8a2d\u5b9a\u3002", + "zha_migration_failed": "ZHA \u9077\u79fb\u672a\u6210\u529f\u3002" + }, + "error": { + "unknown": "\u672a\u9810\u671f\u932f\u8aa4" + }, + "progress": { + "install_addon": "\u8acb\u7a0d\u7b49 Silicon Labs Multiprotocol \u9644\u52a0\u5143\u4ef6\u5b89\u88dd\u5b8c\u6210\u3002\u53ef\u80fd\u9700\u8981\u5e7e\u5206\u9418\u3002", + "start_addon": "\u8acb\u7a0d\u7b49 Silicon Labs Multiprotocol \u9644\u52a0\u5143\u4ef6\u555f\u59cb\u5b8c\u6210\uff0c\u53ef\u80fd\u6703\u9700\u8981\u5e7e\u5206\u9418\u3002" + }, + "step": { + "addon_installed_other_device": { + "title": "Multiprotocol \u652f\u63f4\u5df2\u7d93\u65bc\u5176\u4ed6\u88dd\u7f6e\u958b\u555f" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "\u555f\u7528 Multiprotocol \u652f\u63f4" + }, + "description": "\u7576\u555f\u7528 Multiprotocol \u652f\u63f4\u6642\u3001{hardware_name} \u7684 IEEE 802.15.4 radio \u53ef\u540c\u6642\u4f5c\u70ba Zigbee \u8207 Thread \uff08\u7528\u65bc Matter\uff09\u4f7f\u7528\u3002\u5047\u5982 Radio \u5df2\u7d93\u88ab ZHA Zigbee \u6574\u5408\u6240\u4f7f\u7528\u3001ZHA \u5c07\u6703\u9032\u884c\u91cd\u65b0\u8a2d\u5b9a\u4ee5\u4f7f\u7528 Multiprotocol \u97cc\u9ad4\u3002\n\n\u6ce8\u610f\uff1a\u76ee\u524d\u70ba\u5be6\u9a57\u6027\u529f\u80fd\u3002", + "title": "\u65bc IEEE 802.15.4 radio \u4e0a\u555f\u7528 Multiprotocol \u652f\u63f4" + }, + "install_addon": { + "title": "Silicon Labs Multiprotocol \u9644\u52a0\u5143\u4ef6\u5df2\u555f\u7528\u3002" + }, + "show_revert_guide": { + "description": "\u5047\u5982\u60f3\u8b8a\u66f4\u70ba\u50c5 Zigbee \u97cc\u9ad4\u3001\u8acb\u5b8c\u6210\u4ee5\u4e0b\u624b\u52d5\u6b65\u9a5f\uff1a\n\n * \u79fb\u9664 Silicon Labs Multiprotocol \u9644\u52a0\u5143\u4ef6\n\n * \u5237\u5165\u50c5 Zigbee \u97cc\u9ad4\uff0c\u8acb\u53c3\u95b1\u64cd\u4f5c\u6307\u5f15 https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually\u3002\n\n * \u91cd\u65b0\u8a2d\u5b9a ZHA \u4ee5\u9077\u79fb\u8a2d\u5b9a\u81f3\u91cd\u65b0\u5237\u5165\u7684 Radio", + "title": "\u88dd\u7f6e\u4e4b Multiprotocol \u652f\u63f4\u5df2\u958b\u555f" + }, + "start_addon": { + "title": "Silicon Labs Multiprotocol \u9644\u52a0\u5143\u4ef6\u555f\u7528\u4e2d\u3002" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_sky_connect/__init__.py b/homeassistant/components/homeassistant_sky_connect/__init__.py index e65394ca15c..08d54bdef12 100644 --- a/homeassistant/components/homeassistant_sky_connect/__init__.py +++ b/homeassistant/components/homeassistant_sky_connect/__init__.py @@ -25,7 +25,9 @@ from .util import get_usb_service_info _LOGGER = logging.getLogger(__name__) -async def _multi_pan_addon_info(hass, entry: ConfigEntry) -> AddonInfo | None: +async def _multi_pan_addon_info( + hass: HomeAssistant, entry: ConfigEntry +) -> AddonInfo | None: """Return AddonInfo if the multi-PAN addon is enabled for our SkyConnect.""" if not is_hassio(hass): return None diff --git a/homeassistant/components/homeassistant_sky_connect/translations/bg.json b/homeassistant/components/homeassistant_sky_connect/translations/bg.json new file mode 100644 index 00000000000..36086dcaf64 --- /dev/null +++ b/homeassistant/components/homeassistant_sky_connect/translations/bg.json @@ -0,0 +1,27 @@ +{ + "options": { + "abort": { + "not_hassio": "\u0425\u0430\u0440\u0434\u0443\u0435\u0440\u043d\u0438\u0442\u0435 \u043e\u043f\u0446\u0438\u0438 \u043c\u043e\u0433\u0430\u0442 \u0434\u0430 \u0431\u044a\u0434\u0430\u0442 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0438 \u0441\u0430\u043c\u043e \u043f\u0440\u0438 \u0438\u043d\u0441\u0442\u0430\u043b\u0430\u0446\u0438\u0438 \u043d\u0430 HassOS.", + "zha_migration_failed": "\u041c\u0438\u0433\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u043d\u0430 ZHA \u043d\u0435 \u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430." + }, + "error": { + "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" + }, + "step": { + "addon_installed_other_device": { + "title": "\u041c\u043d\u043e\u0433\u043e\u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043d\u0430\u0442\u0430 \u043f\u043e\u0434\u0434\u0440\u044a\u0436\u043a\u0430 \u0432\u0435\u0447\u0435 \u0435 \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u0430\u043d\u0430 \u0437\u0430 \u0434\u0440\u0443\u0433\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "\u0410\u043a\u0442\u0438\u0432\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u043c\u043d\u043e\u0433\u043e\u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043d\u0430 \u043f\u043e\u0434\u0434\u0440\u044a\u0436\u043a\u0430" + } + }, + "install_addon": { + "title": "\u0418\u043d\u0441\u0442\u0430\u043b\u0438\u0440\u0430\u043d\u0435\u0442\u043e \u043d\u0430 \u0434\u043e\u0431\u0430\u0432\u043a\u0430\u0442\u0430 Silicon Labs Multiprotocol \u0437\u0430\u043f\u043e\u0447\u043d\u0430" + }, + "show_revert_guide": { + "title": "\u041c\u043d\u043e\u0433\u043e\u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043d\u0430\u0442\u0430 \u043f\u043e\u0434\u0434\u0440\u044a\u0436\u043a\u0430 \u0435 \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u0430\u043d\u0430 \u0437\u0430 \u0442\u043e\u0432\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_sky_connect/translations/ca.json b/homeassistant/components/homeassistant_sky_connect/translations/ca.json new file mode 100644 index 00000000000..07bb2ae05aa --- /dev/null +++ b/homeassistant/components/homeassistant_sky_connect/translations/ca.json @@ -0,0 +1,42 @@ +{ + "options": { + "abort": { + "addon_info_failed": "No s'ha pogut obtenir la informaci\u00f3 del complement Silicon Labs Multiprotocol.", + "addon_install_failed": "No s'ha pogut instal\u00b7lar el complement Silicon Labs Multiprotocol-", + "addon_set_config_failed": "No s'ha pogut establir la configuraci\u00f3 de Silicon Labs Multiprotocol.", + "addon_start_failed": "No s'ha pogut iniciar el complement Silicon Labs Multiprotocol.", + "disabled_due_to_bug": "Les opcions de maquinari s'han desactivat temporalment mentre es corregeix un error. [M\u00e9s informaci\u00f3]({url})", + "not_hassio": "Les opcions de maquinari nom\u00e9s es poden configurar a les instal\u00b7lacions HassOS.", + "zha_migration_failed": "La migraci\u00f3 ZHA no ha tingut \u00e8xit." + }, + "error": { + "unknown": "Error inesperat" + }, + "progress": { + "install_addon": "Espera mentre finalitza la instal\u00b7laci\u00f3 del complement Silicon Labs Multiprotocol. Pot tardar uns minuts.", + "start_addon": "Espera mentre es completa la inicialitzaci\u00f3 del complement Silicon Labs Multiprotocol. Pot tardar uns segons." + }, + "step": { + "addon_installed_other_device": { + "title": "El suport multiprotocol ja est\u00e0 activat per a un altre dispositiu" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "Activa el suport multiprotocol" + }, + "description": "Quan el suport multiprotocol est\u00e0 activat, la r\u00e0dio IEEE 802.15.4 de {hardware_name} es pot utilitzar tant per a Zigbee com per a Thread alhora (utilitzat per Matter). Si la r\u00e0dio ja utilitza la integraci\u00f3 ZHA Zigbee, ZHA es reconfigurar\u00e0 per utilitzar el microprogramari multiprotocol. \n\nNota: aquesta \u00e9s una caracter\u00edstica experimental.", + "title": "Activa el suport multiprotocol per l'est\u00e0ndard de r\u00e0dio IEEE 802.15.4" + }, + "install_addon": { + "title": "Ha comen\u00e7at la instal\u00b7laci\u00f3 del complement Silicon Labs Multiprotocol" + }, + "show_revert_guide": { + "description": "Si vols canviar a nom\u00e9s microprogramari Zigbee, completa els passos manuals seg\u00fcents: \n\n * Elimina el complement Silicon Labs Multiprotocol\n\n * Carrega el microprogramari (firmware) de Zigbee, segueix la guia a https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually. \n\n * Torna a configurar ZHA per migrar la configuraci\u00f3 al dispositiu r\u00e0dio actualitzat", + "title": "El suport multiprotocol est\u00e0 activat per a aquest dispositiu" + }, + "start_addon": { + "title": "El complement Silicon Labs Multiprotocol s'est\u00e0 iniciant." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_sky_connect/translations/de.json b/homeassistant/components/homeassistant_sky_connect/translations/de.json new file mode 100644 index 00000000000..9282520684a --- /dev/null +++ b/homeassistant/components/homeassistant_sky_connect/translations/de.json @@ -0,0 +1,42 @@ +{ + "options": { + "abort": { + "addon_info_failed": "Silicon Labs Multiprotokoll-Zusatzinformationen konnten nicht abgerufen werden.", + "addon_install_failed": "Die Installation des Silicon Labs Multiprotokoll Add-Ons ist fehlgeschlagen.", + "addon_set_config_failed": "Die Silicon Labs Multiprotokoll Konfiguration konnte nicht eingestellt werden.", + "addon_start_failed": "Das Silicon Labs Multiprotokoll Add-on konnte nicht gestartet werden.", + "disabled_due_to_bug": "Die Hardwareoptionen sind vor\u00fcbergehend deaktiviert, w\u00e4hrend wir einen Fehler beheben. [Weitere Informationen]({url})", + "not_hassio": "Die Hardwareoptionen k\u00f6nnen nur auf HassOS-Installationen konfiguriert werden.", + "zha_migration_failed": "Die ZHA Migration war nicht erfolgreich." + }, + "error": { + "unknown": "Unerwarteter Fehler" + }, + "progress": { + "install_addon": "Bitte warte, bis die Installation des Silicon Labs Multiprotokoll Add-ons abgeschlossen ist. Dies kann einige Minuten dauern.", + "start_addon": "Bitte warte, bis der Start des Silicon Labs Multiprotokoll Add-Ons abgeschlossen ist. Dies kann einige Sekunden dauern." + }, + "step": { + "addon_installed_other_device": { + "title": "Die Multiprotokoll Unterst\u00fctzung ist bereits f\u00fcr ein anderes Ger\u00e4t aktiviert" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "Multiprotokoll Unterst\u00fctzung aktivieren" + }, + "description": "Wenn die Multiprotokoll Unterst\u00fctzung aktiviert ist, kann das IEEE 802.15.4-Funkger\u00e4t von {hardware_name} gleichzeitig f\u00fcr Zigbee und Thread (verwendet von Matter) verwendet werden. Wenn das Funkger\u00e4t bereits von der Zigbee-Integration des ZHA verwendet wird, wird der ZHA neu konfiguriert, um die Multiprotokoll-Firmware zu verwenden.\n\nHinweis: Dies ist eine experimentelle Funktion.", + "title": "Aktiviere die Multiprotokoll Unterst\u00fctzung auf dem IEEE 802.15.4-Funkger\u00e4t" + }, + "install_addon": { + "title": "Die Installation des Silicon Labs Multiprotokoll Add-Ons hat begonnen" + }, + "show_revert_guide": { + "description": "Wenn du zu einer reinen Zigbee-Firmware wechseln m\u00f6chtest, f\u00fchre bitte die folgenden manuellen Schritte aus:\n\n * Entferne das Silicon Labs Multiprotokoll-Add-On\n\n * Flashe die reine Zigbee-Firmware, folge der Anleitung unter https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually.\n\n * Rekonfiguriere ZHA, um die Einstellungen auf das neu geflashte Funkger\u00e4t zu migrieren.", + "title": "Multiprotokoll Unterst\u00fctzung ist f\u00fcr dieses Ger\u00e4t aktiviert" + }, + "start_addon": { + "title": "Das Silicon Labs Multiprotokoll Add-on wird gestartet." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_sky_connect/translations/el.json b/homeassistant/components/homeassistant_sky_connect/translations/el.json new file mode 100644 index 00000000000..00aa8684ad1 --- /dev/null +++ b/homeassistant/components/homeassistant_sky_connect/translations/el.json @@ -0,0 +1,42 @@ +{ + "options": { + "abort": { + "addon_info_failed": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03bb\u03ae\u03c8\u03b7\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03b9\u03ce\u03bd \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf\u03c5 Silicon Labs Multiprotocol.", + "addon_install_failed": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03b5\u03b3\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf\u03c5 Silicon Labs Multiprotocol.", + "addon_set_config_failed": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7\u03c2 \u03c0\u03b1\u03c1\u03b1\u03bc\u03ad\u03c4\u03c1\u03c9\u03bd \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ce\u03bd \u03c0\u03c1\u03c9\u03c4\u03bf\u03ba\u03cc\u03bb\u03bb\u03c9\u03bd Silicon Labs.", + "addon_start_failed": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03b5\u03ba\u03ba\u03af\u03bd\u03b7\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf\u03c5 Silicon Labs Multiprotocol.", + "disabled_due_to_bug": "\u039f\u03b9 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03c5\u03bb\u03b9\u03ba\u03bf\u03cd \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03bf\u03cd\u03bd\u03c4\u03b1\u03b9 \u03c0\u03c1\u03bf\u03c3\u03c9\u03c1\u03b9\u03bd\u03ac \u03b5\u03bd\u03ce \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03bd\u03bf\u03c5\u03bc\u03b5 \u03ad\u03bd\u03b1 \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1. [\u039c\u03ac\u03b8\u03b5\u03c4\u03b5 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b1]({url})", + "not_hassio": "\u039f\u03b9 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03c5\u03bb\u03b9\u03ba\u03bf\u03cd \u03bc\u03c0\u03bf\u03c1\u03bf\u03cd\u03bd \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03bf\u03cd\u03bd \u03bc\u03cc\u03bd\u03bf \u03c3\u03b5 \u03b5\u03b3\u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03ac\u03c3\u03b5\u03b9\u03c2 HassOS.", + "zha_migration_failed": "\u0397 \u03bc\u03b5\u03c4\u03ac\u03b2\u03b1\u03c3\u03b7 ZHA \u03b4\u03b5\u03bd \u03c0\u03ad\u03c4\u03c5\u03c7\u03b5." + }, + "error": { + "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" + }, + "progress": { + "install_addon": "\u03a0\u03b5\u03c1\u03b9\u03bc\u03ad\u03bd\u03b5\u03c4\u03b5 \u03bc\u03ad\u03c7\u03c1\u03b9 \u03bd\u03b1 \u03bf\u03bb\u03bf\u03ba\u03bb\u03b7\u03c1\u03c9\u03b8\u03b5\u03af \u03b7 \u03b5\u03b3\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf\u03c5 Silicon Labs Multiprotocol. \u0391\u03c5\u03c4\u03cc \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03b4\u03b9\u03b1\u03c1\u03ba\u03ad\u03c3\u03b5\u03b9 \u03b1\u03c1\u03ba\u03b5\u03c4\u03ac \u03bb\u03b5\u03c0\u03c4\u03ac.", + "start_addon": "\u03a0\u03b5\u03c1\u03b9\u03bc\u03ad\u03bd\u03b5\u03c4\u03b5 \u03bc\u03ad\u03c7\u03c1\u03b9 \u03bd\u03b1 \u03bf\u03bb\u03bf\u03ba\u03bb\u03b7\u03c1\u03c9\u03b8\u03b5\u03af \u03b7 \u03ad\u03bd\u03b1\u03c1\u03be\u03b7 \u03c4\u03bf\u03c5 \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf\u03c5 Silicon Labs Multiprotocol. \u0391\u03c5\u03c4\u03cc \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03b4\u03b9\u03b1\u03c1\u03ba\u03ad\u03c3\u03b5\u03b9 \u03bc\u03b5\u03c1\u03b9\u03ba\u03ac \u03b4\u03b5\u03c5\u03c4\u03b5\u03c1\u03cc\u03bb\u03b5\u03c0\u03c4\u03b1." + }, + "step": { + "addon_installed_other_device": { + "title": "\u0397 \u03c5\u03c0\u03bf\u03c3\u03c4\u03ae\u03c1\u03b9\u03be\u03b7 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ce\u03bd \u03c0\u03c1\u03c9\u03c4\u03bf\u03ba\u03cc\u03bb\u03bb\u03c9\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03ae\u03b4\u03b7 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7 \u03b3\u03b9\u03b1 \u03ac\u03bb\u03bb\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "\u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03ae\u03c1\u03b9\u03be\u03b7 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ce\u03bd \u03c0\u03c1\u03c9\u03c4\u03bf\u03ba\u03cc\u03bb\u03bb\u03c9\u03bd" + }, + "description": "\u038c\u03c4\u03b1\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7 \u03b7 \u03c5\u03c0\u03bf\u03c3\u03c4\u03ae\u03c1\u03b9\u03be\u03b7 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ce\u03bd \u03c0\u03c1\u03c9\u03c4\u03bf\u03ba\u03cc\u03bb\u03bb\u03c9\u03bd, \u03bf \u03c0\u03bf\u03bc\u03c0\u03bf\u03b4\u03ad\u03ba\u03c4\u03b7\u03c2 IEEE 802.15.4 \u03c4\u03bf\u03c5 {hardware_name} \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b7\u03b8\u03b5\u03af \u03c4\u03b1\u03c5\u03c4\u03cc\u03c7\u03c1\u03bf\u03bd\u03b1 \u03ba\u03b1\u03b9 \u03b3\u03b9\u03b1 \u03c4\u03bf Zigbee \u03ba\u03b1\u03b9 \u03b3\u03b9\u03b1 \u03c4\u03bf Thread (\u03c0\u03bf\u03c5 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03bf Matter). \u0395\u03ac\u03bd \u03bf \u03c0\u03bf\u03bc\u03c0\u03bf\u03b4\u03ad\u03ba\u03c4\u03b7\u03c2 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03ae\u03b4\u03b7 \u03b1\u03c0\u03cc \u03c4\u03b7\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 ZHA Zigbee, \u03c4\u03bf ZHA \u03b8\u03b1 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03ce\u03c3\u03c4\u03b5 \u03bd\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af \u03c4\u03bf \u03c5\u03bb\u03b9\u03ba\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03bc\u03b9\u03ba\u03cc \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ce\u03bd \u03c0\u03c1\u03c9\u03c4\u03bf\u03ba\u03cc\u03bb\u03bb\u03c9\u03bd. \n\n \u03a3\u03b7\u03bc\u03b5\u03af\u03c9\u03c3\u03b7: \u0391\u03c5\u03c4\u03cc \u03b5\u03af\u03bd\u03b1\u03b9 \u03ad\u03bd\u03b1 \u03c0\u03b5\u03b9\u03c1\u03b1\u03bc\u03b1\u03c4\u03b9\u03ba\u03cc \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03b7\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc.", + "title": "\u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03ae\u03c1\u03b9\u03be\u03b7 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ce\u03bd \u03c0\u03c1\u03c9\u03c4\u03bf\u03ba\u03cc\u03bb\u03bb\u03c9\u03bd \u03c3\u03c4\u03bd \u03c0\u03bf\u03bc\u03c0\u03bf\u03b4\u03ad\u03ba\u03c4\u03b7 IEEE 802.15.4" + }, + "install_addon": { + "title": "\u0397 \u03b5\u03b3\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf\u03c5 Silicon Labs Multiprotocol \u03ad\u03c7\u03b5\u03b9 \u03be\u03b5\u03ba\u03b9\u03bd\u03ae\u03c3\u03b5\u03b9" + }, + "show_revert_guide": { + "description": "\u0395\u03ac\u03bd \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03b1\u03bb\u03bb\u03ac\u03be\u03b5\u03c4\u03b5 \u03c3\u03b5 \u03c5\u03bb\u03b9\u03ba\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03bc\u03b9\u03ba\u03cc \u03bc\u03cc\u03bd\u03bf Zigbee, \u03bf\u03bb\u03bf\u03ba\u03bb\u03b7\u03c1\u03ce\u03c3\u03c4\u03b5 \u03c4\u03b1 \u03c0\u03b1\u03c1\u03b1\u03ba\u03ac\u03c4\u03c9 \u03bc\u03b7 \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b1 \u03b2\u03ae\u03bc\u03b1\u03c4\u03b1: \n\n * \u039a\u03b1\u03c4\u03b1\u03c1\u03b3\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf Silicon Labs Multiprotocol \n\n * \u0391\u03bd\u03b1\u03b2\u03bf\u03c3\u03b2\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf \u03c5\u03bb\u03b9\u03ba\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03bc\u03b9\u03ba\u03cc \u03bc\u03cc\u03bd\u03bf Zigbee, \u03b1\u03ba\u03bf\u03bb\u03bf\u03c5\u03b8\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd \u03bf\u03b4\u03b7\u03b3\u03cc \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manual. \n\n * \u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac \u03c4\u03bf ZHA \u03b3\u03b9\u03b1 \u03bc\u03b5\u03c4\u03b5\u03b3\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7 \u03c4\u03c9\u03bd \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c9\u03bd \u03c3\u03c4\u03bf\u03bd \u03b1\u03bd\u03b1\u03bd\u03b5\u03c9\u03bc\u03ad\u03bd\u03bf \u03c0\u03bf\u03bc\u03c0\u03bf\u03b4\u03ad\u03ba\u03c4\u03b7", + "title": "\u0397 \u03c5\u03c0\u03bf\u03c3\u03c4\u03ae\u03c1\u03b9\u03be\u03b7 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ce\u03bd \u03c0\u03c1\u03c9\u03c4\u03bf\u03ba\u03cc\u03bb\u03bb\u03c9\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7 \u03b3\u03b9\u03b1 \u03b1\u03c5\u03c4\u03ae\u03bd \u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae" + }, + "start_addon": { + "title": "\u039e\u03b5\u03ba\u03b9\u03bd\u03ac \u03c4\u03bf \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf Silicon Labs Multiprotocol." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_sky_connect/translations/es.json b/homeassistant/components/homeassistant_sky_connect/translations/es.json new file mode 100644 index 00000000000..9f53790f770 --- /dev/null +++ b/homeassistant/components/homeassistant_sky_connect/translations/es.json @@ -0,0 +1,42 @@ +{ + "options": { + "abort": { + "addon_info_failed": "No se pudo obtener la informaci\u00f3n del complemento Silicon Labs Multiprotocol.", + "addon_install_failed": "No se pudo instalar el complemento Silicon Labs Multiprotocol.", + "addon_set_config_failed": "No se pudo establecer la configuraci\u00f3n de Silicon Labs Multiprotocol.", + "addon_start_failed": "No se pudo iniciar el complemento Silicon Labs Multiprotocol.", + "disabled_due_to_bug": "Las opciones de hardware est\u00e1n deshabilitadas temporalmente mientras solucionamos un error. [M\u00e1s informaci\u00f3n]({url})", + "not_hassio": "Las opciones de hardware solo se pueden configurar en instalaciones de HassOS.", + "zha_migration_failed": "La migraci\u00f3n de ZHA no tuvo \u00e9xito." + }, + "error": { + "unknown": "Error inesperado" + }, + "progress": { + "install_addon": "Por favor, espera mientras finaliza la instalaci\u00f3n del complemento Silicon Labs Multiprotocol. Esto puede tardar varios minutos.", + "start_addon": "Por favor, espera mientras se completa el inicio del complemento Silicon Labs Multiprotocol. Esto puede tardar unos segundos." + }, + "step": { + "addon_installed_other_device": { + "title": "El soporte multiprotocolo ya est\u00e1 habilitado para otro dispositivo" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "Habilitar soporte multiprotocolo" + }, + "description": "Cuando el soporte multiprotocolo est\u00e1 habilitado, la radio IEEE 802.15.4 de {hardware_name} se puede usar tanto para Zigbee como para Thread (usado por Matter) al mismo tiempo. Si la integraci\u00f3n ZHA Zigbee ya usa la radio, ZHA se volver\u00e1 a configurar para usar el firmware multiprotocolo. \n\nNota: Esta es una caracter\u00edstica experimental.", + "title": "Habilitar el soporte multiprotocolo en la radio IEEE 802.15.4" + }, + "install_addon": { + "title": "La instalaci\u00f3n del complemento Silicon Labs Multiprotocol ha comenzado" + }, + "show_revert_guide": { + "description": "Si quieres cambiar el firmware a solo Zigbee, completa los siguientes pasos manuales: \n\n * Elimina el complemento Silicon Labs Multiprotocol \n\n * Actualiza el firmware solo de Zigbee, sigue la gu\u00eda en https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually. \n\n * Vuelve a configurar ZHA para migrar la configuraci\u00f3n a la radio actualizada", + "title": "El soporte multiprotocolo est\u00e1 habilitado para este dispositivo" + }, + "start_addon": { + "title": "El complemento Silicon Labs Multiprotocol se est\u00e1 iniciando." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_sky_connect/translations/et.json b/homeassistant/components/homeassistant_sky_connect/translations/et.json new file mode 100644 index 00000000000..9c48660ce65 --- /dev/null +++ b/homeassistant/components/homeassistant_sky_connect/translations/et.json @@ -0,0 +1,42 @@ +{ + "options": { + "abort": { + "addon_info_failed": "Silicon Labs Multiprotocol add-on info saamine eba\u00f5nnestus.", + "addon_install_failed": "Silicon Labs Multiprotocol add-on'i paigaldamine nurjus.", + "addon_set_config_failed": "Silicon Labs Multiprotocol konfiguratsiooni seadistamine eba\u00f5nnestus.", + "addon_start_failed": "Silicon Labsi mitmeprotokolli lisandmooduli k\u00e4ivitamine nurjus.", + "disabled_due_to_bug": "Riistvaravalikud on vea parandamise ajal ajutiselt keelatud. [Lisateave] ({url})", + "not_hassio": "Riistvaravalikuid saab konfigureerida ainult HassOS-i paigaldustes.", + "zha_migration_failed": "ZHA sidumise siirdamine nurjus." + }, + "error": { + "unknown": "Ootamatu t\u00f5rge" + }, + "progress": { + "install_addon": "Oota kuni Silicon Labsi mitmeprotokolli lisandmooduli installimine l\u00f5peb. Selleks v\u00f5ib kuluda mitu minutit.", + "start_addon": "Oota kuni Silicon Labsi mitmeprotokolli lisandmooduli k\u00e4ivitamine on l\u00f5pule viidud. See v\u00f5ib v\u00f5tta m\u00f5ne sekundi." + }, + "step": { + "addon_installed_other_device": { + "title": "Mitmeprotokolli tugi on m\u00f5ne teise seadme jaoks juba lubatud" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "Luba multiprotokolli tugi" + }, + "description": "Kui mitme protokolli tugi on lubatud, saab seadme {hardware_name} IEEE 802.15.4 raadiot kasutada samaaegselt nii Zigbee kui ka Threadi jaoks (kasutab Matter). Kui ZHA Zigbee kasutab juba kasutab raadiot, konfigureeritakse ZHA uuesti kasutama mitmeprotokollilist p\u00fcsivara. \n\n M\u00e4rkus. See on eksperimentaalne funktsioon.", + "title": "Multiprotokollide toe lubamine IEEE 802.15.4 raadiosides" + }, + "install_addon": { + "title": "Silicon Labs Multiprotocol add-on paigaldus on alanud" + }, + "show_revert_guide": { + "description": "Kui soovid muuta ainult Zigbee p\u00fcsivara, tee j\u00e4rgmised sammud: \n\n * Eemalda Silicon Labsi mitmeprotokolli lisand \n\n * V\u00e4rskenda ainult Zigbee p\u00fcsivara, j\u00e4rgi juhendit aadressil https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually. \n\n * Seadistuste \u00fcleviimiseks v\u00e4rskendatud raadiosse seadista ZHA uuesti", + "title": "Selle seadmel on multiprotokolli tugi lubatud" + }, + "start_addon": { + "title": "Silicon Labs Multiprotocol lisandmoodul k\u00e4ivitub" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_sky_connect/translations/he.json b/homeassistant/components/homeassistant_sky_connect/translations/he.json new file mode 100644 index 00000000000..7c83080d4aa --- /dev/null +++ b/homeassistant/components/homeassistant_sky_connect/translations/he.json @@ -0,0 +1,42 @@ +{ + "options": { + "abort": { + "addon_info_failed": "\u05e0\u05db\u05e9\u05dc\u05d4 \u05e7\u05d1\u05dc\u05ea \u05de\u05d9\u05d3\u05e2 \u05e2\u05dc \u05d4\u05d4\u05e8\u05d7\u05d1\u05d4 \u05e8\u05d9\u05d1\u05d5\u05d9 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc\u05d9\u05dd \u05e9\u05dc \u05de\u05e2\u05d1\u05d3\u05d5\u05ea \u05d4\u05e1\u05d9\u05dc\u05d9\u05e7\u05d5\u05df.", + "addon_install_failed": "\u05d4\u05ea\u05e7\u05e0\u05ea \u05d4\u05d4\u05e8\u05d7\u05d1\u05d4 \u05e9\u05dc \u05e8\u05d9\u05d1\u05d5\u05d9 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05dc\u05d9\u05dd \u05e9\u05dc \u05de\u05e2\u05d1\u05d3\u05d5\u05ea \u05d4\u05e1\u05d9\u05dc\u05d9\u05e7\u05d5\u05df \u05e0\u05db\u05e9\u05dc\u05d4.", + "addon_set_config_failed": "\u05d4\u05d2\u05d3\u05e8\u05ea \u05ea\u05e6\u05d5\u05e8\u05ea \u05e8\u05d9\u05d1\u05d5\u05d9 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05dc\u05d9\u05dd \u05e9\u05dc \u05de\u05e2\u05d1\u05d3\u05d5\u05ea \u05d4\u05e1\u05d9\u05dc\u05d9\u05e7\u05d5\u05df \u05e0\u05db\u05e9\u05dc\u05d4.", + "addon_start_failed": "\u05d4\u05e4\u05e2\u05dc\u05ea \u05d4\u05d4\u05e8\u05d7\u05d1\u05d4 \u05e8\u05d9\u05d1\u05d5\u05d9 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05dc\u05d9\u05dd \u05e9\u05dc \u05de\u05e2\u05d1\u05d3\u05d5\u05ea \u05d4\u05e1\u05d9\u05dc\u05d9\u05e7\u05d5\u05df \u05e0\u05db\u05e9\u05dc\u05d4.", + "disabled_due_to_bug": "\u05d0\u05e4\u05e9\u05e8\u05d5\u05d9\u05d5\u05ea \u05d4\u05d7\u05d5\u05de\u05e8\u05d4 \u05de\u05d5\u05e9\u05d1\u05ea\u05d5\u05ea \u05d1\u05d0\u05d5\u05e4\u05df \u05d6\u05de\u05e0\u05d9 \u05d1\u05d6\u05de\u05df \u05e9\u05d0\u05e0\u05d5 \u05de\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05d0\u05d2. [\u05dc\u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3] ({url})", + "not_hassio": "\u05e0\u05d9\u05ea\u05df \u05dc\u05d4\u05d2\u05d3\u05d9\u05e8 \u05d0\u05ea \u05d0\u05e4\u05e9\u05e8\u05d5\u05d9\u05d5\u05ea \u05d4\u05d7\u05d5\u05de\u05e8\u05d4 \u05e8\u05e7 \u05d1\u05d4\u05ea\u05e7\u05e0\u05d5\u05ea HassOS.", + "zha_migration_failed": "\u05e0\u05d3\u05d9\u05d3\u05ea \u05d4-ZHA \u05dc\u05d0 \u05d4\u05e6\u05dc\u05d9\u05d7\u05d4." + }, + "error": { + "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" + }, + "progress": { + "install_addon": "\u05e0\u05d0 \u05dc\u05d4\u05de\u05ea\u05d9\u05df \u05e2\u05d3 \u05dc\u05e1\u05d9\u05d5\u05dd \u05d4\u05d4\u05ea\u05e7\u05e0\u05d4 \u05e9\u05dc \u05ea\u05d5\u05e1\u05e3 \u05d4-Silicon Labs Multiprotocol. \u05e4\u05e2\u05d5\u05dc\u05d4 \u05d6\u05d5 \u05e2\u05e9\u05d5\u05d9\u05d4 \u05dc\u05d4\u05d9\u05de\u05e9\u05da \u05de\u05e1\u05e4\u05e8 \u05d3\u05e7\u05d5\u05ea.", + "start_addon": "\u05e0\u05d0 \u05dc\u05d4\u05de\u05ea\u05d9\u05df \u05e2\u05d3 \u05dc\u05d4\u05e9\u05dc\u05de\u05ea \u05d4\u05ea\u05d5\u05e1\u05e3 \u05e8\u05d9\u05d1\u05d5\u05d9 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc\u05d9\u05dd \u05e9\u05dc \u05de\u05e2\u05d1\u05d3\u05d5\u05ea \u05d4\u05e1\u05d9\u05dc\u05d9\u05e7\u05d5\u05df. \u05e4\u05e2\u05d5\u05dc\u05d4 \u05d6\u05d5 \u05e2\u05e9\u05d5\u05d9\u05d4 \u05dc\u05d4\u05d9\u05de\u05e9\u05da \u05de\u05e1\u05e4\u05e8 \u05e9\u05e0\u05d9\u05d5\u05ea." + }, + "step": { + "addon_installed_other_device": { + "title": "\u05ea\u05de\u05d9\u05db\u05d4 \u05d1\u05e8\u05d9\u05d1\u05d5\u05d9 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc\u05d9\u05dd \u05db\u05d1\u05e8 \u05d6\u05de\u05d9\u05e0\u05d4 \u05e2\u05d1\u05d5\u05e8 \u05d4\u05ea\u05e7\u05df \u05d0\u05d7\u05e8" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "\u05d0\u05e4\u05e9\u05e8 \u05ea\u05de\u05d9\u05db\u05d4 \u05d1\u05e8\u05d9\u05d1\u05d5\u05d9 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc\u05d9\u05dd" + }, + "description": "\u05db\u05d0\u05e9\u05e8 \u05ea\u05de\u05d9\u05db\u05d4 \u05d1\u05e8\u05d9\u05d1\u05d5\u05d9 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc\u05d9\u05dd \u05de\u05d5\u05e4\u05e2\u05dc\u05ea, \u05e0\u05d9\u05ea\u05df \u05dc\u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05e8\u05d3\u05d9\u05d5 IEEE 802.15.4 \u05e9\u05dc {hardware_name} \u05d4\u05df \u05e2\u05d1\u05d5\u05e8 Zigbee \u05d5\u05d4\u05df \u05e2\u05d1\u05d5\u05e8 Thread (\u05d1\u05e9\u05d9\u05de\u05d5\u05e9 \u05e2\u05dc-\u05d9\u05d3\u05d9 Matter) \u05d1\u05d5-\u05d6\u05de\u05e0\u05d9\u05ea. \u05d0\u05dd \u05d4\u05e8\u05d3\u05d9\u05d5 \u05db\u05d1\u05e8 \u05e0\u05de\u05e6\u05d0 \u05d1\u05e9\u05d9\u05de\u05d5\u05e9 \u05e2\u05dc-\u05d9\u05d3\u05d9 \u05e9\u05d9\u05dc\u05d5\u05d1 ZHA Zigbee, ZHA \u05d9\u05d5\u05d2\u05d3\u05e8 \u05de\u05d7\u05d3\u05e9 \u05dc\u05e9\u05d9\u05de\u05d5\u05e9 \u05d1\u05e7\u05d5\u05e9\u05d7\u05d4 \u05de\u05e8\u05d5\u05d1\u05ea \u05d4\u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc\u05d9\u05dd.\n\n\u05d4\u05e2\u05e8\u05d4: \u05d6\u05d5\u05d4\u05d9 \u05ea\u05db\u05d5\u05e0\u05d4 \u05e0\u05d9\u05e1\u05d9\u05d5\u05e0\u05d9\u05ea.", + "title": "\u05d0\u05e4\u05e9\u05e8 \u05ea\u05de\u05d9\u05db\u05d4 \u05d1\u05e8\u05d9\u05d1\u05d5\u05d9 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc\u05d9\u05dd \u05d1\u05e8\u05d3\u05d9\u05d5 IEEE 802.15.4" + }, + "install_addon": { + "title": "\u05d4\u05ea\u05e7\u05e0\u05ea \u05d4\u05d4\u05e8\u05d7\u05d1\u05d4 \u05e8\u05d9\u05d1\u05d5\u05d9 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc\u05d9\u05dd \u05e9\u05dc \u05de\u05e2\u05d1\u05d3\u05d5\u05ea \u05d4\u05e1\u05d9\u05dc\u05d9\u05e7\u05d5\u05df \u05d4\u05d7\u05dc\u05d4" + }, + "show_revert_guide": { + "description": "\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05e9\u05e0\u05d5\u05ea \u05dc\u05e7\u05d5\u05e9\u05d7\u05d4 \u05e9\u05dc Zigbee \u05d1\u05dc\u05d1\u05d3, \u05d9\u05e9 \u05dc\u05d1\u05e6\u05e2 \u05d0\u05ea \u05d4\u05e9\u05dc\u05d1\u05d9\u05dd \u05d4\u05d9\u05d3\u05e0\u05d9\u05d9\u05dd \u05d4\u05d1\u05d0\u05d9\u05dd:\n\n* \u05d4\u05e1\u05e8\u05ea \u05d4\u05d4\u05e8\u05d7\u05d1\u05d4 \u05e8\u05d9\u05d1\u05d5\u05d9 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc\u05d9\u05dd \u05e9\u05dc \u05de\u05e2\u05d1\u05d3\u05d5\u05ea \u05d4\u05e1\u05d9\u05dc\u05d9\u05e7\u05d5\u05df\n\n* \u05e6\u05e8\u05d9\u05d1\u05ea \u05d4\u05e7\u05d5\u05e9\u05d7\u05d4 Zigbee \u05d1\u05dc\u05d1\u05d3, \u05dc\u05e2\u05e7\u05d5\u05d1 \u05d0\u05d7\u05e8 \u05d4\u05de\u05d3\u05e8\u05d9\u05da https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually.\n\n* \u05dc\u05d4\u05d2\u05d3\u05d9\u05e8 \u05de\u05d7\u05d3\u05e9 \u05d0\u05ea ZHA \u05db\u05d3\u05d9 \u05dc\u05d4\u05e2\u05d1\u05d9\u05e8 \u05d4\u05d2\u05d3\u05e8\u05d5\u05ea \u05dc\u05e8\u05d3\u05d9\u05d5 \u05d4\u05de\u05d7\u05d5\u05d3\u05e9", + "title": "\u05ea\u05de\u05d9\u05db\u05d4 \u05d1\u05e8\u05d9\u05d1\u05d5\u05d9 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc\u05d9\u05dd \u05d6\u05de\u05d9\u05e0\u05d4 \u05e2\u05d1\u05d5\u05e8 \u05d4\u05ea\u05e7\u05df \u05d6\u05d4" + }, + "start_addon": { + "title": "\u05d4\u05d4\u05e8\u05d7\u05d1\u05d4 \u05e8\u05d9\u05d1\u05d5\u05d9 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc\u05d9\u05dd \u05e9\u05dc \u05de\u05e2\u05d1\u05d3\u05d5\u05ea \u05d4\u05e1\u05d9\u05dc\u05d9\u05e7\u05d5\u05df \u05d4\u05d7\u05dc\u05d4." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_sky_connect/translations/hu.json b/homeassistant/components/homeassistant_sky_connect/translations/hu.json new file mode 100644 index 00000000000..f02ebb7b333 --- /dev/null +++ b/homeassistant/components/homeassistant_sky_connect/translations/hu.json @@ -0,0 +1,42 @@ +{ + "options": { + "abort": { + "addon_info_failed": "Nem siker\u00fclt lek\u00e9rni a Silicon Labs Multiprotocol kieg\u00e9sz\u00edt\u0151 adatait.", + "addon_install_failed": "Nem siker\u00fclt telep\u00edteni a Silicon Labs Multiprotocol b\u0151v\u00edtm\u00e9nyt.", + "addon_set_config_failed": "A Silicon Labs Multiprotokoll konfigur\u00e1ci\u00f3 be\u00e1ll\u00edt\u00e1sa sikertelen volt.", + "addon_start_failed": "Nem siker\u00fclt elind\u00edtani a Silicon Labs Multiprotocol b\u0151v\u00edtm\u00e9nyt.", + "disabled_due_to_bug": "A hardver opci\u00f3k \u00e1tmenetileg le vannak tiltva, am\u00edg kijav\u00edtunk egy hib\u00e1t. [Tov\u00e1bbi inform\u00e1ci\u00f3]({url})", + "not_hassio": "A hardverbe\u00e1ll\u00edt\u00e1sok csak HassOS telep\u00edt\u00e9sekn\u00e9l konfigur\u00e1lhat\u00f3k.", + "zha_migration_failed": "A ZHA migr\u00e1ci\u00f3 nem siker\u00fclt." + }, + "error": { + "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" + }, + "progress": { + "install_addon": "K\u00e9rj\u00fck, v\u00e1rjon, am\u00edg a Silicon Labs Multiprotocol b\u0151v\u00edtm\u00e9ny telep\u00edt\u00e9se befejez\u0151dik. Ez t\u00f6bb percig is eltarthat.", + "start_addon": "K\u00e9rj\u00fck, v\u00e1rjon, am\u00edg a Silicon Labs Multiprotocol b\u0151v\u00edtm\u00e9ny elindul. Ez eltarthat n\u00e9h\u00e1ny m\u00e1sodpercig." + }, + "step": { + "addon_installed_other_device": { + "title": "A multiprotokoll-t\u00e1mogat\u00e1s m\u00e1r enged\u00e9lyezve van egy m\u00e1sik eszk\u00f6z sz\u00e1m\u00e1ra" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "Multiprotokoll-t\u00e1mogat\u00e1s enged\u00e9lyez\u00e9se" + }, + "description": "Ha a multiprotokoll-t\u00e1mogat\u00e1s enged\u00e9lyezve van, a {hardware_name} IEEE 802.15.4 r\u00e1di\u00f3ja egyszerre haszn\u00e1lhat\u00f3 a Zigbee \u00e9s a Thread (a Matter \u00e1ltal haszn\u00e1lt) funkcionalit\u00e1shoz. Ha a r\u00e1di\u00f3t m\u00e1r haszn\u00e1lja a ZHA Zigbee integr\u00e1ci\u00f3, a ZHA-t \u00e1t kell konfigur\u00e1lni a multiprotokoll firmware haszn\u00e1lat\u00e1ra.\n\nMegjegyz\u00e9s: Ez egy k\u00eds\u00e9rleti funkci\u00f3.", + "title": "Multiprotokoll t\u00e1mogat\u00e1s\u00e1nak enged\u00e9lyez\u00e9se az IEEE 802.15.4 r\u00e1di\u00f3ban" + }, + "install_addon": { + "title": "A Silicon Labs Multiprotocol b\u0151v\u00edtm\u00e9ny telep\u00edt\u00e9se folyamatban van" + }, + "show_revert_guide": { + "description": "Ha csak Zigbee firmware-re szeretne v\u00e1ltani, k\u00e9rj\u00fck, hajtsa v\u00e9gre a k\u00f6vetkez\u0151 manu\u00e1lis l\u00e9p\u00e9seket:\n\n * T\u00e1vol\u00edtsa el a Silicon Labs Multiprotocol b\u0151v\u00edtm\u00e9nyt.\n\n * Flashelje a csak Zigbee firmware-t, k\u00f6vesse a https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually oldalon tal\u00e1lhat\u00f3 \u00fatmutat\u00f3t.\n\n * Konfigur\u00e1lja \u00fajra a ZHA-t, hogy a be\u00e1ll\u00edt\u00e1sokat \u00e1tvigye az \u00fajraflashelt r\u00e1di\u00f3ra.", + "title": "A multiprotokoll-t\u00e1mogat\u00e1s enged\u00e9lyezve van az eszk\u00f6z\u00f6n." + }, + "start_addon": { + "title": "A Silicon Labs Multiprotocol b\u0151v\u00edtm\u00e9ny elind\u00edt\u00e1sa folyamatban van" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_sky_connect/translations/id.json b/homeassistant/components/homeassistant_sky_connect/translations/id.json new file mode 100644 index 00000000000..5fdfd444af9 --- /dev/null +++ b/homeassistant/components/homeassistant_sky_connect/translations/id.json @@ -0,0 +1,42 @@ +{ + "options": { + "abort": { + "addon_info_failed": "Gagal mendapatkan info add-on Silicon Labs Multiprotocol.", + "addon_install_failed": "Gagal menginstal add-on Silicon Labs Multiprotocol.", + "addon_set_config_failed": "Gagal mengatur konfigurasi Silicon Labs Multiprotocol.", + "addon_start_failed": "Gagal memulai add-on Silicon Labs Multiprotocol.", + "disabled_due_to_bug": "Opsi perangkat keras untuk sementara ini dinonaktifkan karena kami sedang memperbaiki bugnya. [Pelajari lebih lanjut]({url})", + "not_hassio": "Opsi perangkat keras hanya bisa dikonfigurasi pada instalasi HassOS.", + "zha_migration_failed": "Migrasi ZHA tidak berhasil." + }, + "error": { + "unknown": "Kesalahan yang tidak diharapkan" + }, + "progress": { + "install_addon": "Harap tunggu hingga penginstalan add-on Silicon Labs Multiprotocol selesai. Ini bisa memakan waktu beberapa saat.", + "start_addon": "Harap tunggu hingga add-on Silicon Labs Multiprotocol selesai. Ini mungkin perlu waktu beberapa saat." + }, + "step": { + "addon_installed_other_device": { + "title": "Dukungan multiprotokol sudah diaktifkan untuk perangkat lain" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "Aktifkan dukungan multiprotokol" + }, + "description": "Jika dukungan multiprotocol diaktifkan, radio IEEE 802.15.4 {hardware_name} dapat digunakan untuk Zigbee dan Thread (digunakan oleh Matter) secara bersamaan. Catatan: Ini adalah fitur eksperimental. Jika komponen radio telah digunakan oleh integrasi ZHA Zigbee, ZHA akan dikonfigurasi ulang untuk menggunakan firmware multiprotokol.\n\nCatatan: Fitur ini bersifat eksperimental.", + "title": "Aktifkan dukungan multiprotokol pada radio IEEE 802.15.4" + }, + "install_addon": { + "title": "Penginstalan add-on Multiprotocol Silicon Labs telah dimulai" + }, + "show_revert_guide": { + "description": "Jika Anda ingin mengubah ke firmware Zigbee saja, selesaikan langkah-langkah manual berikut ini:\n\n * Hapus add-on Multiprotocol Silicon Labs\n\n * Flash firmware khusus Zigbee, ikuti panduan di https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually.\n\n * Konfigurasikan ulang ZHA untuk memigrasikan pengaturan ke radio yang diflash ulang", + "title": "Dukungan multiprotokol diaktifkan untuk perangkat ini" + }, + "start_addon": { + "title": "Add-on Multiprotokol Silicon Labs sedang dimulai." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_sky_connect/translations/it.json b/homeassistant/components/homeassistant_sky_connect/translations/it.json new file mode 100644 index 00000000000..2c95698344a --- /dev/null +++ b/homeassistant/components/homeassistant_sky_connect/translations/it.json @@ -0,0 +1,42 @@ +{ + "options": { + "abort": { + "addon_info_failed": "Impossibile ottenere informazioni sul componente aggiuntivo Silicon Labs Multiprotocol.", + "addon_install_failed": "Impossibile installare il componente aggiuntivo Silicon Labs Multiprotocol.", + "addon_set_config_failed": "Impossibile impostare la configurazione di Silicon Labs Multiprotocol.", + "addon_start_failed": "Impossibile avviare il componente aggiuntivo Silicon Labs Multiprotocol.", + "disabled_due_to_bug": "Le opzioni hardware sono temporaneamente disabilitate mentre correggiamo un bug. [Ulteriori informazioni]({url})", + "not_hassio": "Le opzioni hardware possono essere configurate solo su installazioni HassOS.", + "zha_migration_failed": "La migrazione ZHA non \u00e8 riuscita." + }, + "error": { + "unknown": "Errore imprevisto" + }, + "progress": { + "install_addon": "Attendi il completamento dell'installazione del componente aggiuntivo Silicon Labs Multiprotocol. Questo pu\u00f2 richiedere alcuni minuti.", + "start_addon": "Attendi il completamento dell'avvio del componente aggiuntivo Silicon Labs Multiprotocol. Questo potrebbe richiedere alcuni secondi." + }, + "step": { + "addon_installed_other_device": { + "title": "Il supporto multiprotocollo \u00e8 gi\u00e0 abilitato per un altro dispositivo" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "Abilita il supporto multiprotocollo" + }, + "description": "Quando il supporto multiprotocollo \u00e8 abilitato, la radio IEEE 802.15.4 di {hardware_name} pu\u00f2 essere utilizzata contemporaneamente sia per Zigbee che per Thread (utilizzato da Matter). Se la radio \u00e8 gi\u00e0 utilizzata dall'integrazione ZHA Zigbee, ZHA verr\u00e0 riconfigurata per utilizzare il firmware multiprotocollo. \n\nNota: questa \u00e8 una funzione sperimentale.", + "title": "Abilita il supporto multiprotocollo sulla radio IEEE 802.15.4" + }, + "install_addon": { + "title": "L'installazione del componente aggiuntivo Silicon Labs Multiprotocol \u00e8 iniziata" + }, + "show_revert_guide": { + "description": "Se desideri passare al solo firmware Zigbee, completa i seguenti passaggi manuali: \n\n * Rimuovi il componente aggiuntivo Silicon Labs Multiprotocol \n\n * Caricare solo il firmware Zigbee, segui la guida su https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually. \n\n * Riconfigurare ZHA per migrare le impostazioni alla radio con firmware ricaricato", + "title": "Il supporto multiprotocollo \u00e8 abilitato per questo dispositivo" + }, + "start_addon": { + "title": "Il componente aggiuntivo Silicon Labs Multiprotocol \u00e8 in fase di avvio." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_sky_connect/translations/ja.json b/homeassistant/components/homeassistant_sky_connect/translations/ja.json new file mode 100644 index 00000000000..65eea011fa9 --- /dev/null +++ b/homeassistant/components/homeassistant_sky_connect/translations/ja.json @@ -0,0 +1,8 @@ +{ + "options": { + "abort": { + "disabled_due_to_bug": "\u30d0\u30b0\u3092\u4fee\u6b63\u3059\u308b\u9593\u3001\u30cf\u30fc\u30c9\u30a6\u30a7\u30a2\u30aa\u30d7\u30b7\u30e7\u30f3\u306f\u4e00\u6642\u7684\u306b\u7121\u52b9\u306b\u306a\u308a\u307e\u3059\u3002 [\u8a73\u7d30]({url})", + "zha_migration_failed": "ZHA\u3078\u306e\u79fb\u884c\u306f\u6210\u529f\u3057\u307e\u305b\u3093\u3067\u3057\u305f\u3002" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_sky_connect/translations/ko.json b/homeassistant/components/homeassistant_sky_connect/translations/ko.json new file mode 100644 index 00000000000..76968abdc72 --- /dev/null +++ b/homeassistant/components/homeassistant_sky_connect/translations/ko.json @@ -0,0 +1,39 @@ +{ + "options": { + "abort": { + "addon_set_config_failed": "Silicon Labs \ub2e4\uc911\ud504\ub85c\ud1a0\ucf5c \uad6c\uc131\uc744 \uc124\uc815\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4.", + "addon_start_failed": "Silicon Labs \ub2e4\uc911\ud504\ub85c\ud1a0\ucf5c \uc560\ub4dc\uc628\uc744 \uc2dc\uc791\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4.", + "not_hassio": "\ud558\ub4dc\uc6e8\uc5b4 \uc635\uc158\uc740 HassOS \uc124\uce58\uc5d0\uc11c\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.", + "zha_migration_failed": "ZHA \ub9c8\uc774\uadf8\ub808\uc774\uc158\uc5d0 \uc2e4\ud328\ud588\uc2b5\ub2c8\ub2e4." + }, + "error": { + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "progress": { + "install_addon": "Silicon Labs \ub2e4\uc911\ud504\ub85c\ud1a0\ucf5c \uc560\ub4dc\uc628 \uc124\uce58\uac00 \uc644\ub8cc\ub418\ub294 \ub3d9\uc548 \uae30\ub2e4\ub824 \uc8fc\uc2ed\uc2dc\uc624. \uba87 \ubd84\uc774 \uac78\ub9b4 \uc218 \uc788\uc2b5\ub2c8\ub2e4.", + "start_addon": "Silicon Labs \ub2e4\uc911\ud504\ub85c\ud1a0\ucf5c \uc560\ub4dc\uc628 \uc2dc\uc791\uc774 \uc644\ub8cc\ub420 \ub54c\uae4c\uc9c0 \uae30\ub2e4\ub9ac\uc2ed\uc2dc\uc624. \uba87 \ucd08 \uc815\ub3c4 \uac78\ub9b4 \uc218 \uc788\uc2b5\ub2c8\ub2e4." + }, + "step": { + "addon_installed_other_device": { + "title": "\ub2e4\ub978 \uae30\uae30\uc5d0 \ub300\ud574 \ub2e4\uc911 \ud504\ub85c\ud1a0\ucf5c \uc9c0\uc6d0\uc774 \uc774\ubbf8 \ud65c\uc131\ud654\ub418\uc5b4 \uc788\uc2b5\ub2c8\ub2e4." + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "\ub2e4\uc911 \ud504\ub85c\ud1a0\ucf5c \uc9c0\uc6d0 \ud65c\uc131\ud654" + }, + "description": "\ub2e4\uc911 \ud504\ub85c\ud1a0\ucf5c \uc9c0\uc6d0\uc774 \ud65c\uc131\ud654\ub418\uba74 {hardware_name} \uc758 IEEE 802.15.4 \ud1b5\uc2e0\ubc29\ubc95\uc73c\ub85c Zigbee\uc640 Thread(Matter\uc5d0\uc11c \uc0ac\uc6a9)\ub97c \ub3d9\uc2dc\uc5d0 \uc0ac\uc6a9\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4. \ud504\ub85c\ud1a0\ucf5c\uc774 ZHA Zigbee \ud1b5\ud569\uad6c\uc131\uc694\uc18c\uc5d0\uc11c \uc774\ubbf8 \uc0ac\uc6a9 \uc911\uc778 \uacbd\uc6b0 ZHA\ub294 \ub2e4\uc911 \ud504\ub85c\ud1a0\ucf5c \ud38c\uc6e8\uc5b4\ub97c \uc0ac\uc6a9\ud558\ub3c4\ub85d \uc7ac\uad6c\uc131\ub429\ub2c8\ub2e4. \n\n \ucc38\uace0: \uc774\uac83\uc740 \uc2e4\ud5d8\uc801\uc778 \uae30\ub2a5\uc785\ub2c8\ub2e4.", + "title": "IEEE 802.15.4 \ud1b5\uc2e0\ubc29\ubc95\uc5d0\uc11c \ub2e4\uc911\ud504\ub85c\ud1a0\ucf5c \uc9c0\uc6d0 \ud65c\uc131\ud654" + }, + "install_addon": { + "title": "Silicon Labs \ub2e4\uc911\ud504\ub85c\ud1a0\ucf5c \uc560\ub4dc\uc628\uc744 \uc124\uce58 \uc911\uc785\ub2c8\ub2e4" + }, + "show_revert_guide": { + "description": "Zigbee \uc804\uc6a9 \ud38c\uc6e8\uc5b4\ub85c \ubcc0\uacbd\ud558\ub824\uba74 \ub2e4\uc74c \uc548\ub0b4\ub97c \ub530\ub974\uc138\uc694. \n\n * Silicon Labs \ub2e4\uc911\ud504\ub85c\ud1a0\ucf5c \uc560\ub4dc\uc628 \uc81c\uac70 \n\n * Zigbee \uc804\uc6a9 \ud38c\uc6e8\uc5b4\ub97c \ud50c\ub798\uc2dc. \ub2e4\uc74c \uc548\ub0b4\ub97c \ub530\ub974\uc138\uc694. (https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually)\n\n * ZHA\ub97c \uc7ac\uad6c\uc131\ud558\uc5ec \ubc14\ub010 \ud1b5\uc2e0\ubc29\ubc95\uc744 \uc801\uc6a9", + "title": "\uc774 \uae30\uae30\uc5d0 \ub2e4\uc911\ud504\ub85c\ud1a0\ucf5c \uc774 \uc0ac\uc6a9\ub418\ub3c4\ub85d \uc124\uc815\ub418\uc5c8\uc2b5\ub2c8\ub2e4." + }, + "start_addon": { + "title": "Silicon Labs \ub2e4\uc911\ud504\ub85c\ud1a0\ucf5c \uc560\ub4dc\uc628\uc774 \uc2dc\uc791\ub429\ub2c8\ub2e4." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_sky_connect/translations/nl.json b/homeassistant/components/homeassistant_sky_connect/translations/nl.json new file mode 100644 index 00000000000..0b1a0595844 --- /dev/null +++ b/homeassistant/components/homeassistant_sky_connect/translations/nl.json @@ -0,0 +1,42 @@ +{ + "options": { + "abort": { + "addon_info_failed": "Ophalen van de Silicon Labs multi-protocol add-on is mislukt", + "addon_install_failed": "Installeren van de Silicon Labs multi-protocol add-on is mislukt", + "addon_set_config_failed": "Het instellen van de configuratie van de Silicon Labs multi-protocol add-on is mislukt", + "addon_start_failed": "Het starten van de Silicon Labs multi-protocol add-on is mislukt", + "disabled_due_to_bug": "De hardware opties zijn tijdelijk uitgeschakeld terwijl we een fout herstellen. [Lees meer]({url})", + "not_hassio": "De hardwareopties kunnen alleen worden ingesteld voor HassOS installaties", + "zha_migration_failed": "De ZHA migratie is mislukt" + }, + "error": { + "unknown": "Onverwachte fout" + }, + "progress": { + "install_addon": "Een moment geduld. De installatie van de Sillicon Labs multi-protocol add-on wordt afgerond. Dit kan enkele minuten duren.", + "start_addon": "Een moment geduld. De Sillicon Labs multi-protocol add-on wordt gestart. Dit kan enkele seconden duren." + }, + "step": { + "addon_installed_other_device": { + "title": "Multi-protocol ondersteuning is al ingeschakeld voor een ander apparaat" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "Inschakelen multi-protocol ondersteuning" + }, + "description": "Wanneer multi-protocol ondersteuning is geactiveerd, kan het {hardware_name} IEEE 802.15.4 toegangspunt gelijktijdig voor zowel Zigbee als Thread (gebruikt door Matter) worden gebruikt. Als het toegangspunt al is gebruikt door de ZHA integratie, dan zal ZHA worden geconfigureerd om de multi-protocol firmware te gebruiken.\n\nLet op: Dit is een experimentele functionaliteit.", + "title": "Inschakelen multi-protocol ondersteuning op het IEEE 802.15.4 toegangspunt" + }, + "install_addon": { + "title": "De Silicon Labs multi-protocol add-on installatie is gestart" + }, + "show_revert_guide": { + "description": "Als je wilt overschakelen naar Zigbee-only firmware, volg dan de volgende stappen:\n\n* Verwijder de Silicon Laps multi-protocol add-on\n \n* Flash de Zigbee-only firmware, volg hiervoor de aanwijzingen op https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually\n\n* Herconfigureer ZHA om de instellingen te migreren naar het ge-flash-te apparaat", + "title": "Multi-protocol ondersteuning is ingeschakeld voor dit apparaat" + }, + "start_addon": { + "title": "De Silicon Labs multi-protocol add-on is aan het opstarten" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_sky_connect/translations/no.json b/homeassistant/components/homeassistant_sky_connect/translations/no.json new file mode 100644 index 00000000000..4349387fd53 --- /dev/null +++ b/homeassistant/components/homeassistant_sky_connect/translations/no.json @@ -0,0 +1,42 @@ +{ + "options": { + "abort": { + "addon_info_failed": "Kunne ikke hente informasjon om tilleggsprogrammet for Silicon Labs Multiprotocol.", + "addon_install_failed": "Kunne ikke installere Silicon Labs Multiprotocol-tillegget.", + "addon_set_config_failed": "Kunne ikke angi Silicon Labs Multiprotocol-konfigurasjon.", + "addon_start_failed": "Kunne ikke starte Silicon Labs Multiprotocol-tillegget.", + "disabled_due_to_bug": "Maskinvarealternativene er midlertidig deaktivert mens vi fikser en feil. [Finn ut mer]( {url} )", + "not_hassio": "Maskinvarealternativene kan bare konfigureres p\u00e5 HassOS-installasjoner.", + "zha_migration_failed": "ZHA-migreringen lyktes ikke." + }, + "error": { + "unknown": "Uventet feil" + }, + "progress": { + "install_addon": "Vennligst vent mens installasjonen av Silicon Labs Multiprotocol-tillegget fullf\u00f8res. Dette kan ta flere minutter.", + "start_addon": "Vennligst vent mens oppstarten av Silicon Labs Multiprotocol-tillegget fullf\u00f8res. Dette kan ta noen sekunder." + }, + "step": { + "addon_installed_other_device": { + "title": "Multiprotokollst\u00f8tte er allerede aktivert for en annen enhet" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "Aktiver st\u00f8tte for multiprotokoll" + }, + "description": "N\u00e5r multiprotokollst\u00f8tte er aktivert, kan {hardware_name} sin IEEE 802.15.4-radio brukes for b\u00e5de Zigbee og Thread (brukt av Matter) samtidig. Hvis radioen allerede brukes av ZHA Zigbee-integrasjonen, vil ZHA bli rekonfigurert til \u00e5 bruke multiprotokoll-fastvaren. \n\n Merk: Dette er en eksperimentell funksjon.", + "title": "Aktiver st\u00f8tte for multiprotokoll p\u00e5 IEEE 802.15.4-radioen" + }, + "install_addon": { + "title": "Silicon Labs Multiprotocol-tilleggsinstallasjonen har startet" + }, + "show_revert_guide": { + "description": "Hvis du vil bytte til kun Zigbee-firmware, m\u00e5 du fullf\u00f8re f\u00f8lgende manuelle trinn: \n\n * Fjern Silicon Labs Multiprotocol-tillegget \n\n * Flash den eneste Zigbee-fastvaren, f\u00f8lg veiledningen p\u00e5 https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually. \n\n * Konfigurer ZHA p\u00e5 nytt for \u00e5 migrere innstillinger til radioen med oppdatering", + "title": "Multiprotokollst\u00f8tte er aktivert for denne enheten" + }, + "start_addon": { + "title": "Silicon Labs Multiprotocol-tillegget starter." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_sky_connect/translations/pl.json b/homeassistant/components/homeassistant_sky_connect/translations/pl.json new file mode 100644 index 00000000000..b678d2a97a5 --- /dev/null +++ b/homeassistant/components/homeassistant_sky_connect/translations/pl.json @@ -0,0 +1,42 @@ +{ + "options": { + "abort": { + "addon_info_failed": "Nie uda\u0142o si\u0119 pobra\u0107 informacji o dodatku Silicon Labs Multiprotocol.", + "addon_install_failed": "Nie uda\u0142o si\u0119 zainstalowa\u0107 dodatku Silicon Labs Multiprotocol.", + "addon_set_config_failed": "Nie uda\u0142o si\u0119 ustawi\u0107 konfiguracji Silicon Labs Multiprotocol.", + "addon_start_failed": "Nie uda\u0142o si\u0119 uruchomi\u0107 dodatku Silicon Labs Multiprotocol.", + "disabled_due_to_bug": "Opcje sprz\u0119towe s\u0105 tymczasowo wy\u0142\u0105czone na czas naprawiania przez nas b\u0142\u0119du. [Dowiedz si\u0119 wi\u0119cej]({url})", + "not_hassio": "Opcje sprz\u0119towe mo\u017cna skonfigurowa\u0107 tylko w instalacjach HassOS.", + "zha_migration_failed": "Migracja ZHA nie powiod\u0142a si\u0119." + }, + "error": { + "unknown": "Nieoczekiwany b\u0142\u0105d" + }, + "progress": { + "install_addon": "Poczekaj, a\u017c zako\u0144czy si\u0119 instalacja dodatku Silicon Labs Multiprotocol. Mo\u017ce to potrwa\u0107 kilka minut.", + "start_addon": "Poczekaj, a\u017c zako\u0144czy si\u0119 uruchamianie dodatku Silicon Labs Multiprotocol. Mo\u017ce to potrwa\u0107 kilka sekund." + }, + "step": { + "addon_installed_other_device": { + "title": "Obs\u0142uga Multiprotocol jest ju\u017c w\u0142\u0105czona dla innego urz\u0105dzenia" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "W\u0142\u0105cz obs\u0142ug\u0119 Multiprotocol" + }, + "description": "Gdy w\u0142\u0105czona jest obs\u0142uga Multiprotocol, radio IEEE 802.15.4 dla {hardware_name} mo\u017ce by\u0107 u\u017cywane jednocze\u015bnie dla Zigbee i Thread (u\u017cywane przez Matter). Uwaga: jest to funkcja eksperymentalna. Je\u015bli radio jest ju\u017c u\u017cywane przez integracj\u0119 ZHA Zigbee, ZHA zostanie ponownie skonfigurowane, aby korzysta\u0107 z oprogramowania multiprotocol.\n\nUwaga: jest to funkcja eksperymentalna.", + "title": "W\u0142\u0105cz obs\u0142ug\u0119 multiprotocol w radiu IEEE 802.15.4" + }, + "install_addon": { + "title": "Rozpocz\u0119\u0142a si\u0119 instalacja dodatku Silicon Labs Multiprotocol" + }, + "show_revert_guide": { + "description": "Je\u015bli chcesz zmieni\u0107 oprogramowanie obs\u0142uguj\u0105ce tylko Zigbee, wykonaj nast\u0119puj\u0105ce czynno\u015bci r\u0119czne: \n\n* Usu\u0144 dodatek Silicon Labs Multiprotocol \n\n* Wgraj oprogramowanie tylko dla Zigbee, post\u0119puj zgodnie z instrukcjami na stronie https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually. \n\n* Ponownie skonfiguruj ZHA, aby przeprowadzi\u0107 migracj\u0119 ustawie\u0144 do przeprogramowanego radia", + "title": "Obs\u0142uga multiprotocol jest w\u0142\u0105czona dla tego urz\u0105dzenia" + }, + "start_addon": { + "title": "Uruchamianie dodatku Silicon Labs Multiprotocol." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_sky_connect/translations/pt-BR.json b/homeassistant/components/homeassistant_sky_connect/translations/pt-BR.json new file mode 100644 index 00000000000..d6d9a53a09d --- /dev/null +++ b/homeassistant/components/homeassistant_sky_connect/translations/pt-BR.json @@ -0,0 +1,42 @@ +{ + "options": { + "abort": { + "addon_info_failed": "Falha ao obter informa\u00e7\u00f5es do add-on Silicon Labs Multiprotocol.", + "addon_install_failed": "Falha ao instalar o add-on Silicon Labs Multiprotocol.", + "addon_set_config_failed": "Falha ao definir a configura\u00e7\u00e3o multiprotocolo da Silicon Labs.", + "addon_start_failed": "Falha ao iniciar o add-on Silicon Labs Multiprotocol.", + "disabled_due_to_bug": "As op\u00e7\u00f5es de hardware est\u00e3o temporariamente desativadas enquanto corrigimos um bug. [Saiba mais]({url})", + "not_hassio": "As op\u00e7\u00f5es de hardware s\u00f3 podem ser configuradas em instala\u00e7\u00f5es HassOS.", + "zha_migration_failed": "A migra\u00e7\u00e3o ZHA n\u00e3o foi bem-sucedida." + }, + "error": { + "unknown": "Erro inesperado" + }, + "progress": { + "install_addon": "Aguarde enquanto a instala\u00e7\u00e3o do add-on Silicon Labs Multiprotocol \u00e9 conclu\u00edda. Isso pode levar v\u00e1rios minutos.", + "start_addon": "Aguarde enquanto a inicializa\u00e7\u00e3o do add-on Silicon Labs Multiprotocol \u00e9 conclu\u00edda. Isso pode levar alguns segundos." + }, + "step": { + "addon_installed_other_device": { + "title": "O suporte multiprotocolo j\u00e1 est\u00e1 ativado para outro dispositivo" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "Ativar suporte multiprotocolo" + }, + "description": "Quando o suporte multiprotocolo est\u00e1 ativado, o r\u00e1dio IEEE 802.15.4 do {hardware_name} pode ser usado para Zigbee e Thread (usado por Matter) ao mesmo tempo. Se o r\u00e1dio j\u00e1 estiver sendo usado pela integra\u00e7\u00e3o ZHA Zigbee, o ZHA ser\u00e1 reconfigurado para usar o firmware multiprotocolo. \n\n Nota: Esse \u00e9 um recurso experimental.", + "title": "Habilite o suporte multiprotocolo no r\u00e1dio IEEE 802.15.4" + }, + "install_addon": { + "title": "A instala\u00e7\u00e3o do add-on Silicon Labs Multiprotocol foi iniciada" + }, + "show_revert_guide": { + "description": "Se voc\u00ea deseja alterar para o firmware somente Zigbee, conclua as seguintes etapas manuais: \n\n * Remova o add-on Silicon Labs Multiprotocol \n\n * Atualize apenas o firmware Zigbee, siga o guia em https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually. \n\n * Reconfigure o ZHA para migrar as configura\u00e7\u00f5es para o r\u00e1dio reflashed", + "title": "O suporte multiprotocolo est\u00e1 ativado para este dispositivo" + }, + "start_addon": { + "title": "O add-on Silicon Labs Multiprotocol est\u00e1 iniciando." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_sky_connect/translations/ru.json b/homeassistant/components/homeassistant_sky_connect/translations/ru.json new file mode 100644 index 00000000000..e9aead744c9 --- /dev/null +++ b/homeassistant/components/homeassistant_sky_connect/translations/ru.json @@ -0,0 +1,42 @@ +{ + "options": { + "abort": { + "addon_info_failed": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0438 \"Silicon Labs Multiprotocol\".", + "addon_install_failed": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \"Silicon Labs Multiprotocol\".", + "addon_set_config_failed": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \"Silicon Labs Multiprotocol\".", + "addon_start_failed": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \"Silicon Labs Multiprotocol\".", + "disabled_due_to_bug": "\u0410\u043f\u043f\u0430\u0440\u0430\u0442\u043d\u044b\u0435 \u043e\u043f\u0446\u0438\u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u044b, \u043f\u043e\u043a\u0430 \u043c\u044b \u0438\u0441\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u043e\u0448\u0438\u0431\u043a\u0443. [\u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435]({url})", + "not_hassio": "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u043e\u0431\u043e\u0440\u0443\u0434\u043e\u0432\u0430\u043d\u0438\u044f \u043c\u043e\u0436\u043d\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0445 \u0441\u0438\u0441\u0442\u0435\u043c\u0430\u0445 HassOS.", + "zha_migration_failed": "\u041c\u0438\u0433\u0440\u0430\u0446\u0438\u044f ZHA \u043d\u0435 \u0443\u0434\u0430\u043b\u0430\u0441\u044c." + }, + "error": { + "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." + }, + "progress": { + "install_addon": "\u041f\u043e\u0434\u043e\u0436\u0434\u0438\u0442\u0435, \u043f\u043e\u043a\u0430 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u0441\u044f \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \"Silicon Labs Multiprotocol\". \u042d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u043d\u044f\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043c\u0438\u043d\u0443\u0442.", + "start_addon": "\u041f\u043e\u0434\u043e\u0436\u0434\u0438\u0442\u0435, \u043f\u043e\u043a\u0430 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u0441\u044f \u0437\u0430\u043f\u0443\u0441\u043a \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \"Silicon Labs Multiprotocol\". \u042d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u043d\u044f\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u0435\u043a\u0443\u043d\u0434." + }, + "step": { + "addon_installed_other_device": { + "title": "\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u0432 \u0443\u0436\u0435 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0430 \u0434\u043b\u044f \u0434\u0440\u0443\u0433\u043e\u0433\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u0432" + }, + "description": "\u041a\u043e\u0433\u0434\u0430 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0430 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u0432, \u0440\u0430\u0434\u0438\u043e\u043c\u043e\u0434\u0443\u043b\u044c {hardware_name} \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u0430 IEEE 802.15.4 \u043c\u043e\u0436\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u043a\u0430\u043a \u0434\u043b\u044f Zigbee, \u0442\u0430\u043a \u0438 \u0434\u043b\u044f Thread (\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0432 Matter). \u0415\u0441\u043b\u0438 \u0440\u0430\u0434\u0438\u043e\u043c\u043e\u0434\u0443\u043b\u044c \u0443\u0436\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d \u0432 ZHA \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u0430 Zigbee, ZHA \u0431\u0443\u0434\u0435\u0442 \u043f\u0435\u0440\u0435\u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u0430 \u0434\u043b\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u0432.\n\n\u041f\u0440\u0438\u043c\u0435\u0447\u0430\u043d\u0438\u0435: \u044d\u0442\u043e \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u0430\u043b\u044c\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f.", + "title": "\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u0432 \u043d\u0430 \u0440\u0430\u0434\u0438\u043e\u043c\u043e\u0434\u0443\u043b\u0435 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u0430 IEEE 802.15.4." + }, + "install_addon": { + "title": "\u041d\u0430\u0447\u0430\u043b\u0430\u0441\u044c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \"Silicon Labs Multiprotocol\"" + }, + "show_revert_guide": { + "description": "\u0415\u0441\u043b\u0438 \u0412\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u043f\u0435\u0440\u0435\u0439\u0442\u0438 \u043d\u0430 \u043f\u0440\u043e\u0448\u0438\u0432\u043a\u0443, \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044e\u0449\u0443\u044e \u0442\u043e\u043b\u044c\u043a\u043e Zigbee, \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f:\n\n* \u0423\u0434\u0430\u043b\u0438\u0442\u0435 \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \"Silicon Labs Multiprotocol\".\n\n* \u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u0435 \u043f\u0440\u043e\u0448\u0438\u0432\u043a\u0443 Zigbee, \u0441\u043b\u0435\u0434\u0443\u044f \u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432\u0443 \u043d\u0430 \u0441\u0430\u0439\u0442\u0435 https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually.\n\n* \u041f\u0435\u0440\u0435\u043d\u0430\u0441\u0442\u0440\u043e\u0439\u0442\u0435 ZHA \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u043d\u043e\u0441\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u043d\u0430 \u0437\u0430\u043d\u043e\u0432\u043e \u043f\u0440\u043e\u0448\u0438\u0442\u044b\u0439 \u0440\u0430\u0434\u0438\u043e\u043c\u043e\u0434\u0443\u043b\u044c.", + "title": "\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0430 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u0432" + }, + "start_addon": { + "title": "\u0414\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 Silicon Labs Multiprotocol \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_sky_connect/translations/sk.json b/homeassistant/components/homeassistant_sky_connect/translations/sk.json new file mode 100644 index 00000000000..73c3b3e551a --- /dev/null +++ b/homeassistant/components/homeassistant_sky_connect/translations/sk.json @@ -0,0 +1,42 @@ +{ + "options": { + "abort": { + "addon_info_failed": "Nepodarilo sa z\u00edska\u0165 inform\u00e1cie o doplnku Silicon Labs Multiprotocol.", + "addon_install_failed": "Nepodarilo sa nain\u0161talova\u0165 doplnok Silicon Labs Multiprotocol.", + "addon_set_config_failed": "Nepodarilo sa nastavi\u0165 konfigur\u00e1ciu Silicon Labs Multiprotocol.", + "addon_start_failed": "Nepodarilo sa spusti\u0165 doplnok Silicon Labs Multiprotocol.", + "disabled_due_to_bug": "Hardv\u00e9rov\u00e9 mo\u017enosti s\u00fa do\u010dasne deaktivovan\u00e9, k\u00fdm oprav\u00edme chybu. [Viac inform\u00e1ci\u00ed]({url})", + "not_hassio": "Hardv\u00e9rov\u00e9 mo\u017enosti je mo\u017en\u00e9 nakonfigurova\u0165 iba v in\u0161tal\u00e1ci\u00e1ch HassOS.", + "zha_migration_failed": "Migr\u00e1cia ZHA nebola \u00faspe\u0161n\u00e1." + }, + "error": { + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "progress": { + "install_addon": "Po\u010dkajte, k\u00fdm sa dokon\u010d\u00ed in\u0161tal\u00e1cia doplnku Silicon Labs Multiprotocol. M\u00f4\u017ee to trva\u0165 nieko\u013eko min\u00fat.", + "start_addon": "Po\u010dkajte, k\u00fdm sa dokon\u010d\u00ed spustenie doplnku Silicon Labs Multiprotocol. M\u00f4\u017ee to trva\u0165 nieko\u013eko sek\u00fand." + }, + "step": { + "addon_installed_other_device": { + "title": "Podpora multiprotocol je u\u017e povolen\u00e1 pre in\u00e9 zariadenie" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "Povoli\u0165 podporu multiprotocol" + }, + "description": "Ke\u010f je povolen\u00e1 podpora viacer\u00fdch protokolov, r\u00e1dio {hardware_name} IEEE 802.15.4 mo\u017eno pou\u017ei\u0165 s\u00fa\u010dasne pre Zigbee aj vl\u00e1kno (pou\u017e\u00edvan\u00e9 spolo\u010dnos\u0165ou Matter). Ak u\u017e r\u00e1dio pou\u017e\u00edva integr\u00e1cia ZHA Zigbee, ZHA sa prekonfiguruje na pou\u017e\u00edvanie multiprotokolov\u00e9ho firmv\u00e9ru. \n\n Pozn\u00e1mka: Toto je experiment\u00e1lna funkcia.", + "title": "Povo\u013ete podporu viacer\u00fdch protokolov na r\u00e1diu IEEE 802.15.4" + }, + "install_addon": { + "title": "In\u0161tal\u00e1cia doplnku Silicon Labs Multiprotocol sa za\u010dala" + }, + "show_revert_guide": { + "description": "Ak chcete zmeni\u0165 firmv\u00e9r iba na Zigbee, vykonajte nasleduj\u00face manu\u00e1lne kroky: \n\n * Odstr\u00e1\u0148te doplnok Silicon Labs Multiprotocol \n\n * Flashujte iba firmv\u00e9r Zigbee, postupujte pod\u013ea n\u00e1vodu na https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually. \n\n * Prekonfigurujte ZHA na migr\u00e1ciu nastaven\u00ed do preflashovan\u00e9ho r\u00e1dia", + "title": "Pre toto zariadenie je povolen\u00e1 podpora multiprotocol" + }, + "start_addon": { + "title": "Sp\u00fa\u0161\u0165a sa doplnok Silicon Labs Multiprotocol." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_sky_connect/translations/zh-Hant.json b/homeassistant/components/homeassistant_sky_connect/translations/zh-Hant.json new file mode 100644 index 00000000000..ac239820820 --- /dev/null +++ b/homeassistant/components/homeassistant_sky_connect/translations/zh-Hant.json @@ -0,0 +1,42 @@ +{ + "options": { + "abort": { + "addon_info_failed": "\u53d6\u5f97 Silicon Labs Multiprotocol \u9644\u52a0\u5143\u4ef6\u8cc7\u8a0a\u5931\u6557\u3002", + "addon_install_failed": "\u5b89\u88dd Silicon Labs Multiprotocol \u9644\u52a0\u5143\u4ef6\u5931\u6557\u3002", + "addon_set_config_failed": "\u8a2d\u5b9a Silicon Labs Multiprotocol \u9644\u52a0\u5143\u4ef6\u5931\u6557\u3002", + "addon_start_failed": "\u555f\u52d5 Silicon Labs Multiprotocol \u9644\u52a0\u5143\u4ef6\u5931\u6557\u3002", + "disabled_due_to_bug": "\u7531\u65bc\u6b63\u5728\u4fee\u6b63\u932f\u8aa4\u3001\u786c\u9ad4\u9078\u9805\u66ab\u6642\u95dc\u9589\u3002 [\u4e86\u89e3\u66f4\u591a]({url})", + "not_hassio": "\u786c\u9ad4\u9078\u9805\u50c5\u80fd\u65bc HassOS \u5b89\u88dd\u6a21\u5f0f\u9032\u884c\u8a2d\u5b9a\u3002", + "zha_migration_failed": "ZHA \u9077\u79fb\u672a\u6210\u529f\u3002" + }, + "error": { + "unknown": "\u672a\u9810\u671f\u932f\u8aa4" + }, + "progress": { + "install_addon": "\u8acb\u7a0d\u7b49 Silicon Labs Multiprotocol \u9644\u52a0\u5143\u4ef6\u5b89\u88dd\u5b8c\u6210\u3002\u53ef\u80fd\u9700\u8981\u5e7e\u5206\u9418\u3002", + "start_addon": "\u8acb\u7a0d\u7b49 Silicon Labs Multiprotocol \u9644\u52a0\u5143\u4ef6\u555f\u59cb\u5b8c\u6210\uff0c\u53ef\u80fd\u6703\u9700\u8981\u5e7e\u5206\u9418\u3002" + }, + "step": { + "addon_installed_other_device": { + "title": "Multiprotocol \u652f\u63f4\u5df2\u7d93\u65bc\u5176\u4ed6\u88dd\u7f6e\u958b\u555f" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "\u555f\u7528 Multiprotocol \u652f\u63f4" + }, + "description": "\u7576\u555f\u7528 Multiprotocol \u652f\u63f4\u6642\u3001{hardware_name} \u7684 IEEE 802.15.4 radio \u53ef\u540c\u6642\u4f5c\u70ba Zigbee \u8207 Thread \uff08\u7528\u65bc Matter\uff09\u4f7f\u7528\u3002\u5047\u5982 Radio \u5df2\u7d93\u88ab ZHA Zigbee \u6574\u5408\u6240\u4f7f\u7528\u3001ZHA \u5c07\u6703\u9032\u884c\u91cd\u65b0\u8a2d\u5b9a\u4ee5\u4f7f\u7528 Multiprotocol \u97cc\u9ad4\u3002\n\n\u6ce8\u610f\uff1a\u76ee\u524d\u70ba\u5be6\u9a57\u6027\u529f\u80fd\u3002", + "title": "\u65bc IEEE 802.15.4 radio \u4e0a\u555f\u7528 Multiprotocol \u652f\u63f4" + }, + "install_addon": { + "title": "Silicon Labs Multiprotocol \u9644\u52a0\u5143\u4ef6\u5df2\u555f\u7528\u3002" + }, + "show_revert_guide": { + "description": "\u5047\u5982\u60f3\u8b8a\u66f4\u70ba\u50c5 Zigbee \u97cc\u9ad4\u3001\u8acb\u5b8c\u6210\u4ee5\u4e0b\u624b\u52d5\u6b65\u9a5f\uff1a\n\n * \u79fb\u9664 Silicon Labs Multiprotocol \u9644\u52a0\u5143\u4ef6\n\n * \u5237\u5165\u50c5 Zigbee \u97cc\u9ad4\uff0c\u8acb\u53c3\u95b1\u64cd\u4f5c\u6307\u5f15 https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually\u3002\n\n * \u91cd\u65b0\u8a2d\u5b9a ZHA \u4ee5\u9077\u79fb\u8a2d\u5b9a\u81f3\u91cd\u65b0\u5237\u5165\u7684 Radio", + "title": "\u88dd\u7f6e\u4e4b Multiprotocol \u652f\u63f4\u5df2\u958b\u555f" + }, + "start_addon": { + "title": "Silicon Labs Multiprotocol \u9644\u52a0\u5143\u4ef6\u555f\u7528\u4e2d\u3002" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_yellow/__init__.py b/homeassistant/components/homeassistant_yellow/__init__.py index 6099dc014df..48c2e74bd38 100644 --- a/homeassistant/components/homeassistant_yellow/__init__.py +++ b/homeassistant/components/homeassistant_yellow/__init__.py @@ -23,7 +23,9 @@ from .const import RADIO_DEVICE, ZHA_HW_DISCOVERY_DATA _LOGGER = logging.getLogger(__name__) -async def _multi_pan_addon_info(hass, entry: ConfigEntry) -> AddonInfo | None: +async def _multi_pan_addon_info( + hass: HomeAssistant, entry: ConfigEntry +) -> AddonInfo | None: """Return AddonInfo if the multi-PAN addon is enabled for the Yellow's radio.""" addon_manager: AddonManager = get_addon_manager(hass) try: diff --git a/homeassistant/components/homeassistant_yellow/translations/ca.json b/homeassistant/components/homeassistant_yellow/translations/ca.json index c7fdf1a4acd..07bb2ae05aa 100644 --- a/homeassistant/components/homeassistant_yellow/translations/ca.json +++ b/homeassistant/components/homeassistant_yellow/translations/ca.json @@ -5,13 +5,35 @@ "addon_install_failed": "No s'ha pogut instal\u00b7lar el complement Silicon Labs Multiprotocol-", "addon_set_config_failed": "No s'ha pogut establir la configuraci\u00f3 de Silicon Labs Multiprotocol.", "addon_start_failed": "No s'ha pogut iniciar el complement Silicon Labs Multiprotocol.", + "disabled_due_to_bug": "Les opcions de maquinari s'han desactivat temporalment mentre es corregeix un error. [M\u00e9s informaci\u00f3]({url})", "not_hassio": "Les opcions de maquinari nom\u00e9s es poden configurar a les instal\u00b7lacions HassOS.", "zha_migration_failed": "La migraci\u00f3 ZHA no ha tingut \u00e8xit." }, "error": { "unknown": "Error inesperat" }, + "progress": { + "install_addon": "Espera mentre finalitza la instal\u00b7laci\u00f3 del complement Silicon Labs Multiprotocol. Pot tardar uns minuts.", + "start_addon": "Espera mentre es completa la inicialitzaci\u00f3 del complement Silicon Labs Multiprotocol. Pot tardar uns segons." + }, "step": { + "addon_installed_other_device": { + "title": "El suport multiprotocol ja est\u00e0 activat per a un altre dispositiu" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "Activa el suport multiprotocol" + }, + "description": "Quan el suport multiprotocol est\u00e0 activat, la r\u00e0dio IEEE 802.15.4 de {hardware_name} es pot utilitzar tant per a Zigbee com per a Thread alhora (utilitzat per Matter). Si la r\u00e0dio ja utilitza la integraci\u00f3 ZHA Zigbee, ZHA es reconfigurar\u00e0 per utilitzar el microprogramari multiprotocol. \n\nNota: aquesta \u00e9s una caracter\u00edstica experimental.", + "title": "Activa el suport multiprotocol per l'est\u00e0ndard de r\u00e0dio IEEE 802.15.4" + }, + "install_addon": { + "title": "Ha comen\u00e7at la instal\u00b7laci\u00f3 del complement Silicon Labs Multiprotocol" + }, + "show_revert_guide": { + "description": "Si vols canviar a nom\u00e9s microprogramari Zigbee, completa els passos manuals seg\u00fcents: \n\n * Elimina el complement Silicon Labs Multiprotocol\n\n * Carrega el microprogramari (firmware) de Zigbee, segueix la guia a https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually. \n\n * Torna a configurar ZHA per migrar la configuraci\u00f3 al dispositiu r\u00e0dio actualitzat", + "title": "El suport multiprotocol est\u00e0 activat per a aquest dispositiu" + }, "start_addon": { "title": "El complement Silicon Labs Multiprotocol s'est\u00e0 iniciant." } diff --git a/homeassistant/components/homeassistant_yellow/translations/de.json b/homeassistant/components/homeassistant_yellow/translations/de.json index 0b406d0f486..9282520684a 100644 --- a/homeassistant/components/homeassistant_yellow/translations/de.json +++ b/homeassistant/components/homeassistant_yellow/translations/de.json @@ -4,7 +4,8 @@ "addon_info_failed": "Silicon Labs Multiprotokoll-Zusatzinformationen konnten nicht abgerufen werden.", "addon_install_failed": "Die Installation des Silicon Labs Multiprotokoll Add-Ons ist fehlgeschlagen.", "addon_set_config_failed": "Die Silicon Labs Multiprotokoll Konfiguration konnte nicht eingestellt werden.", - "addon_start_failed": "Das Silicon Labs Multiprotokoll Add-On konnte nicht gestartet werden.", + "addon_start_failed": "Das Silicon Labs Multiprotokoll Add-on konnte nicht gestartet werden.", + "disabled_due_to_bug": "Die Hardwareoptionen sind vor\u00fcbergehend deaktiviert, w\u00e4hrend wir einen Fehler beheben. [Weitere Informationen]({url})", "not_hassio": "Die Hardwareoptionen k\u00f6nnen nur auf HassOS-Installationen konfiguriert werden.", "zha_migration_failed": "Die ZHA Migration war nicht erfolgreich." }, @@ -23,7 +24,7 @@ "data": { "enable_multi_pan": "Multiprotokoll Unterst\u00fctzung aktivieren" }, - "description": "Wenn die Multiprotokoll Unterst\u00fctzung aktiviert ist, kann das IEEE 802.15.4-Radio des Home Assistant Yellow gleichzeitig f\u00fcr Zigbee und Thread (von Matter verwendet) verwendet werden. Wenn das Funkger\u00e4t bereits von der ZHA-Zigbee-Integration verwendet wird, wird ZHA neu konfiguriert, um die Multiprotokoll-Firmware zu verwenden. \n\nHinweis: Dies ist eine experimentelle Funktion.", + "description": "Wenn die Multiprotokoll Unterst\u00fctzung aktiviert ist, kann das IEEE 802.15.4-Funkger\u00e4t von {hardware_name} gleichzeitig f\u00fcr Zigbee und Thread (verwendet von Matter) verwendet werden. Wenn das Funkger\u00e4t bereits von der Zigbee-Integration des ZHA verwendet wird, wird der ZHA neu konfiguriert, um die Multiprotokoll-Firmware zu verwenden.\n\nHinweis: Dies ist eine experimentelle Funktion.", "title": "Aktiviere die Multiprotokoll Unterst\u00fctzung auf dem IEEE 802.15.4-Funkger\u00e4t" }, "install_addon": { diff --git a/homeassistant/components/homeassistant_yellow/translations/el.json b/homeassistant/components/homeassistant_yellow/translations/el.json index 8652b4e8a98..d806ec556fc 100644 --- a/homeassistant/components/homeassistant_yellow/translations/el.json +++ b/homeassistant/components/homeassistant_yellow/translations/el.json @@ -5,6 +5,7 @@ "addon_install_failed": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03b5\u03b3\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf\u03c5 Silicon Labs Multiprotocol.", "addon_set_config_failed": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7\u03c2 \u03c0\u03b1\u03c1\u03b1\u03bc\u03ad\u03c4\u03c1\u03c9\u03bd \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ce\u03bd \u03c0\u03c1\u03c9\u03c4\u03bf\u03ba\u03cc\u03bb\u03bb\u03c9\u03bd Silicon Labs.", "addon_start_failed": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03b5\u03ba\u03ba\u03af\u03bd\u03b7\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf\u03c5 Silicon Labs Multiprotocol.", + "disabled_due_to_bug": "\u039f\u03b9 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03c5\u03bb\u03b9\u03ba\u03bf\u03cd \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03bf\u03cd\u03bd\u03c4\u03b1\u03b9 \u03c0\u03c1\u03bf\u03c3\u03c9\u03c1\u03b9\u03bd\u03ac \u03b5\u03bd\u03ce \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03bd\u03bf\u03c5\u03bc\u03b5 \u03ad\u03bd\u03b1 \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1. [\u039c\u03ac\u03b8\u03b5\u03c4\u03b5 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b1]({url})", "not_hassio": "\u039f\u03b9 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03c5\u03bb\u03b9\u03ba\u03bf\u03cd \u03bc\u03c0\u03bf\u03c1\u03bf\u03cd\u03bd \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03bf\u03cd\u03bd \u03bc\u03cc\u03bd\u03bf \u03c3\u03b5 \u03b5\u03b3\u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03ac\u03c3\u03b5\u03b9\u03c2 HassOS.", "zha_migration_failed": "\u0397 \u03bc\u03b5\u03c4\u03ac\u03b2\u03b1\u03c3\u03b7 ZHA \u03b4\u03b5\u03bd \u03c0\u03ad\u03c4\u03c5\u03c7\u03b5." }, diff --git a/homeassistant/components/homeassistant_yellow/translations/es.json b/homeassistant/components/homeassistant_yellow/translations/es.json index d93a329efd6..9f53790f770 100644 --- a/homeassistant/components/homeassistant_yellow/translations/es.json +++ b/homeassistant/components/homeassistant_yellow/translations/es.json @@ -5,6 +5,7 @@ "addon_install_failed": "No se pudo instalar el complemento Silicon Labs Multiprotocol.", "addon_set_config_failed": "No se pudo establecer la configuraci\u00f3n de Silicon Labs Multiprotocol.", "addon_start_failed": "No se pudo iniciar el complemento Silicon Labs Multiprotocol.", + "disabled_due_to_bug": "Las opciones de hardware est\u00e1n deshabilitadas temporalmente mientras solucionamos un error. [M\u00e1s informaci\u00f3n]({url})", "not_hassio": "Las opciones de hardware solo se pueden configurar en instalaciones de HassOS.", "zha_migration_failed": "La migraci\u00f3n de ZHA no tuvo \u00e9xito." }, @@ -23,18 +24,18 @@ "data": { "enable_multi_pan": "Habilitar soporte multiprotocolo" }, - "description": "Cuando el soporte multiprotocolo est\u00e1 habilitado, la radio IEEE 802.15.4 de Home Assistant Yellow se puede usar tanto para Zigbee como para Thread (usado por Matter) al mismo tiempo. Si la integraci\u00f3n ZHA Zigbee ya usa la radio, ZHA se volver\u00e1 a configurar para usar el firmware multiprotocolo. \n\nNota: Esta es una caracter\u00edstica experimental.", + "description": "Cuando el soporte multiprotocolo est\u00e1 habilitado, la radio IEEE 802.15.4 de {hardware_name} se puede usar tanto para Zigbee como para Thread (usado por Matter) al mismo tiempo. Si la integraci\u00f3n ZHA Zigbee ya usa la radio, ZHA se volver\u00e1 a configurar para usar el firmware multiprotocolo. \n\nNota: Esta es una caracter\u00edstica experimental.", "title": "Habilitar el soporte multiprotocolo en la radio IEEE 802.15.4" }, "install_addon": { "title": "La instalaci\u00f3n del complemento Silicon Labs Multiprotocol ha comenzado" }, "show_revert_guide": { - "description": "Si quieres cambiar el firmware a solo Zigbee, completa los siguientes pasos manuales: \n\n* Eliminar el complemento Silicon Labs Multiprotocol \n\n* Actualiza el firmware a solo Zigbee, sigue la gu\u00eda en https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually. \n\n* Vuelve a configurar ZHA para migrar la configuraci\u00f3n a la radio actualizada", + "description": "Si quieres cambiar el firmware a solo Zigbee, completa los siguientes pasos manuales: \n\n * Elimina el complemento Silicon Labs Multiprotocol \n\n * Actualiza el firmware solo de Zigbee, sigue la gu\u00eda en https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually. \n\n * Vuelve a configurar ZHA para migrar la configuraci\u00f3n a la radio actualizada", "title": "El soporte multiprotocolo est\u00e1 habilitado para este dispositivo" }, "start_addon": { - "title": "El complemento Silicon Labs Multiprotocol est\u00e1 iniciando." + "title": "El complemento Silicon Labs Multiprotocol se est\u00e1 iniciando." } } } diff --git a/homeassistant/components/homeassistant_yellow/translations/et.json b/homeassistant/components/homeassistant_yellow/translations/et.json index 2f2203d1e3c..cf38458a11d 100644 --- a/homeassistant/components/homeassistant_yellow/translations/et.json +++ b/homeassistant/components/homeassistant_yellow/translations/et.json @@ -5,6 +5,7 @@ "addon_install_failed": "Silicon Labs Multiprotocol add-on'i paigaldamine eba\u00f5nnestus.", "addon_set_config_failed": "Silicon Labs Multiprotocol konfiguratsiooni seadistamine eba\u00f5nnestus.", "addon_start_failed": "Silicon Labsi mitmeprotokolli lisandmooduli k\u00e4ivitamine nurjus.", + "disabled_due_to_bug": "Riistvaravalikud on vea parandamise ajal ajutiselt keelatud. [Lisateave] ({url})", "not_hassio": "Riistvaravalikuid saab konfigureerida ainult HassOS-i paigaldustes.", "zha_migration_failed": "ZHA sidumise siirdamine nurjus." }, diff --git a/homeassistant/components/homeassistant_yellow/translations/he.json b/homeassistant/components/homeassistant_yellow/translations/he.json new file mode 100644 index 00000000000..7c83080d4aa --- /dev/null +++ b/homeassistant/components/homeassistant_yellow/translations/he.json @@ -0,0 +1,42 @@ +{ + "options": { + "abort": { + "addon_info_failed": "\u05e0\u05db\u05e9\u05dc\u05d4 \u05e7\u05d1\u05dc\u05ea \u05de\u05d9\u05d3\u05e2 \u05e2\u05dc \u05d4\u05d4\u05e8\u05d7\u05d1\u05d4 \u05e8\u05d9\u05d1\u05d5\u05d9 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc\u05d9\u05dd \u05e9\u05dc \u05de\u05e2\u05d1\u05d3\u05d5\u05ea \u05d4\u05e1\u05d9\u05dc\u05d9\u05e7\u05d5\u05df.", + "addon_install_failed": "\u05d4\u05ea\u05e7\u05e0\u05ea \u05d4\u05d4\u05e8\u05d7\u05d1\u05d4 \u05e9\u05dc \u05e8\u05d9\u05d1\u05d5\u05d9 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05dc\u05d9\u05dd \u05e9\u05dc \u05de\u05e2\u05d1\u05d3\u05d5\u05ea \u05d4\u05e1\u05d9\u05dc\u05d9\u05e7\u05d5\u05df \u05e0\u05db\u05e9\u05dc\u05d4.", + "addon_set_config_failed": "\u05d4\u05d2\u05d3\u05e8\u05ea \u05ea\u05e6\u05d5\u05e8\u05ea \u05e8\u05d9\u05d1\u05d5\u05d9 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05dc\u05d9\u05dd \u05e9\u05dc \u05de\u05e2\u05d1\u05d3\u05d5\u05ea \u05d4\u05e1\u05d9\u05dc\u05d9\u05e7\u05d5\u05df \u05e0\u05db\u05e9\u05dc\u05d4.", + "addon_start_failed": "\u05d4\u05e4\u05e2\u05dc\u05ea \u05d4\u05d4\u05e8\u05d7\u05d1\u05d4 \u05e8\u05d9\u05d1\u05d5\u05d9 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05dc\u05d9\u05dd \u05e9\u05dc \u05de\u05e2\u05d1\u05d3\u05d5\u05ea \u05d4\u05e1\u05d9\u05dc\u05d9\u05e7\u05d5\u05df \u05e0\u05db\u05e9\u05dc\u05d4.", + "disabled_due_to_bug": "\u05d0\u05e4\u05e9\u05e8\u05d5\u05d9\u05d5\u05ea \u05d4\u05d7\u05d5\u05de\u05e8\u05d4 \u05de\u05d5\u05e9\u05d1\u05ea\u05d5\u05ea \u05d1\u05d0\u05d5\u05e4\u05df \u05d6\u05de\u05e0\u05d9 \u05d1\u05d6\u05de\u05df \u05e9\u05d0\u05e0\u05d5 \u05de\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05d0\u05d2. [\u05dc\u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3] ({url})", + "not_hassio": "\u05e0\u05d9\u05ea\u05df \u05dc\u05d4\u05d2\u05d3\u05d9\u05e8 \u05d0\u05ea \u05d0\u05e4\u05e9\u05e8\u05d5\u05d9\u05d5\u05ea \u05d4\u05d7\u05d5\u05de\u05e8\u05d4 \u05e8\u05e7 \u05d1\u05d4\u05ea\u05e7\u05e0\u05d5\u05ea HassOS.", + "zha_migration_failed": "\u05e0\u05d3\u05d9\u05d3\u05ea \u05d4-ZHA \u05dc\u05d0 \u05d4\u05e6\u05dc\u05d9\u05d7\u05d4." + }, + "error": { + "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" + }, + "progress": { + "install_addon": "\u05e0\u05d0 \u05dc\u05d4\u05de\u05ea\u05d9\u05df \u05e2\u05d3 \u05dc\u05e1\u05d9\u05d5\u05dd \u05d4\u05d4\u05ea\u05e7\u05e0\u05d4 \u05e9\u05dc \u05ea\u05d5\u05e1\u05e3 \u05d4-Silicon Labs Multiprotocol. \u05e4\u05e2\u05d5\u05dc\u05d4 \u05d6\u05d5 \u05e2\u05e9\u05d5\u05d9\u05d4 \u05dc\u05d4\u05d9\u05de\u05e9\u05da \u05de\u05e1\u05e4\u05e8 \u05d3\u05e7\u05d5\u05ea.", + "start_addon": "\u05e0\u05d0 \u05dc\u05d4\u05de\u05ea\u05d9\u05df \u05e2\u05d3 \u05dc\u05d4\u05e9\u05dc\u05de\u05ea \u05d4\u05ea\u05d5\u05e1\u05e3 \u05e8\u05d9\u05d1\u05d5\u05d9 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc\u05d9\u05dd \u05e9\u05dc \u05de\u05e2\u05d1\u05d3\u05d5\u05ea \u05d4\u05e1\u05d9\u05dc\u05d9\u05e7\u05d5\u05df. \u05e4\u05e2\u05d5\u05dc\u05d4 \u05d6\u05d5 \u05e2\u05e9\u05d5\u05d9\u05d4 \u05dc\u05d4\u05d9\u05de\u05e9\u05da \u05de\u05e1\u05e4\u05e8 \u05e9\u05e0\u05d9\u05d5\u05ea." + }, + "step": { + "addon_installed_other_device": { + "title": "\u05ea\u05de\u05d9\u05db\u05d4 \u05d1\u05e8\u05d9\u05d1\u05d5\u05d9 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc\u05d9\u05dd \u05db\u05d1\u05e8 \u05d6\u05de\u05d9\u05e0\u05d4 \u05e2\u05d1\u05d5\u05e8 \u05d4\u05ea\u05e7\u05df \u05d0\u05d7\u05e8" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "\u05d0\u05e4\u05e9\u05e8 \u05ea\u05de\u05d9\u05db\u05d4 \u05d1\u05e8\u05d9\u05d1\u05d5\u05d9 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc\u05d9\u05dd" + }, + "description": "\u05db\u05d0\u05e9\u05e8 \u05ea\u05de\u05d9\u05db\u05d4 \u05d1\u05e8\u05d9\u05d1\u05d5\u05d9 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc\u05d9\u05dd \u05de\u05d5\u05e4\u05e2\u05dc\u05ea, \u05e0\u05d9\u05ea\u05df \u05dc\u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05e8\u05d3\u05d9\u05d5 IEEE 802.15.4 \u05e9\u05dc {hardware_name} \u05d4\u05df \u05e2\u05d1\u05d5\u05e8 Zigbee \u05d5\u05d4\u05df \u05e2\u05d1\u05d5\u05e8 Thread (\u05d1\u05e9\u05d9\u05de\u05d5\u05e9 \u05e2\u05dc-\u05d9\u05d3\u05d9 Matter) \u05d1\u05d5-\u05d6\u05de\u05e0\u05d9\u05ea. \u05d0\u05dd \u05d4\u05e8\u05d3\u05d9\u05d5 \u05db\u05d1\u05e8 \u05e0\u05de\u05e6\u05d0 \u05d1\u05e9\u05d9\u05de\u05d5\u05e9 \u05e2\u05dc-\u05d9\u05d3\u05d9 \u05e9\u05d9\u05dc\u05d5\u05d1 ZHA Zigbee, ZHA \u05d9\u05d5\u05d2\u05d3\u05e8 \u05de\u05d7\u05d3\u05e9 \u05dc\u05e9\u05d9\u05de\u05d5\u05e9 \u05d1\u05e7\u05d5\u05e9\u05d7\u05d4 \u05de\u05e8\u05d5\u05d1\u05ea \u05d4\u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc\u05d9\u05dd.\n\n\u05d4\u05e2\u05e8\u05d4: \u05d6\u05d5\u05d4\u05d9 \u05ea\u05db\u05d5\u05e0\u05d4 \u05e0\u05d9\u05e1\u05d9\u05d5\u05e0\u05d9\u05ea.", + "title": "\u05d0\u05e4\u05e9\u05e8 \u05ea\u05de\u05d9\u05db\u05d4 \u05d1\u05e8\u05d9\u05d1\u05d5\u05d9 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc\u05d9\u05dd \u05d1\u05e8\u05d3\u05d9\u05d5 IEEE 802.15.4" + }, + "install_addon": { + "title": "\u05d4\u05ea\u05e7\u05e0\u05ea \u05d4\u05d4\u05e8\u05d7\u05d1\u05d4 \u05e8\u05d9\u05d1\u05d5\u05d9 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc\u05d9\u05dd \u05e9\u05dc \u05de\u05e2\u05d1\u05d3\u05d5\u05ea \u05d4\u05e1\u05d9\u05dc\u05d9\u05e7\u05d5\u05df \u05d4\u05d7\u05dc\u05d4" + }, + "show_revert_guide": { + "description": "\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05e9\u05e0\u05d5\u05ea \u05dc\u05e7\u05d5\u05e9\u05d7\u05d4 \u05e9\u05dc Zigbee \u05d1\u05dc\u05d1\u05d3, \u05d9\u05e9 \u05dc\u05d1\u05e6\u05e2 \u05d0\u05ea \u05d4\u05e9\u05dc\u05d1\u05d9\u05dd \u05d4\u05d9\u05d3\u05e0\u05d9\u05d9\u05dd \u05d4\u05d1\u05d0\u05d9\u05dd:\n\n* \u05d4\u05e1\u05e8\u05ea \u05d4\u05d4\u05e8\u05d7\u05d1\u05d4 \u05e8\u05d9\u05d1\u05d5\u05d9 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc\u05d9\u05dd \u05e9\u05dc \u05de\u05e2\u05d1\u05d3\u05d5\u05ea \u05d4\u05e1\u05d9\u05dc\u05d9\u05e7\u05d5\u05df\n\n* \u05e6\u05e8\u05d9\u05d1\u05ea \u05d4\u05e7\u05d5\u05e9\u05d7\u05d4 Zigbee \u05d1\u05dc\u05d1\u05d3, \u05dc\u05e2\u05e7\u05d5\u05d1 \u05d0\u05d7\u05e8 \u05d4\u05de\u05d3\u05e8\u05d9\u05da https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually.\n\n* \u05dc\u05d4\u05d2\u05d3\u05d9\u05e8 \u05de\u05d7\u05d3\u05e9 \u05d0\u05ea ZHA \u05db\u05d3\u05d9 \u05dc\u05d4\u05e2\u05d1\u05d9\u05e8 \u05d4\u05d2\u05d3\u05e8\u05d5\u05ea \u05dc\u05e8\u05d3\u05d9\u05d5 \u05d4\u05de\u05d7\u05d5\u05d3\u05e9", + "title": "\u05ea\u05de\u05d9\u05db\u05d4 \u05d1\u05e8\u05d9\u05d1\u05d5\u05d9 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc\u05d9\u05dd \u05d6\u05de\u05d9\u05e0\u05d4 \u05e2\u05d1\u05d5\u05e8 \u05d4\u05ea\u05e7\u05df \u05d6\u05d4" + }, + "start_addon": { + "title": "\u05d4\u05d4\u05e8\u05d7\u05d1\u05d4 \u05e8\u05d9\u05d1\u05d5\u05d9 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc\u05d9\u05dd \u05e9\u05dc \u05de\u05e2\u05d1\u05d3\u05d5\u05ea \u05d4\u05e1\u05d9\u05dc\u05d9\u05e7\u05d5\u05df \u05d4\u05d7\u05dc\u05d4." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_yellow/translations/hu.json b/homeassistant/components/homeassistant_yellow/translations/hu.json index b2fdffb1450..f02ebb7b333 100644 --- a/homeassistant/components/homeassistant_yellow/translations/hu.json +++ b/homeassistant/components/homeassistant_yellow/translations/hu.json @@ -5,7 +5,9 @@ "addon_install_failed": "Nem siker\u00fclt telep\u00edteni a Silicon Labs Multiprotocol b\u0151v\u00edtm\u00e9nyt.", "addon_set_config_failed": "A Silicon Labs Multiprotokoll konfigur\u00e1ci\u00f3 be\u00e1ll\u00edt\u00e1sa sikertelen volt.", "addon_start_failed": "Nem siker\u00fclt elind\u00edtani a Silicon Labs Multiprotocol b\u0151v\u00edtm\u00e9nyt.", - "not_hassio": "A hardverbe\u00e1ll\u00edt\u00e1sok csak HassOS telep\u00edt\u00e9sekn\u00e9l konfigur\u00e1lhat\u00f3k." + "disabled_due_to_bug": "A hardver opci\u00f3k \u00e1tmenetileg le vannak tiltva, am\u00edg kijav\u00edtunk egy hib\u00e1t. [Tov\u00e1bbi inform\u00e1ci\u00f3]({url})", + "not_hassio": "A hardverbe\u00e1ll\u00edt\u00e1sok csak HassOS telep\u00edt\u00e9sekn\u00e9l konfigur\u00e1lhat\u00f3k.", + "zha_migration_failed": "A ZHA migr\u00e1ci\u00f3 nem siker\u00fclt." }, "error": { "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" @@ -22,7 +24,7 @@ "data": { "enable_multi_pan": "Multiprotokoll-t\u00e1mogat\u00e1s enged\u00e9lyez\u00e9se" }, - "description": "Ha a multiprotokoll-t\u00e1mogat\u00e1s enged\u00e9lyezve van, a Home Assistant Yellow IEEE 802.15.4 r\u00e1di\u00f3ja egyszerre haszn\u00e1lhat\u00f3 a Zigbee \u00e9s a Thread (a Matter \u00e1ltal haszn\u00e1lt) r\u00e1di\u00f3hoz. Megjegyz\u00e9s: Ez egy k\u00eds\u00e9rleti funkci\u00f3.", + "description": "Ha a multiprotokoll-t\u00e1mogat\u00e1s enged\u00e9lyezve van, a {hardware_name} IEEE 802.15.4 r\u00e1di\u00f3ja egyszerre haszn\u00e1lhat\u00f3 a Zigbee \u00e9s a Thread (a Matter \u00e1ltal haszn\u00e1lt) funkcionalit\u00e1shoz. Ha a r\u00e1di\u00f3t m\u00e1r haszn\u00e1lja a ZHA Zigbee integr\u00e1ci\u00f3, a ZHA-t \u00e1t kell konfigur\u00e1lni a multiprotokoll firmware haszn\u00e1lat\u00e1ra.\n\nMegjegyz\u00e9s: Ez egy k\u00eds\u00e9rleti funkci\u00f3.", "title": "Multiprotokoll t\u00e1mogat\u00e1s\u00e1nak enged\u00e9lyez\u00e9se az IEEE 802.15.4 r\u00e1di\u00f3ban" }, "install_addon": { diff --git a/homeassistant/components/homeassistant_yellow/translations/id.json b/homeassistant/components/homeassistant_yellow/translations/id.json index e3237d7b95e..5fdfd444af9 100644 --- a/homeassistant/components/homeassistant_yellow/translations/id.json +++ b/homeassistant/components/homeassistant_yellow/translations/id.json @@ -5,6 +5,7 @@ "addon_install_failed": "Gagal menginstal add-on Silicon Labs Multiprotocol.", "addon_set_config_failed": "Gagal mengatur konfigurasi Silicon Labs Multiprotocol.", "addon_start_failed": "Gagal memulai add-on Silicon Labs Multiprotocol.", + "disabled_due_to_bug": "Opsi perangkat keras untuk sementara ini dinonaktifkan karena kami sedang memperbaiki bugnya. [Pelajari lebih lanjut]({url})", "not_hassio": "Opsi perangkat keras hanya bisa dikonfigurasi pada instalasi HassOS.", "zha_migration_failed": "Migrasi ZHA tidak berhasil." }, @@ -23,7 +24,7 @@ "data": { "enable_multi_pan": "Aktifkan dukungan multiprotokol" }, - "description": "Jika dukungan multiprotocol diaktifkan, radio IEEE 802.15.4 Home Assistant Yellow dapat digunakan untuk Zigbee dan Thread (digunakan oleh Matter) secara bersamaan. Catatan: Ini adalah fitur eksperimental. Jika komponen radio telah digunakan oleh integrasi ZHA Zigbee, ZHA akan dikonfigurasi ulang untuk menggunakan firmware multiprotokol.\n\nCatatan: Fitur ini bersifat eksperimental.", + "description": "Jika dukungan multiprotocol diaktifkan, radio IEEE 802.15.4 {hardware_name} dapat digunakan untuk Zigbee dan Thread (digunakan oleh Matter) secara bersamaan. Catatan: Ini adalah fitur eksperimental. Jika komponen radio telah digunakan oleh integrasi ZHA Zigbee, ZHA akan dikonfigurasi ulang untuk menggunakan firmware multiprotokol.\n\nCatatan: Fitur ini bersifat eksperimental.", "title": "Aktifkan dukungan multiprotokol pada radio IEEE 802.15.4" }, "install_addon": { diff --git a/homeassistant/components/homeassistant_yellow/translations/it.json b/homeassistant/components/homeassistant_yellow/translations/it.json index 556d0cd3737..2c95698344a 100644 --- a/homeassistant/components/homeassistant_yellow/translations/it.json +++ b/homeassistant/components/homeassistant_yellow/translations/it.json @@ -5,6 +5,7 @@ "addon_install_failed": "Impossibile installare il componente aggiuntivo Silicon Labs Multiprotocol.", "addon_set_config_failed": "Impossibile impostare la configurazione di Silicon Labs Multiprotocol.", "addon_start_failed": "Impossibile avviare il componente aggiuntivo Silicon Labs Multiprotocol.", + "disabled_due_to_bug": "Le opzioni hardware sono temporaneamente disabilitate mentre correggiamo un bug. [Ulteriori informazioni]({url})", "not_hassio": "Le opzioni hardware possono essere configurate solo su installazioni HassOS.", "zha_migration_failed": "La migrazione ZHA non \u00e8 riuscita." }, @@ -12,7 +13,7 @@ "unknown": "Errore imprevisto" }, "progress": { - "install_addon": "Attendere il completamento dell'installazione del componente aggiuntivo Silicon Labs Multiprotocol. Questo pu\u00f2 richiedere diversi minuti.", + "install_addon": "Attendi il completamento dell'installazione del componente aggiuntivo Silicon Labs Multiprotocol. Questo pu\u00f2 richiedere alcuni minuti.", "start_addon": "Attendi il completamento dell'avvio del componente aggiuntivo Silicon Labs Multiprotocol. Questo potrebbe richiedere alcuni secondi." }, "step": { @@ -23,18 +24,18 @@ "data": { "enable_multi_pan": "Abilita il supporto multiprotocollo" }, - "description": "Quando il supporto multiprotocollo \u00e8 abilitato, la radio IEEE 802.15.4 di Home Assistant Yellow pu\u00f2 essere utilizzata contemporaneamente sia per Zigbee che per Thread (utilizzato da Matter). Nota: questa \u00e8 una funzione sperimentale.", + "description": "Quando il supporto multiprotocollo \u00e8 abilitato, la radio IEEE 802.15.4 di {hardware_name} pu\u00f2 essere utilizzata contemporaneamente sia per Zigbee che per Thread (utilizzato da Matter). Se la radio \u00e8 gi\u00e0 utilizzata dall'integrazione ZHA Zigbee, ZHA verr\u00e0 riconfigurata per utilizzare il firmware multiprotocollo. \n\nNota: questa \u00e8 una funzione sperimentale.", "title": "Abilita il supporto multiprotocollo sulla radio IEEE 802.15.4" }, "install_addon": { "title": "L'installazione del componente aggiuntivo Silicon Labs Multiprotocol \u00e8 iniziata" }, "show_revert_guide": { - "description": "Se desideri passare al solo firmware Zigbee, completa i seguenti passaggi manuali: \n\n * Rimuovi il componente aggiuntivo Silicon Labs Multiprotocol \n\n * Caricare solo il firmware Zigbee, segui la guida su https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually. \n\n * Riconfigurare ZHA per migrare le impostazioni alla radio ricaricata", + "description": "Se desideri passare al solo firmware Zigbee, completa i seguenti passaggi manuali: \n\n * Rimuovi il componente aggiuntivo Silicon Labs Multiprotocol \n\n * Caricare solo il firmware Zigbee, segui la guida su https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually. \n\n * Riconfigurare ZHA per migrare le impostazioni alla radio con firmware ricaricato", "title": "Il supporto multiprotocollo \u00e8 abilitato per questo dispositivo" }, "start_addon": { - "title": "Il componente aggiuntivo Silicon Labs Multiprotocol sta per essere avviato." + "title": "Il componente aggiuntivo Silicon Labs Multiprotocol \u00e8 in fase di avvio." } } } diff --git a/homeassistant/components/homeassistant_yellow/translations/ja.json b/homeassistant/components/homeassistant_yellow/translations/ja.json new file mode 100644 index 00000000000..65eea011fa9 --- /dev/null +++ b/homeassistant/components/homeassistant_yellow/translations/ja.json @@ -0,0 +1,8 @@ +{ + "options": { + "abort": { + "disabled_due_to_bug": "\u30d0\u30b0\u3092\u4fee\u6b63\u3059\u308b\u9593\u3001\u30cf\u30fc\u30c9\u30a6\u30a7\u30a2\u30aa\u30d7\u30b7\u30e7\u30f3\u306f\u4e00\u6642\u7684\u306b\u7121\u52b9\u306b\u306a\u308a\u307e\u3059\u3002 [\u8a73\u7d30]({url})", + "zha_migration_failed": "ZHA\u3078\u306e\u79fb\u884c\u306f\u6210\u529f\u3057\u307e\u305b\u3093\u3067\u3057\u305f\u3002" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_yellow/translations/ko.json b/homeassistant/components/homeassistant_yellow/translations/ko.json new file mode 100644 index 00000000000..76968abdc72 --- /dev/null +++ b/homeassistant/components/homeassistant_yellow/translations/ko.json @@ -0,0 +1,39 @@ +{ + "options": { + "abort": { + "addon_set_config_failed": "Silicon Labs \ub2e4\uc911\ud504\ub85c\ud1a0\ucf5c \uad6c\uc131\uc744 \uc124\uc815\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4.", + "addon_start_failed": "Silicon Labs \ub2e4\uc911\ud504\ub85c\ud1a0\ucf5c \uc560\ub4dc\uc628\uc744 \uc2dc\uc791\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4.", + "not_hassio": "\ud558\ub4dc\uc6e8\uc5b4 \uc635\uc158\uc740 HassOS \uc124\uce58\uc5d0\uc11c\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.", + "zha_migration_failed": "ZHA \ub9c8\uc774\uadf8\ub808\uc774\uc158\uc5d0 \uc2e4\ud328\ud588\uc2b5\ub2c8\ub2e4." + }, + "error": { + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "progress": { + "install_addon": "Silicon Labs \ub2e4\uc911\ud504\ub85c\ud1a0\ucf5c \uc560\ub4dc\uc628 \uc124\uce58\uac00 \uc644\ub8cc\ub418\ub294 \ub3d9\uc548 \uae30\ub2e4\ub824 \uc8fc\uc2ed\uc2dc\uc624. \uba87 \ubd84\uc774 \uac78\ub9b4 \uc218 \uc788\uc2b5\ub2c8\ub2e4.", + "start_addon": "Silicon Labs \ub2e4\uc911\ud504\ub85c\ud1a0\ucf5c \uc560\ub4dc\uc628 \uc2dc\uc791\uc774 \uc644\ub8cc\ub420 \ub54c\uae4c\uc9c0 \uae30\ub2e4\ub9ac\uc2ed\uc2dc\uc624. \uba87 \ucd08 \uc815\ub3c4 \uac78\ub9b4 \uc218 \uc788\uc2b5\ub2c8\ub2e4." + }, + "step": { + "addon_installed_other_device": { + "title": "\ub2e4\ub978 \uae30\uae30\uc5d0 \ub300\ud574 \ub2e4\uc911 \ud504\ub85c\ud1a0\ucf5c \uc9c0\uc6d0\uc774 \uc774\ubbf8 \ud65c\uc131\ud654\ub418\uc5b4 \uc788\uc2b5\ub2c8\ub2e4." + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "\ub2e4\uc911 \ud504\ub85c\ud1a0\ucf5c \uc9c0\uc6d0 \ud65c\uc131\ud654" + }, + "description": "\ub2e4\uc911 \ud504\ub85c\ud1a0\ucf5c \uc9c0\uc6d0\uc774 \ud65c\uc131\ud654\ub418\uba74 {hardware_name} \uc758 IEEE 802.15.4 \ud1b5\uc2e0\ubc29\ubc95\uc73c\ub85c Zigbee\uc640 Thread(Matter\uc5d0\uc11c \uc0ac\uc6a9)\ub97c \ub3d9\uc2dc\uc5d0 \uc0ac\uc6a9\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4. \ud504\ub85c\ud1a0\ucf5c\uc774 ZHA Zigbee \ud1b5\ud569\uad6c\uc131\uc694\uc18c\uc5d0\uc11c \uc774\ubbf8 \uc0ac\uc6a9 \uc911\uc778 \uacbd\uc6b0 ZHA\ub294 \ub2e4\uc911 \ud504\ub85c\ud1a0\ucf5c \ud38c\uc6e8\uc5b4\ub97c \uc0ac\uc6a9\ud558\ub3c4\ub85d \uc7ac\uad6c\uc131\ub429\ub2c8\ub2e4. \n\n \ucc38\uace0: \uc774\uac83\uc740 \uc2e4\ud5d8\uc801\uc778 \uae30\ub2a5\uc785\ub2c8\ub2e4.", + "title": "IEEE 802.15.4 \ud1b5\uc2e0\ubc29\ubc95\uc5d0\uc11c \ub2e4\uc911\ud504\ub85c\ud1a0\ucf5c \uc9c0\uc6d0 \ud65c\uc131\ud654" + }, + "install_addon": { + "title": "Silicon Labs \ub2e4\uc911\ud504\ub85c\ud1a0\ucf5c \uc560\ub4dc\uc628\uc744 \uc124\uce58 \uc911\uc785\ub2c8\ub2e4" + }, + "show_revert_guide": { + "description": "Zigbee \uc804\uc6a9 \ud38c\uc6e8\uc5b4\ub85c \ubcc0\uacbd\ud558\ub824\uba74 \ub2e4\uc74c \uc548\ub0b4\ub97c \ub530\ub974\uc138\uc694. \n\n * Silicon Labs \ub2e4\uc911\ud504\ub85c\ud1a0\ucf5c \uc560\ub4dc\uc628 \uc81c\uac70 \n\n * Zigbee \uc804\uc6a9 \ud38c\uc6e8\uc5b4\ub97c \ud50c\ub798\uc2dc. \ub2e4\uc74c \uc548\ub0b4\ub97c \ub530\ub974\uc138\uc694. (https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually)\n\n * ZHA\ub97c \uc7ac\uad6c\uc131\ud558\uc5ec \ubc14\ub010 \ud1b5\uc2e0\ubc29\ubc95\uc744 \uc801\uc6a9", + "title": "\uc774 \uae30\uae30\uc5d0 \ub2e4\uc911\ud504\ub85c\ud1a0\ucf5c \uc774 \uc0ac\uc6a9\ub418\ub3c4\ub85d \uc124\uc815\ub418\uc5c8\uc2b5\ub2c8\ub2e4." + }, + "start_addon": { + "title": "Silicon Labs \ub2e4\uc911\ud504\ub85c\ud1a0\ucf5c \uc560\ub4dc\uc628\uc774 \uc2dc\uc791\ub429\ub2c8\ub2e4." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homeassistant_yellow/translations/nl.json b/homeassistant/components/homeassistant_yellow/translations/nl.json index ee6cec8c81c..871092b79a7 100644 --- a/homeassistant/components/homeassistant_yellow/translations/nl.json +++ b/homeassistant/components/homeassistant_yellow/translations/nl.json @@ -23,14 +23,14 @@ "data": { "enable_multi_pan": "Inschakelen multi-protocol ondersteuning" }, - "description": "Wanneer multi-protocol ondersteuning is ingeschakeld, zal het Home Assistant Yellow IEEE 802.15.4 toegangspunt gelijktijdig voor zowel Zigbee als Thread (gebruikt door Matter) ingezet worden. Als het toegangspunt als wordt gebruikt door de ZHA Zigbee integratie, dan zal, ZHA worden be ge-reconfigureerd om de multi-protocol firmware te gaan gebruiken.\n\nOpmerking: Dit is een experimentele functie.", + "description": "Wanneer multi-protocol ondersteuning is ingeschakeld, zal het Home Assistant Yellow IEEE 802.15.4 toegangspunt gelijktijdig voor zowel Zigbee als Thread (gebruikt door Matter) ingezet worden. Als het toegangspunt al wordt gebruikt door de ZHA Zigbee integratie, dan zal ZHA worden geconfigureerd om de multi-protocol firmware te gaan gebruiken.\n\nOpmerking: Dit is een experimentele functionaliteit.", "title": "Inschakelen multi-protocol ondersteuning op het IEEE 802.15.4 toegangspunt" }, "install_addon": { "title": "De Silicon Labs Multiprotocol add-on is gestart" }, "show_revert_guide": { - "description": "Als je naar Zigbee-only firmware wilt overstappen, voltooi dan de volgende handmatig stappen:\n\n * Verwijderd de Silicon Labs Multiprotocol addon\n\n * Flash de Zigbee-only firmware, volg de aanwijzigingen op https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually\n\n * Herconfigureer ZHA om de instellingen te migreren naar het ge-reflashed toegangspunt" + "description": "Als je naar Zigbee-only firmware wilt overstappen, voltooi dan de volgende handmatig stappen:\n\n * Verwijderd de Silicon Labs multi-protocol add-on\n\n * Flash de Zigbee-only firmware, volg de aanwijzigingen op https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually\n\n * Herconfigureer ZHA om de instellingen te migreren naar het ge-reflashed toegangspunt" }, "start_addon": { "title": "De Silicon Labs Multiprotocol add-on is aan het opstarten." diff --git a/homeassistant/components/homeassistant_yellow/translations/no.json b/homeassistant/components/homeassistant_yellow/translations/no.json index d93b078dd4b..4349387fd53 100644 --- a/homeassistant/components/homeassistant_yellow/translations/no.json +++ b/homeassistant/components/homeassistant_yellow/translations/no.json @@ -5,6 +5,7 @@ "addon_install_failed": "Kunne ikke installere Silicon Labs Multiprotocol-tillegget.", "addon_set_config_failed": "Kunne ikke angi Silicon Labs Multiprotocol-konfigurasjon.", "addon_start_failed": "Kunne ikke starte Silicon Labs Multiprotocol-tillegget.", + "disabled_due_to_bug": "Maskinvarealternativene er midlertidig deaktivert mens vi fikser en feil. [Finn ut mer]( {url} )", "not_hassio": "Maskinvarealternativene kan bare konfigureres p\u00e5 HassOS-installasjoner.", "zha_migration_failed": "ZHA-migreringen lyktes ikke." }, @@ -23,7 +24,7 @@ "data": { "enable_multi_pan": "Aktiver st\u00f8tte for multiprotokoll" }, - "description": "N\u00e5r multiprotokollst\u00f8tte er aktivert, kan Home Assistant Yellows IEEE 802.15.4-radio brukes for b\u00e5de Zigbee og Thread (brukt av Matter) samtidig. Hvis radioen allerede brukes av ZHA Zigbee-integrasjonen, vil ZHA bli rekonfigurert til \u00e5 bruke multiprotokollfastvaren. \n\n Merk: Dette er en eksperimentell funksjon.", + "description": "N\u00e5r multiprotokollst\u00f8tte er aktivert, kan {hardware_name} sin IEEE 802.15.4-radio brukes for b\u00e5de Zigbee og Thread (brukt av Matter) samtidig. Hvis radioen allerede brukes av ZHA Zigbee-integrasjonen, vil ZHA bli rekonfigurert til \u00e5 bruke multiprotokoll-fastvaren. \n\n Merk: Dette er en eksperimentell funksjon.", "title": "Aktiver st\u00f8tte for multiprotokoll p\u00e5 IEEE 802.15.4-radioen" }, "install_addon": { diff --git a/homeassistant/components/homeassistant_yellow/translations/pl.json b/homeassistant/components/homeassistant_yellow/translations/pl.json index a0c4c902730..b678d2a97a5 100644 --- a/homeassistant/components/homeassistant_yellow/translations/pl.json +++ b/homeassistant/components/homeassistant_yellow/translations/pl.json @@ -5,6 +5,7 @@ "addon_install_failed": "Nie uda\u0142o si\u0119 zainstalowa\u0107 dodatku Silicon Labs Multiprotocol.", "addon_set_config_failed": "Nie uda\u0142o si\u0119 ustawi\u0107 konfiguracji Silicon Labs Multiprotocol.", "addon_start_failed": "Nie uda\u0142o si\u0119 uruchomi\u0107 dodatku Silicon Labs Multiprotocol.", + "disabled_due_to_bug": "Opcje sprz\u0119towe s\u0105 tymczasowo wy\u0142\u0105czone na czas naprawiania przez nas b\u0142\u0119du. [Dowiedz si\u0119 wi\u0119cej]({url})", "not_hassio": "Opcje sprz\u0119towe mo\u017cna skonfigurowa\u0107 tylko w instalacjach HassOS.", "zha_migration_failed": "Migracja ZHA nie powiod\u0142a si\u0119." }, @@ -23,7 +24,7 @@ "data": { "enable_multi_pan": "W\u0142\u0105cz obs\u0142ug\u0119 Multiprotocol" }, - "description": "Gdy w\u0142\u0105czona jest obs\u0142uga Multiprotocol, radio IEEE 802.15.4 Home Assistant Yellow mo\u017ce by\u0107 u\u017cywane jednocze\u015bnie dla Zigbee i Thread (u\u017cywane przez Matter). Uwaga: jest to funkcja eksperymentalna. Je\u015bli radio jest ju\u017c u\u017cywane przez integracj\u0119 ZHA Zigbee, ZHA zostanie ponownie skonfigurowane, aby korzysta\u0107 z oprogramowania multiprotocol.\n\nUwaga: jest to funkcja eksperymentalna.", + "description": "Gdy w\u0142\u0105czona jest obs\u0142uga Multiprotocol, radio IEEE 802.15.4 dla {hardware_name} mo\u017ce by\u0107 u\u017cywane jednocze\u015bnie dla Zigbee i Thread (u\u017cywane przez Matter). Uwaga: jest to funkcja eksperymentalna. Je\u015bli radio jest ju\u017c u\u017cywane przez integracj\u0119 ZHA Zigbee, ZHA zostanie ponownie skonfigurowane, aby korzysta\u0107 z oprogramowania multiprotocol.\n\nUwaga: jest to funkcja eksperymentalna.", "title": "W\u0142\u0105cz obs\u0142ug\u0119 multiprotocol w radiu IEEE 802.15.4" }, "install_addon": { diff --git a/homeassistant/components/homeassistant_yellow/translations/pt-BR.json b/homeassistant/components/homeassistant_yellow/translations/pt-BR.json index b828d7a9d5b..d6d9a53a09d 100644 --- a/homeassistant/components/homeassistant_yellow/translations/pt-BR.json +++ b/homeassistant/components/homeassistant_yellow/translations/pt-BR.json @@ -5,6 +5,7 @@ "addon_install_failed": "Falha ao instalar o add-on Silicon Labs Multiprotocol.", "addon_set_config_failed": "Falha ao definir a configura\u00e7\u00e3o multiprotocolo da Silicon Labs.", "addon_start_failed": "Falha ao iniciar o add-on Silicon Labs Multiprotocol.", + "disabled_due_to_bug": "As op\u00e7\u00f5es de hardware est\u00e3o temporariamente desativadas enquanto corrigimos um bug. [Saiba mais]({url})", "not_hassio": "As op\u00e7\u00f5es de hardware s\u00f3 podem ser configuradas em instala\u00e7\u00f5es HassOS.", "zha_migration_failed": "A migra\u00e7\u00e3o ZHA n\u00e3o foi bem-sucedida." }, @@ -21,10 +22,10 @@ }, "addon_not_installed": { "data": { - "enable_multi_pan": "Habilitar suporte multiprotocolo" + "enable_multi_pan": "Ativar suporte multiprotocolo" }, - "description": "Quando o suporte multiprotocolo est\u00e1 ativado, o r\u00e1dio IEEE 802.15.4 do Home Assistant Yellow pode ser usado para Zigbee e Thread (usado por Matter) ao mesmo tempo. Se o r\u00e1dio j\u00e1 estiver sendo usado pela integra\u00e7\u00e3o ZHA Zigbee, o ZHA ser\u00e1 reconfigurado para usar o firmware multiprotocolo. \n\n Nota: Este \u00e9 um recurso experimental.", - "title": "Habilitar o suporte multiprotocolo no r\u00e1dio IEEE 802.15.4" + "description": "Quando o suporte multiprotocolo est\u00e1 ativado, o r\u00e1dio IEEE 802.15.4 do {hardware_name} pode ser usado para Zigbee e Thread (usado por Matter) ao mesmo tempo. Se o r\u00e1dio j\u00e1 estiver sendo usado pela integra\u00e7\u00e3o ZHA Zigbee, o ZHA ser\u00e1 reconfigurado para usar o firmware multiprotocolo. \n\n Nota: Esse \u00e9 um recurso experimental.", + "title": "Habilite o suporte multiprotocolo no r\u00e1dio IEEE 802.15.4" }, "install_addon": { "title": "A instala\u00e7\u00e3o do add-on Silicon Labs Multiprotocol foi iniciada" diff --git a/homeassistant/components/homeassistant_yellow/translations/ru.json b/homeassistant/components/homeassistant_yellow/translations/ru.json index f1ae0759e15..e9aead744c9 100644 --- a/homeassistant/components/homeassistant_yellow/translations/ru.json +++ b/homeassistant/components/homeassistant_yellow/translations/ru.json @@ -5,6 +5,7 @@ "addon_install_failed": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \"Silicon Labs Multiprotocol\".", "addon_set_config_failed": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \"Silicon Labs Multiprotocol\".", "addon_start_failed": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \"Silicon Labs Multiprotocol\".", + "disabled_due_to_bug": "\u0410\u043f\u043f\u0430\u0440\u0430\u0442\u043d\u044b\u0435 \u043e\u043f\u0446\u0438\u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u044b, \u043f\u043e\u043a\u0430 \u043c\u044b \u0438\u0441\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u043e\u0448\u0438\u0431\u043a\u0443. [\u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435]({url})", "not_hassio": "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u043e\u0431\u043e\u0440\u0443\u0434\u043e\u0432\u0430\u043d\u0438\u044f \u043c\u043e\u0436\u043d\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0445 \u0441\u0438\u0441\u0442\u0435\u043c\u0430\u0445 HassOS.", "zha_migration_failed": "\u041c\u0438\u0433\u0440\u0430\u0446\u0438\u044f ZHA \u043d\u0435 \u0443\u0434\u0430\u043b\u0430\u0441\u044c." }, @@ -23,7 +24,7 @@ "data": { "enable_multi_pan": "\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u0432" }, - "description": "\u041a\u043e\u0433\u0434\u0430 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0430 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u0432, \u0440\u0430\u0434\u0438\u043e\u043c\u043e\u0434\u0443\u043b\u044c Home Assistant Yellow \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u0430 IEEE 802.15.4 \u043c\u043e\u0436\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u043a\u0430\u043a \u0434\u043b\u044f Zigbee, \u0442\u0430\u043a \u0438 \u0434\u043b\u044f Thread (\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0432 Matter). \u0415\u0441\u043b\u0438 \u0440\u0430\u0434\u0438\u043e\u043c\u043e\u0434\u0443\u043b\u044c \u0443\u0436\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d \u0432 ZHA \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u0430 Zigbee, ZHA \u0431\u0443\u0434\u0435\u0442 \u043f\u0435\u0440\u0435\u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u0430 \u0434\u043b\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u0432.\n\n\u041f\u0440\u0438\u043c\u0435\u0447\u0430\u043d\u0438\u0435: \u044d\u0442\u043e \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u0430\u043b\u044c\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f.", + "description": "\u041a\u043e\u0433\u0434\u0430 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0430 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u0432, \u0440\u0430\u0434\u0438\u043e\u043c\u043e\u0434\u0443\u043b\u044c {hardware_name} \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u0430 IEEE 802.15.4 \u043c\u043e\u0436\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u043a\u0430\u043a \u0434\u043b\u044f Zigbee, \u0442\u0430\u043a \u0438 \u0434\u043b\u044f Thread (\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0432 Matter). \u0415\u0441\u043b\u0438 \u0440\u0430\u0434\u0438\u043e\u043c\u043e\u0434\u0443\u043b\u044c \u0443\u0436\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d \u0432 ZHA \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u0430 Zigbee, ZHA \u0431\u0443\u0434\u0435\u0442 \u043f\u0435\u0440\u0435\u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u0430 \u0434\u043b\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u0432.\n\n\u041f\u0440\u0438\u043c\u0435\u0447\u0430\u043d\u0438\u0435: \u044d\u0442\u043e \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u0430\u043b\u044c\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f.", "title": "\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u0432 \u043d\u0430 \u0440\u0430\u0434\u0438\u043e\u043c\u043e\u0434\u0443\u043b\u0435 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u0430 IEEE 802.15.4." }, "install_addon": { diff --git a/homeassistant/components/homeassistant_yellow/translations/sk.json b/homeassistant/components/homeassistant_yellow/translations/sk.json index f40c05f8068..91f2f37907e 100644 --- a/homeassistant/components/homeassistant_yellow/translations/sk.json +++ b/homeassistant/components/homeassistant_yellow/translations/sk.json @@ -1,11 +1,41 @@ { "options": { + "abort": { + "addon_info_failed": "Nepodarilo sa z\u00edska\u0165 inform\u00e1cie o doplnku Silicon Labs Multiprotocol.", + "addon_install_failed": "Nepodarilo sa nain\u0161talova\u0165 doplnok Silicon Labs Multiprotocol.", + "addon_set_config_failed": "Nepodarilo sa nastavi\u0165 konfigur\u00e1ciu Silicon Labs Multiprotocol.", + "addon_start_failed": "Nepodarilo sa spusti\u0165 doplnok Silicon Labs Multiprotocol.", + "disabled_due_to_bug": "Hardv\u00e9rov\u00e9 mo\u017enosti s\u00fa do\u010dasne deaktivovan\u00e9, k\u00fdm oprav\u00edme chybu. [Viac inform\u00e1ci\u00ed]({url})", + "not_hassio": "Hardv\u00e9rov\u00e9 mo\u017enosti je mo\u017en\u00e9 nakonfigurova\u0165 iba v in\u0161tal\u00e1ci\u00e1ch HassOS.", + "zha_migration_failed": "Migr\u00e1cia ZHA nebola \u00faspe\u0161n\u00e1." + }, "error": { "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, + "progress": { + "install_addon": "Po\u010dkajte, k\u00fdm sa dokon\u010d\u00ed in\u0161tal\u00e1cia doplnku Silicon Labs Multiprotocol. M\u00f4\u017ee to trva\u0165 nieko\u013eko min\u00fat.", + "start_addon": "Po\u010dkajte, k\u00fdm sa dokon\u010d\u00ed spustenie doplnku Silicon Labs Multiprotocol. M\u00f4\u017ee to trva\u0165 nieko\u013eko sek\u00fand." + }, "step": { "addon_installed_other_device": { "title": "Podpora viacer\u00fdch protokolov je u\u017e povolen\u00e1 pre in\u00e9 zariadenie" + }, + "addon_not_installed": { + "data": { + "enable_multi_pan": "Povoli\u0165 podporu multiprotocol" + }, + "description": "Ke\u010f je povolen\u00e1 podpora viacer\u00fdch protokolov, r\u00e1dio {hardware_name} IEEE 802.15.4 mo\u017eno pou\u017ei\u0165 s\u00fa\u010dasne pre Zigbee aj vl\u00e1kno (pou\u017e\u00edvan\u00e9 spolo\u010dnos\u0165ou Matter). Ak u\u017e r\u00e1dio pou\u017e\u00edva integr\u00e1cia ZHA Zigbee, ZHA sa prekonfiguruje na pou\u017e\u00edvanie multiprotokolov\u00e9ho firmv\u00e9ru. \n\n Pozn\u00e1mka: Toto je experiment\u00e1lna funkcia.", + "title": "Povo\u013ete podporu viacer\u00fdch protokolov na r\u00e1diu IEEE 802.15.4" + }, + "install_addon": { + "title": "In\u0161tal\u00e1cia doplnku Silicon Labs Multiprotocol sa za\u010dala" + }, + "show_revert_guide": { + "description": "Ak chcete zmeni\u0165 firmv\u00e9r iba na Zigbee, vykonajte nasleduj\u00face manu\u00e1lne kroky: \n\n * Odstr\u00e1\u0148te doplnok Silicon Labs Multiprotocol \n\n * Flashujte iba firmv\u00e9r Zigbee, postupujte pod\u013ea n\u00e1vodu na https://github.com/NabuCasa/silabs-firmware/wiki/Flash-Silicon-Labs-radio-firmware-manually. \n\n * Prekonfigurujte ZHA na migr\u00e1ciu nastaven\u00ed do preflashovan\u00e9ho r\u00e1dia", + "title": "Pre toto zariadenie je povolen\u00e1 podpora multiprotocol" + }, + "start_addon": { + "title": "Sp\u00fa\u0161\u0165a sa doplnok Silicon Labs Multiprotocol." } } } diff --git a/homeassistant/components/homeassistant_yellow/translations/zh-Hant.json b/homeassistant/components/homeassistant_yellow/translations/zh-Hant.json index bab420862dc..ac239820820 100644 --- a/homeassistant/components/homeassistant_yellow/translations/zh-Hant.json +++ b/homeassistant/components/homeassistant_yellow/translations/zh-Hant.json @@ -5,6 +5,7 @@ "addon_install_failed": "\u5b89\u88dd Silicon Labs Multiprotocol \u9644\u52a0\u5143\u4ef6\u5931\u6557\u3002", "addon_set_config_failed": "\u8a2d\u5b9a Silicon Labs Multiprotocol \u9644\u52a0\u5143\u4ef6\u5931\u6557\u3002", "addon_start_failed": "\u555f\u52d5 Silicon Labs Multiprotocol \u9644\u52a0\u5143\u4ef6\u5931\u6557\u3002", + "disabled_due_to_bug": "\u7531\u65bc\u6b63\u5728\u4fee\u6b63\u932f\u8aa4\u3001\u786c\u9ad4\u9078\u9805\u66ab\u6642\u95dc\u9589\u3002 [\u4e86\u89e3\u66f4\u591a]({url})", "not_hassio": "\u786c\u9ad4\u9078\u9805\u50c5\u80fd\u65bc HassOS \u5b89\u88dd\u6a21\u5f0f\u9032\u884c\u8a2d\u5b9a\u3002", "zha_migration_failed": "ZHA \u9077\u79fb\u672a\u6210\u529f\u3002" }, @@ -23,7 +24,7 @@ "data": { "enable_multi_pan": "\u555f\u7528 Multiprotocol \u652f\u63f4" }, - "description": "\u7576\u555f\u7528 Multiprotocol \u652f\u63f4\u6642\u3001Home Assistant Yellow \u7684 IEEE 802.15.4 radio \u53ef\u540c\u6642\u4f5c\u70ba Zigbee \u8207 Thread \uff08\u7528\u65bc Matter\uff09\u4f7f\u7528\u3002\u5047\u5982 Radio \u5df2\u7d93\u88ab ZHA Zigbee \u6574\u5408\u6240\u4f7f\u7528\u3001ZHA \u5c07\u6703\u9032\u884c\u91cd\u65b0\u8a2d\u5b9a\u4ee5\u4f7f\u7528 Multiprotocol \u97cc\u9ad4\u3002\n\n\u6ce8\u610f\uff1a\u76ee\u524d\u70ba\u5be6\u9a57\u6027\u529f\u80fd\u3002", + "description": "\u7576\u555f\u7528 Multiprotocol \u652f\u63f4\u6642\u3001{hardware_name} \u7684 IEEE 802.15.4 radio \u53ef\u540c\u6642\u4f5c\u70ba Zigbee \u8207 Thread \uff08\u7528\u65bc Matter\uff09\u4f7f\u7528\u3002\u5047\u5982 Radio \u5df2\u7d93\u88ab ZHA Zigbee \u6574\u5408\u6240\u4f7f\u7528\u3001ZHA \u5c07\u6703\u9032\u884c\u91cd\u65b0\u8a2d\u5b9a\u4ee5\u4f7f\u7528 Multiprotocol \u97cc\u9ad4\u3002\n\n\u6ce8\u610f\uff1a\u76ee\u524d\u70ba\u5be6\u9a57\u6027\u529f\u80fd\u3002", "title": "\u65bc IEEE 802.15.4 radio \u4e0a\u555f\u7528 Multiprotocol \u652f\u63f4" }, "install_addon": { diff --git a/homeassistant/components/homekit/__init__.py b/homeassistant/components/homekit/__init__.py index 1129e8c3f66..c9faa2e28e0 100644 --- a/homeassistant/components/homekit/__init__.py +++ b/homeassistant/components/homekit/__init__.py @@ -653,10 +653,12 @@ class HomeKit: if self._exclude_accessory_mode: return None _LOGGER.warning( - "The bridge %s has entity %s. For best performance, " - "and to prevent unexpected unavailability, create and " - "pair a separate HomeKit instance in accessory mode for " - "this entity", + ( + "The bridge %s has entity %s. For best performance, " + "and to prevent unexpected unavailability, create and " + "pair a separate HomeKit instance in accessory mode for " + "this entity" + ), self._name, state.entity_id, ) @@ -685,7 +687,10 @@ class HomeKit: assert self.bridge is not None if len(self.bridge.accessories) + 1 >= MAX_DEVICES: _LOGGER.warning( - "Cannot add %s as this would exceed the %d device limit. Consider using the filter option", + ( + "Cannot add %s as this would exceed the %d device limit. Consider" + " using the filter option" + ), name, MAX_DEVICES, ) @@ -920,7 +925,10 @@ class HomeKit: for device_id in self._devices: if not dev_reg.async_get(device_id): _LOGGER.warning( - "HomeKit %s cannot add device %s because it is missing from the device registry", + ( + "HomeKit %s cannot add device %s because it is missing from the" + " device registry" + ), self._name, device_id, ) @@ -941,7 +949,10 @@ class HomeKit: await async_validate_trigger_config(self.hass, trigger) except vol.Invalid as ex: _LOGGER.debug( - "%s: cannot add unsupported trigger %s because it requires additional inputs which are not supported by HomeKit: %s", + ( + "%s: cannot add unsupported trigger %s because it requires" + " additional inputs which are not supported by HomeKit: %s" + ), self._name, trigger, ex, diff --git a/homeassistant/components/homekit/accessories.py b/homeassistant/components/homekit/accessories.py index eef43892677..adab539fb30 100644 --- a/homeassistant/components/homekit/accessories.py +++ b/homeassistant/components/homekit/accessories.py @@ -35,8 +35,7 @@ from homeassistant.const import ( PERCENTAGE, STATE_ON, STATE_UNAVAILABLE, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, + UnitOfTemperature, __version__, ) from homeassistant.core import ( @@ -115,8 +114,10 @@ def get_accessory( # noqa: C901 """Take state and return an accessory object if supported.""" if not aid: _LOGGER.warning( - 'The entity "%s" is not supported, since it ' - "generates an invalid aid, please change it", + ( + 'The entity "%s" is not supported, since it ' + "generates an invalid aid, please change it" + ), state.entity_id, ) return None @@ -184,8 +185,8 @@ def get_accessory( # noqa: C901 unit = state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) if device_class == SensorDeviceClass.TEMPERATURE or unit in ( - TEMP_CELSIUS, - TEMP_FAHRENHEIT, + UnitOfTemperature.CELSIUS, + UnitOfTemperature.FAHRENHEIT, ): a_type = "TemperatureSensor" elif device_class == SensorDeviceClass.HUMIDITY and unit == PERCENTAGE: @@ -213,7 +214,7 @@ def get_accessory( # noqa: C901 a_type = "CarbonMonoxideSensor" elif device_class == SensorDeviceClass.CO2 or "co2" in state.entity_id: a_type = "CarbonDioxideSensor" - elif device_class == SensorDeviceClass.ILLUMINANCE or unit in ("lm", LIGHT_LUX): + elif device_class == SensorDeviceClass.ILLUMINANCE or unit == LIGHT_LUX: a_type = "LightSensor" elif state.domain == "switch": @@ -667,6 +668,7 @@ class HomeIIDManager(IIDManager): # type: ignore[misc] ) if iid in self.objs: raise RuntimeError( - f"Cannot assign IID {iid} to {obj} as it is already in use by: {self.objs[iid]}" + f"Cannot assign IID {iid} to {obj} as it is already in use by:" + f" {self.objs[iid]}" ) return iid diff --git a/homeassistant/components/homekit/config_flow.py b/homeassistant/components/homekit/config_flow.py index 5f142fdb0fe..e1d107065a4 100644 --- a/homeassistant/components/homekit/config_flow.py +++ b/homeassistant/components/homekit/config_flow.py @@ -667,7 +667,9 @@ def _async_get_matching_entities( """Fetch all entities or entities in the given domains.""" ent_reg = entity_registry.async_get(hass) return { - state.entity_id: f"{state.attributes.get(ATTR_FRIENDLY_NAME, state.entity_id)} ({state.entity_id})" + state.entity_id: ( + f"{state.attributes.get(ATTR_FRIENDLY_NAME, state.entity_id)} ({state.entity_id})" + ) for state in sorted( hass.states.async_all(domains and set(domains)), key=lambda item: item.entity_id, diff --git a/homeassistant/components/homekit/logbook.py b/homeassistant/components/homekit/logbook.py index 17cdac51799..e71695883a8 100644 --- a/homeassistant/components/homekit/logbook.py +++ b/homeassistant/components/homekit/logbook.py @@ -28,7 +28,10 @@ def async_describe_events( value = data.get(ATTR_VALUE) value_msg = f" to {value}" if value else "" - message = f"send command {data[ATTR_SERVICE]}{value_msg} for {data[ATTR_DISPLAY_NAME]}" + message = ( + f"send command {data[ATTR_SERVICE]}{value_msg} for" + f" {data[ATTR_DISPLAY_NAME]}" + ) return { LOGBOOK_ENTRY_NAME: "HomeKit", diff --git a/homeassistant/components/homekit/manifest.json b/homeassistant/components/homekit/manifest.json index 187eaf4c869..857f832e283 100644 --- a/homeassistant/components/homekit/manifest.json +++ b/homeassistant/components/homekit/manifest.json @@ -3,7 +3,7 @@ "name": "HomeKit", "documentation": "https://www.home-assistant.io/integrations/homekit", "requirements": [ - "ha-HAP-python==4.5.2", + "HAP-python==4.6.0", "fnvhash==0.1.0", "PyQRCode==1.2.1", "base36==0.1.1" diff --git a/homeassistant/components/homekit/strings.json b/homeassistant/components/homekit/strings.json index 2c0fad0290e..d041f8e0551 100644 --- a/homeassistant/components/homekit/strings.json +++ b/homeassistant/components/homekit/strings.json @@ -62,7 +62,7 @@ }, "pairing": { "title": "Pair HomeKit", - "description": "To complete pairing following the instructions in \u201cNotifications\u201d under \u201cHomeKit Pairing\u201d." + "description": "To complete pairing follow the instructions in \u201cNotifications\u201d under \u201cHomeKit Pairing\u201d." } }, "abort": { diff --git a/homeassistant/components/homekit/translations/ca.json b/homeassistant/components/homekit/translations/ca.json index 16843fa1741..819606cfbb8 100644 --- a/homeassistant/components/homekit/translations/ca.json +++ b/homeassistant/components/homekit/translations/ca.json @@ -5,7 +5,7 @@ }, "step": { "pairing": { - "description": "Per completar la vinculaci\u00f3, segueix les instruccions a \"Configuraci\u00f3 de l'enlla\u00e7 HomeKit\" sota \"Notificacions\".", + "description": "Per completar la vinculaci\u00f3, segueix les instruccions a \u201cNotificacions\u201d sota \u201cVinculaci\u00f3 ('pairing') HomeKit\u201d.", "title": "Vinculaci\u00f3 HomeKit" }, "user": { diff --git a/homeassistant/components/homekit/translations/en.json b/homeassistant/components/homekit/translations/en.json index f0c26868e7d..0ec23f9389d 100644 --- a/homeassistant/components/homekit/translations/en.json +++ b/homeassistant/components/homekit/translations/en.json @@ -5,7 +5,7 @@ }, "step": { "pairing": { - "description": "To complete pairing following the instructions in \u201cNotifications\u201d under \u201cHomeKit Pairing\u201d.", + "description": "To complete pairing follow the instructions in \u201cNotifications\u201d under \u201cHomeKit Pairing\u201d.", "title": "Pair HomeKit" }, "user": { diff --git a/homeassistant/components/homekit/translations/es.json b/homeassistant/components/homekit/translations/es.json index f0b6a1ecb6d..913a5d2195f 100644 --- a/homeassistant/components/homekit/translations/es.json +++ b/homeassistant/components/homekit/translations/es.json @@ -5,7 +5,7 @@ }, "step": { "pairing": { - "description": "Para completar el emparejamiento, sigue las instrucciones en \"Notificaciones\" bajo \"Emparejamiento HomeKit\".", + "description": "Para completar el emparejamiento, sigue las instrucciones en \"Notificaciones\" bajo \"Emparejamiento de HomeKit\".", "title": "Emparejar HomeKit" }, "user": { diff --git a/homeassistant/components/homekit/translations/et.json b/homeassistant/components/homekit/translations/et.json index 350812600e7..dad5812d92c 100644 --- a/homeassistant/components/homekit/translations/et.json +++ b/homeassistant/components/homekit/translations/et.json @@ -5,7 +5,7 @@ }, "step": { "pairing": { - "description": "Sidumise l\u00f5puleviimiseks j\u00e4rgi jaotises \"HomeKiti sidumine\" toodud juhiseid alajaotises \"Teatised\".", + "description": "Sidumise l\u00f5puleviimiseks j\u00e4rgi jaotises \"HomeKiti sidumine\" jaotises \"Teatised\" olevaid juhiseid.", "title": "HomeKiti sidumine" }, "user": { diff --git a/homeassistant/components/homekit/translations/id.json b/homeassistant/components/homekit/translations/id.json index eca3dcdd173..57f49eb19d1 100644 --- a/homeassistant/components/homekit/translations/id.json +++ b/homeassistant/components/homekit/translations/id.json @@ -5,7 +5,7 @@ }, "step": { "pairing": { - "description": "Untuk menyelesaikan pemasangan ikuti petunjuk di \"Notifikasi\" di bawah \"Pemasangan HomeKit\".", + "description": "Untuk menyelesaikan pemasangan, ikuti petunjuk di \"Notifikasi\" di bawah \"Pemasangan HomeKit\".", "title": "Pasangkan HomeKit" }, "user": { @@ -51,7 +51,7 @@ "data": { "entities": "Entitas" }, - "description": "Semua entitas \"{domains}\" akan disertakan kecuali entitas tertentu dipilih.", + "description": "Hanya entitas \"{domains}\" yang dipilih yang akan disertakan.", "title": "Pilih entitas yang akan disertakan" }, "init": { diff --git a/homeassistant/components/homekit/translations/no.json b/homeassistant/components/homekit/translations/no.json index 463b52bc27b..3eee793460b 100644 --- a/homeassistant/components/homekit/translations/no.json +++ b/homeassistant/components/homekit/translations/no.json @@ -5,7 +5,7 @@ }, "step": { "pairing": { - "description": "For \u00e5 fullf\u00f8re sammenkoblingen ved \u00e5 f\u00f8lge instruksjonene i \"Varsler\" under \"Sammenkobling av HomeKit\".", + "description": "For \u00e5 fullf\u00f8re sammenkoblingen, f\u00f8lg instruksjonene i \"Varsler\" under \"HomeKit-paring\".", "title": "Koble sammen HomeKit" }, "user": { diff --git a/homeassistant/components/homekit/translations/pt-BR.json b/homeassistant/components/homekit/translations/pt-BR.json index 5296facd831..485297e21f6 100644 --- a/homeassistant/components/homekit/translations/pt-BR.json +++ b/homeassistant/components/homekit/translations/pt-BR.json @@ -5,7 +5,7 @@ }, "step": { "pairing": { - "description": "Para concluir o emparelhamento, siga as instru\u00e7\u00f5es em \"Emparelhamento do HomeKit\" > \"Notifica\u00e7\u00f5es\".", + "description": "Para concluir o emparelhamento, siga as instru\u00e7\u00f5es em \u201cNotifica\u00e7\u00f5es\u201d em \u201cEmparelhamento do HomeKit\u201d.", "title": "Emparelhar HomeKit" }, "user": { diff --git a/homeassistant/components/homekit/translations/pt.json b/homeassistant/components/homekit/translations/pt.json index 931dfcdd1c9..d48d790ba08 100644 --- a/homeassistant/components/homekit/translations/pt.json +++ b/homeassistant/components/homekit/translations/pt.json @@ -28,7 +28,7 @@ "init": { "data": { "domains": "Dom\u00ednios a incluir", - "mode": "Modo" + "mode": "Modo HomeKit" }, "title": "Selecione os dom\u00ednios a serem expostos." }, diff --git a/homeassistant/components/homekit/translations/sk.json b/homeassistant/components/homekit/translations/sk.json index 4469563f2f3..4c16887b508 100644 --- a/homeassistant/components/homekit/translations/sk.json +++ b/homeassistant/components/homekit/translations/sk.json @@ -1,8 +1,19 @@ { "config": { + "abort": { + "port_name_in_use": "Pr\u00edslu\u0161enstvo alebo bridge s rovnak\u00fdm n\u00e1zvom alebo portom je u\u017e nakonfigurovan\u00fd." + }, "step": { "pairing": { - "description": "Na dokon\u010denie p\u00e1rovania postupujte pod\u013ea pokynov v \u010dasti \u201eUpozornenia\u201c v \u010dasti \u201eP\u00e1rovanie HomeKit\u201c." + "description": "Na dokon\u010denie p\u00e1rovania postupujte pod\u013ea pokynov v \u010dasti \u201eUpozornenia\u201c v \u010dasti \u201eP\u00e1rovanie HomeKit\u201c.", + "title": "P\u00e1rovanie HomeKit" + }, + "user": { + "data": { + "include_domains": "Dom\u00e9ny, ktor\u00e9 sa maj\u00fa zahrn\u00fa\u0165" + }, + "description": "Vyberte dom\u00e9ny, ktor\u00e9 chcete zahrn\u00fa\u0165. V\u0161etky podporovan\u00e9 entity v dom\u00e9ne bud\u00fa zahrnut\u00e9 okrem kategorizovan\u00fdch ent\u00edt. Pre ka\u017ed\u00fd telev\u00edzny prehr\u00e1va\u010d m\u00e9di\u00ed, dia\u013ekov\u00e9 ovl\u00e1danie zalo\u017een\u00e9 na aktivite, z\u00e1mok a fotoapar\u00e1t sa vytvor\u00ed samostatn\u00e1 in\u0161tancia HomeKit v re\u017eime pr\u00edslu\u0161enstva.", + "title": "Vyberte dom\u00e9ny, ktor\u00e9 chcete zahrn\u00fa\u0165" } } }, @@ -11,23 +22,50 @@ "accessory": { "data": { "entities": "Entita" - } + }, + "title": "Vyberte entitu pre pr\u00edslu\u0161enstvo" }, "advanced": { + "data": { + "devices": "Zariadenia (sp\u00fa\u0161\u0165a\u010de)" + }, + "description": "Pre ka\u017ed\u00e9 vybran\u00e9 zariadenie s\u00fa vytvoren\u00e9 programovate\u013en\u00e9 sp\u00edna\u010de. Ke\u010f sa spust\u00ed sp\u00fa\u0161\u0165a\u010d zariadenia, HomeKit mo\u017eno nakonfigurova\u0165 na spustenie automatiz\u00e1cie alebo sc\u00e9ny.", "title": "Roz\u0161\u00edren\u00e1 konfigur\u00e1cia" }, + "cameras": { + "data": { + "camera_audio": "Kamery s podporou zvuku", + "camera_copy": "Kamery, ktor\u00e9 podporuj\u00fa nat\u00edvne streamy H.264" + }, + "description": "Skontrolujte v\u0161etky kamery, ktor\u00e9 podporuj\u00fa nat\u00edvne streamy H.264. Ak kamera nevysiela tok H.264, syst\u00e9m prek\u00f3duje video do H.264 pre HomeKit. Prek\u00f3dovanie vy\u017eaduje v\u00fdkonn\u00fd procesor a je nepravdepodobn\u00e9, \u017ee bude fungova\u0165 na po\u010d\u00edta\u010doch s jednou doskou.", + "title": "Konfigur\u00e1cia kamery" + }, "exclude": { "data": { "entities": "Entity" - } + }, + "description": "V\u0161etky entity \u201e{domains}\u201c bud\u00fa zahrnut\u00e9 okrem vyl\u00fa\u010den\u00fdch ent\u00edt a kategorizovan\u00fdch ent\u00edt.", + "title": "Vyberte entity, ktor\u00e9 chcete vyl\u00fa\u010di\u0165" }, "include": { "data": { "entities": "Entity" - } + }, + "description": "V\u0161etky entity \u201e{domains}\u201c bud\u00fa zahrnut\u00e9, pokia\u013e nevyberiete konkr\u00e9tne entity.", + "title": "Vyberte entity, ktor\u00e9 chcete zahrn\u00fa\u0165" + }, + "init": { + "data": { + "domains": "Dom\u00e9ny, ktor\u00e9 sa maj\u00fa zahrn\u00fa\u0165", + "include_exclude_mode": "Re\u017eim za\u010dlenenia", + "mode": "Re\u017eim HomeKit" + }, + "description": "HomeKit je mo\u017en\u00e9 nakonfigurova\u0165 tak, aby vystavil bridge alebo jedno pr\u00edslu\u0161enstvo. V re\u017eime pr\u00edslu\u0161enstva je mo\u017en\u00e9 pou\u017ei\u0165 iba jednu entitu. Pre spr\u00e1vne fungovanie prehr\u00e1va\u010dov m\u00e9di\u00ed s triedou telev\u00edznych zariaden\u00ed sa vy\u017eaduje re\u017eim pr\u00edslu\u0161enstva. Subjekty v \u010dasti \u201eDom\u00e9ny na zahrnutie\u201c bud\u00fa zahrnut\u00e9 do HomeKitu. Na nasleduj\u00facej obrazovke si budete m\u00f4c\u0165 vybra\u0165, ktor\u00e9 entity chcete zahrn\u00fa\u0165 alebo vyl\u00fa\u010di\u0165 z tohto zoznamu.", + "title": "Vyberte re\u017eim a dom\u00e9ny." }, "yaml": { - "description": "T\u00e1to polo\u017eka sa ovl\u00e1da pomocou YAML" + "description": "T\u00e1to polo\u017eka sa ovl\u00e1da pomocou YAML", + "title": "Upravte mo\u017enosti HomeKit" } } } diff --git a/homeassistant/components/homekit/type_cameras.py b/homeassistant/components/homekit/type_cameras.py index e612c8248be..2bb6272aaeb 100644 --- a/homeassistant/components/homekit/type_cameras.py +++ b/homeassistant/components/homekit/type_cameras.py @@ -325,7 +325,8 @@ class Camera(HomeAccessory, PyhapCamera): ) except Exception: # pylint: disable=broad-except _LOGGER.exception( - "Failed to get stream source - this could be a transient error or your camera might not be compatible with HomeKit yet" + "Failed to get stream source - this could be a transient error or your" + " camera might not be compatible with HomeKit yet" ) return stream_source diff --git a/homeassistant/components/homekit/type_covers.py b/homeassistant/components/homekit/type_covers.py index 6db9b081bf1..4b21bfb77df 100644 --- a/homeassistant/components/homekit/type_covers.py +++ b/homeassistant/components/homekit/type_covers.py @@ -283,7 +283,10 @@ class OpeningDevice(OpeningDeviceBase, HomeAccessory): # since CHAR_CURRENT_POSITION/CHAR_TARGET_POSITION are required # by homekit, but really don't exist. _LOGGER.debug( - "%s does not support setting position, current position will be locked to closed", + ( + "%s does not support setting position, current position will be" + " locked to closed" + ), self.entity_id, ) target_args["properties"] = {PROP_MIN_VALUE: 0, PROP_MAX_VALUE: 0} diff --git a/homeassistant/components/homekit/type_humidifiers.py b/homeassistant/components/homekit/type_humidifiers.py index 0babb285bed..b585f38e981 100644 --- a/homeassistant/components/homekit/type_humidifiers.py +++ b/homeassistant/components/homekit/type_humidifiers.py @@ -174,7 +174,10 @@ class HumidifierDehumidifier(HomeAccessory): """Handle linked humidity sensor state change to update HomeKit value.""" if new_state is None: _LOGGER.error( - "%s: Unable to update from linked humidity sensor %s: the entity state is None", + ( + "%s: Unable to update from linked humidity sensor %s: the entity" + " state is None" + ), self.entity_id, self.linked_humidity_sensor, ) @@ -221,8 +224,10 @@ class HumidifierDehumidifier(HomeAccessory): DOMAIN, SERVICE_SET_HUMIDITY, {ATTR_ENTITY_ID: self.entity_id, ATTR_HUMIDITY: humidity}, - f"{self._target_humidity_char_name} to " - f"{char_values[self._target_humidity_char_name]}{PERCENTAGE}", + ( + f"{self._target_humidity_char_name} to " + f"{char_values[self._target_humidity_char_name]}{PERCENTAGE}" + ), ) @callback diff --git a/homeassistant/components/homekit/type_lights.py b/homeassistant/components/homekit/type_lights.py index 65c0368f6e6..83ce1c3f6cf 100644 --- a/homeassistant/components/homekit/type_lights.py +++ b/homeassistant/components/homekit/type_lights.py @@ -249,7 +249,7 @@ class Light(HomeAccessory): # But if it is set to 0, HomeKit will update the brightness to 100 as # it thinks 0 is off. # - # Therefore, if the the brightness is 0 and the device is still on, + # Therefore, if the brightness is 0 and the device is still on, # the brightness is mapped to 1 otherwise the update is ignored in # order to avoid this incorrect behavior. if brightness == 0 and state == STATE_ON: diff --git a/homeassistant/components/homekit/type_remotes.py b/homeassistant/components/homekit/type_remotes.py index 9cc90a6cc28..1dfcb0f91a3 100644 --- a/homeassistant/components/homekit/type_remotes.py +++ b/homeassistant/components/homekit/type_remotes.py @@ -1,5 +1,5 @@ """Class to hold remote accessories.""" -from abc import abstractmethod +from abc import ABC, abstractmethod import logging from pyhap.const import CATEGORY_TELEVISION @@ -75,7 +75,7 @@ REMOTE_KEYS = { } -class RemoteInputSelectAccessory(HomeAccessory): +class RemoteInputSelectAccessory(HomeAccessory, ABC): """Generate a InputSelect accessory.""" def __init__( diff --git a/homeassistant/components/homekit/type_sensors.py b/homeassistant/components/homekit/type_sensors.py index 4e9c897dff9..27a1156111f 100644 --- a/homeassistant/components/homekit/type_sensors.py +++ b/homeassistant/components/homekit/type_sensors.py @@ -13,7 +13,7 @@ from homeassistant.const import ( ATTR_UNIT_OF_MEASUREMENT, STATE_HOME, STATE_ON, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import callback @@ -124,7 +124,9 @@ class TemperatureSensor(HomeAccessory): @callback def async_update_state(self, new_state): """Update temperature after state changed.""" - unit = new_state.attributes.get(ATTR_UNIT_OF_MEASUREMENT, TEMP_CELSIUS) + unit = new_state.attributes.get( + ATTR_UNIT_OF_MEASUREMENT, UnitOfTemperature.CELSIUS + ) if (temperature := convert_to_float(new_state.state)) is not None: temperature = temperature_to_homekit(temperature, unit) self.char_temp.set_value(temperature) diff --git a/homeassistant/components/homekit/type_thermostats.py b/homeassistant/components/homekit/type_thermostats.py index a924548816b..24a137e4957 100644 --- a/homeassistant/components/homekit/type_thermostats.py +++ b/homeassistant/components/homekit/type_thermostats.py @@ -53,8 +53,7 @@ from homeassistant.const import ( ATTR_SUPPORTED_FEATURES, ATTR_TEMPERATURE, PERCENTAGE, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, + UnitOfTemperature, ) from homeassistant.core import State, callback from homeassistant.util.percentage import ( @@ -98,7 +97,7 @@ DEFAULT_HVAC_MODES = [ ] HC_HOMEKIT_VALID_MODES_WATER_HEATER = {"Heat": 1} -UNIT_HASS_TO_HOMEKIT = {TEMP_CELSIUS: 0, TEMP_FAHRENHEIT: 1} +UNIT_HASS_TO_HOMEKIT = {UnitOfTemperature.CELSIUS: 0, UnitOfTemperature.FAHRENHEIT: 1} HC_HEAT_COOL_OFF = 0 HC_HEAT_COOL_HEAT = 1 @@ -405,7 +404,7 @@ class Thermostat(HomeAccessory): if target_hc not in self.hc_homekit_to_hass: # If the target heating cooling state we want does not # exist on the device, we have to sort it out - # based on the the current and target temperature since + # based on the current and target temperature since # siri will always send HC_HEAT_COOL_AUTO in this case # and hope for the best. hc_target_temp = char_values.get(CHAR_TARGET_TEMPERATURE) @@ -420,7 +419,10 @@ class Thermostat(HomeAccessory): for hc_fallback in hc_fallback_order: if hc_fallback in self.hc_homekit_to_hass: _LOGGER.debug( - "Siri requested target mode: %s and the device does not support, falling back to %s", + ( + "Siri requested target mode: %s and the device does not" + " support, falling back to %s" + ), target_hc, hc_fallback, ) @@ -429,7 +431,8 @@ class Thermostat(HomeAccessory): params[ATTR_HVAC_MODE] = self.hc_homekit_to_hass[target_hc] events.append( - f"{CHAR_TARGET_HEATING_COOLING} to {char_values[CHAR_TARGET_HEATING_COOLING]}" + f"{CHAR_TARGET_HEATING_COOLING} to" + f" {char_values[CHAR_TARGET_HEATING_COOLING]}" ) # Many integrations do not actually implement `hvac_mode` for the # `SERVICE_SET_TEMPERATURE_THERMOSTAT` service so we made a call to @@ -448,7 +451,8 @@ class Thermostat(HomeAccessory): service = SERVICE_SET_TEMPERATURE_THERMOSTAT temperature = self._temperature_to_states(hc_target_temp) events.append( - f"{CHAR_TARGET_TEMPERATURE} to {char_values[CHAR_TARGET_TEMPERATURE]}°C" + f"{CHAR_TARGET_TEMPERATURE} to" + f" {char_values[CHAR_TARGET_TEMPERATURE]}°C" ) params[ATTR_TEMPERATURE] = temperature elif features & ClimateEntityFeature.TARGET_TEMPERATURE_RANGE: @@ -479,7 +483,8 @@ class Thermostat(HomeAccessory): min_temp, max_temp = self.get_temperature_range() if CHAR_COOLING_THRESHOLD_TEMPERATURE in char_values: events.append( - f"{CHAR_COOLING_THRESHOLD_TEMPERATURE} to {char_values[CHAR_COOLING_THRESHOLD_TEMPERATURE]}°C" + f"{CHAR_COOLING_THRESHOLD_TEMPERATURE} to" + f" {char_values[CHAR_COOLING_THRESHOLD_TEMPERATURE]}°C" ) high = char_values[CHAR_COOLING_THRESHOLD_TEMPERATURE] # If the device doesn't support TARGET_TEMPATURE @@ -488,7 +493,8 @@ class Thermostat(HomeAccessory): low = high - HEAT_COOL_DEADBAND if CHAR_HEATING_THRESHOLD_TEMPERATURE in char_values: events.append( - f"{CHAR_HEATING_THRESHOLD_TEMPERATURE} to {char_values[CHAR_HEATING_THRESHOLD_TEMPERATURE]}°C" + f"{CHAR_HEATING_THRESHOLD_TEMPERATURE} to" + f" {char_values[CHAR_HEATING_THRESHOLD_TEMPERATURE]}°C" ) low = char_values[CHAR_HEATING_THRESHOLD_TEMPERATURE] # If the device doesn't support TARGET_TEMPATURE @@ -599,7 +605,10 @@ class Thermostat(HomeAccessory): self.char_target_heat_cool.set_value(homekit_hvac_mode) else: _LOGGER.error( - "Cannot map hvac target mode: %s to homekit as only %s modes are supported", + ( + "Cannot map hvac target mode: %s to homekit as only %s modes" + " are supported" + ), hvac_mode, self.hc_homekit_to_hass, ) diff --git a/homeassistant/components/homekit/util.py b/homeassistant/components/homekit/util.py index 413786c22c4..53fd9fbfed7 100644 --- a/homeassistant/components/homekit/util.py +++ b/homeassistant/components/homekit/util.py @@ -35,7 +35,7 @@ from homeassistant.const import ( CONF_NAME, CONF_PORT, CONF_TYPE, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import Event, HomeAssistant, State, callback, split_entity_id import homeassistant.helpers.config_validation as cv @@ -349,7 +349,7 @@ def async_show_setup_message( message = ( f"To set up {bridge_name} in the Home App, " - f"scan the QR code or enter the following code:\n" + "scan the QR code or enter the following code:\n" f"### {pin}\n" f"![image](/api/homekit/pairingqr?{entry_id}-{pairing_secret})" ) @@ -391,12 +391,20 @@ def cleanup_name_for_homekit(name: str | None) -> str: def temperature_to_homekit(temperature: float | int, unit: str) -> float: """Convert temperature to Celsius for HomeKit.""" - return round(TemperatureConverter.convert(temperature, unit, TEMP_CELSIUS), 1) + return round( + TemperatureConverter.convert(temperature, unit, UnitOfTemperature.CELSIUS), 1 + ) def temperature_to_states(temperature: float | int, unit: str) -> float: """Convert temperature back from Celsius to Home Assistant unit.""" - return round(TemperatureConverter.convert(temperature, TEMP_CELSIUS, unit) * 2) / 2 + return ( + round( + TemperatureConverter.convert(temperature, UnitOfTemperature.CELSIUS, unit) + * 2 + ) + / 2 + ) def density_to_air_quality(density: float) -> int: diff --git a/homeassistant/components/homekit_controller/__init__.py b/homeassistant/components/homekit_controller/__init__.py index aa56bbbda78..ecd8113a2bb 100644 --- a/homeassistant/components/homekit_controller/__init__.py +++ b/homeassistant/components/homekit_controller/__init__.py @@ -97,9 +97,11 @@ async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None: await controller.remove_pairing(hkid) except aiohomekit.AccessoryDisconnectedError: _LOGGER.warning( - "Accessory %s was removed from HomeAssistant but was not reachable " - "to properly unpair. It may need resetting before you can use it with " - "HomeKit again", + ( + "Accessory %s was removed from HomeAssistant but was not reachable " + "to properly unpair. It may need resetting before you can use it with " + "HomeKit again" + ), entry.title, ) diff --git a/homeassistant/components/homekit_controller/climate.py b/homeassistant/components/homekit_controller/climate.py index 2dc998200a4..df43d8929e9 100644 --- a/homeassistant/components/homekit_controller/climate.py +++ b/homeassistant/components/homekit_controller/climate.py @@ -32,7 +32,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS, Platform +from homeassistant.const import ATTR_TEMPERATURE, Platform, UnitOfTemperature from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -114,7 +114,7 @@ async def async_setup_entry( class HomeKitBaseClimateEntity(HomeKitEntity, ClimateEntity): """The base HomeKit Controller climate entity.""" - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS def get_characteristic_types(self) -> list[str]: """Define the homekit characteristics the entity cares about.""" @@ -187,8 +187,11 @@ class HomeKitHeaterCoolerEntity(HomeKitBaseClimateEntity): else: hvac_mode = TARGET_HEATER_COOLER_STATE_HOMEKIT_TO_HASS.get(state) _LOGGER.warning( - "HomeKit device %s: Setting temperature in %s mode is not supported yet;" - " Consider raising a ticket if you have this device and want to help us implement this feature", + ( + "HomeKit device %s: Setting temperature in %s mode is not supported" + " yet; Consider raising a ticket if you have this device and want" + " to help us implement this feature" + ), self.entity_id, hvac_mode, ) @@ -202,8 +205,11 @@ class HomeKitHeaterCoolerEntity(HomeKitBaseClimateEntity): return if hvac_mode not in {HVACMode.HEAT, HVACMode.COOL}: _LOGGER.warning( - "HomeKit device %s: Setting temperature in %s mode is not supported yet;" - " Consider raising a ticket if you have this device and want to help us implement this feature", + ( + "HomeKit device %s: Setting temperature in %s mode is not supported" + " yet; Consider raising a ticket if you have this device and want" + " to help us implement this feature" + ), self.entity_id, hvac_mode, ) diff --git a/homeassistant/components/homekit_controller/config_flow.py b/homeassistant/components/homekit_controller/config_flow.py index 1cfedb05847..71884f25ef4 100644 --- a/homeassistant/components/homekit_controller/config_flow.py +++ b/homeassistant/components/homekit_controller/config_flow.py @@ -167,7 +167,9 @@ class HomekitControllerFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): { vol.Required("device"): vol.In( { - key: f"{key} ({formatted_category(discovery.description.category)})" + key: ( + f"{key} ({formatted_category(discovery.description.category)})" + ) for key, discovery in self.devices.items() } ) @@ -232,7 +234,10 @@ class HomekitControllerFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): # This can happen if the TXT record is received after the PTR record # we will wait for the next update in this case _LOGGER.debug( - "HomeKit device %s: id not exposed; TXT record may have not yet been received", + ( + "HomeKit device %s: id not exposed; TXT record may have not yet" + " been received" + ), properties, ) return self.async_abort(reason="invalid_properties") @@ -291,7 +296,10 @@ class HomekitControllerFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): await pairing.list_accessories_and_characteristics() except AuthenticationError: _LOGGER.debug( - "%s (%s - %s) is unpaired. Removing invalid pairing for this device", + ( + "%s (%s - %s) is unpaired. Removing invalid pairing for this" + " device" + ), name, model, hkid, @@ -299,9 +307,11 @@ class HomekitControllerFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): await self.hass.config_entries.async_remove(existing.entry_id) else: _LOGGER.debug( - "%s (%s - %s) claims to be unpaired but isn't. " - "It's implementation of HomeKit is defective " - "or a zeroconf relay is broadcasting stale data", + ( + "%s (%s - %s) claims to be unpaired but isn't. " + "It's implementation of HomeKit is defective " + "or a zeroconf relay is broadcasting stale data" + ), name, model, hkid, diff --git a/homeassistant/components/homekit_controller/connection.py b/homeassistant/components/homekit_controller/connection.py index d230fe64517..f0de7307d7c 100644 --- a/homeassistant/components/homekit_controller/connection.py +++ b/homeassistant/components/homekit_controller/connection.py @@ -194,8 +194,10 @@ class HKDevice: await self.pairing.async_populate_accessories_state(force_update=True) except STARTUP_EXCEPTIONS as ex: _LOGGER.debug( - "Failed to populate BLE accessory state for %s, accessory may be sleeping" - " and will be retried the next time it advertises: %s", + ( + "Failed to populate BLE accessory state for %s, accessory may be" + " sleeping and will be retried the next time it advertises: %s" + ), self.config_entry.title, ex, ) @@ -360,7 +362,10 @@ class HKDevice: if self.config_entry.entry_id not in device.config_entries: _LOGGER.info( - "Found candidate device for %s:aid:%s, but owned by a different config entry, skipping", + ( + "Found candidate device for %s:aid:%s, but owned by a different" + " config entry, skipping" + ), self.unique_id, accessory.aid, ) @@ -407,7 +412,10 @@ class HKDevice: platform, DOMAIN, new_unique_id ): _LOGGER.debug( - "Unique ID %s is already in use by %s (system may have been downgraded)", + ( + "Unique ID %s is already in use by %s (system may have been" + " downgraded)" + ), new_unique_id, new_entity_id, ) @@ -429,7 +437,10 @@ class HKDevice: does not require them to be stable. """ _LOGGER.debug( - "Removing legacy serial numbers from device registry entries for pairing %s", + ( + "Removing legacy serial numbers from device registry entries for" + " pairing %s" + ), self.unique_id, ) @@ -491,14 +502,20 @@ class HKDevice: for accessory in self.entity_map.accessories: if not valid_serial_number(accessory.serial_number): _LOGGER.debug( - "Serial number %r is not valid, it cannot be used as a unique identifier", + ( + "Serial number %r is not valid, it cannot be used as a unique" + " identifier" + ), accessory.serial_number, ) unreliable_serial_numbers = True elif accessory.serial_number in devices: _LOGGER.debug( - "Serial number %r is duplicated within this pairing, it cannot be used as a unique identifier", + ( + "Serial number %r is duplicated within this pairing, it cannot" + " be used as a unique identifier" + ), accessory.serial_number, ) unreliable_serial_numbers = True @@ -506,7 +523,10 @@ class HKDevice: elif accessory.serial_number == accessory.hardware_revision: # This is a known bug with some devices (e.g. RYSE SmartShades) _LOGGER.debug( - "Serial number %r is actually the hardware revision, it cannot be used as a unique identifier", + ( + "Serial number %r is actually the hardware revision, it cannot" + " be used as a unique identifier" + ), accessory.serial_number, ) unreliable_serial_numbers = True @@ -670,7 +690,10 @@ class HKDevice: if self._polling_lock.locked(): if not self._polling_lock_warned: _LOGGER.warning( - "HomeKit controller update skipped as previous poll still in flight: %s", + ( + "HomeKit controller update skipped as previous poll still in" + " flight: %s" + ), self.unique_id, ) self._polling_lock_warned = True @@ -678,7 +701,10 @@ class HKDevice: if self._polling_lock_warned: _LOGGER.info( - "HomeKit controller no longer detecting back pressure - not skipping poll: %s", + ( + "HomeKit controller no longer detecting back pressure - not" + " skipping poll: %s" + ), self.unique_id, ) self._polling_lock_warned = False diff --git a/homeassistant/components/homekit_controller/const.py b/homeassistant/components/homekit_controller/const.py index 8b6a3e5fd30..3727fc272ad 100644 --- a/homeassistant/components/homekit_controller/const.py +++ b/homeassistant/components/homekit_controller/const.py @@ -1,6 +1,5 @@ """Constants for the homekit_controller component.""" import asyncio -from typing import Final from aiohomekit.exceptions import ( AccessoryDisconnectedError, @@ -97,10 +96,6 @@ CHARACTERISTIC_PLATFORMS = { CharacteristicsTypes.THREAD_NODE_CAPABILITIES: "sensor", } - -# Device classes -DEVICE_CLASS_ECOBEE_MODE: Final = "homekit_controller__ecobee_mode" - STARTUP_EXCEPTIONS = ( asyncio.TimeoutError, AccessoryNotFoundError, diff --git a/homeassistant/components/homekit_controller/manifest.json b/homeassistant/components/homekit_controller/manifest.json index 47112d3bd50..195e3330c7c 100644 --- a/homeassistant/components/homekit_controller/manifest.json +++ b/homeassistant/components/homekit_controller/manifest.json @@ -3,7 +3,7 @@ "name": "HomeKit Controller", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/homekit_controller", - "requirements": ["aiohomekit==2.4.1"], + "requirements": ["aiohomekit==2.4.3"], "zeroconf": ["_hap._tcp.local.", "_hap._udp.local."], "bluetooth": [{ "manufacturer_id": 76, "manufacturer_data_start": [6] }], "dependencies": ["bluetooth", "zeroconf"], diff --git a/homeassistant/components/homekit_controller/select.py b/homeassistant/components/homekit_controller/select.py index ca5eaec4dc5..76067aea061 100644 --- a/homeassistant/components/homekit_controller/select.py +++ b/homeassistant/components/homekit_controller/select.py @@ -11,7 +11,6 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from . import KNOWN_DEVICES from .connection import HKDevice -from .const import DEVICE_CLASS_ECOBEE_MODE from .entity import CharacteristicEntity _ECOBEE_MODE_TO_TEXT = { @@ -26,7 +25,7 @@ class EcobeeModeSelect(CharacteristicEntity, SelectEntity): """Represents a ecobee mode select entity.""" _attr_options = ["home", "sleep", "away"] - _attr_device_class = DEVICE_CLASS_ECOBEE_MODE + _attr_translation_key = "ecobee_mode" @property def name(self) -> str: diff --git a/homeassistant/components/homekit_controller/sensor.py b/homeassistant/components/homekit_controller/sensor.py index 49047b28eae..5d3e9669705 100644 --- a/homeassistant/components/homekit_controller/sensor.py +++ b/homeassistant/components/homekit_controller/sensor.py @@ -3,7 +3,6 @@ from __future__ import annotations from collections.abc import Callable from dataclasses import dataclass -import logging from aiohomekit.model import Accessory, Transport from aiohomekit.model.characteristics import Characteristic, CharacteristicsTypes @@ -24,17 +23,17 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, CONCENTRATION_PARTS_PER_MILLION, - ELECTRIC_CURRENT_AMPERE, - ELECTRIC_POTENTIAL_VOLT, - ENERGY_KILO_WATT_HOUR, LIGHT_LUX, PERCENTAGE, - POWER_WATT, - PRESSURE_HPA, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, - SOUND_PRESSURE_DB, - TEMP_CELSIUS, Platform, + UnitOfElectricCurrent, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfPower, + UnitOfPressure, + UnitOfSoundPressure, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import EntityCategory @@ -46,8 +45,6 @@ from .connection import HKDevice from .entity import CharacteristicEntity, HomeKitEntity from .utils import folded_name -_LOGGER = logging.getLogger(__name__) - @dataclass class HomeKitSensorEntityDescription(SensorEntityDescription): @@ -149,91 +146,91 @@ SIMPLE_SENSOR: dict[str, HomeKitSensorEntityDescription] = { name="Power", device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, ), CharacteristicsTypes.VENDOR_CONNECTSENSE_ENERGY_AMPS: HomeKitSensorEntityDescription( key=CharacteristicsTypes.VENDOR_CONNECTSENSE_ENERGY_AMPS, name="Current", device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, ), CharacteristicsTypes.VENDOR_CONNECTSENSE_ENERGY_AMPS_20: HomeKitSensorEntityDescription( key=CharacteristicsTypes.VENDOR_CONNECTSENSE_ENERGY_AMPS_20, name="Current", device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, ), CharacteristicsTypes.VENDOR_CONNECTSENSE_ENERGY_KW_HOUR: HomeKitSensorEntityDescription( key=CharacteristicsTypes.VENDOR_CONNECTSENSE_ENERGY_KW_HOUR, name="Energy kWh", device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, ), CharacteristicsTypes.VENDOR_EVE_ENERGY_WATT: HomeKitSensorEntityDescription( key=CharacteristicsTypes.VENDOR_EVE_ENERGY_WATT, name="Power", device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, ), CharacteristicsTypes.VENDOR_EVE_ENERGY_KW_HOUR: HomeKitSensorEntityDescription( key=CharacteristicsTypes.VENDOR_EVE_ENERGY_KW_HOUR, name="Energy kWh", device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, ), CharacteristicsTypes.VENDOR_EVE_ENERGY_VOLTAGE: HomeKitSensorEntityDescription( key=CharacteristicsTypes.VENDOR_EVE_ENERGY_VOLTAGE, name="Volts", device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, ), CharacteristicsTypes.VENDOR_EVE_ENERGY_AMPERE: HomeKitSensorEntityDescription( key=CharacteristicsTypes.VENDOR_EVE_ENERGY_AMPERE, name="Amps", device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, ), CharacteristicsTypes.VENDOR_KOOGEEK_REALTIME_ENERGY: HomeKitSensorEntityDescription( key=CharacteristicsTypes.VENDOR_KOOGEEK_REALTIME_ENERGY, name="Power", device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, ), CharacteristicsTypes.VENDOR_KOOGEEK_REALTIME_ENERGY_2: HomeKitSensorEntityDescription( key=CharacteristicsTypes.VENDOR_KOOGEEK_REALTIME_ENERGY_2, name="Power", device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, ), CharacteristicsTypes.VENDOR_EVE_DEGREE_AIR_PRESSURE: HomeKitSensorEntityDescription( key=CharacteristicsTypes.VENDOR_EVE_DEGREE_AIR_PRESSURE, name="Air Pressure", device_class=SensorDeviceClass.PRESSURE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=PRESSURE_HPA, + native_unit_of_measurement=UnitOfPressure.HPA, ), CharacteristicsTypes.VENDOR_VOCOLINC_OUTLET_ENERGY: HomeKitSensorEntityDescription( key=CharacteristicsTypes.VENDOR_VOCOLINC_OUTLET_ENERGY, name="Power", device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, ), CharacteristicsTypes.TEMPERATURE_CURRENT: HomeKitSensorEntityDescription( key=CharacteristicsTypes.TEMPERATURE_CURRENT, name="Current Temperature", device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, # This sensor is only for temperature characteristics that are not part # of a temperature sensor service. probe=(lambda char: char.service.type != ServicesTypes.TEMPERATURE_SENSOR), @@ -299,22 +296,42 @@ SIMPLE_SENSOR: dict[str, HomeKitSensorEntityDescription] = { CharacteristicsTypes.THREAD_NODE_CAPABILITIES: HomeKitSensorEntityDescription( key=CharacteristicsTypes.THREAD_NODE_CAPABILITIES, name="Thread Capabilities", - device_class="homekit_controller__thread_node_capabilities", entity_category=EntityCategory.DIAGNOSTIC, format=thread_node_capability_to_str, + device_class=SensorDeviceClass.ENUM, + options=[ + "border_router_capable", + "full", + "minimal", + "none", + "router_eligible", + "sleepy", + ], + translation_key="thread_node_capabilities", ), CharacteristicsTypes.THREAD_STATUS: HomeKitSensorEntityDescription( key=CharacteristicsTypes.THREAD_STATUS, name="Thread Status", - device_class="homekit_controller__thread_status", entity_category=EntityCategory.DIAGNOSTIC, format=thread_status_to_str, + device_class=SensorDeviceClass.ENUM, + options=[ + "border_router", + "child", + "detached", + "disabled", + "joining", + "leader", + "router", + ], + translation_key="thread_status", ), CharacteristicsTypes.VENDOR_NETATMO_NOISE: HomeKitSensorEntityDescription( key=CharacteristicsTypes.VENDOR_NETATMO_NOISE, name="Noise", state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=SOUND_PRESSURE_DB, + native_unit_of_measurement=UnitOfSoundPressure.DECIBEL, + device_class=SensorDeviceClass.SOUND_PRESSURE, ), } @@ -363,7 +380,7 @@ class HomeKitTemperatureSensor(HomeKitSensor): """Representation of a Homekit temperature sensor.""" _attr_device_class = SensorDeviceClass.TEMPERATURE - _attr_native_unit_of_measurement = TEMP_CELSIUS + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS def get_characteristic_types(self) -> list[str]: """Define the homekit characteristics the entity is tracking.""" diff --git a/homeassistant/components/homekit_controller/strings.json b/homeassistant/components/homekit_controller/strings.json index 201e0a9b3c2..2291f66d88a 100644 --- a/homeassistant/components/homekit_controller/strings.json +++ b/homeassistant/components/homekit_controller/strings.json @@ -69,5 +69,39 @@ "button9": "Button 9", "button10": "Button 10" } + }, + "entity": { + "select": { + "ecobee_mode": { + "state": { + "away": "Away", + "home": "Home", + "sleep": "Sleep" + } + } + }, + "sensor": { + "thread_node_capabilities": { + "state": { + "border_router_capable": "Border Router Capable", + "full": "Full End Device", + "minimal": "Minimal End Device", + "none": "None", + "router_eligible": "Router Eligible End Device", + "sleepy": "Sleepy End Device" + } + }, + "thread_status": { + "state": { + "border_router": "Border Router", + "child": "Child", + "detached": "Detached", + "disabled": "Disabled", + "joining": "Joining", + "leader": "Leader", + "router": "Router" + } + } + } } } diff --git a/homeassistant/components/homekit_controller/strings.select.json b/homeassistant/components/homekit_controller/strings.select.json deleted file mode 100644 index af925b6681e..00000000000 --- a/homeassistant/components/homekit_controller/strings.select.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "state": { - "homekit_controller__ecobee_mode": { - "home": "Home", - "sleep": "Sleep", - "away": "Away" - } - } -} diff --git a/homeassistant/components/homekit_controller/strings.sensor.json b/homeassistant/components/homekit_controller/strings.sensor.json deleted file mode 100644 index ceede708572..00000000000 --- a/homeassistant/components/homekit_controller/strings.sensor.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "state": { - "homekit_controller__thread_node_capabilities": { - "border_router_capable": "Border Router Capable", - "router_eligible": "Router Eligible End Device", - "full": "Full End Device", - "minimal": "Minimal End Device", - "sleepy": "Sleepy End Device", - "none": "None" - }, - "homekit_controller__thread_status": { - "border_router": "Border Router", - "leader": "Leader", - "router": "Router", - "child": "Child", - "joining": "Joining", - "detached": "Detached", - "disabled": "Disabled" - } - } -} diff --git a/homeassistant/components/homekit_controller/translations/ca.json b/homeassistant/components/homekit_controller/translations/ca.json index 5cb98f7287d..f760057e3fa 100644 --- a/homeassistant/components/homekit_controller/translations/ca.json +++ b/homeassistant/components/homekit_controller/translations/ca.json @@ -14,7 +14,7 @@ "authentication_error": "Codi HomeKit incorrecte. Verifica'l i torna-ho a provar.", "insecure_setup_code": "El codi de configuraci\u00f3 sol\u00b7licitat no \u00e9s segur per naturalesa. Aquest accessori no compleix els requisits b\u00e0sics de seguretat.", "max_peers_error": "El dispositiu ha refusat la vinculaci\u00f3 perqu\u00e8 no t\u00e9 suficient espai lliure.", - "pairing_failed": "S'ha produ\u00eft un error mentre s'intentava la vinculaci\u00f3 amb aquest dispositiu. Pot ser que sigui un error temporal o pot ser que el teu dispositiu encara no sigui compatible.", + "pairing_failed": "S'ha produ\u00eft un error mentre s'intentava la vinculaci\u00f3 amb aquest dispositiu. Pot ser que sigui un error temporal o que el teu dispositiu encara no sigui compatible: {error}", "unable_to_pair": "No s'ha pogut vincular, torna-ho a provar.", "unknown_error": "El dispositiu ha em\u00e8s un error desconegut. Vinculaci\u00f3 fallida." }, @@ -69,5 +69,39 @@ "single_press": "\"{subtype}\" premut" } }, + "entity": { + "select": { + "ecobee_mode": { + "state": { + "away": "A fora", + "home": "A casa", + "sleep": "Dormint" + } + } + }, + "sensor": { + "thread_node_capabilities": { + "state": { + "border_router_capable": "Pot funcionar com a encaminador (router) frontera", + "full": "Dispositiu final complet", + "minimal": "Dispositiu final redu\u00eft", + "none": "Cap", + "router_eligible": "Dispositiu final apte per ser encaminador (router)", + "sleepy": "Dispositiu final dorment" + } + }, + "thread_status": { + "state": { + "border_router": "Encaminador (router) frontera", + "child": "Fill", + "detached": "Desconnectat", + "disabled": "Desactivat", + "joining": "Unint-se", + "leader": "L\u00edder", + "router": "Encaminador (router)" + } + } + } + }, "title": "Controlador HomeKit" } \ No newline at end of file diff --git a/homeassistant/components/homekit_controller/translations/de.json b/homeassistant/components/homekit_controller/translations/de.json index 904a438d699..6c8e01eb04b 100644 --- a/homeassistant/components/homekit_controller/translations/de.json +++ b/homeassistant/components/homekit_controller/translations/de.json @@ -15,7 +15,7 @@ "insecure_setup_code": "Der angeforderte Setup-Code ist unsicher, da er zu trivial ist. Dieses Zubeh\u00f6r erf\u00fcllt nicht die grundlegenden Sicherheitsanforderungen.", "max_peers_error": "Das Ger\u00e4t weigerte sich, die Kopplung durchzuf\u00fchren, da es keinen freien Kopplungs-Speicher hat.", "pairing_failed": "Beim Koppeln mit diesem Ger\u00e4t ist ein nicht behandelter Fehler aufgetreten. Dies kann ein vor\u00fcbergehender Fehler sein oder das oder dein Ger\u00e4t wird derzeit nicht unterst\u00fctzt: {error}", - "unable_to_pair": "Koppeln fehltgeschlagen, bitte versuche es erneut", + "unable_to_pair": "Koppeln fehlgeschlagen, bitte versuche es erneut", "unknown_error": "Das Ger\u00e4t meldete einen unbekannten Fehler. Die Kopplung ist fehlgeschlagen." }, "flow_title": "{name} ({category})", @@ -33,7 +33,7 @@ "allow_insecure_setup_codes": "Pairing mit unsicheren Setup-Codes zulassen.", "pairing_code": "Kopplungscode" }, - "description": "HomeKit Controller kommuniziert mit {name} ( {category} ) \u00fcber das lokale Netzwerk unter Verwendung einer sicheren verschl\u00fcsselten Verbindung ohne separaten HomeKit Controller oder iCloud. Gib deinen HomeKit-Kopplungscode (im Format XXX-XX-XXX) ein, um dieses Zubeh\u00f6r zu verwenden. Dieser Code befindet sich normalerweise auf dem Ger\u00e4t selbst oder in der Verpackung.", + "description": "HomeKit Controller kommuniziert mit {name} ({category}) \u00fcber das lokale Netzwerk unter Verwendung einer sicheren verschl\u00fcsselten Verbindung ohne separaten HomeKit Controller oder iCloud. Gib deinen HomeKit-Kopplungscode (im Format XXX-XX-XXX) ein, um dieses Zubeh\u00f6r zu verwenden. Dieser Code befindet sich normalerweise auf dem Ger\u00e4t selbst oder in der Verpackung.", "title": "Mit HomeKit Zubeh\u00f6r koppeln" }, "protocol_error": { @@ -69,5 +69,39 @@ "single_press": "\"{subtype}\" gedr\u00fcckt" } }, + "entity": { + "select": { + "ecobee_mode": { + "state": { + "away": "Abwesend", + "home": "Zu Hause", + "sleep": "Schlafen" + } + } + }, + "sensor": { + "thread_node_capabilities": { + "state": { + "border_router_capable": "Border Router-f\u00e4hig", + "full": "Vollst\u00e4ndiges Endger\u00e4t", + "minimal": "Minimales Endger\u00e4t", + "none": "Keine", + "router_eligible": "Router-f\u00e4higes Endger\u00e4t", + "sleepy": "Sleepy Endger\u00e4t" + } + }, + "thread_status": { + "state": { + "border_router": "Border Router", + "child": "Kind", + "detached": "Freistehend", + "disabled": "Deaktiviert", + "joining": "Beitreten", + "leader": "Anf\u00fchrer", + "router": "Router" + } + } + } + }, "title": "HomeKit-Controller" } \ No newline at end of file diff --git a/homeassistant/components/homekit_controller/translations/el.json b/homeassistant/components/homekit_controller/translations/el.json index ebfc1687301..424b0253019 100644 --- a/homeassistant/components/homekit_controller/translations/el.json +++ b/homeassistant/components/homekit_controller/translations/el.json @@ -69,5 +69,39 @@ "single_press": "\u03a0\u03b1\u03c4\u03ae\u03b8\u03b7\u03ba\u03b5 \u03c4\u03bf \" {subtype} \"" } }, + "entity": { + "select": { + "ecobee_mode": { + "state": { + "away": "\u0395\u03ba\u03c4\u03cc\u03c2 \u03c3\u03c0\u03b9\u03c4\u03b9\u03bf\u03cd", + "home": "\u03a3\u03c0\u03af\u03c4\u03b9", + "sleep": "\u038e\u03c0\u03bd\u03bf\u03c2" + } + } + }, + "sensor": { + "thread_node_capabilities": { + "state": { + "border_router_capable": "\u0394\u03c5\u03bd\u03b1\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1 \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03bf\u03b3\u03b7\u03c4\u03ae \u03c3\u03c5\u03bd\u03cc\u03c1\u03c9\u03bd", + "full": "\u03a3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03c0\u03bb\u03ae\u03c1\u03bf\u03c5\u03c2 \u03bb\u03ae\u03be\u03b7\u03c2", + "minimal": "\u0395\u03bb\u03ac\u03c7\u03b9\u03c3\u03c4\u03b7 \u03c4\u03b5\u03bb\u03b9\u03ba\u03ae \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae", + "none": "\u03a4\u03af\u03c0\u03bf\u03c4\u03b1", + "router_eligible": "\u039a\u03b1\u03c4\u03ac\u03bb\u03bb\u03b7\u03bb\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03c4\u03b5\u03c1\u03bc\u03b1\u03c4\u03b9\u03c3\u03bc\u03bf\u03cd \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03bf\u03b3\u03b7\u03c4\u03ae", + "sleepy": "Sleepy End Device" + } + }, + "thread_status": { + "state": { + "border_router": "Border Router", + "child": "\u03a0\u03b1\u03b9\u03b4\u03af", + "detached": "\u0391\u03c0\u03bf\u03bc\u03bf\u03bd\u03c9\u03bc\u03ad\u03bd\u03bf", + "disabled": "\u0391\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03bf", + "joining": "\u03a3\u03c5\u03bc\u03bc\u03b5\u03c4\u03bf\u03c7\u03ae", + "leader": "\u0397\u03b3\u03ad\u03c4\u03b7\u03c2", + "router": "\u0394\u03c1\u03bf\u03bc\u03bf\u03bb\u03bf\u03b3\u03b7\u03c4\u03ae\u03c2" + } + } + } + }, "title": "HomeKit Controller" } \ No newline at end of file diff --git a/homeassistant/components/homekit_controller/translations/en.json b/homeassistant/components/homekit_controller/translations/en.json index c5b5178a58b..7f6e1848e9c 100644 --- a/homeassistant/components/homekit_controller/translations/en.json +++ b/homeassistant/components/homekit_controller/translations/en.json @@ -69,5 +69,39 @@ "single_press": "\"{subtype}\" pressed" } }, + "entity": { + "select": { + "ecobee_mode": { + "state": { + "away": "Away", + "home": "Home", + "sleep": "Sleep" + } + } + }, + "sensor": { + "thread_node_capabilities": { + "state": { + "border_router_capable": "Border Router Capable", + "full": "Full End Device", + "minimal": "Minimal End Device", + "none": "None", + "router_eligible": "Router Eligible End Device", + "sleepy": "Sleepy End Device" + } + }, + "thread_status": { + "state": { + "border_router": "Border Router", + "child": "Child", + "detached": "Detached", + "disabled": "Disabled", + "joining": "Joining", + "leader": "Leader", + "router": "Router" + } + } + } + }, "title": "HomeKit Controller" } \ No newline at end of file diff --git a/homeassistant/components/homekit_controller/translations/es.json b/homeassistant/components/homekit_controller/translations/es.json index 8e3eb44475b..b7e51d5c390 100644 --- a/homeassistant/components/homekit_controller/translations/es.json +++ b/homeassistant/components/homekit_controller/translations/es.json @@ -69,5 +69,39 @@ "single_press": "\"{subtype}\" pulsado" } }, + "entity": { + "select": { + "ecobee_mode": { + "state": { + "away": "Ausente", + "home": "En casa", + "sleep": "Dormir" + } + } + }, + "sensor": { + "thread_node_capabilities": { + "state": { + "border_router_capable": "Capacidad de router fronterizo", + "full": "Dispositivo final completo", + "minimal": "Dispositivo final m\u00ednimo", + "none": "Ninguna", + "router_eligible": "Dispositivo final elegible como router", + "sleepy": "Dispositivo final dormido" + } + }, + "thread_status": { + "state": { + "border_router": "Router fronterizo", + "child": "Hijo", + "detached": "Aislado", + "disabled": "Deshabilitado", + "joining": "Uniendo", + "leader": "L\u00edder", + "router": "Router" + } + } + } + }, "title": "Controlador HomeKit" } \ No newline at end of file diff --git a/homeassistant/components/homekit_controller/translations/et.json b/homeassistant/components/homekit_controller/translations/et.json index 1cefd89db57..301eda7b403 100644 --- a/homeassistant/components/homekit_controller/translations/et.json +++ b/homeassistant/components/homekit_controller/translations/et.json @@ -69,5 +69,39 @@ "single_press": "\" {subtype} \" on vajutatud" } }, + "entity": { + "select": { + "ecobee_mode": { + "state": { + "away": "Eemal", + "home": "Kodus", + "sleep": "Uneaeg" + } + } + }, + "sensor": { + "thread_node_capabilities": { + "state": { + "border_router_capable": "Piiriruuteri v\u00f5imekus", + "full": "T\u00e4ielik l\u00f5ppseade", + "minimal": "Minimaalne l\u00f5ppseade", + "none": "Puudub", + "router_eligible": "Ruuteriks sobiv l\u00f5ppseade", + "sleepy": "Uinuv l\u00f5ppseade" + } + }, + "thread_status": { + "state": { + "border_router": "Rajaruuter", + "child": "Alamseade", + "detached": "Eraldatud", + "disabled": "Keelatud", + "joining": "Liitun", + "leader": "Juhtseade", + "router": "Ruuter" + } + } + } + }, "title": "HomeKit kontroller" } \ No newline at end of file diff --git a/homeassistant/components/homekit_controller/translations/he.json b/homeassistant/components/homekit_controller/translations/he.json index c5ec291569d..f96c517bbc9 100644 --- a/homeassistant/components/homekit_controller/translations/he.json +++ b/homeassistant/components/homekit_controller/translations/he.json @@ -11,5 +11,14 @@ } } } + }, + "entity": { + "sensor": { + "thread_status": { + "state": { + "disabled": "\u05de\u05d5\u05e9\u05d1\u05ea" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/homekit_controller/translations/hu.json b/homeassistant/components/homekit_controller/translations/hu.json index 9bf7922e3e0..99a14981026 100644 --- a/homeassistant/components/homekit_controller/translations/hu.json +++ b/homeassistant/components/homekit_controller/translations/hu.json @@ -14,7 +14,7 @@ "authentication_error": "Helytelen HomeKit k\u00f3d. K\u00e9rem, ellen\u0151rizze, \u00e9s pr\u00f3b\u00e1lja \u00fajra.", "insecure_setup_code": "A k\u00e9rt telep\u00edt\u00e9si k\u00f3d trivi\u00e1lis jellege miatt nem biztons\u00e1gos. Ez a tartoz\u00e9k nem felel meg az alapvet\u0151 biztons\u00e1gi k\u00f6vetelm\u00e9nyeknek.", "max_peers_error": "Az eszk\u00f6z megtagadta a p\u00e1ros\u00edt\u00e1s hozz\u00e1ad\u00e1s\u00e1t, mivel nincs szabad p\u00e1ros\u00edt\u00e1si t\u00e1rhelye.", - "pairing_failed": "Nem kezelt hiba t\u00f6rt\u00e9nt az eszk\u00f6zzel val\u00f3 p\u00e1ros\u00edt\u00e1s sor\u00e1n. Lehet, hogy ez \u00e1tmeneti hiba, vagy az eszk\u00f6z jelenleg m\u00e9g nem t\u00e1mogatott.", + "pairing_failed": "Nem kezelt hiba t\u00f6rt\u00e9nt az eszk\u00f6zzel val\u00f3 p\u00e1ros\u00edt\u00e1s sor\u00e1n. Ez \u00e1tmeneti hiba lehet, vagy el\u0151fordulhat, hogy az eszk\u00f6z jelenleg nem t\u00e1mogatott: {error}", "unable_to_pair": "Nem siker\u00fclt p\u00e1ros\u00edtani, pr\u00f3b\u00e1ld \u00fajra.", "unknown_error": "Az eszk\u00f6z ismeretlen hib\u00e1t jelentett. A p\u00e1ros\u00edt\u00e1s sikertelen." }, @@ -69,5 +69,39 @@ "single_press": "\"{subtype}\" lenyomva" } }, + "entity": { + "select": { + "ecobee_mode": { + "state": { + "away": "T\u00e1vol", + "home": "Otthon", + "sleep": "Alv\u00e1s" + } + } + }, + "sensor": { + "thread_node_capabilities": { + "state": { + "border_router_capable": "Hat\u00e1rol\u00f3 \u00fatv\u00e1laszt\u00f3 k\u00e9pess\u00e9g", + "full": "Teljes k\u00e9pess\u00e9g\u0171 v\u00e9gberendez\u00e9s", + "minimal": "Minim\u00e1lis k\u00e9pess\u00e9g\u0171 v\u00e9gberendez\u00e9s", + "none": "Nincs", + "router_eligible": "\u00datv\u00e1laszt\u00f3ra alkalmas v\u00e9geszk\u00f6z", + "sleepy": "Alv\u00f3 v\u00e9gberendez\u00e9s" + } + }, + "thread_status": { + "state": { + "border_router": "Hat\u00e1rol\u00f3 \u00fatv\u00e1laszt\u00f3", + "child": "Gyermek", + "detached": "Lev\u00e1lasztva", + "disabled": "Letiltva", + "joining": "Csatlakoz\u00e1s", + "leader": "Vezet\u0151", + "router": "\u00datv\u00e1laszt\u00f3" + } + } + } + }, "title": "HomeKit Vez\u00e9rl\u0151" } \ No newline at end of file diff --git a/homeassistant/components/homekit_controller/translations/id.json b/homeassistant/components/homekit_controller/translations/id.json index aa2d5111756..f0fca1fe4b4 100644 --- a/homeassistant/components/homekit_controller/translations/id.json +++ b/homeassistant/components/homekit_controller/translations/id.json @@ -69,5 +69,39 @@ "single_press": "\"{subtype}\" ditekan" } }, + "entity": { + "select": { + "ecobee_mode": { + "state": { + "away": "Keluar", + "home": "Di Rumah", + "sleep": "Tidur" + } + } + }, + "sensor": { + "thread_node_capabilities": { + "state": { + "border_router_capable": "Kemampuan Router Perbatasan", + "full": "Perangkat Akhir Lengkap", + "minimal": "Perangkat Akhir Minimal", + "none": "Tidak Ada", + "router_eligible": "Perangkat Akhir yang Memenuhi Syarat Router", + "sleepy": "Perangkat Akhir yang Jenak" + } + }, + "thread_status": { + "state": { + "border_router": "Router Perbatasan", + "child": "Anakan", + "detached": "Terpisah", + "disabled": "Dinonaktifkan", + "joining": "Bergabung", + "leader": "Kepala", + "router": "Router" + } + } + } + }, "title": "Pengontrol HomeKit" } \ No newline at end of file diff --git a/homeassistant/components/homekit_controller/translations/it.json b/homeassistant/components/homekit_controller/translations/it.json index 5c71b00a801..1ab51dde99d 100644 --- a/homeassistant/components/homekit_controller/translations/it.json +++ b/homeassistant/components/homekit_controller/translations/it.json @@ -14,7 +14,7 @@ "authentication_error": "Codice HomeKit errato. Per favore, controllate e riprovate.", "insecure_setup_code": "Il codice di installazione richiesto non \u00e8 sicuro a causa della sua natura banale. Questo accessorio non soddisfa i requisiti di sicurezza di base.", "max_peers_error": "Il dispositivo ha rifiutato di aggiungere l'abbinamento in quanto non dispone di una memoria libera per esso.", - "pairing_failed": "Si \u00e8 verificato un errore non gestito durante il tentativo di abbinamento con questo dispositivo. Potrebbe trattarsi di un errore temporaneo o il dispositivo potrebbe non essere attualmente supportato.", + "pairing_failed": "Si \u00e8 verificato un errore non gestito durante il tentativo di associazione con questo dispositivo. Potrebbe trattarsi di un errore temporaneo o il dispositivo potrebbe non essere attualmente supportato: {error}", "unable_to_pair": "Impossibile abbinare, riprova.", "unknown_error": "Il dispositivo ha riportato un errore sconosciuto. L'abbinamento non \u00e8 riuscito." }, @@ -69,5 +69,39 @@ "single_press": "\"{subtype}\" premuto" } }, + "entity": { + "select": { + "ecobee_mode": { + "state": { + "away": "Lontano", + "home": "Casa", + "sleep": "Sonno" + } + } + }, + "sensor": { + "thread_node_capabilities": { + "state": { + "border_router_capable": "Compatibile con router di confine", + "full": "Dispositivo finale completo", + "minimal": "Dispositivo finale minimo", + "none": "Nessuno", + "router_eligible": "Dispositivo finale idoneo per il router", + "sleepy": "Dispositivo finale sonnolento" + } + }, + "thread_status": { + "state": { + "border_router": "Router di confine", + "child": "Figlio", + "detached": "Indipendente", + "disabled": "Disabilitato", + "joining": "In unione", + "leader": "Capo", + "router": "Router" + } + } + } + }, "title": "Controller HomeKit" } \ No newline at end of file diff --git a/homeassistant/components/homekit_controller/translations/nl.json b/homeassistant/components/homekit_controller/translations/nl.json index fd966ad43fa..403e997e52d 100644 --- a/homeassistant/components/homekit_controller/translations/nl.json +++ b/homeassistant/components/homekit_controller/translations/nl.json @@ -69,5 +69,14 @@ "single_press": "\" {subtype} \" ingedrukt" } }, + "entity": { + "select": { + "ecobee_mode": { + "state": { + "home": "Thuis" + } + } + } + }, "title": "HomeKit Controller" } \ No newline at end of file diff --git a/homeassistant/components/homekit_controller/translations/no.json b/homeassistant/components/homekit_controller/translations/no.json index 3775734f7c0..eb7f3dc0a0d 100644 --- a/homeassistant/components/homekit_controller/translations/no.json +++ b/homeassistant/components/homekit_controller/translations/no.json @@ -69,5 +69,39 @@ "single_press": "\"{subtype}\" trykket" } }, + "entity": { + "select": { + "ecobee_mode": { + "state": { + "away": "Borte", + "home": "Hjemme", + "sleep": "Sove" + } + } + }, + "sensor": { + "thread_node_capabilities": { + "state": { + "border_router_capable": "Kan med kantruter", + "full": "Full End-enhet", + "minimal": "Minimal sluttenhet", + "none": "Ingen", + "router_eligible": "Ruterkvalifisert sluttenhet", + "sleepy": "S\u00f8vnig sluttenhet" + } + }, + "thread_status": { + "state": { + "border_router": "Border Router", + "child": "Barn", + "detached": "Frakoblet", + "disabled": "Deaktivert", + "joining": "Blir med", + "leader": "Leder", + "router": "Ruter" + } + } + } + }, "title": "HomeKit-kontroller" } \ No newline at end of file diff --git a/homeassistant/components/homekit_controller/translations/pl.json b/homeassistant/components/homekit_controller/translations/pl.json index efd4f616553..76a3885c6bd 100644 --- a/homeassistant/components/homekit_controller/translations/pl.json +++ b/homeassistant/components/homekit_controller/translations/pl.json @@ -69,5 +69,39 @@ "single_press": "przycisk \"{subtype}\" zostanie naci\u015bni\u0119ty" } }, + "entity": { + "select": { + "ecobee_mode": { + "state": { + "away": "poza domem", + "home": "w domu", + "sleep": "noc" + } + } + }, + "sensor": { + "thread_node_capabilities": { + "state": { + "border_router_capable": "funkcje routera granicznego", + "full": "pe\u0142ne urz\u0105dzenie ko\u0144cowe", + "minimal": "podstawowe urz\u0105dzenie ko\u0144cowe", + "none": "brak", + "router_eligible": "urz\u0105dzenie ko\u0144cowe kwalifikuj\u0105ce si\u0119 jako router", + "sleepy": "u\u015bpione urz\u0105dzenie ko\u0144cowe" + } + }, + "thread_status": { + "state": { + "border_router": "router graniczny", + "child": "dziecko", + "detached": "od\u0142\u0105czony", + "disabled": "wy\u0142\u0105czony", + "joining": "do\u0142\u0105czanie", + "leader": "lider", + "router": "router" + } + } + } + }, "title": "Kontroler HomeKit" } \ No newline at end of file diff --git a/homeassistant/components/homekit_controller/translations/pt-BR.json b/homeassistant/components/homekit_controller/translations/pt-BR.json index 1055cd6c921..68ad58a7e96 100644 --- a/homeassistant/components/homekit_controller/translations/pt-BR.json +++ b/homeassistant/components/homekit_controller/translations/pt-BR.json @@ -69,5 +69,39 @@ "single_press": "\"{subtype}\" pressionado" } }, + "entity": { + "select": { + "ecobee_mode": { + "state": { + "away": "Fora", + "home": "Casa", + "sleep": "Dorme" + } + } + }, + "sensor": { + "thread_node_capabilities": { + "state": { + "border_router_capable": "Compat\u00edvel com roteador de borda", + "full": "Dispositivo final completo", + "minimal": "Dispositivo final m\u00ednimo", + "none": "Nenhum", + "router_eligible": "Dispositivo final eleg\u00edvel como roteador", + "sleepy": "Dispositivo final inativo" + } + }, + "thread_status": { + "state": { + "border_router": "Roteador de borda", + "child": "Filho", + "detached": "Independente", + "disabled": "Desabilitado", + "joining": "Juntando-se", + "leader": "L\u00edder", + "router": "Roteador" + } + } + } + }, "title": "" } \ No newline at end of file diff --git a/homeassistant/components/homekit_controller/translations/ru.json b/homeassistant/components/homekit_controller/translations/ru.json index 0e7c4f94628..fd4ed0e58f2 100644 --- a/homeassistant/components/homekit_controller/translations/ru.json +++ b/homeassistant/components/homekit_controller/translations/ru.json @@ -69,5 +69,39 @@ "single_press": "\"{subtype}\" \u043d\u0430\u0436\u0430\u0442\u0430" } }, + "entity": { + "select": { + "ecobee_mode": { + "state": { + "away": "\u041d\u0435 \u0434\u043e\u043c\u0430", + "home": "\u0414\u043e\u043c\u0430", + "sleep": "\u0421\u043e\u043d" + } + } + }, + "sensor": { + "thread_node_capabilities": { + "state": { + "border_router_capable": "\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u0433\u0440\u0430\u043d\u0438\u0447\u043d\u043e\u0433\u043e \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0442\u043e\u0440\u0430", + "full": "\u041f\u043e\u043b\u043d\u043e\u0435 \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e", + "minimal": "\u041c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e", + "none": "\u041e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442", + "router_eligible": "\u041a\u043e\u043d\u0435\u0447\u043d\u043e\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e, \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0435 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0442\u043e\u0440\u0443", + "sleepy": "\u0421\u043f\u044f\u0449\u0435\u0435 \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e" + } + }, + "thread_status": { + "state": { + "border_router": "\u0413\u0440\u0430\u043d\u0438\u0447\u043d\u044b\u0439 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0442\u043e\u0440", + "child": "\u0414\u043e\u0447\u0435\u0440\u043d\u0435\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e", + "detached": "\u041e\u0442\u0434\u0435\u043b\u0435\u043d\u043e", + "disabled": "\u041e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u043e", + "joining": "\u041f\u0440\u0438\u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435", + "leader": "\u041b\u0438\u0434\u0435\u0440", + "router": "\u041c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0442\u043e\u0440" + } + } + } + }, "title": "HomeKit Controller" } \ No newline at end of file diff --git a/homeassistant/components/homekit_controller/translations/sensor.pl.json b/homeassistant/components/homekit_controller/translations/sensor.pl.json index a11105cfc15..4dbe88b16b6 100644 --- a/homeassistant/components/homekit_controller/translations/sensor.pl.json +++ b/homeassistant/components/homekit_controller/translations/sensor.pl.json @@ -9,7 +9,7 @@ "sleepy": "u\u015bpione urz\u0105dzenie ko\u0144cowe" }, "homekit_controller__thread_status": { - "border_router": "router graniczny", + "border_router": "router brzegowy", "child": "dziecko", "detached": "od\u0142\u0105czony", "disabled": "wy\u0142\u0105czony", diff --git a/homeassistant/components/homekit_controller/translations/sensor.ru.json b/homeassistant/components/homekit_controller/translations/sensor.ru.json index c6e67b645e3..5f5ec057e68 100644 --- a/homeassistant/components/homekit_controller/translations/sensor.ru.json +++ b/homeassistant/components/homekit_controller/translations/sensor.ru.json @@ -1,7 +1,21 @@ { "state": { "homekit_controller__thread_node_capabilities": { + "border_router_capable": "\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u0433\u0440\u0430\u043d\u0438\u0447\u043d\u043e\u0433\u043e \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0442\u043e\u0440\u0430", + "full": "\u041f\u043e\u043b\u043d\u043e\u0435 \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e", + "minimal": "\u041c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e", + "none": "\u041e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442", + "router_eligible": "\u041a\u043e\u043d\u0435\u0447\u043d\u043e\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e, \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0435 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0442\u043e\u0440\u0443", "sleepy": "\u0421\u043f\u044f\u0449\u0435\u0435 \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e" + }, + "homekit_controller__thread_status": { + "border_router": "\u0413\u0440\u0430\u043d\u0438\u0447\u043d\u044b\u0439 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0442\u043e\u0440", + "child": "\u0414\u043e\u0447\u0435\u0440\u043d\u0435\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e", + "detached": "\u041e\u0442\u0434\u0435\u043b\u0435\u043d\u043e", + "disabled": "\u041e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u043e", + "joining": "\u041f\u0440\u0438\u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435", + "leader": "\u041b\u0438\u0434\u0435\u0440", + "router": "\u041c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0442\u043e\u0440" } } } \ No newline at end of file diff --git a/homeassistant/components/homekit_controller/translations/sensor.sk.json b/homeassistant/components/homekit_controller/translations/sensor.sk.json index 9d22481d838..e8f90b6f3f2 100644 --- a/homeassistant/components/homekit_controller/translations/sensor.sk.json +++ b/homeassistant/components/homekit_controller/translations/sensor.sk.json @@ -1,7 +1,20 @@ { "state": { + "homekit_controller__thread_node_capabilities": { + "border_router_capable": "Mo\u017enos\u0165 Border Router", + "full": "\u00dapln\u00e9 koncov\u00e9 zariadenie", + "minimal": "Minim\u00e1lne koncov\u00e9 zariadenie", + "none": "\u017diadne", + "router_eligible": "Koncov\u00e9 zariadenie vhodn\u00e9 pre router", + "sleepy": "Sleepy koncov\u00e9 zaradenie" + }, "homekit_controller__thread_status": { + "border_router": "Border Router", + "child": "Child", + "detached": "Oddelen\u00e9", "disabled": "Zak\u00e1zan\u00e9", + "joining": "Prip\u00e1ja sa", + "leader": "Leader", "router": "Router" } } diff --git a/homeassistant/components/homekit_controller/translations/sk.json b/homeassistant/components/homekit_controller/translations/sk.json index a62d9c6334a..fbc3d833901 100644 --- a/homeassistant/components/homekit_controller/translations/sk.json +++ b/homeassistant/components/homekit_controller/translations/sk.json @@ -5,10 +5,15 @@ "already_configured": "Pr\u00edslu\u0161enstvo je u\u017e nakonfigurovan\u00e9 s t\u00fdmto kontrol\u00e9rom.", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "already_paired": "Toto pr\u00edslu\u0161enstvo je u\u017e sp\u00e1rovan\u00e9 s in\u00fdm zariaden\u00edm. Resetujte pr\u00edslu\u0161enstvo a sk\u00faste to znova.", + "ignored_model": "Podpora HomeKit pre tento model je zablokovan\u00e1, preto\u017ee je k dispoz\u00edcii kompletnej\u0161ia nat\u00edvna integr\u00e1cia s viacer\u00fdmi funkciami.", + "invalid_config_entry": "Toto zariadenie sa zobrazuje ako pripraven\u00e9 na p\u00e1rovanie, ale v aplik\u00e1cii Home Assistant u\u017e pre\u0148 existuje konfliktn\u00e1 konfigura\u010dn\u00e1 polo\u017eka, ktor\u00fa je potrebn\u00e9 najsk\u00f4r odstr\u00e1ni\u0165.", + "invalid_properties": "Neplatn\u00e9 vlastnosti ozn\u00e1men\u00e9 zariaden\u00edm.", "no_devices": "Nena\u0161li sa \u017eiadne nesp\u00e1rovan\u00e9 zariadenia" }, "error": { "authentication_error": "Nespr\u00e1vny k\u00f3d HomeKit. Skontrolujte to a sk\u00faste to znova.", + "insecure_setup_code": "Po\u017eadovan\u00fd in\u0161tala\u010dn\u00fd k\u00f3d je nezabezpe\u010den\u00fd z d\u00f4vodu jeho trivi\u00e1lnej povahy. Toto pr\u00edslu\u0161enstvo nesp\u013a\u0148a z\u00e1kladn\u00e9 bezpe\u010dnostn\u00e9 po\u017eiadavky.", + "max_peers_error": "Zariadenie odmietlo prida\u0165 p\u00e1rovanie, preto\u017ee nem\u00e1 vo\u013en\u00e9 \u00falo\u017eisko na p\u00e1rovanie.", "pairing_failed": "Pri pokuse o sp\u00e1rovanie s t\u00fdmto zariaden\u00edm do\u0161lo k neobsluhovanej chybe. M\u00f4\u017ee \u00eds\u0165 o do\u010dasn\u00fa chybu alebo va\u0161e zariadenie nemus\u00ed by\u0165 v s\u00fa\u010dasnosti podporovan\u00e9: {error}", "unable_to_pair": "Nie je mo\u017en\u00e9 sp\u00e1rova\u0165, sk\u00faste to znova.", "unknown_error": "Zariadenie hl\u00e1silo nezn\u00e1mu chybu. Sp\u00e1rovanie zlyhalo." @@ -16,24 +21,30 @@ "flow_title": "{name} ({category})", "step": { "busy_error": { + "description": "Preru\u0161te p\u00e1rovanie na v\u0161etk\u00fdch ovl\u00e1da\u010doch alebo sk\u00faste re\u0161tartova\u0165 zariadenie a potom pokra\u010dujte v p\u00e1rovan\u00ed.", "title": "Zariadenie sa u\u017e sp\u00e1ruje s in\u00fdm kontrol\u00e9rom" }, "max_tries_error": { + "description": "Zariadenie prijalo viac ako 100 ne\u00faspe\u0161n\u00fdch pokusov o overenie. Sk\u00faste re\u0161tartova\u0165 zariadenie a potom pokra\u010dujte v p\u00e1rovan\u00ed.", "title": "Bol prekro\u010den\u00fd maxim\u00e1lny po\u010det pokusov o overenie" }, "pair": { "data": { "allow_insecure_setup_codes": "Povoli\u0165 p\u00e1rovanie s nezabezpe\u010den\u00fdmi k\u00f3dmi nastavenia.", "pairing_code": "K\u00f3d p\u00e1rovania" - } + }, + "description": "HomeKit kontrol\u00e9r komunikuje s pou\u017e\u00edvate\u013eom {name} ({category}) cez lok\u00e1lnu sie\u0165 pomocou zabezpe\u010den\u00e9ho \u0161ifrovan\u00e9ho pripojenia bez samostatn\u00e9ho ovl\u00e1da\u010da HomeKit alebo iCloud. Ak chcete pou\u017ei\u0165 toto pr\u00edslu\u0161enstvo, zadajte p\u00e1rovac\u00ed k\u00f3d HomeKit (vo form\u00e1te XXX-XX-XXX). Tento k\u00f3d sa zvy\u010dajne nach\u00e1dza na samotnom zariaden\u00ed alebo v obale.", + "title": "Sp\u00e1rujte so zariaden\u00edm cez HomeKit Accessory Protocol" }, "protocol_error": { + "description": "Zariadenie nemus\u00ed by\u0165 v re\u017eime p\u00e1rovania a m\u00f4\u017ee vy\u017eadova\u0165 fyzick\u00e9 alebo virtu\u00e1lne stla\u010denie tla\u010didla. Uistite sa, \u017ee je zariadenie v re\u017eime p\u00e1rovania alebo sk\u00faste zariadenie re\u0161tartova\u0165 a potom pokra\u010dujte v p\u00e1rovan\u00ed.", "title": "Chyba pri komunik\u00e1cii s pr\u00edslu\u0161enstvom" }, "user": { "data": { "device": "Zariadenie" }, + "description": "HomeKit kontrol\u00e9r komunikuje cez lok\u00e1lnu sie\u0165 pomocou zabezpe\u010den\u00e9ho \u0161ifrovan\u00e9ho pripojenia bez samostatn\u00e9ho ovl\u00e1da\u010da HomeKit alebo iCloud. Vyberte zariadenie, s ktor\u00fdm chcete sp\u00e1rova\u0165:", "title": "V\u00fdber zariadenia" } } @@ -57,5 +68,40 @@ "long_press": "\"{subtype}\" stla\u010den\u00e9 a podr\u017ean\u00e9", "single_press": "\"{subtype}\" stla\u010den\u00e9" } - } + }, + "entity": { + "select": { + "ecobee_mode": { + "state": { + "away": "Pre\u010d", + "home": "Doma", + "sleep": "Sp\u00e1nok" + } + } + }, + "sensor": { + "thread_node_capabilities": { + "state": { + "border_router_capable": "Mo\u017enos\u0165 Border Router", + "full": "\u00dapln\u00e9 koncov\u00e9 zariadenie", + "minimal": "Minim\u00e1lne koncov\u00e9 zariadenie", + "none": "\u017diadne", + "router_eligible": "Koncov\u00e9 zariadenie vhodn\u00e9 pre router", + "sleepy": "Sleepy koncov\u00e9 zaradenie" + } + }, + "thread_status": { + "state": { + "border_router": "Border Router", + "child": "Child", + "detached": "Oddelen\u00e9", + "disabled": "Zak\u00e1zan\u00e9", + "joining": "Prip\u00e1ja sa", + "leader": "Leader", + "router": "Router" + } + } + } + }, + "title": "HomeKit kontrol\u00e9r" } \ No newline at end of file diff --git a/homeassistant/components/homekit_controller/translations/zh-Hant.json b/homeassistant/components/homekit_controller/translations/zh-Hant.json index 4614676e797..7d407949084 100644 --- a/homeassistant/components/homekit_controller/translations/zh-Hant.json +++ b/homeassistant/components/homekit_controller/translations/zh-Hant.json @@ -69,5 +69,39 @@ "single_press": "\"{subtype}\" \u6309\u4e0b" } }, + "entity": { + "select": { + "ecobee_mode": { + "state": { + "away": "\u96e2\u5bb6", + "home": "\u5728\u5bb6", + "sleep": "\u7761\u7720" + } + } + }, + "sensor": { + "thread_node_capabilities": { + "state": { + "border_router_capable": "Border Router \u80fd\u529b", + "full": "\u6240\u6709\u7d42\u7aef\u88dd\u7f6e", + "minimal": "\u6700\u4f4e\u7d42\u7aef\u88dd\u7f6e", + "none": "\u7121", + "router_eligible": "Router \u7d42\u7aef\u88dd\u7f6e", + "sleepy": "\u5f85\u547d\u7d42\u7aef\u88dd\u7f6e" + } + }, + "thread_status": { + "state": { + "border_router": "Border Router", + "child": "Child", + "detached": "Detached", + "disabled": "\u5df2\u95dc\u9589", + "joining": "\u52a0\u5165", + "leader": "Leader", + "router": "Router" + } + } + } + }, "title": "HomeKit \u63a7\u5236\u5668" } \ No newline at end of file diff --git a/homeassistant/components/homematic/climate.py b/homeassistant/components/homematic/climate.py index d597eca30cd..998113f1bcf 100644 --- a/homeassistant/components/homematic/climate.py +++ b/homeassistant/components/homematic/climate.py @@ -12,7 +12,7 @@ from homeassistant.components.climate import ( ClimateEntityFeature, HVACMode, ) -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType @@ -58,7 +58,7 @@ class HMThermostat(HMDevice, ClimateEntity): _attr_supported_features = ( ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE ) - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS @property def hvac_mode(self) -> HVACMode: diff --git a/homeassistant/components/homematic/sensor.py b/homeassistant/components/homematic/sensor.py index 4c18afd0458..8b5d7b6bff1 100644 --- a/homeassistant/components/homematic/sensor.py +++ b/homeassistant/components/homematic/sensor.py @@ -15,19 +15,18 @@ from homeassistant.const import ( CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, CONCENTRATION_PARTS_PER_MILLION, DEGREE, - ELECTRIC_CURRENT_MILLIAMPERE, - ELECTRIC_POTENTIAL_VOLT, - ENERGY_KILO_WATT_HOUR, - ENERGY_WATT_HOUR, - FREQUENCY_HERTZ, - LENGTH_MILLIMETERS, LIGHT_LUX, PERCENTAGE, - POWER_WATT, - PRESSURE_HPA, - SPEED_KILOMETERS_PER_HOUR, - TEMP_CELSIUS, - VOLUME_CUBIC_METERS, + UnitOfElectricCurrent, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfFrequency, + UnitOfPower, + UnitOfPrecipitationDepth, + UnitOfPressure, + UnitOfSpeed, + UnitOfTemperature, + UnitOfVolume, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -64,13 +63,13 @@ SENSOR_DESCRIPTIONS: dict[str, SensorEntityDescription] = { ), "ACTUAL_TEMPERATURE": SensorEntityDescription( key="ACTUAL_TEMPERATURE", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), "TEMPERATURE": SensorEntityDescription( key="TEMPERATURE", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), @@ -112,19 +111,19 @@ SENSOR_DESCRIPTIONS: dict[str, SensorEntityDescription] = { ), "POWER": SensorEntityDescription( key="POWER", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), "IEC_POWER": SensorEntityDescription( key="IEC_POWER", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), "CURRENT": SensorEntityDescription( key="CURRENT", - native_unit_of_measurement=ELECTRIC_CURRENT_MILLIAMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.MILLIAMPERE, device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, ), @@ -136,42 +135,43 @@ SENSOR_DESCRIPTIONS: dict[str, SensorEntityDescription] = { ), "ENERGY_COUNTER": SensorEntityDescription( key="ENERGY_COUNTER", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), "IEC_ENERGY_COUNTER": SensorEntityDescription( key="IEC_ENERGY_COUNTER", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), "VOLTAGE": SensorEntityDescription( key="VOLTAGE", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, ), "GAS_POWER": SensorEntityDescription( key="GAS_POWER", - native_unit_of_measurement=VOLUME_CUBIC_METERS, + native_unit_of_measurement=UnitOfVolume.CUBIC_METERS, device_class=SensorDeviceClass.GAS, state_class=SensorStateClass.MEASUREMENT, ), "GAS_ENERGY_COUNTER": SensorEntityDescription( key="GAS_ENERGY_COUNTER", - native_unit_of_measurement=VOLUME_CUBIC_METERS, + native_unit_of_measurement=UnitOfVolume.CUBIC_METERS, device_class=SensorDeviceClass.GAS, state_class=SensorStateClass.TOTAL_INCREASING, ), "RAIN_COUNTER": SensorEntityDescription( key="RAIN_COUNTER", - native_unit_of_measurement=LENGTH_MILLIMETERS, + native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + device_class=SensorDeviceClass.PRECIPITATION, ), "WIND_SPEED": SensorEntityDescription( key="WIND_SPEED", - native_unit_of_measurement=SPEED_KILOMETERS_PER_HOUR, - device_class=SensorDeviceClass.SPEED, + native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, + device_class=SensorDeviceClass.WIND_SPEED, icon="mdi:weather-windy", ), "WIND_DIRECTION": SensorEntityDescription( @@ -188,13 +188,14 @@ SENSOR_DESCRIPTIONS: dict[str, SensorEntityDescription] = { ), "AIR_PRESSURE": SensorEntityDescription( key="AIR_PRESSURE", - native_unit_of_measurement=PRESSURE_HPA, + native_unit_of_measurement=UnitOfPressure.HPA, device_class=SensorDeviceClass.PRESSURE, state_class=SensorStateClass.MEASUREMENT, ), "FREQUENCY": SensorEntityDescription( key="FREQUENCY", - native_unit_of_measurement=FREQUENCY_HERTZ, + native_unit_of_measurement=UnitOfFrequency.HERTZ, + device_class=SensorDeviceClass.FREQUENCY, ), "VALUE": SensorEntityDescription( key="VALUE", @@ -307,7 +308,10 @@ def setup_platform( if (entity_desc := SENSOR_DESCRIPTIONS.get(state)) is None: name = conf.get(ATTR_NAME) _LOGGER.warning( - "Sensor (%s) entity description is missing. Sensor state (%s) needs to be maintained", + ( + "Sensor (%s) entity description is missing. Sensor state (%s) needs" + " to be maintained" + ), name, state, ) diff --git a/homeassistant/components/homematicip_cloud/alarm_control_panel.py b/homeassistant/components/homematicip_cloud/alarm_control_panel.py index 85d241051c1..1b29fcb1068 100644 --- a/homeassistant/components/homematicip_cloud/alarm_control_panel.py +++ b/homeassistant/components/homematicip_cloud/alarm_control_panel.py @@ -109,7 +109,10 @@ class HomematicipAlarmControlPanelEntity(AlarmControlPanelEntity): self.async_write_ha_state() else: _LOGGER.debug( - "Device Changed Event for %s (Alarm Control Panel) not fired. Entity is disabled", + ( + "Device Changed Event for %s (Alarm Control Panel) not fired." + " Entity is disabled" + ), self.name, ) diff --git a/homeassistant/components/homematicip_cloud/climate.py b/homeassistant/components/homematicip_cloud/climate.py index b6ac23b5c71..737ffaf8c19 100644 --- a/homeassistant/components/homematicip_cloud/climate.py +++ b/homeassistant/components/homematicip_cloud/climate.py @@ -24,7 +24,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -69,7 +69,7 @@ class HomematicipHeatingGroup(HomematicipGenericEntity, ClimateEntity): _attr_supported_features = ( ClimateEntityFeature.PRESET_MODE | ClimateEntityFeature.TARGET_TEMPERATURE ) - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS def __init__(self, hap: HomematicipHAP, device: AsyncHeatingGroup) -> None: """Initialize heating group.""" diff --git a/homeassistant/components/homematicip_cloud/cover.py b/homeassistant/components/homematicip_cloud/cover.py index 7038c423df0..e5007b5a15f 100644 --- a/homeassistant/components/homematicip_cloud/cover.py +++ b/homeassistant/components/homematicip_cloud/cover.py @@ -69,7 +69,7 @@ class HomematicipBlindModule(HomematicipGenericEntity, CoverEntity): """Representation of the HomematicIP blind module.""" @property - def device_class(self) -> str: + def device_class(self) -> CoverDeviceClass: """Return the class of the cover.""" return CoverDeviceClass.BLIND @@ -162,7 +162,7 @@ class HomematicipMultiCoverShutter(HomematicipGenericEntity, CoverEntity): ) @property - def device_class(self) -> str: + def device_class(self) -> CoverDeviceClass: """Return the class of the cover.""" return CoverDeviceClass.SHUTTER @@ -284,7 +284,7 @@ class HomematicipGarageDoorModule(HomematicipGenericEntity, CoverEntity): return door_state_to_position.get(self._device.doorState) @property - def device_class(self) -> str: + def device_class(self) -> CoverDeviceClass: """Return the class of the cover.""" return CoverDeviceClass.GARAGE @@ -315,7 +315,7 @@ class HomematicipCoverShutterGroup(HomematicipGenericEntity, CoverEntity): super().__init__(hap, device, post, is_multi_channel=False) @property - def device_class(self) -> str: + def device_class(self) -> CoverDeviceClass: """Return the class of the cover.""" return CoverDeviceClass.SHUTTER diff --git a/homeassistant/components/homematicip_cloud/hap.py b/homeassistant/components/homematicip_cloud/hap.py index 3960dc1f9b9..7ba30372451 100644 --- a/homeassistant/components/homematicip_cloud/hap.py +++ b/homeassistant/components/homematicip_cloud/hap.py @@ -190,8 +190,10 @@ class HomematicipHAP: await hmip_events except HmipConnectionError: _LOGGER.error( - "Error connecting to HomematicIP with HAP %s. " - "Retrying in %d seconds", + ( + "Error connecting to HomematicIP with HAP %s. " + "Retrying in %d seconds" + ), self.config_entry.unique_id, retry_delay, ) diff --git a/homeassistant/components/homematicip_cloud/light.py b/homeassistant/components/homematicip_cloud/light.py index 09a5cd7ec34..4ad418bc8ad 100644 --- a/homeassistant/components/homematicip_cloud/light.py +++ b/homeassistant/components/homematicip_cloud/light.py @@ -8,6 +8,7 @@ from homematicip.aio.device import ( AsyncBrandSwitchMeasuring, AsyncBrandSwitchNotificationLight, AsyncDimmer, + AsyncDinRailDimmer3, AsyncFullFlushDimmer, AsyncPluggableDimmer, AsyncWiredDimmer3, @@ -53,7 +54,7 @@ async def async_setup_entry( hap, device, device.bottomLightChannelIndex ) ) - elif isinstance(device, AsyncWiredDimmer3): + elif isinstance(device, (AsyncWiredDimmer3, AsyncDinRailDimmer3)): for channel in range(1, 4): entities.append(HomematicipMultiDimmer(hap, device, channel=channel)) elif isinstance( diff --git a/homeassistant/components/homematicip_cloud/manifest.json b/homeassistant/components/homematicip_cloud/manifest.json index d8d0b3e9836..073d54fcf1e 100644 --- a/homeassistant/components/homematicip_cloud/manifest.json +++ b/homeassistant/components/homematicip_cloud/manifest.json @@ -3,7 +3,7 @@ "name": "HomematicIP Cloud", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/homematicip_cloud", - "requirements": ["homematicip==1.0.11"], + "requirements": ["homematicip==1.0.13"], "codeowners": [], "quality_scale": "platinum", "iot_class": "cloud_push", diff --git a/homeassistant/components/homematicip_cloud/sensor.py b/homeassistant/components/homematicip_cloud/sensor.py index fdf125dbfec..b4eb89f06c8 100644 --- a/homeassistant/components/homematicip_cloud/sensor.py +++ b/homeassistant/components/homematicip_cloud/sensor.py @@ -35,13 +35,13 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - ENERGY_KILO_WATT_HOUR, - LENGTH_MILLIMETERS, LIGHT_LUX, PERCENTAGE, - POWER_WATT, - SPEED_KILOMETERS_PER_HOUR, - TEMP_CELSIUS, + UnitOfEnergy, + UnitOfPower, + UnitOfPrecipitationDepth, + UnitOfSpeed, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -144,31 +144,25 @@ async def async_setup_entry( class HomematicipAccesspointDutyCycle(HomematicipGenericEntity, SensorEntity): """Representation of then HomeMaticIP access point.""" + _attr_icon = "mdi:access-point-network" + _attr_native_unit_of_measurement = PERCENTAGE _attr_state_class = SensorStateClass.MEASUREMENT def __init__(self, hap: HomematicipHAP, device) -> None: """Initialize access point status entity.""" super().__init__(hap, device, post="Duty Cycle") - @property - def icon(self) -> str: - """Return the icon of the access point entity.""" - return "mdi:access-point-network" - @property def native_value(self) -> float: """Return the state of the access point.""" return self._device.dutyCycleLevel - @property - def native_unit_of_measurement(self) -> str: - """Return the unit this state is expressed in.""" - return PERCENTAGE - class HomematicipHeatingThermostat(HomematicipGenericEntity, SensorEntity): """Representation of the HomematicIP heating thermostat.""" + _attr_native_unit_of_measurement = PERCENTAGE + def __init__(self, hap: HomematicipHAP, device) -> None: """Initialize heating thermostat device.""" super().__init__(hap, device, post="Heating") @@ -189,51 +183,35 @@ class HomematicipHeatingThermostat(HomematicipGenericEntity, SensorEntity): return self._device.valveState return round(self._device.valvePosition * 100) - @property - def native_unit_of_measurement(self) -> str: - """Return the unit this state is expressed in.""" - return PERCENTAGE - class HomematicipHumiditySensor(HomematicipGenericEntity, SensorEntity): """Representation of the HomematicIP humidity sensor.""" + _attr_device_class = SensorDeviceClass.HUMIDITY + _attr_native_unit_of_measurement = PERCENTAGE _attr_state_class = SensorStateClass.MEASUREMENT def __init__(self, hap: HomematicipHAP, device) -> None: """Initialize the thermometer device.""" super().__init__(hap, device, post="Humidity") - @property - def device_class(self) -> SensorDeviceClass: - """Return the device class of the sensor.""" - return SensorDeviceClass.HUMIDITY - @property def native_value(self) -> int: """Return the state.""" return self._device.humidity - @property - def native_unit_of_measurement(self) -> str: - """Return the unit this state is expressed in.""" - return PERCENTAGE - class HomematicipTemperatureSensor(HomematicipGenericEntity, SensorEntity): """Representation of the HomematicIP thermometer.""" + _attr_device_class = SensorDeviceClass.TEMPERATURE + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS _attr_state_class = SensorStateClass.MEASUREMENT def __init__(self, hap: HomematicipHAP, device) -> None: """Initialize the thermometer device.""" super().__init__(hap, device, post="Temperature") - @property - def device_class(self) -> SensorDeviceClass: - """Return the device class of the sensor.""" - return SensorDeviceClass.TEMPERATURE - @property def native_value(self) -> float: """Return the state.""" @@ -242,11 +220,6 @@ class HomematicipTemperatureSensor(HomematicipGenericEntity, SensorEntity): return self._device.actualTemperature - @property - def native_unit_of_measurement(self) -> str: - """Return the unit this state is expressed in.""" - return TEMP_CELSIUS - @property def extra_state_attributes(self) -> dict[str, Any]: """Return the state attributes of the windspeed sensor.""" @@ -262,17 +235,14 @@ class HomematicipTemperatureSensor(HomematicipGenericEntity, SensorEntity): class HomematicipIlluminanceSensor(HomematicipGenericEntity, SensorEntity): """Representation of the HomematicIP Illuminance sensor.""" + _attr_device_class = SensorDeviceClass.ILLUMINANCE + _attr_native_unit_of_measurement = LIGHT_LUX _attr_state_class = SensorStateClass.MEASUREMENT def __init__(self, hap: HomematicipHAP, device) -> None: """Initialize the device.""" super().__init__(hap, device, post="Illuminance") - @property - def device_class(self) -> SensorDeviceClass: - """Return the device class of the sensor.""" - return SensorDeviceClass.ILLUMINANCE - @property def native_value(self) -> float: """Return the state.""" @@ -281,11 +251,6 @@ class HomematicipIlluminanceSensor(HomematicipGenericEntity, SensorEntity): return self._device.illumination - @property - def native_unit_of_measurement(self) -> str: - """Return the unit this state is expressed in.""" - return LIGHT_LUX - @property def extra_state_attributes(self) -> dict[str, Any]: """Return the state attributes of the wind speed sensor.""" @@ -301,57 +266,42 @@ class HomematicipIlluminanceSensor(HomematicipGenericEntity, SensorEntity): class HomematicipPowerSensor(HomematicipGenericEntity, SensorEntity): """Representation of the HomematicIP power measuring sensor.""" + _attr_device_class = SensorDeviceClass.POWER + _attr_native_unit_of_measurement = UnitOfPower.WATT _attr_state_class = SensorStateClass.MEASUREMENT def __init__(self, hap: HomematicipHAP, device) -> None: """Initialize the device.""" super().__init__(hap, device, post="Power") - @property - def device_class(self) -> SensorDeviceClass: - """Return the device class of the sensor.""" - return SensorDeviceClass.POWER - @property def native_value(self) -> float: """Return the power consumption value.""" return self._device.currentPowerConsumption - @property - def native_unit_of_measurement(self) -> str: - """Return the unit this state is expressed in.""" - return POWER_WATT - class HomematicipEnergySensor(HomematicipGenericEntity, SensorEntity): """Representation of the HomematicIP energy measuring sensor.""" + _attr_device_class = SensorDeviceClass.ENERGY + _attr_native_unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR _attr_state_class = SensorStateClass.TOTAL_INCREASING def __init__(self, hap: HomematicipHAP, device) -> None: """Initialize the device.""" super().__init__(hap, device, post="Energy") - @property - def device_class(self) -> SensorDeviceClass: - """Return the device class of the sensor.""" - return SensorDeviceClass.ENERGY - @property def native_value(self) -> float: """Return the energy counter value.""" return self._device.energyCounter - @property - def native_unit_of_measurement(self) -> str: - """Return the unit this state is expressed in.""" - return ENERGY_KILO_WATT_HOUR - class HomematicipWindspeedSensor(HomematicipGenericEntity, SensorEntity): """Representation of the HomematicIP wind speed sensor.""" - _attr_device_class = SensorDeviceClass.SPEED + _attr_device_class = SensorDeviceClass.WIND_SPEED + _attr_native_unit_of_measurement = UnitOfSpeed.KILOMETERS_PER_HOUR def __init__(self, hap: HomematicipHAP, device) -> None: """Initialize the windspeed sensor.""" @@ -362,11 +312,6 @@ class HomematicipWindspeedSensor(HomematicipGenericEntity, SensorEntity): """Return the wind speed value.""" return self._device.windSpeed - @property - def native_unit_of_measurement(self) -> str: - """Return the unit this state is expressed in.""" - return SPEED_KILOMETERS_PER_HOUR - @property def extra_state_attributes(self) -> dict[str, Any]: """Return the state attributes of the wind speed sensor.""" @@ -386,6 +331,9 @@ class HomematicipWindspeedSensor(HomematicipGenericEntity, SensorEntity): class HomematicipTodayRainSensor(HomematicipGenericEntity, SensorEntity): """Representation of the HomematicIP rain counter of a day sensor.""" + _attr_device_class = SensorDeviceClass.PRECIPITATION + _attr_native_unit_of_measurement = UnitOfPrecipitationDepth.MILLIMETERS + def __init__(self, hap: HomematicipHAP, device) -> None: """Initialize the device.""" super().__init__(hap, device, post="Today Rain") @@ -395,86 +343,57 @@ class HomematicipTodayRainSensor(HomematicipGenericEntity, SensorEntity): """Return the today's rain value.""" return round(self._device.todayRainCounter, 2) - @property - def native_unit_of_measurement(self) -> str: - """Return the unit this state is expressed in.""" - return LENGTH_MILLIMETERS - class HomematicpTemperatureExternalSensorCh1(HomematicipGenericEntity, SensorEntity): """Representation of the HomematicIP device HmIP-STE2-PCB.""" + _attr_device_class = SensorDeviceClass.TEMPERATURE + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS _attr_state_class = SensorStateClass.MEASUREMENT def __init__(self, hap: HomematicipHAP, device) -> None: """Initialize the device.""" super().__init__(hap, device, post="Channel 1 Temperature") - @property - def device_class(self) -> SensorDeviceClass: - """Return the device class of the sensor.""" - return SensorDeviceClass.TEMPERATURE - @property def native_value(self) -> float: """Return the state.""" return self._device.temperatureExternalOne - @property - def native_unit_of_measurement(self) -> str: - """Return the unit this state is expressed in.""" - return TEMP_CELSIUS - class HomematicpTemperatureExternalSensorCh2(HomematicipGenericEntity, SensorEntity): """Representation of the HomematicIP device HmIP-STE2-PCB.""" + _attr_device_class = SensorDeviceClass.TEMPERATURE + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS _attr_state_class = SensorStateClass.MEASUREMENT def __init__(self, hap: HomematicipHAP, device) -> None: """Initialize the device.""" super().__init__(hap, device, post="Channel 2 Temperature") - @property - def device_class(self) -> SensorDeviceClass: - """Return the device class of the sensor.""" - return SensorDeviceClass.TEMPERATURE - @property def native_value(self) -> float: """Return the state.""" return self._device.temperatureExternalTwo - @property - def native_unit_of_measurement(self) -> str: - """Return the unit this state is expressed in.""" - return TEMP_CELSIUS - class HomematicpTemperatureExternalSensorDelta(HomematicipGenericEntity, SensorEntity): """Representation of the HomematicIP device HmIP-STE2-PCB.""" + _attr_device_class = SensorDeviceClass.TEMPERATURE + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS _attr_state_class = SensorStateClass.MEASUREMENT def __init__(self, hap: HomematicipHAP, device) -> None: """Initialize the device.""" super().__init__(hap, device, post="Delta Temperature") - @property - def device_class(self) -> SensorDeviceClass: - """Return the device class of the sensor.""" - return SensorDeviceClass.TEMPERATURE - @property def native_value(self) -> float: """Return the state.""" return self._device.temperatureExternalDelta - @property - def native_unit_of_measurement(self) -> str: - """Return the unit this state is expressed in.""" - return TEMP_CELSIUS - class HomematicipPassageDetectorDeltaCounter(HomematicipGenericEntity, SensorEntity): """Representation of the HomematicIP passage detector delta counter.""" diff --git a/homeassistant/components/homematicip_cloud/translations/lb.json b/homeassistant/components/homematicip_cloud/translations/lb.json index ec83f988b07..6706bf35cae 100644 --- a/homeassistant/components/homematicip_cloud/translations/lb.json +++ b/homeassistant/components/homematicip_cloud/translations/lb.json @@ -6,7 +6,7 @@ "unknown": "Onerwaarte Feeler" }, "error": { - "invalid_sgtin_or_pin": "Ong\u00ebltege SGTIN oder PIN Code, prob\u00e9ier w.e.g. nach emol.", + "invalid_sgtin_or_pin": "Ong\u00ebltege SGTIN oder PIN-Code, prob\u00e9ier w.e.g. nach emol.", "press_the_button": "Dr\u00e9ckt w.e.g. de bloe Kn\u00e4ppchen.", "register_failed": "Feeler beim registr\u00e9ieren, prob\u00e9iert w.e.g. nach emol.", "timeout_button": "Z\u00e4itiwwerschreidung beim dr\u00e9cken vum bloe Kn\u00e4ppchen, prob\u00e9iert w.e.g. nach emol." @@ -16,7 +16,7 @@ "data": { "hapid": "ID vum Accesspoint (SGTIN)", "name": "Numm (optionell, g\u00ebtt als prefixe fir all Apparat benotzt)", - "pin": "Pin Code" + "pin": "PIN-Code" }, "title": "HomematicIP Accesspoint auswielen" }, diff --git a/homeassistant/components/homematicip_cloud/translations/sk.json b/homeassistant/components/homematicip_cloud/translations/sk.json index 209e87329e3..1794d30814c 100644 --- a/homeassistant/components/homematicip_cloud/translations/sk.json +++ b/homeassistant/components/homematicip_cloud/translations/sk.json @@ -6,8 +6,10 @@ "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "error": { + "invalid_sgtin_or_pin": "Neplatn\u00e9 SGTIN alebo PIN k\u00f3d , sk\u00faste to znova.", "press_the_button": "Stla\u010dte pros\u00edm modr\u00e9 tla\u010didlo.", - "register_failed": "Registr\u00e1cia zlyhala, sk\u00faste to znova." + "register_failed": "Registr\u00e1cia zlyhala, sk\u00faste to znova.", + "timeout_button": "\u010casov\u00fd limit stla\u010denia modr\u00e9ho tla\u010didla vypr\u0161al, sk\u00faste to znova." }, "step": { "init": { @@ -15,7 +17,12 @@ "hapid": "ID pr\u00edstupov\u00e9ho bodu (SGTIN)", "name": "N\u00e1zov (volite\u013en\u00e9, pou\u017e\u00edva sa ako predpona pre v\u0161etky zariadenia)", "pin": "PIN k\u00f3d" - } + }, + "title": "Vyberte pr\u00edstupov\u00fd bod HomematicIP" + }, + "link": { + "description": "Stla\u010den\u00edm modr\u00e9ho tla\u010didla na pr\u00edstupovom bode a tla\u010didla odoslania zaregistrujte HomematicIP s Home Assistant. \n\n ![Umiestnenie tla\u010didla na bridge](/static/images/config_flows/config_homematicip_cloud.png)", + "title": "Link Pr\u00edstupov\u00fd bod" } } } diff --git a/homeassistant/components/homewizard/__init__.py b/homeassistant/components/homewizard/__init__.py index a236c392c07..df58ccccd30 100644 --- a/homeassistant/components/homewizard/__init__.py +++ b/homeassistant/components/homewizard/__init__.py @@ -1,7 +1,7 @@ """The Homewizard integration.""" import logging -from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry +from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_REAUTH, ConfigEntry from homeassistant.const import CONF_IP_ADDRESS from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady @@ -64,13 +64,28 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass.async_create_task(hass.config_entries.async_remove(old_config_entry_id)) # Create coordinator - coordinator = Coordinator(hass, entry.data[CONF_IP_ADDRESS]) + coordinator = Coordinator(hass, entry.entry_id, entry.data[CONF_IP_ADDRESS]) try: await coordinator.async_config_entry_first_refresh() + except ConfigEntryNotReady: + await coordinator.api.close() + + if coordinator.api_disabled: + entry.async_start_reauth(hass) + raise + # Abort reauth config flow if active + for progress_flow in hass.config_entries.flow.async_progress_by_handler(DOMAIN): + if progress_flow["context"].get("source") == SOURCE_REAUTH: + hass.config_entries.flow.async_abort(progress_flow["flow_id"]) + + # Setup entry + hass.data.setdefault(DOMAIN, {}) + hass.data[DOMAIN][entry.entry_id] = coordinator + # Register device device_registry = dr.async_get(hass) device_registry.async_get_or_create( @@ -83,9 +98,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: ) # Finalize - hass.data.setdefault(DOMAIN, {}) - hass.data[DOMAIN][entry.entry_id] = coordinator - await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) return True @@ -98,7 +110,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) if unload_ok: - config_data = hass.data[DOMAIN].pop(entry.entry_id) - await config_data.api.close() + coordinator = hass.data[DOMAIN].pop(entry.entry_id) + await coordinator.api.close() return unload_ok diff --git a/homeassistant/components/homewizard/config_flow.py b/homeassistant/components/homewizard/config_flow.py index 6f164637a7c..bdd84c0d07e 100644 --- a/homeassistant/components/homewizard/config_flow.py +++ b/homeassistant/components/homewizard/config_flow.py @@ -1,6 +1,7 @@ """Config flow for Homewizard.""" from __future__ import annotations +from collections.abc import Mapping import logging from typing import Any, cast @@ -33,6 +34,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): def __init__(self) -> None: """Initialize the HomeWizard config flow.""" self.config: dict[str, str | int] = {} + self.entry: config_entries.ConfigEntry | None = None async def async_step_import(self, import_config: dict[str, Any]) -> FlowResult: """Handle a flow initiated by older `homewizard_energy` component.""" @@ -42,8 +44,9 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): self.hass, title="HomeWizard Energy", message=( - "The custom integration of HomeWizard Energy has been migrated to core. " - "You can safely remove the custom integration from the custom_integrations folder." + "The custom integration of HomeWizard Energy has been migrated to core." + " You can safely remove the custom integration from the" + " custom_integrations folder." ), notification_id=f"homewizard_energy_to_{DOMAIN}", ) @@ -57,27 +60,38 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): _LOGGER.debug("config_flow async_step_user") + data_schema = Schema( + { + Required(CONF_IP_ADDRESS): str, + } + ) + if user_input is None: return self.async_show_form( step_id="user", - data_schema=Schema( - { - Required(CONF_IP_ADDRESS): str, - } - ), + data_schema=data_schema, errors=None, ) - device_info = await self._async_try_connect_and_fetch( - user_input[CONF_IP_ADDRESS] - ) + error = await self._async_try_connect(user_input[CONF_IP_ADDRESS]) + if error is not None: + return self.async_show_form( + step_id="user", + data_schema=data_schema, + errors={"base": error}, + ) + + # Fetch device information + api = HomeWizardEnergy(user_input[CONF_IP_ADDRESS]) + device_info = await api.device() + await api.close() # Sets unique ID and aborts if it is already exists await self._async_set_and_check_unique_id( { CONF_IP_ADDRESS: user_input[CONF_IP_ADDRESS], - CONF_PRODUCT_TYPE: device_info[CONF_PRODUCT_TYPE], - CONF_SERIAL: device_info[CONF_SERIAL], + CONF_PRODUCT_TYPE: device_info.product_type, + CONF_SERIAL: device_info.serial, } ) @@ -90,7 +104,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): # Add entry return self.async_create_entry( - title=f"{device_info[CONF_PRODUCT_NAME]} ({device_info[CONF_SERIAL]})", + title=f"{device_info.product_name} ({device_info.serial})", data=data, ) @@ -138,11 +152,14 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): ) -> FlowResult: """Confirm discovery.""" if user_input is not None: - if (self.config[CONF_API_ENABLED]) != "1": - raise AbortFlow(reason="api_not_enabled") # Check connection - await self._async_try_connect_and_fetch(str(self.config[CONF_IP_ADDRESS])) + error = await self._async_try_connect(str(self.config[CONF_IP_ADDRESS])) + if error is not None: + return self.async_show_form( + step_id="discovery_confirm", + errors={"base": error}, + ) return self.async_create_entry( title=f"{self.config[CONF_PRODUCT_NAME]} ({self.config[CONF_SERIAL]})", @@ -166,46 +183,67 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): }, ) + async def async_step_reauth(self, entry_data: Mapping[str, Any]) -> FlowResult: + """Handle re-auth if API was disabled.""" + + self.entry = self.hass.config_entries.async_get_entry(self.context["entry_id"]) + return await self.async_step_reauth_confirm() + + async def async_step_reauth_confirm( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Confirm reauth dialog.""" + + if user_input is not None: + assert self.entry is not None + + error = await self._async_try_connect(self.entry.data[CONF_IP_ADDRESS]) + if error is not None: + return self.async_show_form( + step_id="reauth_confirm", + errors={"base": error}, + ) + + await self.hass.config_entries.async_reload(self.entry.entry_id) + return self.async_abort(reason="reauth_successful") + + return self.async_show_form( + step_id="reauth_confirm", + ) + @staticmethod - async def _async_try_connect_and_fetch(ip_address: str) -> dict[str, Any]: + async def _async_try_connect(ip_address: str) -> str | None: """Try to connect.""" - _LOGGER.debug("config_flow _async_try_connect_and_fetch") + _LOGGER.debug("config_flow _async_try_connect") # Make connection with device # This is to test the connection and to get info for unique_id energy_api = HomeWizardEnergy(ip_address) try: - device = await energy_api.device() + await energy_api.device() - except DisabledError as ex: + except DisabledError: _LOGGER.error("API disabled, API must be enabled in the app") - raise AbortFlow("api_not_enabled") from ex + return "api_not_enabled" except UnsupportedError as ex: _LOGGER.error("API version unsuppored") raise AbortFlow("unsupported_api_version") from ex except RequestError as ex: - _LOGGER.error("Unexpected or no response") - raise AbortFlow("unknown_error") from ex + _LOGGER.exception(ex) + return "network_error" except Exception as ex: - _LOGGER.exception( - "Error connecting with Energy Device at %s", - ip_address, - ) + _LOGGER.exception(ex) raise AbortFlow("unknown_error") from ex finally: await energy_api.close() - return { - CONF_PRODUCT_NAME: device.product_name, - CONF_PRODUCT_TYPE: device.product_type, - CONF_SERIAL: device.serial, - } + return None async def _async_set_and_check_unique_id(self, entry_info: dict[str, Any]) -> None: """Validate if entry exists.""" diff --git a/homeassistant/components/homewizard/coordinator.py b/homeassistant/components/homewizard/coordinator.py index dc441836d9a..d8c20a6cc92 100644 --- a/homeassistant/components/homewizard/coordinator.py +++ b/homeassistant/components/homewizard/coordinator.py @@ -1,6 +1,7 @@ """Update coordinator for HomeWizard.""" from __future__ import annotations +from datetime import timedelta import logging from homewizard_energy import HomeWizardEnergy @@ -15,20 +16,25 @@ from .const import DOMAIN, UPDATE_INTERVAL, DeviceResponseEntry _LOGGER = logging.getLogger(__name__) +MAX_UPDATE_INTERVAL = timedelta(minutes=30) + class HWEnergyDeviceUpdateCoordinator(DataUpdateCoordinator[DeviceResponseEntry]): """Gather data for the energy device.""" api: HomeWizardEnergy + api_disabled: bool = False def __init__( self, hass: HomeAssistant, + entry_id: str, host: str, ) -> None: """Initialize Update Coordinator.""" super().__init__(hass, _LOGGER, name=DOMAIN, update_interval=UPDATE_INTERVAL) + self.entry_id = entry_id self.api = HomeWizardEnergy(host, clientsession=async_get_clientsession(hass)) @property @@ -55,9 +61,19 @@ class HWEnergyDeviceUpdateCoordinator(DataUpdateCoordinator[DeviceResponseEntry] data["system"] = await self.api.system() except RequestError as ex: - raise UpdateFailed("Device did not respond as expected") from ex + raise UpdateFailed(ex) from ex except DisabledError as ex: - raise UpdateFailed("API disabled, API must be enabled in the app") from ex + if not self.api_disabled: + self.api_disabled = True + + # Do not reload when performing first refresh + if self.data is not None: + await self.hass.config_entries.async_reload(self.entry_id) + + raise UpdateFailed(ex) from ex + + else: + self.api_disabled = False return data diff --git a/homeassistant/components/homewizard/sensor.py b/homeassistant/components/homewizard/sensor.py index 493b5f1c0e3..edd6a40a7ef 100644 --- a/homeassistant/components/homewizard/sensor.py +++ b/homeassistant/components/homewizard/sensor.py @@ -1,7 +1,6 @@ """Creates Homewizard sensor entities.""" from __future__ import annotations -import logging from typing import Final, cast from homeassistant.components.sensor import ( @@ -11,12 +10,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - ENERGY_KILO_WATT_HOUR, - PERCENTAGE, - POWER_WATT, - VOLUME_CUBIC_METERS, -) +from homeassistant.const import PERCENTAGE, UnitOfEnergy, UnitOfPower, UnitOfVolume from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -26,7 +20,7 @@ from homeassistant.helpers.update_coordinator import CoordinatorEntity from .const import DOMAIN, DeviceResponseEntry from .coordinator import HWEnergyDeviceUpdateCoordinator -_LOGGER = logging.getLogger(__name__) +PARALLEL_UPDATES = 1 SENSORS: Final[tuple[SensorEntityDescription, ...]] = ( SensorEntityDescription( @@ -59,63 +53,63 @@ SENSORS: Final[tuple[SensorEntityDescription, ...]] = ( SensorEntityDescription( key="total_power_import_t1_kwh", name="Total power import T1", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), SensorEntityDescription( key="total_power_import_t2_kwh", name="Total power import T2", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), SensorEntityDescription( key="total_power_export_t1_kwh", name="Total power export T1", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), SensorEntityDescription( key="total_power_export_t2_kwh", name="Total power export T2", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), SensorEntityDescription( key="active_power_w", name="Active power", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="active_power_l1_w", name="Active power L1", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="active_power_l2_w", name="Active power L2", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="active_power_l3_w", name="Active power L3", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="total_gas_m3", name="Total gas", - native_unit_of_measurement=VOLUME_CUBIC_METERS, + native_unit_of_measurement=UnitOfVolume.CUBIC_METERS, device_class=SensorDeviceClass.GAS, state_class=SensorStateClass.TOTAL_INCREASING, ), @@ -129,7 +123,7 @@ SENSORS: Final[tuple[SensorEntityDescription, ...]] = ( SensorEntityDescription( key="total_liter_m3", name="Total water usage", - native_unit_of_measurement=VOLUME_CUBIC_METERS, + native_unit_of_measurement=UnitOfVolume.CUBIC_METERS, icon="mdi:gauge", device_class=SensorDeviceClass.WATER, state_class=SensorStateClass.TOTAL_INCREASING, diff --git a/homeassistant/components/homewizard/strings.json b/homeassistant/components/homewizard/strings.json index b34c6906cc1..53eeafe99e1 100644 --- a/homeassistant/components/homewizard/strings.json +++ b/homeassistant/components/homewizard/strings.json @@ -10,15 +10,22 @@ }, "discovery_confirm": { "title": "Confirm", - "description": "Do you want to setup {product_type} ({serial}) at {ip_address}?" + "description": "Do you want to set up {product_type} ({serial}) at {ip_address}?" + }, + "reauth_confirm": { + "description": "The local API is disabled. Go to the HomeWizard Energy app and enable the API in the device settings." } }, + "error": { + "api_not_enabled": "The API is not enabled. Enable API in the HomeWizard Energy App under settings", + "network_error": "Device unreachable, make sure that you have entered the correct IP address and that the device is available in your network" + }, "abort": { "already_configured": "[%key:common::config_flow::abort::already_configured_device%]", "invalid_discovery_parameters": "Detected unsupported API version", - "api_not_enabled": "The API is not enabled. Enable API in the HomeWizard Energy App under settings", "device_not_supported": "This device is not supported", - "unknown_error": "[%key:common::config_flow::error::unknown%]" + "unknown_error": "[%key:common::config_flow::error::unknown%]", + "reauth_successful": "Enabling API was successful" } } } diff --git a/homeassistant/components/homewizard/translations/bg.json b/homeassistant/components/homewizard/translations/bg.json index dedf6ca570b..753e437e302 100644 --- a/homeassistant/components/homewizard/translations/bg.json +++ b/homeassistant/components/homewizard/translations/bg.json @@ -4,6 +4,7 @@ "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", "device_not_supported": "\u0422\u043e\u0432\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043d\u0435 \u0441\u0435 \u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430", "invalid_discovery_parameters": "\u041e\u0442\u043a\u0440\u0438\u0442\u0430 \u0435 \u043d\u0435\u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430\u043d\u0430 \u0432\u0435\u0440\u0441\u0438\u044f \u043d\u0430 API", + "reauth_successful": "\u0410\u043a\u0442\u0438\u0432\u0438\u0440\u0430\u043d\u0435\u0442\u043e \u043d\u0430 API \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e", "unknown_error": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, "step": { diff --git a/homeassistant/components/homewizard/translations/ca.json b/homeassistant/components/homewizard/translations/ca.json index 4e855e3bc07..2cf2933a77b 100644 --- a/homeassistant/components/homewizard/translations/ca.json +++ b/homeassistant/components/homewizard/translations/ca.json @@ -5,13 +5,21 @@ "api_not_enabled": "L'API no est\u00e0 activada. Activa-la a la configuraci\u00f3 de l'aplicaci\u00f3 HomeWizard Energy", "device_not_supported": "Aquest dispositiu no \u00e9s compatible", "invalid_discovery_parameters": "Versi\u00f3 d'API no compatible detectada", + "reauth_successful": "S'ha activat l'API correctament", "unknown_error": "Error inesperat" }, + "error": { + "api_not_enabled": "L'API no est\u00e0 activada. Activa-la a la configuraci\u00f3 de l'aplicaci\u00f3 HomeWizard Energy", + "network_error": "Dispositiu inaccessible, assegura't que has introdu\u00eft l'adre\u00e7a IP correcta i que el dispositiu est\u00e0 disponible a la xarxa" + }, "step": { "discovery_confirm": { "description": "Vols configurar {product_type} ({serial}) a {ip_address}?", "title": "Confirmaci\u00f3" }, + "reauth_confirm": { + "description": "L'API local no est\u00e0 activada. V\u00e9s l'aplicaci\u00f3 HomeWizard Energy i activa l'API a la configuraci\u00f3 de dispositiu." + }, "user": { "data": { "ip_address": "Adre\u00e7a IP" diff --git a/homeassistant/components/homewizard/translations/de.json b/homeassistant/components/homewizard/translations/de.json index f9dc268f99b..56065e8e498 100644 --- a/homeassistant/components/homewizard/translations/de.json +++ b/homeassistant/components/homewizard/translations/de.json @@ -5,13 +5,21 @@ "api_not_enabled": "Die API ist nicht aktiviert. Aktiviere die API in der HomeWizard Energy App unter Einstellungen", "device_not_supported": "Dieses Ger\u00e4t wird nicht unterst\u00fctzt", "invalid_discovery_parameters": "Nicht unterst\u00fctzte API-Version erkannt", + "reauth_successful": "Das Aktivieren der API war erfolgreich", "unknown_error": "Unerwarteter Fehler" }, + "error": { + "api_not_enabled": "Die API ist nicht aktiviert. Aktiviere die API in der HomeWizard Energy App unter Einstellungen", + "network_error": "Ger\u00e4t nicht erreichbar, vergewissere dich, dass du die richtige IP-Adresse eingegeben hast und dass das Ger\u00e4t in deinem Netzwerk verf\u00fcgbar ist" + }, "step": { "discovery_confirm": { "description": "M\u00f6chtest du {product_type} ({serial}) unter {ip_address} einrichten?", "title": "Best\u00e4tigen" }, + "reauth_confirm": { + "description": "Die lokale API ist deaktiviert. Gehe zur HomeWizard Energy App und aktiviere die API in den Ger\u00e4teeinstellungen." + }, "user": { "data": { "ip_address": "IP-Adresse" diff --git a/homeassistant/components/homewizard/translations/el.json b/homeassistant/components/homewizard/translations/el.json index dd85c5e87f5..f546a0974f4 100644 --- a/homeassistant/components/homewizard/translations/el.json +++ b/homeassistant/components/homewizard/translations/el.json @@ -5,13 +5,21 @@ "api_not_enabled": "\u03a4\u03bf API \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03bf. \u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf API \u03c3\u03c4\u03b7\u03bd \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae HomeWizard Energy App \u03c3\u03c4\u03b9\u03c2 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2", "device_not_supported": "\u0391\u03c5\u03c4\u03ae \u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9", "invalid_discovery_parameters": "\u0395\u03bd\u03c4\u03bf\u03c0\u03af\u03c3\u03c4\u03b7\u03ba\u03b5 \u03bc\u03b7 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03b7 \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7 API", + "reauth_successful": "\u0397 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 API \u03ae\u03c4\u03b1\u03bd \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2", "unknown_error": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" }, + "error": { + "api_not_enabled": "\u03a4\u03bf API \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03bf. \u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf API \u03c3\u03c4\u03b7\u03bd \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae HomeWizard Energy \u03c3\u03c4\u03b9\u03c2 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2", + "network_error": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03c0\u03c1\u03bf\u03c3\u03b2\u03ac\u03c3\u03b9\u03bc\u03b7, \u03b2\u03b5\u03b2\u03b1\u03b9\u03c9\u03b8\u03b5\u03af\u03c4\u03b5 \u03cc\u03c4\u03b9 \u03ad\u03c7\u03b5\u03c4\u03b5 \u03b5\u03b9\u03c3\u03b1\u03b3\u03ac\u03b3\u03b5\u03b9 \u03c4\u03b7 \u03c3\u03c9\u03c3\u03c4\u03ae \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP \u03ba\u03b1\u03b9 \u03cc\u03c4\u03b9 \u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03b9\u03b1\u03b8\u03ad\u03c3\u03b9\u03bc\u03b7 \u03c3\u03c4\u03bf \u03b4\u03af\u03ba\u03c4\u03c5\u03cc \u03c3\u03b1\u03c2" + }, "step": { "discovery_confirm": { "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf {product_type} ({serial}) \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 {ip_address};", "title": "\u0395\u03c0\u03b9\u03b2\u03b5\u03b2\u03b1\u03af\u03c9\u03c3\u03b7" }, + "reauth_confirm": { + "description": "\u03a4\u03bf \u03c4\u03bf\u03c0\u03b9\u03ba\u03cc API \u03b5\u03af\u03bd\u03b1\u03b9 \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03bf. \u039c\u03b5\u03c4\u03b1\u03b2\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03b7\u03bd \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae HomeWizard Energy \u03ba\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf API \u03c3\u03c4\u03b9\u03c2 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2 \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2." + }, "user": { "data": { "ip_address": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP" diff --git a/homeassistant/components/homewizard/translations/en.json b/homeassistant/components/homewizard/translations/en.json index b2ef3c16b1e..58645b03249 100644 --- a/homeassistant/components/homewizard/translations/en.json +++ b/homeassistant/components/homewizard/translations/en.json @@ -5,13 +5,21 @@ "api_not_enabled": "The API is not enabled. Enable API in the HomeWizard Energy App under settings", "device_not_supported": "This device is not supported", "invalid_discovery_parameters": "Detected unsupported API version", + "reauth_successful": "Enabling API was successful", "unknown_error": "Unexpected error" }, + "error": { + "api_not_enabled": "The API is not enabled. Enable API in the HomeWizard Energy App under settings", + "network_error": "Device unreachable, make sure that you have entered the correct IP address and that the device is available in your network" + }, "step": { "discovery_confirm": { - "description": "Do you want to setup {product_type} ({serial}) at {ip_address}?", + "description": "Do you want to set up {product_type} ({serial}) at {ip_address}?", "title": "Confirm" }, + "reauth_confirm": { + "description": "The local API is disabled. Go to the HomeWizard Energy app and enable the API in the device settings." + }, "user": { "data": { "ip_address": "IP Address" diff --git a/homeassistant/components/homewizard/translations/es.json b/homeassistant/components/homewizard/translations/es.json index c2fe80926da..98d12d7612b 100644 --- a/homeassistant/components/homewizard/translations/es.json +++ b/homeassistant/components/homewizard/translations/es.json @@ -5,13 +5,21 @@ "api_not_enabled": "La API no est\u00e1 habilitada. Habilita la API en la aplicaci\u00f3n HomeWizard Energy dentro de Configuraci\u00f3n", "device_not_supported": "Este dispositivo no es compatible", "invalid_discovery_parameters": "Se ha detectado una versi\u00f3n de API no compatible", + "reauth_successful": "La API se ha habilitado correctamente", "unknown_error": "Error inesperado" }, + "error": { + "api_not_enabled": "La API no est\u00e1 habilitada. Habilita la API en la aplicaci\u00f3n HomeWizard Energy dentro de Configuraci\u00f3n", + "network_error": "Dispositivo inaccesible, aseg\u00farate de haber introducido la direcci\u00f3n IP correcta y que el dispositivo est\u00e9 disponible en tu red" + }, "step": { "discovery_confirm": { - "description": "\u00bfQuieres configurar {product_type} ({serial}) en {ip_address} ?", + "description": "\u00bfQuieres configurar {product_type} ({serial}) en {ip_address}?", "title": "Confirmar" }, + "reauth_confirm": { + "description": "La API local est\u00e1 deshabilitada. Ve a la aplicaci\u00f3n HomeWizard Energy y habilita la API en la configuraci\u00f3n del dispositivo." + }, "user": { "data": { "ip_address": "Direcci\u00f3n IP" diff --git a/homeassistant/components/homewizard/translations/et.json b/homeassistant/components/homewizard/translations/et.json index 360010b4d57..1db845301d2 100644 --- a/homeassistant/components/homewizard/translations/et.json +++ b/homeassistant/components/homewizard/translations/et.json @@ -5,13 +5,21 @@ "api_not_enabled": "API pole lubatud. Luba API HomeWizard Energy rakenduse seadete all", "device_not_supported": "Seda seadet ei toetata", "invalid_discovery_parameters": "Leiti toetuseta API versioon", + "reauth_successful": "API lubamine \u00f5nnestus", "unknown_error": "Ootamatu t\u00f5rge" }, + "error": { + "api_not_enabled": "API pole lubatud. Luba API HomeWizard Energy rakenduse seadete all", + "network_error": "Seade ei ole k\u00e4ttesaadav, veendu, et oled sisestanud \u00f5ige IP-aadressi ja et seade on v\u00f5rgus saadaval." + }, "step": { "discovery_confirm": { "description": "Kas seadistada {product_type} ( {serial} ) aadressil {ip_address} ?", "title": "Kinnita" }, + "reauth_confirm": { + "description": "Kohalik API on keelatud. Ava rakendus HomeWizard Energy ja luba seadme seadetes API." + }, "user": { "data": { "ip_address": "IP aadress" diff --git a/homeassistant/components/homewizard/translations/hu.json b/homeassistant/components/homewizard/translations/hu.json index 060e7e90248..d6fa0b87e9f 100644 --- a/homeassistant/components/homewizard/translations/hu.json +++ b/homeassistant/components/homewizard/translations/hu.json @@ -5,13 +5,21 @@ "api_not_enabled": "Az API nincs enged\u00e9lyezve. Enged\u00e9lyezze az API-t a HomeWizard Energy alkalmaz\u00e1sban a be\u00e1ll\u00edt\u00e1sok k\u00f6z\u00f6tt.", "device_not_supported": "Ez az eszk\u00f6z nem t\u00e1mogatott", "invalid_discovery_parameters": "Nem t\u00e1mogatott API-verzi\u00f3 \u00e9szlel\u00e9se", + "reauth_successful": "Az API enged\u00e9lyez\u00e9se sikeres volt", "unknown_error": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" }, + "error": { + "api_not_enabled": "Az API nincs enged\u00e9lyezve. Enged\u00e9lyezze az API-t a HomeWizard Energy alkalmaz\u00e1s be\u00e1ll\u00edt\u00e1saiban.", + "network_error": "Az eszk\u00f6z nem \u00e9rhet\u0151 el, gy\u0151z\u0151dj\u00f6n meg arr\u00f3l, hogy a helyes IP-c\u00edmet adta meg, \u00e9s hogy az eszk\u00f6z a h\u00e1l\u00f3zaton van." + }, "step": { "discovery_confirm": { "description": "Szeretn\u00e9 be\u00e1ll\u00edtani: {product_type} ({serial}) {ip_address} c\u00edmen?", "title": "Meger\u0151s\u00edt\u00e9s" }, + "reauth_confirm": { + "description": "A helyi API le van tiltva. Nyissa meg a HomeWizard Energy alkalmaz\u00e1st, \u00e9s enged\u00e9lyezze az API-t az eszk\u00f6zbe\u00e1ll\u00edt\u00e1sokban." + }, "user": { "data": { "ip_address": "IP c\u00edm" diff --git a/homeassistant/components/homewizard/translations/id.json b/homeassistant/components/homewizard/translations/id.json index 6363e9de21d..4227c6f425b 100644 --- a/homeassistant/components/homewizard/translations/id.json +++ b/homeassistant/components/homewizard/translations/id.json @@ -5,13 +5,21 @@ "api_not_enabled": "API tidak diaktifkan. Aktifkan API di Aplikasi Energi HomeWizard di bawah pengaturan", "device_not_supported": "Perangkat ini tidak didukung", "invalid_discovery_parameters": "Terdeteksi versi API yang tidak didukung", + "reauth_successful": "Pengaktifkan API berhasil", "unknown_error": "Kesalahan yang tidak diharapkan" }, + "error": { + "api_not_enabled": "API tidak diaktifkan. Aktifkan API di Aplikasi Energi HomeWizard di bawah pengaturan", + "network_error": "Perangkat tidak dapat dijangkau, pastikan Anda telah memasukkan alamat IP yang benar dan perangkat tersedia di jaringan Anda" + }, "step": { "discovery_confirm": { "description": "Ingin menyiapkan {product_type} ({serial}) di {ip_address}?", "title": "Konfirmasikan" }, + "reauth_confirm": { + "description": "API lokal tidak diaktifkan. Buka aplikasi Energi HomeWizard dan aktifkan API di pengaturan perangkat." + }, "user": { "data": { "ip_address": "Alamat IP" diff --git a/homeassistant/components/homewizard/translations/it.json b/homeassistant/components/homewizard/translations/it.json index 61f8a62a6c1..f840ac84341 100644 --- a/homeassistant/components/homewizard/translations/it.json +++ b/homeassistant/components/homewizard/translations/it.json @@ -5,13 +5,21 @@ "api_not_enabled": "L'API non \u00e8 abilitata. Abilita API nell'applicazione HomeWizard Energy sotto impostazioni", "device_not_supported": "Questo dispositivo non \u00e8 supportato", "invalid_discovery_parameters": "Rilevata versione API non supportata", + "reauth_successful": "L'abilitazione dell'API \u00e8 riuscita", "unknown_error": "Errore imprevisto" }, + "error": { + "api_not_enabled": "L'API non \u00e8 abilitata. Abilita l'API nell'app HomeWizard Energy nelle impostazioni", + "network_error": "Dispositivo irraggiungibile, assicurati di aver inserito l'indirizzo IP corretto e che il dispositivo sia disponibile nella tua rete" + }, "step": { "discovery_confirm": { - "description": "Vuoi configurare {product_type} ({serial}) in {ip_address}?", + "description": "Vuoi configurare {product_type} ({serial}) su {ip_address}?", "title": "Conferma" }, + "reauth_confirm": { + "description": "L'API locale \u00e8 disabilitata. Vai all'app HomeWizard Energy e abilita l'API nelle impostazioni del dispositivo." + }, "user": { "data": { "ip_address": "Indirizzo IP" diff --git a/homeassistant/components/homewizard/translations/ko.json b/homeassistant/components/homewizard/translations/ko.json new file mode 100644 index 00000000000..8fce8218106 --- /dev/null +++ b/homeassistant/components/homewizard/translations/ko.json @@ -0,0 +1,15 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown_error": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "ip_address": "IP \uc8fc\uc18c" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homewizard/translations/no.json b/homeassistant/components/homewizard/translations/no.json index e2e0aadeb5e..b158cd72596 100644 --- a/homeassistant/components/homewizard/translations/no.json +++ b/homeassistant/components/homewizard/translations/no.json @@ -5,13 +5,21 @@ "api_not_enabled": "API-en er ikke aktivert. Aktiver API i HomeWizard Energy-appen under innstillinger", "device_not_supported": "Denne enheten st\u00f8ttes ikke", "invalid_discovery_parameters": "Oppdaget API-versjon som ikke st\u00f8ttes", + "reauth_successful": "Aktivering av API var vellykket", "unknown_error": "Uventet feil" }, + "error": { + "api_not_enabled": "API-en er ikke aktivert. Aktiver API i HomeWizard Energy-appen under innstillinger", + "network_error": "Enheten er utilgjengelig, s\u00f8rg for at du har oppgitt riktig IP-adresse og at enheten er tilgjengelig i nettverket ditt" + }, "step": { "discovery_confirm": { - "description": "Vil du konfigurere {product_type} ( {serial} ) p\u00e5 {ip_address} ?", + "description": "Vil du sette opp {product_type} ({seriell}) p\u00e5 {ip_address}?", "title": "Bekrefte" }, + "reauth_confirm": { + "description": "Den lokale APIen er deaktivert. G\u00e5 til HomeWizard Energy-appen og aktiver API-en i enhetsinnstillingene." + }, "user": { "data": { "ip_address": "IP adresse" diff --git a/homeassistant/components/homewizard/translations/pl.json b/homeassistant/components/homewizard/translations/pl.json index 4388fa7d517..0a75fb88321 100644 --- a/homeassistant/components/homewizard/translations/pl.json +++ b/homeassistant/components/homewizard/translations/pl.json @@ -5,13 +5,21 @@ "api_not_enabled": "Interfejs API nie jest w\u0142\u0105czony. W\u0142\u0105cz API w ustawieniach aplikacji HomeWizard Energy.", "device_not_supported": "To urz\u0105dzenie nie jest obs\u0142ugiwane", "invalid_discovery_parameters": "Wykryto nieobs\u0142ugiwan\u0105 wersj\u0119 API", + "reauth_successful": "W\u0142\u0105czenie interfejsu API powiod\u0142o si\u0119", "unknown_error": "Nieoczekiwany b\u0142\u0105d" }, + "error": { + "api_not_enabled": "Interfejs API nie jest w\u0142\u0105czony. W\u0142\u0105cz API w ustawieniach aplikacji HomeWizard Energy.", + "network_error": "Urz\u0105dzenie nieosi\u0105galne, upewnij si\u0119, \u017ce wprowadzi\u0142e\u015b poprawny adres IP i \u017ce urz\u0105dzenie jest dost\u0119pne w Twojej sieci" + }, "step": { "discovery_confirm": { - "description": "Czy chcesz skonfigurowa\u0107 {product_type} ({serial}) pod adresem {ip_address}?", + "description": "Czy chcesz skonfigurowa\u0107 {product_type} ({serial}) o adresie {ip_address}?", "title": "Potwierdzenie" }, + "reauth_confirm": { + "description": "Lokalny interfejs API jest wy\u0142\u0105czony. Przejd\u017a do aplikacji HomeWizard Energy i w\u0142\u0105cz API w ustawieniach urz\u0105dzenia." + }, "user": { "data": { "ip_address": "Adres IP" diff --git a/homeassistant/components/homewizard/translations/pt-BR.json b/homeassistant/components/homewizard/translations/pt-BR.json index b1abeef9927..cfd067475cf 100644 --- a/homeassistant/components/homewizard/translations/pt-BR.json +++ b/homeassistant/components/homewizard/translations/pt-BR.json @@ -5,13 +5,21 @@ "api_not_enabled": "A API n\u00e3o est\u00e1 habilitada. Ative a API no aplicativo HomeWizard Energy em configura\u00e7\u00f5es", "device_not_supported": "Este dispositivo n\u00e3o \u00e9 compat\u00edvel", "invalid_discovery_parameters": "Vers\u00e3o de API n\u00e3o compat\u00edvel detectada", + "reauth_successful": "A ativa\u00e7\u00e3o da API foi bem-sucedida", "unknown_error": "Erro inesperado" }, + "error": { + "api_not_enabled": "A API n\u00e3o est\u00e1 habilitada. Ative a API no aplicativo HomeWizard Energy nas configura\u00e7\u00f5es", + "network_error": "Dispositivo inacess\u00edvel, verifique se voc\u00ea inseriu o endere\u00e7o IP correto e se o dispositivo est\u00e1 dispon\u00edvel em sua rede" + }, "step": { "discovery_confirm": { - "description": "Deseja configurar {product_type} ( {serial} ) em {ip_address} ?", + "description": "Deseja configurar {product_type} ({serial}) em {ip_address}?", "title": "Confirmar" }, + "reauth_confirm": { + "description": "A API local est\u00e1 desativada. V\u00e1 para o aplicativo HomeWizard Energy e habilite a API nas configura\u00e7\u00f5es do dispositivo." + }, "user": { "data": { "ip_address": "Endere\u00e7o IP" diff --git a/homeassistant/components/homewizard/translations/ru.json b/homeassistant/components/homewizard/translations/ru.json index 61f4cfd8fea..0b5438d4d35 100644 --- a/homeassistant/components/homewizard/translations/ru.json +++ b/homeassistant/components/homewizard/translations/ru.json @@ -5,13 +5,21 @@ "api_not_enabled": "\u0410\u043a\u0442\u0438\u0432\u0438\u0440\u0443\u0439\u0442\u0435 API \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f HomeWizard Energy.", "device_not_supported": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f.", "invalid_discovery_parameters": "\u041e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0430 \u043d\u0435\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f API.", + "reauth_successful": "\u0412\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 API \u043f\u0440\u043e\u0448\u043b\u043e \u0443\u0441\u043f\u0435\u0448\u043d\u043e", "unknown_error": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, + "error": { + "api_not_enabled": "\u0410\u043a\u0442\u0438\u0432\u0438\u0440\u0443\u0439\u0442\u0435 API \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f HomeWizard Energy.", + "network_error": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e, \u0443\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u0443\u043a\u0430\u0437\u0430\u043d \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0439 IP-\u0430\u0434\u0440\u0435\u0441 \u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e \u0432 \u0412\u0430\u0448\u0435\u0439 \u0441\u0435\u0442\u0438." + }, "step": { "discovery_confirm": { "description": "\u0425\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c {product_type} ({serial}) \u0441 IP-\u0430\u0434\u0440\u0435\u0441\u043e\u043c {ip_address}?", "title": "\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435" }, + "reauth_confirm": { + "description": "\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 API \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 HomeWizard Energy \u0438 \u0432\u043a\u043b\u044e\u0447\u0438\u0442\u0435 API \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u0445 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430." + }, "user": { "data": { "ip_address": "IP-\u0430\u0434\u0440\u0435\u0441" diff --git a/homeassistant/components/homewizard/translations/sk.json b/homeassistant/components/homewizard/translations/sk.json index 2dc757a5250..9db3eac1ee0 100644 --- a/homeassistant/components/homewizard/translations/sk.json +++ b/homeassistant/components/homewizard/translations/sk.json @@ -2,19 +2,29 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "api_not_enabled": "Rozhranie API nie je povolen\u00e9. Povo\u013ete API v aplik\u00e1cii HomeWizard Energy v nastaveniach", "device_not_supported": "Toto zariadenie nie je podporovan\u00e9", "invalid_discovery_parameters": "Zisten\u00e1 nepodporovan\u00e1 verzia API", + "reauth_successful": "Povolenie API bolo \u00faspe\u0161n\u00e9", "unknown_error": "Neo\u010dak\u00e1van\u00e1 chyba" }, + "error": { + "api_not_enabled": "Rozhranie API nie je povolen\u00e9. Povo\u013ete API v aplik\u00e1cii HomeWizard Energy v nastaveniach", + "network_error": "Zariadenie je nedostupn\u00e9, skontrolujte, \u010di ste zadali spr\u00e1vnu IP adresu a \u010di je zariadenie dostupn\u00e9 vo va\u0161ej sieti" + }, "step": { "discovery_confirm": { "description": "Chcete nastavi\u0165 {product_type} ({serial}) na {ip_address}?", "title": "Potvrdi\u0165" }, + "reauth_confirm": { + "description": "Lok\u00e1lne API je zak\u00e1zan\u00e9. Prejdite do aplik\u00e1cie HomeWizard Energy a povo\u013ete rozhranie API v nastaveniach zariadenia." + }, "user": { "data": { "ip_address": "IP adresa" }, + "description": "Zadajte IP adresu svojho zariadenia HomeWizard Energy na integr\u00e1ciu s Home Assistant.", "title": "Konfigur\u00e1cia zariadenia" } } diff --git a/homeassistant/components/homewizard/translations/zh-Hant.json b/homeassistant/components/homewizard/translations/zh-Hant.json index d68bd74668c..d66f4d66cb8 100644 --- a/homeassistant/components/homewizard/translations/zh-Hant.json +++ b/homeassistant/components/homewizard/translations/zh-Hant.json @@ -5,13 +5,21 @@ "api_not_enabled": "API \u672a\u958b\u555f\u3002\u8acb\u65bc HomeWizard Energy App \u8a2d\u5b9a\u5167\u555f\u7528 API", "device_not_supported": "\u4e0d\u652f\u63f4\u6b64\u88dd\u7f6e", "invalid_discovery_parameters": "\u5075\u6e2c\u5230\u4e0d\u652f\u63f4 API \u7248\u672c", + "reauth_successful": "\u555f\u7528 API \u6210\u529f", "unknown_error": "\u672a\u9810\u671f\u932f\u8aa4" }, + "error": { + "api_not_enabled": "API \u672a\u958b\u555f\u3002\u8acb\u65bc HomeWizard Energy App \u8a2d\u5b9a\u5167\u555f\u7528 API", + "network_error": "\u7121\u6cd5\u8a2a\u554f\u88dd\u7f6e\u3001\u8acb\u78ba\u5b9a\u8f38\u5165\u6b63\u78ba\u7684 IP \u4f4d\u5740\u3001\u540c\u6642\u88dd\u7f6e\u5df2\u7d93\u9023\u7dda\u81f3\u7db2\u8def" + }, "step": { "discovery_confirm": { "description": "\u662f\u5426\u8981\u8a2d\u5b9a\u4f4d\u65bc {ip_address} \u7684 {product_type} ({serial})\uff1f", "title": "\u78ba\u8a8d" }, + "reauth_confirm": { + "description": "\u672c\u5730\u7aef API \u5df2\u95dc\u9589\u3002\u8acb\u65bc HomeWizard Energy App \u7684\u88dd\u7f6e\u8a2d\u5b9a\u4e2d\u555f\u7528 API\u3002" + }, "user": { "data": { "ip_address": "IP \u4f4d\u5740" diff --git a/homeassistant/components/honeywell/__init__.py b/homeassistant/components/honeywell/__init__.py index 146b8e3c035..99f682fd7a4 100644 --- a/homeassistant/components/honeywell/__init__.py +++ b/homeassistant/components/honeywell/__init__.py @@ -90,7 +90,7 @@ async def update_listener(hass: HomeAssistant, config_entry: ConfigEntry) -> Non async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: - """Unload the config config and platforms.""" + """Unload the config and platforms.""" unload_ok = await hass.config_entries.async_unload_platforms( config_entry, PLATFORMS ) @@ -162,7 +162,8 @@ class HoneywellData: self.devices[updated_device.deviceid] = updated_device else: _LOGGER.info( - "New device with ID %s detected, reload the honeywell integration if you want to access it in Home Assistant" + "New device with ID %s detected, reload the honeywell integration" + " if you want to access it in Home Assistant" ) await self._hass.config_entries.async_reload(self._config.entry_id) diff --git a/homeassistant/components/honeywell/climate.py b/homeassistant/components/honeywell/climate.py index 64f87fd19ae..3bc5e0e7ef8 100644 --- a/homeassistant/components/honeywell/climate.py +++ b/homeassistant/components/honeywell/climate.py @@ -22,7 +22,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS, TEMP_FAHRENHEIT +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -103,9 +103,9 @@ class HoneywellUSThermostat(ClimateEntity): self._attr_unique_id = device.deviceid self._attr_name = device.name - self._attr_temperature_unit = ( - TEMP_CELSIUS if device.temperature_unit == "C" else TEMP_FAHRENHEIT - ) + self._attr_temperature_unit = UnitOfTemperature.FAHRENHEIT + if device.temperature_unit == "C": + self._attr_temperature_unit = UnitOfTemperature.CELSIUS self._attr_preset_modes = [PRESET_NONE, PRESET_AWAY, PRESET_HOLD] self._attr_is_aux_heat = device.system_mode == "emheat" diff --git a/homeassistant/components/honeywell/sensor.py b/homeassistant/components/honeywell/sensor.py index 3a4e8f777b2..ca7320d7c4c 100644 --- a/homeassistant/components/honeywell/sensor.py +++ b/homeassistant/components/honeywell/sensor.py @@ -14,7 +14,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import PERCENTAGE, TEMP_CELSIUS, TEMP_FAHRENHEIT +from homeassistant.const import PERCENTAGE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import StateType @@ -24,7 +24,9 @@ from .const import DOMAIN, HUMIDITY_STATUS_KEY, TEMPERATURE_STATUS_KEY def _get_temperature_sensor_unit(device: Device) -> str: """Get the correct temperature unit for the device.""" - return TEMP_CELSIUS if device.temperature_unit == "C" else TEMP_FAHRENHEIT + if device.temperature_unit == "C": + return UnitOfTemperature.CELSIUS + return UnitOfTemperature.FAHRENHEIT @dataclass diff --git a/homeassistant/components/honeywell/translations/ko.json b/homeassistant/components/honeywell/translations/ko.json new file mode 100644 index 00000000000..825af5fb4b2 --- /dev/null +++ b/homeassistant/components/honeywell/translations/ko.json @@ -0,0 +1,15 @@ +{ + "config": { + "error": { + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/honeywell/translations/sk.json b/homeassistant/components/honeywell/translations/sk.json index 04cb927ad6f..35bd0d2c2f7 100644 --- a/homeassistant/components/honeywell/translations/sk.json +++ b/homeassistant/components/honeywell/translations/sk.json @@ -8,7 +8,19 @@ "data": { "password": "Heslo", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" - } + }, + "description": "Zadajte prihlasovacie \u00fadaje pou\u017eit\u00e9 na prihl\u00e1senie na mytotalconnectcomfort.com." + } + } + }, + "options": { + "step": { + "init": { + "data": { + "away_cool_temperature": "Ch\u00fdbaj\u00faca teplota chladenia", + "away_heat_temperature": "Ch\u00fdbaj\u00faca teplota vykurovania" + }, + "description": "\u010eal\u0161ie mo\u017enosti konfigur\u00e1cie Honeywell. Teploty s\u00fa nastaven\u00e9 vo stup\u0148och Fahrenheita." } } } diff --git a/homeassistant/components/html5/notify.py b/homeassistant/components/html5/notify.py index 30465c9bd81..30fa5b749ca 100644 --- a/homeassistant/components/html5/notify.py +++ b/homeassistant/components/html5/notify.py @@ -17,7 +17,6 @@ import voluptuous as vol from voluptuous.humanize import humanize_error from homeassistant.components import websocket_api -from homeassistant.components.frontend import add_manifest_json_key from homeassistant.components.http import HomeAssistantView from homeassistant.components.notify import ( ATTR_DATA, @@ -40,8 +39,6 @@ _LOGGER = logging.getLogger(__name__) REGISTRATIONS_FILE = "html5_push_registrations.conf" -ATTR_GCM_SENDER_ID = "gcm_sender_id" -ATTR_GCM_API_KEY = "gcm_api_key" ATTR_VAPID_PUB_KEY = "vapid_pub_key" ATTR_VAPID_PRV_KEY = "vapid_prv_key" ATTR_VAPID_EMAIL = "vapid_email" @@ -52,7 +49,7 @@ def gcm_api_deprecated(value): if value: _LOGGER.warning( "Configuring html5_push_notifications via the GCM api" - " has been deprecated and will stop working after April 11," + " has been deprecated and stopped working since May 29," " 2019. Use the VAPID configuration instead. For instructions," " see https://www.home-assistant.io/integrations/html5/" ) @@ -61,11 +58,11 @@ def gcm_api_deprecated(value): PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( { - vol.Optional(ATTR_GCM_SENDER_ID): vol.All(cv.string, gcm_api_deprecated), - vol.Optional(ATTR_GCM_API_KEY): cv.string, - vol.Optional(ATTR_VAPID_PUB_KEY): cv.string, - vol.Optional(ATTR_VAPID_PRV_KEY): cv.string, - vol.Optional(ATTR_VAPID_EMAIL): cv.string, + vol.Optional("gcm_sender_id"): vol.All(cv.string, gcm_api_deprecated), + vol.Optional("gcm_api_key"): cv.string, + vol.Required(ATTR_VAPID_PUB_KEY): cv.string, + vol.Required(ATTR_VAPID_PRV_KEY): cv.string, + vol.Required(ATTR_VAPID_EMAIL): cv.string, } ) @@ -173,9 +170,9 @@ def get_service(hass, config, discovery_info=None): if registrations is None: return None - vapid_pub_key = config.get(ATTR_VAPID_PUB_KEY) - vapid_prv_key = config.get(ATTR_VAPID_PRV_KEY) - vapid_email = config.get(ATTR_VAPID_EMAIL) + vapid_pub_key = config[ATTR_VAPID_PUB_KEY] + vapid_prv_key = config[ATTR_VAPID_PRV_KEY] + vapid_email = config[ATTR_VAPID_EMAIL] def websocket_appkey(hass, connection, msg): connection.send_message(websocket_api.result_message(msg["id"], vapid_pub_key)) @@ -187,13 +184,8 @@ def get_service(hass, config, discovery_info=None): hass.http.register_view(HTML5PushRegistrationView(registrations, json_path)) hass.http.register_view(HTML5PushCallbackView(registrations)) - gcm_api_key = config.get(ATTR_GCM_API_KEY) - - if config.get(ATTR_GCM_SENDER_ID) is not None: - add_manifest_json_key(ATTR_GCM_SENDER_ID, config.get(ATTR_GCM_SENDER_ID)) - return HTML5NotificationService( - hass, gcm_api_key, vapid_prv_key, vapid_email, registrations, json_path + hass, vapid_prv_key, vapid_email, registrations, json_path ) @@ -399,9 +391,8 @@ class HTML5PushCallbackView(HomeAssistantView): class HTML5NotificationService(BaseNotificationService): """Implement the notification service for HTML5.""" - def __init__(self, hass, gcm_key, vapid_prv, vapid_email, registrations, json_path): + def __init__(self, hass, vapid_prv, vapid_email, registrations, json_path): """Initialize the service.""" - self._gcm_key = gcm_key self._vapid_prv = vapid_prv self._vapid_email = vapid_email self.registrations = registrations @@ -506,33 +497,26 @@ class HTML5NotificationService(BaseNotificationService): "%s is not a valid HTML5 push notification target", target ) continue + subscription = info[ATTR_SUBSCRIPTION] payload[ATTR_DATA][ATTR_JWT] = add_jwt( timestamp, target, payload[ATTR_TAG], - info[ATTR_SUBSCRIPTION][ATTR_KEYS][ATTR_AUTH], + subscription[ATTR_KEYS][ATTR_AUTH], ) webpusher = WebPusher(info[ATTR_SUBSCRIPTION]) - if self._vapid_prv and self._vapid_email: - vapid_headers = create_vapid_headers( - self._vapid_email, - info[ATTR_SUBSCRIPTION], - self._vapid_prv, - timestamp, - ) - vapid_headers.update({"urgency": priority, "priority": priority}) - response = webpusher.send( - data=json.dumps(payload), headers=vapid_headers, ttl=ttl - ) - else: - # Only pass the gcm key if we're actually using GCM - # If we don't, notifications break on FireFox - gcm_key = ( - self._gcm_key - if "googleapis.com" in info[ATTR_SUBSCRIPTION][ATTR_ENDPOINT] - else None - ) - response = webpusher.send(json.dumps(payload), gcm_key=gcm_key, ttl=ttl) + + endpoint = urlparse(subscription[ATTR_ENDPOINT]) + vapid_claims = { + "sub": f"mailto:{self._vapid_email}", + "aud": f"{endpoint.scheme}://{endpoint.netloc}", + "exp": timestamp + (VAPID_CLAIM_VALID_HOURS * 60 * 60), + } + vapid_headers = Vapid.from_string(self._vapid_prv).sign(vapid_claims) + vapid_headers.update({"urgency": priority, "priority": priority}) + response = webpusher.send( + data=json.dumps(payload), headers=vapid_headers, ttl=ttl + ) if response.status_code == 410: _LOGGER.info("Notification channel has expired") @@ -564,26 +548,3 @@ def add_jwt(timestamp, target, tag, jwt_secret): ATTR_TAG: tag, } return jwt.encode(jwt_claims, jwt_secret) - - -def create_vapid_headers(vapid_email, subscription_info, vapid_private_key, timestamp): - """Create encrypted headers to send to WebPusher.""" - - if ( - vapid_email - and vapid_private_key - and ATTR_ENDPOINT in subscription_info - and timestamp - ): - vapid_exp = datetime.fromtimestamp(timestamp) + timedelta( - hours=VAPID_CLAIM_VALID_HOURS - ) - url = urlparse(subscription_info.get(ATTR_ENDPOINT)) - vapid_claims = { - "sub": f"mailto:{vapid_email}", - "aud": f"{url.scheme}://{url.netloc}", - "exp": int(vapid_exp.timestamp()), - } - vapid = Vapid.from_string(private_key=vapid_private_key) - return vapid.sign(vapid_claims) - return None diff --git a/homeassistant/components/http/__init__.py b/homeassistant/components/http/__init__.py index 9e022ab0444..6624d941114 100644 --- a/homeassistant/components/http/__init__.py +++ b/homeassistant/components/http/__init__.py @@ -361,7 +361,8 @@ class HomeAssistantHTTP: except OSError as error: if not self.hass.config.safe_mode: raise HomeAssistantError( - f"Could not use SSL certificate from {self.ssl_certificate}: {error}" + f"Could not use SSL certificate from {self.ssl_certificate}:" + f" {error}" ) from error _LOGGER.error( "Could not read SSL certificate from %s: %s", @@ -378,14 +379,17 @@ class HomeAssistantHTTP: context = None else: _LOGGER.critical( - "Home Assistant is running in safe mode with an emergency self signed ssl certificate because the configured SSL certificate was not usable" + "Home Assistant is running in safe mode with an emergency self" + " signed ssl certificate because the configured SSL certificate was" + " not usable" ) return context if self.ssl_peer_certificate: if context is None: raise HomeAssistantError( - "Failed to create ssl context, no fallback available because a peer certificate is required." + "Failed to create ssl context, no fallback available because a peer" + " certificate is required." ) context.verify_mode = ssl.CERT_REQUIRED diff --git a/homeassistant/components/http/ban.py b/homeassistant/components/http/ban.py index 7be8634212a..9f300aa9db9 100644 --- a/homeassistant/components/http/ban.py +++ b/homeassistant/components/http/ban.py @@ -116,7 +116,10 @@ async def process_wrong_login(request: Request) -> None: gethostbyaddr, request.remote ) - base_msg = f"Login attempt or request with invalid authentication from {remote_host} ({remote_addr})." + base_msg = ( + "Login attempt or request with invalid authentication from" + f" {remote_host} ({remote_addr})." + ) # The user-agent is unsanitized input so we only include it in the log user_agent = request.headers.get("user-agent") diff --git a/homeassistant/components/http/forwarded.py b/homeassistant/components/http/forwarded.py index c0aaa31fab0..5d122436af2 100644 --- a/homeassistant/components/http/forwarded.py +++ b/homeassistant/components/http/forwarded.py @@ -112,8 +112,10 @@ def async_setup_forwarded( # We have X-Forwarded-For, but config does not agree if not use_x_forwarded_for: _LOGGER.error( - "A request from a reverse proxy was received from %s, but your " - "HTTP integration is not set-up for reverse proxies", + ( + "A request from a reverse proxy was received from %s, but your " + "HTTP integration is not set-up for reverse proxies" + ), connected_ip, ) raise HTTPBadRequest @@ -186,7 +188,10 @@ def async_setup_forwarded( # of elements as X-Forwarded-For if len(forwarded_proto) not in (1, len(forwarded_for)): _LOGGER.error( - "Incorrect number of elements in X-Forward-Proto. Expected 1 or %d, got %d: %s", + ( + "Incorrect number of elements in X-Forward-Proto. Expected 1 or" + " %d, got %d: %s" + ), len(forwarded_for), len(forwarded_proto), forwarded_proto_headers[0], diff --git a/homeassistant/components/huawei_lte/__init__.py b/homeassistant/components/huawei_lte/__init__.py index 5a3461b02e9..b34425e79ee 100644 --- a/homeassistant/components/huawei_lte/__init__.py +++ b/homeassistant/components/huawei_lte/__init__.py @@ -56,9 +56,11 @@ from .const import ( ADMIN_SERVICES, ALL_KEYS, ATTR_UNIQUE_ID, + CONF_MANUFACTURER, CONF_UNAUTHENTICATED_MODE, CONNECTION_TIMEOUT, DEFAULT_DEVICE_NAME, + DEFAULT_MANUFACTURER, DEFAULT_NOTIFY_SERVICE_NAME, DOMAIN, KEY_DEVICE_BASIC_INFORMATION, @@ -412,9 +414,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: device_info = DeviceInfo( configuration_url=router.url, connections=router.device_connections, + default_manufacturer=DEFAULT_MANUFACTURER, identifiers=router.device_identifiers, + manufacturer=entry.data.get(CONF_MANUFACTURER), name=router.device_name, - manufacturer="Huawei", ) hw_version = None sw_version = None diff --git a/homeassistant/components/huawei_lte/config_flow.py b/homeassistant/components/huawei_lte/config_flow.py index 036eec37d44..d1ab0547801 100644 --- a/homeassistant/components/huawei_lte/config_flow.py +++ b/homeassistant/components/huawei_lte/config_flow.py @@ -34,6 +34,7 @@ from homeassistant.core import callback from homeassistant.data_entry_flow import FlowResult from .const import ( + CONF_MANUFACTURER, CONF_TRACK_WIRED_CLIENTS, CONF_UNAUTHENTICATED_MODE, CONNECTION_TIMEOUT, @@ -208,7 +209,12 @@ class ConfigFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): ) await self.hass.async_add_executor_job(self._logout, conn) - user_input[CONF_MAC] = get_device_macs(info, wlan_settings) + user_input.update( + { + CONF_MAC: get_device_macs(info, wlan_settings), + CONF_MANUFACTURER: self.context.get(CONF_MANUFACTURER), + } + ) if not self.unique_id: if serial_number := info.get("SerialNumber"): @@ -228,16 +234,6 @@ class ConfigFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): async def async_step_ssdp(self, discovery_info: ssdp.SsdpServiceInfo) -> FlowResult: """Handle SSDP initiated config flow.""" - await self.async_set_unique_id(discovery_info.upnp[ssdp.ATTR_UPNP_UDN]) - self._abort_if_unique_id_configured() - - # Attempt to distinguish from other non-LTE Huawei router devices, at least - # some ones we are interested in have "Mobile Wi-Fi" friendlyName. - if ( - "mobile" - not in discovery_info.upnp.get(ssdp.ATTR_UPNP_FRIENDLY_NAME, "").lower() - ): - return self.async_abort(reason="not_huawei_lte") if TYPE_CHECKING: assert discovery_info.ssdp_location @@ -248,18 +244,22 @@ class ConfigFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): ) ) - if serial_number := discovery_info.upnp.get(ssdp.ATTR_UPNP_SERIAL): - await self.async_set_unique_id(serial_number) - self._abort_if_unique_id_configured() - else: - await self._async_handle_discovery_without_unique_id() + unique_id = discovery_info.upnp.get( + ssdp.ATTR_UPNP_SERIAL, discovery_info.upnp[ssdp.ATTR_UPNP_UDN] + ) + await self.async_set_unique_id(unique_id) + self._abort_if_unique_id_configured(updates={CONF_URL: url}) - user_input = {CONF_URL: url} - - self.context["title_placeholders"] = { - CONF_NAME: discovery_info.upnp.get(ssdp.ATTR_UPNP_FRIENDLY_NAME) - } - return await self._async_show_user_form(user_input) + self.context.update( + { + "title_placeholders": { + CONF_NAME: discovery_info.upnp.get(ssdp.ATTR_UPNP_FRIENDLY_NAME) + }, + CONF_MANUFACTURER: discovery_info.upnp.get(ssdp.ATTR_UPNP_MANUFACTURER), + CONF_URL: url, + } + ) + return await self._async_show_user_form() async def async_step_reauth(self, entry_data: Mapping[str, Any]) -> FlowResult: """Perform reauth upon an API authentication error.""" diff --git a/homeassistant/components/huawei_lte/const.py b/homeassistant/components/huawei_lte/const.py index 754be6bf2f3..0b968cee58f 100644 --- a/homeassistant/components/huawei_lte/const.py +++ b/homeassistant/components/huawei_lte/const.py @@ -4,10 +4,12 @@ DOMAIN = "huawei_lte" ATTR_UNIQUE_ID = "unique_id" +CONF_MANUFACTURER = "manufacturer" CONF_TRACK_WIRED_CLIENTS = "track_wired_clients" CONF_UNAUTHENTICATED_MODE = "unauthenticated_mode" DEFAULT_DEVICE_NAME = "LTE" +DEFAULT_MANUFACTURER = "Huawei Technologies Co., Ltd." DEFAULT_NOTIFY_SERVICE_NAME = DOMAIN DEFAULT_TRACK_WIRED_CLIENTS = True DEFAULT_UNAUTHENTICATED_MODE = False diff --git a/homeassistant/components/huawei_lte/manifest.json b/homeassistant/components/huawei_lte/manifest.json index 2c777aa4339..f0997e2e165 100644 --- a/homeassistant/components/huawei_lte/manifest.json +++ b/homeassistant/components/huawei_lte/manifest.json @@ -12,6 +12,14 @@ { "deviceType": "urn:schemas-upnp-org:device:InternetGatewayDevice:1", "manufacturer": "Huawei" + }, + { + "deviceType": "urn:schemas-upnp-org:device:InternetGatewayDevice:1", + "manufacturer": "Huawei Technologies Co., Ltd." + }, + { + "deviceType": "urn:schemas-upnp-org:device:InternetGatewayDevice:1", + "manufacturer": "SOYEA TECHNOLOGY CO., LTD." } ], "codeowners": ["@scop", "@fphammerle"], diff --git a/homeassistant/components/huawei_lte/sensor.py b/homeassistant/components/huawei_lte/sensor.py index c4cce70cbb7..b9bf741ac48 100644 --- a/homeassistant/components/huawei_lte/sensor.py +++ b/homeassistant/components/huawei_lte/sensor.py @@ -6,22 +6,22 @@ from collections.abc import Callable from dataclasses import dataclass, field import logging import re -from typing import NamedTuple from homeassistant.components.sensor import ( DOMAIN as SENSOR_DOMAIN, SensorDeviceClass, SensorEntity, + SensorEntityDescription, SensorStateClass, ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - DATA_BYTES, - DATA_RATE_BYTES_PER_SECOND, - FREQUENCY_MEGAHERTZ, PERCENTAGE, STATE_UNKNOWN, - TIME_SECONDS, + UnitOfDataRate, + UnitOfFrequency, + UnitOfInformation, + UnitOfTime, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import Entity, EntityCategory @@ -46,451 +46,577 @@ from .const import ( _LOGGER = logging.getLogger(__name__) -class SensorMeta(NamedTuple): - """Metadata for defining sensors.""" +def format_default(value: StateType) -> tuple[StateType, str | None]: + """Format value.""" + unit = None + if value is not None: + # Clean up value and infer unit, e.g. -71dBm, 15 dB + if match := re.match( + r"([>=<]*)(?P.+?)\s*(?P[a-zA-Z]+)\s*$", str(value) + ): + try: + value = float(match.group("value")) + unit = match.group("unit") + except ValueError: + pass + return value, unit - name: str | None = None - device_class: SensorDeviceClass | None = None - icon: str | Callable[[StateType], str] | None = None - native_unit_of_measurement: str | None = None - state_class: SensorStateClass | None = None - entity_registry_enabled_default: bool = False - entity_category: EntityCategory | None = None + +@dataclass +class HuaweiSensorGroup: + """Class describing Huawei LTE sensor groups.""" + + descriptions: dict[str, HuaweiSensorEntityDescription] include: re.Pattern[str] | None = None exclude: re.Pattern[str] | None = None - formatter: Callable[[str], tuple[StateType, str | None]] | None = None -SENSOR_META: dict[str | tuple[str, str], SensorMeta] = { +@dataclass +class HuaweiSensorEntityDescription(SensorEntityDescription): + """Class describing Huawei LTE sensor entities.""" + + formatter: Callable[[str], tuple[StateType, str | None]] = format_default + icon_fn: Callable[[StateType], str] | None = None + + +SENSOR_META: dict[str, HuaweiSensorGroup] = { # # Device information # - KEY_DEVICE_INFORMATION: SensorMeta( - include=re.compile(r"^(WanIP.*Address|uptime)$", re.IGNORECASE) - ), - (KEY_DEVICE_INFORMATION, "uptime"): SensorMeta( - name="Uptime", - icon="mdi:timer-outline", - native_unit_of_measurement=TIME_SECONDS, - entity_category=EntityCategory.DIAGNOSTIC, - ), - (KEY_DEVICE_INFORMATION, "WanIPAddress"): SensorMeta( - name="WAN IP address", - icon="mdi:ip", - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=True, - ), - (KEY_DEVICE_INFORMATION, "WanIPv6Address"): SensorMeta( - name="WAN IPv6 address", - icon="mdi:ip", - entity_category=EntityCategory.DIAGNOSTIC, + KEY_DEVICE_INFORMATION: HuaweiSensorGroup( + include=re.compile(r"^(WanIP.*Address|uptime)$", re.IGNORECASE), + descriptions={ + "uptime": HuaweiSensorEntityDescription( + key="uptime", + name="Uptime", + icon="mdi:timer-outline", + native_unit_of_measurement=UnitOfTime.SECONDS, + device_class=SensorDeviceClass.DURATION, + entity_category=EntityCategory.DIAGNOSTIC, + ), + "WanIPAddress": HuaweiSensorEntityDescription( + key="WanIPAddress", + name="WAN IP address", + icon="mdi:ip", + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=True, + ), + "WanIPv6Address": HuaweiSensorEntityDescription( + key="WanIPv6Address", + name="WAN IPv6 address", + icon="mdi:ip", + entity_category=EntityCategory.DIAGNOSTIC, + ), + }, ), # # Signal # - (KEY_DEVICE_SIGNAL, "band"): SensorMeta( - name="Band", - entity_category=EntityCategory.DIAGNOSTIC, - ), - (KEY_DEVICE_SIGNAL, "cell_id"): SensorMeta( - name="Cell ID", - icon="mdi:transmission-tower", - entity_category=EntityCategory.DIAGNOSTIC, - ), - (KEY_DEVICE_SIGNAL, "cqi0"): SensorMeta( - name="CQI 0", - icon="mdi:speedometer", - entity_category=EntityCategory.DIAGNOSTIC, - ), - (KEY_DEVICE_SIGNAL, "cqi1"): SensorMeta( - name="CQI 1", - icon="mdi:speedometer", - ), - (KEY_DEVICE_SIGNAL, "dl_mcs"): SensorMeta( - name="Downlink MCS", - entity_category=EntityCategory.DIAGNOSTIC, - ), - (KEY_DEVICE_SIGNAL, "dlbandwidth"): SensorMeta( - name="Downlink bandwidth", - icon=lambda x: ( - "mdi:speedometer-slow", - "mdi:speedometer-medium", - "mdi:speedometer", - )[bisect((8, 15), x if x is not None else -1000)], - entity_category=EntityCategory.DIAGNOSTIC, - ), - (KEY_DEVICE_SIGNAL, "earfcn"): SensorMeta( - name="EARFCN", - entity_category=EntityCategory.DIAGNOSTIC, - ), - (KEY_DEVICE_SIGNAL, "ecio"): SensorMeta( - name="EC/IO", - device_class=SensorDeviceClass.SIGNAL_STRENGTH, - # https://wiki.teltonika.lt/view/EC/IO - icon=lambda x: ( - "mdi:signal-cellular-outline", - "mdi:signal-cellular-1", - "mdi:signal-cellular-2", - "mdi:signal-cellular-3", - )[bisect((-20, -10, -6), x if x is not None else -1000)], - state_class=SensorStateClass.MEASUREMENT, - entity_category=EntityCategory.DIAGNOSTIC, - ), - (KEY_DEVICE_SIGNAL, "enodeb_id"): SensorMeta( - name="eNodeB ID", - entity_category=EntityCategory.DIAGNOSTIC, - ), - (KEY_DEVICE_SIGNAL, "lac"): SensorMeta( - name="LAC", - icon="mdi:map-marker", - entity_category=EntityCategory.DIAGNOSTIC, - ), - (KEY_DEVICE_SIGNAL, "ltedlfreq"): SensorMeta( - name="Downlink frequency", - formatter=lambda x: ( - round(int(x) / 10) if x is not None else None, - FREQUENCY_MEGAHERTZ, - ), - entity_category=EntityCategory.DIAGNOSTIC, - ), - (KEY_DEVICE_SIGNAL, "lteulfreq"): SensorMeta( - name="Uplink frequency", - formatter=lambda x: ( - round(int(x) / 10) if x is not None else None, - FREQUENCY_MEGAHERTZ, - ), - entity_category=EntityCategory.DIAGNOSTIC, - ), - (KEY_DEVICE_SIGNAL, "mode"): SensorMeta( - name="Mode", - formatter=lambda x: ({"0": "2G", "2": "3G", "7": "4G"}.get(x, "Unknown"), None), - icon=lambda x: ( - {"2G": "mdi:signal-2g", "3G": "mdi:signal-3g", "4G": "mdi:signal-4g"}.get( - str(x), "mdi:signal" - ) - ), - entity_category=EntityCategory.DIAGNOSTIC, - ), - (KEY_DEVICE_SIGNAL, "pci"): SensorMeta( - name="PCI", - icon="mdi:transmission-tower", - entity_category=EntityCategory.DIAGNOSTIC, - ), - (KEY_DEVICE_SIGNAL, "plmn"): SensorMeta( - name="PLMN", - entity_category=EntityCategory.DIAGNOSTIC, - ), - (KEY_DEVICE_SIGNAL, "rac"): SensorMeta( - name="RAC", - icon="mdi:map-marker", - entity_category=EntityCategory.DIAGNOSTIC, - ), - (KEY_DEVICE_SIGNAL, "rrc_status"): SensorMeta( - name="RRC status", - entity_category=EntityCategory.DIAGNOSTIC, - ), - (KEY_DEVICE_SIGNAL, "rscp"): SensorMeta( - name="RSCP", - device_class=SensorDeviceClass.SIGNAL_STRENGTH, - # https://wiki.teltonika.lt/view/RSCP - icon=lambda x: ( - "mdi:signal-cellular-outline", - "mdi:signal-cellular-1", - "mdi:signal-cellular-2", - "mdi:signal-cellular-3", - )[bisect((-95, -85, -75), x if x is not None else -1000)], - state_class=SensorStateClass.MEASUREMENT, - entity_category=EntityCategory.DIAGNOSTIC, - ), - (KEY_DEVICE_SIGNAL, "rsrp"): SensorMeta( - name="RSRP", - device_class=SensorDeviceClass.SIGNAL_STRENGTH, - # http://www.lte-anbieter.info/technik/rsrp.php - icon=lambda x: ( - "mdi:signal-cellular-outline", - "mdi:signal-cellular-1", - "mdi:signal-cellular-2", - "mdi:signal-cellular-3", - )[bisect((-110, -95, -80), x if x is not None else -1000)], - state_class=SensorStateClass.MEASUREMENT, - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=True, - ), - (KEY_DEVICE_SIGNAL, "rsrq"): SensorMeta( - name="RSRQ", - device_class=SensorDeviceClass.SIGNAL_STRENGTH, - # http://www.lte-anbieter.info/technik/rsrq.php - icon=lambda x: ( - "mdi:signal-cellular-outline", - "mdi:signal-cellular-1", - "mdi:signal-cellular-2", - "mdi:signal-cellular-3", - )[bisect((-11, -8, -5), x if x is not None else -1000)], - state_class=SensorStateClass.MEASUREMENT, - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=True, - ), - (KEY_DEVICE_SIGNAL, "rssi"): SensorMeta( - name="RSSI", - device_class=SensorDeviceClass.SIGNAL_STRENGTH, - # https://eyesaas.com/wi-fi-signal-strength/ - icon=lambda x: ( - "mdi:signal-cellular-outline", - "mdi:signal-cellular-1", - "mdi:signal-cellular-2", - "mdi:signal-cellular-3", - )[bisect((-80, -70, -60), x if x is not None else -1000)], - state_class=SensorStateClass.MEASUREMENT, - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=True, - ), - (KEY_DEVICE_SIGNAL, "sinr"): SensorMeta( - name="SINR", - device_class=SensorDeviceClass.SIGNAL_STRENGTH, - # http://www.lte-anbieter.info/technik/sinr.php - icon=lambda x: ( - "mdi:signal-cellular-outline", - "mdi:signal-cellular-1", - "mdi:signal-cellular-2", - "mdi:signal-cellular-3", - )[bisect((0, 5, 10), x if x is not None else -1000)], - state_class=SensorStateClass.MEASUREMENT, - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=True, - ), - (KEY_DEVICE_SIGNAL, "tac"): SensorMeta( - name="TAC", - icon="mdi:map-marker", - entity_category=EntityCategory.DIAGNOSTIC, - ), - (KEY_DEVICE_SIGNAL, "tdd"): SensorMeta( - name="TDD", - entity_category=EntityCategory.DIAGNOSTIC, - ), - (KEY_DEVICE_SIGNAL, "transmode"): SensorMeta( - name="Transmission mode", - entity_category=EntityCategory.DIAGNOSTIC, - ), - (KEY_DEVICE_SIGNAL, "txpower"): SensorMeta( - name="Transmit power", - device_class=SensorDeviceClass.SIGNAL_STRENGTH, - entity_category=EntityCategory.DIAGNOSTIC, - ), - (KEY_DEVICE_SIGNAL, "ul_mcs"): SensorMeta( - name="Uplink MCS", - entity_category=EntityCategory.DIAGNOSTIC, - ), - (KEY_DEVICE_SIGNAL, "ulbandwidth"): SensorMeta( - name="Uplink bandwidth", - icon=lambda x: ( - "mdi:speedometer-slow", - "mdi:speedometer-medium", - "mdi:speedometer", - )[bisect((8, 15), x if x is not None else -1000)], - entity_category=EntityCategory.DIAGNOSTIC, + KEY_DEVICE_SIGNAL: HuaweiSensorGroup( + descriptions={ + "band": HuaweiSensorEntityDescription( + key="band", + name="Band", + entity_category=EntityCategory.DIAGNOSTIC, + ), + "cell_id": HuaweiSensorEntityDescription( + key="cell_id", + name="Cell ID", + icon="mdi:transmission-tower", + entity_category=EntityCategory.DIAGNOSTIC, + ), + "cqi0": HuaweiSensorEntityDescription( + key="cqi0", + name="CQI 0", + icon="mdi:speedometer", + entity_category=EntityCategory.DIAGNOSTIC, + ), + "cqi1": HuaweiSensorEntityDescription( + key="cqi1", + name="CQI 1", + icon="mdi:speedometer", + ), + "dl_mcs": HuaweiSensorEntityDescription( + key="dl_mcs", + name="Downlink MCS", + entity_category=EntityCategory.DIAGNOSTIC, + ), + "dlbandwidth": HuaweiSensorEntityDescription( + key="dlbandwidth", + name="Downlink bandwidth", + icon_fn=lambda x: ( + "mdi:speedometer-slow", + "mdi:speedometer-medium", + "mdi:speedometer", + )[bisect((8, 15), x if x is not None else -1000)], + entity_category=EntityCategory.DIAGNOSTIC, + ), + "earfcn": HuaweiSensorEntityDescription( + key="earfcn", + name="EARFCN", + entity_category=EntityCategory.DIAGNOSTIC, + ), + "ecio": HuaweiSensorEntityDescription( + key="ecio", + name="EC/IO", + device_class=SensorDeviceClass.SIGNAL_STRENGTH, + # https://wiki.teltonika.lt/view/EC/IO + icon_fn=lambda x: ( + "mdi:signal-cellular-outline", + "mdi:signal-cellular-1", + "mdi:signal-cellular-2", + "mdi:signal-cellular-3", + )[bisect((-20, -10, -6), x if x is not None else -1000)], + state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, + ), + "enodeb_id": HuaweiSensorEntityDescription( + key="enodeb_id", + name="eNodeB ID", + entity_category=EntityCategory.DIAGNOSTIC, + ), + "lac": HuaweiSensorEntityDescription( + key="lac", + name="LAC", + icon="mdi:map-marker", + entity_category=EntityCategory.DIAGNOSTIC, + ), + "ltedlfreq": HuaweiSensorEntityDescription( + key="ltedlfreq", + name="Downlink frequency", + formatter=lambda x: ( + round(int(x) / 10) if x is not None else None, + UnitOfFrequency.MEGAHERTZ, + ), + device_class=SensorDeviceClass.FREQUENCY, + entity_category=EntityCategory.DIAGNOSTIC, + ), + "lteulfreq": HuaweiSensorEntityDescription( + key="lteulfreq", + name="Uplink frequency", + formatter=lambda x: ( + round(int(x) / 10) if x is not None else None, + UnitOfFrequency.MEGAHERTZ, + ), + device_class=SensorDeviceClass.FREQUENCY, + entity_category=EntityCategory.DIAGNOSTIC, + ), + "mode": HuaweiSensorEntityDescription( + key="mode", + name="Mode", + formatter=lambda x: ( + {"0": "2G", "2": "3G", "7": "4G"}.get(x, "Unknown"), + None, + ), + icon_fn=lambda x: ( + { + "2G": "mdi:signal-2g", + "3G": "mdi:signal-3g", + "4G": "mdi:signal-4g", + }.get(str(x), "mdi:signal") + ), + entity_category=EntityCategory.DIAGNOSTIC, + ), + "pci": HuaweiSensorEntityDescription( + key="pci", + name="PCI", + icon="mdi:transmission-tower", + entity_category=EntityCategory.DIAGNOSTIC, + ), + "plmn": HuaweiSensorEntityDescription( + key="plmn", + name="PLMN", + entity_category=EntityCategory.DIAGNOSTIC, + ), + "rac": HuaweiSensorEntityDescription( + key="rac", + name="RAC", + icon="mdi:map-marker", + entity_category=EntityCategory.DIAGNOSTIC, + ), + "rrc_status": HuaweiSensorEntityDescription( + key="rrc_status", + name="RRC status", + entity_category=EntityCategory.DIAGNOSTIC, + ), + "rscp": HuaweiSensorEntityDescription( + key="rscp", + name="RSCP", + device_class=SensorDeviceClass.SIGNAL_STRENGTH, + # https://wiki.teltonika.lt/view/RSCP + icon_fn=lambda x: ( + "mdi:signal-cellular-outline", + "mdi:signal-cellular-1", + "mdi:signal-cellular-2", + "mdi:signal-cellular-3", + )[bisect((-95, -85, -75), x if x is not None else -1000)], + state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, + ), + "rsrp": HuaweiSensorEntityDescription( + key="rsrp", + name="RSRP", + device_class=SensorDeviceClass.SIGNAL_STRENGTH, + # http://www.lte-anbieter.info/technik/rsrp.php + icon_fn=lambda x: ( + "mdi:signal-cellular-outline", + "mdi:signal-cellular-1", + "mdi:signal-cellular-2", + "mdi:signal-cellular-3", + )[bisect((-110, -95, -80), x if x is not None else -1000)], + state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=True, + ), + "rsrq": HuaweiSensorEntityDescription( + key="rsrq", + name="RSRQ", + device_class=SensorDeviceClass.SIGNAL_STRENGTH, + # http://www.lte-anbieter.info/technik/rsrq.php + icon_fn=lambda x: ( + "mdi:signal-cellular-outline", + "mdi:signal-cellular-1", + "mdi:signal-cellular-2", + "mdi:signal-cellular-3", + )[bisect((-11, -8, -5), x if x is not None else -1000)], + state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=True, + ), + "rssi": HuaweiSensorEntityDescription( + key="rssi", + name="RSSI", + device_class=SensorDeviceClass.SIGNAL_STRENGTH, + # https://eyesaas.com/wi-fi-signal-strength/ + icon_fn=lambda x: ( + "mdi:signal-cellular-outline", + "mdi:signal-cellular-1", + "mdi:signal-cellular-2", + "mdi:signal-cellular-3", + )[bisect((-80, -70, -60), x if x is not None else -1000)], + state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=True, + ), + "sinr": HuaweiSensorEntityDescription( + key="sinr", + name="SINR", + device_class=SensorDeviceClass.SIGNAL_STRENGTH, + # http://www.lte-anbieter.info/technik/sinr.php + icon_fn=lambda x: ( + "mdi:signal-cellular-outline", + "mdi:signal-cellular-1", + "mdi:signal-cellular-2", + "mdi:signal-cellular-3", + )[bisect((0, 5, 10), x if x is not None else -1000)], + state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=True, + ), + "tac": HuaweiSensorEntityDescription( + key="tac", + name="TAC", + icon="mdi:map-marker", + entity_category=EntityCategory.DIAGNOSTIC, + ), + "tdd": HuaweiSensorEntityDescription( + key="tdd", + name="TDD", + entity_category=EntityCategory.DIAGNOSTIC, + ), + "transmode": HuaweiSensorEntityDescription( + key="transmode", + name="Transmission mode", + entity_category=EntityCategory.DIAGNOSTIC, + ), + "txpower": HuaweiSensorEntityDescription( + key="txpower", + name="Transmit power", + device_class=SensorDeviceClass.SIGNAL_STRENGTH, + entity_category=EntityCategory.DIAGNOSTIC, + ), + "ul_mcs": HuaweiSensorEntityDescription( + key="ul_mcs", + name="Uplink MCS", + entity_category=EntityCategory.DIAGNOSTIC, + ), + "ulbandwidth": HuaweiSensorEntityDescription( + key="ulbandwidth", + name="Uplink bandwidth", + icon_fn=lambda x: ( + "mdi:speedometer-slow", + "mdi:speedometer-medium", + "mdi:speedometer", + )[bisect((8, 15), x if x is not None else -1000)], + entity_category=EntityCategory.DIAGNOSTIC, + ), + } ), # # Monitoring # - KEY_MONITORING_CHECK_NOTIFICATIONS: SensorMeta( + KEY_MONITORING_CHECK_NOTIFICATIONS: HuaweiSensorGroup( exclude=re.compile( r"^(onlineupdatestatus|smsstoragefull)$", re.IGNORECASE, - ) + ), + descriptions={ + "UnreadMessage": HuaweiSensorEntityDescription( + key="UnreadMessage", name="SMS unread", icon="mdi:email-arrow-left" + ), + }, ), - (KEY_MONITORING_CHECK_NOTIFICATIONS, "UnreadMessage"): SensorMeta( - name="SMS unread", icon="mdi:email-arrow-left" + KEY_MONITORING_MONTH_STATISTICS: HuaweiSensorGroup( + exclude=re.compile(r"^month(duration|lastcleartime)$", re.IGNORECASE), + descriptions={ + "CurrentMonthDownload": HuaweiSensorEntityDescription( + key="CurrentMonthDownload", + name="Current month download", + native_unit_of_measurement=UnitOfInformation.BYTES, + device_class=SensorDeviceClass.DATA_SIZE, + icon="mdi:download", + state_class=SensorStateClass.TOTAL_INCREASING, + ), + "CurrentMonthUpload": HuaweiSensorEntityDescription( + key="CurrentMonthUpload", + name="Current month upload", + native_unit_of_measurement=UnitOfInformation.BYTES, + device_class=SensorDeviceClass.DATA_SIZE, + icon="mdi:upload", + state_class=SensorStateClass.TOTAL_INCREASING, + ), + }, ), - KEY_MONITORING_MONTH_STATISTICS: SensorMeta( - exclude=re.compile(r"^month(duration|lastcleartime)$", re.IGNORECASE) - ), - (KEY_MONITORING_MONTH_STATISTICS, "CurrentMonthDownload"): SensorMeta( - name="Current month download", - native_unit_of_measurement=DATA_BYTES, - icon="mdi:download", - state_class=SensorStateClass.TOTAL_INCREASING, - ), - (KEY_MONITORING_MONTH_STATISTICS, "CurrentMonthUpload"): SensorMeta( - name="Current month upload", - native_unit_of_measurement=DATA_BYTES, - icon="mdi:upload", - state_class=SensorStateClass.TOTAL_INCREASING, - ), - KEY_MONITORING_STATUS: SensorMeta( + KEY_MONITORING_STATUS: HuaweiSensorGroup( include=re.compile( r"^(batterypercent|currentwifiuser|(primary|secondary).*dns)$", re.IGNORECASE, - ) + ), + descriptions={ + "BatteryPercent": HuaweiSensorEntityDescription( + key="BatteryPercent", + name="Battery", + device_class=SensorDeviceClass.BATTERY, + native_unit_of_measurement=PERCENTAGE, + state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, + ), + "CurrentWifiUser": HuaweiSensorEntityDescription( + key="CurrentWifiUser", + name="WiFi clients connected", + icon="mdi:wifi", + state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, + ), + "PrimaryDns": HuaweiSensorEntityDescription( + key="PrimaryDns", + name="Primary DNS server", + icon="mdi:ip", + entity_category=EntityCategory.DIAGNOSTIC, + ), + "PrimaryIPv6Dns": HuaweiSensorEntityDescription( + key="PrimaryIPv6Dns", + name="Primary IPv6 DNS server", + icon="mdi:ip", + entity_category=EntityCategory.DIAGNOSTIC, + ), + "SecondaryDns": HuaweiSensorEntityDescription( + key="SecondaryDns", + name="Secondary DNS server", + icon="mdi:ip", + entity_category=EntityCategory.DIAGNOSTIC, + ), + "SecondaryIPv6Dns": HuaweiSensorEntityDescription( + key="SecondaryIPv6Dns", + name="Secondary IPv6 DNS server", + icon="mdi:ip", + entity_category=EntityCategory.DIAGNOSTIC, + ), + }, ), - (KEY_MONITORING_STATUS, "BatteryPercent"): SensorMeta( - name="Battery", - device_class=SensorDeviceClass.BATTERY, - native_unit_of_measurement=PERCENTAGE, - state_class=SensorStateClass.MEASUREMENT, - entity_category=EntityCategory.DIAGNOSTIC, - ), - (KEY_MONITORING_STATUS, "CurrentWifiUser"): SensorMeta( - name="WiFi clients connected", - icon="mdi:wifi", - state_class=SensorStateClass.MEASUREMENT, - entity_category=EntityCategory.DIAGNOSTIC, - ), - (KEY_MONITORING_STATUS, "PrimaryDns"): SensorMeta( - name="Primary DNS server", - icon="mdi:ip", - entity_category=EntityCategory.DIAGNOSTIC, - ), - (KEY_MONITORING_STATUS, "PrimaryIPv6Dns"): SensorMeta( - name="Primary IPv6 DNS server", - icon="mdi:ip", - entity_category=EntityCategory.DIAGNOSTIC, - ), - (KEY_MONITORING_STATUS, "SecondaryDns"): SensorMeta( - name="Secondary DNS server", - icon="mdi:ip", - entity_category=EntityCategory.DIAGNOSTIC, - ), - (KEY_MONITORING_STATUS, "SecondaryIPv6Dns"): SensorMeta( - name="Secondary IPv6 DNS server", - icon="mdi:ip", - entity_category=EntityCategory.DIAGNOSTIC, - ), - KEY_MONITORING_TRAFFIC_STATISTICS: SensorMeta( - exclude=re.compile(r"^showtraffic$", re.IGNORECASE) - ), - (KEY_MONITORING_TRAFFIC_STATISTICS, "CurrentConnectTime"): SensorMeta( - name="Current connection duration", - native_unit_of_measurement=TIME_SECONDS, - icon="mdi:timer-outline", - ), - (KEY_MONITORING_TRAFFIC_STATISTICS, "CurrentDownload"): SensorMeta( - name="Current connection download", - native_unit_of_measurement=DATA_BYTES, - icon="mdi:download", - state_class=SensorStateClass.TOTAL_INCREASING, - ), - (KEY_MONITORING_TRAFFIC_STATISTICS, "CurrentDownloadRate"): SensorMeta( - name="Current download rate", - native_unit_of_measurement=DATA_RATE_BYTES_PER_SECOND, - icon="mdi:download", - state_class=SensorStateClass.MEASUREMENT, - ), - (KEY_MONITORING_TRAFFIC_STATISTICS, "CurrentUpload"): SensorMeta( - name="Current connection upload", - native_unit_of_measurement=DATA_BYTES, - icon="mdi:upload", - state_class=SensorStateClass.TOTAL_INCREASING, - ), - (KEY_MONITORING_TRAFFIC_STATISTICS, "CurrentUploadRate"): SensorMeta( - name="Current upload rate", - native_unit_of_measurement=DATA_RATE_BYTES_PER_SECOND, - icon="mdi:upload", - state_class=SensorStateClass.MEASUREMENT, - ), - (KEY_MONITORING_TRAFFIC_STATISTICS, "TotalConnectTime"): SensorMeta( - name="Total connected duration", - native_unit_of_measurement=TIME_SECONDS, - icon="mdi:timer-outline", - state_class=SensorStateClass.TOTAL_INCREASING, - ), - (KEY_MONITORING_TRAFFIC_STATISTICS, "TotalDownload"): SensorMeta( - name="Total download", - native_unit_of_measurement=DATA_BYTES, - icon="mdi:download", - state_class=SensorStateClass.TOTAL_INCREASING, - ), - (KEY_MONITORING_TRAFFIC_STATISTICS, "TotalUpload"): SensorMeta( - name="Total upload", - native_unit_of_measurement=DATA_BYTES, - icon="mdi:upload", - state_class=SensorStateClass.TOTAL_INCREASING, + KEY_MONITORING_TRAFFIC_STATISTICS: HuaweiSensorGroup( + exclude=re.compile(r"^showtraffic$", re.IGNORECASE), + descriptions={ + "CurrentConnectTime": HuaweiSensorEntityDescription( + key="CurrentConnectTime", + name="Current connection duration", + native_unit_of_measurement=UnitOfTime.SECONDS, + device_class=SensorDeviceClass.DURATION, + icon="mdi:timer-outline", + ), + "CurrentDownload": HuaweiSensorEntityDescription( + key="CurrentDownload", + name="Current connection download", + native_unit_of_measurement=UnitOfInformation.BYTES, + device_class=SensorDeviceClass.DATA_SIZE, + icon="mdi:download", + state_class=SensorStateClass.TOTAL_INCREASING, + ), + "CurrentDownloadRate": HuaweiSensorEntityDescription( + key="CurrentDownloadRate", + name="Current download rate", + native_unit_of_measurement=UnitOfDataRate.BYTES_PER_SECOND, + device_class=SensorDeviceClass.DATA_RATE, + icon="mdi:download", + state_class=SensorStateClass.MEASUREMENT, + ), + "CurrentUpload": HuaweiSensorEntityDescription( + key="CurrentUpload", + name="Current connection upload", + native_unit_of_measurement=UnitOfInformation.BYTES, + device_class=SensorDeviceClass.DATA_SIZE, + icon="mdi:upload", + state_class=SensorStateClass.TOTAL_INCREASING, + ), + "CurrentUploadRate": HuaweiSensorEntityDescription( + key="CurrentUploadRate", + name="Current upload rate", + native_unit_of_measurement=UnitOfDataRate.BYTES_PER_SECOND, + device_class=SensorDeviceClass.DATA_RATE, + icon="mdi:upload", + state_class=SensorStateClass.MEASUREMENT, + ), + "TotalConnectTime": HuaweiSensorEntityDescription( + key="TotalConnectTime", + name="Total connected duration", + native_unit_of_measurement=UnitOfTime.SECONDS, + device_class=SensorDeviceClass.DURATION, + icon="mdi:timer-outline", + state_class=SensorStateClass.TOTAL_INCREASING, + ), + "TotalDownload": HuaweiSensorEntityDescription( + key="TotalDownload", + name="Total download", + native_unit_of_measurement=UnitOfInformation.BYTES, + device_class=SensorDeviceClass.DATA_SIZE, + icon="mdi:download", + state_class=SensorStateClass.TOTAL_INCREASING, + ), + "TotalUpload": HuaweiSensorEntityDescription( + key="TotalUpload", + name="Total upload", + native_unit_of_measurement=UnitOfInformation.BYTES, + device_class=SensorDeviceClass.DATA_SIZE, + icon="mdi:upload", + state_class=SensorStateClass.TOTAL_INCREASING, + ), + }, ), # # Network # - KEY_NET_CURRENT_PLMN: SensorMeta( - exclude=re.compile(r"^(Rat|ShortName|Spn)$", re.IGNORECASE) + KEY_NET_CURRENT_PLMN: HuaweiSensorGroup( + exclude=re.compile(r"^(Rat|ShortName|Spn)$", re.IGNORECASE), + descriptions={ + "FullName": HuaweiSensorEntityDescription( + key="FullName", + name="Operator name", + entity_category=EntityCategory.DIAGNOSTIC, + ), + "Numeric": HuaweiSensorEntityDescription( + key="Numeric", + name="Operator code", + entity_category=EntityCategory.DIAGNOSTIC, + ), + "State": HuaweiSensorEntityDescription( + key="State", + name="Operator search mode", + formatter=lambda x: ( + {"0": "Auto", "1": "Manual"}.get(x, "Unknown"), + None, + ), + entity_category=EntityCategory.CONFIG, + ), + }, ), - (KEY_NET_CURRENT_PLMN, "FullName"): SensorMeta( - name="Operator name", - entity_category=EntityCategory.DIAGNOSTIC, - ), - (KEY_NET_CURRENT_PLMN, "Numeric"): SensorMeta( - name="Operator code", - entity_category=EntityCategory.DIAGNOSTIC, - ), - (KEY_NET_CURRENT_PLMN, "State"): SensorMeta( - name="Operator search mode", - formatter=lambda x: ({"0": "Auto", "1": "Manual"}.get(x, "Unknown"), None), - entity_category=EntityCategory.CONFIG, - ), - KEY_NET_NET_MODE: SensorMeta(include=re.compile(r"^NetworkMode$", re.IGNORECASE)), - (KEY_NET_NET_MODE, "NetworkMode"): SensorMeta( - name="Preferred mode", - formatter=lambda x: ( - { - "00": "4G/3G/2G", - "01": "2G", - "02": "3G", - "03": "4G", - "0301": "4G/2G", - "0302": "4G/3G", - "0201": "3G/2G", - }.get(x, "Unknown"), - None, - ), - entity_category=EntityCategory.CONFIG, + KEY_NET_NET_MODE: HuaweiSensorGroup( + include=re.compile(r"^NetworkMode$", re.IGNORECASE), + descriptions={ + "NetworkMode": HuaweiSensorEntityDescription( + key="NetworkMode", + name="Preferred mode", + formatter=lambda x: ( + { + "00": "4G/3G/2G", + "01": "2G", + "02": "3G", + "03": "4G", + "0301": "4G/2G", + "0302": "4G/3G", + "0201": "3G/2G", + }.get(x, "Unknown"), + None, + ), + entity_category=EntityCategory.CONFIG, + ), + }, ), # # SMS # - (KEY_SMS_SMS_COUNT, "LocalDeleted"): SensorMeta( - name="SMS deleted (device)", - icon="mdi:email-minus", - ), - (KEY_SMS_SMS_COUNT, "LocalDraft"): SensorMeta( - name="SMS drafts (device)", - icon="mdi:email-arrow-right-outline", - ), - (KEY_SMS_SMS_COUNT, "LocalInbox"): SensorMeta( - name="SMS inbox (device)", - icon="mdi:email", - ), - (KEY_SMS_SMS_COUNT, "LocalMax"): SensorMeta( - name="SMS capacity (device)", - icon="mdi:email", - ), - (KEY_SMS_SMS_COUNT, "LocalOutbox"): SensorMeta( - name="SMS outbox (device)", - icon="mdi:email-arrow-right", - ), - (KEY_SMS_SMS_COUNT, "LocalUnread"): SensorMeta( - name="SMS unread (device)", - icon="mdi:email-arrow-left", - ), - (KEY_SMS_SMS_COUNT, "SimDraft"): SensorMeta( - name="SMS drafts (SIM)", - icon="mdi:email-arrow-right-outline", - ), - (KEY_SMS_SMS_COUNT, "SimInbox"): SensorMeta( - name="SMS inbox (SIM)", - icon="mdi:email", - ), - (KEY_SMS_SMS_COUNT, "SimMax"): SensorMeta( - name="SMS capacity (SIM)", - icon="mdi:email", - ), - (KEY_SMS_SMS_COUNT, "SimOutbox"): SensorMeta( - name="SMS outbox (SIM)", - icon="mdi:email-arrow-right", - ), - (KEY_SMS_SMS_COUNT, "SimUnread"): SensorMeta( - name="SMS unread (SIM)", - icon="mdi:email-arrow-left", - ), - (KEY_SMS_SMS_COUNT, "SimUsed"): SensorMeta( - name="SMS messages (SIM)", - icon="mdi:email-arrow-left", + KEY_SMS_SMS_COUNT: HuaweiSensorGroup( + descriptions={ + "LocalDeleted": HuaweiSensorEntityDescription( + key="LocalDeleted", + name="SMS deleted (device)", + icon="mdi:email-minus", + ), + "LocalDraft": HuaweiSensorEntityDescription( + key="LocalDraft", + name="SMS drafts (device)", + icon="mdi:email-arrow-right-outline", + ), + "LocalInbox": HuaweiSensorEntityDescription( + key="LocalInbox", + name="SMS inbox (device)", + icon="mdi:email", + ), + "LocalMax": HuaweiSensorEntityDescription( + key="LocalMax", + name="SMS capacity (device)", + icon="mdi:email", + ), + "LocalOutbox": HuaweiSensorEntityDescription( + key="LocalOutbox", + name="SMS outbox (device)", + icon="mdi:email-arrow-right", + ), + "LocalUnread": HuaweiSensorEntityDescription( + key="LocalUnread", + name="SMS unread (device)", + icon="mdi:email-arrow-left", + ), + "SimDraft": HuaweiSensorEntityDescription( + key="SimDraft", + name="SMS drafts (SIM)", + icon="mdi:email-arrow-right-outline", + ), + "SimInbox": HuaweiSensorEntityDescription( + key="SimInbox", + name="SMS inbox (SIM)", + icon="mdi:email", + ), + "SimMax": HuaweiSensorEntityDescription( + key="SimMax", + name="SMS capacity (SIM)", + icon="mdi:email", + ), + "SimOutbox": HuaweiSensorEntityDescription( + key="SimOutbox", + name="SMS outbox (SIM)", + icon="mdi:email-arrow-right", + ), + "SimUnread": HuaweiSensorEntityDescription( + key="SimUnread", + name="SMS unread (SIM)", + icon="mdi:email-arrow-left", + ), + "SimUsed": HuaweiSensorEntityDescription( + key="SimUsed", + name="SMS messages (SIM)", + icon="mdi:email-arrow-left", + ), + }, ), } @@ -514,43 +640,32 @@ async def async_setup_entry( for item in items: sensors.append( HuaweiLteSensor( - router, key, item, SENSOR_META.get((key, item), SensorMeta()) + router, + key, + item, + SENSOR_META[key].descriptions.get( + item, HuaweiSensorEntityDescription(key=item) + ), ) ) async_add_entities(sensors, True) -def format_default(value: StateType) -> tuple[StateType, str | None]: - """Format value.""" - unit = None - if value is not None: - # Clean up value and infer unit, e.g. -71dBm, 15 dB - if match := re.match( - r"([>=<]*)(?P.+?)\s*(?P[a-zA-Z]+)\s*$", str(value) - ): - try: - value = float(match.group("value")) - unit = match.group("unit") - except ValueError: - pass - return value, unit - - @dataclass class HuaweiLteSensor(HuaweiLteBaseEntityWithDevice, SensorEntity): """Huawei LTE sensor entity.""" key: str item: str - meta: SensorMeta + entity_description: HuaweiSensorEntityDescription _state: StateType = field(default=STATE_UNKNOWN, init=False) _unit: str | None = field(default=None, init=False) def __post_init__(self) -> None: """Initialize remaining attributes.""" - self._attr_name = self.meta.name or self.item + self._attr_name = self.entity_description.name or self.item async def async_added_to_hass(self) -> None: """Subscribe to needed data on add.""" @@ -571,33 +686,17 @@ class HuaweiLteSensor(HuaweiLteBaseEntityWithDevice, SensorEntity): """Return sensor state.""" return self._state - @property - def device_class(self) -> SensorDeviceClass | None: - """Return sensor device class.""" - return self.meta.device_class - @property def native_unit_of_measurement(self) -> str | None: """Return sensor's unit of measurement.""" - return self.meta.native_unit_of_measurement or self._unit + return self.entity_description.native_unit_of_measurement or self._unit @property def icon(self) -> str | None: """Return icon for sensor.""" - icon = self.meta.icon - if callable(icon): - return icon(self.state) - return icon - - @property - def state_class(self) -> SensorStateClass | None: - """Return sensor state class.""" - return self.meta.state_class - - @property - def entity_registry_enabled_default(self) -> bool: - """Return if the entity should be enabled when first added to the entity registry.""" - return self.meta.entity_registry_enabled_default + if self.entity_description.icon_fn: + return self.entity_description.icon_fn(self.state) + return self.entity_description.icon async def async_update(self) -> None: """Update state.""" @@ -607,14 +706,7 @@ class HuaweiLteSensor(HuaweiLteBaseEntityWithDevice, SensorEntity): _LOGGER.debug("%s[%s] not in data", self.key, self.item) value = None - formatter = self.meta.formatter - if not callable(formatter): - formatter = format_default + formatter = self.entity_description.formatter self._state, self._unit = formatter(value) self._available = value is not None - - @property - def entity_category(self) -> EntityCategory | None: - """Return category of entity, if any.""" - return self.meta.entity_category diff --git a/homeassistant/components/huawei_lte/strings.json b/homeassistant/components/huawei_lte/strings.json index 8f6ec64491b..dbc30510d13 100644 --- a/homeassistant/components/huawei_lte/strings.json +++ b/homeassistant/components/huawei_lte/strings.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "not_huawei_lte": "Not a Huawei LTE device", "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]" }, "error": { diff --git a/homeassistant/components/huawei_lte/translations/ko.json b/homeassistant/components/huawei_lte/translations/ko.json index 2a29418e67d..0db0afb11bc 100644 --- a/homeassistant/components/huawei_lte/translations/ko.json +++ b/homeassistant/components/huawei_lte/translations/ko.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "not_huawei_lte": "\ud654\uc6e8\uc774 LTE \uae30\uae30\uac00 \uc544\ub2d9\ub2c8\ub2e4" + "not_huawei_lte": "\ud654\uc6e8\uc774 LTE \uae30\uae30\uac00 \uc544\ub2d9\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" }, "error": { "connection_timeout": "\uc811\uc18d \uc2dc\uac04 \ucd08\uacfc", @@ -15,6 +16,13 @@ }, "flow_title": "Huawei LTE: {name}", "step": { + "reauth_confirm": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + }, + "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" + }, "user": { "data": { "password": "\ube44\ubc00\ubc88\ud638", diff --git a/homeassistant/components/huawei_lte/translations/pt.json b/homeassistant/components/huawei_lte/translations/pt.json index c8856eb2f79..483320078de 100644 --- a/homeassistant/components/huawei_lte/translations/pt.json +++ b/homeassistant/components/huawei_lte/translations/pt.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "reauth_successful": "Reautentica\u00e7\u00e3o bem sucedida" + }, "error": { "connection_timeout": "Liga\u00e7\u00e3o expirou", "incorrect_password": "Palavra-passe incorreta", @@ -9,6 +12,13 @@ }, "flow_title": "Huawei LTE: {name}", "step": { + "reauth_confirm": { + "data": { + "password": "Palavra-passe", + "username": "Nome de Utilizador" + }, + "title": "Reautenticar integra\u00e7\u00e3o" + }, "user": { "data": { "password": "Palavra-passe", diff --git a/homeassistant/components/huawei_lte/translations/sk.json b/homeassistant/components/huawei_lte/translations/sk.json index 2d0f4eacf54..0b9cdc4f5c4 100644 --- a/homeassistant/components/huawei_lte/translations/sk.json +++ b/homeassistant/components/huawei_lte/translations/sk.json @@ -21,6 +21,7 @@ "password": "Heslo", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" }, + "description": "Zadajte prihlasovacie \u00fadaje k zariadeniu.", "title": "Znova overi\u0165 integr\u00e1ciu" }, "user": { @@ -29,6 +30,7 @@ "url": "URL", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" }, + "description": "Zadajte pr\u00edstupov\u00e9 \u00fadaje zariadenia.", "title": "Nakonfigurujte Huawei LTE" } } @@ -38,7 +40,9 @@ "init": { "data": { "name": "N\u00e1zov notifika\u010dnej slu\u017eby (zmena vy\u017eaduje re\u0161tart)", - "recipient": "Pr\u00edjemcovia upozornen\u00ed SMS" + "recipient": "Pr\u00edjemcovia upozornen\u00ed SMS", + "track_wired_clients": "Sledovanie klientov k\u00e1blovej siete", + "unauthenticated_mode": "Neoveren\u00fd re\u017eim (zmena vy\u017eaduje op\u00e4tovn\u00e9 na\u010d\u00edtanie)" } } } diff --git a/homeassistant/components/hue/__init__.py b/homeassistant/components/hue/__init__.py index 9723d506404..21e9bfab6be 100644 --- a/homeassistant/components/hue/__init__.py +++ b/homeassistant/components/hue/__init__.py @@ -78,9 +78,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: ): persistent_notification.async_create( hass, - "Your Hue hub has a known security vulnerability ([CVE-2020-6007] " - "(https://cve.circl.lu/cve/CVE-2020-6007)). " - "Go to the Hue app and check for software updates.", + ( + "Your Hue hub has a known security vulnerability ([CVE-2020-6007] " + "(https://cve.circl.lu/cve/CVE-2020-6007)). " + "Go to the Hue app and check for software updates." + ), "Signify Hue", "hue_hub_firmware", ) diff --git a/homeassistant/components/hue/migration.py b/homeassistant/components/hue/migration.py index 1d56d493785..221888b0854 100644 --- a/homeassistant/components/hue/migration.py +++ b/homeassistant/components/hue/migration.py @@ -115,7 +115,10 @@ async def handle_v2_migration(hass: core.HomeAssistant, entry: ConfigEntry) -> N if hass_dev_id is None: # can be safely ignored, this device does not exist in current config LOGGER.debug( - "Ignoring device %s (%s) as it does not (yet) exist in the device registry", + ( + "Ignoring device %s (%s) as it does not (yet) exist in the" + " device registry" + ), hue_dev.metadata.name, hue_dev.id, ) @@ -149,7 +152,10 @@ async def handle_v2_migration(hass: core.HomeAssistant, entry: ConfigEntry) -> N if new_unique_id is None: # this may happen if we're looking at orphaned or unsupported entity LOGGER.warning( - "Skip migration of %s because it no longer exists on the bridge", + ( + "Skip migration of %s because it no longer exists on the" + " bridge" + ), ent.entity_id, ) continue diff --git a/homeassistant/components/hue/services.py b/homeassistant/components/hue/services.py index 652ec52ebed..5b0b252e8d4 100644 --- a/homeassistant/components/hue/services.py +++ b/homeassistant/components/hue/services.py @@ -133,8 +133,10 @@ async def hue_activate_scene_v2( ) -> bool: """Service for V2 bridge to call scene by name.""" LOGGER.warning( - "Use of service_call '%s' is deprecated and will be removed " - "in a future release. Please use scene entities instead", + ( + "Use of service_call '%s' is deprecated and will be removed " + "in a future release. Please use scene entities instead" + ), SERVICE_HUE_ACTIVATE_SCENE, ) api: HueBridgeV2 = bridge.api diff --git a/homeassistant/components/hue/translations/lb.json b/homeassistant/components/hue/translations/lb.json index 9a713b1cb52..891bce0fdde 100644 --- a/homeassistant/components/hue/translations/lb.json +++ b/homeassistant/components/hue/translations/lb.json @@ -1,11 +1,11 @@ { "config": { "abort": { - "all_configured": "All Philips Hue Bridge si scho\u00a0konfigur\u00e9iert", + "all_configured": "All Philips Hue-Bridges si scho konfigur\u00e9iert", "already_configured": "Apparat ass scho konfigur\u00e9iert", "already_in_progress": "Konfiguratioun's Oflas ass schon am gaang", "cannot_connect": "Feeler beim verbannen", - "discover_timeout": "Keng Hue bridge fonnt", + "discover_timeout": "Et k\u00ebnne keng Hue Bridges erkannt ginn", "no_bridges": "Keng Philips Hue Bridge fonnt", "not_hue_bridge": "Keng Hue Bridge", "unknown": "Onerwaarte Feeler" @@ -23,7 +23,7 @@ }, "link": { "description": "Dr\u00e9ckt de Kn\u00e4ppchen un der Bridge fir den Philips Hue mam Home Assistant ze registr\u00e9ieren.\n\n![Kn\u00e4ppchen un der Bridge](/static/images/config_philips_hue.jpg)", - "title": "Link Hub" + "title": "Hub verbannen" }, "manual": { "data": { diff --git a/homeassistant/components/hue/translations/pt.json b/homeassistant/components/hue/translations/pt.json index 9b982d6c84e..85b0287c27d 100644 --- a/homeassistant/components/hue/translations/pt.json +++ b/homeassistant/components/hue/translations/pt.json @@ -4,7 +4,7 @@ "all_configured": "Todos os hubs Philips Hue j\u00e1 est\u00e3o configurados", "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", "already_in_progress": "O processo de configura\u00e7\u00e3o j\u00e1 est\u00e1 a decorrer", - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "discover_timeout": "Nenhum hub Hue descoberto", "no_bridges": "Nenhum hub Philips Hue descoberto", "not_hue_bridge": "N\u00e3o \u00e9 uma bridge Hue", @@ -17,7 +17,7 @@ "step": { "init": { "data": { - "host": "Servidor" + "host": "Endere\u00e7o" }, "title": "Hue bridge" }, @@ -27,7 +27,7 @@ }, "manual": { "data": { - "host": "Servidor" + "host": "Endere\u00e7o" } } } @@ -35,7 +35,8 @@ "device_automation": { "trigger_subtype": { "button_1": "Primeiro bot\u00e3o", - "button_4": "Quarto bot\u00e3o" + "button_4": "Quarto bot\u00e3o", + "turn_off": "Desligar" }, "trigger_type": { "short_release": "Bot\u00e3o \"{subtype}\" solto ap\u00f3s press\u00e3o curta" diff --git a/homeassistant/components/hue/translations/sk.json b/homeassistant/components/hue/translations/sk.json index 520fee9e6e4..46bddda61db 100644 --- a/homeassistant/components/hue/translations/sk.json +++ b/homeassistant/components/hue/translations/sk.json @@ -5,6 +5,7 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "cannot_connect": "Nepodarilo sa pripoji\u0165", + "discover_timeout": "Nie je mo\u017en\u00e9 zisti\u0165 bridge Hue", "invalid_host": "Neplatn\u00fd hostite\u013e", "no_bridges": "Neboli objaven\u00fd \u017eiaden Philips Hue bridge", "not_hue_bridge": "Nie je to most Hue Bridge", @@ -22,12 +23,14 @@ "title": "Vyberte Hue bridge" }, "link": { - "description": "Pre registr\u00e1ciu Philips Hue s Home Assistant stla\u010dte tla\u010didlo na Philips Hue bridge.\n\n![Location of button on bridge](/static/images/config_philips_hue.jpg)" + "description": "Pre registr\u00e1ciu Philips Hue s Home Assistant stla\u010dte tla\u010didlo na Philips Hue bridge.\n\n![Location of button on bridge](/static/images/config_philips_hue.jpg)", + "title": "Link Hub" }, "manual": { "data": { "host": "Hostite\u013e" - } + }, + "title": "Manu\u00e1lna konfigur\u00e1cia bridge Hue" } } }, @@ -41,6 +44,10 @@ "button_2": "Druh\u00e9 tla\u010didlo", "button_3": "Tretie tla\u010didlo", "button_4": "\u0160tvrt\u00e9 tla\u010didlo", + "clock_wise": "Ot\u00e1\u010danie v smere hodinov\u00fdch ru\u010di\u010diek", + "counter_clock_wise": "Ot\u00e1\u010danie proti smeru hodinov\u00fdch ru\u010di\u010diek", + "dim_down": "Stlmi\u0165", + "dim_up": "Zv\u00fd\u0161i\u0165", "double_buttons_1_3": "Prv\u00e9 a tretie tla\u010didlo", "double_buttons_2_4": "Druh\u00e9 a \u0161tvrt\u00e9 tla\u010didlo", "turn_off": "Vypn\u00fa\u0165", @@ -48,6 +55,7 @@ }, "trigger_type": { "double_short_release": "Obe \u201e{subtype}\u201c boli uvo\u013enen\u00e9", + "initial_press": "\"{subtype}\" stla\u010den\u00fd na za\u010diatku", "long_release": "\"{subtype}\" uvo\u013enen\u00e9 po dlhom stla\u010den\u00ed", "remote_button_long_release": "\"{subtype}\" uvo\u013enen\u00e9 po dlhom stla\u010den\u00ed", "remote_button_short_press": "\"{subtype}\" stla\u010den\u00e9", @@ -55,7 +63,8 @@ "remote_double_button_long_press": "Obe \"{subtype}\" uvo\u013enen\u00e9 po dlhom stla\u010den\u00ed", "remote_double_button_short_press": "Obe \u201e{subtype}\u201c boli uvo\u013enen\u00e9", "repeat": "\u201e{subtype}\u201c podr\u017ean\u00e9", - "short_release": "\"{subtype}\" uvo\u013enen\u00e9 po kr\u00e1tkom stla\u010den\u00ed" + "short_release": "\"{subtype}\" uvo\u013enen\u00e9 po kr\u00e1tkom stla\u010den\u00ed", + "start": "\"{subtype}\" stla\u010den\u00fd na za\u010diatku" } }, "options": { @@ -63,7 +72,9 @@ "init": { "data": { "allow_hue_groups": "Povoli\u0165 skupiny Hue", - "allow_hue_scenes": "Povoli\u0165 sc\u00e9ny Hue" + "allow_hue_scenes": "Povoli\u0165 sc\u00e9ny Hue", + "allow_unreachable": "Umo\u017enite nedostupn\u00fdm \u017eiarovk\u00e1m spr\u00e1vne hl\u00e1si\u0165 svoj stav", + "ignore_availability": "Ignorova\u0165 stav pripojenia pre dan\u00e9 zariadenia" } } } diff --git a/homeassistant/components/hue/v1/sensor.py b/homeassistant/components/hue/v1/sensor.py index 3a79f5f37f1..5adca4ef8d7 100644 --- a/homeassistant/components/hue/v1/sensor.py +++ b/homeassistant/components/hue/v1/sensor.py @@ -11,7 +11,7 @@ from homeassistant.components.sensor import ( SensorEntity, SensorStateClass, ) -from homeassistant.const import LIGHT_LUX, PERCENTAGE, TEMP_CELSIUS +from homeassistant.const import LIGHT_LUX, PERCENTAGE, UnitOfTemperature from homeassistant.helpers.entity import EntityCategory from ..const import DOMAIN as HUE_DOMAIN @@ -76,7 +76,7 @@ class HueTemperature(GenericHueGaugeSensorEntity): _attr_device_class = SensorDeviceClass.TEMPERATURE _attr_state_class = SensorStateClass.MEASUREMENT - _attr_native_unit_of_measurement = TEMP_CELSIUS + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS @property def native_value(self): diff --git a/homeassistant/components/hue/v2/entity.py b/homeassistant/components/hue/v2/entity.py index fec14075b42..7335bf71058 100644 --- a/homeassistant/components/hue/v2/entity.py +++ b/homeassistant/components/hue/v2/entity.py @@ -196,11 +196,13 @@ class HueBaseEntity(Entity): # the device state changed from on->off or off->on # while it was reported as not connected! self.logger.warning( - "Device %s changed state while reported as disconnected. " - "This might be an indicator that routing is not working for this device " - "or the device is having connectivity issues. " - "You can disable availability reporting for this device in the Hue options. " - "Device details: %s - %s (%s) fw: %s", + ( + "Device %s changed state while reported as disconnected. This" + " might be an indicator that routing is not working for this" + " device or the device is having connectivity issues. You can" + " disable availability reporting for this device in the Hue" + " options. Device details: %s - %s (%s) fw: %s" + ), self.name, self.device.product_data.manufacturer_name, self.device.product_data.product_name, diff --git a/homeassistant/components/hue/v2/light.py b/homeassistant/components/hue/v2/light.py index 0f7cc6cdbab..b925177f26b 100644 --- a/homeassistant/components/hue/v2/light.py +++ b/homeassistant/components/hue/v2/light.py @@ -35,6 +35,9 @@ from .helpers import ( ) EFFECT_NONE = "None" +FALLBACK_MIN_MIREDS = 153 # 6500 K +FALLBACK_MAX_MIREDS = 500 # 2000 K +FALLBACK_MIREDS = 173 # halfway async def async_setup_entry( @@ -141,21 +144,24 @@ class HueLight(HueBaseEntity, LightEntity): """Return the color temperature.""" if color_temp := self.resource.color_temperature: return color_temp.mirek - return 0 + # return a fallback value to prevent issues with mired->kelvin conversions + return FALLBACK_MIREDS @property def min_mireds(self) -> int: """Return the coldest color_temp that this light supports.""" if color_temp := self.resource.color_temperature: return color_temp.mirek_schema.mirek_minimum - return 0 + # return a fallback value to prevent issues with mired->kelvin conversions + return FALLBACK_MIN_MIREDS @property def max_mireds(self) -> int: """Return the warmest color_temp that this light supports.""" if color_temp := self.resource.color_temperature: return color_temp.mirek_schema.mirek_maximum - return 0 + # return a fallback value to prevent issues with mired->kelvin conversions + return FALLBACK_MAX_MIREDS @property def supported_color_modes(self) -> set | None: diff --git a/homeassistant/components/hue/v2/sensor.py b/homeassistant/components/hue/v2/sensor.py index d331393d29b..7fa34c59869 100644 --- a/homeassistant/components/hue/v2/sensor.py +++ b/homeassistant/components/hue/v2/sensor.py @@ -17,14 +17,13 @@ from aiohue.v2.models.light_level import LightLevel from aiohue.v2.models.temperature import Temperature from aiohue.v2.models.zigbee_connectivity import ZigbeeConnectivity -from homeassistant.components.binary_sensor import BinarySensorDeviceClass from homeassistant.components.sensor import ( SensorDeviceClass, SensorEntity, SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import LIGHT_LUX, PERCENTAGE, TEMP_CELSIUS +from homeassistant.const import LIGHT_LUX, PERCENTAGE, UnitOfTemperature from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -97,7 +96,7 @@ class HueSensorBase(HueBaseEntity, SensorEntity): class HueTemperatureSensor(HueSensorBase): """Representation of a Hue Temperature sensor.""" - _attr_native_unit_of_measurement = TEMP_CELSIUS + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS _attr_device_class = SensorDeviceClass.TEMPERATURE @property @@ -156,7 +155,6 @@ class HueBatterySensor(HueSensorBase): class HueZigbeeConnectivitySensor(HueSensorBase): """Representation of a Hue ZigbeeConnectivity sensor.""" - _attr_device_class = BinarySensorDeviceClass.CONNECTIVITY _attr_entity_category = EntityCategory.DIAGNOSTIC _attr_entity_registry_enabled_default = False diff --git a/homeassistant/components/huisbaasje/const.py b/homeassistant/components/huisbaasje/const.py index 481f11b2a36..9931b33a996 100644 --- a/homeassistant/components/huisbaasje/const.py +++ b/homeassistant/components/huisbaasje/const.py @@ -8,14 +8,13 @@ from energyflip.const import ( SOURCE_TYPE_GAS, ) -from homeassistant.components.sensor import SensorDeviceClass, SensorStateClass -from homeassistant.const import ENERGY_KILO_WATT_HOUR, TIME_HOURS, VOLUME_CUBIC_METERS +from homeassistant.const import UnitOfTime, UnitOfVolume DATA_COORDINATOR = "coordinator" DOMAIN = "huisbaasje" -FLOW_CUBIC_METERS_PER_HOUR = f"{VOLUME_CUBIC_METERS}/{TIME_HOURS}" +FLOW_CUBIC_METERS_PER_HOUR = f"{UnitOfVolume.CUBIC_METERS}/{UnitOfTime.HOURS}" """Interval in seconds between polls to huisbaasje.""" POLLING_INTERVAL = 20 @@ -37,146 +36,3 @@ SOURCE_TYPES = [ SOURCE_TYPE_ELECTRICITY_OUT_LOW, SOURCE_TYPE_GAS, ] - -SENSORS_INFO = [ - { - "name": "Huisbaasje Current Power", - "device_class": SensorDeviceClass.POWER, - "source_type": SOURCE_TYPE_ELECTRICITY, - }, - { - "name": "Huisbaasje Current Power In Peak", - "device_class": SensorDeviceClass.POWER, - "source_type": SOURCE_TYPE_ELECTRICITY_IN, - }, - { - "name": "Huisbaasje Current Power In Off Peak", - "device_class": SensorDeviceClass.POWER, - "source_type": SOURCE_TYPE_ELECTRICITY_IN_LOW, - }, - { - "name": "Huisbaasje Current Power Out Peak", - "device_class": SensorDeviceClass.POWER, - "source_type": SOURCE_TYPE_ELECTRICITY_OUT, - }, - { - "name": "Huisbaasje Current Power Out Off Peak", - "device_class": SensorDeviceClass.POWER, - "source_type": SOURCE_TYPE_ELECTRICITY_OUT_LOW, - }, - { - "name": "Huisbaasje Energy Consumption Peak Today", - "device_class": SensorDeviceClass.ENERGY, - "unit_of_measurement": ENERGY_KILO_WATT_HOUR, - "source_type": SOURCE_TYPE_ELECTRICITY_IN, - "sensor_type": SENSOR_TYPE_THIS_DAY, - "state_class": SensorStateClass.TOTAL_INCREASING, - "precision": 3, - }, - { - "name": "Huisbaasje Energy Consumption Off Peak Today", - "device_class": SensorDeviceClass.ENERGY, - "unit_of_measurement": ENERGY_KILO_WATT_HOUR, - "source_type": SOURCE_TYPE_ELECTRICITY_IN_LOW, - "sensor_type": SENSOR_TYPE_THIS_DAY, - "state_class": SensorStateClass.TOTAL_INCREASING, - "precision": 3, - }, - { - "name": "Huisbaasje Energy Production Peak Today", - "device_class": SensorDeviceClass.ENERGY, - "unit_of_measurement": ENERGY_KILO_WATT_HOUR, - "source_type": SOURCE_TYPE_ELECTRICITY_OUT, - "sensor_type": SENSOR_TYPE_THIS_DAY, - "state_class": SensorStateClass.TOTAL_INCREASING, - "precision": 3, - }, - { - "name": "Huisbaasje Energy Production Off Peak Today", - "device_class": SensorDeviceClass.ENERGY, - "unit_of_measurement": ENERGY_KILO_WATT_HOUR, - "source_type": SOURCE_TYPE_ELECTRICITY_OUT_LOW, - "sensor_type": SENSOR_TYPE_THIS_DAY, - "state_class": SensorStateClass.TOTAL_INCREASING, - "precision": 3, - }, - { - "name": "Huisbaasje Energy Today", - "device_class": SensorDeviceClass.ENERGY, - "unit_of_measurement": ENERGY_KILO_WATT_HOUR, - "source_type": SOURCE_TYPE_ELECTRICITY, - "sensor_type": SENSOR_TYPE_THIS_DAY, - "precision": 1, - }, - { - "name": "Huisbaasje Energy This Week", - "device_class": SensorDeviceClass.ENERGY, - "unit_of_measurement": ENERGY_KILO_WATT_HOUR, - "source_type": SOURCE_TYPE_ELECTRICITY, - "sensor_type": SENSOR_TYPE_THIS_WEEK, - "precision": 1, - }, - { - "name": "Huisbaasje Energy This Month", - "device_class": SensorDeviceClass.ENERGY, - "unit_of_measurement": ENERGY_KILO_WATT_HOUR, - "source_type": SOURCE_TYPE_ELECTRICITY, - "sensor_type": SENSOR_TYPE_THIS_MONTH, - "precision": 1, - }, - { - "name": "Huisbaasje Energy This Year", - "device_class": SensorDeviceClass.ENERGY, - "unit_of_measurement": ENERGY_KILO_WATT_HOUR, - "source_type": SOURCE_TYPE_ELECTRICITY, - "sensor_type": SENSOR_TYPE_THIS_YEAR, - "precision": 1, - }, - { - "name": "Huisbaasje Current Gas", - "unit_of_measurement": FLOW_CUBIC_METERS_PER_HOUR, - "source_type": SOURCE_TYPE_GAS, - "icon": "mdi:fire", - "precision": 1, - }, - { - "name": "Huisbaasje Gas Today", - "device_class": SensorDeviceClass.GAS, - "unit_of_measurement": VOLUME_CUBIC_METERS, - "source_type": SOURCE_TYPE_GAS, - "sensor_type": SENSOR_TYPE_THIS_DAY, - "state_class": SensorStateClass.TOTAL_INCREASING, - "icon": "mdi:counter", - "precision": 1, - }, - { - "name": "Huisbaasje Gas This Week", - "device_class": SensorDeviceClass.GAS, - "unit_of_measurement": VOLUME_CUBIC_METERS, - "source_type": SOURCE_TYPE_GAS, - "sensor_type": SENSOR_TYPE_THIS_WEEK, - "state_class": SensorStateClass.TOTAL_INCREASING, - "icon": "mdi:counter", - "precision": 1, - }, - { - "name": "Huisbaasje Gas This Month", - "device_class": SensorDeviceClass.GAS, - "unit_of_measurement": VOLUME_CUBIC_METERS, - "source_type": SOURCE_TYPE_GAS, - "sensor_type": SENSOR_TYPE_THIS_MONTH, - "state_class": SensorStateClass.TOTAL_INCREASING, - "icon": "mdi:counter", - "precision": 1, - }, - { - "name": "Huisbaasje Gas This Year", - "device_class": SensorDeviceClass.GAS, - "unit_of_measurement": VOLUME_CUBIC_METERS, - "source_type": SOURCE_TYPE_GAS, - "sensor_type": SENSOR_TYPE_THIS_YEAR, - "state_class": SensorStateClass.TOTAL_INCREASING, - "icon": "mdi:counter", - "precision": 1, - }, -] diff --git a/homeassistant/components/huisbaasje/sensor.py b/homeassistant/components/huisbaasje/sensor.py index 7c0060067c2..7117a977380 100644 --- a/homeassistant/components/huisbaasje/sensor.py +++ b/homeassistant/components/huisbaasje/sensor.py @@ -1,15 +1,26 @@ """Platform for sensor integration.""" from __future__ import annotations +from dataclasses import dataclass import logging +from energyflip.const import ( + SOURCE_TYPE_ELECTRICITY, + SOURCE_TYPE_ELECTRICITY_IN, + SOURCE_TYPE_ELECTRICITY_IN_LOW, + SOURCE_TYPE_ELECTRICITY_OUT, + SOURCE_TYPE_ELECTRICITY_OUT_LOW, + SOURCE_TYPE_GAS, +) + from homeassistant.components.sensor import ( SensorDeviceClass, SensorEntity, + SensorEntityDescription, SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_ID, POWER_WATT +from homeassistant.const import CONF_ID, UnitOfEnergy, UnitOfPower, UnitOfVolume from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import ( @@ -17,11 +28,206 @@ from homeassistant.helpers.update_coordinator import ( DataUpdateCoordinator, ) -from .const import DATA_COORDINATOR, DOMAIN, SENSOR_TYPE_RATE, SENSORS_INFO +from .const import ( + DATA_COORDINATOR, + DOMAIN, + FLOW_CUBIC_METERS_PER_HOUR, + SENSOR_TYPE_RATE, + SENSOR_TYPE_THIS_DAY, + SENSOR_TYPE_THIS_MONTH, + SENSOR_TYPE_THIS_WEEK, + SENSOR_TYPE_THIS_YEAR, +) _LOGGER = logging.getLogger(__name__) +@dataclass +class HuisbaasjeSensorEntityDescription(SensorEntityDescription): + """Class describing Airly sensor entities.""" + + sensor_type: str = SENSOR_TYPE_RATE + precision: int = 0 + + +SENSORS_INFO = [ + HuisbaasjeSensorEntityDescription( + name="Huisbaasje Current Power", + sensor_type=SENSOR_TYPE_RATE, + device_class=SensorDeviceClass.POWER, + native_unit_of_measurement=UnitOfPower.WATT, + key=SOURCE_TYPE_ELECTRICITY, + state_class=SensorStateClass.MEASUREMENT, + icon="mdi:lightning-bolt", + ), + HuisbaasjeSensorEntityDescription( + name="Huisbaasje Current Power In Peak", + sensor_type=SENSOR_TYPE_RATE, + device_class=SensorDeviceClass.POWER, + native_unit_of_measurement=UnitOfPower.WATT, + key=SOURCE_TYPE_ELECTRICITY_IN, + state_class=SensorStateClass.MEASUREMENT, + icon="mdi:lightning-bolt", + ), + HuisbaasjeSensorEntityDescription( + name="Huisbaasje Current Power In Off Peak", + sensor_type=SENSOR_TYPE_RATE, + device_class=SensorDeviceClass.POWER, + native_unit_of_measurement=UnitOfPower.WATT, + key=SOURCE_TYPE_ELECTRICITY_IN_LOW, + state_class=SensorStateClass.MEASUREMENT, + icon="mdi:lightning-bolt", + ), + HuisbaasjeSensorEntityDescription( + name="Huisbaasje Current Power Out Peak", + sensor_type=SENSOR_TYPE_RATE, + device_class=SensorDeviceClass.POWER, + native_unit_of_measurement=UnitOfPower.WATT, + key=SOURCE_TYPE_ELECTRICITY_OUT, + state_class=SensorStateClass.MEASUREMENT, + icon="mdi:lightning-bolt", + ), + HuisbaasjeSensorEntityDescription( + name="Huisbaasje Current Power Out Off Peak", + sensor_type=SENSOR_TYPE_RATE, + device_class=SensorDeviceClass.POWER, + native_unit_of_measurement=UnitOfPower.WATT, + key=SOURCE_TYPE_ELECTRICITY_OUT_LOW, + state_class=SensorStateClass.MEASUREMENT, + icon="mdi:lightning-bolt", + ), + HuisbaasjeSensorEntityDescription( + name="Huisbaasje Energy Consumption Peak Today", + device_class=SensorDeviceClass.ENERGY, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, + key=SOURCE_TYPE_ELECTRICITY_IN, + sensor_type=SENSOR_TYPE_THIS_DAY, + state_class=SensorStateClass.TOTAL_INCREASING, + precision=3, + icon="mdi:lightning-bolt", + ), + HuisbaasjeSensorEntityDescription( + name="Huisbaasje Energy Consumption Off Peak Today", + device_class=SensorDeviceClass.ENERGY, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, + key=SOURCE_TYPE_ELECTRICITY_IN_LOW, + sensor_type=SENSOR_TYPE_THIS_DAY, + state_class=SensorStateClass.TOTAL_INCREASING, + precision=3, + icon="mdi:lightning-bolt", + ), + HuisbaasjeSensorEntityDescription( + name="Huisbaasje Energy Production Peak Today", + device_class=SensorDeviceClass.ENERGY, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, + key=SOURCE_TYPE_ELECTRICITY_OUT, + sensor_type=SENSOR_TYPE_THIS_DAY, + state_class=SensorStateClass.TOTAL_INCREASING, + precision=3, + icon="mdi:lightning-bolt", + ), + HuisbaasjeSensorEntityDescription( + name="Huisbaasje Energy Production Off Peak Today", + device_class=SensorDeviceClass.ENERGY, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, + key=SOURCE_TYPE_ELECTRICITY_OUT_LOW, + sensor_type=SENSOR_TYPE_THIS_DAY, + state_class=SensorStateClass.TOTAL_INCREASING, + precision=3, + icon="mdi:lightning-bolt", + ), + HuisbaasjeSensorEntityDescription( + name="Huisbaasje Energy Today", + device_class=SensorDeviceClass.ENERGY, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, + state_class=SensorStateClass.MEASUREMENT, + key=SOURCE_TYPE_ELECTRICITY, + sensor_type=SENSOR_TYPE_THIS_DAY, + precision=1, + icon="mdi:lightning-bolt", + ), + HuisbaasjeSensorEntityDescription( + name="Huisbaasje Energy This Week", + device_class=SensorDeviceClass.ENERGY, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, + state_class=SensorStateClass.MEASUREMENT, + key=SOURCE_TYPE_ELECTRICITY, + sensor_type=SENSOR_TYPE_THIS_WEEK, + precision=1, + icon="mdi:lightning-bolt", + ), + HuisbaasjeSensorEntityDescription( + name="Huisbaasje Energy This Month", + device_class=SensorDeviceClass.ENERGY, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, + state_class=SensorStateClass.MEASUREMENT, + key=SOURCE_TYPE_ELECTRICITY, + sensor_type=SENSOR_TYPE_THIS_MONTH, + precision=1, + icon="mdi:lightning-bolt", + ), + HuisbaasjeSensorEntityDescription( + name="Huisbaasje Energy This Year", + device_class=SensorDeviceClass.ENERGY, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, + state_class=SensorStateClass.MEASUREMENT, + key=SOURCE_TYPE_ELECTRICITY, + sensor_type=SENSOR_TYPE_THIS_YEAR, + precision=1, + icon="mdi:lightning-bolt", + ), + HuisbaasjeSensorEntityDescription( + name="Huisbaasje Current Gas", + native_unit_of_measurement=FLOW_CUBIC_METERS_PER_HOUR, + sensor_type=SENSOR_TYPE_RATE, + state_class=SensorStateClass.MEASUREMENT, + key=SOURCE_TYPE_GAS, + icon="mdi:fire", + precision=1, + ), + HuisbaasjeSensorEntityDescription( + name="Huisbaasje Gas Today", + device_class=SensorDeviceClass.GAS, + native_unit_of_measurement=UnitOfVolume.CUBIC_METERS, + key=SOURCE_TYPE_GAS, + sensor_type=SENSOR_TYPE_THIS_DAY, + state_class=SensorStateClass.TOTAL_INCREASING, + icon="mdi:counter", + precision=1, + ), + HuisbaasjeSensorEntityDescription( + name="Huisbaasje Gas This Week", + device_class=SensorDeviceClass.GAS, + native_unit_of_measurement=UnitOfVolume.CUBIC_METERS, + key=SOURCE_TYPE_GAS, + sensor_type=SENSOR_TYPE_THIS_WEEK, + state_class=SensorStateClass.TOTAL_INCREASING, + icon="mdi:counter", + precision=1, + ), + HuisbaasjeSensorEntityDescription( + name="Huisbaasje Gas This Month", + device_class=SensorDeviceClass.GAS, + native_unit_of_measurement=UnitOfVolume.CUBIC_METERS, + key=SOURCE_TYPE_GAS, + sensor_type=SENSOR_TYPE_THIS_MONTH, + state_class=SensorStateClass.TOTAL_INCREASING, + icon="mdi:counter", + precision=1, + ), + HuisbaasjeSensorEntityDescription( + name="Huisbaasje Gas This Year", + device_class=SensorDeviceClass.GAS, + native_unit_of_measurement=UnitOfVolume.CUBIC_METERS, + key=SOURCE_TYPE_GAS, + sensor_type=SENSOR_TYPE_THIS_YEAR, + state_class=SensorStateClass.TOTAL_INCREASING, + icon="mdi:counter", + precision=1, + ), +] + + async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, @@ -32,74 +238,43 @@ async def async_setup_entry( user_id = config_entry.data[CONF_ID] async_add_entities( - HuisbaasjeSensor(coordinator, user_id=user_id, **sensor_info) - for sensor_info in SENSORS_INFO + HuisbaasjeSensor(coordinator, user_id, description) + for description in SENSORS_INFO ) class HuisbaasjeSensor(CoordinatorEntity, SensorEntity): """Defines a Huisbaasje sensor.""" + entity_description: HuisbaasjeSensorEntityDescription + def __init__( self, coordinator: DataUpdateCoordinator, user_id: str, - name: str, - source_type: str, - device_class: SensorDeviceClass | None = None, - sensor_type: str = SENSOR_TYPE_RATE, - unit_of_measurement: str = POWER_WATT, - icon: str = "mdi:lightning-bolt", - precision: int = 0, - state_class: str | None = SensorStateClass.MEASUREMENT, + description: HuisbaasjeSensorEntityDescription, ) -> None: """Initialize the sensor.""" super().__init__(coordinator) - self._user_id = user_id - self._name = name - self._device_class = device_class - self._unit_of_measurement = unit_of_measurement - self._source_type = source_type - self._sensor_type = sensor_type - self._icon = icon - self._precision = precision - self._attr_state_class = state_class + self.entity_description = description + self._source_type = description.key + self._sensor_type = description.sensor_type + self._precision = description.precision + self._attr_unique_id = ( + f"{DOMAIN}_{user_id}_{description.key}_{description.sensor_type}" + ) @property - def unique_id(self) -> str: - """Return an unique id for the sensor.""" - return f"{DOMAIN}_{self._user_id}_{self._source_type}_{self._sensor_type}" - - @property - def name(self) -> str: - """Return the name of the sensor.""" - return self._name - - @property - def device_class(self) -> SensorDeviceClass | None: - """Return the device class of the sensor.""" - return self._device_class - - @property - def icon(self) -> str: - """Return the icon to use for the sensor.""" - return self._icon - - @property - def native_value(self): + def native_value(self) -> int | float | None: """Return the state of the sensor.""" - if self.coordinator.data[self._source_type][self._sensor_type] is not None: - return round( - self.coordinator.data[self._source_type][self._sensor_type], - self._precision, - ) + if ( + data := self.coordinator.data[self.entity_description.key][ + self.entity_description.sensor_type + ] + ) is not None: + return round(data, self._precision) return None - @property - def native_unit_of_measurement(self) -> str: - """Return the unit of measurement.""" - return self._unit_of_measurement - @property def available(self) -> bool: """Return if entity is available.""" diff --git a/homeassistant/components/huisbaasje/translations/pt.json b/homeassistant/components/huisbaasje/translations/pt.json index a2f32087684..47b25129962 100644 --- a/homeassistant/components/huisbaasje/translations/pt.json +++ b/homeassistant/components/huisbaasje/translations/pt.json @@ -1,7 +1,7 @@ { "config": { "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida" }, "step": { diff --git a/homeassistant/components/humidifier/__init__.py b/homeassistant/components/humidifier/__init__.py index 61d7b6c3944..0bc7e242d55 100644 --- a/homeassistant/components/humidifier/__init__.py +++ b/homeassistant/components/humidifier/__init__.py @@ -125,7 +125,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: class HumidifierEntityDescription(ToggleEntityDescription): """A class that describes humidifier entities.""" - device_class: HumidifierDeviceClass | str | None = None + device_class: HumidifierDeviceClass | None = None class HumidifierEntity(ToggleEntity): @@ -133,7 +133,7 @@ class HumidifierEntity(ToggleEntity): entity_description: HumidifierEntityDescription _attr_available_modes: list[str] | None - _attr_device_class: HumidifierDeviceClass | str | None + _attr_device_class: HumidifierDeviceClass | None _attr_max_humidity: int = DEFAULT_MAX_HUMIDITY _attr_min_humidity: int = DEFAULT_MIN_HUMIDITY _attr_mode: str | None @@ -154,7 +154,7 @@ class HumidifierEntity(ToggleEntity): return data @property - def device_class(self) -> HumidifierDeviceClass | str | None: + def device_class(self) -> HumidifierDeviceClass | None: """Return the class of this entity.""" if hasattr(self, "_attr_device_class"): return self._attr_device_class diff --git a/homeassistant/components/humidifier/device_trigger.py b/homeassistant/components/humidifier/device_trigger.py index b9abb231dfd..ed1620c51a2 100644 --- a/homeassistant/components/humidifier/device_trigger.py +++ b/homeassistant/components/humidifier/device_trigger.py @@ -87,7 +87,9 @@ async def async_attach_trigger( numeric_state_config = { numeric_state_trigger.CONF_PLATFORM: "numeric_state", numeric_state_trigger.CONF_ENTITY_ID: config[CONF_ENTITY_ID], - numeric_state_trigger.CONF_VALUE_TEMPLATE: "{{ state.attributes.humidity }}", + numeric_state_trigger.CONF_VALUE_TEMPLATE: ( + "{{ state.attributes.humidity }}" + ), } if CONF_ABOVE in config: diff --git a/homeassistant/components/humidifier/intent.py b/homeassistant/components/humidifier/intent.py index 57f42f58fe0..4d28cf5838c 100644 --- a/homeassistant/components/humidifier/intent.py +++ b/homeassistant/components/humidifier/intent.py @@ -1,4 +1,6 @@ """Intents for the humidifier integration.""" +from __future__ import annotations + import voluptuous as vol from homeassistant.const import ATTR_ENTITY_ID, ATTR_MODE, STATE_OFF diff --git a/homeassistant/components/humidifier/translations/sk.json b/homeassistant/components/humidifier/translations/sk.json index d9cd16a451c..d8827c6a14f 100644 --- a/homeassistant/components/humidifier/translations/sk.json +++ b/homeassistant/components/humidifier/translations/sk.json @@ -2,16 +2,21 @@ "device_automation": { "action_type": { "set_humidity": "Nastavi\u0165 vlhkos\u0165 pre {entity_name}", + "set_mode": "Zmeni\u0165 re\u017eim na {entity_name}", + "toggle": "Prepn\u00fa\u0165 {entity_name}", "turn_off": "Vypn\u00fa\u0165 {entity_name}", "turn_on": "Zapn\u00fa\u0165 {entity_name}" }, "condition_type": { + "is_mode": "{entity_name} je nastaven\u00fd na \u0161pecifick\u00fd re\u017eim", "is_off": "{entity_name} je vypnut\u00e9", "is_on": "{entity_name} je zapnut\u00e9" }, "trigger_type": { "changed_states": "{entity_name} zapnut\u00e9 alebo vypnut\u00e9", - "target_humidity_changed": "Cie\u013eov\u00e1 vlhkos\u0165 {entity_name} sa zmenila" + "target_humidity_changed": "Cie\u013eov\u00e1 vlhkos\u0165 {entity_name} sa zmenila", + "turned_off": "{entity_name} vypnut\u00e9", + "turned_on": "{entity_name} zapnut\u00e9" } }, "state": { diff --git a/homeassistant/components/hunterdouglas_powerview/entity.py b/homeassistant/components/hunterdouglas_powerview/entity.py index a2bbf39fb96..655eb572c31 100644 --- a/homeassistant/components/hunterdouglas_powerview/entity.py +++ b/homeassistant/components/hunterdouglas_powerview/entity.py @@ -95,7 +95,9 @@ class ShadeEntity(HDEntity): manufacturer=MANUFACTURER, model=str(self._shade.raw_data[ATTR_TYPE]), via_device=(DOMAIN, self._device_info.serial_number), - configuration_url=f"http://{self._device_info.hub_address}/api/shades/{self._shade.id}", + configuration_url=( + f"http://{self._device_info.hub_address}/api/shades/{self._shade.id}" + ), ) for shade in self._shade.shade_types: diff --git a/homeassistant/components/hunterdouglas_powerview/strings.json b/homeassistant/components/hunterdouglas_powerview/strings.json index e78cc105db6..41a16408783 100644 --- a/homeassistant/components/hunterdouglas_powerview/strings.json +++ b/homeassistant/components/hunterdouglas_powerview/strings.json @@ -9,7 +9,7 @@ }, "link": { "title": "Connect to the PowerView Hub", - "description": "Do you want to setup {name} ({host})?" + "description": "Do you want to set up {name} ({host})?" } }, "flow_title": "{name} ({host})", diff --git a/homeassistant/components/hunterdouglas_powerview/translations/en.json b/homeassistant/components/hunterdouglas_powerview/translations/en.json index d95d7451b1a..dd9a014a3c6 100644 --- a/homeassistant/components/hunterdouglas_powerview/translations/en.json +++ b/homeassistant/components/hunterdouglas_powerview/translations/en.json @@ -10,7 +10,7 @@ "flow_title": "{name} ({host})", "step": { "link": { - "description": "Do you want to setup {name} ({host})?", + "description": "Do you want to set up {name} ({host})?", "title": "Connect to the PowerView Hub" }, "user": { diff --git a/homeassistant/components/hunterdouglas_powerview/translations/it.json b/homeassistant/components/hunterdouglas_powerview/translations/it.json index 6a5606b05f4..90da53436e7 100644 --- a/homeassistant/components/hunterdouglas_powerview/translations/it.json +++ b/homeassistant/components/hunterdouglas_powerview/translations/it.json @@ -10,7 +10,7 @@ "flow_title": "{name} ({host})", "step": { "link": { - "description": "Vuoi impostare {name} ({host})?", + "description": "Vuoi configurare {name} ({host})?", "title": "Connettiti all'Hub PowerView" }, "user": { diff --git a/homeassistant/components/hunterdouglas_powerview/translations/no.json b/homeassistant/components/hunterdouglas_powerview/translations/no.json index 36c1702fde8..a8e6cb8eadc 100644 --- a/homeassistant/components/hunterdouglas_powerview/translations/no.json +++ b/homeassistant/components/hunterdouglas_powerview/translations/no.json @@ -10,7 +10,7 @@ "flow_title": "{name} ({host})", "step": { "link": { - "description": "Vil du konfigurere {name} ({host})?", + "description": "Vil du sette opp {name} ( {host} )?", "title": "Koble til PowerView-huben" }, "user": { diff --git a/homeassistant/components/hunterdouglas_powerview/translations/pt-BR.json b/homeassistant/components/hunterdouglas_powerview/translations/pt-BR.json index b170cb59882..a8f12529a30 100644 --- a/homeassistant/components/hunterdouglas_powerview/translations/pt-BR.json +++ b/homeassistant/components/hunterdouglas_powerview/translations/pt-BR.json @@ -10,7 +10,7 @@ "flow_title": "{name} ( {host} )", "step": { "link": { - "description": "Deseja configurar {name} ( {host} )?", + "description": "Deseja configurar {name} ({host})?", "title": "Conecte-se ao PowerView Hub" }, "user": { diff --git a/homeassistant/components/hunterdouglas_powerview/translations/pt.json b/homeassistant/components/hunterdouglas_powerview/translations/pt.json index ef5279e090a..6b3cc9437a2 100644 --- a/homeassistant/components/hunterdouglas_powerview/translations/pt.json +++ b/homeassistant/components/hunterdouglas_powerview/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "unknown": "Erro inesperado" }, "step": { diff --git a/homeassistant/components/hunterdouglas_powerview/translations/sk.json b/homeassistant/components/hunterdouglas_powerview/translations/sk.json index bd0a4fe1d64..cc09a948731 100644 --- a/homeassistant/components/hunterdouglas_powerview/translations/sk.json +++ b/homeassistant/components/hunterdouglas_powerview/translations/sk.json @@ -10,12 +10,14 @@ "flow_title": "{name} ({host})", "step": { "link": { - "description": "Chcete nastavi\u0165 {name} ({host})?" + "description": "Chcete nastavi\u0165 {name} ({host})?", + "title": "Pripojte sa k hubu PowerView" }, "user": { "data": { "host": "IP adresa" - } + }, + "title": "Pripojte sa k hubu PowerView" } } } diff --git a/homeassistant/components/hvv_departures/config_flow.py b/homeassistant/components/hvv_departures/config_flow.py index d96ab359dda..5f07593eeb5 100644 --- a/homeassistant/components/hvv_departures/config_flow.py +++ b/homeassistant/components/hvv_departures/config_flow.py @@ -198,7 +198,10 @@ class OptionsFlowHandler(config_entries.OptionsFlow): { vol.Optional(CONF_FILTER, default=old_filter): cv.multi_select( { - key: f"{departure_filter['serviceName']}, {departure_filter['label']}" + key: ( + f"{departure_filter['serviceName']}," + f" {departure_filter['label']}" + ) for key, departure_filter in self.departure_filters.items() } ), diff --git a/homeassistant/components/hvv_departures/translations/pt.json b/homeassistant/components/hvv_departures/translations/pt.json index cbd43a04cfd..026a02553be 100644 --- a/homeassistant/components/hvv_departures/translations/pt.json +++ b/homeassistant/components/hvv_departures/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida" }, "step": { diff --git a/homeassistant/components/hvv_departures/translations/sk.json b/homeassistant/components/hvv_departures/translations/sk.json index bacfbd506d5..1a3adf21139 100644 --- a/homeassistant/components/hvv_departures/translations/sk.json +++ b/homeassistant/components/hvv_departures/translations/sk.json @@ -35,8 +35,11 @@ "step": { "init": { "data": { - "offset": "Posun (v min\u00fatach)" + "filter": "Vyberte riadky", + "offset": "Posun (v min\u00fatach)", + "real_time": "Pou\u017e\u00edvanie \u00fadajov v re\u00e1lnom \u010dase" }, + "description": "Mo\u017enosti zmeny tohto sn\u00edma\u010da odchodu", "title": "Mo\u017enosti" } } diff --git a/homeassistant/components/hydrawise/sensor.py b/homeassistant/components/hydrawise/sensor.py index f9201cc0420..3114a50673f 100644 --- a/homeassistant/components/hydrawise/sensor.py +++ b/homeassistant/components/hydrawise/sensor.py @@ -11,7 +11,7 @@ from homeassistant.components.sensor import ( SensorEntity, SensorEntityDescription, ) -from homeassistant.const import CONF_MONITORED_CONDITIONS, TIME_MINUTES +from homeassistant.const import CONF_MONITORED_CONDITIONS, UnitOfTime from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -32,7 +32,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( key="watering_time", name="Watering Time", icon="mdi:water-pump", - native_unit_of_measurement=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, ), ) diff --git a/homeassistant/components/hyperion/__init__.py b/homeassistant/components/hyperion/__init__.py index ec478883e8f..c3cd473c3f4 100644 --- a/homeassistant/components/hyperion/__init__.py +++ b/homeassistant/components/hyperion/__init__.py @@ -153,9 +153,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: with suppress(ValueError): if AwesomeVersion(version) < AwesomeVersion(HYPERION_VERSION_WARN_CUTOFF): _LOGGER.warning( - "Using a Hyperion server version < %s is not recommended -- " - "some features may be unavailable or may not function correctly. " - "Please consider upgrading: %s", + ( + "Using a Hyperion server version < %s is not recommended --" + " some features may be unavailable or may not function" + " correctly. Please consider upgrading: %s" + ), HYPERION_VERSION_WARN_CUTOFF, HYPERION_RELEASES_URL, ) @@ -241,7 +243,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: instance_name, ) - # Remove entities that are are not running instances on Hyperion. + # Remove entities that are not running instances on Hyperion. for instance_num in set(existing_instances) - running_instances: del existing_instances[instance_num] async_dispatcher_send( diff --git a/homeassistant/components/hyperion/const.py b/homeassistant/components/hyperion/const.py index 271618fb962..e7e4e7f70a4 100644 --- a/homeassistant/components/hyperion/const.py +++ b/homeassistant/components/hyperion/const.py @@ -26,9 +26,9 @@ NAME_SUFFIX_HYPERION_PRIORITY_LIGHT = "Priority" NAME_SUFFIX_HYPERION_COMPONENT_SWITCH = "Component" NAME_SUFFIX_HYPERION_CAMERA = "" -SIGNAL_INSTANCE_ADD = f"{DOMAIN}_instance_add_signal." "{}" -SIGNAL_INSTANCE_REMOVE = f"{DOMAIN}_instance_remove_signal." "{}" -SIGNAL_ENTITY_REMOVE = f"{DOMAIN}_entity_remove_signal." "{}" +SIGNAL_INSTANCE_ADD = f"{DOMAIN}_instance_add_signal.{{}}" +SIGNAL_INSTANCE_REMOVE = f"{DOMAIN}_instance_remove_signal.{{}}" +SIGNAL_ENTITY_REMOVE = f"{DOMAIN}_entity_remove_signal.{{}}" TYPE_HYPERION_CAMERA = "hyperion_camera" TYPE_HYPERION_LIGHT = "hyperion_light" diff --git a/homeassistant/components/hyperion/light.py b/homeassistant/components/hyperion/light.py index 49177ac94c6..ba1dbfbafc2 100644 --- a/homeassistant/components/hyperion/light.py +++ b/homeassistant/components/hyperion/light.py @@ -292,8 +292,10 @@ class HyperionBaseLight(LightEntity): component = const.KEY_COMPONENTID_FROM_NAME[effect] else: _LOGGER.warning( - "Use of Hyperion effect '%s' is deprecated and will be removed " - "in a future release. Please use '%s' instead", + ( + "Use of Hyperion effect '%s' is deprecated and will be removed " + "in a future release. Please use '%s' instead" + ), effect, const.KEY_COMPONENTID_TO_NAME[effect], ) @@ -433,8 +435,10 @@ class HyperionBaseLight(LightEntity): self._update_effect_list() _LOGGER.debug( - "Hyperion full state update: On=%s,Brightness=%i,Effect=%s " - "(%i effects total),Color=%s", + ( + "Hyperion full state update: On=%s,Brightness=%i,Effect=%s " + "(%i effects total),Color=%s" + ), self.is_on, self._brightness, self._effect, diff --git a/homeassistant/components/hyperion/translations/en_GB.json b/homeassistant/components/hyperion/translations/en-GB.json similarity index 100% rename from homeassistant/components/hyperion/translations/en_GB.json rename to homeassistant/components/hyperion/translations/en-GB.json diff --git a/homeassistant/components/hyperion/translations/pt.json b/homeassistant/components/hyperion/translations/pt.json index 0a402a87037..79b48b07516 100644 --- a/homeassistant/components/hyperion/translations/pt.json +++ b/homeassistant/components/hyperion/translations/pt.json @@ -3,10 +3,10 @@ "abort": { "already_configured": "O servi\u00e7o j\u00e1 est\u00e1 configurado", "already_in_progress": "O processo de configura\u00e7\u00e3o j\u00e1 est\u00e1 a decorrer", - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_access_token": "Token de acesso inv\u00e1lido" }, "step": { @@ -17,7 +17,7 @@ }, "user": { "data": { - "host": "Servidor", + "host": "Endere\u00e7o", "port": "Porta" } } diff --git a/homeassistant/components/hyperion/translations/sk.json b/homeassistant/components/hyperion/translations/sk.json index 270b19bdc1a..39c8ef5b4ef 100644 --- a/homeassistant/components/hyperion/translations/sk.json +++ b/homeassistant/components/hyperion/translations/sk.json @@ -5,7 +5,9 @@ "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "auth_new_token_not_granted_error": "Novovytvoren\u00fd token nebol schv\u00e1len\u00fd v pou\u017e\u00edvate\u013eskom rozhran\u00ed Hyperion", "auth_new_token_not_work_error": "Nepodarilo sa overi\u0165 pomocou novovytvoren\u00e9ho tokenu", + "auth_required_error": "Nepodarilo sa ur\u010di\u0165, \u010di je potrebn\u00e1 autoriz\u00e1cia", "cannot_connect": "Nepodarilo sa pripoji\u0165", + "no_id": "In\u0161tancia Hyperion Ambilight nenahl\u00e1sila svoje ID", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { @@ -17,9 +19,15 @@ "data": { "create_token": "Automaticky vytvori\u0165 nov\u00fd token", "token": "Alebo poskytnite u\u017e existuj\u00faci token" - } + }, + "description": "Nakonfigurujte autoriz\u00e1ciu pre v\u00e1\u0161 server Hyperion Ambilight" + }, + "confirm": { + "description": "Chcete prida\u0165 tento Hyperion Ambilight do Home Assistant? \n\n **Hostite\u013e:** {host}\n **Port:** {port}\n **ID**: {id}", + "title": "Potvr\u010fte pridanie slu\u017eby Hyperion Ambilight" }, "create_token": { + "description": "Ak chcete po\u017eiada\u0165 o nov\u00fd overovac\u00ed token, ni\u017e\u0161ie vyberte mo\u017enos\u0165 **Odosla\u0165**. Budete presmerovan\u00ed do pou\u017e\u00edvate\u013esk\u00e9ho rozhrania Hyperion, aby ste schv\u00e1lili po\u017eiadavku. Overte, \u017ee zobrazen\u00e9 ID je \u201e{auth_id}\u201c", "title": "Automaticky vytvori\u0165 nov\u00fd autentifika\u010dn\u00fd token" }, "create_token_external": { @@ -32,5 +40,15 @@ } } } + }, + "options": { + "step": { + "init": { + "data": { + "effect_show_list": "Efekty Hyperionu na zobrazenie", + "priority": "Priorita Hyperion na pou\u017e\u00edvanie farieb a efektov" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/ialarm/__init__.py b/homeassistant/components/ialarm/__init__.py index 374ba29dccf..b258c702725 100644 --- a/homeassistant/components/ialarm/__init__.py +++ b/homeassistant/components/ialarm/__init__.py @@ -54,7 +54,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return unload_ok -class IAlarmDataUpdateCoordinator(DataUpdateCoordinator): +class IAlarmDataUpdateCoordinator(DataUpdateCoordinator[None]): """Class to manage fetching iAlarm data.""" def __init__(self, hass: HomeAssistant, ialarm: IAlarm, mac: str) -> None: diff --git a/homeassistant/components/iaqualink/climate.py b/homeassistant/components/iaqualink/climate.py index 408bd56778e..7c67dbdea4b 100644 --- a/homeassistant/components/iaqualink/climate.py +++ b/homeassistant/components/iaqualink/climate.py @@ -11,7 +11,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS, TEMP_FAHRENHEIT +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -68,8 +68,8 @@ class HassAqualinkThermostat(AqualinkEntity, ClimateEntity): def temperature_unit(self) -> str: """Return the unit of measurement.""" if self.dev.unit == "F": - return TEMP_FAHRENHEIT - return TEMP_CELSIUS + return UnitOfTemperature.FAHRENHEIT + return UnitOfTemperature.CELSIUS @property def min_temp(self) -> int: diff --git a/homeassistant/components/iaqualink/const.py b/homeassistant/components/iaqualink/const.py index 7cabfa2b4f6..ef939e3fc52 100644 --- a/homeassistant/components/iaqualink/const.py +++ b/homeassistant/components/iaqualink/const.py @@ -1,4 +1,4 @@ -"""Constants for the the iaqualink component.""" +"""Constants for the iaqualink component.""" from datetime import timedelta DOMAIN = "iaqualink" diff --git a/homeassistant/components/iaqualink/sensor.py b/homeassistant/components/iaqualink/sensor.py index f1636a09c90..8086aa29ee0 100644 --- a/homeassistant/components/iaqualink/sensor.py +++ b/homeassistant/components/iaqualink/sensor.py @@ -3,7 +3,7 @@ from __future__ import annotations from homeassistant.components.sensor import DOMAIN, SensorDeviceClass, SensorEntity from homeassistant.config_entries import ConfigEntry -from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT +from homeassistant.const import UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -38,8 +38,8 @@ class HassAqualinkSensor(AqualinkEntity, SensorEntity): """Return the measurement unit for the sensor.""" if self.dev.name.endswith("_temp"): if self.dev.system.temp_unit == "F": - return TEMP_FAHRENHEIT - return TEMP_CELSIUS + return UnitOfTemperature.FAHRENHEIT + return UnitOfTemperature.CELSIUS return None @property diff --git a/homeassistant/components/iaqualink/translations/de.json b/homeassistant/components/iaqualink/translations/de.json index 79a845ea668..bcaa7f908b0 100644 --- a/homeassistant/components/iaqualink/translations/de.json +++ b/homeassistant/components/iaqualink/translations/de.json @@ -13,7 +13,7 @@ "password": "Passwort", "username": "Benutzername" }, - "description": "Bitte gib den Benutzernamen und das Passwort f\u00fcr dein iAqualink-Konto ein.", + "description": "Bitte gib den Benutzernamen und das Passwort f\u00fcr dein iAqualink Konto ein.", "title": "Mit iAqualink verbinden" } } diff --git a/homeassistant/components/iaqualink/translations/ko.json b/homeassistant/components/iaqualink/translations/ko.json index ef77daf978b..1e115decddd 100644 --- a/homeassistant/components/iaqualink/translations/ko.json +++ b/homeassistant/components/iaqualink/translations/ko.json @@ -4,7 +4,8 @@ "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." }, "error": { - "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" }, "step": { "user": { diff --git a/homeassistant/components/iaqualink/translations/pt.json b/homeassistant/components/iaqualink/translations/pt.json index f73c9be5561..9124f7508f3 100644 --- a/homeassistant/components/iaqualink/translations/pt.json +++ b/homeassistant/components/iaqualink/translations/pt.json @@ -4,7 +4,7 @@ "single_instance_allowed": "J\u00e1 configurado. Apenas uma \u00fanica configura\u00e7\u00e3o \u00e9 poss\u00edvel." }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "user": { diff --git a/homeassistant/components/ibeacon/sensor.py b/homeassistant/components/ibeacon/sensor.py index 32c17957b60..c0b9e92decc 100644 --- a/homeassistant/components/ibeacon/sensor.py +++ b/homeassistant/components/ibeacon/sensor.py @@ -13,7 +13,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import LENGTH_METERS, SIGNAL_STRENGTH_DECIBELS_MILLIWATT +from homeassistant.const import SIGNAL_STRENGTH_DECIBELS_MILLIWATT, UnitOfLength from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -58,7 +58,7 @@ SENSOR_DESCRIPTIONS = ( key="estimated_distance", name="Estimated Distance", icon="mdi:signal-distance-variant", - native_unit_of_measurement=LENGTH_METERS, + native_unit_of_measurement=UnitOfLength.METERS, value_fn=lambda ibeacon_advertisement: ibeacon_advertisement.distance, state_class=SensorStateClass.MEASUREMENT, device_class=SensorDeviceClass.DISTANCE, diff --git a/homeassistant/components/ibeacon/strings.json b/homeassistant/components/ibeacon/strings.json index e2a1ab8393f..b91ba459bd7 100644 --- a/homeassistant/components/ibeacon/strings.json +++ b/homeassistant/components/ibeacon/strings.json @@ -2,7 +2,7 @@ "config": { "step": { "user": { - "description": "Do you want to setup iBeacon Tracker?" + "description": "Do you want to set up iBeacon Tracker?" } }, "abort": { diff --git a/homeassistant/components/ibeacon/translations/en.json b/homeassistant/components/ibeacon/translations/en.json index 1125e778b19..8b83379e18f 100644 --- a/homeassistant/components/ibeacon/translations/en.json +++ b/homeassistant/components/ibeacon/translations/en.json @@ -6,7 +6,7 @@ }, "step": { "user": { - "description": "Do you want to setup iBeacon Tracker?" + "description": "Do you want to set up iBeacon Tracker?" } } }, diff --git a/homeassistant/components/ibeacon/translations/et.json b/homeassistant/components/ibeacon/translations/et.json index 34d319b2a08..3ede0968bff 100644 --- a/homeassistant/components/ibeacon/translations/et.json +++ b/homeassistant/components/ibeacon/translations/et.json @@ -6,7 +6,7 @@ }, "step": { "user": { - "description": "Kas soovite iBeacon Tracker?" + "description": "Kas seadistada iBeacon Tracker?" } } }, diff --git a/homeassistant/components/ibeacon/translations/he.json b/homeassistant/components/ibeacon/translations/he.json index 04f865d0d69..b33cc27fa30 100644 --- a/homeassistant/components/ibeacon/translations/he.json +++ b/homeassistant/components/ibeacon/translations/he.json @@ -15,7 +15,8 @@ "init": { "data": { "min_rssi": "RSSI \u05de\u05d9\u05e0\u05d9\u05de\u05dc\u05d9" - } + }, + "description": "iBeacons \u05e2\u05dd \u05e2\u05e8\u05da RSSI \u05e0\u05de\u05d5\u05da \u05de\u05d4-RSSI \u05d4\u05de\u05d9\u05e0\u05d9\u05de\u05dc\u05d9 \u05d9\u05ea\u05e2\u05dc\u05de\u05d5. \u05d0\u05dd \u05d4\u05e9\u05d9\u05dc\u05d5\u05d1 \u05e8\u05d5\u05d0\u05d4 iBeacons \u05e9\u05db\u05e0\u05d9\u05dd, \u05d4\u05d2\u05d3\u05dc\u05ea \u05e2\u05e8\u05da \u05d6\u05d4 \u05e2\u05e9\u05d5\u05d9\u05d4 \u05dc\u05e2\u05d6\u05d5\u05e8." } } } diff --git a/homeassistant/components/ibeacon/translations/ko.json b/homeassistant/components/ibeacon/translations/ko.json new file mode 100644 index 00000000000..416fb8d164e --- /dev/null +++ b/homeassistant/components/ibeacon/translations/ko.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ibeacon/translations/pt.json b/homeassistant/components/ibeacon/translations/pt.json new file mode 100644 index 00000000000..25538aa0036 --- /dev/null +++ b/homeassistant/components/ibeacon/translations/pt.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "J\u00e1 configurado. Apenas uma \u00fanica configura\u00e7\u00e3o \u00e9 poss\u00edvel." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ibeacon/translations/sk.json b/homeassistant/components/ibeacon/translations/sk.json index c294bc45d7c..9caf41e25a3 100644 --- a/homeassistant/components/ibeacon/translations/sk.json +++ b/homeassistant/components/ibeacon/translations/sk.json @@ -1,7 +1,23 @@ { "config": { "abort": { + "bluetooth_not_available": "Na pou\u017e\u00edvanie iBeacon Tracker mus\u00ed by\u0165 nakonfigurovan\u00fd aspo\u0148 jeden adapt\u00e9r Bluetooth alebo dia\u013ekov\u00e9 ovl\u00e1danie.", "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, + "step": { + "user": { + "description": "Chcete nastavi\u0165 iBeacon Tracker?" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "min_rssi": "Minim\u00e1lne RSSI" + }, + "description": "iBeacons s hodnotou RSSI ni\u017e\u0161ou ako je minimum RSSI bud\u00fa ignorovan\u00e9. Ak integr\u00e1cia vid\u00ed susedn\u00e9 iBeacony, zv\u00fd\u0161enie tejto hodnoty m\u00f4\u017ee pom\u00f4c\u0165." + } } } } \ No newline at end of file diff --git a/homeassistant/components/icloud/translations/ko.json b/homeassistant/components/icloud/translations/ko.json index fcc89a25722..4ca9664272f 100644 --- a/homeassistant/components/icloud/translations/ko.json +++ b/homeassistant/components/icloud/translations/ko.json @@ -11,6 +11,12 @@ "validate_verification_code": "\uc778\uc99d \ucf54\ub4dc \ud655\uc778\uc5d0 \uc2e4\ud328\ud558\uc600\uc2b5\ub2c8\ub2e4. \ub2e4\uc2dc \uc2dc\ub3c4\ud574\uc8fc\uc138\uc694" }, "step": { + "reauth_confirm": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638" + }, + "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" + }, "trusted_device": { "data": { "trusted_device": "\uc2e0\ub8b0\ud560 \uc218 \uc788\ub294 \uae30\uae30" diff --git a/homeassistant/components/icloud/translations/pt.json b/homeassistant/components/icloud/translations/pt.json index 7eb0b4f4498..f0f7272b189 100644 --- a/homeassistant/components/icloud/translations/pt.json +++ b/homeassistant/components/icloud/translations/pt.json @@ -9,7 +9,11 @@ }, "step": { "reauth_confirm": { - "description": "Sua senha inserida anteriormente para {username} n\u00e3o est\u00e1 mais funcionando. Atualize sua senha para continuar usando esta integra\u00e7\u00e3o." + "data": { + "password": "Palavra-passe" + }, + "description": "Sua senha inserida anteriormente para {username} n\u00e3o est\u00e1 mais funcionando. Atualize sua senha para continuar usando esta integra\u00e7\u00e3o.", + "title": "Reautenticar integra\u00e7\u00e3o" }, "trusted_device": { "data": { @@ -19,7 +23,7 @@ "user": { "data": { "password": "Palavra-passe", - "username": "Email", + "username": "", "with_family": "Com a fam\u00edlia" } } diff --git a/homeassistant/components/icloud/translations/sk.json b/homeassistant/components/icloud/translations/sk.json index fbd317aec47..6c58bf6984a 100644 --- a/homeassistant/components/icloud/translations/sk.json +++ b/homeassistant/components/icloud/translations/sk.json @@ -2,6 +2,7 @@ "config": { "abort": { "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", + "no_device": "\u017diadne z va\u0161ich zariaden\u00ed nem\u00e1 aktivovan\u00fa funkciu \u201eN\u00e1js\u0165 m\u00f4j iPhone\u201c.", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { @@ -14,6 +15,7 @@ "data": { "password": "Heslo" }, + "description": "Va\u0161e predt\u00fdm zadan\u00e9 heslo pre {username} u\u017e nefunguje. Ak chcete na\u010falej pou\u017e\u00edva\u0165 t\u00fato integr\u00e1ciu, aktualizujte svoje heslo.", "title": "Znova overi\u0165 integr\u00e1ciu" }, "trusted_device": { @@ -26,8 +28,10 @@ "user": { "data": { "password": "Heslo", - "username": "Email" + "username": "Email", + "with_family": "S rodinou" }, + "description": "Zadajte svoje poverenia", "title": "iCloud poverenia" }, "verification_code": { diff --git a/homeassistant/components/ifttt/translations/ko.json b/homeassistant/components/ifttt/translations/ko.json index 8f01109da76..d3d3d2be1f7 100644 --- a/homeassistant/components/ifttt/translations/ko.json +++ b/homeassistant/components/ifttt/translations/ko.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "cloud_not_connected": "Home Assistant \ud074\ub77c\uc6b0\ub4dc\uc5d0 \uc5f0\uacb0\ub418\uc5b4 \uc788\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.", "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.", "webhook_not_internet_accessible": "\uc6f9 \ud6c5 \uba54\uc2dc\uc9c0\ub97c \ubc1b\uc73c\ub824\uba74 \uc778\ud130\ub137\uc5d0\uc11c Home Assistant \uc778\uc2a4\ud134\uc2a4\uc5d0 \uc811\uadfc\ud560 \uc218 \uc788\uc5b4\uc57c \ud569\ub2c8\ub2e4." }, diff --git a/homeassistant/components/ifttt/translations/sk.json b/homeassistant/components/ifttt/translations/sk.json index 97f8b46cbdf..d0550d90757 100644 --- a/homeassistant/components/ifttt/translations/sk.json +++ b/homeassistant/components/ifttt/translations/sk.json @@ -5,9 +5,13 @@ "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia.", "webhook_not_internet_accessible": "Va\u0161a in\u0161tancia Home Assistant mus\u00ed by\u0165 pr\u00edstupn\u00e1 z internetu, aby ste mohli prij\u00edma\u0165 spr\u00e1vy webhooku." }, + "create_entry": { + "default": "Ak chcete odosla\u0165 udalosti Home Assistant, budete musie\u0165 pou\u017ei\u0165 akciu \u201eVytvori\u0165 webov\u00fa \u017eiados\u0165\u201c z [apletu IFTTT Webhook]({applet_url}).\n\nVypl\u0148te nasleduj\u00face inform\u00e1cie: \n\n - URL: `{webhook_url}`\n - Met\u00f3da: POST\n - Typ obsahu: application/json \n\nPozrite si [dokument\u00e1ciu]({docs_url}), ako nakonfigurova\u0165 automatiz\u00e1ciu na spracovanie prich\u00e1dzaj\u00facich \u00fadajov." + }, "step": { "user": { - "description": "Naozaj chcete nastavi\u0165 IFTTT?" + "description": "Naozaj chcete nastavi\u0165 IFTTT?", + "title": "Nastavte IFTTT Webhook Applet" } } } diff --git a/homeassistant/components/ign_sismologia/geo_location.py b/homeassistant/components/ign_sismologia/geo_location.py index 1194c58d2ca..e78dafae8ee 100644 --- a/homeassistant/components/ign_sismologia/geo_location.py +++ b/homeassistant/components/ign_sismologia/geo_location.py @@ -19,7 +19,7 @@ from homeassistant.const import ( CONF_RADIUS, CONF_SCAN_INTERVAL, EVENT_HOMEASSISTANT_START, - LENGTH_KILOMETERS, + UnitOfLength, ) from homeassistant.core import Event, HomeAssistant, callback import homeassistant.helpers.config_validation as cv @@ -146,7 +146,7 @@ class IgnSismologiaLocationEvent(GeolocationEvent): _attr_icon = "mdi:pulse" _attr_should_poll = False _attr_source = SOURCE - _attr_unit_of_measurement = LENGTH_KILOMETERS + _attr_unit_of_measurement = UnitOfLength.KILOMETERS def __init__( self, feed_manager: IgnSismologiaFeedEntityManager, external_id: str diff --git a/homeassistant/components/ihc/auto_setup.py b/homeassistant/components/ihc/auto_setup.py index ae271108848..d23c3e65a41 100644 --- a/homeassistant/components/ihc/auto_setup.py +++ b/homeassistant/components/ihc/auto_setup.py @@ -6,7 +6,7 @@ from defusedxml import ElementTree import voluptuous as vol from homeassistant.config import load_yaml_config_file -from homeassistant.const import CONF_TYPE, CONF_UNIT_OF_MEASUREMENT, TEMP_CELSIUS +from homeassistant.const import CONF_TYPE, CONF_UNIT_OF_MEASUREMENT, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers import discovery import homeassistant.helpers.config_validation as cv @@ -63,7 +63,7 @@ AUTO_SETUP_SCHEMA = vol.Schema( vol.Required(CONF_NODE): cv.string, vol.Required(CONF_XPATH): cv.string, vol.Optional( - CONF_UNIT_OF_MEASUREMENT, default=TEMP_CELSIUS + CONF_UNIT_OF_MEASUREMENT, default=UnitOfTemperature.CELSIUS ): cv.string, } ) diff --git a/homeassistant/components/ihc/manual_setup.py b/homeassistant/components/ihc/manual_setup.py index 297997281c6..c14d387ba61 100644 --- a/homeassistant/components/ihc/manual_setup.py +++ b/homeassistant/components/ihc/manual_setup.py @@ -12,7 +12,7 @@ from homeassistant.const import ( CONF_UNIT_OF_MEASUREMENT, CONF_URL, CONF_USERNAME, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers import discovery @@ -81,7 +81,11 @@ LIGHT_SCHEMA = DEVICE_SCHEMA.extend( ) SENSOR_SCHEMA = DEVICE_SCHEMA.extend( - {vol.Optional(CONF_UNIT_OF_MEASUREMENT, default=TEMP_CELSIUS): cv.string} + { + vol.Optional( + CONF_UNIT_OF_MEASUREMENT, default=UnitOfTemperature.CELSIUS + ): cv.string + } ) IHC_SCHEMA = vol.Schema( diff --git a/homeassistant/components/image/const.py b/homeassistant/components/image/const.py deleted file mode 100644 index 5241f7ec07b..00000000000 --- a/homeassistant/components/image/const.py +++ /dev/null @@ -1,3 +0,0 @@ -"""Constants for the Image integration.""" - -DOMAIN = "image" diff --git a/homeassistant/components/image_processing/__init__.py b/homeassistant/components/image_processing/__init__.py index 29adafe90b8..733a1344538 100644 --- a/homeassistant/components/image_processing/__init__.py +++ b/homeassistant/components/image_processing/__init__.py @@ -2,6 +2,7 @@ from __future__ import annotations import asyncio +from dataclasses import dataclass from datetime import timedelta import logging from typing import Any, Final, TypedDict, final @@ -21,7 +22,7 @@ from homeassistant.core import HomeAssistant, ServiceCall, callback from homeassistant.exceptions import HomeAssistantError import homeassistant.helpers.config_validation as cv from homeassistant.helpers.config_validation import make_entity_service_schema -from homeassistant.helpers.entity import Entity +from homeassistant.helpers.entity import Entity, EntityDescription from homeassistant.helpers.entity_component import EntityComponent from homeassistant.helpers.typing import ConfigType from homeassistant.util.async_ import run_callback_threadsafe @@ -119,20 +120,49 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: return True +@dataclass +class ImageProcessingEntityDescription(EntityDescription): + """A class that describes sensor entities.""" + + device_class: ImageProcessingDeviceClass | None = None + camera_entity: str | None = None + confidence: float | None = None + + class ImageProcessingEntity(Entity): """Base entity class for image processing.""" - _attr_device_class: ImageProcessingDeviceClass | str | None + entity_description: ImageProcessingEntityDescription + _attr_device_class: ImageProcessingDeviceClass | None + _attr_camera_entity: str | None + _attr_confidence: float | None timeout = DEFAULT_TIMEOUT @property def camera_entity(self) -> str | None: """Return camera entity id from process pictures.""" + if hasattr(self, "_attr_camera_entity"): + return self._attr_camera_entity + if hasattr(self, "entity_description"): + return self.entity_description.camera_entity return None @property def confidence(self) -> float | None: - """Return minimum confidence for do some things.""" + """Return minimum confidence to do some things.""" + if hasattr(self, "_attr_confidence"): + return self._attr_confidence + if hasattr(self, "entity_description"): + return self.entity_description.confidence + return None + + @property + def device_class(self) -> ImageProcessingDeviceClass | None: + """Return the class of this entity.""" + if hasattr(self, "_attr_device_class"): + return self._attr_device_class + if hasattr(self, "entity_description"): + return self.entity_description.device_class return None def process_image(self, image: bytes) -> None: diff --git a/homeassistant/components/image/__init__.py b/homeassistant/components/image_upload/__init__.py similarity index 98% rename from homeassistant/components/image/__init__.py rename to homeassistant/components/image_upload/__init__.py index 23ab393aabd..7ff69145a05 100644 --- a/homeassistant/components/image/__init__.py +++ b/homeassistant/components/image_upload/__init__.py @@ -1,4 +1,4 @@ -"""The Picture integration.""" +"""The Image Upload integration.""" from __future__ import annotations import asyncio @@ -25,7 +25,7 @@ import homeassistant.util.dt as dt_util from .const import DOMAIN _LOGGER = logging.getLogger(__name__) -STORAGE_KEY = DOMAIN +STORAGE_KEY = "image" STORAGE_VERSION = 1 VALID_SIZES = {256, 512} MAX_SIZE = 1024 * 1024 * 10 @@ -41,13 +41,13 @@ UPDATE_FIELDS = { async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up the Image integration.""" - image_dir = pathlib.Path(hass.config.path(DOMAIN)) + image_dir = pathlib.Path(hass.config.path("image")) hass.data[DOMAIN] = storage_collection = ImageStorageCollection(hass, image_dir) await storage_collection.async_load() collection.StorageCollectionWebsocket( storage_collection, - DOMAIN, - DOMAIN, + "image", + "image", CREATE_FIELDS, UPDATE_FIELDS, ).async_setup(hass, create_create=False) diff --git a/homeassistant/components/image_upload/const.py b/homeassistant/components/image_upload/const.py new file mode 100644 index 00000000000..f7607f745c7 --- /dev/null +++ b/homeassistant/components/image_upload/const.py @@ -0,0 +1,3 @@ +"""Constants for the Image Upload integration.""" + +DOMAIN = "image_upload" diff --git a/homeassistant/components/image/manifest.json b/homeassistant/components/image_upload/manifest.json similarity index 79% rename from homeassistant/components/image/manifest.json rename to homeassistant/components/image_upload/manifest.json index 888d6fc1fab..e8b3342d7bf 100644 --- a/homeassistant/components/image/manifest.json +++ b/homeassistant/components/image_upload/manifest.json @@ -1,8 +1,8 @@ { - "domain": "image", - "name": "Image", + "domain": "image_upload", + "name": "Image Upload", "config_flow": false, - "documentation": "https://www.home-assistant.io/integrations/image", + "documentation": "https://www.home-assistant.io/integrations/image_upload", "requirements": ["pillow==9.3.0"], "dependencies": ["http"], "codeowners": ["@home-assistant/core"], diff --git a/homeassistant/components/incomfort/climate.py b/homeassistant/components/incomfort/climate.py index 1d3c18fa608..cae73495438 100644 --- a/homeassistant/components/incomfort/climate.py +++ b/homeassistant/components/incomfort/climate.py @@ -9,7 +9,7 @@ from homeassistant.components.climate import ( ClimateEntityFeature, HVACMode, ) -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType @@ -41,7 +41,7 @@ class InComfortClimate(IncomfortChild, ClimateEntity): _attr_hvac_mode = HVACMode.HEAT _attr_hvac_modes = [HVACMode.HEAT] _attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS def __init__(self, client, heater, room) -> None: """Initialize the climate device.""" diff --git a/homeassistant/components/incomfort/sensor.py b/homeassistant/components/incomfort/sensor.py index 59cd7ed8ae5..b1b391aaaab 100644 --- a/homeassistant/components/incomfort/sensor.py +++ b/homeassistant/components/incomfort/sensor.py @@ -10,7 +10,7 @@ from homeassistant.components.sensor import ( SensorEntity, SensorEntityDescription, ) -from homeassistant.const import PRESSURE_BAR, TEMP_CELSIUS +from homeassistant.const import UnitOfPressure, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType @@ -35,20 +35,20 @@ SENSOR_TYPES: tuple[IncomfortSensorEntityDescription, ...] = ( key="pressure", name=INCOMFORT_PRESSURE, device_class=SensorDeviceClass.PRESSURE, - native_unit_of_measurement=PRESSURE_BAR, + native_unit_of_measurement=UnitOfPressure.BAR, ), IncomfortSensorEntityDescription( key="heater_temp", name=INCOMFORT_HEATER_TEMP, device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, extra_key="is_pumping", ), IncomfortSensorEntityDescription( key="tap_temp", name=INCOMFORT_TAP_TEMP, device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, extra_key="is_tapping", ), ) diff --git a/homeassistant/components/incomfort/water_heater.py b/homeassistant/components/incomfort/water_heater.py index fb16cc48f8b..f906270b2f5 100644 --- a/homeassistant/components/incomfort/water_heater.py +++ b/homeassistant/components/incomfort/water_heater.py @@ -11,7 +11,7 @@ from homeassistant.components.water_heater import ( DOMAIN as WATER_HEATER_DOMAIN, WaterHeaterEntity, ) -from homeassistant.const import TEMP_CELSIUS +from homeassistant.const import UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -86,7 +86,7 @@ class IncomfortWaterHeater(IncomfortEntity, WaterHeaterEntity): @property def temperature_unit(self) -> str: """Return the unit of measurement.""" - return TEMP_CELSIUS + return UnitOfTemperature.CELSIUS @property def current_operation(self) -> str: diff --git a/homeassistant/components/influxdb/__init__.py b/homeassistant/components/influxdb/__init__.py index 4fd6eb58fdd..d342a9d3aca 100644 --- a/homeassistant/components/influxdb/__init__.py +++ b/homeassistant/components/influxdb/__init__.py @@ -135,18 +135,21 @@ def validate_version_specific_config(conf: dict) -> dict: if conf[CONF_API_VERSION] == API_VERSION_2: if CONF_TOKEN not in conf: raise vol.Invalid( - f"{CONF_TOKEN} and {CONF_BUCKET} are required when {CONF_API_VERSION} is {API_VERSION_2}" + f"{CONF_TOKEN} and {CONF_BUCKET} are required when" + f" {CONF_API_VERSION} is {API_VERSION_2}" ) if CONF_USERNAME in conf: raise vol.Invalid( - f"{CONF_USERNAME} and {CONF_PASSWORD} are only allowed when {CONF_API_VERSION} is {DEFAULT_API_VERSION}" + f"{CONF_USERNAME} and {CONF_PASSWORD} are only allowed when" + f" {CONF_API_VERSION} is {DEFAULT_API_VERSION}" ) else: if CONF_TOKEN in conf: raise vol.Invalid( - f"{CONF_TOKEN} and {CONF_BUCKET} are only allowed when {CONF_API_VERSION} is {API_VERSION_2}" + f"{CONF_TOKEN} and {CONF_BUCKET} are only allowed when" + f" {CONF_API_VERSION} is {API_VERSION_2}" ) return conf diff --git a/homeassistant/components/influxdb/sensor.py b/homeassistant/components/influxdb/sensor.py index dfb5ee57b6a..24625a66099 100644 --- a/homeassistant/components/influxdb/sensor.py +++ b/homeassistant/components/influxdb/sensor.py @@ -83,7 +83,8 @@ def validate_query_format_for_version(conf: dict) -> dict: if conf[CONF_API_VERSION] == API_VERSION_2: if CONF_QUERIES_FLUX not in conf: raise vol.Invalid( - f"{CONF_QUERIES_FLUX} is required when {CONF_API_VERSION} is {API_VERSION_2}" + f"{CONF_QUERIES_FLUX} is required when {CONF_API_VERSION} is" + f" {API_VERSION_2}" ) for query in conf[CONF_QUERIES_FLUX]: @@ -95,7 +96,8 @@ def validate_query_format_for_version(conf: dict) -> dict: else: if CONF_QUERIES not in conf: raise vol.Invalid( - f"{CONF_QUERIES} is required when {CONF_API_VERSION} is {DEFAULT_API_VERSION}" + f"{CONF_QUERIES} is required when {CONF_API_VERSION} is" + f" {DEFAULT_API_VERSION}" ) for query in conf[CONF_QUERIES]: @@ -270,7 +272,10 @@ class InfluxFluxSensorData: self.value = None self.full_query = None - self.query_prefix = f'from(bucket:"{bucket}") |> range(start: {range_start}, stop: {range_stop}) |>' + self.query_prefix = ( + f'from(bucket:"{bucket}") |> range(start: {range_start}, stop:' + f" {range_stop}) |>" + ) if imports is not None: for i in imports: self.query_prefix = f'import "{i}" {self.query_prefix}' @@ -334,7 +339,10 @@ class InfluxQLSensorData: _LOGGER.error(RENDERING_WHERE_ERROR_MESSAGE, ex) return - self.query = f"select {self.group}({self.field}) as {INFLUX_CONF_VALUE} from {self.measurement} where {where_clause}" + self.query = ( + f"select {self.group}({self.field}) as {INFLUX_CONF_VALUE} from" + f" {self.measurement} where {where_clause}" + ) _LOGGER.debug(RUNNING_QUERY_MESSAGE, self.query) diff --git a/homeassistant/components/inkbird/sensor.py b/homeassistant/components/inkbird/sensor.py index d0e06e81647..74716d465a8 100644 --- a/homeassistant/components/inkbird/sensor.py +++ b/homeassistant/components/inkbird/sensor.py @@ -22,7 +22,7 @@ from homeassistant.components.sensor import ( from homeassistant.const import ( PERCENTAGE, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -34,7 +34,7 @@ SENSOR_DESCRIPTIONS = { (DeviceClass.TEMPERATURE, Units.TEMP_CELSIUS): SensorEntityDescription( key=f"{DeviceClass.TEMPERATURE}_{Units.TEMP_CELSIUS}", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, ), (DeviceClass.HUMIDITY, Units.PERCENTAGE): SensorEntityDescription( diff --git a/homeassistant/components/inkbird/translations/en.json b/homeassistant/components/inkbird/translations/en.json index d24df64f135..f99ec0bbe63 100644 --- a/homeassistant/components/inkbird/translations/en.json +++ b/homeassistant/components/inkbird/translations/en.json @@ -8,13 +8,13 @@ "flow_title": "{name}", "step": { "bluetooth_confirm": { - "description": "Do you want to setup {name}?" + "description": "Do you want to set up {name}?" }, "user": { "data": { "address": "Device" }, - "description": "Choose a device to setup" + "description": "Choose a device to set up" } } } diff --git a/homeassistant/components/inkbird/translations/it.json b/homeassistant/components/inkbird/translations/it.json index 501b5095826..3ede7709c00 100644 --- a/homeassistant/components/inkbird/translations/it.json +++ b/homeassistant/components/inkbird/translations/it.json @@ -14,7 +14,7 @@ "data": { "address": "Dispositivo" }, - "description": "Seleziona un dispositivo da configurare" + "description": "Scegli un dispositivo da configurare" } } } diff --git a/homeassistant/components/inkbird/translations/ko.json b/homeassistant/components/inkbird/translations/ko.json new file mode 100644 index 00000000000..1a59c05b30c --- /dev/null +++ b/homeassistant/components/inkbird/translations/ko.json @@ -0,0 +1,9 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", + "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/inkbird/translations/no.json b/homeassistant/components/inkbird/translations/no.json index 28ec4582177..0a44ef08d80 100644 --- a/homeassistant/components/inkbird/translations/no.json +++ b/homeassistant/components/inkbird/translations/no.json @@ -8,7 +8,7 @@ "flow_title": "{name}", "step": { "bluetooth_confirm": { - "description": "Vil du konfigurere {name}?" + "description": "Vil du sette opp {name} ?" }, "user": { "data": { diff --git a/homeassistant/components/inkbird/translations/pt.json b/homeassistant/components/inkbird/translations/pt.json new file mode 100644 index 00000000000..c6a15010072 --- /dev/null +++ b/homeassistant/components/inkbird/translations/pt.json @@ -0,0 +1,9 @@ +{ + "config": { + "abort": { + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", + "already_in_progress": "O processo de configura\u00e7\u00e3o j\u00e1 est\u00e1 a decorrer", + "no_devices_found": "Nenhum dispositivo encontrado na rede" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/input_number/__init__.py b/homeassistant/components/input_number/__init__.py index 99e54dc9baa..d81ec581bc8 100644 --- a/homeassistant/components/input_number/__init__.py +++ b/homeassistant/components/input_number/__init__.py @@ -307,7 +307,8 @@ class InputNumber(collection.CollectionEntity, RestoreEntity): if num_value < self._minimum or num_value > self._maximum: raise vol.Invalid( - f"Invalid value for {self.entity_id}: {value} (range {self._minimum} - {self._maximum})" + f"Invalid value for {self.entity_id}: {value} (range {self._minimum} -" + f" {self._maximum})" ) self._current_value = num_value diff --git a/homeassistant/components/input_select/__init__.py b/homeassistant/components/input_select/__init__.py index f30b2ca1e36..bd9c43e8538 100644 --- a/homeassistant/components/input_select/__init__.py +++ b/homeassistant/components/input_select/__init__.py @@ -73,7 +73,10 @@ def _remove_duplicates(options: list[str], name: str | None) -> list[str]: # Reject YAML configured input_select with duplicates from 2022.6 if len(unique_options) != len(options): _LOGGER.warning( - "Input select '%s' with options %s had duplicated options, the duplicates have been removed", + ( + "Input select '%s' with options %s had duplicated options, the" + " duplicates have been removed" + ), name or "", options, ) diff --git a/homeassistant/components/insteon/climate.py b/homeassistant/components/insteon/climate.py index 0211b316b96..f88dadf1223 100644 --- a/homeassistant/components/insteon/climate.py +++ b/homeassistant/components/insteon/climate.py @@ -17,7 +17,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS, TEMP_FAHRENHEIT +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -89,8 +89,8 @@ class InsteonClimateEntity(InsteonEntity, ClimateEntity): def temperature_unit(self) -> str: """Return the unit of measurement.""" if self._insteon_device.configuration[CELSIUS].value: - return TEMP_CELSIUS - return TEMP_FAHRENHEIT + return UnitOfTemperature.CELSIUS + return UnitOfTemperature.FAHRENHEIT @property def current_humidity(self) -> int | None: diff --git a/homeassistant/components/insteon/insteon_entity.py b/homeassistant/components/insteon/insteon_entity.py index d1ba7c5f829..dd7acc65458 100644 --- a/homeassistant/components/insteon/insteon_entity.py +++ b/homeassistant/components/insteon/insteon_entity.py @@ -83,9 +83,15 @@ class InsteonEntity(Entity): return DeviceInfo( identifiers={(DOMAIN, str(self._insteon_device.address))}, manufacturer="SmartLabs, Inc", - model=f"{self._insteon_device.model} ({self._insteon_device.cat!r}, 0x{self._insteon_device.subcat:02x})", + model=( + f"{self._insteon_device.model} ({self._insteon_device.cat!r}," + f" 0x{self._insteon_device.subcat:02x})" + ), name=f"{self._insteon_device.description} {self._insteon_device.address}", - sw_version=f"{self._insteon_device.firmware:02x} Engine Version: {self._insteon_device.engine_version}", + sw_version=( + f"{self._insteon_device.firmware:02x} Engine Version:" + f" {self._insteon_device.engine_version}" + ), via_device=(DOMAIN, str(devices.modem.address)), ) diff --git a/homeassistant/components/insteon/services.yaml b/homeassistant/components/insteon/services.yaml index c2cd5ee9d25..164c917c793 100644 --- a/homeassistant/components/insteon/services.yaml +++ b/homeassistant/components/insteon/services.yaml @@ -1,6 +1,6 @@ add_all_link: name: Add all link - description: Tells the Insteom Modem (IM) start All-Linking mode. Once the the IM is in All-Linking mode, press the link button on the device to complete All-Linking. + description: Tells the Insteom Modem (IM) start All-Linking mode. Once the IM is in All-Linking mode, press the link button on the device to complete All-Linking. fields: group: name: Group diff --git a/homeassistant/components/insteon/strings.json b/homeassistant/components/insteon/strings.json index e451e080539..ddeed18edcd 100644 --- a/homeassistant/components/insteon/strings.json +++ b/homeassistant/components/insteon/strings.json @@ -34,7 +34,7 @@ } }, "confirm_usb": { - "description": "Do you want to setup {name}?" + "description": "Do you want to set up {name}?" } }, "error": { diff --git a/homeassistant/components/insteon/translations/de.json b/homeassistant/components/insteon/translations/de.json index 798dba41984..70a8ded1a36 100644 --- a/homeassistant/components/insteon/translations/de.json +++ b/homeassistant/components/insteon/translations/de.json @@ -56,9 +56,9 @@ "step": { "add_override": { "data": { - "address": "Ger\u00e4teadresse (z. B. 1a2b3c)", - "cat": "Ger\u00e4tekategorie (z. B. 0x10)", - "subcat": "Ger\u00e4teunterkategorie (z. B. 0x0a)" + "address": "Ger\u00e4teadresse (z.B. 1a2b3c)", + "cat": "Ger\u00e4tekategorie (z.B. 0x10)", + "subcat": "Ger\u00e4teunterkategorie (z.B. 0x0a)" }, "description": "F\u00fcge eine Ger\u00e4te\u00fcberschreibung hinzu." }, @@ -67,7 +67,7 @@ "housecode": "Hauscode (a - p)", "platform": "Plattform", "steps": "Dimmerstufen (nur f\u00fcr Lichtger\u00e4te, Voreinstellung 22)", - "unitcode": "Unitcode (1 - 16)" + "unitcode": "Einheitencode (1 - 16)" }, "description": "\u00c4ndere das Insteon Hub-Passwort." }, diff --git a/homeassistant/components/insteon/translations/en.json b/homeassistant/components/insteon/translations/en.json index 441f5cf576a..b1c4a8d792a 100644 --- a/homeassistant/components/insteon/translations/en.json +++ b/homeassistant/components/insteon/translations/en.json @@ -12,7 +12,7 @@ "flow_title": "{name}", "step": { "confirm_usb": { - "description": "Do you want to setup {name}?" + "description": "Do you want to set up {name}?" }, "hubv1": { "data": { diff --git a/homeassistant/components/insteon/translations/no.json b/homeassistant/components/insteon/translations/no.json index 70ecd17fb21..48ed9d61998 100644 --- a/homeassistant/components/insteon/translations/no.json +++ b/homeassistant/components/insteon/translations/no.json @@ -12,7 +12,7 @@ "flow_title": "{name}", "step": { "confirm_usb": { - "description": "Vil du konfigurere {name}?" + "description": "Vil du sette opp {name} ?" }, "hubv1": { "data": { diff --git a/homeassistant/components/insteon/translations/pt.json b/homeassistant/components/insteon/translations/pt.json index 4654d2c4de1..4ff3b547c82 100644 --- a/homeassistant/components/insteon/translations/pt.json +++ b/homeassistant/components/insteon/translations/pt.json @@ -1,11 +1,11 @@ { "config": { "abort": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "single_instance_allowed": "J\u00e1 configurado. Apenas uma \u00fanica configura\u00e7\u00e3o \u00e9 poss\u00edvel." }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "hubv1": { @@ -41,7 +41,7 @@ }, "options": { "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "select_single": "Selecione uma op\u00e7\u00e3o." }, "step": { diff --git a/homeassistant/components/insteon/translations/sk.json b/homeassistant/components/insteon/translations/sk.json index 651f7012660..d3c2a12b202 100644 --- a/homeassistant/components/insteon/translations/sk.json +++ b/homeassistant/components/insteon/translations/sk.json @@ -2,6 +2,7 @@ "config": { "abort": { "cannot_connect": "Nepodarilo sa pripoji\u0165", + "not_insteon_device": "Zisten\u00e9 zariadenie nie je zariadenie Insteon", "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." }, "error": { @@ -17,7 +18,9 @@ "data": { "host": "IP adresa", "port": "Port" - } + }, + "description": "Nakonfigurujte Insteon Hub Verzia 1 (pred rokom 2014).", + "title": "Insteon Hub Verzia 1" }, "hubv2": { "data": { @@ -25,12 +28,22 @@ "password": "Heslo", "port": "Port", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" - } + }, + "description": "Nakonfigurujte Insteon Hub Verzia 2.", + "title": "Insteon Hub Verzia 2" }, "plm": { "data": { "device": "Cesta k zariadeniu USB" - } + }, + "description": "Nakonfigurujte Insteon PowerLink Modem (PLM).", + "title": "Insteon PLM" + }, + "user": { + "data": { + "modem_type": "Typ modemu." + }, + "description": "Vyberte typ modemu Insteon." } } }, @@ -46,13 +59,17 @@ "address": "Adresa zariadenia (t. j. 1a2b3c)", "cat": "Kateg\u00f3ria zariadenia (t. j. 0x10)", "subcat": "Podkateg\u00f3ria zariadenia (napr. 0x0a)" - } + }, + "description": "Pridanie prep\u00edsania zariadenia." }, "add_x10": { "data": { "housecode": "K\u00f3d domu (a - p)", - "platform": "Platforma" - } + "platform": "Platforma", + "steps": "Stmievacie kroky (iba pre sveteln\u00e9 zariadenia, predvolen\u00e1 hodnota 22)", + "unitcode": "Unitcode (1 - 16)" + }, + "description": "Zme\u0148te heslo Insteon Hub." }, "change_hub_config": { "data": { @@ -60,17 +77,29 @@ "password": "Heslo", "port": "Port", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" + }, + "description": "Zme\u0148te inform\u00e1cie o pripojen\u00ed Insteon Hub. Po vykonan\u00ed tejto zmeny mus\u00edte Home Assistant re\u0161tartova\u0165. Toto nemen\u00ed konfigur\u00e1ciu samotn\u00e9ho Hubu. Ak chcete zmeni\u0165 konfigur\u00e1ciu v Hub, pou\u017eite aplik\u00e1ciu Hub." + }, + "init": { + "data": { + "add_override": "Pridanie prep\u00edsania zariadenia.", + "add_x10": "Pridajte zariadenie X10.", + "change_hub_config": "Zme\u0148te konfigur\u00e1ciu hubu.", + "remove_override": "Odstr\u00e1nenie prep\u00edsania zariadenia.", + "remove_x10": "Odstr\u00e1\u0148te zariadenie X10." } }, "remove_override": { "data": { "address": "Vyberte adresu zariadenia, ktor\u00fa chcete odstr\u00e1ni\u0165" - } + }, + "description": "Odstr\u00e1nenie prep\u00edsania zariadenia" }, "remove_x10": { "data": { "address": "Vyberte adresu zariadenia, ktor\u00fa chcete odstr\u00e1ni\u0165" - } + }, + "description": "Odstr\u00e1\u0148te zariadenie X10" } } } diff --git a/homeassistant/components/integration/config_flow.py b/homeassistant/components/integration/config_flow.py index 446132ece7c..147c4262319 100644 --- a/homeassistant/components/integration/config_flow.py +++ b/homeassistant/components/integration/config_flow.py @@ -7,14 +7,7 @@ from typing import Any, cast import voluptuous as vol from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN -from homeassistant.const import ( - CONF_METHOD, - CONF_NAME, - TIME_DAYS, - TIME_HOURS, - TIME_MINUTES, - TIME_SECONDS, -) +from homeassistant.const import CONF_METHOD, CONF_NAME, UnitOfTime from homeassistant.helpers import selector from homeassistant.helpers.schema_config_entry_flow import ( SchemaConfigFlowHandler, @@ -40,10 +33,10 @@ UNIT_PREFIXES = [ selector.SelectOptionDict(value="T", label="T (tera)"), ] TIME_UNITS = [ - selector.SelectOptionDict(value=TIME_SECONDS, label="s (seconds)"), - selector.SelectOptionDict(value=TIME_MINUTES, label="min (minutes)"), - selector.SelectOptionDict(value=TIME_HOURS, label="h (hours)"), - selector.SelectOptionDict(value=TIME_DAYS, label="d (days)"), + selector.SelectOptionDict(value=UnitOfTime.SECONDS, label="s (seconds)"), + selector.SelectOptionDict(value=UnitOfTime.MINUTES, label="min (minutes)"), + selector.SelectOptionDict(value=UnitOfTime.HOURS, label="h (hours)"), + selector.SelectOptionDict(value=UnitOfTime.DAYS, label="d (days)"), ] INTEGRATION_METHODS = [ selector.SelectOptionDict(value=METHOD_TRAPEZOIDAL, label="Trapezoidal rule"), @@ -81,7 +74,7 @@ CONFIG_SCHEMA = vol.Schema( vol.Required(CONF_UNIT_PREFIX, default="none"): selector.SelectSelector( selector.SelectSelectorConfig(options=UNIT_PREFIXES), ), - vol.Required(CONF_UNIT_TIME, default=TIME_HOURS): selector.SelectSelector( + vol.Required(CONF_UNIT_TIME, default=UnitOfTime.HOURS): selector.SelectSelector( selector.SelectSelectorConfig( options=TIME_UNITS, mode=selector.SelectSelectorMode.DROPDOWN ), diff --git a/homeassistant/components/integration/sensor.py b/homeassistant/components/integration/sensor.py index d08d04e094d..54e50b7b1de 100644 --- a/homeassistant/components/integration/sensor.py +++ b/homeassistant/components/integration/sensor.py @@ -22,10 +22,7 @@ from homeassistant.const import ( CONF_UNIQUE_ID, STATE_UNAVAILABLE, STATE_UNKNOWN, - TIME_DAYS, - TIME_HOURS, - TIME_MINUTES, - TIME_SECONDS, + UnitOfTime, ) from homeassistant.core import Event, HomeAssistant, State, callback from homeassistant.helpers import config_validation as cv, entity_registry as er @@ -55,10 +52,10 @@ UNIT_PREFIXES = {None: 1, "k": 10**3, "M": 10**6, "G": 10**9, "T": 10**12} # SI Time prefixes UNIT_TIME = { - TIME_SECONDS: 1, - TIME_MINUTES: 60, - TIME_HOURS: 60 * 60, - TIME_DAYS: 24 * 60 * 60, + UnitOfTime.SECONDS: 1, + UnitOfTime.MINUTES: 60, + UnitOfTime.HOURS: 60 * 60, + UnitOfTime.DAYS: 24 * 60 * 60, } DEFAULT_ROUND = 3 @@ -72,7 +69,7 @@ PLATFORM_SCHEMA = vol.All( vol.Required(CONF_SOURCE_SENSOR): cv.entity_id, vol.Optional(CONF_ROUND_DIGITS, default=DEFAULT_ROUND): vol.Coerce(int), vol.Optional(CONF_UNIT_PREFIX, default=None): vol.In(UNIT_PREFIXES), - vol.Optional(CONF_UNIT_TIME, default=TIME_HOURS): vol.In(UNIT_TIME), + vol.Optional(CONF_UNIT_TIME, default=UnitOfTime.HOURS): vol.In(UNIT_TIME), vol.Remove(CONF_UNIT_OF_MEASUREMENT): cv.string, vol.Optional(CONF_METHOD, default=METHOD_TRAPEZOIDAL): vol.In( INTEGRATION_METHODS @@ -146,7 +143,7 @@ class IntegrationSensor(RestoreEntity, SensorEntity): source_entity: str, unique_id: str | None, unit_prefix: str | None, - unit_time: str, + unit_time: UnitOfTime, ) -> None: """Initialize the integration sensor.""" self._attr_unique_id = unique_id diff --git a/homeassistant/components/integration/translations/sk.json b/homeassistant/components/integration/translations/sk.json index 25cf5102677..91fcef6848c 100644 --- a/homeassistant/components/integration/translations/sk.json +++ b/homeassistant/components/integration/translations/sk.json @@ -3,10 +3,20 @@ "step": { "user": { "data": { + "method": "Met\u00f3da integr\u00e1cie", "name": "N\u00e1zov", "round": "Presnos\u0165", - "source": "Vstupn\u00fd sn\u00edma\u010d" - } + "source": "Vstupn\u00fd sn\u00edma\u010d", + "unit_prefix": "Metrick\u00fd prefix", + "unit_time": "\u010casov\u00e1 jednotka" + }, + "data_description": { + "round": "Ovl\u00e1da po\u010det desatinn\u00fdch \u010d\u00edslic vo v\u00fdstupe.", + "unit_prefix": "V\u00fdstup sa \u0161k\u00e1luje pod\u013ea vybran\u00e9ho metrick\u00e9ho prefixu.", + "unit_time": "V\u00fdstup bude \u0161k\u00e1lovan\u00fd pod\u013ea zvolenej \u010dasovej jednotky." + }, + "description": "Vytvorte sn\u00edma\u010d, ktor\u00fd vypo\u010d\u00edtava Riemannov s\u00fa\u010det na odhad integr\u00e1lu sn\u00edma\u010da.", + "title": "Pridajte integr\u00e1lny sn\u00edma\u010d Riemann s\u00fa\u010dtu" } } }, @@ -15,8 +25,12 @@ "init": { "data": { "round": "Presnos\u0165" + }, + "data_description": { + "round": "Ovl\u00e1da po\u010det desatinn\u00fdch \u010d\u00edslic vo v\u00fdstupe." } } } - } + }, + "title": "Integr\u00e1cia - Riemannov s\u00fa\u010dtov\u00fd integr\u00e1lny sn\u00edma\u010d" } \ No newline at end of file diff --git a/homeassistant/components/intellifire/climate.py b/homeassistant/components/intellifire/climate.py index cf36e6b48a0..649234d7568 100644 --- a/homeassistant/components/intellifire/climate.py +++ b/homeassistant/components/intellifire/climate.py @@ -10,7 +10,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -51,7 +51,7 @@ class IntellifireClimate(IntellifireEntity, ClimateEntity): _attr_max_temp = 37 _attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE _attr_target_temperature_step = 1.0 - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS last_temp = DEFAULT_THERMOSTAT_TEMP def __init__( diff --git a/homeassistant/components/intellifire/sensor.py b/homeassistant/components/intellifire/sensor.py index cbd31249133..0bb398ea70f 100644 --- a/homeassistant/components/intellifire/sensor.py +++ b/homeassistant/components/intellifire/sensor.py @@ -14,7 +14,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import TEMP_CELSIUS +from homeassistant.const import UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -68,7 +68,7 @@ INTELLIFIRE_SENSORS: tuple[IntellifireSensorEntityDescription, ...] = ( name="Temperature", state_class=SensorStateClass.MEASUREMENT, device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, value_fn=lambda data: data.temperature_c, ), IntellifireSensorEntityDescription( @@ -76,7 +76,7 @@ INTELLIFIRE_SENSORS: tuple[IntellifireSensorEntityDescription, ...] = ( name="Target Temperature", state_class=SensorStateClass.MEASUREMENT, device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, value_fn=lambda data: data.thermostat_setpoint_c, ), IntellifireSensorEntityDescription( diff --git a/homeassistant/components/intellifire/strings.json b/homeassistant/components/intellifire/strings.json index dd5c66adebd..a8c8d76a601 100644 --- a/homeassistant/components/intellifire/strings.json +++ b/homeassistant/components/intellifire/strings.json @@ -15,7 +15,7 @@ } }, "dhcp_confirm": { - "description": "Do you want to setup {host}\nSerial: {serial}?" + "description": "Do you want to set up {host}\nSerial: {serial}?" }, "pick_device": { "title": "Device Selection", diff --git a/homeassistant/components/intellifire/translations/de.json b/homeassistant/components/intellifire/translations/de.json index 2840db3a1e3..9352aeea87f 100644 --- a/homeassistant/components/intellifire/translations/de.json +++ b/homeassistant/components/intellifire/translations/de.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Ger\u00e4t ist bereits konfiguriert", - "not_intellifire_device": "Kein IntelliFire-Ger\u00e4t.", + "not_intellifire_device": "Kein IntelliFire Ger\u00e4t.", "reauth_successful": "Die erneute Authentifizierung war erfolgreich" }, "error": { diff --git a/homeassistant/components/intellifire/translations/en.json b/homeassistant/components/intellifire/translations/en.json index 83acafacd48..5029a6b349f 100644 --- a/homeassistant/components/intellifire/translations/en.json +++ b/homeassistant/components/intellifire/translations/en.json @@ -19,7 +19,7 @@ } }, "dhcp_confirm": { - "description": "Do you want to setup {host}\nSerial: {serial}?" + "description": "Do you want to set up {host}\nSerial: {serial}?" }, "manual_device_entry": { "data": { diff --git a/homeassistant/components/intellifire/translations/it.json b/homeassistant/components/intellifire/translations/it.json index a1282698c88..93429c5c614 100644 --- a/homeassistant/components/intellifire/translations/it.json +++ b/homeassistant/components/intellifire/translations/it.json @@ -19,7 +19,7 @@ } }, "dhcp_confirm": { - "description": "Vuoi configurare {host}\n Seriale: {serial}?" + "description": "Vuoi configurare {host}\nSeriale: {serial}?" }, "manual_device_entry": { "data": { diff --git a/homeassistant/components/intellifire/translations/ko.json b/homeassistant/components/intellifire/translations/ko.json index fde189b9541..220f8403502 100644 --- a/homeassistant/components/intellifire/translations/ko.json +++ b/homeassistant/components/intellifire/translations/ko.json @@ -1,10 +1,12 @@ { "config": { "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" }, "error": { "api_error": "\ub85c\uadf8\uc778 \uc2e4\ud328", + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", "iftapi_connect": "iftapi.net\uc5d0 \uc5f0\uacb0\ud558\ub294 \ub3d9\uc548 \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4." }, "step": { @@ -15,6 +17,9 @@ } }, "pick_device": { + "data": { + "host": "\ud638\uc2a4\ud2b8" + }, "description": "IntelliFire \uc7a5\uce58\uac00 \uac80\uc0c9\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \uad6c\uc131\ud560 \ud56d\ubaa9\uc744 \uc120\ud0dd\ud558\uc2ed\uc2dc\uc624.", "title": "\uae30\uae30 \uc120\ud0dd" } diff --git a/homeassistant/components/intellifire/translations/no.json b/homeassistant/components/intellifire/translations/no.json index 77fdb74a3ab..2ab289906c3 100644 --- a/homeassistant/components/intellifire/translations/no.json +++ b/homeassistant/components/intellifire/translations/no.json @@ -19,7 +19,7 @@ } }, "dhcp_confirm": { - "description": "Vil du konfigurere {host}\n Serienummer: {serial} ?" + "description": "Vil du sette opp {host}\n Serie: {serial} ?" }, "manual_device_entry": { "data": { diff --git a/homeassistant/components/intellifire/translations/sk.json b/homeassistant/components/intellifire/translations/sk.json index 944a3c43d39..7c10da98b1e 100644 --- a/homeassistant/components/intellifire/translations/sk.json +++ b/homeassistant/components/intellifire/translations/sk.json @@ -2,6 +2,7 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "not_intellifire_device": "Nie je to zariadenie IntelliFire.", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { @@ -30,6 +31,7 @@ "data": { "host": "Hostite\u013e" }, + "description": "Boli objaven\u00e9 nasleduj\u00face zariadenia IntelliFire. Vyberte, ktor\u00e9 chcete konfigurova\u0165.", "title": "V\u00fdber zariadenia" } } diff --git a/homeassistant/components/intent/__init__.py b/homeassistant/components/intent/__init__.py index c43643b0244..c6ca9212c74 100644 --- a/homeassistant/components/intent/__init__.py +++ b/homeassistant/components/intent/__init__.py @@ -63,6 +63,7 @@ class IntentHandleView(http.HomeAssistantView): async def post(self, request, data): """Handle intent with name/data.""" hass = request.app["hass"] + language = hass.config.language try: intent_name = data["name"] @@ -73,11 +74,11 @@ class IntentHandleView(http.HomeAssistantView): hass, DOMAIN, intent_name, slots, "", self.context(request) ) except intent.IntentHandleError as err: - intent_result = intent.IntentResponse() + intent_result = intent.IntentResponse(language=language) intent_result.async_set_speech(str(err)) if intent_result is None: - intent_result = intent.IntentResponse() + intent_result = intent.IntentResponse(language=language) intent_result.async_set_speech("Sorry, I couldn't handle that") return self.json(intent_result) diff --git a/homeassistant/components/intent_script/__init__.py b/homeassistant/components/intent_script/__init__.py index 0090b1def9b..128c9332aeb 100644 --- a/homeassistant/components/intent_script/__init__.py +++ b/homeassistant/components/intent_script/__init__.py @@ -1,4 +1,6 @@ """Handle intents with scripts.""" +from __future__ import annotations + import copy import logging @@ -77,7 +79,7 @@ class ScriptIntentHandler(intent.IntentHandler): self.intent_type = intent_type self.config = config - async def async_handle(self, intent_obj): + async def async_handle(self, intent_obj: intent.Intent): """Handle the intent.""" speech = self.config.get(CONF_SPEECH) reprompt = self.config.get(CONF_REPROMPT) diff --git a/homeassistant/components/intesishome/climate.py b/homeassistant/components/intesishome/climate.py index bfdf406ee1a..16cf62627f1 100644 --- a/homeassistant/components/intesishome/climate.py +++ b/homeassistant/components/intesishome/climate.py @@ -27,7 +27,7 @@ from homeassistant.const import ( CONF_DEVICE, CONF_PASSWORD, CONF_USERNAME, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.exceptions import PlatformNotReady @@ -145,7 +145,7 @@ class IntesisAC(ClimateEntity): """Represents an Intesishome air conditioning device.""" _attr_should_poll = False - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS def __init__(self, ih_device_id, ih_device, controller): """Initialize the thermostat.""" diff --git a/homeassistant/components/ios/translations/en.json b/homeassistant/components/ios/translations/en.json index 051b032e82b..651e61a8c76 100644 --- a/homeassistant/components/ios/translations/en.json +++ b/homeassistant/components/ios/translations/en.json @@ -5,7 +5,7 @@ }, "step": { "confirm": { - "description": "Do you want to start set up?" + "description": "Do you want to start setup?" } } } diff --git a/homeassistant/components/ios/translations/it.json b/homeassistant/components/ios/translations/it.json index 35d906f2276..23d142f6e92 100644 --- a/homeassistant/components/ios/translations/it.json +++ b/homeassistant/components/ios/translations/it.json @@ -5,7 +5,7 @@ }, "step": { "confirm": { - "description": "Vuoi iniziare la configurazione?" + "description": "Vuoi avviare la configurazione?" } } } diff --git a/homeassistant/components/iotawatt/sensor.py b/homeassistant/components/iotawatt/sensor.py index dd04a32cce4..4839b8af8a7 100644 --- a/homeassistant/components/iotawatt/sensor.py +++ b/homeassistant/components/iotawatt/sensor.py @@ -15,13 +15,13 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - ELECTRIC_CURRENT_AMPERE, - ELECTRIC_POTENTIAL_VOLT, - ENERGY_WATT_HOUR, - FREQUENCY_HERTZ, PERCENTAGE, - POWER_VOLT_AMPERE, - POWER_WATT, + UnitOfApparentPower, + UnitOfElectricCurrent, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfFrequency, + UnitOfPower, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import entity, entity_registry @@ -53,15 +53,16 @@ class IotaWattSensorEntityDescription(SensorEntityDescription): ENTITY_DESCRIPTION_KEY_MAP: dict[str, IotaWattSensorEntityDescription] = { "Amps": IotaWattSensorEntityDescription( "Amps", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, state_class=SensorStateClass.MEASUREMENT, device_class=SensorDeviceClass.CURRENT, entity_registry_enabled_default=False, ), "Hz": IotaWattSensorEntityDescription( "Hz", - native_unit_of_measurement=FREQUENCY_HERTZ, + native_unit_of_measurement=UnitOfFrequency.HERTZ, state_class=SensorStateClass.MEASUREMENT, + device_class=SensorDeviceClass.FREQUENCY, icon="mdi:flash", entity_registry_enabled_default=False, ), @@ -75,21 +76,21 @@ ENTITY_DESCRIPTION_KEY_MAP: dict[str, IotaWattSensorEntityDescription] = { ), "Watts": IotaWattSensorEntityDescription( "Watts", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, state_class=SensorStateClass.MEASUREMENT, device_class=SensorDeviceClass.POWER, ), "WattHours": IotaWattSensorEntityDescription( "WattHours", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, state_class=SensorStateClass.TOTAL, device_class=SensorDeviceClass.ENERGY, ), "VA": IotaWattSensorEntityDescription( "VA", - native_unit_of_measurement=POWER_VOLT_AMPERE, + native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE, state_class=SensorStateClass.MEASUREMENT, - icon="mdi:flash", + device_class=SensorDeviceClass.APPARENT_POWER, entity_registry_enabled_default=False, ), "VAR": IotaWattSensorEntityDescription( @@ -108,7 +109,7 @@ ENTITY_DESCRIPTION_KEY_MAP: dict[str, IotaWattSensorEntityDescription] = { ), "Volts": IotaWattSensorEntityDescription( "Volts", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, state_class=SensorStateClass.MEASUREMENT, device_class=SensorDeviceClass.VOLTAGE, entity_registry_enabled_default=False, diff --git a/homeassistant/components/iotawatt/translations/ko.json b/homeassistant/components/iotawatt/translations/ko.json index f3fac4e1e74..5be9ff44d36 100644 --- a/homeassistant/components/iotawatt/translations/ko.json +++ b/homeassistant/components/iotawatt/translations/ko.json @@ -2,12 +2,19 @@ "config": { "error": { "cannot_connect": "\uc5f0\uacb0 \uc2e4\ud328", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc5d0\ub7ec" }, "step": { "auth": { "data": { - "password": "\uc554\ud638" + "password": "\uc554\ud638", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + } + }, + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8" } } } diff --git a/homeassistant/components/iotawatt/translations/pt.json b/homeassistant/components/iotawatt/translations/pt.json index 6e210edaef7..cf71fab1dcd 100644 --- a/homeassistant/components/iotawatt/translations/pt.json +++ b/homeassistant/components/iotawatt/translations/pt.json @@ -11,7 +11,7 @@ }, "user": { "data": { - "host": "Servidor" + "host": "Endere\u00e7o" } } } diff --git a/homeassistant/components/iotawatt/translations/sk.json b/homeassistant/components/iotawatt/translations/sk.json index 0fcf434a18d..b099b910709 100644 --- a/homeassistant/components/iotawatt/translations/sk.json +++ b/homeassistant/components/iotawatt/translations/sk.json @@ -10,7 +10,8 @@ "data": { "password": "Heslo", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" - } + }, + "description": "Zariadenie IoTawatt vy\u017eaduje autentifik\u00e1ciu. Zadajte pou\u017e\u00edvate\u013esk\u00e9 meno a heslo a kliknite na tla\u010didlo Odosla\u0165." }, "user": { "data": { diff --git a/homeassistant/components/iperf3/__init__.py b/homeassistant/components/iperf3/__init__.py index ad7c6775107..951397e7e61 100644 --- a/homeassistant/components/iperf3/__init__.py +++ b/homeassistant/components/iperf3/__init__.py @@ -9,6 +9,7 @@ import voluptuous as vol from homeassistant.components.sensor import ( DOMAIN as SENSOR_DOMAIN, + SensorDeviceClass, SensorEntityDescription, ) from homeassistant.const import ( @@ -18,7 +19,7 @@ from homeassistant.const import ( CONF_PORT, CONF_PROTOCOL, CONF_SCAN_INTERVAL, - DATA_RATE_MEGABITS_PER_SECOND, + UnitOfDataRate, ) from homeassistant.core import HomeAssistant, ServiceCall import homeassistant.helpers.config_validation as cv @@ -51,12 +52,14 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key=ATTR_DOWNLOAD, name=ATTR_DOWNLOAD.capitalize(), - native_unit_of_measurement=DATA_RATE_MEGABITS_PER_SECOND, + device_class=SensorDeviceClass.DATA_RATE, + native_unit_of_measurement=UnitOfDataRate.MEGABITS_PER_SECOND, ), SensorEntityDescription( key=ATTR_UPLOAD, name=ATTR_UPLOAD.capitalize(), - native_unit_of_measurement=DATA_RATE_MEGABITS_PER_SECOND, + device_class=SensorDeviceClass.DATA_RATE, + native_unit_of_measurement=UnitOfDataRate.MEGABITS_PER_SECOND, ), ) SENSOR_KEYS: list[str] = [desc.key for desc in SENSOR_TYPES] diff --git a/homeassistant/components/ipma/translations/de.json b/homeassistant/components/ipma/translations/de.json index 9fa766c190b..e96b08e6905 100644 --- a/homeassistant/components/ipma/translations/de.json +++ b/homeassistant/components/ipma/translations/de.json @@ -11,14 +11,14 @@ "mode": "Modus", "name": "Name" }, - "description": "Instituto Portugu\u00eas do Mar e Atmosfera", + "description": "Portugiesisches Institut f\u00fcr Meeres- und Atmosph\u00e4renforschung", "title": "Standort" } } }, "system_health": { "info": { - "api_endpoint_reachable": "IPMA-API-Endpunkt erreichbar" + "api_endpoint_reachable": "IPMA-API Endpunkt erreichbar" } } } \ No newline at end of file diff --git a/homeassistant/components/ipma/translations/sk.json b/homeassistant/components/ipma/translations/sk.json index 645383285b9..8a51a1fd8dd 100644 --- a/homeassistant/components/ipma/translations/sk.json +++ b/homeassistant/components/ipma/translations/sk.json @@ -11,8 +11,14 @@ "mode": "Re\u017eim", "name": "N\u00e1zov" }, + "description": "Instituto Portugu\u00eas do Mar e Atmosfera", "title": "Umiestnenie" } } + }, + "system_health": { + "info": { + "api_endpoint_reachable": "Koncov\u00fd bod IPMA API je dosiahnute\u013en\u00fd" + } } } \ No newline at end of file diff --git a/homeassistant/components/ipma/weather.py b/homeassistant/components/ipma/weather.py index 467523c5830..4387e2d8e1e 100644 --- a/homeassistant/components/ipma/weather.py +++ b/homeassistant/components/ipma/weather.py @@ -158,7 +158,10 @@ class IPMAWeather(WeatherEntity): @property def unique_id(self) -> str: """Return a unique id.""" - return f"{self._location.station_latitude}, {self._location.station_longitude}, {self._mode}" + return ( + f"{self._location.station_latitude}, {self._location.station_longitude}," + f" {self._mode}" + ) @property def name(self): diff --git a/homeassistant/components/ipp/config_flow.py b/homeassistant/components/ipp/config_flow.py index 7f953c4cd9a..a00190eebce 100644 --- a/homeassistant/components/ipp/config_flow.py +++ b/homeassistant/components/ipp/config_flow.py @@ -22,13 +22,14 @@ from homeassistant.const import ( CONF_NAME, CONF_PORT, CONF_SSL, + CONF_UUID, CONF_VERIFY_SSL, ) from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers.aiohttp_client import async_get_clientsession -from .const import CONF_BASE_PATH, CONF_SERIAL, CONF_UUID, DOMAIN +from .const import CONF_BASE_PATH, CONF_SERIAL, DOMAIN _LOGGER = logging.getLogger(__name__) @@ -89,7 +90,8 @@ class IPPFlowHandler(ConfigFlow, domain=DOMAIN): if not unique_id and info[CONF_SERIAL]: _LOGGER.debug( - "Printer UUID is missing from IPP response. Falling back to IPP serial number" + "Printer UUID is missing from IPP response. Falling back to IPP serial" + " number" ) unique_id = info[CONF_SERIAL] elif not unique_id: @@ -153,7 +155,8 @@ class IPPFlowHandler(ConfigFlow, domain=DOMAIN): unique_id = self.discovery_info[CONF_UUID] = info[CONF_UUID] elif not unique_id and info[CONF_SERIAL]: _LOGGER.debug( - "Printer UUID is missing from discovery info and IPP response. Falling back to IPP serial number" + "Printer UUID is missing from discovery info and IPP response. Falling" + " back to IPP serial number" ) unique_id = info[CONF_SERIAL] elif not unique_id: diff --git a/homeassistant/components/ipp/const.py b/homeassistant/components/ipp/const.py index 3501759074f..64289838543 100644 --- a/homeassistant/components/ipp/const.py +++ b/homeassistant/components/ipp/const.py @@ -18,4 +18,3 @@ ATTR_URI_SUPPORTED = "uri_supported" CONF_BASE_PATH = "base_path" CONF_SERIAL = "serial" CONF_TLS = "tls" -CONF_UUID = "uuid" diff --git a/homeassistant/components/ipp/sensor.py b/homeassistant/components/ipp/sensor.py index 718b35dbc6a..293a6378b78 100644 --- a/homeassistant/components/ipp/sensor.py +++ b/homeassistant/components/ipp/sensor.py @@ -101,7 +101,9 @@ class IPPMarkerSensor(IPPSensor): unique_id=unique_id, icon="mdi:water", key=f"marker_{marker_index}", - name=f"{coordinator.data.info.name} {coordinator.data.markers[marker_index].name}", + name=( + f"{coordinator.data.info.name} {coordinator.data.markers[marker_index].name}" + ), unit_of_measurement=PERCENTAGE, ) diff --git a/homeassistant/components/ipp/translations/pl.json b/homeassistant/components/ipp/translations/pl.json index b2eb67a6d93..b44904095de 100644 --- a/homeassistant/components/ipp/translations/pl.json +++ b/homeassistant/components/ipp/translations/pl.json @@ -24,7 +24,7 @@ "verify_ssl": "Weryfikacja certyfikatu SSL" }, "description": "Skonfiguruj drukark\u0119 za pomoc\u0105 protoko\u0142u IPP (Internet Printing Protocol), aby zintegrowa\u0107 j\u0105 z Home Assistantem.", - "title": "Po\u0142\u0105czenie z Twoj\u0105 drukark\u0105" + "title": "Po\u0142\u0105cz swoj\u0105 drukark\u0119" }, "zeroconf_confirm": { "description": "Czy chcesz doda\u0107 drukark\u0119 o nazwie `{name}` do Home Assistanta?", diff --git a/homeassistant/components/ipp/translations/pt.json b/homeassistant/components/ipp/translations/pt.json index 1f312c187cf..5af13185791 100644 --- a/homeassistant/components/ipp/translations/pt.json +++ b/homeassistant/components/ipp/translations/pt.json @@ -2,12 +2,12 @@ "config": { "abort": { "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "ipp_error": "Erro IPP encontrado.", "ipp_version_error": "Vers\u00e3o IPP n\u00e3o suportada pela impressora." }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "user": { diff --git a/homeassistant/components/ipp/translations/sk.json b/homeassistant/components/ipp/translations/sk.json index 1f5c5b348ec..8583079b1a2 100644 --- a/homeassistant/components/ipp/translations/sk.json +++ b/homeassistant/components/ipp/translations/sk.json @@ -3,8 +3,11 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "cannot_connect": "Nepodarilo sa pripoji\u0165", + "connection_upgrade": "Nepodarilo sa pripoji\u0165 k tla\u010diarni z d\u00f4vodu vy\u017eadovania aktualiz\u00e1cie pripojenia.", "ipp_error": "Vyskytla sa chyba IPP.", - "ipp_version_error": "Verzia IPP nie je podporovan\u00e1 tla\u010diar\u0148ou." + "ipp_version_error": "Verzia IPP nie je podporovan\u00e1 tla\u010diar\u0148ou.", + "parse_error": "Nepodarilo sa analyzova\u0165 odpove\u010f z tla\u010diarne.", + "unique_id_required": "Zariadeniu ch\u00fdba jedine\u010dn\u00e1 identifik\u00e1cia potrebn\u00e1 na objavenie." }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", @@ -14,11 +17,13 @@ "step": { "user": { "data": { + "base_path": "Relat\u00edvna cesta k tla\u010diarni", "host": "Hostite\u013e", "port": "Port", "ssl": "Pou\u017e\u00edva SSL certifik\u00e1t", "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" }, + "description": "Nastavte svoju tla\u010diare\u0148 cez Internet Printing Protocol (IPP) na integr\u00e1ciu s Home Assistant.", "title": "Prepojte tla\u010diare\u0148" }, "zeroconf_confirm": { diff --git a/homeassistant/components/iqvia/sensor.py b/homeassistant/components/iqvia/sensor.py index 033ed4e3031..455711c0ead 100644 --- a/homeassistant/components/iqvia/sensor.py +++ b/homeassistant/components/iqvia/sensor.py @@ -47,7 +47,6 @@ ATTR_ZIP_CODE = "zip_code" API_CATEGORY_MAPPING = { TYPE_ALLERGY_TODAY: TYPE_ALLERGY_INDEX, TYPE_ALLERGY_TOMORROW: TYPE_ALLERGY_INDEX, - TYPE_ALLERGY_TOMORROW: TYPE_ALLERGY_INDEX, TYPE_ASTHMA_TODAY: TYPE_ASTHMA_INDEX, TYPE_ASTHMA_TOMORROW: TYPE_ASTHMA_INDEX, TYPE_DISEASE_TODAY: TYPE_DISEASE_INDEX, diff --git a/homeassistant/components/irish_rail_transport/sensor.py b/homeassistant/components/irish_rail_transport/sensor.py index d11a0b31809..7ac30cc5a23 100644 --- a/homeassistant/components/irish_rail_transport/sensor.py +++ b/homeassistant/components/irish_rail_transport/sensor.py @@ -7,7 +7,7 @@ from pyirishrail.pyirishrail import IrishRailRTPI import voluptuous as vol from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity -from homeassistant.const import CONF_NAME, TIME_MINUTES +from homeassistant.const import CONF_NAME, UnitOfTime from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -126,7 +126,7 @@ class IrishRailTransportSensor(SensorEntity): @property def native_unit_of_measurement(self): """Return the unit this state is expressed in.""" - return TIME_MINUTES + return UnitOfTime.MINUTES @property def icon(self): diff --git a/homeassistant/components/islamic_prayer_times/__init__.py b/homeassistant/components/islamic_prayer_times/__init__.py index 406eaf23670..55c27c4cc59 100644 --- a/homeassistant/components/islamic_prayer_times/__init__.py +++ b/homeassistant/components/islamic_prayer_times/__init__.py @@ -105,11 +105,13 @@ class IslamicPrayerClient: if now > dt_util.as_utc(midnight_dt): next_update_at = midnight_dt + timedelta(days=1, minutes=1) _LOGGER.debug( - "Midnight is after day the changes so schedule update for after Midnight the next day" + "Midnight is after day the changes so schedule update for after" + " Midnight the next day" ) else: _LOGGER.debug( - "Midnight is before the day changes so schedule update for the next start of day" + "Midnight is before the day changes so schedule update for the next" + " start of day" ) next_update_at = dt_util.start_of_local_day(now + timedelta(days=1)) diff --git a/homeassistant/components/iss/translations/ko.json b/homeassistant/components/iss/translations/ko.json new file mode 100644 index 00000000000..416fb8d164e --- /dev/null +++ b/homeassistant/components/iss/translations/ko.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/iss/translations/sk.json b/homeassistant/components/iss/translations/sk.json index 0d6627fce15..12a53caa14d 100644 --- a/homeassistant/components/iss/translations/sk.json +++ b/homeassistant/components/iss/translations/sk.json @@ -3,6 +3,20 @@ "abort": { "latitude_longitude_not_defined": "Zemepisn\u00e1 \u0161\u00edrka a d\u013a\u017eka nie s\u00fa definovan\u00e9 v aplik\u00e1cii Home Assistant.", "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, + "step": { + "user": { + "description": "Chcete nakonfigurova\u0165 Medzin\u00e1rodn\u00fa vesm\u00edrnu stanicu (ISS)?" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "show_on_map": "Zobrazi\u0165 na mape" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/isy994/__init__.py b/homeassistant/components/isy994/__init__.py index 1b689e563dc..4fabb120102 100644 --- a/homeassistant/components/isy994/__init__.py +++ b/homeassistant/components/isy994/__init__.py @@ -180,7 +180,8 @@ async def async_setup_entry( await isy.initialize() except asyncio.TimeoutError as err: raise ConfigEntryNotReady( - f"Timed out initializing the ISY; device may be busy, trying again later: {err}" + "Timed out initializing the ISY; device may be busy, trying again later:" + f" {err}" ) from err except ISYInvalidAuthError as err: raise ConfigEntryAuthFailed(f"Invalid credentials for the ISY: {err}") from err @@ -190,7 +191,8 @@ async def async_setup_entry( ) from err except ISYResponseParseError as err: raise ConfigEntryNotReady( - f"Invalid XML response from ISY; Ensure the ISY is running the latest firmware: {err}" + "Invalid XML response from ISY; Ensure the ISY is running the latest" + f" firmware: {err}" ) from err except TypeError as err: raise ConfigEntryNotReady( diff --git a/homeassistant/components/isy994/binary_sensor.py b/homeassistant/components/isy994/binary_sensor.py index 81efdb5922a..399d9953170 100644 --- a/homeassistant/components/isy994/binary_sensor.py +++ b/homeassistant/components/isy994/binary_sensor.py @@ -116,8 +116,10 @@ async def async_setup_entry( parent_entity = entities_by_address.get(node.parent_node.address) if not parent_entity: _LOGGER.error( - "Node %s has a parent node %s, but no device " - "was created for the parent. Skipping", + ( + "Node %s has a parent node %s, but no device " + "was created for the parent. Skipping" + ), node.address, node.parent_node, ) diff --git a/homeassistant/components/isy994/climate.py b/homeassistant/components/isy994/climate.py index c141f856408..5267dbff48d 100644 --- a/homeassistant/components/isy994/climate.py +++ b/homeassistant/components/isy994/climate.py @@ -29,12 +29,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - ATTR_TEMPERATURE, - PRECISION_TENTHS, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, -) +from homeassistant.const import ATTR_TEMPERATURE, PRECISION_TENTHS, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -103,10 +98,10 @@ class ISYThermostatEntity(ISYNodeEntity, ClimateEntity): if not (uom := self._node.aux_properties.get(PROP_UOM)): return self.hass.config.units.temperature_unit if uom.value == UOM_ISY_CELSIUS: - return TEMP_CELSIUS + return UnitOfTemperature.CELSIUS if uom.value == UOM_ISY_FAHRENHEIT: - return TEMP_FAHRENHEIT - return TEMP_FAHRENHEIT + return UnitOfTemperature.FAHRENHEIT + return UnitOfTemperature.FAHRENHEIT @property def current_humidity(self) -> int | None: diff --git a/homeassistant/components/isy994/const.py b/homeassistant/components/isy994/const.py index d9c78f904e2..8f6c9b0f888 100644 --- a/homeassistant/components/isy994/const.py +++ b/homeassistant/components/isy994/const.py @@ -17,37 +17,11 @@ from homeassistant.const import ( CURRENCY_CENT, CURRENCY_DOLLAR, DEGREE, - ELECTRIC_CURRENT_MILLIAMPERE, - ELECTRIC_POTENTIAL_MILLIVOLT, - ELECTRIC_POTENTIAL_VOLT, - ENERGY_KILO_WATT_HOUR, - ENERGY_WATT_HOUR, - FREQUENCY_HERTZ, - IRRADIATION_WATTS_PER_SQUARE_METER, - LENGTH_CENTIMETERS, - LENGTH_FEET, - LENGTH_INCHES, - LENGTH_KILOMETERS, - LENGTH_METERS, - LENGTH_MILES, - LENGTH_MILLIMETERS, LIGHT_LUX, - MASS_KILOGRAMS, - MASS_POUNDS, PERCENTAGE, - POWER_KILO_WATT, - POWER_WATT, - PRESSURE_HPA, - PRESSURE_INHG, - PRESSURE_MBAR, REVOLUTIONS_PER_MINUTE, SERVICE_LOCK, SERVICE_UNLOCK, - SOUND_PRESSURE_DB, - SOUND_PRESSURE_WEIGHTED_DBA, - SPEED_KILOMETERS_PER_HOUR, - SPEED_METERS_PER_SECOND, - SPEED_MILES_PER_HOUR, STATE_CLOSED, STATE_CLOSING, STATE_LOCKED, @@ -58,24 +32,23 @@ from homeassistant.const import ( STATE_PROBLEM, STATE_UNKNOWN, STATE_UNLOCKED, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, - TEMP_KELVIN, - TIME_DAYS, - TIME_HOURS, - TIME_MILLISECONDS, - TIME_MINUTES, - TIME_MONTHS, - TIME_SECONDS, - TIME_YEARS, UV_INDEX, - VOLUME_CUBIC_FEET, - VOLUME_CUBIC_METERS, - VOLUME_FLOW_RATE_CUBIC_FEET_PER_MINUTE, - VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR, - VOLUME_GALLONS, - VOLUME_LITERS, Platform, + UnitOfElectricCurrent, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfFrequency, + UnitOfIrradiance, + UnitOfLength, + UnitOfMass, + UnitOfPower, + UnitOfPressure, + UnitOfSoundPressure, + UnitOfSpeed, + UnitOfTemperature, + UnitOfTime, + UnitOfVolume, + UnitOfVolumeFlowRate, UnitOfVolumetricFlux, ) @@ -320,59 +293,59 @@ NODE_FILTERS: dict[Platform, dict[str, list[str]]] = { UOM_FRIENDLY_NAME = { "1": "A", UOM_ON_OFF: "", # Binary, no unit - "3": f"btu/{TIME_HOURS}", - "4": TEMP_CELSIUS, - "5": LENGTH_CENTIMETERS, - "6": VOLUME_CUBIC_FEET, - "7": VOLUME_FLOW_RATE_CUBIC_FEET_PER_MINUTE, - "8": VOLUME_CUBIC_METERS, - "9": TIME_DAYS, - "10": TIME_DAYS, - "12": SOUND_PRESSURE_DB, - "13": SOUND_PRESSURE_WEIGHTED_DBA, + "3": f"btu/{UnitOfTime.HOURS}", + "4": UnitOfTemperature.CELSIUS, + "5": UnitOfLength.CENTIMETERS, + "6": UnitOfVolume.CUBIC_FEET, + "7": UnitOfVolumeFlowRate.CUBIC_FEET_PER_MINUTE, + "8": UnitOfVolume.CUBIC_METERS, + "9": UnitOfTime.DAYS, + "10": UnitOfTime.DAYS, + "12": UnitOfSoundPressure.DECIBEL, + "13": UnitOfSoundPressure.WEIGHTED_DECIBEL_A, "14": DEGREE, "16": "macroseismic", - "17": TEMP_FAHRENHEIT, - "18": LENGTH_FEET, - "19": TIME_HOURS, - "20": TIME_HOURS, + "17": UnitOfTemperature.FAHRENHEIT, + "18": UnitOfLength.FEET, + "19": UnitOfTime.HOURS, + "20": UnitOfTime.HOURS, "21": "%AH", "22": "%RH", - "23": PRESSURE_INHG, + "23": UnitOfPressure.INHG, "24": UnitOfVolumetricFlux.INCHES_PER_HOUR, UOM_INDEX: UOM_INDEX, # Index type. Use "node.formatted" for value - "26": TEMP_KELVIN, + "26": UnitOfTemperature.KELVIN, "27": "keyword", - "28": MASS_KILOGRAMS, + "28": UnitOfMass.KILOGRAMS, "29": "kV", - "30": POWER_KILO_WATT, + "30": UnitOfPower.KILO_WATT, "31": "kPa", - "32": SPEED_KILOMETERS_PER_HOUR, - "33": ENERGY_KILO_WATT_HOUR, + "32": UnitOfSpeed.KILOMETERS_PER_HOUR, + "33": UnitOfEnergy.KILO_WATT_HOUR, "34": "liedu", - "35": VOLUME_LITERS, + "35": UnitOfVolume.LITERS, "36": LIGHT_LUX, "37": "mercalli", - "38": LENGTH_METERS, - "39": VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR, - "40": SPEED_METERS_PER_SECOND, - "41": ELECTRIC_CURRENT_MILLIAMPERE, - "42": TIME_MILLISECONDS, - "43": ELECTRIC_POTENTIAL_MILLIVOLT, - "44": TIME_MINUTES, - "45": TIME_MINUTES, + "38": UnitOfLength.METERS, + "39": UnitOfVolumeFlowRate.CUBIC_METERS_PER_HOUR, + "40": UnitOfSpeed.METERS_PER_SECOND, + "41": UnitOfElectricCurrent.MILLIAMPERE, + "42": UnitOfTime.MILLISECONDS, + "43": UnitOfElectricPotential.MILLIVOLT, + "44": UnitOfTime.MINUTES, + "45": UnitOfTime.MINUTES, "46": UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR, - "47": TIME_MONTHS, - "48": SPEED_MILES_PER_HOUR, - "49": SPEED_METERS_PER_SECOND, + "47": UnitOfTime.MONTHS, + "48": UnitOfSpeed.MILES_PER_HOUR, + "49": UnitOfSpeed.METERS_PER_SECOND, "50": "Ω", UOM_PERCENTAGE: PERCENTAGE, - "52": MASS_POUNDS, + "52": UnitOfMass.POUNDS, "53": "pf", "54": CONCENTRATION_PARTS_PER_MILLION, "55": "pulse count", - "57": TIME_SECONDS, - "58": TIME_SECONDS, + "57": UnitOfTime.SECONDS, + "58": UnitOfTime.SECONDS, "59": "S/m", "60": "m_b", "61": "M_L", @@ -380,22 +353,22 @@ UOM_FRIENDLY_NAME = { "63": "M_S", "64": "shindo", "65": "SML", - "69": VOLUME_GALLONS, + "69": UnitOfVolume.GALLONS, "71": UV_INDEX, - "72": ELECTRIC_POTENTIAL_VOLT, - "73": POWER_WATT, - "74": IRRADIATION_WATTS_PER_SQUARE_METER, + "72": UnitOfElectricPotential.VOLT, + "73": UnitOfPower.WATT, + "74": UnitOfIrradiance.WATTS_PER_SQUARE_METER, "75": "weekday", "76": DEGREE, - "77": TIME_YEARS, - "82": LENGTH_MILLIMETERS, - "83": LENGTH_KILOMETERS, + "77": UnitOfTime.YEARS, + "82": UnitOfLength.MILLIMETERS, + "83": UnitOfLength.KILOMETERS, "85": "Ω", "86": "kΩ", - "87": f"{VOLUME_CUBIC_METERS}/{VOLUME_CUBIC_METERS}", + "87": f"{UnitOfVolume.CUBIC_METERS}/{UnitOfVolume.CUBIC_METERS}", "88": "Water activity", "89": REVOLUTIONS_PER_MINUTE, - "90": FREQUENCY_HERTZ, + "90": UnitOfFrequency.HERTZ, "91": DEGREE, "92": f"{DEGREE} South", UOM_8_BIT_RANGE: "", # Range 0-255, no unit. @@ -403,7 +376,7 @@ UOM_FRIENDLY_NAME = { "102": "kWs", "103": CURRENCY_DOLLAR, "104": CURRENCY_CENT, - "105": LENGTH_INCHES, + "105": UnitOfLength.INCHES, "106": UnitOfVolumetricFlux.MILLIMETERS_PER_DAY, "107": "", # raw 1-byte unsigned value "108": "", # raw 2-byte unsigned value @@ -413,10 +386,10 @@ UOM_FRIENDLY_NAME = { "112": "", # raw 2-byte signed value "113": "", # raw 3-byte signed value "114": "", # raw 4-byte signed value - "116": LENGTH_MILES, - "117": PRESSURE_MBAR, - "118": PRESSURE_HPA, - "119": ENERGY_WATT_HOUR, + "116": UnitOfLength.MILES, + "117": UnitOfPressure.MBAR, + "118": UnitOfPressure.HPA, + "119": UnitOfEnergy.WATT_HOUR, "120": UnitOfVolumetricFlux.INCHES_PER_DAY, } diff --git a/homeassistant/components/isy994/entity.py b/homeassistant/components/isy994/entity.py index 61f42a60a6e..a90701f3323 100644 --- a/homeassistant/components/isy994/entity.py +++ b/homeassistant/components/isy994/entity.py @@ -209,7 +209,8 @@ class ISYNodeEntity(ISYEntity): """Respond to an entity service command to request a Z-Wave device parameter from the ISY.""" if not hasattr(self._node, "protocol") or self._node.protocol != PROTO_ZWAVE: raise HomeAssistantError( - f"Invalid service call: cannot request Z-Wave Parameter for non-Z-Wave device {self.entity_id}" + "Invalid service call: cannot request Z-Wave Parameter for non-Z-Wave" + f" device {self.entity_id}" ) await self._node.get_zwave_parameter(parameter) @@ -219,7 +220,8 @@ class ISYNodeEntity(ISYEntity): """Respond to an entity service command to set a Z-Wave device parameter via the ISY.""" if not hasattr(self._node, "protocol") or self._node.protocol != PROTO_ZWAVE: raise HomeAssistantError( - f"Invalid service call: cannot set Z-Wave Parameter for non-Z-Wave device {self.entity_id}" + "Invalid service call: cannot set Z-Wave Parameter for non-Z-Wave" + f" device {self.entity_id}" ) await self._node.set_zwave_parameter(parameter, value, size) await self._node.get_zwave_parameter(parameter) diff --git a/homeassistant/components/isy994/helpers.py b/homeassistant/components/isy994/helpers.py index 736cd12b9ea..c4f0c2ea595 100644 --- a/homeassistant/components/isy994/helpers.py +++ b/homeassistant/components/isy994/helpers.py @@ -352,7 +352,10 @@ def _categorize_programs(hass_isy_data: dict, programs: Programs) -> None: actions = entity_folder.get_by_name(KEY_ACTIONS) if not actions or actions.protocol != PROTO_PROGRAM: _LOGGER.warning( - "Program %s entity '%s' not loaded, invalid/missing actions program", + ( + "Program %s entity '%s' not loaded, invalid/missing actions" + " program" + ), platform, entity_folder.name, ) diff --git a/homeassistant/components/isy994/manifest.json b/homeassistant/components/isy994/manifest.json index d1c18fce595..cfc7f5d0e22 100644 --- a/homeassistant/components/isy994/manifest.json +++ b/homeassistant/components/isy994/manifest.json @@ -3,7 +3,7 @@ "name": "Universal Devices ISY994", "integration_type": "hub", "documentation": "https://www.home-assistant.io/integrations/isy994", - "requirements": ["pyisy==3.0.8"], + "requirements": ["pyisy==3.0.10"], "codeowners": ["@bdraco", "@shbatm"], "config_flow": true, "ssdp": [ diff --git a/homeassistant/components/isy994/sensor.py b/homeassistant/components/isy994/sensor.py index b02050f6f7b..3931c9d4cf9 100644 --- a/homeassistant/components/isy994/sensor.py +++ b/homeassistant/components/isy994/sensor.py @@ -27,7 +27,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT +from homeassistant.const import UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -173,7 +173,7 @@ class ISYSensorEntity(ISYNodeEntity, SensorEntity): value = convert_isy_value_to_hass(value, uom, self.target.prec) # Convert temperatures to Home Assistant's unit - if uom in (TEMP_CELSIUS, TEMP_FAHRENHEIT): + if uom in (UnitOfTemperature.CELSIUS, UnitOfTemperature.FAHRENHEIT): value = self.hass.config.units.temperature(value, uom) if value is None: @@ -189,7 +189,11 @@ class ISYSensorEntity(ISYNodeEntity, SensorEntity): # Check if this is a known index pair UOM if isinstance(raw_units, dict) or raw_units in (UOM_ON_OFF, UOM_INDEX): return None - if raw_units in (TEMP_FAHRENHEIT, TEMP_CELSIUS, UOM_DOUBLE_TEMP): + if raw_units in ( + UnitOfTemperature.FAHRENHEIT, + UnitOfTemperature.CELSIUS, + UOM_DOUBLE_TEMP, + ): return self.hass.config.units.temperature_unit return raw_units diff --git a/homeassistant/components/isy994/services.py b/homeassistant/components/isy994/services.py index 30b5f121df3..18076e2da98 100644 --- a/homeassistant/components/isy994/services.py +++ b/homeassistant/components/isy994/services.py @@ -299,8 +299,10 @@ def async_setup_services(hass: HomeAssistant) -> None: # noqa: C901 entity_registry.async_remove(entity_id) _LOGGER.debug( - "Cleaning up ISY994 Entities and devices: Config Entries: %s, Current Entries: %s, " - "Extra Entries Removed: %s", + ( + "Cleaning up ISY994 Entities and devices: Config Entries: %s, Current" + " Entries: %s, Extra Entries Removed: %s" + ), len(config_ids), len(current_unique_ids), len(extra_entities), diff --git a/homeassistant/components/isy994/translations/de.json b/homeassistant/components/isy994/translations/de.json index a183e6f7853..ecc42152766 100644 --- a/homeassistant/components/isy994/translations/de.json +++ b/homeassistant/components/isy994/translations/de.json @@ -6,7 +6,7 @@ "error": { "cannot_connect": "Verbindung fehlgeschlagen", "invalid_auth": "Ung\u00fcltige Authentifizierung", - "invalid_host": "Der Hosteintrag hatte nicht das vollst\u00e4ndige URL-Format, z. B. http://192.168.10.100:80", + "invalid_host": "Der Host Eintrag hatte nicht das vollst\u00e4ndige URL-Format, z.B. http://192.168.10.100:80", "reauth_successful": "Die erneute Authentifizierung war erfolgreich", "unknown": "Unerwarteter Fehler" }, @@ -27,7 +27,7 @@ "tls": "Die TLS-Version des ISY-Controllers.", "username": "Benutzername" }, - "description": "Der Hosteintrag muss im vollst\u00e4ndigen URL-Format vorliegen, z. B. http://192.168.10.100:80", + "description": "Der Host Eintrag muss im vollst\u00e4ndigen URL-Format vorliegen, z.B. http://192.168.10.100:80", "title": "Verbindung zu deinem ISY" } } diff --git a/homeassistant/components/isy994/translations/pt.json b/homeassistant/components/isy994/translations/pt.json index 6d8ee09a26c..abc140353f6 100644 --- a/homeassistant/components/isy994/translations/pt.json +++ b/homeassistant/components/isy994/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, diff --git a/homeassistant/components/isy994/translations/sk.json b/homeassistant/components/isy994/translations/sk.json index 06886d7c6bc..6779170653a 100644 --- a/homeassistant/components/isy994/translations/sk.json +++ b/homeassistant/components/isy994/translations/sk.json @@ -7,6 +7,7 @@ "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie", "invalid_host": "Z\u00e1znam hostite\u013ea nemal \u00fapln\u00fd form\u00e1t adresy URL, napr. http://192.168.10.100:80", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "flow_title": "{name} ({host})", @@ -16,12 +17,14 @@ "password": "Heslo", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" }, - "description": "Poverenia pre {host} u\u017e nie s\u00fa platn\u00e9." + "description": "Poverenia pre {host} u\u017e nie s\u00fa platn\u00e9.", + "title": "Znova overte svoje ISY" }, "user": { "data": { "host": "URL", "password": "Heslo", + "tls": "Verzia TLS ovl\u00e1da\u010da ISY.", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" }, "description": "Polo\u017eka hostite\u013ea mus\u00ed by\u0165 v \u00faplnom form\u00e1te URL, napr. http://192.168.10.100:80", @@ -33,15 +36,22 @@ "step": { "init": { "data": { - "restore_light_state": "Obnovenie jasu osvetlenia" - } + "ignore_string": "Ignorova\u0165 re\u0165azec", + "restore_light_state": "Obnovenie jasu osvetlenia", + "sensor_string": "Re\u0165azec sn\u00edma\u010da uzla", + "variable_sensor_string": "Variabiln\u00fd re\u0165azec sn\u00edma\u010da" + }, + "description": "Nastavte mo\u017enosti pre integr\u00e1ciu ISY:\n \u2022 Re\u0165azec sn\u00edma\u010da uzla: Ka\u017ed\u00e9 zariadenie alebo prie\u010dinok, ktor\u00fd v n\u00e1zve obsahuje re\u0165azec sn\u00edma\u010da uzla, sa bude pova\u017eova\u0165 za sn\u00edma\u010d alebo bin\u00e1rny sn\u00edma\u010d.\n \u2022 Ignorova\u0165 re\u0165azec: Ka\u017ed\u00e9 zariadenie s 'Ignorova\u0165 re\u0165azec' v n\u00e1zve bude ignorovan\u00e9.\n \u2022 Variabiln\u00fd re\u0165azec sn\u00edma\u010da: Ak\u00e1ko\u013evek premenn\u00e1, ktor\u00e1 obsahuje re\u0165azec premenn\u00fdch sn\u00edma\u010dov, bude pridan\u00e1 ako sn\u00edma\u010d.\n \u2022 Obnovi\u0165 jas svetla: Ak je povolen\u00e1, pri zapnut\u00ed svetla sa obnov\u00ed predch\u00e1dzaj\u00faci jas namiesto vstavanej On-Level zariadenia.", + "title": "Mo\u017enosti ISY" } } }, "system_health": { "info": { "device_connected": "ISY pripojen\u00e9", - "host_reachable": "Hostite\u013e je dostupn\u00fd" + "host_reachable": "Hostite\u013e je dostupn\u00fd", + "last_heartbeat": "Posledn\u00e1 aktualiz\u00e1cia", + "websocket_status": "Stav socketu udalosti" } } } \ No newline at end of file diff --git a/homeassistant/components/izone/climate.py b/homeassistant/components/izone/climate.py index c94ccada5b3..59b57f888a7 100644 --- a/homeassistant/components/izone/climate.py +++ b/homeassistant/components/izone/climate.py @@ -25,7 +25,7 @@ from homeassistant.const import ( CONF_EXCLUDE, PRECISION_HALVES, PRECISION_TENTHS, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import entity_platform @@ -129,7 +129,7 @@ class ControllerDevice(ClimateEntity): _attr_precision = PRECISION_TENTHS _attr_should_poll = False - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS def __init__(self, controller: Controller) -> None: """Initialise ControllerDevice.""" @@ -437,7 +437,7 @@ class ZoneDevice(ClimateEntity): _attr_precision = PRECISION_TENTHS _attr_should_poll = False - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS def __init__(self, controller: ControllerDevice, zone: Zone) -> None: """Initialise ZoneDevice.""" diff --git a/homeassistant/components/izone/translations/sk.json b/homeassistant/components/izone/translations/sk.json index 99798036ffd..60ef376096e 100644 --- a/homeassistant/components/izone/translations/sk.json +++ b/homeassistant/components/izone/translations/sk.json @@ -3,6 +3,11 @@ "abort": { "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, + "step": { + "confirm": { + "description": "Chcete nastavi\u0165 iZone?" + } } } } \ No newline at end of file diff --git a/homeassistant/components/jellyfin/coordinator.py b/homeassistant/components/jellyfin/coordinator.py index 626b2126fee..ac2cd78d257 100644 --- a/homeassistant/components/jellyfin/coordinator.py +++ b/homeassistant/components/jellyfin/coordinator.py @@ -1,7 +1,7 @@ """Data update coordinator for the Jellyfin integration.""" from __future__ import annotations -from abc import abstractmethod +from abc import ABC, abstractmethod from datetime import timedelta from typing import Any, TypeVar, Union @@ -22,7 +22,7 @@ JellyfinDataT = TypeVar( ) -class JellyfinDataUpdateCoordinator(DataUpdateCoordinator[JellyfinDataT]): +class JellyfinDataUpdateCoordinator(DataUpdateCoordinator[JellyfinDataT], ABC): """Data update coordinator for the Jellyfin integration.""" config_entry: ConfigEntry diff --git a/homeassistant/components/jellyfin/translations/ko.json b/homeassistant/components/jellyfin/translations/ko.json new file mode 100644 index 00000000000..5794b07ce25 --- /dev/null +++ b/homeassistant/components/jellyfin/translations/ko.json @@ -0,0 +1,21 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638", + "url": "URL \uc8fc\uc18c", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/juicenet/entity.py b/homeassistant/components/juicenet/entity.py index c0151a0cd00..0f3811bef6f 100644 --- a/homeassistant/components/juicenet/entity.py +++ b/homeassistant/components/juicenet/entity.py @@ -31,7 +31,9 @@ class JuiceNetDevice(CoordinatorEntity): def device_info(self) -> DeviceInfo: """Return device information about this JuiceNet Device.""" return DeviceInfo( - configuration_url=f"https://home.juice.net/Portal/Details?unitID={self.device.id}", + configuration_url=( + f"https://home.juice.net/Portal/Details?unitID={self.device.id}" + ), identifiers={(DOMAIN, self.device.id)}, manufacturer="JuiceNet", name=self.device.name, diff --git a/homeassistant/components/juicenet/sensor.py b/homeassistant/components/juicenet/sensor.py index 7ae7b148552..e87c752962b 100644 --- a/homeassistant/components/juicenet/sensor.py +++ b/homeassistant/components/juicenet/sensor.py @@ -9,12 +9,12 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - ELECTRIC_CURRENT_AMPERE, - ELECTRIC_POTENTIAL_VOLT, - ENERGY_WATT_HOUR, - POWER_WATT, - TEMP_CELSIUS, - TIME_SECONDS, + UnitOfElectricCurrent, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfPower, + UnitOfTemperature, + UnitOfTime, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -30,40 +30,40 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="temperature", name="Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="voltage", name="Voltage", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, ), SensorEntityDescription( key="amps", name="Amps", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="watts", name="Watts", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="charge_time", name="Charge time", - native_unit_of_measurement=TIME_SECONDS, + native_unit_of_measurement=UnitOfTime.SECONDS, icon="mdi:timer-outline", ), SensorEntityDescription( key="energy_added", name="Energy added", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), diff --git a/homeassistant/components/juicenet/translations/pt.json b/homeassistant/components/juicenet/translations/pt.json index db82206819d..67edc605953 100644 --- a/homeassistant/components/juicenet/translations/pt.json +++ b/homeassistant/components/juicenet/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "Conta j\u00e1 configurada" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, diff --git a/homeassistant/components/justnimbus/sensor.py b/homeassistant/components/justnimbus/sensor.py index 41f1e81d5b3..310a9a939c0 100644 --- a/homeassistant/components/justnimbus/sensor.py +++ b/homeassistant/components/justnimbus/sensor.py @@ -14,10 +14,10 @@ from homeassistant.components.sensor import ( from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONF_CLIENT_ID, - PRESSURE_BAR, - TEMP_CELSIUS, - TIME_HOURS, - VOLUME_LITERS, + UnitOfPressure, + UnitOfTemperature, + UnitOfTime, + UnitOfVolume, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory @@ -65,7 +65,7 @@ SENSOR_TYPES = ( JustNimbusEntityDescription( key="pump_pressure", name="Pump pressure", - native_unit_of_measurement=PRESSURE_BAR, + native_unit_of_measurement=UnitOfPressure.BAR, device_class=SensorDeviceClass.PRESSURE, state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, @@ -84,7 +84,7 @@ SENSOR_TYPES = ( name="Pump hours", icon="mdi:clock", device_class=SensorDeviceClass.DURATION, - native_unit_of_measurement=TIME_HOURS, + native_unit_of_measurement=UnitOfTime.HOURS, state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, value_fn=lambda coordinator: coordinator.data.pump_hours, @@ -92,7 +92,7 @@ SENSOR_TYPES = ( JustNimbusEntityDescription( key="reservoir_temp", name="Reservoir Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, @@ -102,7 +102,7 @@ SENSOR_TYPES = ( key="reservoir_content", name="Reservoir content", icon="mdi:car-coolant-level", - native_unit_of_measurement=VOLUME_LITERS, + native_unit_of_measurement=UnitOfVolume.LITERS, device_class=SensorDeviceClass.VOLUME, state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, @@ -112,7 +112,7 @@ SENSOR_TYPES = ( key="total_saved", name="Total saved", icon="mdi:water-opacity", - native_unit_of_measurement=VOLUME_LITERS, + native_unit_of_measurement=UnitOfVolume.LITERS, device_class=SensorDeviceClass.VOLUME, state_class=SensorStateClass.TOTAL_INCREASING, entity_category=EntityCategory.DIAGNOSTIC, @@ -122,7 +122,7 @@ SENSOR_TYPES = ( key="total_replenished", name="Total replenished", icon="mdi:water", - native_unit_of_measurement=VOLUME_LITERS, + native_unit_of_measurement=UnitOfVolume.LITERS, device_class=SensorDeviceClass.VOLUME, state_class=SensorStateClass.TOTAL_INCREASING, entity_category=EntityCategory.DIAGNOSTIC, @@ -140,7 +140,7 @@ SENSOR_TYPES = ( key="totver", name="Total use", icon="mdi:chart-donut", - native_unit_of_measurement=VOLUME_LITERS, + native_unit_of_measurement=UnitOfVolume.LITERS, device_class=SensorDeviceClass.VOLUME, state_class=SensorStateClass.TOTAL_INCREASING, entity_category=EntityCategory.DIAGNOSTIC, @@ -150,7 +150,7 @@ SENSOR_TYPES = ( key="reservoir_content_max", name="Max reservoir content", icon="mdi:waves", - native_unit_of_measurement=VOLUME_LITERS, + native_unit_of_measurement=UnitOfVolume.LITERS, device_class=SensorDeviceClass.VOLUME, state_class=SensorStateClass.TOTAL, entity_category=EntityCategory.DIAGNOSTIC, diff --git a/homeassistant/components/justnimbus/translations/ko.json b/homeassistant/components/justnimbus/translations/ko.json new file mode 100644 index 00000000000..eb41273be59 --- /dev/null +++ b/homeassistant/components/justnimbus/translations/ko.json @@ -0,0 +1,12 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/justnimbus/translations/pt.json b/homeassistant/components/justnimbus/translations/pt.json index 8df0a5b68e3..b5ee07080df 100644 --- a/homeassistant/components/justnimbus/translations/pt.json +++ b/homeassistant/components/justnimbus/translations/pt.json @@ -1,5 +1,13 @@ { "config": { + "abort": { + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" + }, + "error": { + "cannot_connect": "A liga\u00e7\u00e3o falhou", + "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", + "unknown": "Erro inesperado" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/kaiterra/sensor.py b/homeassistant/components/kaiterra/sensor.py index 29013052c1c..63a73a1157a 100644 --- a/homeassistant/components/kaiterra/sensor.py +++ b/homeassistant/components/kaiterra/sensor.py @@ -8,7 +8,7 @@ from homeassistant.components.sensor import ( SensorEntity, SensorEntityDescription, ) -from homeassistant.const import CONF_DEVICE_ID, CONF_NAME, TEMP_CELSIUS, TEMP_FAHRENHEIT +from homeassistant.const import CONF_DEVICE_ID, CONF_NAME, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -105,9 +105,9 @@ class KaiterraSensor(SensorEntity): value = self._sensor["units"].value if value == "F": - return TEMP_FAHRENHEIT + return UnitOfTemperature.FAHRENHEIT if value == "C": - return TEMP_CELSIUS + return UnitOfTemperature.CELSIUS return value async def async_added_to_hass(self) -> None: diff --git a/homeassistant/components/kaleidescape/translations/ko.json b/homeassistant/components/kaleidescape/translations/ko.json new file mode 100644 index 00000000000..19364e261e0 --- /dev/null +++ b/homeassistant/components/kaleidescape/translations/ko.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/kaleidescape/translations/pt.json b/homeassistant/components/kaleidescape/translations/pt.json index dee723be258..eff05001034 100644 --- a/homeassistant/components/kaleidescape/translations/pt.json +++ b/homeassistant/components/kaleidescape/translations/pt.json @@ -6,7 +6,7 @@ "step": { "user": { "data": { - "host": "Servidor" + "host": "Endere\u00e7o" } } } diff --git a/homeassistant/components/kaleidescape/translations/sk.json b/homeassistant/components/kaleidescape/translations/sk.json index 0dce503bffa..4f97778e11c 100644 --- a/homeassistant/components/kaleidescape/translations/sk.json +++ b/homeassistant/components/kaleidescape/translations/sk.json @@ -12,6 +12,9 @@ }, "flow_title": "{model} ({name})", "step": { + "discovery_confirm": { + "description": "Chcete nastavi\u0165 prehr\u00e1va\u010d {model} s n\u00e1zvom {name}?" + }, "user": { "data": { "host": "Hostite\u013e" diff --git a/homeassistant/components/keba/__init__.py b/homeassistant/components/keba/__init__.py index b957acd2a31..1c99a6500c5 100644 --- a/homeassistant/components/keba/__init__.py +++ b/homeassistant/components/keba/__init__.py @@ -235,7 +235,9 @@ class KebaHandler(KebaKeContact): self._set_fast_polling() except (KeyError, ValueError) as ex: _LOGGER.warning( - "Values are not correct for: failsafe_timeout, failsafe_fallback and/or " - "failsafe_persist: %s", + ( + "Values are not correct for: failsafe_timeout, failsafe_fallback" + " and/or failsafe_persist: %s" + ), ex, ) diff --git a/homeassistant/components/keba/sensor.py b/homeassistant/components/keba/sensor.py index d35c22905f1..635683419b2 100644 --- a/homeassistant/components/keba/sensor.py +++ b/homeassistant/components/keba/sensor.py @@ -7,11 +7,7 @@ from homeassistant.components.sensor import ( SensorEntityDescription, SensorStateClass, ) -from homeassistant.const import ( - ELECTRIC_CURRENT_AMPERE, - ENERGY_KILO_WATT_HOUR, - POWER_KILO_WATT, -) +from homeassistant.const import UnitOfElectricCurrent, UnitOfEnergy, UnitOfPower from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType @@ -38,7 +34,7 @@ async def async_setup_platform( SensorEntityDescription( key="Curr user", name="Max Current", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, device_class=SensorDeviceClass.CURRENT, ), ), @@ -48,7 +44,7 @@ async def async_setup_platform( SensorEntityDescription( key="Setenergy", name="Energy Target", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, ), ), @@ -58,7 +54,7 @@ async def async_setup_platform( SensorEntityDescription( key="P", name="Charging Power", - native_unit_of_measurement=POWER_KILO_WATT, + native_unit_of_measurement=UnitOfPower.KILO_WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), @@ -69,7 +65,7 @@ async def async_setup_platform( SensorEntityDescription( key="E pres", name="Session Energy", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, ), ), @@ -79,7 +75,7 @@ async def async_setup_platform( SensorEntityDescription( key="E total", name="Total Energy", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), diff --git a/homeassistant/components/keenetic_ndms2/translations/de.json b/homeassistant/components/keenetic_ndms2/translations/de.json index c165cbd9043..ec3aa86fb09 100644 --- a/homeassistant/components/keenetic_ndms2/translations/de.json +++ b/homeassistant/components/keenetic_ndms2/translations/de.json @@ -2,8 +2,8 @@ "config": { "abort": { "already_configured": "Konto wurde bereits konfiguriert", - "no_udn": "SSDP-Erkennungsinfo hat keine UDN", - "not_keenetic_ndms2": "Gefundenes Ger\u00e4t ist kein Keenetic-Router" + "no_udn": "SSDP Erkennungsinformation hat keine UDN", + "not_keenetic_ndms2": "Gefundenes Ger\u00e4t ist kein Keenetic Router" }, "error": { "cannot_connect": "Verbindung fehlgeschlagen" @@ -30,7 +30,7 @@ "include_associated": "WLAN-AP-Zuordnungsdaten verwenden (wird ignoriert, wenn Hotspot-Daten verwendet werden)", "interfaces": "Schnittstellen zum Scannen ausw\u00e4hlen", "scan_interval": "Scanintervall", - "try_hotspot": "'ip hotspot'-Daten verwenden (am genauesten)" + "try_hotspot": "'IP Hotspot' Daten verwenden (am genauesten)" } } } diff --git a/homeassistant/components/keenetic_ndms2/translations/pt.json b/homeassistant/components/keenetic_ndms2/translations/pt.json index df69b1c4ff3..7a1a1253720 100644 --- a/homeassistant/components/keenetic_ndms2/translations/pt.json +++ b/homeassistant/components/keenetic_ndms2/translations/pt.json @@ -1,12 +1,12 @@ { "config": { "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "user": { "data": { - "host": "Servidor", + "host": "Endere\u00e7o", "password": "Palavra-passe" } } diff --git a/homeassistant/components/keenetic_ndms2/translations/sk.json b/homeassistant/components/keenetic_ndms2/translations/sk.json index a53f9a1b785..eb8cc33bc5b 100644 --- a/homeassistant/components/keenetic_ndms2/translations/sk.json +++ b/homeassistant/components/keenetic_ndms2/translations/sk.json @@ -1,7 +1,9 @@ { "config": { "abort": { - "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", + "no_udn": "Inform\u00e1cie o zis\u0165ovan\u00ed SSDP nemaj\u00fa UDN", + "not_keenetic_ndms2": "Objaven\u00e1 polo\u017eka nie je router Keenetic" }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165" @@ -14,6 +16,21 @@ "password": "Heslo", "port": "Port", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" + }, + "title": "Nastavte router Keenetic NDMS2" + } + } + }, + "options": { + "step": { + "user": { + "data": { + "consider_home": "Zv\u00e1\u017ete intervaly v dom\u00e1cnosti", + "include_arp": "Pou\u017ei\u0165 \u00fadaje ARP (ignorovan\u00e9, ak sa pou\u017e\u00edvaj\u00fa \u00fadaje hotspotu)", + "include_associated": "Pou\u017ei\u0165 \u00fadaje o pridru\u017een\u00ed WiFi AP (ignorovan\u00e9, ak sa pou\u017e\u00edvaj\u00fa \u00fadaje hotspotu)", + "interfaces": "Vyberte rozhrania na skenovanie", + "scan_interval": "Interval skenovania", + "try_hotspot": "Pou\u017ei\u0165 \u00fadaje \u201eip hotspot\u201c (najpresnej\u0161ie)" } } } diff --git a/homeassistant/components/kegtron/sensor.py b/homeassistant/components/kegtron/sensor.py index b9386dd9bb4..c80788d2503 100644 --- a/homeassistant/components/kegtron/sensor.py +++ b/homeassistant/components/kegtron/sensor.py @@ -22,7 +22,7 @@ from homeassistant.components.sensor import ( SensorEntityDescription, SensorStateClass, ) -from homeassistant.const import SIGNAL_STRENGTH_DECIBELS_MILLIWATT, VOLUME_LITERS +from homeassistant.const import SIGNAL_STRENGTH_DECIBELS_MILLIWATT, UnitOfVolume from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -39,7 +39,7 @@ SENSOR_DESCRIPTIONS = { KegtronSensorDeviceClass.KEG_SIZE: SensorEntityDescription( key=KegtronSensorDeviceClass.KEG_SIZE, icon="mdi:keg", - native_unit_of_measurement=VOLUME_LITERS, + native_unit_of_measurement=UnitOfVolume.LITERS, device_class=SensorDeviceClass.VOLUME, state_class=SensorStateClass.MEASUREMENT, ), @@ -50,14 +50,14 @@ SENSOR_DESCRIPTIONS = { KegtronSensorDeviceClass.VOLUME_START: SensorEntityDescription( key=KegtronSensorDeviceClass.VOLUME_START, icon="mdi:keg", - native_unit_of_measurement=VOLUME_LITERS, + native_unit_of_measurement=UnitOfVolume.LITERS, device_class=SensorDeviceClass.VOLUME, state_class=SensorStateClass.MEASUREMENT, ), KegtronSensorDeviceClass.VOLUME_DISPENSED: SensorEntityDescription( key=KegtronSensorDeviceClass.VOLUME_DISPENSED, icon="mdi:keg", - native_unit_of_measurement=VOLUME_LITERS, + native_unit_of_measurement=UnitOfVolume.LITERS, device_class=SensorDeviceClass.VOLUME, state_class=SensorStateClass.TOTAL, ), diff --git a/homeassistant/components/kegtron/translations/en.json b/homeassistant/components/kegtron/translations/en.json index ebd9760c161..afe859ca766 100644 --- a/homeassistant/components/kegtron/translations/en.json +++ b/homeassistant/components/kegtron/translations/en.json @@ -9,13 +9,13 @@ "flow_title": "{name}", "step": { "bluetooth_confirm": { - "description": "Do you want to setup {name}?" + "description": "Do you want to set up {name}?" }, "user": { "data": { "address": "Device" }, - "description": "Choose a device to setup" + "description": "Choose a device to set up" } } } diff --git a/homeassistant/components/kegtron/translations/it.json b/homeassistant/components/kegtron/translations/it.json index 7784ed3a240..97113c57103 100644 --- a/homeassistant/components/kegtron/translations/it.json +++ b/homeassistant/components/kegtron/translations/it.json @@ -15,7 +15,7 @@ "data": { "address": "Dispositivo" }, - "description": "Seleziona un dispositivo da configurare" + "description": "Scegli un dispositivo da configurare" } } } diff --git a/homeassistant/components/kegtron/translations/ko.json b/homeassistant/components/kegtron/translations/ko.json new file mode 100644 index 00000000000..1a59c05b30c --- /dev/null +++ b/homeassistant/components/kegtron/translations/ko.json @@ -0,0 +1,9 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", + "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/kegtron/translations/no.json b/homeassistant/components/kegtron/translations/no.json index 0bf8b1695ec..38ab3d096f2 100644 --- a/homeassistant/components/kegtron/translations/no.json +++ b/homeassistant/components/kegtron/translations/no.json @@ -9,7 +9,7 @@ "flow_title": "{name}", "step": { "bluetooth_confirm": { - "description": "Vil du konfigurere {name}?" + "description": "Vil du sette opp {name} ?" }, "user": { "data": { diff --git a/homeassistant/components/kegtron/translations/pt.json b/homeassistant/components/kegtron/translations/pt.json new file mode 100644 index 00000000000..c6a15010072 --- /dev/null +++ b/homeassistant/components/kegtron/translations/pt.json @@ -0,0 +1,9 @@ +{ + "config": { + "abort": { + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", + "already_in_progress": "O processo de configura\u00e7\u00e3o j\u00e1 est\u00e1 a decorrer", + "no_devices_found": "Nenhum dispositivo encontrado na rede" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/keymitt_ble/strings.json b/homeassistant/components/keymitt_ble/strings.json index 3914a2f9a30..b1c7f7bb8dd 100644 --- a/homeassistant/components/keymitt_ble/strings.json +++ b/homeassistant/components/keymitt_ble/strings.json @@ -3,7 +3,7 @@ "flow_title": "{name}", "step": { "init": { - "title": "Setup MicroBot device", + "title": "Set up MicroBot device", "data": { "address": "Device address", "name": "Name" diff --git a/homeassistant/components/keymitt_ble/translations/en.json b/homeassistant/components/keymitt_ble/translations/en.json index ca5fa547770..0af1f6592aa 100644 --- a/homeassistant/components/keymitt_ble/translations/en.json +++ b/homeassistant/components/keymitt_ble/translations/en.json @@ -16,7 +16,7 @@ "address": "Device address", "name": "Name" }, - "title": "Setup MicroBot device" + "title": "Set up MicroBot device" }, "link": { "description": "Press the button on the MicroBot Push when the LED is solid pink or green to register with Home Assistant.", diff --git a/homeassistant/components/keymitt_ble/translations/ko.json b/homeassistant/components/keymitt_ble/translations/ko.json new file mode 100644 index 00000000000..29b8f09ee53 --- /dev/null +++ b/homeassistant/components/keymitt_ble/translations/ko.json @@ -0,0 +1,9 @@ +{ + "config": { + "abort": { + "already_configured_device": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/keymitt_ble/translations/pt-BR.json b/homeassistant/components/keymitt_ble/translations/pt-BR.json index a04c7d7f90f..7d9093eefb3 100644 --- a/homeassistant/components/keymitt_ble/translations/pt-BR.json +++ b/homeassistant/components/keymitt_ble/translations/pt-BR.json @@ -16,7 +16,7 @@ "address": "Endere\u00e7o do dispositivo", "name": "Nome" }, - "title": "Configurar dispositivo MicroBot" + "title": "Configurar o dispositivo MicroBot" }, "link": { "description": "Pressione o bot\u00e3o no MicroBot Push quando o LED estiver rosa ou verde s\u00f3lido para se registrar no Home Assistant.", diff --git a/homeassistant/components/keymitt_ble/translations/pt.json b/homeassistant/components/keymitt_ble/translations/pt.json new file mode 100644 index 00000000000..af66b56ddba --- /dev/null +++ b/homeassistant/components/keymitt_ble/translations/pt.json @@ -0,0 +1,9 @@ +{ + "config": { + "abort": { + "already_configured_device": "O dispositivo j\u00e1 est\u00e1 configurado", + "cannot_connect": "A liga\u00e7\u00e3o falhou", + "unknown": "Erro inesperado" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/keymitt_ble/translations/sk.json b/homeassistant/components/keymitt_ble/translations/sk.json index f9c4614994b..546d33ab495 100644 --- a/homeassistant/components/keymitt_ble/translations/sk.json +++ b/homeassistant/components/keymitt_ble/translations/sk.json @@ -15,9 +15,11 @@ "data": { "address": "Adresa zariadenia", "name": "N\u00e1zov" - } + }, + "title": "Nastavte zariadenie MicroBot" }, "link": { + "description": "Stla\u010dte tla\u010didlo na MicroBot Push, ke\u010f LED svieti ru\u017eovo alebo nazeleno, aby ste sa zaregistrovali do Home Assistant.", "title": "P\u00e1rovanie" } } diff --git a/homeassistant/components/kmtronic/translations/de.json b/homeassistant/components/kmtronic/translations/de.json index 336348274ac..82ae794a6a9 100644 --- a/homeassistant/components/kmtronic/translations/de.json +++ b/homeassistant/components/kmtronic/translations/de.json @@ -22,7 +22,7 @@ "step": { "init": { "data": { - "reverse": "Umgekehrte Schalterlogik (NC verwenden)" + "reverse": "Umgekehrte Schaltlogik (NC verwenden)" } } } diff --git a/homeassistant/components/kmtronic/translations/pt.json b/homeassistant/components/kmtronic/translations/pt.json index 6ff15c6c8d7..72aede2a15a 100644 --- a/homeassistant/components/kmtronic/translations/pt.json +++ b/homeassistant/components/kmtronic/translations/pt.json @@ -4,14 +4,14 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, "step": { "user": { "data": { - "host": "Servidor", + "host": "Endere\u00e7o", "password": "Palavra-passe", "username": "Nome de Utilizador" } diff --git a/homeassistant/components/kmtronic/translations/sk.json b/homeassistant/components/kmtronic/translations/sk.json index 666f6e28840..c1459f4e7cc 100644 --- a/homeassistant/components/kmtronic/translations/sk.json +++ b/homeassistant/components/kmtronic/translations/sk.json @@ -17,5 +17,14 @@ } } } + }, + "options": { + "step": { + "init": { + "data": { + "reverse": "Logika sp\u00e4tn\u00e9ho sp\u00edna\u010da (pou\u017eite NC)" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/knx/__init__.py b/homeassistant/components/knx/__init__.py index 5d2ab031323..0159f6f1cac 100644 --- a/homeassistant/components/knx/__init__.py +++ b/homeassistant/components/knx/__init__.py @@ -495,7 +495,10 @@ class KNXModule: value = transcoder.from_knx(data) except ConversionError as err: _LOGGER.warning( - "Error in `knx_event` at decoding type '%s' from telegram %s\n%s", + ( + "Error in `knx_event` at decoding type '%s' from" + " telegram %s\n%s" + ), transcoder.__name__, telegram, err, @@ -584,7 +587,10 @@ class KNXModule: if group_address in self.service_exposures: replaced_exposure = self.service_exposures.pop(group_address) _LOGGER.warning( - "Service exposure_register replacing already registered exposure for '%s' - %s", + ( + "Service exposure_register replacing already registered exposure" + " for '%s' - %s" + ), group_address, replaced_exposure.device.name, ) diff --git a/homeassistant/components/knx/climate.py b/homeassistant/components/knx/climate.py index bb2fe7dfbcc..72039e1300f 100644 --- a/homeassistant/components/knx/climate.py +++ b/homeassistant/components/knx/climate.py @@ -19,8 +19,8 @@ from homeassistant.const import ( ATTR_TEMPERATURE, CONF_ENTITY_CATEGORY, CONF_NAME, - TEMP_CELSIUS, Platform, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -133,7 +133,7 @@ class KNXClimate(KnxEntity, ClimateEntity): """Representation of a KNX climate device.""" _device: XknxClimate - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS def __init__(self, xknx: XKNX, config: ConfigType) -> None: """Initialize of a KNX climate device.""" diff --git a/homeassistant/components/knx/config_flow.py b/homeassistant/components/knx/config_flow.py index 6e0c357b5fa..b03a59b2d4e 100644 --- a/homeassistant/components/knx/config_flow.py +++ b/homeassistant/components/knx/config_flow.py @@ -11,7 +11,7 @@ from xknx.exceptions.exception import CommunicationError, InvalidSecureConfigura from xknx.io import DEFAULT_MCAST_GRP, DEFAULT_MCAST_PORT from xknx.io.gateway_scanner import GatewayDescriptor, GatewayScanner from xknx.io.self_description import request_description -from xknx.secure import load_keyring +from xknx.secure.keyring import XMLInterface, load_keyring from homeassistant.config_entries import ConfigEntry, ConfigFlow, OptionsFlow from homeassistant.const import CONF_HOST, CONF_PORT @@ -96,6 +96,7 @@ class KNXCommonFlow(ABC, FlowHandler): self._found_gateways: list[GatewayDescriptor] = [] self._found_tunnels: list[GatewayDescriptor] = [] self._selected_tunnel: GatewayDescriptor | None = None + self._tunnel_endpoints: list[XMLInterface] = [] self._gatewayscanner: GatewayScanner | None = None self._async_scan_gen: AsyncGenerator[GatewayDescriptor, None] | None = None @@ -399,7 +400,10 @@ class KNXCommonFlow(ABC, FlowHandler): ], ) return self.finish_flow( - title=f"Secure Routing as {self.new_entry_data[CONF_KNX_INDIVIDUAL_ADDRESS]}" + title=( + "Secure Routing as" + f" {self.new_entry_data[CONF_KNX_INDIVIDUAL_ADDRESS]}" + ) ) fields = { @@ -437,18 +441,36 @@ class KNXCommonFlow(ABC, FlowHandler): ) -> FlowResult: """Configure secure knxkeys used to authenticate.""" errors = {} + description_placeholders = {} if user_input is not None: + connection_type = self.new_entry_data[CONF_KNX_CONNECTION_TYPE] storage_key = CONST_KNX_STORAGE_KEY + user_input[CONF_KNX_KNXKEY_FILENAME] try: - await load_keyring( + keyring = await load_keyring( path=self.hass.config.path(STORAGE_DIR, storage_key), password=user_input[CONF_KNX_KNXKEY_PASSWORD], ) except FileNotFoundError: - errors[CONF_KNX_KNXKEY_FILENAME] = "file_not_found" + errors[CONF_KNX_KNXKEY_FILENAME] = "keyfile_not_found" except InvalidSecureConfiguration: - errors[CONF_KNX_KNXKEY_PASSWORD] = "invalid_signature" + errors[CONF_KNX_KNXKEY_PASSWORD] = "keyfile_invalid_signature" + else: + if ( + connection_type == CONF_KNX_TUNNELING_TCP_SECURE + and self._selected_tunnel is not None + ): + if host_ia := self._selected_tunnel.individual_address: + self._tunnel_endpoints = keyring.get_tunnel_interfaces_by_host( + host=host_ia + ) + if not self._tunnel_endpoints: + errors["base"] = "keyfile_no_tunnel_for_host" + description_placeholders = {CONF_HOST: str(host_ia)} + + if connection_type == CONF_KNX_ROUTING_SECURE: + if not (keyring.backbone is not None and keyring.backbone.key): + errors["base"] = "keyfile_no_backbone_key" if not errors: self.new_entry_data |= KNXConfigEntryData( @@ -460,14 +482,14 @@ class KNXCommonFlow(ABC, FlowHandler): user_id=None, user_password=None, ) - if ( - self.new_entry_data[CONF_KNX_CONNECTION_TYPE] - == CONF_KNX_ROUTING_SECURE - ): - title = f"Secure Routing as {self.new_entry_data[CONF_KNX_INDIVIDUAL_ADDRESS]}" - else: - title = f"Secure Tunneling @ {self.new_entry_data[CONF_HOST]}" - return self.finish_flow(title=title) + if connection_type == CONF_KNX_ROUTING_SECURE: + return self.finish_flow( + title=( + "Secure Routing as" + f" {self.new_entry_data[CONF_KNX_INDIVIDUAL_ADDRESS]}" + ) + ) + return await self.async_step_knxkeys_tunnel_select() if _default_filename := self.initial_data.get(CONF_KNX_KNXKEY_FILENAME): _default_filename = _default_filename.lstrip(CONST_KNX_STORAGE_KEY) @@ -482,7 +504,52 @@ class KNXCommonFlow(ABC, FlowHandler): } return self.async_show_form( - step_id="secure_knxkeys", data_schema=vol.Schema(fields), errors=errors + step_id="secure_knxkeys", + data_schema=vol.Schema(fields), + errors=errors, + description_placeholders=description_placeholders, + ) + + async def async_step_knxkeys_tunnel_select( + self, user_input: dict | None = None + ) -> FlowResult: + """Select if a specific tunnel should be used from knxkeys file.""" + if user_input is not None: + if user_input[CONF_KNX_SECURE_USER_ID] == CONF_KNX_AUTOMATIC: + selected_user_id = None + else: + selected_user_id = int(user_input[CONF_KNX_SECURE_USER_ID]) + self.new_entry_data |= KNXConfigEntryData(user_id=selected_user_id) + return self.finish_flow( + title=f"Secure Tunneling @ {self.new_entry_data[CONF_HOST]}" + ) + + tunnel_endpoint_options = [ + selector.SelectOptionDict( + value=CONF_KNX_AUTOMATIC, label=CONF_KNX_AUTOMATIC.capitalize() + ) + ] + for endpoint in self._tunnel_endpoints: + tunnel_endpoint_options.append( + selector.SelectOptionDict( + value=str(endpoint.user_id), + label=f"{endpoint.individual_address} (User ID: {endpoint.user_id})", + ) + ) + return self.async_show_form( + step_id="knxkeys_tunnel_select", + data_schema=vol.Schema( + { + vol.Required( + CONF_KNX_SECURE_USER_ID, default=CONF_KNX_AUTOMATIC + ): selector.SelectSelector( + selector.SelectSelectorConfig( + options=tunnel_endpoint_options, + mode=selector.SelectSelectorMode.LIST, + ) + ), + } + ), ) async def async_step_routing(self, user_input: dict | None = None) -> FlowResult: diff --git a/homeassistant/components/knx/expose.py b/homeassistant/components/knx/expose.py index 0963ec3be43..05e367faeec 100644 --- a/homeassistant/components/knx/expose.py +++ b/homeassistant/components/knx/expose.py @@ -21,7 +21,7 @@ from homeassistant.core import Event, HomeAssistant, State, callback from homeassistant.helpers.event import async_track_state_change_event from homeassistant.helpers.typing import ConfigType, StateType -from .const import KNX_ADDRESS +from .const import CONF_RESPOND_TO_READ, KNX_ADDRESS from .schema import ExposeSchema _LOGGER = logging.getLogger(__name__) @@ -32,27 +32,23 @@ def create_knx_exposure( hass: HomeAssistant, xknx: XKNX, config: ConfigType ) -> KNXExposeSensor | KNXExposeTime: """Create exposures from config.""" - address = config[KNX_ADDRESS] + expose_type = config[ExposeSchema.CONF_KNX_EXPOSE_TYPE] - attribute = config.get(ExposeSchema.CONF_KNX_EXPOSE_ATTRIBUTE) - default = config.get(ExposeSchema.CONF_KNX_EXPOSE_DEFAULT) exposure: KNXExposeSensor | KNXExposeTime if ( isinstance(expose_type, str) and expose_type.lower() in ExposeSchema.EXPOSE_TIME_TYPES ): - exposure = KNXExposeTime(xknx, expose_type, address) + exposure = KNXExposeTime( + xknx=xknx, + config=config, + ) else: - entity_id = config[CONF_ENTITY_ID] exposure = KNXExposeSensor( hass, - xknx, - expose_type, - entity_id, - attribute, - default, - address, + xknx=xknx, + config=config, ) return exposure @@ -64,36 +60,37 @@ class KNXExposeSensor: self, hass: HomeAssistant, xknx: XKNX, - expose_type: int | str, - entity_id: str, - attribute: str | None, - default: StateType, - address: str, + config: ConfigType, ) -> None: """Initialize of Expose class.""" self.hass = hass self.xknx = xknx - self.type = expose_type - self.entity_id = entity_id - self.expose_attribute = attribute - self.expose_default = default - self.address = address + + self.entity_id: str = config[CONF_ENTITY_ID] + self.expose_attribute: str | None = config.get( + ExposeSchema.CONF_KNX_EXPOSE_ATTRIBUTE + ) + self.expose_default = config.get(ExposeSchema.CONF_KNX_EXPOSE_DEFAULT) + self.expose_type: int | str = config[ExposeSchema.CONF_KNX_EXPOSE_TYPE] + self._remove_listener: Callable[[], None] | None = None - self.device: ExposeSensor = self.async_register() + self.device: ExposeSensor = self.async_register(config) self._init_expose_state() @callback - def async_register(self) -> ExposeSensor: + def async_register(self, config: ConfigType) -> ExposeSensor: """Register listener.""" if self.expose_attribute is not None: _name = self.entity_id + "__" + self.expose_attribute else: _name = self.entity_id device = ExposeSensor( - self.xknx, + xknx=self.xknx, name=_name, - group_address=self.address, - value_type=self.type, + group_address=config[KNX_ADDRESS], + respond_to_read=config[CONF_RESPOND_TO_READ], + value_type=self.expose_type, + cooldown=config[ExposeSchema.CONF_KNX_EXPOSE_COOLDOWN], ) self._remove_listener = async_track_state_change_event( self.hass, [self.entity_id], self._async_entity_changed @@ -118,7 +115,7 @@ class KNXExposeSensor: self._remove_listener = None self.device.shutdown() - def _get_expose_value(self, state: State | None) -> StateType: + def _get_expose_value(self, state: State | None) -> bool | int | float | str | None: """Extract value from state.""" if state is None or state.state in (STATE_UNKNOWN, STATE_UNAVAILABLE): value = self.expose_default @@ -128,7 +125,7 @@ class KNXExposeSensor: if self.expose_attribute is None else state.attributes.get(self.expose_attribute, self.expose_default) ) - if self.type == "binary": + if self.expose_type == "binary": if value in (1, STATE_ON, "True"): return True if value in (0, STATE_OFF, "False"): @@ -171,22 +168,21 @@ class KNXExposeSensor: class KNXExposeTime: """Object to Expose Time/Date object to KNX bus.""" - def __init__(self, xknx: XKNX, expose_type: str, address: str) -> None: + def __init__(self, xknx: XKNX, config: ConfigType) -> None: """Initialize of Expose class.""" self.xknx = xknx - self.expose_type = expose_type - self.address = address - self.device: DateTime = self.async_register() + self.device: DateTime = self.async_register(config) @callback - def async_register(self) -> DateTime: + def async_register(self, config: ConfigType) -> DateTime: """Register listener.""" + expose_type = config[ExposeSchema.CONF_KNX_EXPOSE_TYPE] return DateTime( self.xknx, - name=self.expose_type.capitalize(), - broadcast_type=self.expose_type.upper(), + name=expose_type.capitalize(), + broadcast_type=expose_type.upper(), localtime=True, - group_address=self.address, + group_address=config[KNX_ADDRESS], ) @callback diff --git a/homeassistant/components/knx/manifest.json b/homeassistant/components/knx/manifest.json index 4bf1b13672b..60feb3c7419 100644 --- a/homeassistant/components/knx/manifest.json +++ b/homeassistant/components/knx/manifest.json @@ -3,7 +3,7 @@ "name": "KNX", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/knx", - "requirements": ["xknx==2.1.0"], + "requirements": ["xknx==2.2.0"], "codeowners": ["@Julius2342", "@farmio", "@marvin-w"], "quality_scale": "platinum", "iot_class": "local_push", diff --git a/homeassistant/components/knx/schema.py b/homeassistant/components/knx/schema.py index 87a7b6fdab5..b1ae55ba9dc 100644 --- a/homeassistant/components/knx/schema.py +++ b/homeassistant/components/knx/schema.py @@ -74,7 +74,8 @@ def dpt_subclass_validator(dpt_base_class: type[DPTBase]) -> Callable[[Any], str ): return value raise vol.Invalid( - f"type '{value}' is not a valid DPT identifier for {dpt_base_class.__name__}." + f"type '{value}' is not a valid DPT identifier for" + f" {dpt_base_class.__name__}." ) return dpt_value_validator @@ -94,8 +95,9 @@ def ga_validator(value: Any) -> str | int: except CouldNotParseAddress: pass raise vol.Invalid( - f"value '{value}' is not a valid KNX group address '
//
', '
/' " - "or '' (eg.'1/2/3', '9/234', '123'), nor xknx internal address 'i-'." + f"value '{value}' is not a valid KNX group address '
//'," + " '
/' or '' (eg.'1/2/3', '9/234', '123'), nor xknx internal" + " address 'i-'." ) @@ -104,7 +106,10 @@ ga_list_validator = vol.All(cv.ensure_list, [ga_validator]) ia_validator = vol.Any( vol.All(str, str.strip, cv.matches_regex(IndividualAddress.ADDRESS_RE.pattern)), vol.All(vol.Coerce(int), vol.Range(min=1, max=65535)), - msg="value does not match pattern for KNX individual address '..' (eg.'1.1.100')", + msg=( + "value does not match pattern for KNX individual address" + " '..' (eg.'1.1.100')" + ), ) @@ -120,7 +125,8 @@ def ip_v4_validator(value: Any, multicast: bool | None = None) -> str: raise vol.Invalid(f"value '{value}' is not a valid IPv4 address: {ex}") from ex if multicast is not None and address.is_multicast != multicast: raise vol.Invalid( - f"value '{value}' is not a valid IPv4 {'multicast' if multicast else 'unicast'} address" + f"value '{value}' is not a valid IPv4" + f" {'multicast' if multicast else 'unicast'} address" ) return str(address) @@ -433,14 +439,18 @@ class ClimateSchema(KNXPlatformSchema): vol.Inclusive( CONF_SETPOINT_SHIFT_ADDRESS, "setpoint_shift", - msg="'setpoint_shift_address' and 'setpoint_shift_state_address' " - "are required for setpoint_shift configuration", + msg=( + "'setpoint_shift_address' and 'setpoint_shift_state_address' " + "are required for setpoint_shift configuration" + ), ): ga_list_validator, vol.Inclusive( CONF_SETPOINT_SHIFT_STATE_ADDRESS, "setpoint_shift", - msg="'setpoint_shift_address' and 'setpoint_shift_state_address' " - "are required for setpoint_shift configuration", + msg=( + "'setpoint_shift_address' and 'setpoint_shift_state_address' " + "are required for setpoint_shift configuration" + ), ): ga_list_validator, vol.Optional(CONF_SETPOINT_SHIFT_MODE): vol.Maybe( vol.All(vol.Upper, cv.enum(SetpointShiftMode)) @@ -509,7 +519,10 @@ class CoverSchema(KNXPlatformSchema): { vol.Required( vol.Any(CONF_MOVE_LONG_ADDRESS, CONF_POSITION_ADDRESS), - msg=f"At least one of '{CONF_MOVE_LONG_ADDRESS}' or '{CONF_POSITION_ADDRESS}' is required.", + msg=( + f"At least one of '{CONF_MOVE_LONG_ADDRESS}' or" + f" '{CONF_POSITION_ADDRESS}' is required." + ), ): object, }, extra=vol.ALLOW_EXTRA, @@ -548,6 +561,7 @@ class ExposeSchema(KNXPlatformSchema): CONF_KNX_EXPOSE_TYPE = CONF_TYPE CONF_KNX_EXPOSE_ATTRIBUTE = "attribute" CONF_KNX_EXPOSE_BINARY = "binary" + CONF_KNX_EXPOSE_COOLDOWN = "cooldown" CONF_KNX_EXPOSE_DEFAULT = "default" EXPOSE_TIME_TYPES: Final = [ "time", @@ -565,6 +579,8 @@ class ExposeSchema(KNXPlatformSchema): ) EXPOSE_SENSOR_SCHEMA = vol.Schema( { + vol.Optional(CONF_KNX_EXPOSE_COOLDOWN, default=0): cv.positive_float, + vol.Optional(CONF_RESPOND_TO_READ, default=True): cv.boolean, vol.Required(CONF_KNX_EXPOSE_TYPE): vol.Any( CONF_KNX_EXPOSE_BINARY, sensor_type_validator ), @@ -669,17 +685,26 @@ class LightSchema(KNXPlatformSchema): vol.Inclusive( CONF_RED, "individual_colors", - msg="'red', 'green' and 'blue' are required for individual colors configuration", + msg=( + "'red', 'green' and 'blue' are required for individual" + " colors configuration" + ), ): INDIVIDUAL_COLOR_SCHEMA, vol.Inclusive( CONF_GREEN, "individual_colors", - msg="'red', 'green' and 'blue' are required for individual colors configuration", + msg=( + "'red', 'green' and 'blue' are required for individual" + " colors configuration" + ), ): INDIVIDUAL_COLOR_SCHEMA, vol.Inclusive( CONF_BLUE, "individual_colors", - msg="'red', 'green' and 'blue' are required for individual colors configuration", + msg=( + "'red', 'green' and 'blue' are required for individual" + " colors configuration" + ), ): INDIVIDUAL_COLOR_SCHEMA, vol.Optional(CONF_WHITE): INDIVIDUAL_COLOR_SCHEMA, }, diff --git a/homeassistant/components/knx/strings.json b/homeassistant/components/knx/strings.json index 632af9961dc..d1ac3793c05 100644 --- a/homeassistant/components/knx/strings.json +++ b/homeassistant/components/knx/strings.json @@ -2,18 +2,21 @@ "config": { "step": { "connection_type": { + "title": "KNX connection", "description": "Please enter the connection type we should use for your KNX connection. \n AUTOMATIC - The integration takes care of the connectivity to your KNX Bus by performing a gateway scan. \n TUNNELING - The integration will connect to your KNX bus via tunneling. \n ROUTING - The integration will connect to your KNX bus via routing.", "data": { "connection_type": "KNX Connection Type" } }, "tunnel": { + "title": "Tunnel", "description": "Please select a gateway from the list.", "data": { "gateway": "KNX Tunnel Connection" } }, "manual_tunnel": { + "title": "Tunnel settings", "description": "Please enter the connection information of your tunneling device.", "data": { "tunneling_type": "KNX Tunneling Type", @@ -30,6 +33,7 @@ } }, "secure_key_source": { + "title": "KNX IP-Secure", "description": "Select how you want to configure KNX/IP Secure.", "menu_options": { "secure_knxkeys": "Use a `.knxkeys` file containing IP secure keys", @@ -38,6 +42,7 @@ } }, "secure_knxkeys": { + "title": "Keyfile", "description": "Please enter the information for your `.knxkeys` file.", "data": { "knxkeys_filename": "The filename of your `.knxkeys` file (including extension)", @@ -48,7 +53,15 @@ "knxkeys_password": "This was set when exporting the file from ETS." } }, + "knxkeys_tunnel_select": { + "title": "Tunnel endpoint", + "description": "Select the tunnel used for connection.", + "data": { + "user_id": "`Automatic` will use the first free tunnel endpoint." + } + }, "secure_tunnel_manual": { + "title": "Secure tunnelling", "description": "Please enter your IP secure information.", "data": { "user_id": "User ID", @@ -62,6 +75,7 @@ } }, "secure_routing_manual": { + "title": "Secure routing", "description": "Please enter your IP secure information.", "data": { "backbone_key": "Backbone key", @@ -73,6 +87,7 @@ } }, "routing": { + "title": "Routing", "description": "Please configure the routing options.", "data": { "individual_address": "Individual address", @@ -96,8 +111,10 @@ "invalid_backbone_key": "Invalid backbone key. 32 hexadecimal numbers expected.", "invalid_individual_address": "Value does not match pattern for KNX individual address.\n'area.line.device'", "invalid_ip_address": "Invalid IPv4 address.", - "invalid_signature": "The password to decrypt the `.knxkeys` file is wrong.", - "file_not_found": "The specified `.knxkeys` file was not found in the path config/.storage/knx/", + "keyfile_invalid_signature": "The password to decrypt the `.knxkeys` file is wrong.", + "keyfile_no_backbone_key": "The `.knxkeys` file does not contain a backbone key for secure routing.", + "keyfile_no_tunnel_for_host": "The `.knxkeys` file does not contain credentials for host `{host}`.", + "keyfile_not_found": "The specified `.knxkeys` file was not found in the path config/.storage/knx/", "no_router_discovered": "No KNXnet/IP router was discovered on the network.", "no_tunnel_discovered": "Could not find a KNX tunneling server on your network.", "unsupported_tunnel_type": "Selected tunnelling type not supported by gateway." @@ -106,12 +123,14 @@ "options": { "step": { "options_init": { + "title": "KNX Settings", "menu_options": { "connection_type": "Configure KNX interface", "communication_settings": "Communication settings" } }, "communication_settings": { + "title": "Communication settings", "data": { "state_updater": "State updater", "rate_limit": "Rate limit" @@ -122,18 +141,21 @@ } }, "connection_type": { + "title": "[%key:component::knx::config::step::connection_type::title%]", "description": "Please enter the connection type we should use for your KNX connection. \n AUTOMATIC - The integration takes care of the connectivity to your KNX Bus by performing a gateway scan. \n TUNNELING - The integration will connect to your KNX bus via tunneling. \n ROUTING - The integration will connect to your KNX bus via routing.", "data": { "connection_type": "KNX Connection Type" } }, "tunnel": { + "title": "[%key:component::knx::config::step::tunnel::title%]", "description": "[%key:component::knx::config::step::tunnel::description%]", "data": { "gateway": "[%key:component::knx::config::step::tunnel::data::gateway%]" } }, "manual_tunnel": { + "title": "[%key:component::knx::config::step::manual_tunnel::title%]", "description": "[%key:component::knx::config::step::manual_tunnel::description%]", "data": { "tunneling_type": "[%key:component::knx::config::step::manual_tunnel::data::tunneling_type%]", @@ -150,6 +172,7 @@ } }, "secure_key_source": { + "title": "[%key:component::knx::config::step::secure_key_source::title%]", "description": "[%key:component::knx::config::step::secure_key_source::description%]", "menu_options": { "secure_knxkeys": "[%key:component::knx::config::step::secure_key_source::menu_options::secure_knxkeys%]", @@ -158,6 +181,7 @@ } }, "secure_knxkeys": { + "title": "[%key:component::knx::config::step::secure_knxkeys::title%]", "description": "[%key:component::knx::config::step::secure_knxkeys::description%]", "data": { "knxkeys_filename": "[%key:component::knx::config::step::secure_knxkeys::data::knxkeys_filename%]", @@ -168,7 +192,15 @@ "knxkeys_password": "[%key:component::knx::config::step::secure_knxkeys::data_description::knxkeys_password%]" } }, + "knxkeys_tunnel_select": { + "title": "[%key:component::knx::config::step::knxkeys_tunnel_select::title%]", + "description": "[%key:component::knx::config::step::knxkeys_tunnel_select::description%]", + "data": { + "user_id": "[%key:component::knx::config::step::knxkeys_tunnel_select::data::user_id%]" + } + }, "secure_tunnel_manual": { + "title": "[%key:component::knx::config::step::secure_tunnel_manual::title%]", "description": "[%key:component::knx::config::step::secure_tunnel_manual::description%]", "data": { "user_id": "[%key:component::knx::config::step::secure_tunnel_manual::data::user_id%]", @@ -182,6 +214,7 @@ } }, "secure_routing_manual": { + "title": "[%key:component::knx::config::step::secure_routing_manual::title%]", "description": "[%key:component::knx::config::step::secure_routing_manual::description%]", "data": { "backbone_key": "[%key:component::knx::config::step::secure_routing_manual::data::backbone_key%]", @@ -193,6 +226,7 @@ } }, "routing": { + "title": "[%key:component::knx::config::step::routing::title%]", "description": "[%key:component::knx::config::step::routing::description%]", "data": { "individual_address": "[%key:component::knx::config::step::routing::data::individual_address%]", @@ -212,8 +246,10 @@ "invalid_backbone_key": "[%key:component::knx::config::error::invalid_backbone_key%]", "invalid_individual_address": "[%key:component::knx::config::error::invalid_individual_address%]", "invalid_ip_address": "[%key:component::knx::config::error::invalid_ip_address%]", - "invalid_signature": "[%key:component::knx::config::error::invalid_signature%]", - "file_not_found": "[%key:component::knx::config::error::file_not_found%]", + "keyfile_no_backbone_key": "[%key:component::knx::config::error::keyfile_no_backbone_key%]", + "keyfile_invalid_signature": "[%key:component::knx::config::error::keyfile_invalid_signature%]", + "keyfile_no_tunnel_for_host": "[%key:component::knx::config::error::keyfile_no_tunnel_for_host%]", + "keyfile_not_found": "[%key:component::knx::config::error::keyfile_not_found%]", "no_router_discovered": "[%key:component::knx::config::error::no_router_discovered%]", "no_tunnel_discovered": "[%key:component::knx::config::error::no_tunnel_discovered%]", "unsupported_tunnel_type": "[%key:component::knx::config::error::unsupported_tunnel_type%]" diff --git a/homeassistant/components/knx/translations/bg.json b/homeassistant/components/knx/translations/bg.json index af87f7d99ba..eadda227e1d 100644 --- a/homeassistant/components/knx/translations/bg.json +++ b/homeassistant/components/knx/translations/bg.json @@ -6,7 +6,8 @@ }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", - "invalid_ip_address": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u0435\u043d IPv4 \u0430\u0434\u0440\u0435\u0441." + "invalid_ip_address": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u0435\u043d IPv4 \u0430\u0434\u0440\u0435\u0441.", + "unsupported_tunnel_type": "\u0418\u0437\u0431\u0440\u0430\u043d\u0438\u044f\u0442 \u0442\u0438\u043f \u0442\u0443\u043d\u0435\u043b\u0438\u0440\u0430\u043d\u0435 \u043d\u0435 \u0441\u0435 \u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430 \u043e\u0442 \u0448\u043b\u044e\u0437\u0430." }, "step": { "manual_tunnel": { @@ -44,7 +45,8 @@ "options": { "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", - "invalid_ip_address": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u0435\u043d IPv4 \u0430\u0434\u0440\u0435\u0441." + "invalid_ip_address": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u0435\u043d IPv4 \u0430\u0434\u0440\u0435\u0441.", + "unsupported_tunnel_type": "\u0418\u0437\u0431\u0440\u0430\u043d\u0438\u044f\u0442 \u0442\u0438\u043f \u0442\u0443\u043d\u0435\u043b\u0438\u0440\u0430\u043d\u0435 \u043d\u0435 \u0441\u0435 \u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430 \u043e\u0442 \u0448\u043b\u044e\u0437\u0430." }, "step": { "manual_tunnel": { diff --git a/homeassistant/components/knx/translations/ca.json b/homeassistant/components/knx/translations/ca.json index ebd50a5810f..ff039591f21 100644 --- a/homeassistant/components/knx/translations/ca.json +++ b/homeassistant/components/knx/translations/ca.json @@ -11,15 +11,21 @@ "invalid_individual_address": "El valor no coincideix amb el patr\u00f3 d'adre\u00e7a KNX individual.\n'area.line.device'", "invalid_ip_address": "Adre\u00e7a IPv4 inv\u00e0lida.", "invalid_signature": "La contrasenya per desxifrar el fitxer `.knxkeys` \u00e9s incorrecta.", + "keyfile_invalid_signature": "La contrasenya per desxifrar el fitxer `.knxkeys` \u00e9s incorrecta.", + "keyfile_no_backbone_key": "El fitxer `.knxkeys` no cont\u00e9 una clau de 'backbone' per a l'encaminament segur.", + "keyfile_no_tunnel_for_host": "El fitxer `.knxkeys` no cont\u00e9 credencials per a l'amfitri\u00f3 `{host}`.", + "keyfile_not_found": "No s'ha trobat el fitxer `.knxkeys` especificat a la ruta config/.storage/knx/", "no_router_discovered": "No s'ha descobert cap encaminador ('router') KNXnet/IP a la xarxa.", - "no_tunnel_discovered": "No s'ha trobat cap servidor de tunelitzaci\u00f3 KNX a la xarxa." + "no_tunnel_discovered": "No s'ha trobat cap servidor de tunelitzaci\u00f3 KNX a la xarxa.", + "unsupported_tunnel_type": "El tipus de t\u00fanel seleccionat no \u00e9s compatible amb la passarel\u00b7la." }, "step": { "connection_type": { "data": { "connection_type": "Tipus de connexi\u00f3 KNX" }, - "description": "Introdueix el tipus de connexi\u00f3 a utilitzar per a la connexi\u00f3 KNX.\n AUTOM\u00c0TICA: la integraci\u00f3 s'encarrega de la connectivitat al bus KNX realitzant una exploraci\u00f3 de la passarel\u00b7la.\n T\u00daNEL: la integraci\u00f3 es connectar\u00e0 al bus KNX mitjan\u00e7ant un t\u00fanel.\n ENCAMINAMENT: la integraci\u00f3 es connectar\u00e0 al bus KNX mitjan\u00e7ant l'encaminament." + "description": "Introdueix el tipus de connexi\u00f3 a utilitzar per a la connexi\u00f3 KNX.\n AUTOM\u00c0TICA: la integraci\u00f3 s'encarrega de la connectivitat al bus KNX realitzant una exploraci\u00f3 de la passarel\u00b7la.\n T\u00daNEL: la integraci\u00f3 es connectar\u00e0 al bus KNX mitjan\u00e7ant un t\u00fanel.\n ENCAMINAMENT: la integraci\u00f3 es connectar\u00e0 al bus KNX mitjan\u00e7ant l'encaminament.", + "title": "Connexi\u00f3 KNX" }, "manual_tunnel": { "data": { @@ -35,7 +41,8 @@ "port": "Port del dispositiu de tunelitzaci\u00f3 KNX/IP.", "route_back": "Activa-ho si el teun servidor de tunelitzaci\u00f3 KNXnet/IP est\u00e0 darrere una NAT. Nom\u00e9s s'aplica a connexions UDP." }, - "description": "Introdueix la informaci\u00f3 de connexi\u00f3 del dispositiu de t\u00fanel." + "description": "Introdueix la informaci\u00f3 de connexi\u00f3 del dispositiu de t\u00fanel.", + "title": "Configuraci\u00f3 del t\u00fanel" }, "routing": { "data": { @@ -49,7 +56,8 @@ "individual_address": "Adre\u00e7a KNX per utilitzar amb Home Assistant, p. ex. `0.0.4`", "local_ip": "Deixa-ho en blanc per utilitzar el descobriment autom\u00e0tic." }, - "description": "Configura les opcions d'encaminament." + "description": "Configura les opcions d'encaminament.", + "title": "Encaminament" }, "secure_key_source": { "description": "Selecciona com vols configurar KNX/IP Secure.", @@ -57,7 +65,8 @@ "secure_knxkeys": "Utilitza un fitxer `.knxkeys` que contingui les claus de seguretat IP (IP Secure)", "secure_routing_manual": "Configura manualment la clau troncal de seguretat IP (IP Secure)", "secure_tunnel_manual": "Configura manualment les credencials de seguretat IP (IP Secure)" - } + }, + "title": "KNX IP-Secure" }, "secure_knxkeys": { "data": { @@ -68,7 +77,8 @@ "knxkeys_filename": "S'espera que el fitxer es trobi al teu directori de configuraci\u00f3 a `.storage/knx/`.\nA Home Assistant aix\u00f2 estaria a `/config/.storage/knx/`\nExemple: `el_meu_projecte.knxkeys`", "knxkeys_password": "S'ha definit durant l'exportaci\u00f3 del fitxer des d'ETS." }, - "description": "Introdueix la informaci\u00f3 del teu fitxer `.knxkeys`." + "description": "Introdueix la informaci\u00f3 del teu fitxer `.knxkeys`.", + "title": "Fitxer de claus ('keyfile')" }, "secure_routing_manual": { "data": { @@ -79,7 +89,8 @@ "backbone_key": "Es pot veure dins l'informe de 'Seguretat' d'un projecte ETS. Per exemple: '00112233445566778899AABBCCDDEEFF'", "sync_latency_tolerance": "El valor per defecte \u00e9s 1000." }, - "description": "Introdueix la informaci\u00f3 de seguretat IP (IP Secure)." + "description": "Introdueix la informaci\u00f3 de seguretat IP (IP Secure).", + "title": "Encaminament segur" }, "secure_tunnel_manual": { "data": { @@ -92,20 +103,15 @@ "user_id": "Sovint \u00e9s el n\u00famero del t\u00fanel +1. Per tant, 'T\u00fanel 2' tindria l'ID d'usuari '3'.", "user_password": "Contrasenya per a la connexi\u00f3 t\u00fanel espec\u00edfica configurada al panell 'Propietats' del t\u00fanel a ETS." }, - "description": "Introdueix la informaci\u00f3 de seguretat IP (IP Secure)." - }, - "secure_tunneling": { - "description": "Selecciona com vols configurar KNX/IP Secure.", - "menu_options": { - "secure_knxkeys": "Utilitza un fitxer `.knxkeys` que contingui les claus de seguretat IP (IP Secure)", - "secure_tunnel_manual": "Configura manualment les claus de seguretat IP (IP Secure)" - } + "description": "Introdueix la informaci\u00f3 de seguretat IP (IP Secure).", + "title": "T\u00fanels segurs" }, "tunnel": { "data": { "gateway": "Connexi\u00f3 t\u00fanel KNX" }, - "description": "Selecciona una passarel\u00b7la d'enlla\u00e7 de la llista." + "description": "Selecciona una passarel\u00b7la d'enlla\u00e7 de la llista.", + "title": "T\u00fanel" } } }, @@ -117,8 +123,13 @@ "invalid_individual_address": "El valor no coincideix amb el patr\u00f3 d'adre\u00e7a KNX individual.\n'area.line.device'", "invalid_ip_address": "Adre\u00e7a IPv4 inv\u00e0lida.", "invalid_signature": "La contrasenya per desxifrar el fitxer `.knxkeys` \u00e9s incorrecta.", + "keyfile_invalid_signature": "La contrasenya per desxifrar el fitxer `.knxkeys` \u00e9s incorrecta.", + "keyfile_no_backbone_key": "El fitxer `.knxkeys` no cont\u00e9 una clau de 'backbone' per a l'encaminament segur.", + "keyfile_no_tunnel_for_host": "El fitxer `.knxkeys` no cont\u00e9 credencials per a l'amfitri\u00f3 `{host}`.", + "keyfile_not_found": "No s'ha trobat el fitxer `.knxkeys` especificat a la ruta config/.storage/knx/", "no_router_discovered": "No s'ha descobert cap encaminador ('router') KNXnet/IP a la xarxa.", - "no_tunnel_discovered": "No s'ha trobat cap servidor de tunelitzaci\u00f3 KNX a la xarxa." + "no_tunnel_discovered": "No s'ha trobat cap servidor de tunelitzaci\u00f3 KNX a la xarxa.", + "unsupported_tunnel_type": "El tipus de t\u00fanel seleccionat no \u00e9s compatible amb la passarel\u00b7la." }, "step": { "communication_settings": { @@ -129,13 +140,15 @@ "data_description": { "rate_limit": "Telegrames de sortida m\u00e0xims per segon.\nUtilitza `0` per desactivar la limitaci\u00f3. Recomanat: 0 o, de 20 a 40", "state_updater": "Configuraci\u00f3 predeterminadament per llegir els estats del bus KNX. Si est\u00e0 desactivat, Home Assistant no obtindr\u00e0 activament els estats del bus KNX. Les opcions d'entitat `sync_state` poden substituir-ho." - } + }, + "title": "Configuraci\u00f3 de la comunicaci\u00f3" }, "connection_type": { "data": { "connection_type": "Tipus de connexi\u00f3 KNX" }, - "description": "Introdueix el tipus de connexi\u00f3 a utilitzar per a la connexi\u00f3 KNX.\n AUTOM\u00c0TICA: la integraci\u00f3 s'encarrega de la connectivitat al bus KNX realitzant una exploraci\u00f3 de la passarel\u00b7la.\n T\u00daNEL: la integraci\u00f3 es connectar\u00e0 al bus KNX mitjan\u00e7ant un t\u00fanel.\n ENCAMINAMENT: la integraci\u00f3 es connectar\u00e0 al bus KNX mitjan\u00e7ant l'encaminament." + "description": "Introdueix el tipus de connexi\u00f3 a utilitzar per a la connexi\u00f3 KNX.\n AUTOM\u00c0TICA: la integraci\u00f3 s'encarrega de la connectivitat al bus KNX realitzant una exploraci\u00f3 de la passarel\u00b7la.\n T\u00daNEL: la integraci\u00f3 es connectar\u00e0 al bus KNX mitjan\u00e7ant un t\u00fanel.\n ENCAMINAMENT: la integraci\u00f3 es connectar\u00e0 al bus KNX mitjan\u00e7ant l'encaminament.", + "title": "Connexi\u00f3 KNX" }, "manual_tunnel": { "data": { @@ -151,13 +164,15 @@ "port": "Port del dispositiu de tunelitzaci\u00f3 KNX/IP.", "route_back": "Activa-ho si el teun servidor de tunelitzaci\u00f3 KNXnet/IP est\u00e0 darrere una NAT. Nom\u00e9s s'aplica a connexions UDP." }, - "description": "Introdueix la informaci\u00f3 de connexi\u00f3 del dispositiu de t\u00fanel." + "description": "Introdueix la informaci\u00f3 de connexi\u00f3 del dispositiu de t\u00fanel.", + "title": "Configuraci\u00f3 del t\u00fanel" }, "options_init": { "menu_options": { "communication_settings": "Configuraci\u00f3 de la comunicaci\u00f3", "connection_type": "Configura la interf\u00edcie KNX" - } + }, + "title": "Configuraci\u00f3 de KNX" }, "routing": { "data": { @@ -171,7 +186,8 @@ "individual_address": "Adre\u00e7a KNX per utilitzar amb Home Assistant, p. ex. `0.0.4`", "local_ip": "Deixa-ho en blanc per utilitzar el descobriment autom\u00e0tic." }, - "description": "Configura les opcions d'encaminament." + "description": "Configura les opcions d'encaminament.", + "title": "Encaminament" }, "secure_key_source": { "description": "Selecciona com vols configurar KNX/IP Secure.", @@ -179,7 +195,8 @@ "secure_knxkeys": "Utilitza un fitxer `.knxkeys` que contingui les claus de seguretat IP (IP Secure)", "secure_routing_manual": "Configura manualment la clau troncal de seguretat IP (IP Secure)", "secure_tunnel_manual": "Configura manualment les credencials de seguretat IP (IP Secure)" - } + }, + "title": "KNX IP-Secure" }, "secure_knxkeys": { "data": { @@ -190,7 +207,8 @@ "knxkeys_filename": "S'espera que el fitxer es trobi al teu directori de configuraci\u00f3 a `.storage/knx/`.\nA Home Assistant aix\u00f2 estaria a `/config/.storage/knx/`\nExemple: `el_meu_projecte.knxkeys`", "knxkeys_password": "S'ha definit durant l'exportaci\u00f3 del fitxer des d'ETS." }, - "description": "Introdueix la informaci\u00f3 del teu fitxer `.knxkeys`." + "description": "Introdueix la informaci\u00f3 del teu fitxer `.knxkeys`.", + "title": "Fitxer de claus ('keyfile')" }, "secure_routing_manual": { "data": { @@ -201,7 +219,8 @@ "backbone_key": "Es pot veure dins l'informe de 'Seguretat' d'un projecte ETS. Per exemple: '00112233445566778899AABBCCDDEEFF'", "sync_latency_tolerance": "El valor per defecte \u00e9s 1000." }, - "description": "Introdueix la informaci\u00f3 de seguretat IP (IP Secure)." + "description": "Introdueix la informaci\u00f3 de seguretat IP (IP Secure).", + "title": "Encaminament segur" }, "secure_tunnel_manual": { "data": { @@ -214,20 +233,15 @@ "user_id": "Sovint \u00e9s el n\u00famero del t\u00fanel +1. Per tant, 'T\u00fanel 2' tindria l'ID d'usuari '3'.", "user_password": "Contrasenya per a la connexi\u00f3 t\u00fanel espec\u00edfica configurada al panell 'Propietats' del t\u00fanel a ETS." }, - "description": "Introdueix la informaci\u00f3 de seguretat IP (IP Secure)." - }, - "secure_tunneling": { - "description": "Selecciona com vols configurar KNX/IP Secure.", - "menu_options": { - "secure_knxkeys": "Utilitza un fitxer `.knxkeys` que contingui les claus de seguretat IP (IP Secure)", - "secure_tunnel_manual": "Configura manualment les claus de seguretat IP (IP Secure)" - } + "description": "Introdueix la informaci\u00f3 de seguretat IP (IP Secure).", + "title": "T\u00fanels segurs" }, "tunnel": { "data": { "gateway": "Connexi\u00f3 t\u00fanel KNX" }, - "description": "Selecciona una passarel\u00b7la d'enlla\u00e7 de la llista." + "description": "Selecciona una passarel\u00b7la d'enlla\u00e7 de la llista.", + "title": "T\u00fanel" } } } diff --git a/homeassistant/components/knx/translations/cs.json b/homeassistant/components/knx/translations/cs.json index 325e4710145..1f36076ba5f 100644 --- a/homeassistant/components/knx/translations/cs.json +++ b/homeassistant/components/knx/translations/cs.json @@ -23,6 +23,7 @@ }, "options": { "error": { + "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", "invalid_ip_address": "Neplatn\u00e1 adresa IPv4." }, "step": { diff --git a/homeassistant/components/knx/translations/de.json b/homeassistant/components/knx/translations/de.json index 9c64e5b6edb..f2a63223cbe 100644 --- a/homeassistant/components/knx/translations/de.json +++ b/homeassistant/components/knx/translations/de.json @@ -12,7 +12,8 @@ "invalid_ip_address": "Ung\u00fcltige IPv4 Adresse.", "invalid_signature": "Das Passwort zum Entschl\u00fcsseln der `.knxkeys` Datei ist ung\u00fcltig.", "no_router_discovered": "Es wurde kein KNXnet/IP-Router im Netzwerk gefunden.", - "no_tunnel_discovered": "Es konnte kein KNX Tunneling Server in deinem Netzwerk gefunden werden." + "no_tunnel_discovered": "Es konnte kein KNX Tunneling Server in deinem Netzwerk gefunden werden.", + "unsupported_tunnel_type": "Ausgew\u00e4hlter Tunneltyp wird vom Gateway nicht unterst\u00fctzt." }, "step": { "connection_type": { @@ -26,7 +27,7 @@ "host": "Host", "local_ip": "Lokale IP von Home Assistant", "port": "Port", - "route_back": "Zur\u00fcckrouten / NAT-Modus", + "route_back": "Route back / NAT-Modus", "tunneling_type": "KNX Tunneling Typ" }, "data_description": { @@ -54,9 +55,9 @@ "secure_key_source": { "description": "W\u00e4hle aus, wie du KNX/IP-Secure konfigurieren m\u00f6chtest.", "menu_options": { - "secure_knxkeys": "Verwenden einer \"knxkeys\"-Datei mit IP-Secure-Schl\u00fcsseln", + "secure_knxkeys": "Verwende eine \".knxkeys\" Datei mit IP-Secure-Schl\u00fcsseln", "secure_routing_manual": "IP-Secure Backbone-Schl\u00fcssel manuell konfigurieren", - "secure_tunnel_manual": "IP-Secure-Anmeldeinformationen manuell konfigurieren" + "secure_tunnel_manual": "IP-Secure-Schl\u00fcssel manuell konfigurieren" } }, "secure_knxkeys": { @@ -73,10 +74,10 @@ "secure_routing_manual": { "data": { "backbone_key": "Backbone-Schl\u00fcssel", - "sync_latency_tolerance": "Netzwerklatenztoleranz" + "sync_latency_tolerance": "Netzwerklatenz-Toleranz" }, "data_description": { - "backbone_key": "Kann im Bericht \"Sicherheit\" eines ETS-Projekts eingesehen werden. z.B. '00112233445566778899AABBCCDDEEFF'", + "backbone_key": "Kann im Report \"Projekt-Sicherheit\" eines ETS-Projekts eingesehen werden. z.B. '00112233445566778899AABBCCDDEEFF'", "sync_latency_tolerance": "Der Standardwert ist 1000." }, "description": "Bitte gib deine IP-Secure Informationen ein." @@ -94,13 +95,6 @@ }, "description": "Bitte gib deine IP-Secure Informationen ein." }, - "secure_tunneling": { - "description": "W\u00e4hle aus, wie du KNX/IP-Secure konfigurieren m\u00f6chtest.", - "menu_options": { - "secure_knxkeys": "Verwende eine `.knxkeys` Datei, die IP-Secure Schl\u00fcssel enth\u00e4lt", - "secure_tunnel_manual": "IP-Secure Schl\u00fcssel manuell konfigurieren" - } - }, "tunnel": { "data": { "gateway": "KNX Tunnel Verbindung" @@ -118,7 +112,8 @@ "invalid_ip_address": "Ung\u00fcltige IPv4 Adresse.", "invalid_signature": "Das Passwort zum Entschl\u00fcsseln der `.knxkeys` Datei ist ung\u00fcltig.", "no_router_discovered": "Es wurde kein KNXnet/IP-Router im Netzwerk gefunden.", - "no_tunnel_discovered": "Es konnte kein KNX Tunneling Server in deinem Netzwerk gefunden werden." + "no_tunnel_discovered": "Es konnte kein KNX Tunneling Server in deinem Netzwerk gefunden werden.", + "unsupported_tunnel_type": "Ausgew\u00e4hlter Tunneltyp wird vom Gateway nicht unterst\u00fctzt." }, "step": { "communication_settings": { @@ -142,7 +137,7 @@ "host": "Host", "local_ip": "Lokale IP von Home Assistant", "port": "Port", - "route_back": "Zur\u00fcckrouten / NAT-Modus", + "route_back": "Route back / NAT-Modus", "tunneling_type": "KNX Tunneling Typ" }, "data_description": { @@ -176,9 +171,9 @@ "secure_key_source": { "description": "W\u00e4hle aus, wie du KNX/IP-Secure konfigurieren m\u00f6chtest.", "menu_options": { - "secure_knxkeys": "Verwenden einer \"knxkeys\"-Datei mit IP-Secure-Schl\u00fcsseln", + "secure_knxkeys": "Verwende eine \".knxkeys\" Datei mit IP-Secure-Schl\u00fcsseln", "secure_routing_manual": "IP-Secure Backbone-Schl\u00fcssel manuell konfigurieren", - "secure_tunnel_manual": "IP-Secure-Anmeldeinformationen manuell konfigurieren" + "secure_tunnel_manual": "IP-Secure-Schl\u00fcssel manuell konfigurieren" } }, "secure_knxkeys": { @@ -195,10 +190,10 @@ "secure_routing_manual": { "data": { "backbone_key": "Backbone-Schl\u00fcssel", - "sync_latency_tolerance": "Netzwerklatenztoleranz" + "sync_latency_tolerance": "Netzwerklatenz-Toleranz" }, "data_description": { - "backbone_key": "Kann im Bericht \"Sicherheit\" eines ETS-Projekts eingesehen werden. z.B. '00112233445566778899AABBCCDDEEFF'", + "backbone_key": "Kann im Report \"Projekt-Sicherheit\" eines ETS-Projekts eingesehen werden. z.B. '00112233445566778899AABBCCDDEEFF'", "sync_latency_tolerance": "Der Standardwert ist 1000." }, "description": "Bitte gib deine IP-Secure Informationen ein." @@ -216,13 +211,6 @@ }, "description": "Bitte gib deine IP-Secure Informationen ein." }, - "secure_tunneling": { - "description": "W\u00e4hle aus, wie du KNX/IP-Secure konfigurieren m\u00f6chtest.", - "menu_options": { - "secure_knxkeys": "Verwende eine `.knxkeys` Datei, die IP-Secure Schl\u00fcssel enth\u00e4lt", - "secure_tunnel_manual": "IP-Secure Schl\u00fcssel manuell konfigurieren" - } - }, "tunnel": { "data": { "gateway": "KNX Tunnel Verbindung" diff --git a/homeassistant/components/knx/translations/el.json b/homeassistant/components/knx/translations/el.json index 660ce9218f9..b9e71239cdb 100644 --- a/homeassistant/components/knx/translations/el.json +++ b/homeassistant/components/knx/translations/el.json @@ -7,11 +7,13 @@ "error": { "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", "file_not_found": "\u03a4\u03bf \u03ba\u03b1\u03b8\u03bf\u03c1\u03b9\u03c3\u03bc\u03ad\u03bd\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf knxkeys \u03b4\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b5 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b1\u03b4\u03c1\u03bf\u03bc\u03ae config/.storage/knx/", + "invalid_backbone_key": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af \u03ba\u03bf\u03c1\u03bc\u03bf\u03cd. \u0391\u03bd\u03b1\u03bc\u03ad\u03bd\u03bf\u03bd\u03c4\u03b1\u03b9 32 \u03b4\u03b5\u03ba\u03b1\u03b5\u03be\u03b1\u03b4\u03b9\u03ba\u03bf\u03af \u03b1\u03c1\u03b9\u03b8\u03bc\u03bf\u03af.", "invalid_individual_address": "\u0397 \u03c4\u03b9\u03bc\u03ae \u03b4\u03b5\u03bd \u03c4\u03b1\u03b9\u03c1\u03b9\u03ac\u03b6\u03b5\u03b9 \u03bc\u03b5 \u03c4\u03bf \u03bc\u03bf\u03c4\u03af\u03b2\u03bf \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03bc\u03b5\u03bc\u03bf\u03bd\u03c9\u03bc\u03ad\u03bd\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 KNX.\n \"area.line.device\"", "invalid_ip_address": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IPv4.", "invalid_signature": "\u039f \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd \u03b1\u03c0\u03bf\u03ba\u03c1\u03c5\u03c0\u03c4\u03bf\u03b3\u03c1\u03ac\u03c6\u03b7\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf\u03c5 knxkeys \u03b5\u03af\u03bd\u03b1\u03b9 \u03bb\u03ac\u03b8\u03bf\u03c2.", "no_router_discovered": "\u0394\u03b5\u03bd \u03b1\u03bd\u03b1\u03ba\u03b1\u03bb\u03cd\u03c6\u03b8\u03b7\u03ba\u03b5 \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03bf\u03b3\u03b7\u03c4\u03ae\u03c2 KNXnet/IP \u03c3\u03c4\u03bf \u03b4\u03af\u03ba\u03c4\u03c5\u03bf.", - "no_tunnel_discovered": "\u0394\u03b5\u03bd \u03ae\u03c4\u03b1\u03bd \u03b4\u03c5\u03bd\u03b1\u03c4\u03cc\u03c2 \u03bf \u03b5\u03bd\u03c4\u03bf\u03c0\u03b9\u03c3\u03bc\u03cc\u03c2 \u03b4\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ae \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2 KNX \u03c3\u03c4\u03bf \u03b4\u03af\u03ba\u03c4\u03c5\u03cc \u03c3\u03b1\u03c2." + "no_tunnel_discovered": "\u0394\u03b5\u03bd \u03ae\u03c4\u03b1\u03bd \u03b4\u03c5\u03bd\u03b1\u03c4\u03cc\u03c2 \u03bf \u03b5\u03bd\u03c4\u03bf\u03c0\u03b9\u03c3\u03bc\u03cc\u03c2 \u03b4\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ae \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2 KNX \u03c3\u03c4\u03bf \u03b4\u03af\u03ba\u03c4\u03c5\u03cc \u03c3\u03b1\u03c2.", + "unsupported_tunnel_type": "\u039f \u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03bf\u03c2 \u03c4\u03cd\u03c0\u03bf\u03c2 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2 \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03b7\u03bd \u03c0\u03cd\u03bb\u03b7." }, "step": { "connection_type": { @@ -41,7 +43,8 @@ "individual_address": "\u0391\u03c4\u03bf\u03bc\u03b9\u03ba\u03ae \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7", "local_ip": "\u03a4\u03bf\u03c0\u03b9\u03ba\u03ae IP \u03c4\u03bf\u03c5 Home Assistant", "multicast_group": "\u039f\u03bc\u03ac\u03b4\u03b1 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ae\u03c2 \u03b4\u03b9\u03b1\u03bd\u03bf\u03bc\u03ae\u03c2", - "multicast_port": "\u0398\u03cd\u03c1\u03b1 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ae\u03c2 \u03b5\u03ba\u03c0\u03bf\u03bc\u03c0\u03ae\u03c2" + "multicast_port": "\u0398\u03cd\u03c1\u03b1 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ae\u03c2 \u03b5\u03ba\u03c0\u03bf\u03bc\u03c0\u03ae\u03c2", + "routing_secure": "\u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf KNX IP Secure" }, "data_description": { "individual_address": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 KNX \u03c0\u03bf\u03c5 \u03b8\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b7\u03b8\u03b5\u03af \u03b1\u03c0\u03cc \u03c4\u03bf Home Assistant, \u03c0.\u03c7. `0.0.4`.", @@ -49,6 +52,14 @@ }, "description": "\u0394\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03ce\u03c3\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03cc\u03b3\u03b7\u03c3\u03b7\u03c2." }, + "secure_key_source": { + "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c0\u03ce\u03c2 \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03ce\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf KNX/IP Secure.", + "menu_options": { + "secure_knxkeys": "\u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03ad\u03bd\u03b1 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf `.knxkeys` \u03c0\u03bf\u03c5 \u03c0\u03b5\u03c1\u03b9\u03ad\u03c7\u03b5\u03b9 \u03ba\u03bb\u03b5\u03b9\u03b4\u03b9\u03ac \u03b1\u03c3\u03c6\u03b1\u03bb\u03b5\u03af\u03b1\u03c2 IP", + "secure_routing_manual": "\u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03ba\u03bb\u03b5\u03b9\u03b4\u03b9\u03bf\u03cd IP \u03b1\u03c3\u03c6\u03b1\u03bb\u03bf\u03cd\u03c2 \u03ba\u03bf\u03c1\u03bc\u03bf\u03cd \u03bc\u03b7 \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b1", + "secure_tunnel_manual": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03c0\u03b1\u03c1\u03b1\u03bc\u03ad\u03c4\u03c1\u03bf\u03c5\u03c2 \u03c4\u03c9\u03bd \u03b4\u03b9\u03b1\u03c0\u03b9\u03c3\u03c4\u03b5\u03c5\u03c4\u03b7\u03c1\u03af\u03c9\u03bd \u03b1\u03c3\u03c6\u03b1\u03bb\u03b5\u03af\u03b1\u03c2 IP \u03bc\u03b5 \u03bc\u03b7 \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03bf \u03c4\u03c1\u03cc\u03c0\u03bf" + } + }, "secure_knxkeys": { "data": { "knxkeys_filename": "\u03a4\u03bf \u03cc\u03bd\u03bf\u03bc\u03b1 \u03c4\u03bf\u03c5 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf\u03c5 \u03c3\u03b1\u03c2 `.knxkeys` (\u03c3\u03c5\u03bc\u03c0\u03b5\u03c1\u03b9\u03bb\u03b1\u03bc\u03b2\u03b1\u03bd\u03bf\u03bc\u03ad\u03bd\u03b7\u03c2 \u03c4\u03b7\u03c2 \u03b5\u03c0\u03ad\u03ba\u03c4\u03b1\u03c3\u03b7\u03c2)", @@ -60,6 +71,17 @@ }, "description": "\u0395\u03b9\u03c3\u03b1\u03b3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 \u03b3\u03b9\u03b1 \u03c4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf knxkeys \u03c3\u03b1\u03c2." }, + "secure_routing_manual": { + "data": { + "backbone_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af \u03ba\u03bf\u03c1\u03bc\u03bf\u03cd", + "sync_latency_tolerance": "\u0391\u03bd\u03bf\u03c7\u03ae \u03ba\u03b1\u03b8\u03c5\u03c3\u03c4\u03ad\u03c1\u03b7\u03c3\u03b7\u03c2 \u03b4\u03b9\u03ba\u03c4\u03cd\u03bf\u03c5" + }, + "data_description": { + "backbone_key": "\u039c\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03c6\u03b1\u03bd\u03b5\u03af \u03c3\u03c4\u03b7\u03bd \u03b1\u03bd\u03b1\u03c6\u03bf\u03c1\u03ac \u00ab\u0391\u03c3\u03c6\u03ac\u03bb\u03b5\u03b9\u03b1\u00bb \u03b5\u03bd\u03cc\u03c2 \u03ad\u03c1\u03b3\u03bf\u03c5 ETS. \u03a0.\u03c7. '00112233445566778899AABBCCDDEEFF'", + "sync_latency_tolerance": "\u0397 \u03c0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u03b5\u03af\u03bd\u03b1\u03b9 1000." + }, + "description": "\u0395\u03b9\u03c3\u03b1\u03b3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03b1\u03c3\u03c6\u03b1\u03bb\u03b5\u03af\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 IP \u03c3\u03b1\u03c2." + }, "secure_tunnel_manual": { "data": { "device_authentication": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2", @@ -73,13 +95,6 @@ }, "description": "\u0395\u03b9\u03c3\u03b1\u03b3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03b1\u03c3\u03c6\u03b1\u03bb\u03b5\u03af\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 IP \u03c3\u03b1\u03c2." }, - "secure_tunneling": { - "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c0\u03ce\u03c2 \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03ce\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf IP Secure.", - "menu_options": { - "secure_knxkeys": "\u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03ad\u03bd\u03b1 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf `.knxkeys` \u03c0\u03bf\u03c5 \u03c0\u03b5\u03c1\u03b9\u03ad\u03c7\u03b5\u03b9 \u03ba\u03bb\u03b5\u03b9\u03b4\u03b9\u03ac \u03b1\u03c3\u03c6\u03b1\u03bb\u03b5\u03af\u03b1\u03c2 IP", - "secure_tunnel_manual": "\u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03ba\u03bb\u03b5\u03b9\u03b4\u03b9\u03ce\u03bd \u03b1\u03c3\u03c6\u03b1\u03bb\u03b5\u03af\u03b1\u03c2 IP \u03bc\u03b5 \u03bc\u03b7 \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03bf \u03c4\u03c1\u03cc\u03c0\u03bf" - } - }, "tunnel": { "data": { "gateway": "\u03a3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2 KNX" @@ -92,11 +107,13 @@ "error": { "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", "file_not_found": "\u03a4\u03bf \u03ba\u03b1\u03b8\u03bf\u03c1\u03b9\u03c3\u03bc\u03ad\u03bd\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf knxkeys \u03b4\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b5 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b1\u03b4\u03c1\u03bf\u03bc\u03ae config/.storage/knx/", + "invalid_backbone_key": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af \u03ba\u03bf\u03c1\u03bc\u03bf\u03cd. \u0391\u03bd\u03b1\u03bc\u03ad\u03bd\u03bf\u03bd\u03c4\u03b1\u03b9 32 \u03b4\u03b5\u03ba\u03b1\u03b5\u03be\u03b1\u03b4\u03b9\u03ba\u03bf\u03af \u03b1\u03c1\u03b9\u03b8\u03bc\u03bf\u03af.", "invalid_individual_address": "\u0397 \u03c4\u03b9\u03bc\u03ae \u03b4\u03b5\u03bd \u03c4\u03b1\u03b9\u03c1\u03b9\u03ac\u03b6\u03b5\u03b9 \u03bc\u03b5 \u03c4\u03bf \u03bc\u03bf\u03c4\u03af\u03b2\u03bf \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03bc\u03b5\u03bc\u03bf\u03bd\u03c9\u03bc\u03ad\u03bd\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 KNX.\n \"area.line.device\"", "invalid_ip_address": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IPv4.", "invalid_signature": "\u039f \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd \u03b1\u03c0\u03bf\u03ba\u03c1\u03c5\u03c0\u03c4\u03bf\u03b3\u03c1\u03ac\u03c6\u03b7\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf\u03c5 knxkeys \u03b5\u03af\u03bd\u03b1\u03b9 \u03bb\u03ac\u03b8\u03bf\u03c2.", "no_router_discovered": "\u0394\u03b5\u03bd \u03b1\u03bd\u03b1\u03ba\u03b1\u03bb\u03cd\u03c6\u03b8\u03b7\u03ba\u03b5 \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03bf\u03b3\u03b7\u03c4\u03ae\u03c2 KNXnet/IP \u03c3\u03c4\u03bf \u03b4\u03af\u03ba\u03c4\u03c5\u03bf.", - "no_tunnel_discovered": "\u0394\u03b5\u03bd \u03ae\u03c4\u03b1\u03bd \u03b4\u03c5\u03bd\u03b1\u03c4\u03cc\u03c2 \u03bf \u03b5\u03bd\u03c4\u03bf\u03c0\u03b9\u03c3\u03bc\u03cc\u03c2 \u03b4\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ae \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2 KNX \u03c3\u03c4\u03bf \u03b4\u03af\u03ba\u03c4\u03c5\u03cc \u03c3\u03b1\u03c2." + "no_tunnel_discovered": "\u0394\u03b5\u03bd \u03ae\u03c4\u03b1\u03bd \u03b4\u03c5\u03bd\u03b1\u03c4\u03cc\u03c2 \u03bf \u03b5\u03bd\u03c4\u03bf\u03c0\u03b9\u03c3\u03bc\u03cc\u03c2 \u03b4\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ae \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2 KNX \u03c3\u03c4\u03bf \u03b4\u03af\u03ba\u03c4\u03c5\u03cc \u03c3\u03b1\u03c2.", + "unsupported_tunnel_type": "\u039f \u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03bf\u03c2 \u03c4\u03cd\u03c0\u03bf\u03c2 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2 \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03b7\u03bd \u03c0\u03cd\u03bb\u03b7." }, "step": { "communication_settings": { @@ -142,7 +159,8 @@ "individual_address": "\u0391\u03c4\u03bf\u03bc\u03b9\u03ba\u03ae \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7", "local_ip": "\u03a4\u03bf\u03c0\u03b9\u03ba\u03ae IP \u03c4\u03bf\u03c5 Home Assistant", "multicast_group": "\u039f\u03bc\u03ac\u03b4\u03b1 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ae\u03c2 \u03b4\u03b9\u03b1\u03bd\u03bf\u03bc\u03ae\u03c2", - "multicast_port": "\u0398\u03cd\u03c1\u03b1 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ae\u03c2 \u03b5\u03ba\u03c0\u03bf\u03bc\u03c0\u03ae\u03c2" + "multicast_port": "\u0398\u03cd\u03c1\u03b1 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ae\u03c2 \u03b5\u03ba\u03c0\u03bf\u03bc\u03c0\u03ae\u03c2", + "routing_secure": "\u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf KNX IP Secure" }, "data_description": { "individual_address": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 KNX \u03c0\u03bf\u03c5 \u03b8\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b7\u03b8\u03b5\u03af \u03b1\u03c0\u03cc \u03c4\u03bf Home Assistant, \u03c0.\u03c7. `0.0.4`.", @@ -150,6 +168,14 @@ }, "description": "\u0394\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03ce\u03c3\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03cc\u03b3\u03b7\u03c3\u03b7\u03c2." }, + "secure_key_source": { + "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c0\u03ce\u03c2 \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03ce\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf KNX/IP Secure.", + "menu_options": { + "secure_knxkeys": "\u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03ad\u03bd\u03b1 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf `.knxkeys` \u03c0\u03bf\u03c5 \u03c0\u03b5\u03c1\u03b9\u03ad\u03c7\u03b5\u03b9 \u03ba\u03bb\u03b5\u03b9\u03b4\u03b9\u03ac \u03b1\u03c3\u03c6\u03b1\u03bb\u03b5\u03af\u03b1\u03c2 IP", + "secure_routing_manual": "\u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03ba\u03bb\u03b5\u03b9\u03b4\u03b9\u03bf\u03cd IP \u03b1\u03c3\u03c6\u03b1\u03bb\u03bf\u03cd\u03c2 \u03ba\u03bf\u03c1\u03bc\u03bf\u03cd \u03bc\u03b7 \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b1", + "secure_tunnel_manual": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03c0\u03b1\u03c1\u03b1\u03bc\u03ad\u03c4\u03c1\u03bf\u03c5\u03c2 \u03c4\u03c9\u03bd \u03b4\u03b9\u03b1\u03c0\u03b9\u03c3\u03c4\u03b5\u03c5\u03c4\u03b7\u03c1\u03af\u03c9\u03bd \u03b1\u03c3\u03c6\u03b1\u03bb\u03b5\u03af\u03b1\u03c2 IP \u03bc\u03b5 \u03bc\u03b7 \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03bf \u03c4\u03c1\u03cc\u03c0\u03bf" + } + }, "secure_knxkeys": { "data": { "knxkeys_filename": "\u03a4\u03bf \u03cc\u03bd\u03bf\u03bc\u03b1 \u03c4\u03bf\u03c5 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf\u03c5 \u03c3\u03b1\u03c2 `.knxkeys` (\u03c3\u03c5\u03bc\u03c0\u03b5\u03c1\u03b9\u03bb\u03b1\u03bc\u03b2\u03b1\u03bd\u03bf\u03bc\u03ad\u03bd\u03b7\u03c2 \u03c4\u03b7\u03c2 \u03b5\u03c0\u03ad\u03ba\u03c4\u03b1\u03c3\u03b7\u03c2)", @@ -161,6 +187,17 @@ }, "description": "\u0395\u03b9\u03c3\u03b1\u03b3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 \u03b3\u03b9\u03b1 \u03c4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf knxkeys \u03c3\u03b1\u03c2." }, + "secure_routing_manual": { + "data": { + "backbone_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af \u03ba\u03bf\u03c1\u03bc\u03bf\u03cd", + "sync_latency_tolerance": "\u0391\u03bd\u03bf\u03c7\u03ae \u03ba\u03b1\u03b8\u03c5\u03c3\u03c4\u03ad\u03c1\u03b7\u03c3\u03b7\u03c2 \u03b4\u03b9\u03ba\u03c4\u03cd\u03bf\u03c5" + }, + "data_description": { + "backbone_key": "\u039c\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03c6\u03b1\u03bd\u03b5\u03af \u03c3\u03c4\u03b7\u03bd \u03b1\u03bd\u03b1\u03c6\u03bf\u03c1\u03ac \u00ab\u0391\u03c3\u03c6\u03ac\u03bb\u03b5\u03b9\u03b1\u00bb \u03b5\u03bd\u03cc\u03c2 \u03ad\u03c1\u03b3\u03bf\u03c5 ETS. \u03a0.\u03c7. '00112233445566778899AABBCCDDEEFF'", + "sync_latency_tolerance": "\u0397 \u03c0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u03b5\u03af\u03bd\u03b1\u03b9 1000." + }, + "description": "\u0395\u03b9\u03c3\u03b1\u03b3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03b1\u03c3\u03c6\u03b1\u03bb\u03b5\u03af\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 IP \u03c3\u03b1\u03c2." + }, "secure_tunnel_manual": { "data": { "device_authentication": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2", @@ -174,13 +211,6 @@ }, "description": "\u0395\u03b9\u03c3\u03b1\u03b3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03b1\u03c3\u03c6\u03b1\u03bb\u03b5\u03af\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 IP \u03c3\u03b1\u03c2." }, - "secure_tunneling": { - "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c0\u03ce\u03c2 \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03ce\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf IP Secure.", - "menu_options": { - "secure_knxkeys": "\u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03ad\u03bd\u03b1 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf `.knxkeys` \u03c0\u03bf\u03c5 \u03c0\u03b5\u03c1\u03b9\u03ad\u03c7\u03b5\u03b9 \u03ba\u03bb\u03b5\u03b9\u03b4\u03b9\u03ac \u03b1\u03c3\u03c6\u03b1\u03bb\u03b5\u03af\u03b1\u03c2 IP", - "secure_tunnel_manual": "\u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03ba\u03bb\u03b5\u03b9\u03b4\u03b9\u03ce\u03bd \u03b1\u03c3\u03c6\u03b1\u03bb\u03b5\u03af\u03b1\u03c2 IP \u03bc\u03b5 \u03bc\u03b7 \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03bf \u03c4\u03c1\u03cc\u03c0\u03bf" - } - }, "tunnel": { "data": { "gateway": "\u03a3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2 KNX" diff --git a/homeassistant/components/knx/translations/en.json b/homeassistant/components/knx/translations/en.json index 76ed9ba27b2..54cbabc8272 100644 --- a/homeassistant/components/knx/translations/en.json +++ b/homeassistant/components/knx/translations/en.json @@ -11,15 +11,28 @@ "invalid_individual_address": "Value does not match pattern for KNX individual address.\n'area.line.device'", "invalid_ip_address": "Invalid IPv4 address.", "invalid_signature": "The password to decrypt the `.knxkeys` file is wrong.", + "keyfile_invalid_signature": "The password to decrypt the `.knxkeys` file is wrong.", + "keyfile_no_backbone_key": "The `.knxkeys` file does not contain a backbone key for secure routing.", + "keyfile_no_tunnel_for_host": "The `.knxkeys` file does not contain credentials for host `{host}`.", + "keyfile_not_found": "The specified `.knxkeys` file was not found in the path config/.storage/knx/", "no_router_discovered": "No KNXnet/IP router was discovered on the network.", - "no_tunnel_discovered": "Could not find a KNX tunneling server on your network." + "no_tunnel_discovered": "Could not find a KNX tunneling server on your network.", + "unsupported_tunnel_type": "Selected tunnelling type not supported by gateway." }, "step": { "connection_type": { "data": { "connection_type": "KNX Connection Type" }, - "description": "Please enter the connection type we should use for your KNX connection. \n AUTOMATIC - The integration takes care of the connectivity to your KNX Bus by performing a gateway scan. \n TUNNELING - The integration will connect to your KNX bus via tunneling. \n ROUTING - The integration will connect to your KNX bus via routing." + "description": "Please enter the connection type we should use for your KNX connection. \n AUTOMATIC - The integration takes care of the connectivity to your KNX Bus by performing a gateway scan. \n TUNNELING - The integration will connect to your KNX bus via tunneling. \n ROUTING - The integration will connect to your KNX bus via routing.", + "title": "KNX connection" + }, + "knxkeys_tunnel_select": { + "data": { + "user_id": "`Automatic` will use the first free tunnel endpoint." + }, + "description": "Select the tunnel used for connection.", + "title": "Tunnel endpoint" }, "manual_tunnel": { "data": { @@ -35,7 +48,8 @@ "port": "Port of the KNX/IP tunneling device.", "route_back": "Enable if your KNXnet/IP tunneling server is behind NAT. Only applies for UDP connections." }, - "description": "Please enter the connection information of your tunneling device." + "description": "Please enter the connection information of your tunneling device.", + "title": "Tunnel settings" }, "routing": { "data": { @@ -49,7 +63,8 @@ "individual_address": "KNX address to be used by Home Assistant, e.g. `0.0.4`", "local_ip": "Leave blank to use auto-discovery." }, - "description": "Please configure the routing options." + "description": "Please configure the routing options.", + "title": "Routing" }, "secure_key_source": { "description": "Select how you want to configure KNX/IP Secure.", @@ -57,7 +72,8 @@ "secure_knxkeys": "Use a `.knxkeys` file containing IP secure keys", "secure_routing_manual": "Configure IP secure backbone key manually", "secure_tunnel_manual": "Configure IP secure credentials manually" - } + }, + "title": "KNX IP-Secure" }, "secure_knxkeys": { "data": { @@ -68,7 +84,8 @@ "knxkeys_filename": "The file is expected to be found in your config directory in `.storage/knx/`.\nIn Home Assistant OS this would be `/config/.storage/knx/`\nExample: `my_project.knxkeys`", "knxkeys_password": "This was set when exporting the file from ETS." }, - "description": "Please enter the information for your `.knxkeys` file." + "description": "Please enter the information for your `.knxkeys` file.", + "title": "Keyfile" }, "secure_routing_manual": { "data": { @@ -79,7 +96,8 @@ "backbone_key": "Can be seen in the 'Security' report of an ETS project. Eg. '00112233445566778899AABBCCDDEEFF'", "sync_latency_tolerance": "Default is 1000." }, - "description": "Please enter your IP secure information." + "description": "Please enter your IP secure information.", + "title": "Secure routing" }, "secure_tunnel_manual": { "data": { @@ -92,20 +110,15 @@ "user_id": "This is often tunnel number +1. So 'Tunnel 2' would have User-ID '3'.", "user_password": "Password for the specific tunnel connection set in the 'Properties' panel of the tunnel in ETS." }, - "description": "Please enter your IP secure information." - }, - "secure_tunneling": { - "description": "Select how you want to configure KNX/IP Secure.", - "menu_options": { - "secure_knxkeys": "Use a `.knxkeys` file containing IP secure keys", - "secure_tunnel_manual": "Configure IP secure keys manually" - } + "description": "Please enter your IP secure information.", + "title": "Secure tunnelling" }, "tunnel": { "data": { "gateway": "KNX Tunnel Connection" }, - "description": "Please select a gateway from the list." + "description": "Please select a gateway from the list.", + "title": "Tunnel" } } }, @@ -117,8 +130,13 @@ "invalid_individual_address": "Value does not match pattern for KNX individual address.\n'area.line.device'", "invalid_ip_address": "Invalid IPv4 address.", "invalid_signature": "The password to decrypt the `.knxkeys` file is wrong.", + "keyfile_invalid_signature": "The password to decrypt the `.knxkeys` file is wrong.", + "keyfile_no_backbone_key": "The `.knxkeys` file does not contain a backbone key for secure routing.", + "keyfile_no_tunnel_for_host": "The `.knxkeys` file does not contain credentials for host `{host}`.", + "keyfile_not_found": "The specified `.knxkeys` file was not found in the path config/.storage/knx/", "no_router_discovered": "No KNXnet/IP router was discovered on the network.", - "no_tunnel_discovered": "Could not find a KNX tunneling server on your network." + "no_tunnel_discovered": "Could not find a KNX tunneling server on your network.", + "unsupported_tunnel_type": "Selected tunnelling type not supported by gateway." }, "step": { "communication_settings": { @@ -129,13 +147,22 @@ "data_description": { "rate_limit": "Maximum outgoing telegrams per second.\n`0` to disable limit. Recommended: 0 or 20 to 40", "state_updater": "Set default for reading states from the KNX Bus. When disabled, Home Assistant will not actively retrieve entity states from the KNX Bus. Can be overridden by `sync_state` entity options." - } + }, + "title": "Communication settings" }, "connection_type": { "data": { "connection_type": "KNX Connection Type" }, - "description": "Please enter the connection type we should use for your KNX connection. \n AUTOMATIC - The integration takes care of the connectivity to your KNX Bus by performing a gateway scan. \n TUNNELING - The integration will connect to your KNX bus via tunneling. \n ROUTING - The integration will connect to your KNX bus via routing." + "description": "Please enter the connection type we should use for your KNX connection. \n AUTOMATIC - The integration takes care of the connectivity to your KNX Bus by performing a gateway scan. \n TUNNELING - The integration will connect to your KNX bus via tunneling. \n ROUTING - The integration will connect to your KNX bus via routing.", + "title": "KNX connection" + }, + "knxkeys_tunnel_select": { + "data": { + "user_id": "`Automatic` will use the first free tunnel endpoint." + }, + "description": "Select the tunnel used for connection.", + "title": "Tunnel endpoint" }, "manual_tunnel": { "data": { @@ -151,13 +178,15 @@ "port": "Port of the KNX/IP tunneling device.", "route_back": "Enable if your KNXnet/IP tunneling server is behind NAT. Only applies for UDP connections." }, - "description": "Please enter the connection information of your tunneling device." + "description": "Please enter the connection information of your tunneling device.", + "title": "Tunnel settings" }, "options_init": { "menu_options": { "communication_settings": "Communication settings", "connection_type": "Configure KNX interface" - } + }, + "title": "KNX Settings" }, "routing": { "data": { @@ -171,7 +200,8 @@ "individual_address": "KNX address to be used by Home Assistant, e.g. `0.0.4`", "local_ip": "Leave blank to use auto-discovery." }, - "description": "Please configure the routing options." + "description": "Please configure the routing options.", + "title": "Routing" }, "secure_key_source": { "description": "Select how you want to configure KNX/IP Secure.", @@ -179,7 +209,8 @@ "secure_knxkeys": "Use a `.knxkeys` file containing IP secure keys", "secure_routing_manual": "Configure IP secure backbone key manually", "secure_tunnel_manual": "Configure IP secure credentials manually" - } + }, + "title": "KNX IP-Secure" }, "secure_knxkeys": { "data": { @@ -190,7 +221,8 @@ "knxkeys_filename": "The file is expected to be found in your config directory in `.storage/knx/`.\nIn Home Assistant OS this would be `/config/.storage/knx/`\nExample: `my_project.knxkeys`", "knxkeys_password": "This was set when exporting the file from ETS." }, - "description": "Please enter the information for your `.knxkeys` file." + "description": "Please enter the information for your `.knxkeys` file.", + "title": "Keyfile" }, "secure_routing_manual": { "data": { @@ -201,7 +233,8 @@ "backbone_key": "Can be seen in the 'Security' report of an ETS project. Eg. '00112233445566778899AABBCCDDEEFF'", "sync_latency_tolerance": "Default is 1000." }, - "description": "Please enter your IP secure information." + "description": "Please enter your IP secure information.", + "title": "Secure routing" }, "secure_tunnel_manual": { "data": { @@ -214,20 +247,15 @@ "user_id": "This is often tunnel number +1. So 'Tunnel 2' would have User-ID '3'.", "user_password": "Password for the specific tunnel connection set in the 'Properties' panel of the tunnel in ETS." }, - "description": "Please enter your IP secure information." - }, - "secure_tunneling": { - "description": "Select how you want to configure KNX/IP Secure.", - "menu_options": { - "secure_knxkeys": "Use a `.knxkeys` file containing IP secure keys", - "secure_tunnel_manual": "Configure IP secure keys manually" - } + "description": "Please enter your IP secure information.", + "title": "Secure tunnelling" }, "tunnel": { "data": { "gateway": "KNX Tunnel Connection" }, - "description": "Please select a gateway from the list." + "description": "Please select a gateway from the list.", + "title": "Tunnel" } } } diff --git a/homeassistant/components/knx/translations/es.json b/homeassistant/components/knx/translations/es.json index f710dad5597..25605e9d471 100644 --- a/homeassistant/components/knx/translations/es.json +++ b/homeassistant/components/knx/translations/es.json @@ -11,15 +11,21 @@ "invalid_individual_address": "El valor no coincide con el patr\u00f3n de la direcci\u00f3n KNX individual. 'area.line.device'", "invalid_ip_address": "Direcci\u00f3n IPv4 no v\u00e1lida.", "invalid_signature": "La contrase\u00f1a para descifrar el archivo `.knxkeys` es incorrecta.", + "keyfile_invalid_signature": "La contrase\u00f1a para descifrar el archivo `.knxkeys` es incorrecta.", + "keyfile_no_backbone_key": "El archivo `.knxkeys` no contiene una clave principal para el enrutamiento seguro.", + "keyfile_no_tunnel_for_host": "El archivo `.knxkeys` no contiene credenciales para el host `{host}`.", + "keyfile_not_found": "El archivo `.knxkeys` especificado no se encontr\u00f3 en la ruta config/.storage/knx/", "no_router_discovered": "No se ha descubierto ning\u00fan router KNXnet/IP en la red.", - "no_tunnel_discovered": "No se pudo encontrar un servidor de t\u00fanel KNX en tu red." + "no_tunnel_discovered": "No se pudo encontrar un servidor de t\u00fanel KNX en tu red.", + "unsupported_tunnel_type": "El tipo de t\u00fanel seleccionado no es compatible con la puerta de enlace." }, "step": { "connection_type": { "data": { "connection_type": "Tipo de conexi\u00f3n KNX" }, - "description": "Por favor, introduce el tipo de conexi\u00f3n que debemos usar para tu conexi\u00f3n KNX.\n AUTOM\u00c1TICO: la integraci\u00f3n se encarga de la conectividad a tu bus KNX mediante la realizaci\u00f3n de un escaneo de la puerta de enlace.\n T\u00daNELES: la integraci\u00f3n se conectar\u00e1 a tu bus KNX a trav\u00e9s de t\u00faneles.\n ENRUTAMIENTO: la integraci\u00f3n se conectar\u00e1 a su tus KNX a trav\u00e9s del enrutamiento." + "description": "Por favor, introduce el tipo de conexi\u00f3n que debemos usar para tu conexi\u00f3n KNX.\n AUTOM\u00c1TICO: la integraci\u00f3n se encarga de la conectividad a tu bus KNX mediante la realizaci\u00f3n de un escaneo de la puerta de enlace.\n T\u00daNELES: la integraci\u00f3n se conectar\u00e1 a tu bus KNX a trav\u00e9s de t\u00faneles.\n ENRUTAMIENTO: la integraci\u00f3n se conectar\u00e1 a su tus KNX a trav\u00e9s del enrutamiento.", + "title": "Conexi\u00f3n KNX" }, "manual_tunnel": { "data": { @@ -35,7 +41,8 @@ "port": "Puerto del dispositivo de t\u00fanel KNX/IP.", "route_back": "Habilitar si tu servidor de t\u00fanel IP/KNXnet est\u00e1 detr\u00e1s de NAT. Solo aplica para conexiones UDP." }, - "description": "Por favor, introduce la informaci\u00f3n de conexi\u00f3n de tu dispositivo de t\u00fanel." + "description": "Por favor, introduce la informaci\u00f3n de conexi\u00f3n de tu dispositivo de t\u00fanel.", + "title": "Configuraci\u00f3n del t\u00fanel" }, "routing": { "data": { @@ -49,7 +56,8 @@ "individual_address": "Direcci\u00f3n KNX que usar\u00e1 Home Assistant, por ejemplo, `0.0.4`", "local_ip": "D\u00e9jalo en blanco para usar el descubrimiento autom\u00e1tico." }, - "description": "Por favor, configura las opciones de enrutamiento." + "description": "Por favor, configura las opciones de enrutamiento.", + "title": "Enrutado" }, "secure_key_source": { "description": "Selecciona c\u00f3mo deseas configurar KNX/IP Secure.", @@ -57,7 +65,8 @@ "secure_knxkeys": "Utilizar un archivo `.knxkeys` que contenga claves seguras de IP", "secure_routing_manual": "Configurar la clave de red troncal segura de IP manualmente", "secure_tunnel_manual": "Configurar las credenciales seguras de IP manualmente" - } + }, + "title": "KNX IP Secure" }, "secure_knxkeys": { "data": { @@ -68,7 +77,8 @@ "knxkeys_filename": "Se espera que el archivo se encuentre en tu directorio de configuraci\u00f3n en `.storage/knx/`.\nEn Home Assistant OS ser\u00eda `/config/.storage/knx/`\nEjemplo: `mi_proyecto.knxkeys`", "knxkeys_password": "Esto se configur\u00f3 al exportar el archivo desde ETS." }, - "description": "Por favor, introduce la informaci\u00f3n de tu archivo `.knxkeys`." + "description": "Por favor, introduce la informaci\u00f3n de tu archivo `.knxkeys`.", + "title": "Archivo de clave" }, "secure_routing_manual": { "data": { @@ -79,7 +89,8 @@ "backbone_key": "Se puede ver en el informe de 'Seguridad' de un proyecto ETS. Ej. '00112233445566778899AABBCCDDEEFF'", "sync_latency_tolerance": "El valor predeterminado es 1000." }, - "description": "Por favor, introduce tu informaci\u00f3n de IP segura." + "description": "Por favor, introduce tu informaci\u00f3n de IP segura.", + "title": "Enrutado seguro" }, "secure_tunnel_manual": { "data": { @@ -92,20 +103,15 @@ "user_id": "Este suele ser el n\u00famero de t\u00fanel +1. Por tanto, 'T\u00fanel 2' tendr\u00eda ID de usuario '3'.", "user_password": "Contrase\u00f1a para la conexi\u00f3n de t\u00fanel espec\u00edfica establecida en el panel 'Propiedades' del t\u00fanel en ETS." }, - "description": "Por favor, introduce tu informaci\u00f3n de IP segura." - }, - "secure_tunneling": { - "description": "Selecciona c\u00f3mo quieres configurar KNX/IP Secure.", - "menu_options": { - "secure_knxkeys": "Utilizar un archivo `.knxkeys` que contenga claves seguras de IP", - "secure_tunnel_manual": "Configurar claves seguras de IP manualmente" - } + "description": "Por favor, introduce tu informaci\u00f3n de IP segura.", + "title": "T\u00fanel seguro" }, "tunnel": { "data": { "gateway": "Conexi\u00f3n de t\u00fanel KNX" }, - "description": "Por favor, selecciona una puerta de enlace de la lista." + "description": "Por favor, selecciona una puerta de enlace de la lista.", + "title": "T\u00fanel" } } }, @@ -117,8 +123,13 @@ "invalid_individual_address": "El valor no coincide con el patr\u00f3n de la direcci\u00f3n KNX individual. 'area.line.device'", "invalid_ip_address": "Direcci\u00f3n IPv4 no v\u00e1lida.", "invalid_signature": "La contrase\u00f1a para descifrar el archivo `.knxkeys` es incorrecta.", + "keyfile_invalid_signature": "La contrase\u00f1a para descifrar el archivo `.knxkeys` es incorrecta.", + "keyfile_no_backbone_key": "El archivo `.knxkeys` no contiene una clave principal para el enrutamiento seguro.", + "keyfile_no_tunnel_for_host": "El archivo `.knxkeys` no contiene credenciales para el host `{host}`.", + "keyfile_not_found": "El archivo `.knxkeys` especificado no se encontr\u00f3 en la ruta config/.storage/knx/", "no_router_discovered": "No se ha descubierto ning\u00fan router KNXnet/IP en la red.", - "no_tunnel_discovered": "No se pudo encontrar un servidor de t\u00fanel KNX en tu red." + "no_tunnel_discovered": "No se pudo encontrar un servidor de t\u00fanel KNX en tu red.", + "unsupported_tunnel_type": "El tipo de t\u00fanel seleccionado no es compatible con la puerta de enlace." }, "step": { "communication_settings": { @@ -129,13 +140,15 @@ "data_description": { "rate_limit": "N\u00famero m\u00e1ximo de telegramas salientes por segundo.\n`0` para deshabilitar el l\u00edmite. Recomendado: 0 o 20 a 40", "state_updater": "Establece los valores predeterminados para leer los estados del bus KNX. Cuando est\u00e1 deshabilitado, Home Assistant no recuperar\u00e1 activamente los estados de entidad del bus KNX. Puede ser anulado por las opciones de entidad `sync_state`." - } + }, + "title": "Configuraci\u00f3n de comunicaci\u00f3n" }, "connection_type": { "data": { "connection_type": "Tipo de conexi\u00f3n KNX" }, - "description": "Por favor, introduce el tipo de conexi\u00f3n que debemos usar para tu conexi\u00f3n KNX.\n AUTOM\u00c1TICO: la integraci\u00f3n se encarga de la conectividad a tu bus KNX mediante la realizaci\u00f3n de un escaneo de la puerta de enlace.\n T\u00daNELES: la integraci\u00f3n se conectar\u00e1 a tu bus KNX a trav\u00e9s de t\u00faneles.\n ENRUTAMIENTO: la integraci\u00f3n se conectar\u00e1 a su tus KNX a trav\u00e9s del enrutamiento." + "description": "Por favor, introduce el tipo de conexi\u00f3n que debemos usar para tu conexi\u00f3n KNX.\n AUTOM\u00c1TICO: la integraci\u00f3n se encarga de la conectividad a tu bus KNX mediante la realizaci\u00f3n de un escaneo de la puerta de enlace.\n T\u00daNELES: la integraci\u00f3n se conectar\u00e1 a tu bus KNX a trav\u00e9s de t\u00faneles.\n ENRUTAMIENTO: la integraci\u00f3n se conectar\u00e1 a su tus KNX a trav\u00e9s del enrutamiento.", + "title": "Conexi\u00f3n KNX" }, "manual_tunnel": { "data": { @@ -151,13 +164,15 @@ "port": "Puerto del dispositivo de t\u00fanel KNX/IP.", "route_back": "Habilitar si tu servidor de t\u00fanel IP/KNXnet est\u00e1 detr\u00e1s de NAT. Solo aplica para conexiones UDP." }, - "description": "Por favor, introduce la informaci\u00f3n de conexi\u00f3n de tu dispositivo de t\u00fanel." + "description": "Por favor, introduce la informaci\u00f3n de conexi\u00f3n de tu dispositivo de t\u00fanel.", + "title": "Configuraci\u00f3n del t\u00fanel" }, "options_init": { "menu_options": { "communication_settings": "Configuraci\u00f3n de comunicaci\u00f3n", "connection_type": "Configurar interfaz KNX" - } + }, + "title": "Configuraci\u00f3n KNX" }, "routing": { "data": { @@ -171,7 +186,8 @@ "individual_address": "Direcci\u00f3n KNX que usar\u00e1 Home Assistant, por ejemplo, `0.0.4`", "local_ip": "D\u00e9jalo en blanco para usar el descubrimiento autom\u00e1tico." }, - "description": "Por favor, configura las opciones de enrutamiento." + "description": "Por favor, configura las opciones de enrutamiento.", + "title": "Enrutado" }, "secure_key_source": { "description": "Selecciona c\u00f3mo deseas configurar KNX/IP Secure.", @@ -179,7 +195,8 @@ "secure_knxkeys": "Utilizar un archivo `.knxkeys` que contenga claves seguras de IP", "secure_routing_manual": "Configurar la clave de red troncal segura de IP manualmente", "secure_tunnel_manual": "Configurar las credenciales seguras de IP manualmente" - } + }, + "title": "KNX IP Secure" }, "secure_knxkeys": { "data": { @@ -190,7 +207,8 @@ "knxkeys_filename": "Se espera que el archivo se encuentre en tu directorio de configuraci\u00f3n en `.storage/knx/`.\nEn Home Assistant OS ser\u00eda `/config/.storage/knx/`\nEjemplo: `mi_proyecto.knxkeys`", "knxkeys_password": "Esto se configur\u00f3 al exportar el archivo desde ETS." }, - "description": "Por favor, introduce la informaci\u00f3n de tu archivo `.knxkeys`." + "description": "Por favor, introduce la informaci\u00f3n de tu archivo `.knxkeys`.", + "title": "Archivo de clave" }, "secure_routing_manual": { "data": { @@ -201,7 +219,8 @@ "backbone_key": "Se puede ver en el informe de 'Seguridad' de un proyecto ETS. Ej. '00112233445566778899AABBCCDDEEFF'", "sync_latency_tolerance": "El valor predeterminado es 1000." }, - "description": "Por favor, introduce tu informaci\u00f3n de IP segura." + "description": "Por favor, introduce tu informaci\u00f3n de IP segura.", + "title": "Enrutado seguro" }, "secure_tunnel_manual": { "data": { @@ -214,20 +233,15 @@ "user_id": "Este suele ser el n\u00famero de t\u00fanel +1. Por tanto, 'T\u00fanel 2' tendr\u00eda ID de usuario '3'.", "user_password": "Contrase\u00f1a para la conexi\u00f3n de t\u00fanel espec\u00edfica establecida en el panel 'Propiedades' del t\u00fanel en ETS." }, - "description": "Por favor, introduce tu informaci\u00f3n de IP segura." - }, - "secure_tunneling": { - "description": "Selecciona c\u00f3mo quieres configurar KNX/IP Secure.", - "menu_options": { - "secure_knxkeys": "Utilizar un archivo `.knxkeys` que contenga claves seguras de IP", - "secure_tunnel_manual": "Configurar claves seguras de IP manualmente" - } + "description": "Por favor, introduce tu informaci\u00f3n de IP segura.", + "title": "T\u00fanel seguro" }, "tunnel": { "data": { "gateway": "Conexi\u00f3n de t\u00fanel KNX" }, - "description": "Por favor, selecciona una puerta de enlace de la lista." + "description": "Por favor, selecciona una puerta de enlace de la lista.", + "title": "T\u00fanel" } } } diff --git a/homeassistant/components/knx/translations/et.json b/homeassistant/components/knx/translations/et.json index 1efd61e02ab..ab2e5b21819 100644 --- a/homeassistant/components/knx/translations/et.json +++ b/homeassistant/components/knx/translations/et.json @@ -12,7 +12,8 @@ "invalid_ip_address": "Kehtetu IPv4 aadress.", "invalid_signature": "Parool faili `.knxkeys` dekr\u00fcpteerimiseks on vale.", "no_router_discovered": "V\u00f5rgus ei leitud \u00fchtegi KNXnet/IP-ruuterit.", - "no_tunnel_discovered": "V\u00f5rgust ei leitud KNX tunneliserverit." + "no_tunnel_discovered": "V\u00f5rgust ei leitud KNX tunneliserverit.", + "unsupported_tunnel_type": "L\u00fc\u00fcs ei toeta valitud tunnelit\u00fc\u00fcpi." }, "step": { "connection_type": { @@ -94,13 +95,6 @@ }, "description": "Sisesta oma IP secure teave." }, - "secure_tunneling": { - "description": "Vali kuidas soovid KNX/IP Secure'i seadistada.", - "menu_options": { - "secure_knxkeys": "Kasuta knxkeys fail, mis sisaldab IP Secure teavet.", - "secure_tunnel_manual": "IP Secure v\u00f5tmete k\u00e4sitsi seadistamine" - } - }, "tunnel": { "data": { "gateway": "KNX tunneli \u00fchendus" @@ -118,7 +112,8 @@ "invalid_ip_address": "Vigane IPv4 aadress", "invalid_signature": "'.knxkeys' kirje dekr\u00fcptimisv\u00f5ti on vale.", "no_router_discovered": "V\u00f5rgus ei leitud \u00fchtegi KNXnet/IP-ruuterit.", - "no_tunnel_discovered": "V\u00f5rgust ei leitud KNX tunneliserverit." + "no_tunnel_discovered": "V\u00f5rgust ei leitud KNX tunneliserverit.", + "unsupported_tunnel_type": "L\u00fc\u00fcs ei toeta valitud tunnelit\u00fc\u00fcpi." }, "step": { "communication_settings": { @@ -216,13 +211,6 @@ }, "description": "Sisesta IP secure teave." }, - "secure_tunneling": { - "description": "Vali kuidas seadistada KNX/IP Secure", - "menu_options": { - "secure_knxkeys": "Kasuta IP secure jaoks kirjet '.knxkeys'", - "secure_tunnel_manual": "Seadista IP secure v\u00f5tmed k\u00e4sitsi" - } - }, "tunnel": { "data": { "gateway": "KNX tunnel\u00fchendus" diff --git a/homeassistant/components/knx/translations/fr.json b/homeassistant/components/knx/translations/fr.json index e4bd4d4b059..184a725b777 100644 --- a/homeassistant/components/knx/translations/fr.json +++ b/homeassistant/components/knx/translations/fr.json @@ -71,13 +71,6 @@ }, "description": "Veuillez saisir vos informations de s\u00e9curit\u00e9 IP." }, - "secure_tunneling": { - "description": "S\u00e9lectionnez la mani\u00e8re dont vous souhaitez configurer la s\u00e9curit\u00e9 IP de KNX.", - "menu_options": { - "secure_knxkeys": "Utiliser un fichier `.knxkeys` contenant les cl\u00e9s de s\u00e9curit\u00e9 IP", - "secure_tunnel_manual": "Configurer manuellement les cl\u00e9s de s\u00e9curit\u00e9 IP" - } - }, "tunnel": { "data": { "gateway": "Connexion tunnel KNX" @@ -160,13 +153,6 @@ }, "description": "Veuillez saisir vos informations de s\u00e9curit\u00e9 IP." }, - "secure_tunneling": { - "description": "S\u00e9lectionnez la mani\u00e8re dont vous souhaitez configurer la s\u00e9curit\u00e9 IP de KNX.", - "menu_options": { - "secure_knxkeys": "Utiliser un fichier `.knxkeys` contenant les cl\u00e9s de s\u00e9curit\u00e9 IP", - "secure_tunnel_manual": "Configurer manuellement les cl\u00e9s de s\u00e9curit\u00e9 IP" - } - }, "tunnel": { "data": { "gateway": "Connexion tunnel KNX" diff --git a/homeassistant/components/knx/translations/hu.json b/homeassistant/components/knx/translations/hu.json index ab6e3a8c9af..033ae3d84dd 100644 --- a/homeassistant/components/knx/translations/hu.json +++ b/homeassistant/components/knx/translations/hu.json @@ -7,11 +7,13 @@ "error": { "cannot_connect": "Sikertelen csatlakoz\u00e1s", "file_not_found": "A megadott '.knxkeys' f\u00e1jl nem tal\u00e1lhat\u00f3 a config/.storage/knx/ el\u00e9r\u00e9si \u00fatvonalon.", + "invalid_backbone_key": "\u00c9rv\u00e9nytelen gerinckulcs. 32 hexadecim\u00e1lis sz\u00e1m az elv\u00e1rt.", "invalid_individual_address": "Az \u00e9rt\u00e9k nem felel meg a KNX egyedi c\u00edm mint\u00e1j\u00e1nak.\n'area.line.device'", "invalid_ip_address": "\u00c9rv\u00e9nytelen IPv4-c\u00edm.", "invalid_signature": "A '.knxkeys' f\u00e1jl visszafejt\u00e9s\u00e9hez haszn\u00e1lt jelsz\u00f3 helytelen.", "no_router_discovered": "Nem tal\u00e1lhat\u00f3 KNXnet/IP \u00fatv\u00e1laszt\u00f3 a h\u00e1l\u00f3zaton.", - "no_tunnel_discovered": "Nem tal\u00e1lhat\u00f3 KNX alag\u00fat-kiszolg\u00e1l\u00f3 a h\u00e1l\u00f3zaton." + "no_tunnel_discovered": "Nem tal\u00e1lhat\u00f3 KNX alag\u00fat-kiszolg\u00e1l\u00f3 a h\u00e1l\u00f3zaton.", + "unsupported_tunnel_type": "A kiv\u00e1lasztott alag\u00fatt\u00edpust az \u00e1tj\u00e1r\u00f3 nem t\u00e1mogatja." }, "step": { "connection_type": { @@ -41,7 +43,8 @@ "individual_address": "Egy\u00e9ni c\u00edm", "local_ip": "Home Assistant lok\u00e1lis IP c\u00edme", "multicast_group": "Multicast csoport", - "multicast_port": "Multicast portsz\u00e1m" + "multicast_port": "Multicast portsz\u00e1m", + "routing_secure": "KNX IP Secure haszn\u00e1lata" }, "data_description": { "individual_address": "A Home Assistant \u00e1ltal haszn\u00e1land\u00f3 KNX-c\u00edm, pl. \"0.0.4\".", @@ -49,6 +52,14 @@ }, "description": "K\u00e9rem, konfigur\u00e1lja az \u00fatv\u00e1laszt\u00e1si (routing) be\u00e1ll\u00edt\u00e1sokat." }, + "secure_key_source": { + "description": "V\u00e1lassza ki, hogyan szeretn\u00e9 konfigur\u00e1lni az KNX/IP secure-t.", + "menu_options": { + "secure_knxkeys": "IP secure kulcsokat tartalmaz\u00f3 '.knxkeys' f\u00e1jl haszn\u00e1lata", + "secure_routing_manual": "IP biztons\u00e1gos gerinch\u00e1l\u00f3zati kulcs manu\u00e1lis konfigur\u00e1l\u00e1sa", + "secure_tunnel_manual": "Biztons\u00e1gos IP-hiteles\u00edt\u0151 adatok manu\u00e1lis konfigur\u00e1l\u00e1sa" + } + }, "secure_knxkeys": { "data": { "knxkeys_filename": "A '.knxkeys' f\u00e1jl teljes neve (kiterjeszt\u00e9ssel)", @@ -60,6 +71,17 @@ }, "description": "K\u00e9rem, adja meg a '.knxkeys' f\u00e1jl adatait." }, + "secure_routing_manual": { + "data": { + "backbone_key": "Gerinch\u00e1l\u00f3zati kulcs", + "sync_latency_tolerance": "H\u00e1l\u00f3zatk\u00e9sleltet\u00e9si tolerancia" + }, + "data_description": { + "backbone_key": "Megtekinthet\u0151 egy ETS projekt 'Biztons\u00e1g' jelent\u00e9s\u00e9ben. P\u00e9ld\u00e1ul '00112233445566778899AABBCCDDEEFF'", + "sync_latency_tolerance": "Az alap\u00e9rtelmezett \u00e9rt\u00e9k 1000." + }, + "description": "K\u00e9rem, adja meg az IP secure adatokat." + }, "secure_tunnel_manual": { "data": { "device_authentication": "Eszk\u00f6z hiteles\u00edt\u00e9si jelsz\u00f3", @@ -73,13 +95,6 @@ }, "description": "K\u00e9rem, adja meg az IP secure adatokat." }, - "secure_tunneling": { - "description": "V\u00e1lassza ki, hogyan szeretn\u00e9 konfigur\u00e1lni az KNX/IP secure-t.", - "menu_options": { - "secure_knxkeys": "IP secure kulcsokat tartalmaz\u00f3 '.knxkeys' f\u00e1jl haszn\u00e1lata", - "secure_tunnel_manual": "IP secure kulcsok manu\u00e1lis be\u00e1ll\u00edt\u00e1sa" - } - }, "tunnel": { "data": { "gateway": "KNX alag\u00fat (tunnel) kapcsolat" @@ -92,11 +107,13 @@ "error": { "cannot_connect": "Sikertelen csatlakoz\u00e1s", "file_not_found": "A megadott '.knxkeys' f\u00e1jl nem tal\u00e1lhat\u00f3 a config/.storage/knx/ el\u00e9r\u00e9si \u00fatvonalon.", + "invalid_backbone_key": "\u00c9rv\u00e9nytelen gerinckulcs. 32 hexadecim\u00e1lis sz\u00e1m az elv\u00e1rt.", "invalid_individual_address": "Az \u00e9rt\u00e9k nem felel meg a KNX egyedi c\u00edm mint\u00e1j\u00e1nak.\n'area.line.device'", "invalid_ip_address": "\u00c9rv\u00e9nytelen IPv4-c\u00edm.", "invalid_signature": "A '.knxkeys' f\u00e1jl visszafejt\u00e9s\u00e9hez haszn\u00e1lt jelsz\u00f3 helytelen.", "no_router_discovered": "Nem tal\u00e1lhat\u00f3 KNXnet/IP \u00fatv\u00e1laszt\u00f3 a h\u00e1l\u00f3zaton.", - "no_tunnel_discovered": "Nem tal\u00e1lhat\u00f3 KNX alag\u00fat-kiszolg\u00e1l\u00f3 a h\u00e1l\u00f3zaton." + "no_tunnel_discovered": "Nem tal\u00e1lhat\u00f3 KNX alag\u00fat-kiszolg\u00e1l\u00f3 a h\u00e1l\u00f3zaton.", + "unsupported_tunnel_type": "A kiv\u00e1lasztott alag\u00fatt\u00edpust az \u00e1tj\u00e1r\u00f3 nem t\u00e1mogatja." }, "step": { "communication_settings": { @@ -142,7 +159,8 @@ "individual_address": "Egy\u00e9ni c\u00edm", "local_ip": "Home Assistant lok\u00e1lis IP c\u00edme", "multicast_group": "Multicast csoport", - "multicast_port": "Multicast portsz\u00e1m" + "multicast_port": "Multicast portsz\u00e1m", + "routing_secure": "KNX IP Secure haszn\u00e1lata" }, "data_description": { "individual_address": "A Home Assistant \u00e1ltal haszn\u00e1land\u00f3 KNX-c\u00edm, pl. \"0.0.4\".", @@ -150,6 +168,14 @@ }, "description": "K\u00e9rem, konfigur\u00e1lja az \u00fatv\u00e1laszt\u00e1si (routing) be\u00e1ll\u00edt\u00e1sokat." }, + "secure_key_source": { + "description": "V\u00e1lassza ki, hogyan szeretn\u00e9 konfigur\u00e1lni az KNX/IP secure-t.", + "menu_options": { + "secure_knxkeys": "IP secure kulcsokat tartalmaz\u00f3 '.knxkeys' f\u00e1jl haszn\u00e1lata", + "secure_routing_manual": "IP biztons\u00e1gos gerinch\u00e1l\u00f3zati kulcs manu\u00e1lis konfigur\u00e1l\u00e1sa", + "secure_tunnel_manual": "Biztons\u00e1gos IP-hiteles\u00edt\u0151 adatok manu\u00e1lis konfigur\u00e1l\u00e1sa" + } + }, "secure_knxkeys": { "data": { "knxkeys_filename": "A '.knxkeys' f\u00e1jl teljes neve (kiterjeszt\u00e9ssel)", @@ -161,6 +187,17 @@ }, "description": "K\u00e9rem, adja meg a '.knxkeys' f\u00e1jl adatait." }, + "secure_routing_manual": { + "data": { + "backbone_key": "Gerinch\u00e1l\u00f3zati kulcs", + "sync_latency_tolerance": "H\u00e1l\u00f3zatk\u00e9sleltet\u00e9si tolerancia" + }, + "data_description": { + "backbone_key": "Megtekinthet\u0151 egy ETS projekt 'Biztons\u00e1g' jelent\u00e9s\u00e9ben. P\u00e9ld\u00e1ul '00112233445566778899AABBCCDDEEFF'", + "sync_latency_tolerance": "Az alap\u00e9rtelmezett \u00e9rt\u00e9k 1000." + }, + "description": "K\u00e9rem, adja meg az IP secure adatokat." + }, "secure_tunnel_manual": { "data": { "device_authentication": "Eszk\u00f6z hiteles\u00edt\u00e9si jelsz\u00f3", @@ -174,13 +211,6 @@ }, "description": "K\u00e9rem, adja meg az IP secure adatokat." }, - "secure_tunneling": { - "description": "V\u00e1lassza ki, hogyan szeretn\u00e9 konfigur\u00e1lni az KNX/IP secure-t.", - "menu_options": { - "secure_knxkeys": "IP secure kulcsokat tartalmaz\u00f3 '.knxkeys' f\u00e1jl haszn\u00e1lata", - "secure_tunnel_manual": "IP secure kulcsok manu\u00e1lis be\u00e1ll\u00edt\u00e1sa" - } - }, "tunnel": { "data": { "gateway": "KNX alag\u00fat (tunnel) kapcsolat" diff --git a/homeassistant/components/knx/translations/id.json b/homeassistant/components/knx/translations/id.json index 4e4f17f70bd..4da3f26e148 100644 --- a/homeassistant/components/knx/translations/id.json +++ b/homeassistant/components/knx/translations/id.json @@ -12,7 +12,8 @@ "invalid_ip_address": "Alamat IPv4 tidak valid", "invalid_signature": "Kata sandi untuk mendekripsi file `.knxkeys` salah.", "no_router_discovered": "Tidak ada router KNXnet/IP yang ditemukan di jaringan.", - "no_tunnel_discovered": "Tidak dapat menemukan server tunneling KNX di jaringan Anda." + "no_tunnel_discovered": "Tidak dapat menemukan server tunneling KNX di jaringan Anda.", + "unsupported_tunnel_type": "Jenis tunneling yang dipilih tidak didukung oleh gateway." }, "step": { "connection_type": { @@ -94,13 +95,6 @@ }, "description": "Masukkan informasi IP aman Anda." }, - "secure_tunneling": { - "description": "Pilih cara Anda ingin mengonfigurasi KNX/IP Secure.", - "menu_options": { - "secure_knxkeys": "Gunakan file `.knxkeys` yang berisi kunci aman IP", - "secure_tunnel_manual": "Konfigurasikan kunci aman IP secara manual" - } - }, "tunnel": { "data": { "gateway": "Koneksi Tunnel KNX" @@ -118,7 +112,8 @@ "invalid_ip_address": "Alamat IPv4 tidak valid", "invalid_signature": "Kata sandi untuk mendekripsi file `.knxkeys` salah.", "no_router_discovered": "Tidak ada router KNXnet/IP yang ditemukan di jaringan.", - "no_tunnel_discovered": "Tidak dapat menemukan server tunneling KNX di jaringan Anda." + "no_tunnel_discovered": "Tidak dapat menemukan server tunneling KNX di jaringan Anda.", + "unsupported_tunnel_type": "Jenis tunneling yang dipilih tidak didukung oleh gateway." }, "step": { "communication_settings": { @@ -216,13 +211,6 @@ }, "description": "Masukkan informasi IP aman Anda." }, - "secure_tunneling": { - "description": "Pilih cara Anda ingin mengonfigurasi KNX/IP Secure.", - "menu_options": { - "secure_knxkeys": "Gunakan file `.knxkeys` yang berisi kunci aman IP", - "secure_tunnel_manual": "Konfigurasikan kunci aman IP secara manual" - } - }, "tunnel": { "data": { "gateway": "Koneksi Tunnel KNX" diff --git a/homeassistant/components/knx/translations/it.json b/homeassistant/components/knx/translations/it.json index 75c6cda6ab5..b70b8b06c80 100644 --- a/homeassistant/components/knx/translations/it.json +++ b/homeassistant/components/knx/translations/it.json @@ -7,11 +7,13 @@ "error": { "cannot_connect": "Impossibile connettersi", "file_not_found": "Il file `.knxkeys` specificato non \u00e8 stato trovato nel percorso config/.storage/knx/", + "invalid_backbone_key": "Chiave backbone non valida. Previsti 32 numeri esadecimali.", "invalid_individual_address": "Il valore non corrisponde al modello per l'indirizzo individuale KNX. 'area.line.device'", "invalid_ip_address": "Indirizzo IPv4 non valido.", "invalid_signature": "La password per decifrare il file `.knxkeys` \u00e8 errata.", "no_router_discovered": "Non \u00e8 stato rilevato alcun router KNXnet/IP nella rete.", - "no_tunnel_discovered": "Impossibile trovare un server di tunneling KNX sulla rete." + "no_tunnel_discovered": "Impossibile trovare un server di tunneling KNX sulla rete.", + "unsupported_tunnel_type": "Il tipo di tunnel selezionato non \u00e8 supportato dal gateway." }, "step": { "connection_type": { @@ -41,7 +43,8 @@ "individual_address": "Indirizzo individuale", "local_ip": "IP locale di Home Assistant", "multicast_group": "Gruppo multicast", - "multicast_port": "Porta multicast" + "multicast_port": "Porta multicast", + "routing_secure": "Utilizzare KNX IP Secure" }, "data_description": { "individual_address": "Indirizzo KNX che deve essere utilizzato da Home Assistant, ad es. `0.0.4`", @@ -49,6 +52,14 @@ }, "description": "Configura le opzioni di instradamento." }, + "secure_key_source": { + "description": "Seleziona come vuoi configurare KNX/IP Secure.", + "menu_options": { + "secure_knxkeys": "Usa un file `.knxkeys` contenente le chiavi di sicurezza IP", + "secure_routing_manual": "Configurare manualmente la chiave backbone IP secure", + "secure_tunnel_manual": "Configura manualmente le credenziali di protezione IP" + } + }, "secure_knxkeys": { "data": { "knxkeys_filename": "Il nome del file `.knxkeys` (inclusa l'estensione)", @@ -60,6 +71,17 @@ }, "description": "Inserisci le informazioni per il tuo file `.knxkeys`." }, + "secure_routing_manual": { + "data": { + "backbone_key": "Chiave backbone", + "sync_latency_tolerance": "Tolleranza alla latenza di rete" + }, + "data_description": { + "backbone_key": "Pu\u00f2 essere visualizzato nel rapporto \"Sicurezza\" di un progetto ETS. Eg. '00112233445566778899AABBCCDDEEFF'", + "sync_latency_tolerance": "Il valore predefinito \u00e8 1000." + }, + "description": "Inserisci le tue informazioni di sicurezza IP." + }, "secure_tunnel_manual": { "data": { "device_authentication": "Password di autenticazione del dispositivo", @@ -73,13 +95,6 @@ }, "description": "Inserisci le tue informazioni di sicurezza IP." }, - "secure_tunneling": { - "description": "Seleziona come vuoi configurare KNX/IP Secure.", - "menu_options": { - "secure_knxkeys": "Utilizza un file `.knxkeys` contenente chiavi di sicurezza IP", - "secure_tunnel_manual": "Configura manualmente le chiavi di sicurezza IP" - } - }, "tunnel": { "data": { "gateway": "Connessione tunnel KNX" @@ -92,11 +107,13 @@ "error": { "cannot_connect": "Impossibile connettersi", "file_not_found": "Il file `.knxkeys` specificato non \u00e8 stato trovato nel percorso config/.storage/knx/", + "invalid_backbone_key": "Chiave backbone non valida. Previsti 32 numeri esadecimali.", "invalid_individual_address": "Il valore non corrisponde al modello per l'indirizzo individuale KNX. 'area.line.device'", "invalid_ip_address": "Indirizzo IPv4 non valido.", "invalid_signature": "La password per decifrare il file `.knxkeys` \u00e8 errata.", "no_router_discovered": "Non \u00e8 stato rilevato alcun router KNXnet/IP nella rete.", - "no_tunnel_discovered": "Impossibile trovare un server di tunneling KNX sulla rete." + "no_tunnel_discovered": "Impossibile trovare un server di tunneling KNX sulla rete.", + "unsupported_tunnel_type": "Il tipo di tunnel selezionato non \u00e8 supportato dal gateway." }, "step": { "communication_settings": { @@ -142,7 +159,8 @@ "individual_address": "Indirizzo individuale", "local_ip": "IP locale di Home Assistant", "multicast_group": "Gruppo multicast", - "multicast_port": "Porta multicast" + "multicast_port": "Porta multicast", + "routing_secure": "Utilizzare KNX IP Secure" }, "data_description": { "individual_address": "Indirizzo KNX che deve essere utilizzato da Home Assistant, ad es. `0.0.4`", @@ -150,6 +168,14 @@ }, "description": "Configura le opzioni di instradamento." }, + "secure_key_source": { + "description": "Seleziona come vuoi configurare KNX/IP Secure.", + "menu_options": { + "secure_knxkeys": "Usa un file `.knxkeys` contenente le chiavi di sicurezza IP", + "secure_routing_manual": "Configurare manualmente la chiave backbone IP secure", + "secure_tunnel_manual": "Configura manualmente le credenziali di protezione IP" + } + }, "secure_knxkeys": { "data": { "knxkeys_filename": "Il nome del file `.knxkeys` (inclusa l'estensione)", @@ -161,6 +187,17 @@ }, "description": "Inserisci le informazioni per il tuo file `.knxkeys`." }, + "secure_routing_manual": { + "data": { + "backbone_key": "Chiave backbone", + "sync_latency_tolerance": "Tolleranza alla latenza di rete" + }, + "data_description": { + "backbone_key": "Pu\u00f2 essere visualizzato nel rapporto \"Sicurezza\" di un progetto ETS. Eg. '00112233445566778899AABBCCDDEEFF'", + "sync_latency_tolerance": "Il valore predefinito \u00e8 1000." + }, + "description": "Inserisci le tue informazioni di sicurezza IP." + }, "secure_tunnel_manual": { "data": { "device_authentication": "Password di autenticazione del dispositivo", @@ -174,13 +211,6 @@ }, "description": "Inserisci le tue informazioni di sicurezza IP." }, - "secure_tunneling": { - "description": "Seleziona come vuoi configurare KNX/IP Secure.", - "menu_options": { - "secure_knxkeys": "Utilizza un file `.knxkeys` contenente chiavi di sicurezza IP", - "secure_tunnel_manual": "Configura manualmente le chiavi di sicurezza IP" - } - }, "tunnel": { "data": { "gateway": "Connessione tunnel KNX" diff --git a/homeassistant/components/knx/translations/ja.json b/homeassistant/components/knx/translations/ja.json index 47af3f3998b..42aea352aa7 100644 --- a/homeassistant/components/knx/translations/ja.json +++ b/homeassistant/components/knx/translations/ja.json @@ -50,12 +50,6 @@ }, "description": "'.knxkeys'\u30d5\u30a1\u30a4\u30eb\u306e\u60c5\u5831\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002" }, - "secure_tunneling": { - "description": "KNX/IP \u30bb\u30ad\u30e5\u30a2\u3092\u69cb\u6210\u3059\u308b\u65b9\u6cd5\u3092\u9078\u629e\u3057\u307e\u3059\u3002", - "menu_options": { - "secure_knxkeys": "IP \u30bb\u30ad\u30e5\u30a2 \u30ad\u30fc\u3092\u542b\u3080\u300c.knxkeys\u300d\u30d5\u30a1\u30a4\u30eb\u3092\u4f7f\u7528\u3059\u308b" - } - }, "tunnel": { "data": { "gateway": "KNX\u30c8\u30f3\u30cd\u30eb\u63a5\u7d9a" diff --git a/homeassistant/components/knx/translations/ko.json b/homeassistant/components/knx/translations/ko.json index c65ad01ff79..3de4b49bf57 100644 --- a/homeassistant/components/knx/translations/ko.json +++ b/homeassistant/components/knx/translations/ko.json @@ -1,10 +1,133 @@ { "config": { + "abort": { + "already_configured": "\uc11c\ube44\uc2a4\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_backbone_key": "\ubc31\ubcf8 \ud0a4\uac00 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4. 32\uc790\ub9ac\uc758 16\uc9c4\uc218 \uc22b\uc790\uac00 \ud544\uc694\ud569\ub2c8\ub2e4.", + "no_router_discovered": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c KNXnet/IP \ub77c\uc6b0\ud130\uac00 \uac80\uc0c9\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4.", + "no_tunnel_discovered": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c KNX \ud130\ub110\ub9c1 \uc11c\ubc84\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4." + }, "step": { + "connection_type": { + "data": { + "connection_type": "KNX \uc5f0\uacb0 \uc720\ud615" + }, + "description": "KNX \uc5f0\uacb0\uc5d0 \uc0ac\uc6a9\ud560 \uc5f0\uacb0 \uc720\ud615\uc744 \uc785\ub825\ud558\uc2ed\uc2dc\uc624.\n \uc790\ub3d9 - \ud1b5\ud569\uad6c\uc131\uc694\uc18c\ub294 \uac8c\uc774\ud2b8\uc6e8\uc774 \uc2a4\uce94\uc744 \uc218\ud589\ud558\uc5ec KNX \ubc84\uc2a4\uc5d0 \ub300\ud55c \uc5f0\uacb0\uc744 \uad00\ub9ac\ud569\ub2c8\ub2e4.\n \ud130\ub110\ub9c1 - \ud1b5\ud569\uad6c\uc131\uc694\uc18c\uac00 \ud130\ub110\ub9c1\uc744 \ud1b5\ud574 KNX \ubc84\uc2a4\uc5d0 \uc5f0\uacb0\ub429\ub2c8\ub2e4.\n \ub77c\uc6b0\ud305 - \ud1b5\ud569\uad6c\uc131\uc694\uc18c\uac00 \ub77c\uc6b0\ud305\uc744 \ud1b5\ud574 KNX \ubc84\uc2a4\uc5d0 \uc5f0\uacb0\ub429\ub2c8\ub2e4." + }, + "manual_tunnel": { + "data": { + "host": "\ud638\uc2a4\ud2b8", + "port": "\ud3ec\ud2b8", + "route_back": "\ub77c\uc6b0\ud2b8 \ubc31 / NAT \ubaa8\ub4dc" + }, + "data_description": { + "route_back": "KNXnet/IP \ud130\ub110\ub9c1 \uc11c\ubc84\uac00 NAT \ub4a4\uc5d0 \uc788\ub294 \uacbd\uc6b0 \ud65c\uc131\ud654\ud558\uc2ed\uc2dc\uc624. UDP \uc5f0\uacb0\uc5d0\ub9cc \uc801\uc6a9\ub429\ub2c8\ub2e4." + } + }, + "routing": { + "data": { + "routing_secure": "KNX IP \ubcf4\uc548 \uc0ac\uc6a9" + } + }, + "secure_key_source": { + "description": "KNX/IP \ubcf4\uc548\uc744 \uad6c\uc131\ud560 \ubc29\ubc95\uc744 \uc120\ud0dd\ud569\ub2c8\ub2e4.", + "menu_options": { + "secure_knxkeys": "IP \ubcf4\uc548 \ud0a4\uac00 \ud3ec\ud568\ub41c `.knxkeys` \ud30c\uc77c \uc0ac\uc6a9", + "secure_routing_manual": "IP \ubcf4\uc548 \ubc31\ubcf8 \ud0a4\ub97c \uc218\ub3d9\uc73c\ub85c \uad6c\uc131", + "secure_tunnel_manual": "\uc218\ub3d9\uc73c\ub85c IP \ubcf4\uc548 \uc790\uaca9 \uc99d\uba85 \uad6c\uc131" + } + }, "secure_knxkeys": { "data": { "knxkeys_filename": "`.knxkeys` \ud30c\uc77c\uc758 \ud30c\uc77c \uc774\ub984(\ud655\uc7a5\uc790 \ud3ec\ud568)" } + }, + "secure_routing_manual": { + "data": { + "backbone_key": "\ubc31\ubcf8 \ud0a4", + "sync_latency_tolerance": "\ub124\ud2b8\uc6cc\ud06c \ub300\uae30 \uc2dc\uac04 \ud5c8\uc6a9 \uc624\ucc28" + }, + "data_description": { + "backbone_key": "ETS \ud504\ub85c\uc81d\ud2b8\uc758 '\ubcf4\uc548' \ubcf4\uace0\uc11c\uc5d0\uc11c \ubcfc \uc218 \uc788\uc2b5\ub2c8\ub2e4. \uc608. '00112233445566778899AABBCCDDEEFF'", + "sync_latency_tolerance": "\uae30\ubcf8\uac12\uc740 1000\uc785\ub2c8\ub2e4." + }, + "description": "IP \ubcf4\uc548 \uc815\ubcf4\ub97c \uc785\ub825\ud558\uc138\uc694." + }, + "secure_tunnel_manual": { + "data": { + "device_authentication": "\uae30\uae30 \uc778\uc99d \ube44\ubc00\ubc88\ud638", + "user_id": "\uc0ac\uc6a9\uc790 \uc544\uc774\ub514", + "user_password": "\uc0ac\uc6a9\uc790 \ube44\ubc00\ubc88\ud638" + }, + "data_description": { + "device_authentication": "ETS \uc778\ud130\ud398\uc774\uc2a4\uc758 'IP' \ud328\ub110\uc5d0\uc11c \uc124\uc815\ud569\ub2c8\ub2e4.", + "user_id": "\ubcf4\ud1b5 \ud130\ub110 \ubc88\ud638 +1\uc785\ub2c8\ub2e4. \ub530\ub77c\uc11c '\ud130\ub110 2'\ub294 \uc0ac\uc6a9\uc790 ID '3'\uc744 \uac16\uac8c \ub429\ub2c8\ub2e4.", + "user_password": "ETS\uc5d0\uc11c \ud130\ub110\uc758 '\uc18d\uc131' \ud328\ub110\uc5d0 \uc124\uc815\ub41c \ud2b9\uc815 \ud130\ub110 \uc5f0\uacb0\uc5d0 \ub300\ud55c \ube44\ubc00\ubc88\ud638\uc785\ub2c8\ub2e4." + }, + "description": "IP \ubcf4\uc548 \uc815\ubcf4\ub97c \uc785\ub825\ud558\uc138\uc694." + } + } + }, + "options": { + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_backbone_key": "\ubc31\ubcf8 \ud0a4\uac00 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4. 32\uc790\ub9ac\uc758 16\uc9c4\uc218 \uc22b\uc790\uac00 \ud544\uc694\ud569\ub2c8\ub2e4.", + "no_router_discovered": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c KNXnet/IP \ub77c\uc6b0\ud130\uac00 \uac80\uc0c9\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4.", + "no_tunnel_discovered": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c KNX \ud130\ub110\ub9c1 \uc11c\ubc84\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4." + }, + "step": { + "manual_tunnel": { + "data": { + "host": "\ud638\uc2a4\ud2b8", + "port": "\ud3ec\ud2b8", + "route_back": "\ub77c\uc6b0\ud2b8 \ubc31 / NAT \ubaa8\ub4dc" + }, + "data_description": { + "route_back": "KNXnet/IP \ud130\ub110\ub9c1 \uc11c\ubc84\uac00 NAT \ub4a4\uc5d0 \uc788\ub294 \uacbd\uc6b0 \ud65c\uc131\ud654\ud558\uc2ed\uc2dc\uc624. UDP \uc5f0\uacb0\uc5d0\ub9cc \uc801\uc6a9\ub429\ub2c8\ub2e4." + } + }, + "options_init": { + "menu_options": { + "communication_settings": "\ud1b5\uc2e0\ubc29\ubc95 \uc124\uc815", + "connection_type": "KNX \uc778\ud130\ud398\uc774\uc2a4 \uad6c\uc131" + } + }, + "routing": { + "data": { + "routing_secure": "KNX IP \ubcf4\uc548 \uc0ac\uc6a9" + } + }, + "secure_key_source": { + "description": "KNX/IP \ubcf4\uc548\uc744 \uad6c\uc131\ud560 \ubc29\ubc95\uc744 \uc120\ud0dd\ud569\ub2c8\ub2e4.", + "menu_options": { + "secure_knxkeys": "IP \ubcf4\uc548 \ud0a4\uac00 \ud3ec\ud568\ub41c `.knxkeys` \ud30c\uc77c \uc0ac\uc6a9", + "secure_routing_manual": "IP \ubcf4\uc548 \ubc31\ubcf8 \ud0a4\ub97c \uc218\ub3d9\uc73c\ub85c \uad6c\uc131", + "secure_tunnel_manual": "\uc218\ub3d9\uc73c\ub85c IP \ubcf4\uc548 \uc790\uaca9 \uc99d\uba85 \uad6c\uc131" + } + }, + "secure_knxkeys": { + "data": { + "knxkeys_filename": "`.knxkeys` \ud30c\uc77c\uc758 \ud30c\uc77c \uc774\ub984(\ud655\uc7a5\uc790 \ud3ec\ud568)" + } + }, + "secure_routing_manual": { + "data": { + "backbone_key": "\ubc31\ubcf8 \ud0a4", + "sync_latency_tolerance": "\ub124\ud2b8\uc6cc\ud06c \ub300\uae30 \uc2dc\uac04 \ud5c8\uc6a9 \uc624\ucc28" + }, + "data_description": { + "backbone_key": "ETS \ud504\ub85c\uc81d\ud2b8\uc758 '\ubcf4\uc548' \ubcf4\uace0\uc11c\uc5d0\uc11c \ubcfc \uc218 \uc788\uc2b5\ub2c8\ub2e4. \uc608. '00112233445566778899AABBCCDDEEFF'", + "sync_latency_tolerance": "\uae30\ubcf8\uac12\uc740 1000\uc785\ub2c8\ub2e4." + }, + "description": "IP \ubcf4\uc548 \uc815\ubcf4\ub97c \uc785\ub825\ud558\uc138\uc694." + }, + "secure_tunnel_manual": { + "data": { + "device_authentication": "\uae30\uae30 \uc778\uc99d \ube44\ubc00\ubc88\ud638" + } } } } diff --git a/homeassistant/components/knx/translations/nl.json b/homeassistant/components/knx/translations/nl.json index 897b2dc533e..72d9e6bf1d8 100644 --- a/homeassistant/components/knx/translations/nl.json +++ b/homeassistant/components/knx/translations/nl.json @@ -50,10 +50,9 @@ }, "description": "Voer de informatie voor uw `.knxkeys` bestand in." }, - "secure_tunneling": { - "description": "Kies hoe u KNX/IP Secure wilt configureren.", - "menu_options": { - "secure_knxkeys": "Gebruik een `.knxkeys` bestand met IP beveiligde sleutels" + "secure_routing_manual": { + "data_description": { + "sync_latency_tolerance": "Standaard is 1000" } }, "tunnel": { @@ -68,7 +67,9 @@ "error": { "cannot_connect": "Kan geen verbinding maken", "file_not_found": "Het opgegeven `.knxkeys`-bestand is niet gevonden in het pad config/.storage/knx/", - "invalid_individual_address": "Waarde komt niet overeen met patroon voor KNX individueel adres.\n\"area.line.device" + "invalid_individual_address": "Waarde komt niet overeen met patroon voor KNX individueel adres.\n\"area.line.device", + "invalid_ip_address": "Ongeldig IPv4-adres.", + "invalid_signature": "Het wachtwoord om het `.knxkeys`-bestand te decoderen is verkeerd." }, "step": { "manual_tunnel": { @@ -102,10 +103,9 @@ }, "description": "Voer de informatie voor uw `.knxkeys` bestand in." }, - "secure_tunneling": { - "description": "Kies hoe u KNX/IP Secure wilt configureren.", - "menu_options": { - "secure_knxkeys": "Gebruik een `.knxkeys` bestand met IP beveiligde sleutels" + "secure_routing_manual": { + "data_description": { + "sync_latency_tolerance": "Standaard is 1000" } }, "tunnel": { diff --git a/homeassistant/components/knx/translations/no.json b/homeassistant/components/knx/translations/no.json index 8ddccdf25a3..183a2cd18d2 100644 --- a/homeassistant/components/knx/translations/no.json +++ b/homeassistant/components/knx/translations/no.json @@ -12,7 +12,8 @@ "invalid_ip_address": "Ugyldig IPv4-adresse.", "invalid_signature": "Passordet for \u00e5 dekryptere `.knxkeys`-filen er feil.", "no_router_discovered": "Ingen KNXnet/IP-ruter ble oppdaget p\u00e5 nettverket.", - "no_tunnel_discovered": "Kunne ikke finne en KNX-tunnelserver p\u00e5 nettverket ditt." + "no_tunnel_discovered": "Kunne ikke finne en KNX-tunnelserver p\u00e5 nettverket ditt.", + "unsupported_tunnel_type": "Den valgte tunneltypen st\u00f8ttes ikke av gatewayen." }, "step": { "connection_type": { @@ -94,13 +95,6 @@ }, "description": "Vennligst skriv inn din sikre IP-informasjon." }, - "secure_tunneling": { - "description": "Velg hvordan du vil konfigurere KNX/IP Secure.", - "menu_options": { - "secure_knxkeys": "Bruk en `.knxkeys`-fil som inneholder IP-sikre n\u00f8kler", - "secure_tunnel_manual": "Konfigurer IP-sikre n\u00f8kler manuelt" - } - }, "tunnel": { "data": { "gateway": "KNX Tunneltilkobling" @@ -118,7 +112,8 @@ "invalid_ip_address": "Ugyldig IPv4-adresse.", "invalid_signature": "Passordet for \u00e5 dekryptere `.knxkeys`-filen er feil.", "no_router_discovered": "Ingen KNXnet/IP-ruter ble oppdaget p\u00e5 nettverket.", - "no_tunnel_discovered": "Kunne ikke finne en KNX-tunnelserver p\u00e5 nettverket ditt." + "no_tunnel_discovered": "Kunne ikke finne en KNX-tunnelserver p\u00e5 nettverket ditt.", + "unsupported_tunnel_type": "Den valgte tunneltypen st\u00f8ttes ikke av gatewayen." }, "step": { "communication_settings": { @@ -216,13 +211,6 @@ }, "description": "Vennligst skriv inn din sikre IP-informasjon." }, - "secure_tunneling": { - "description": "Velg hvordan du vil konfigurere KNX/IP Secure.", - "menu_options": { - "secure_knxkeys": "Bruk en `.knxkeys`-fil som inneholder IP-sikre n\u00f8kler", - "secure_tunnel_manual": "Konfigurer IP-sikre n\u00f8kler manuelt" - } - }, "tunnel": { "data": { "gateway": "KNX Tunneltilkobling" diff --git a/homeassistant/components/knx/translations/pl.json b/homeassistant/components/knx/translations/pl.json index 98a9e968595..e8fd5c6dba7 100644 --- a/homeassistant/components/knx/translations/pl.json +++ b/homeassistant/components/knx/translations/pl.json @@ -7,11 +7,13 @@ "error": { "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", "file_not_found": "Podany plik '.knxkeys' nie zosta\u0142 znaleziony w \u015bcie\u017cce config/.storage/knx/", + "invalid_backbone_key": "Nieprawid\u0142owy klucz szkieletowy. Oczekiwano 32 liczb szesnastkowych.", "invalid_individual_address": "Warto\u015b\u0107 nie pasuje do wzorca dla indywidualnego adresu KNX.\n 'obszar.linia.urz\u0105dzenie'", "invalid_ip_address": "Nieprawid\u0142owy adres IPv4.", "invalid_signature": "Has\u0142o do odszyfrowania pliku '.knxkeys' jest nieprawid\u0142owe.", "no_router_discovered": "Nie wykryto w sieci routera KNXnet/IP.", - "no_tunnel_discovered": "Nie mo\u017cna znale\u017a\u0107 serwera tuneluj\u0105cego KNX w Twojej sieci." + "no_tunnel_discovered": "Nie mo\u017cna znale\u017a\u0107 serwera tuneluj\u0105cego KNX w Twojej sieci.", + "unsupported_tunnel_type": "Wybrany typ tunelowania nie jest obs\u0142ugiwany przez bramk\u0119." }, "step": { "connection_type": { @@ -41,7 +43,8 @@ "individual_address": "Adres indywidualny", "local_ip": "Lokalny adres IP Home Assistanta", "multicast_group": "Grupa multicast", - "multicast_port": "Port multicast" + "multicast_port": "Port multicast", + "routing_secure": "U\u017cyj KNX IP Secure" }, "data_description": { "individual_address": "Adres KNX u\u017cywany przez Home Assistanta, np. `0.0.4`", @@ -49,6 +52,14 @@ }, "description": "Prosz\u0119 skonfigurowa\u0107 opcje routingu." }, + "secure_key_source": { + "description": "Wybierz, jak chcesz skonfigurowa\u0107 KNX/IP Secure.", + "menu_options": { + "secure_knxkeys": "U\u017cyj pliku `.knxkeys` zawieraj\u0105cego klucze IP secure", + "secure_routing_manual": "R\u0119czna konfiguracja klucza szkieletowego IP Secure", + "secure_tunnel_manual": "R\u0119czna konfiguracja danych uwierzytelniaj\u0105cych IP Secure" + } + }, "secure_knxkeys": { "data": { "knxkeys_filename": "Nazwa pliku `.knxkeys` (wraz z rozszerzeniem)", @@ -60,6 +71,17 @@ }, "description": "Wprowad\u017a informacje dotycz\u0105ce pliku `.knxkeys`." }, + "secure_routing_manual": { + "data": { + "backbone_key": "Klucz szkieletowy", + "sync_latency_tolerance": "Tolerancja op\u00f3\u017anienia sieci" + }, + "data_description": { + "backbone_key": "Mo\u017cna go zobaczy\u0107 w raporcie \u201eBezpiecze\u0144stwo\u201d projektu ETS. Np. \u201e00112233445566778899AABBCCDDEEFF\u201d.", + "sync_latency_tolerance": "Warto\u015b\u0107 domy\u015blna to 1000." + }, + "description": "Wprowad\u017a informacje o IP Secure." + }, "secure_tunnel_manual": { "data": { "device_authentication": "Has\u0142o uwierzytelniania urz\u0105dzenia", @@ -73,13 +95,6 @@ }, "description": "Wprowad\u017a informacje o IP secure." }, - "secure_tunneling": { - "description": "Wybierz, jak chcesz skonfigurowa\u0107 KNX/IP secure.", - "menu_options": { - "secure_knxkeys": "U\u017cyj pliku `.knxkeys` zawieraj\u0105cego klucze IP secure", - "secure_tunnel_manual": "R\u0119czna konfiguracja kluczy IP secure" - } - }, "tunnel": { "data": { "gateway": "Po\u0142\u0105czenie tunelowe KNX" @@ -92,11 +107,13 @@ "error": { "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", "file_not_found": "Podany plik '.knxkeys' nie zosta\u0142 znaleziony w \u015bcie\u017cce config/.storage/knx/", + "invalid_backbone_key": "Nieprawid\u0142owy klucz szkieletowy. Oczekiwano 32 liczb szesnastkowych.", "invalid_individual_address": "Warto\u015b\u0107 nie pasuje do wzorca dla indywidualnego adresu KNX.\n 'obszar.linia.urz\u0105dzenie'", "invalid_ip_address": "Nieprawid\u0142owy adres IPv4.", "invalid_signature": "Has\u0142o do odszyfrowania pliku '.knxkeys' jest nieprawid\u0142owe.", "no_router_discovered": "Nie wykryto w sieci routera KNXnet/IP.", - "no_tunnel_discovered": "Nie mo\u017cna znale\u017a\u0107 serwera tuneluj\u0105cego KNX w Twojej sieci." + "no_tunnel_discovered": "Nie mo\u017cna znale\u017a\u0107 serwera tuneluj\u0105cego KNX w Twojej sieci.", + "unsupported_tunnel_type": "Wybrany typ tunelowania nie jest obs\u0142ugiwany przez bramk\u0119." }, "step": { "communication_settings": { @@ -142,7 +159,8 @@ "individual_address": "Adres indywidualny", "local_ip": "Lokalny adres IP Home Assistanta", "multicast_group": "Grupa multicast", - "multicast_port": "Port multicast" + "multicast_port": "Port multicast", + "routing_secure": "U\u017cyj KNX IP Secure" }, "data_description": { "individual_address": "Adres KNX u\u017cywany przez Home Assistanta, np. `0.0.4`", @@ -150,6 +168,14 @@ }, "description": "Prosz\u0119 skonfigurowa\u0107 opcje routingu." }, + "secure_key_source": { + "description": "Wybierz, jak chcesz skonfigurowa\u0107 KNX/IP Secure.", + "menu_options": { + "secure_knxkeys": "U\u017cyj pliku `.knxkeys` zawieraj\u0105cego klucze IP secure", + "secure_routing_manual": "R\u0119czna konfiguracja klucza szkieletowego IP Secure", + "secure_tunnel_manual": "R\u0119czna konfiguracja danych uwierzytelniaj\u0105cych IP Secure" + } + }, "secure_knxkeys": { "data": { "knxkeys_filename": "Nazwa pliku `.knxkeys` (wraz z rozszerzeniem)", @@ -161,6 +187,17 @@ }, "description": "Wprowad\u017a informacje dotycz\u0105ce pliku `.knxkeys`." }, + "secure_routing_manual": { + "data": { + "backbone_key": "Klucz szkieletowy", + "sync_latency_tolerance": "Tolerancja op\u00f3\u017anienia sieci" + }, + "data_description": { + "backbone_key": "Mo\u017cna go zobaczy\u0107 w raporcie \u201eBezpiecze\u0144stwo\u201d projektu ETS. Np. \u201e00112233445566778899AABBCCDDEEFF\u201d.", + "sync_latency_tolerance": "Warto\u015b\u0107 domy\u015blna to 1000." + }, + "description": "Wprowad\u017a informacje o IP Secure." + }, "secure_tunnel_manual": { "data": { "device_authentication": "Has\u0142o uwierzytelniania urz\u0105dzenia", @@ -174,13 +211,6 @@ }, "description": "Wprowad\u017a informacje o IP secure." }, - "secure_tunneling": { - "description": "Wybierz, jak chcesz skonfigurowa\u0107 KNX/IP secure.", - "menu_options": { - "secure_knxkeys": "U\u017cyj pliku `.knxkeys` zawieraj\u0105cego klucze IP secure", - "secure_tunnel_manual": "R\u0119czna konfiguracja kluczy IP secure" - } - }, "tunnel": { "data": { "gateway": "Po\u0142\u0105czenie tunelowe KNX" diff --git a/homeassistant/components/knx/translations/pt-BR.json b/homeassistant/components/knx/translations/pt-BR.json index 5217c4315ea..bc934cf135c 100644 --- a/homeassistant/components/knx/translations/pt-BR.json +++ b/homeassistant/components/knx/translations/pt-BR.json @@ -12,7 +12,8 @@ "invalid_ip_address": "Endere\u00e7o IPv4 inv\u00e1lido.", "invalid_signature": "A senha para descriptografar o arquivo `.knxkeys` est\u00e1 errada.", "no_router_discovered": "Nenhum roteador KNXnet/IP foi descoberto na rede.", - "no_tunnel_discovered": "N\u00e3o foi poss\u00edvel encontrar um servidor de encapsulamento KNX em sua rede." + "no_tunnel_discovered": "N\u00e3o foi poss\u00edvel encontrar um servidor de encapsulamento KNX em sua rede.", + "unsupported_tunnel_type": "O tipo de tunelamento selecionado n\u00e3o \u00e9 compat\u00edvel com o gateway." }, "step": { "connection_type": { @@ -94,13 +95,6 @@ }, "description": "Por favor, insira suas informa\u00e7\u00f5es seguras de IP." }, - "secure_tunneling": { - "description": "Selecione como deseja configurar o KNX/IP Secure.", - "menu_options": { - "secure_knxkeys": "Use um arquivo `.knxkeys` contendo chaves seguras de IP", - "secure_tunnel_manual": "Configurar chaves seguras de IP manualmente" - } - }, "tunnel": { "data": { "gateway": "Conex\u00e3o do t\u00fanel KNX" @@ -118,7 +112,8 @@ "invalid_ip_address": "Endere\u00e7o IPv4 inv\u00e1lido.", "invalid_signature": "A senha para descriptografar o arquivo `.knxkeys` est\u00e1 errada.", "no_router_discovered": "Nenhum roteador KNXnet/IP foi descoberto na rede.", - "no_tunnel_discovered": "N\u00e3o foi poss\u00edvel encontrar um servidor de encapsulamento KNX em sua rede." + "no_tunnel_discovered": "N\u00e3o foi poss\u00edvel encontrar um servidor de encapsulamento KNX em sua rede.", + "unsupported_tunnel_type": "O tipo de tunelamento selecionado n\u00e3o \u00e9 compat\u00edvel com o gateway." }, "step": { "communication_settings": { @@ -216,13 +211,6 @@ }, "description": "Por favor, insira suas informa\u00e7\u00f5es seguras de IP." }, - "secure_tunneling": { - "description": "Selecione como deseja configurar o KNX/IP Secure.", - "menu_options": { - "secure_knxkeys": "Use um arquivo `.knxkeys` contendo chaves seguras de IP", - "secure_tunnel_manual": "Configurar chaves seguras de IP manualmente" - } - }, "tunnel": { "data": { "gateway": "Conex\u00e3o do t\u00fanel KNX" diff --git a/homeassistant/components/knx/translations/pt.json b/homeassistant/components/knx/translations/pt.json new file mode 100644 index 00000000000..a6249ac8836 --- /dev/null +++ b/homeassistant/components/knx/translations/pt.json @@ -0,0 +1,15 @@ +{ + "options": { + "error": { + "cannot_connect": "A liga\u00e7\u00e3o falhou" + }, + "step": { + "manual_tunnel": { + "data": { + "host": "Endere\u00e7o", + "port": "Porta" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/knx/translations/ru.json b/homeassistant/components/knx/translations/ru.json index a8c9ba00ed2..d2503b2eab9 100644 --- a/homeassistant/components/knx/translations/ru.json +++ b/homeassistant/components/knx/translations/ru.json @@ -12,7 +12,8 @@ "invalid_ip_address": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0430\u0434\u0440\u0435\u0441 IPv4.", "invalid_signature": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043f\u0430\u0440\u043e\u043b\u044c \u0434\u043b\u044f \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u0438 \u0444\u0430\u0439\u043b\u0430 `.knxkeys`.", "no_router_discovered": "\u0412 \u0441\u0435\u0442\u0438 \u043d\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0442\u043e\u0440 KNXnet/IP.", - "no_tunnel_discovered": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043d\u0430\u0439\u0442\u0438 \u0441\u0435\u0440\u0432\u0435\u0440 \u0442\u0443\u043d\u043d\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f KNX \u0432 \u0412\u0430\u0448\u0435\u0439 \u0441\u0435\u0442\u0438." + "no_tunnel_discovered": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043d\u0430\u0439\u0442\u0438 \u0441\u0435\u0440\u0432\u0435\u0440 \u0442\u0443\u043d\u043d\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f KNX \u0432 \u0412\u0430\u0448\u0435\u0439 \u0441\u0435\u0442\u0438.", + "unsupported_tunnel_type": "\u0412\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0439 \u0442\u0438\u043f \u0442\u0443\u043d\u043d\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0448\u043b\u044e\u0437\u043e\u043c." }, "step": { "connection_type": { @@ -94,13 +95,6 @@ }, "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u0434\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043f\u043e IP Secure." }, - "secure_tunneling": { - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u043f\u043e\u0441\u043e\u0431 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 KNX/IP Secure.", - "menu_options": { - "secure_knxkeys": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0444\u0430\u0439\u043b `.knxkeys`, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0439 \u043a\u043b\u044e\u0447\u0438 IP secure", - "secure_tunnel_manual": "\u041d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043a\u043b\u044e\u0447\u0438 IP Secure \u0432\u0440\u0443\u0447\u043d\u0443\u044e" - } - }, "tunnel": { "data": { "gateway": "\u0422\u0443\u043d\u043d\u0435\u043b\u044c\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f KNX" @@ -118,7 +112,8 @@ "invalid_ip_address": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0430\u0434\u0440\u0435\u0441 IPv4.", "invalid_signature": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043f\u0430\u0440\u043e\u043b\u044c \u0434\u043b\u044f \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u0438 \u0444\u0430\u0439\u043b\u0430 `.knxkeys`.", "no_router_discovered": "\u0412 \u0441\u0435\u0442\u0438 \u043d\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0442\u043e\u0440 KNXnet/IP.", - "no_tunnel_discovered": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043d\u0430\u0439\u0442\u0438 \u0441\u0435\u0440\u0432\u0435\u0440 \u0442\u0443\u043d\u043d\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f KNX \u0432 \u0412\u0430\u0448\u0435\u0439 \u0441\u0435\u0442\u0438." + "no_tunnel_discovered": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043d\u0430\u0439\u0442\u0438 \u0441\u0435\u0440\u0432\u0435\u0440 \u0442\u0443\u043d\u043d\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f KNX \u0432 \u0412\u0430\u0448\u0435\u0439 \u0441\u0435\u0442\u0438.", + "unsupported_tunnel_type": "\u0412\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0439 \u0442\u0438\u043f \u0442\u0443\u043d\u043d\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0448\u043b\u044e\u0437\u043e\u043c." }, "step": { "communication_settings": { @@ -216,13 +211,6 @@ }, "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u0434\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043f\u043e IP Secure." }, - "secure_tunneling": { - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u043f\u043e\u0441\u043e\u0431 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 KNX/IP Secure.", - "menu_options": { - "secure_knxkeys": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0444\u0430\u0439\u043b `.knxkeys`, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0439 \u043a\u043b\u044e\u0447\u0438 IP secure", - "secure_tunnel_manual": "\u041d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043a\u043b\u044e\u0447\u0438 IP Secure \u0432\u0440\u0443\u0447\u043d\u0443\u044e" - } - }, "tunnel": { "data": { "gateway": "\u0422\u0443\u043d\u043d\u0435\u043b\u044c\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f KNX" diff --git a/homeassistant/components/knx/translations/sk.json b/homeassistant/components/knx/translations/sk.json index b1fa9902a2f..4ff1c07de1d 100644 --- a/homeassistant/components/knx/translations/sk.json +++ b/homeassistant/components/knx/translations/sk.json @@ -6,25 +6,45 @@ }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", - "invalid_ip_address": "Neplatn\u00e1 adresa IPv4." + "file_not_found": "Zadan\u00fd s\u00fabor `.knxkeys` sa nena\u0161iel v ceste config/.storage/knx/", + "invalid_backbone_key": "Neplatn\u00fd k\u013e\u00fa\u010d backbone. O\u010dak\u00e1va sa 32 hexadecim\u00e1lnych \u010d\u00edsel.", + "invalid_individual_address": "Hodnota sa nezhoduje so vzorom pre individu\u00e1lnu adresu KNX.\n 'area.line.device'", + "invalid_ip_address": "Neplatn\u00e1 adresa IPv4.", + "invalid_signature": "Heslo na de\u0161ifrovanie s\u00faboru `.knxkeys` je nespr\u00e1vne.", + "no_router_discovered": "V sieti nebol n\u00e1jden\u00fd \u017eiadny router KNXnet/IP.", + "no_tunnel_discovered": "Vo va\u0161ej sieti sa nepodarilo n\u00e1js\u0165 tunelovac\u00ed server KNX.", + "unsupported_tunnel_type": "Vybran\u00fd typ tunelovania nie je podporovan\u00fd br\u00e1nou." }, "step": { "connection_type": { "data": { "connection_type": "Typ pripojenia KNX" - } + }, + "description": "Zadajte typ pripojenia, ktor\u00fd by sme mali pou\u017ei\u0165 pre va\u0161e pripojenie KNX.\n AUTOMATICKY - Integr\u00e1cia sa star\u00e1 o pripojenie k va\u0161ej zbernici KNX vykonan\u00edm skenovania br\u00e1ny.\n TUNNELING - Integr\u00e1cia sa pripoj\u00ed k va\u0161ej KNX zbernici prostredn\u00edctvom tunelovania.\n ROUTOVANIE - Integr\u00e1cia sa pripoj\u00ed k va\u0161ej KNX zbernici cez smerovanie." }, "manual_tunnel": { "data": { "host": "Hostite\u013e", "local_ip": "Lok\u00e1lna IP adresa Home Assistant-a", - "port": "Port" - } + "port": "Port", + "route_back": "Sp\u00e4tn\u00e1 trasa / re\u017eim NAT", + "tunneling_type": "Typ tunelovania KNX" + }, + "data_description": { + "host": "IP adresa tunelovacieho zariadenia KNX/IP.", + "local_ip": "Ak chcete pou\u017ei\u0165 automatick\u00e9 zis\u0165ovanie, nechajte pole pr\u00e1zdne.", + "port": "Port tunelovacieho zariadenia KNX/IP.", + "route_back": "Povo\u013ete, ak je v\u00e1\u0161 server tunelovania KNXnet/IP za NAT. Plat\u00ed len pre pripojenia UDP." + }, + "description": "Zadajte inform\u00e1cie o pripojen\u00ed v\u00e1\u0161ho tunelovacieho zariadenia." }, "routing": { "data": { "individual_address": "Individu\u00e1lna adresa", - "local_ip": "Lok\u00e1lna IP adresa Home Assistant-a" + "local_ip": "Lok\u00e1lna IP adresa Home Assistant-a", + "multicast_group": "Skupina multicast", + "multicast_port": "Multicast port", + "routing_secure": "Pou\u017eite KNX IP Secure" }, "data_description": { "individual_address": "Adresa KNX, ktor\u00fa bude pou\u017e\u00edva\u0165 Home Assistant, napr. `0.0.4`", @@ -44,10 +64,20 @@ "data": { "knxkeys_filename": "N\u00e1zov s\u00faboru v\u00e1\u0161ho s\u00faboru `.knxkeys` (vr\u00e1tane pr\u00edpony)", "knxkeys_password": "Heslo na de\u0161ifrovanie s\u00faboru `.knxkeys`" - } + }, + "data_description": { + "knxkeys_filename": "O\u010dak\u00e1va sa, \u017ee s\u00fabor n\u00e1jdete vo va\u0161om konfigura\u010dnom adres\u00e1ri v `.storage/knx/`.\n V opera\u010dnom syst\u00e9me Home Assistant to bude `/config/.storage/knx/`\n Pr\u00edklad: `my_project.knxkeys`", + "knxkeys_password": "Toto bolo nastaven\u00e9 pri exporte s\u00faboru z ETS." + }, + "description": "Zadajte inform\u00e1cie pre v\u00e1\u0161 s\u00fabor `.knxkeys`." }, "secure_routing_manual": { + "data": { + "backbone_key": "Backbone k\u013e\u00fa\u010d", + "sync_latency_tolerance": "Tolerancia latencie siete" + }, "data_description": { + "backbone_key": "Mo\u017eno ho vidie\u0165 v spr\u00e1ve \u201eBezpe\u010dnos\u0165\u201c projektu ETS. Napr. '00112233445566778899AABBCCDDEEFF'", "sync_latency_tolerance": "Predvolen\u00e1 hodnota je 1000." }, "description": "Pros\u00edm, zadajte svoje IP zabezpe\u010den\u00e9 inform\u00e1cie." @@ -58,32 +88,65 @@ "user_id": "ID pou\u017e\u00edvate\u013ea", "user_password": "Pou\u017e\u00edvate\u013esk\u00e9 heslo" }, + "data_description": { + "device_authentication": "Toto sa nastavuje na paneli \u201eIP\u201c rozhrania v ETS.", + "user_id": "Toto je \u010dasto \u010d\u00edslo tunela +1. Tak\u017ee \u201eTunnel 2\u201c bude ma\u0165 User-ID \u201e3\u201c.", + "user_password": "Heslo pre \u0161pecifick\u00e9 pripojenie tunela nastaven\u00e9 na paneli \u201eVlastnosti\u201c tunela v ETS." + }, "description": "Pros\u00edm, zadajte svoje IP zabezpe\u010den\u00e9 inform\u00e1cie." + }, + "tunnel": { + "data": { + "gateway": "Pripojenie tunela KNX" + }, + "description": "Vyberte br\u00e1nu zo zoznamu." } } }, "options": { "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", - "invalid_ip_address": "Neplatn\u00e1 adresa IPv4." + "file_not_found": "Zadan\u00fd s\u00fabor `.knxkeys` sa nena\u0161iel v ceste config/.storage/knx/", + "invalid_backbone_key": "Neplatn\u00fd k\u013e\u00fa\u010d backbone. O\u010dak\u00e1va sa 32 hexadecim\u00e1lnych \u010d\u00edsel.", + "invalid_individual_address": "Hodnota sa nezhoduje so vzorom pre individu\u00e1lnu adresu KNX.\n 'area.line.device'", + "invalid_ip_address": "Neplatn\u00e1 adresa IPv4.", + "invalid_signature": "Heslo na de\u0161ifrovanie s\u00faboru `.knxkeys` je nespr\u00e1vne.", + "no_router_discovered": "V sieti nebol n\u00e1jden\u00fd \u017eiadny router KNXnet/IP.", + "no_tunnel_discovered": "Vo va\u0161ej sieti sa nepodarilo n\u00e1js\u0165 tunelovac\u00ed server KNX.", + "unsupported_tunnel_type": "Vybran\u00fd typ tunelovania nie je podporovan\u00fd br\u00e1nou." }, "step": { "communication_settings": { + "data": { + "rate_limit": "Limit", + "state_updater": "Aktualiz\u00e1tor stavu" + }, "data_description": { - "rate_limit": "Maxim\u00e1lny po\u010det odch\u00e1dzaj\u00facich telegramov za sekundu.\n `0` pre deaktiv\u00e1ciu limitu. Odpor\u00fa\u010dan\u00e9: 0 alebo 20 a\u017e 40" + "rate_limit": "Maxim\u00e1lny po\u010det odch\u00e1dzaj\u00facich telegramov za sekundu.\n `0` pre deaktiv\u00e1ciu limitu. Odpor\u00fa\u010dan\u00e9: 0 alebo 20 a\u017e 40", + "state_updater": "Nastavi\u0165 predvolen\u00e9 pre \u010d\u00edtanie stavov zo zbernice KNX. Ke\u010f je vypnut\u00fd, Home Assistant nebude akt\u00edvne z\u00edskava\u0165 stavy ent\u00edt zo zbernice KNX. D\u00e1 sa prep\u00edsa\u0165 mo\u017enos\u0165ami entity `sync_state`." } }, "connection_type": { "data": { "connection_type": "Typ pripojenia KNX" - } + }, + "description": "Zadajte typ pripojenia, ktor\u00fd by sme mali pou\u017ei\u0165 pre va\u0161e pripojenie KNX.\n AUTOMATICKY - Integr\u00e1cia sa star\u00e1 o pripojenie k va\u0161ej zbernici KNX vykonan\u00edm skenovania br\u00e1ny.\n TUNNELING - Integr\u00e1cia sa pripoj\u00ed k va\u0161ej KNX zbernici prostredn\u00edctvom tunelovania.\n ROUTOVANIE - Integr\u00e1cia sa pripoj\u00ed k va\u0161ej KNX zbernici cez smerovanie." }, "manual_tunnel": { "data": { "host": "Hostite\u013e", "local_ip": "Lok\u00e1lna IP adresa Home Assistant-a", - "port": "Port" - } + "port": "Port", + "route_back": "Sp\u00e4tn\u00e1 trasa / re\u017eim NAT", + "tunneling_type": "Typ tunelovania KNX" + }, + "data_description": { + "host": "IP adresa tunelovacieho zariadenia KNX/IP.", + "local_ip": "Ak chcete pou\u017ei\u0165 automatick\u00e9 zis\u0165ovanie, nechajte pole pr\u00e1zdne.", + "port": "Port tunelovacieho zariadenia KNX/IP.", + "route_back": "Povo\u013ete, ak je v\u00e1\u0161 server tunelovania KNXnet/IP za NAT. Plat\u00ed len pre pripojenia UDP." + }, + "description": "Zadajte inform\u00e1cie o pripojen\u00ed v\u00e1\u0161ho tunelovacieho zariadenia." }, "options_init": { "menu_options": { @@ -94,7 +157,10 @@ "routing": { "data": { "individual_address": "Individu\u00e1lna adresa", - "local_ip": "Lok\u00e1lna IP adresa Home Assistant-a" + "local_ip": "Lok\u00e1lna IP adresa Home Assistant-a", + "multicast_group": "Skupina multicast", + "multicast_port": "Multicast port", + "routing_secure": "Pou\u017eite KNX IP Secure" }, "data_description": { "individual_address": "Adresa KNX, ktor\u00fa bude pou\u017e\u00edva\u0165 Home Assistant, napr. `0.0.4`", @@ -114,10 +180,20 @@ "data": { "knxkeys_filename": "N\u00e1zov s\u00faboru v\u00e1\u0161ho s\u00faboru `.knxkeys` (vr\u00e1tane pr\u00edpony)", "knxkeys_password": "Heslo na de\u0161ifrovanie s\u00faboru `.knxkeys`" - } + }, + "data_description": { + "knxkeys_filename": "O\u010dak\u00e1va sa, \u017ee s\u00fabor n\u00e1jdete vo va\u0161om konfigura\u010dnom adres\u00e1ri v `.storage/knx/`.\n V opera\u010dnom syst\u00e9me Home Assistant to bude `/config/.storage/knx/`\n Pr\u00edklad: `my_project.knxkeys`", + "knxkeys_password": "Toto bolo nastaven\u00e9 pri exporte s\u00faboru z ETS." + }, + "description": "Zadajte inform\u00e1cie pre v\u00e1\u0161 s\u00fabor `.knxkeys`." }, "secure_routing_manual": { + "data": { + "backbone_key": "Backbone k\u013e\u00fa\u010d", + "sync_latency_tolerance": "Tolerancia latencie siete" + }, "data_description": { + "backbone_key": "Mo\u017eno ho vidie\u0165 v spr\u00e1ve \u201eBezpe\u010dnos\u0165\u201c projektu ETS. Napr. '00112233445566778899AABBCCDDEEFF'", "sync_latency_tolerance": "Predvolen\u00e1 hodnota je 1000." }, "description": "Pros\u00edm, zadajte svoje IP zabezpe\u010den\u00e9 inform\u00e1cie." @@ -128,7 +204,18 @@ "user_id": "ID pou\u017e\u00edvate\u013ea", "user_password": "Pou\u017e\u00edvate\u013esk\u00e9 heslo" }, + "data_description": { + "device_authentication": "Toto sa nastavuje na paneli \u201eIP\u201c rozhrania v ETS.", + "user_id": "Toto je \u010dasto \u010d\u00edslo tunela +1. Tak\u017ee \u201eTunnel 2\u201c bude ma\u0165 User-ID \u201e3\u201c.", + "user_password": "Heslo pre \u0161pecifick\u00e9 pripojenie tunela nastaven\u00e9 na paneli \u201eVlastnosti\u201c tunela v ETS." + }, "description": "Pros\u00edm, zadajte svoje IP zabezpe\u010den\u00e9 inform\u00e1cie." + }, + "tunnel": { + "data": { + "gateway": "Pripojenie tunela KNX" + }, + "description": "Vyberte br\u00e1nu zo zoznamu." } } } diff --git a/homeassistant/components/knx/translations/sv.json b/homeassistant/components/knx/translations/sv.json index 9ee17f9dec2..51917ee9bce 100644 --- a/homeassistant/components/knx/translations/sv.json +++ b/homeassistant/components/knx/translations/sv.json @@ -50,12 +50,6 @@ }, "description": "V\u00e4nligen ange informationen f\u00f6r din `.knxkeys`-fil." }, - "secure_tunneling": { - "description": "V\u00e4lj hur du vill konfigurera KNX/IP Secure.", - "menu_options": { - "secure_knxkeys": "Anv\u00e4nd en fil `.knxkeys` som inneh\u00e5ller s\u00e4kra IP-nycklar." - } - }, "tunnel": { "data": { "gateway": "KNX-tunnelanslutning" diff --git a/homeassistant/components/knx/translations/tr.json b/homeassistant/components/knx/translations/tr.json index e1ddb01e317..86d654adb09 100644 --- a/homeassistant/components/knx/translations/tr.json +++ b/homeassistant/components/knx/translations/tr.json @@ -50,12 +50,6 @@ }, "description": "L\u00fctfen `.knxkeys` dosyan\u0131z i\u00e7in bilgileri girin." }, - "secure_tunneling": { - "description": "KNX/IP Secure'u nas\u0131l yap\u0131land\u0131rmak istedi\u011finizi se\u00e7in.", - "menu_options": { - "secure_knxkeys": "IP g\u00fcvenli anahtarlar\u0131 i\u00e7eren bir \".knxkeys\" dosyas\u0131 kullan\u0131n" - } - }, "tunnel": { "data": { "gateway": "KNX T\u00fcnel Ba\u011flant\u0131s\u0131" diff --git a/homeassistant/components/knx/translations/zh-Hant.json b/homeassistant/components/knx/translations/zh-Hant.json index bd11d7f78a0..99ad9f9b490 100644 --- a/homeassistant/components/knx/translations/zh-Hant.json +++ b/homeassistant/components/knx/translations/zh-Hant.json @@ -12,7 +12,8 @@ "invalid_ip_address": "IPv4 \u4f4d\u5740\u7121\u6548\u3002", "invalid_signature": "\u52a0\u5bc6 `.knxkeys` \u6a94\u6848\u5bc6\u78bc\u932f\u8aa4\u3002", "no_router_discovered": "\u7db2\u8def\u4e0a\u627e\u4e0d\u5230 KNXnet/IP \u8def\u7531\u5668\u3002", - "no_tunnel_discovered": "\u7db2\u8def\u4e0a\u627e\u4e0d\u5230 KNX \u901a\u9053\u4f3a\u670d\u5668\u3002" + "no_tunnel_discovered": "\u7db2\u8def\u4e0a\u627e\u4e0d\u5230 KNX \u901a\u9053\u4f3a\u670d\u5668\u3002", + "unsupported_tunnel_type": "\u9598\u9053\u5668\u4e0d\u652f\u63f4\u6240\u9078\u64c7\u7684\u901a\u9053\u985e\u578b\u3002" }, "step": { "connection_type": { @@ -94,13 +95,6 @@ }, "description": "\u8acb\u8f38\u5165 IP \u52a0\u5bc6\u8cc7\u8a0a\u3002" }, - "secure_tunneling": { - "description": "\u9078\u64c7\u5982\u4f55\u8a2d\u5b9a KNX/IP \u52a0\u5bc6\u3002", - "menu_options": { - "secure_knxkeys": "\u4f7f\u7528\u5305\u542b IP \u52a0\u5bc6\u91d1\u8000\u7684 knxkeys \u6a94\u6848", - "secure_tunnel_manual": "\u624b\u52d5\u8a2d\u5b9a IP \u52a0\u5bc6\u91d1\u8000" - } - }, "tunnel": { "data": { "gateway": "KNX \u901a\u9053\u9023\u7dda" @@ -118,7 +112,8 @@ "invalid_ip_address": "IPv4 \u4f4d\u5740\u7121\u6548\u3002", "invalid_signature": "\u52a0\u5bc6 `.knxkeys` \u6a94\u6848\u5bc6\u78bc\u932f\u8aa4\u3002", "no_router_discovered": "\u7db2\u8def\u4e0a\u627e\u4e0d\u5230 KNXnet/IP \u8def\u7531\u5668\u3002", - "no_tunnel_discovered": "\u7db2\u8def\u4e0a\u627e\u4e0d\u5230 KNX \u901a\u9053\u4f3a\u670d\u5668\u3002" + "no_tunnel_discovered": "\u7db2\u8def\u4e0a\u627e\u4e0d\u5230 KNX \u901a\u9053\u4f3a\u670d\u5668\u3002", + "unsupported_tunnel_type": "\u9598\u9053\u5668\u4e0d\u652f\u63f4\u6240\u9078\u64c7\u7684\u901a\u9053\u985e\u578b\u3002" }, "step": { "communication_settings": { @@ -216,13 +211,6 @@ }, "description": "\u8acb\u8f38\u5165 IP \u52a0\u5bc6\u8cc7\u8a0a\u3002" }, - "secure_tunneling": { - "description": "\u9078\u64c7\u5982\u4f55\u8a2d\u5b9a KNX/IP \u52a0\u5bc6\u3002", - "menu_options": { - "secure_knxkeys": "\u4f7f\u7528\u5305\u542b IP \u52a0\u5bc6\u91d1\u8000\u7684 knxkeys \u6a94\u6848", - "secure_tunnel_manual": "\u624b\u52d5\u8a2d\u5b9a IP \u52a0\u5bc6\u91d1\u8000" - } - }, "tunnel": { "data": { "gateway": "KNX \u901a\u9053\u9023\u7dda" diff --git a/homeassistant/components/kodi/translations/pt.json b/homeassistant/components/kodi/translations/pt.json index a20906321aa..e71a2c360a0 100644 --- a/homeassistant/components/kodi/translations/pt.json +++ b/homeassistant/components/kodi/translations/pt.json @@ -2,12 +2,12 @@ "config": { "abort": { "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, @@ -23,7 +23,7 @@ }, "user": { "data": { - "host": "Servidor", + "host": "Endere\u00e7o", "port": "Porta", "ssl": "Utiliza um certificado SSL" }, diff --git a/homeassistant/components/kodi/translations/sk.json b/homeassistant/components/kodi/translations/sk.json index be6f0128d39..1d604a7c425 100644 --- a/homeassistant/components/kodi/translations/sk.json +++ b/homeassistant/components/kodi/translations/sk.json @@ -4,6 +4,7 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie", + "no_uuid": "In\u0161tancia Kodi nem\u00e1 jedine\u010dn\u00e9 ID. S najv\u00e4\u010d\u0161ou pravdepodobnos\u0165ou je to kv\u00f4li starej verzii Kodi (17.x alebo ni\u017e\u0161ej). Integr\u00e1ciu m\u00f4\u017eete nakonfigurova\u0165 manu\u00e1lne alebo inovova\u0165 na nov\u0161iu verziu Kodi.", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "error": { @@ -17,9 +18,11 @@ "data": { "password": "Heslo", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" - } + }, + "description": "Zadajte svoje pou\u017e\u00edvate\u013esk\u00e9 meno a heslo Kodi. Tie n\u00e1jdete v Syst\u00e9m/Nastavenia/Sie\u0165/Slu\u017eby." }, "discovery_confirm": { + "description": "Chcete prida\u0165 Kodi (`{name}`) do Home Assistant?", "title": "Objaven\u00e9 Kodi" }, "user": { @@ -27,12 +30,14 @@ "host": "Hostite\u013e", "port": "Port", "ssl": "Pou\u017e\u00edva SSL certifik\u00e1t" - } + }, + "description": "Inform\u00e1cie o pripojen\u00ed Kodi. Nezabudnite povoli\u0165 \u201ePovoli\u0165 ovl\u00e1danie Kodi cez HTTP\u201c v \u010dasti Syst\u00e9m/Nastavenia/Sie\u0165/Slu\u017eby." }, "ws_port": { "data": { "ws_port": "Port" - } + }, + "description": "Port WebSocket (niekedy naz\u00fdvan\u00fd TCP port v Kodi). Ak sa chcete pripoji\u0165 cez WebSocket, mus\u00edte povoli\u0165 \u201ePovoli\u0165 programom ... ovl\u00e1da\u0165 Kodi\u201c v \u010dasti Syst\u00e9m/Nastavenia/Sie\u0165/Slu\u017eby. Ak WebSocket nie je povolen\u00fd, odstr\u00e1\u0148te port a nechajte ho pr\u00e1zdny." } } }, diff --git a/homeassistant/components/konnected/panel.py b/homeassistant/components/konnected/panel.py index 2c7b57449b7..61c77f5a7de 100644 --- a/homeassistant/components/konnected/panel.py +++ b/homeassistant/components/konnected/panel.py @@ -145,8 +145,10 @@ class AlarmPanel: self.connect_attempts = 0 self.connected = True _LOGGER.info( - "Set up Konnected device %s. Open http://%s:%s in a " - "web browser to view device status", + ( + "Set up Konnected device %s. Open http://%s:%s in a " + "web browser to view device status" + ), self.device_id, self.host, self.port, diff --git a/homeassistant/components/konnected/sensor.py b/homeassistant/components/konnected/sensor.py index 7bfa1fad446..89436913606 100644 --- a/homeassistant/components/konnected/sensor.py +++ b/homeassistant/components/konnected/sensor.py @@ -14,7 +14,7 @@ from homeassistant.const import ( CONF_TYPE, CONF_ZONE, PERCENTAGE, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect @@ -27,7 +27,7 @@ SENSOR_TYPES: dict[str, SensorEntityDescription] = { "temperature": SensorEntityDescription( key="temperature", name="Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, ), "humidity": SensorEntityDescription( diff --git a/homeassistant/components/konnected/translations/de.json b/homeassistant/components/konnected/translations/de.json index 82c99839f25..f18e34d7351 100644 --- a/homeassistant/components/konnected/translations/de.json +++ b/homeassistant/components/konnected/translations/de.json @@ -85,7 +85,7 @@ "options_misc": { "data": { "api_host": "API-Host-URL \u00fcberschreiben", - "blink": "LED Panel blinkt beim senden von Status\u00e4nderungen", + "blink": "LED Panel blinkt beim Senden von Status\u00e4nderungen", "discovery": "Reagieren auf Suchanfragen in deinem Netzwerk", "override_api_host": "\u00dcberschreibe die Standard-Host-Panel-URL der Home Assistant-API" }, diff --git a/homeassistant/components/konnected/translations/en_GB.json b/homeassistant/components/konnected/translations/en-GB.json similarity index 100% rename from homeassistant/components/konnected/translations/en_GB.json rename to homeassistant/components/konnected/translations/en-GB.json diff --git a/homeassistant/components/konnected/translations/ko.json b/homeassistant/components/konnected/translations/ko.json index eb7fa3de4e0..58e7c1d3c84 100644 --- a/homeassistant/components/konnected/translations/ko.json +++ b/homeassistant/components/konnected/translations/ko.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", "not_konn_panel": "\uc778\uc2dd\ub41c Konnected.io \uae30\uae30\uac00 \uc544\ub2d9\ub2c8\ub2e4", "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" }, diff --git a/homeassistant/components/konnected/translations/pl.json b/homeassistant/components/konnected/translations/pl.json index 3eb24ac2cab..ee5f0f5f487 100644 --- a/homeassistant/components/konnected/translations/pl.json +++ b/homeassistant/components/konnected/translations/pl.json @@ -88,7 +88,7 @@ }, "options_misc": { "data": { - "api_host": "Zast\u0119powanie adresu URL hosta API", + "api_host": "Zast\u0119powanie adresu API", "blink": "Miganie diody LED panelu podczas wysy\u0142ania zmiany stanu", "discovery": "Odpowiadaj na \u017c\u0105dania wykrywania w Twojej sieci", "override_api_host": "Zast\u0105p domy\u015blny adres URL API Home Assistanta" diff --git a/homeassistant/components/konnected/translations/pt.json b/homeassistant/components/konnected/translations/pt.json index 2ba94342093..0943673a23a 100644 --- a/homeassistant/components/konnected/translations/pt.json +++ b/homeassistant/components/konnected/translations/pt.json @@ -6,7 +6,7 @@ "unknown": "Erro inesperado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "user": { diff --git a/homeassistant/components/konnected/translations/sk.json b/homeassistant/components/konnected/translations/sk.json index d84d42532f9..a93e367388a 100644 --- a/homeassistant/components/konnected/translations/sk.json +++ b/homeassistant/components/konnected/translations/sk.json @@ -12,13 +12,19 @@ }, "step": { "confirm": { - "description": "Model: {model}\n ID: {id}\n Hostite\u013e: {host}\n Port: {port} \n\n Spr\u00e1vanie IO a panela m\u00f4\u017eete nakonfigurova\u0165 v nastaveniach panela Pripojen\u00fd alarm." + "description": "Model: {model}\n ID: {id}\n Hostite\u013e: {host}\n Port: {port} \n\n Spr\u00e1vanie IO a panela m\u00f4\u017eete nakonfigurova\u0165 v nastaveniach panela Pripojen\u00fd alarm.", + "title": "Konnected zariadenie pripraven\u00e9" + }, + "import_confirm": { + "description": "Konnected poplachov\u00fd panel s ID {id} bol objaven\u00fd v configuration.yaml. Tento postup v\u00e1m umo\u017en\u00ed importova\u0165 ho do polo\u017eky konfigur\u00e1cie.", + "title": "Importujte Konnected zariadenie" }, "user": { "data": { "host": "IP adresa", "port": "Port" - } + }, + "description": "Zadajte inform\u00e1cie o hostite\u013eovi pre v\u00e1\u0161 Konnected panel." } } }, @@ -27,7 +33,11 @@ "not_konn_panel": "Nie je rozpoznan\u00e9 zariadenie Konnected.io" }, "error": { - "bad_host": "Neplatn\u00e1 adresa URL hostite\u013ea API Override" + "bad_host": "Neplatn\u00e1 adresa URL hostite\u013ea API Override", + "few": "Pr\u00e1zdnych", + "many": "Pr\u00e1zdnych", + "one": "Pr\u00e1zdny", + "other": "Pr\u00e1zdny" }, "step": { "options_binary": { @@ -42,9 +52,11 @@ "options_digital": { "data": { "name": "N\u00e1zov (volite\u013en\u00fd)", + "poll_interval": "Interval po\u017eiadaviek (v min\u00fatach)", "type": "Typ sn\u00edma\u010da" }, - "description": "mo\u017enosti {zone}" + "description": "mo\u017enosti {zone}", + "title": "Konfigur\u00e1cia digit\u00e1lneho sn\u00edma\u010da" }, "options_io": { "data": { @@ -54,21 +66,47 @@ "4": "Z\u00f3na 4", "5": "Z\u00f3na 5", "6": "Z\u00f3na 6", - "7": "Z\u00f3na 7" + "7": "Z\u00f3na 7", + "out": "OUT" }, + "description": "Objaven\u00fd {model} na {host}. Ni\u017e\u0161ie vyberte z\u00e1kladn\u00fa konfigur\u00e1ciu ka\u017ed\u00e9ho I/O \u2013 v z\u00e1vislosti od I/O m\u00f4\u017ee umo\u017e\u0148ova\u0165 bin\u00e1rne sn\u00edma\u010de (kontakt otvoren\u00e9/zatvoren\u00e9), digit\u00e1lne sn\u00edma\u010de (dht a ds18b20) alebo prep\u00ednate\u013en\u00e9 v\u00fdstupy. Podrobn\u00e9 mo\u017enosti budete m\u00f4c\u0165 nakonfigurova\u0165 v \u010fal\u0161\u00edch krokoch.", "title": "Konfigur\u00e1cia I/O" }, + "options_io_ext": { + "data": { + "10": "Z\u00f3na 10", + "11": "Z\u00f3na 11", + "12": "Z\u00f3na 12", + "8": "Z\u00f3na 8", + "9": "Z\u00f3na 9", + "alarm1": "ALARM1", + "alarm2_out2": "OUT2/ALARM2", + "out1": "OUT1" + }, + "description": "Ni\u017e\u0161ie vyberte konfigur\u00e1ciu zost\u00e1vaj\u00facich I/O. Podrobn\u00e9 mo\u017enosti budete m\u00f4c\u0165 nakonfigurova\u0165 v \u010fal\u0161\u00edch krokoch.", + "title": "Nakonfigurujte roz\u0161\u00edren\u00e9 I/O" + }, "options_misc": { "data": { - "api_host": "Prep\u00edsanie adresy URL hostite\u013ea API" - } + "api_host": "Prep\u00edsanie adresy URL hostite\u013ea API", + "blink": "LED panel blik\u00e1 pri zmene stavu odosielania", + "discovery": "Reagovanie na po\u017eiadavky na zis\u0165ovanie vo va\u0161ej sieti", + "override_api_host": "Prep\u00edsanie predvolenej adresy URL hostite\u013esk\u00e9ho panela rozhrania API aplik\u00e1cie Home Assistant" + }, + "description": "Vyberte po\u017eadovan\u00e9 spr\u00e1vanie pre v\u00e1\u0161 panel", + "title": "Konfigur\u00e1cia r\u00f4zne" }, "options_switch": { "data": { + "activation": "V\u00fdstup, ke\u010f je zapnut\u00fd", "momentary": "Trvanie impulzu (ms)", + "more_states": "Nakonfigurujte \u010fal\u0161ie stavy pre t\u00fato z\u00f3nu", "name": "N\u00e1zov (volite\u013en\u00fd)", - "pause": "Pauza medzi impulzmi (ms)" - } + "pause": "Pauza medzi impulzmi (ms)", + "repeat": "Po\u010det opakovan\u00ed (-1 = nekone\u010dno)" + }, + "description": "mo\u017enosti {zone}: state {state}", + "title": "Konfigur\u00e1cia prep\u00ednate\u013en\u00e9ho v\u00fdstupu" } } } diff --git a/homeassistant/components/konnected/translations/zh-Hant.json b/homeassistant/components/konnected/translations/zh-Hant.json index fbce1455270..76ef49ab07c 100644 --- a/homeassistant/components/konnected/translations/zh-Hant.json +++ b/homeassistant/components/konnected/translations/zh-Hant.json @@ -40,19 +40,19 @@ "data": { "inverse": "\u53cd\u8f49\u958b\u555f/\u95dc\u9589\u72c0\u614b", "name": "\u540d\u7a31", - "type": "\u4e8c\u9032\u4f4d\u611f\u61c9\u5668\u985e\u5225" + "type": "\u4e8c\u9032\u4f4d\u611f\u6e2c\u5668\u985e\u5225" }, "description": "{zone}\u9078\u9805", - "title": "\u8a2d\u5b9a\u4e8c\u9032\u4f4d\u611f\u61c9\u5668" + "title": "\u8a2d\u5b9a\u4e8c\u9032\u4f4d\u611f\u6e2c\u5668" }, "options_digital": { "data": { "name": "\u540d\u7a31", "poll_interval": "\u66f4\u65b0\u9593\u8ddd\uff08\u5206\u9418\uff09", - "type": "\u611f\u61c9\u5668\u985e\u5225" + "type": "\u611f\u6e2c\u5668\u985e\u5225" }, "description": "{zone}\u9078\u9805", - "title": "\u8a2d\u5b9a\u6578\u4f4d\u611f\u61c9\u5668" + "title": "\u8a2d\u5b9a\u6578\u4f4d\u611f\u6e2c\u5668" }, "options_io": { "data": { @@ -65,7 +65,7 @@ "7": "\u5340\u57df 7", "out": "OUT" }, - "description": "\u65bc {host} \u767c\u73fe {model}\u3002\u8acb\u65bc\u4e0b\u65b9\u6bcf\u4e00\u500b I/O \u9078\u64c7\u57fa\u672c\u8a2d\u5b9a - \u96a8\u8457 I/O \u4e0d\u540c\uff0c\u53ef\u5141\u8a31\u4e8c\u9032\u4f4d\u611f\u61c9\u5668\uff08\u958b\u555f/\u95dc\u9589\u72c0\u614b\uff09\u3001\u6578\u4f4d\u611f\u61c9\u5668\uff08DHT \u53ca ds18b20\uff09\uff0c\u6216\u8005\u53ef\u5207\u63db\u8f38\u51fa\u3002\u53ef\u4ee5\u65bc\u4e0b\u4e00\u6b65\u8a2d\u5b9a\u8a73\u7d30\u9078\u9805\u3002", + "description": "\u65bc {host} \u767c\u73fe {model}\u3002\u8acb\u65bc\u4e0b\u65b9\u6bcf\u4e00\u500b I/O \u9078\u64c7\u57fa\u672c\u8a2d\u5b9a - \u96a8\u8457 I/O \u4e0d\u540c\uff0c\u53ef\u5141\u8a31\u4e8c\u9032\u4f4d\u611f\u6e2c\u5668\uff08\u958b\u555f/\u95dc\u9589\u72c0\u614b\uff09\u3001\u6578\u4f4d\u611f\u6e2c\u5668\uff08DHT \u53ca ds18b20\uff09\uff0c\u6216\u8005\u53ef\u5207\u63db\u8f38\u51fa\u3002\u53ef\u4ee5\u65bc\u4e0b\u4e00\u6b65\u8a2d\u5b9a\u8a73\u7d30\u9078\u9805\u3002", "title": "\u8a2d\u5b9a I/O" }, "options_io_ext": { diff --git a/homeassistant/components/kostal_plenticore/helper.py b/homeassistant/components/kostal_plenticore/helper.py index ee684b68974..52ff3a7480b 100644 --- a/homeassistant/components/kostal_plenticore/helper.py +++ b/homeassistant/components/kostal_plenticore/helper.py @@ -6,7 +6,7 @@ from collections import defaultdict from collections.abc import Callable from datetime import datetime, timedelta import logging -from typing import Any +from typing import Any, TypeVar, cast from aiohttp.client_exceptions import ClientError from kostal.plenticore import ( @@ -26,6 +26,7 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from .const import DOMAIN _LOGGER = logging.getLogger(__name__) +_DataT = TypeVar("_DataT") class Plenticore: @@ -154,7 +155,7 @@ class DataUpdateCoordinatorMixin: return True -class PlenticoreUpdateCoordinator(DataUpdateCoordinator): +class PlenticoreUpdateCoordinator(DataUpdateCoordinator[_DataT]): """Base implementation of DataUpdateCoordinator for Plenticore data.""" def __init__( @@ -192,7 +193,9 @@ class PlenticoreUpdateCoordinator(DataUpdateCoordinator): self._fetch[module_id].remove(data_id) -class ProcessDataUpdateCoordinator(PlenticoreUpdateCoordinator): +class ProcessDataUpdateCoordinator( + PlenticoreUpdateCoordinator[dict[str, dict[str, str]]] +): """Implementation of PlenticoreUpdateCoordinator for process data.""" async def _async_update_data(self) -> dict[str, dict[str, str]]: @@ -214,7 +217,7 @@ class ProcessDataUpdateCoordinator(PlenticoreUpdateCoordinator): class SettingDataUpdateCoordinator( - PlenticoreUpdateCoordinator, DataUpdateCoordinatorMixin + PlenticoreUpdateCoordinator[dict[str, dict[str, str]]], DataUpdateCoordinatorMixin ): """Implementation of PlenticoreUpdateCoordinator for settings data.""" @@ -230,7 +233,7 @@ class SettingDataUpdateCoordinator( return fetched_data -class PlenticoreSelectUpdateCoordinator(DataUpdateCoordinator): +class PlenticoreSelectUpdateCoordinator(DataUpdateCoordinator[_DataT]): """Base implementation of DataUpdateCoordinator for Plenticore data.""" def __init__( @@ -249,10 +252,12 @@ class PlenticoreSelectUpdateCoordinator(DataUpdateCoordinator): update_interval=update_inverval, ) # data ids to poll - self._fetch: dict[str, list[str]] = defaultdict(list) + self._fetch: dict[str, list[str | list[str]]] = defaultdict(list) self._plenticore = plenticore - def start_fetch_data(self, module_id: str, data_id: str, all_options: str) -> None: + def start_fetch_data( + self, module_id: str, data_id: str, all_options: list[str] + ) -> None: """Start fetching the given data (module-id and entry-id).""" self._fetch[module_id].append(data_id) self._fetch[module_id].append(all_options) @@ -264,14 +269,17 @@ class PlenticoreSelectUpdateCoordinator(DataUpdateCoordinator): async_call_later(self.hass, 2, force_refresh) - def stop_fetch_data(self, module_id: str, data_id: str, all_options: str) -> None: + def stop_fetch_data( + self, module_id: str, data_id: str, all_options: list[str] + ) -> None: """Stop fetching the given data (module-id and entry-id).""" self._fetch[module_id].remove(all_options) self._fetch[module_id].remove(data_id) class SelectDataUpdateCoordinator( - PlenticoreSelectUpdateCoordinator, DataUpdateCoordinatorMixin + PlenticoreSelectUpdateCoordinator[dict[str, dict[str, str]]], + DataUpdateCoordinatorMixin, ): """Implementation of PlenticoreUpdateCoordinator for select data.""" @@ -287,11 +295,11 @@ class SelectDataUpdateCoordinator( async def _async_get_current_option( self, - module_id: dict[str, list[str]], + module_id: dict[str, list[str | list[str]]], ) -> dict[str, dict[str, str]]: """Get current option.""" for mid, pids in module_id.items(): - all_options = pids[1] + all_options = cast(list[str], pids[1]) for all_option in all_options: if all_option == "None" or not ( val := await self.async_read_data(mid, all_option) @@ -299,10 +307,10 @@ class SelectDataUpdateCoordinator( continue for option in val.values(): if option[all_option] == "1": - fetched = {mid: {pids[0]: all_option}} + fetched = {mid: {cast(str, pids[0]): all_option}} return fetched - return {mid: {pids[0]: "None"}} + return {mid: {cast(str, pids[0]): "None"}} return {} diff --git a/homeassistant/components/kostal_plenticore/number.py b/homeassistant/components/kostal_plenticore/number.py index 69fba631b34..2b0726e6255 100644 --- a/homeassistant/components/kostal_plenticore/number.py +++ b/homeassistant/components/kostal_plenticore/number.py @@ -1,7 +1,6 @@ """Platform for Kostal Plenticore numbers.""" from __future__ import annotations -from abc import ABC from dataclasses import dataclass from datetime import timedelta import logging @@ -9,13 +8,13 @@ import logging from kostal.plenticore import SettingsData from homeassistant.components.number import ( + NumberDeviceClass, NumberEntity, NumberEntityDescription, NumberMode, ) -from homeassistant.components.sensor import SensorDeviceClass from homeassistant.config_entries import ConfigEntry -from homeassistant.const import PERCENTAGE, POWER_WATT +from homeassistant.const import PERCENTAGE, UnitOfPower from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo, EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -62,11 +61,11 @@ NUMBER_SETTINGS_DATA = [ ), PlenticoreNumberEntityDescription( key="battery_min_home_consumption", - device_class=SensorDeviceClass.POWER, + device_class=NumberDeviceClass.POWER, entity_category=EntityCategory.CONFIG, entity_registry_enabled_default=False, name="Battery min Home Consumption", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, native_max_value=38000, native_min_value=50, native_step=1, @@ -130,11 +129,12 @@ async def async_setup_entry( async_add_entities(entities) -class PlenticoreDataNumber(CoordinatorEntity, NumberEntity, ABC): +class PlenticoreDataNumber( + CoordinatorEntity[SettingDataUpdateCoordinator], NumberEntity +): """Representation of a Kostal Plenticore Number entity.""" entity_description: PlenticoreNumberEntityDescription - coordinator: SettingDataUpdateCoordinator def __init__( self, diff --git a/homeassistant/components/kostal_plenticore/select.py b/homeassistant/components/kostal_plenticore/select.py index 5f0bb8100c7..cc259bcaf35 100644 --- a/homeassistant/components/kostal_plenticore/select.py +++ b/homeassistant/components/kostal_plenticore/select.py @@ -42,7 +42,6 @@ SELECT_SETTINGS_DATA = [ "Battery:SmartBatteryControl:Enable", "Battery:TimeControl:Enable", ], - device_class="kostal_plenticore__battery", ) ] @@ -88,7 +87,9 @@ async def async_setup_entry( async_add_entities(entities) -class PlenticoreDataSelect(CoordinatorEntity, SelectEntity): +class PlenticoreDataSelect( + CoordinatorEntity[SelectDataUpdateCoordinator], SelectEntity +): """Representation of a Plenticore Select.""" _attr_entity_category = EntityCategory.CONFIG diff --git a/homeassistant/components/kostal_plenticore/sensor.py b/homeassistant/components/kostal_plenticore/sensor.py index 29b42e88b50..f919d15d6b3 100644 --- a/homeassistant/components/kostal_plenticore/sensor.py +++ b/homeassistant/components/kostal_plenticore/sensor.py @@ -15,11 +15,11 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - ELECTRIC_CURRENT_AMPERE, - ELECTRIC_POTENTIAL_VOLT, - ENERGY_KILO_WATT_HOUR, PERCENTAGE, - POWER_WATT, + UnitOfElectricCurrent, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfPower, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo @@ -59,7 +59,7 @@ SENSOR_PROCESS_DATA = [ module_id="devices:local", key="Dc_P", name="Solar Power", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, entity_registry_enabled_default=True, state_class=SensorStateClass.MEASUREMENT, @@ -69,7 +69,7 @@ SENSOR_PROCESS_DATA = [ module_id="devices:local", key="Grid_P", name="Grid Power", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, entity_registry_enabled_default=True, state_class=SensorStateClass.MEASUREMENT, @@ -79,7 +79,7 @@ SENSOR_PROCESS_DATA = [ module_id="devices:local", key="HomeBat_P", name="Home Power from Battery", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, formatter="format_round", ), @@ -87,7 +87,7 @@ SENSOR_PROCESS_DATA = [ module_id="devices:local", key="HomeGrid_P", name="Home Power from Grid", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, formatter="format_round", @@ -96,7 +96,7 @@ SENSOR_PROCESS_DATA = [ module_id="devices:local", key="HomeOwn_P", name="Home Power from Own", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, formatter="format_round", @@ -105,7 +105,7 @@ SENSOR_PROCESS_DATA = [ module_id="devices:local", key="HomePv_P", name="Home Power from PV", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, formatter="format_round", @@ -114,7 +114,7 @@ SENSOR_PROCESS_DATA = [ module_id="devices:local", key="Home_P", name="Home Power", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, formatter="format_round", @@ -123,7 +123,7 @@ SENSOR_PROCESS_DATA = [ module_id="devices:local:ac", key="P", name="AC Power", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, entity_registry_enabled_default=True, state_class=SensorStateClass.MEASUREMENT, @@ -133,7 +133,7 @@ SENSOR_PROCESS_DATA = [ module_id="devices:local:pv1", key="P", name="DC1 Power", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, formatter="format_round", @@ -142,7 +142,7 @@ SENSOR_PROCESS_DATA = [ module_id="devices:local:pv1", key="U", name="DC1 Voltage", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, formatter="format_round", @@ -151,7 +151,7 @@ SENSOR_PROCESS_DATA = [ module_id="devices:local:pv1", key="I", name="DC1 Current", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, formatter="format_float", @@ -160,7 +160,7 @@ SENSOR_PROCESS_DATA = [ module_id="devices:local:pv2", key="P", name="DC2 Power", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, formatter="format_round", @@ -169,7 +169,7 @@ SENSOR_PROCESS_DATA = [ module_id="devices:local:pv2", key="U", name="DC2 Voltage", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, formatter="format_round", @@ -178,7 +178,7 @@ SENSOR_PROCESS_DATA = [ module_id="devices:local:pv2", key="I", name="DC2 Current", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, formatter="format_float", @@ -187,7 +187,7 @@ SENSOR_PROCESS_DATA = [ module_id="devices:local:pv3", key="P", name="DC3 Power", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, formatter="format_round", @@ -196,7 +196,7 @@ SENSOR_PROCESS_DATA = [ module_id="devices:local:pv3", key="U", name="DC3 Voltage", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, formatter="format_round", @@ -205,7 +205,7 @@ SENSOR_PROCESS_DATA = [ module_id="devices:local:pv3", key="I", name="DC3 Current", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, formatter="format_float", @@ -214,7 +214,7 @@ SENSOR_PROCESS_DATA = [ module_id="devices:local", key="PV2Bat_P", name="PV to Battery Power", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, formatter="format_round", @@ -238,7 +238,7 @@ SENSOR_PROCESS_DATA = [ module_id="devices:local:battery", key="P", name="Battery Power", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, formatter="format_round", @@ -321,7 +321,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyHome:Day", name="Home Consumption Day", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, formatter="format_energy", ), @@ -329,7 +329,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyHome:Month", name="Home Consumption Month", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, formatter="format_energy", ), @@ -337,7 +337,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyHome:Year", name="Home Consumption Year", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, formatter="format_energy", ), @@ -345,7 +345,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyHome:Total", name="Home Consumption Total", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, formatter="format_energy", @@ -354,7 +354,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyHomeBat:Day", name="Home Consumption from Battery Day", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, formatter="format_energy", ), @@ -362,7 +362,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyHomeBat:Month", name="Home Consumption from Battery Month", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, formatter="format_energy", ), @@ -370,7 +370,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyHomeBat:Year", name="Home Consumption from Battery Year", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, formatter="format_energy", ), @@ -378,7 +378,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyHomeBat:Total", name="Home Consumption from Battery Total", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, formatter="format_energy", @@ -387,7 +387,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyHomeGrid:Day", name="Home Consumption from Grid Day", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, formatter="format_energy", ), @@ -395,7 +395,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyHomeGrid:Month", name="Home Consumption from Grid Month", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, formatter="format_energy", ), @@ -403,7 +403,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyHomeGrid:Year", name="Home Consumption from Grid Year", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, formatter="format_energy", ), @@ -411,7 +411,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyHomeGrid:Total", name="Home Consumption from Grid Total", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, formatter="format_energy", @@ -420,7 +420,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyHomePv:Day", name="Home Consumption from PV Day", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, formatter="format_energy", ), @@ -428,7 +428,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyHomePv:Month", name="Home Consumption from PV Month", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, formatter="format_energy", ), @@ -436,7 +436,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyHomePv:Year", name="Home Consumption from PV Year", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, formatter="format_energy", ), @@ -444,7 +444,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyHomePv:Total", name="Home Consumption from PV Total", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, formatter="format_energy", @@ -453,7 +453,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyPv1:Day", name="Energy PV1 Day", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, formatter="format_energy", ), @@ -461,7 +461,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyPv1:Month", name="Energy PV1 Month", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, formatter="format_energy", ), @@ -469,7 +469,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyPv1:Year", name="Energy PV1 Year", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, formatter="format_energy", ), @@ -477,7 +477,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyPv1:Total", name="Energy PV1 Total", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, formatter="format_energy", @@ -486,7 +486,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyPv2:Day", name="Energy PV2 Day", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, formatter="format_energy", ), @@ -494,7 +494,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyPv2:Month", name="Energy PV2 Month", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, formatter="format_energy", ), @@ -502,7 +502,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyPv2:Year", name="Energy PV2 Year", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, formatter="format_energy", ), @@ -510,7 +510,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyPv2:Total", name="Energy PV2 Total", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, formatter="format_energy", @@ -519,7 +519,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyPv3:Day", name="Energy PV3 Day", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, formatter="format_energy", ), @@ -527,7 +527,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyPv3:Month", name="Energy PV3 Month", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, formatter="format_energy", ), @@ -535,7 +535,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyPv3:Year", name="Energy PV3 Year", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, formatter="format_energy", ), @@ -543,7 +543,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyPv3:Total", name="Energy PV3 Total", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, formatter="format_energy", @@ -552,7 +552,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:Yield:Day", name="Energy Yield Day", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, entity_registry_enabled_default=True, formatter="format_energy", @@ -561,7 +561,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:Yield:Month", name="Energy Yield Month", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, formatter="format_energy", ), @@ -569,7 +569,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:Yield:Year", name="Energy Yield Year", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, formatter="format_energy", ), @@ -577,7 +577,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:Yield:Total", name="Energy Yield Total", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, formatter="format_energy", @@ -586,7 +586,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyChargeGrid:Day", name="Battery Charge from Grid Day", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, formatter="format_energy", ), @@ -594,7 +594,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyChargeGrid:Month", name="Battery Charge from Grid Month", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, formatter="format_energy", ), @@ -602,7 +602,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyChargeGrid:Year", name="Battery Charge from Grid Year", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, formatter="format_energy", ), @@ -610,7 +610,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyChargeGrid:Total", name="Battery Charge from Grid Total", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, formatter="format_energy", @@ -619,7 +619,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyChargePv:Day", name="Battery Charge from PV Day", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, formatter="format_energy", ), @@ -627,7 +627,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyChargePv:Month", name="Battery Charge from PV Month", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, formatter="format_energy", ), @@ -635,7 +635,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyChargePv:Year", name="Battery Charge from PV Year", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, formatter="format_energy", ), @@ -643,7 +643,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyChargePv:Total", name="Battery Charge from PV Total", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, formatter="format_energy", @@ -652,7 +652,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyDischargeGrid:Day", name="Energy Discharge to Grid Day", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, formatter="format_energy", ), @@ -660,7 +660,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyDischargeGrid:Month", name="Energy Discharge to Grid Month", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, formatter="format_energy", ), @@ -668,7 +668,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyDischargeGrid:Year", name="Energy Discharge to Grid Year", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, formatter="format_energy", ), @@ -676,7 +676,7 @@ SENSOR_PROCESS_DATA = [ module_id="scb:statistic:EnergyFlow", key="Statistic:EnergyDischargeGrid:Total", name="Energy Discharge to Grid Total", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, formatter="format_energy", @@ -725,7 +725,9 @@ async def async_setup_entry( async_add_entities(entities) -class PlenticoreDataSensor(CoordinatorEntity, SensorEntity): +class PlenticoreDataSensor( + CoordinatorEntity[ProcessDataUpdateCoordinator], SensorEntity +): """Representation of a Plenticore data Sensor.""" entity_description: PlenticoreSensorEntityDescription diff --git a/homeassistant/components/kostal_plenticore/translations/pt.json b/homeassistant/components/kostal_plenticore/translations/pt.json index df69b1c4ff3..7a1a1253720 100644 --- a/homeassistant/components/kostal_plenticore/translations/pt.json +++ b/homeassistant/components/kostal_plenticore/translations/pt.json @@ -1,12 +1,12 @@ { "config": { "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "user": { "data": { - "host": "Servidor", + "host": "Endere\u00e7o", "password": "Palavra-passe" } } diff --git a/homeassistant/components/kraken/__init__.py b/homeassistant/components/kraken/__init__.py index db2baf56f97..1cfade2a6b7 100644 --- a/homeassistant/components/kraken/__init__.py +++ b/homeassistant/components/kraken/__init__.py @@ -78,7 +78,8 @@ class KrakenData: except pykrakenapi.pykrakenapi.KrakenAPIError as error: if "Unknown asset pair" in str(error): _LOGGER.info( - "Kraken.com reported an unknown asset pair. Refreshing list of tradable asset pairs" + "Kraken.com reported an unknown asset pair. Refreshing list of" + " tradable asset pairs" ) await self._async_refresh_tradable_asset_pairs() else: @@ -87,7 +88,8 @@ class KrakenData: ) from error except pykrakenapi.pykrakenapi.CallRateLimitError: _LOGGER.warning( - "Exceeded the Kraken.com call rate limit. Increase the update interval to prevent this error" + "Exceeded the Kraken.com call rate limit. Increase the update interval" + " to prevent this error" ) return None diff --git a/homeassistant/components/kraken/translations/en.json b/homeassistant/components/kraken/translations/en.json index ced53b76e10..80845df9493 100644 --- a/homeassistant/components/kraken/translations/en.json +++ b/homeassistant/components/kraken/translations/en.json @@ -5,7 +5,7 @@ }, "step": { "user": { - "description": "Do you want to start set up?" + "description": "Do you want to start setup?" } } }, diff --git a/homeassistant/components/kraken/translations/it.json b/homeassistant/components/kraken/translations/it.json index 2fabbc9ad5c..6c02969d5ca 100644 --- a/homeassistant/components/kraken/translations/it.json +++ b/homeassistant/components/kraken/translations/it.json @@ -13,7 +13,7 @@ "one": "Vuoto", "other": "Vuoti" }, - "description": "Vuoi iniziare la configurazione?" + "description": "Vuoi avviare la configurazione?" } } }, diff --git a/homeassistant/components/kraken/translations/ko.json b/homeassistant/components/kraken/translations/ko.json index 758f3336cd4..f70d33320db 100644 --- a/homeassistant/components/kraken/translations/ko.json +++ b/homeassistant/components/kraken/translations/ko.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." + }, "step": { "user": { "description": "\uc124\uc815\uc744 \uc2dc\uc791\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" diff --git a/homeassistant/components/kraken/translations/pt.json b/homeassistant/components/kraken/translations/pt.json index 66ccff7f372..5d560cf2cec 100644 --- a/homeassistant/components/kraken/translations/pt.json +++ b/homeassistant/components/kraken/translations/pt.json @@ -5,7 +5,7 @@ }, "step": { "user": { - "description": "Quer dar inicio \u00e0 configura\u00e7\u00e3o?" + "description": "Quer dar in\u00edcio \u00e0 configura\u00e7\u00e3o?" } } } diff --git a/homeassistant/components/kraken/translations/sk.json b/homeassistant/components/kraken/translations/sk.json index 715c2e1496d..d2fd9188cc1 100644 --- a/homeassistant/components/kraken/translations/sk.json +++ b/homeassistant/components/kraken/translations/sk.json @@ -3,8 +3,20 @@ "abort": { "already_configured": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." }, + "error": { + "few": "Pr\u00e1zdnych", + "many": "Pr\u00e1zdnych", + "one": "Pr\u00e1zdny", + "other": "Pr\u00e1zdny" + }, "step": { "user": { + "data": { + "few": "Pr\u00e1zdnych", + "many": "Pr\u00e1zdnych", + "one": "Pr\u00e1zdny", + "other": "Pr\u00e1zdny" + }, "description": "Chcete za\u010da\u0165 nastavova\u0165?" } } @@ -13,7 +25,8 @@ "step": { "init": { "data": { - "scan_interval": "Interval aktualiz\u00e1cie" + "scan_interval": "Interval aktualiz\u00e1cie", + "tracked_asset_pairs": "Sledovan\u00e9 p\u00e1ry akt\u00edv" } } } diff --git a/homeassistant/components/kulersky/translations/en.json b/homeassistant/components/kulersky/translations/en.json index f05becffed3..1f858b1dfb5 100644 --- a/homeassistant/components/kulersky/translations/en.json +++ b/homeassistant/components/kulersky/translations/en.json @@ -6,7 +6,7 @@ }, "step": { "confirm": { - "description": "Do you want to start set up?" + "description": "Do you want to start setup?" } } } diff --git a/homeassistant/components/kulersky/translations/it.json b/homeassistant/components/kulersky/translations/it.json index 0278fe07bfe..278f85c5cff 100644 --- a/homeassistant/components/kulersky/translations/it.json +++ b/homeassistant/components/kulersky/translations/it.json @@ -6,7 +6,7 @@ }, "step": { "confirm": { - "description": "Vuoi iniziare la configurazione?" + "description": "Vuoi avviare la configurazione?" } } } diff --git a/homeassistant/components/kulersky/translations/pt.json b/homeassistant/components/kulersky/translations/pt.json index e25888655a9..02b15520b4f 100644 --- a/homeassistant/components/kulersky/translations/pt.json +++ b/homeassistant/components/kulersky/translations/pt.json @@ -6,7 +6,7 @@ }, "step": { "confirm": { - "description": "Quer dar inicio \u00e0 configura\u00e7\u00e3o?" + "description": "Quer dar in\u00edcio \u00e0 configura\u00e7\u00e3o?" } } } diff --git a/homeassistant/components/lacrosse/sensor.py b/homeassistant/components/lacrosse/sensor.py index e2f028907d9..a9edea22b0d 100644 --- a/homeassistant/components/lacrosse/sensor.py +++ b/homeassistant/components/lacrosse/sensor.py @@ -23,7 +23,7 @@ from homeassistant.const import ( CONF_TYPE, EVENT_HOMEASSISTANT_STOP, PERCENTAGE, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant, callback import homeassistant.helpers.config_validation as cv @@ -187,7 +187,7 @@ class LaCrosseTemperature(LaCrosseSensor): _attr_device_class = SensorDeviceClass.TEMPERATURE _attr_state_class = SensorStateClass.MEASUREMENT - _attr_native_unit_of_measurement = TEMP_CELSIUS + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS @property def native_value(self): diff --git a/homeassistant/components/lacrosse_view/sensor.py b/homeassistant/components/lacrosse_view/sensor.py index 684ac884345..7ef154015cb 100644 --- a/homeassistant/components/lacrosse_view/sensor.py +++ b/homeassistant/components/lacrosse_view/sensor.py @@ -15,10 +15,9 @@ from homeassistant.components.sensor import ( from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( PERCENTAGE, - PRECIPITATION_INCHES, - SPEED_KILOMETERS_PER_HOUR, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, + UnitOfPrecipitationDepth, + UnitOfSpeed, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -57,7 +56,7 @@ SENSOR_DESCRIPTIONS = { name="Temperature", state_class=SensorStateClass.MEASUREMENT, value_fn=get_value, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, ), "Humidity": LaCrosseSensorEntityDescription( key="Humidity", @@ -73,22 +72,23 @@ SENSOR_DESCRIPTIONS = { name="Heat index", state_class=SensorStateClass.MEASUREMENT, value_fn=get_value, - native_unit_of_measurement=TEMP_FAHRENHEIT, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, ), "WindSpeed": LaCrosseSensorEntityDescription( key="WindSpeed", name="Wind speed", state_class=SensorStateClass.MEASUREMENT, value_fn=get_value, - native_unit_of_measurement=SPEED_KILOMETERS_PER_HOUR, - device_class=SensorDeviceClass.SPEED, + native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, + device_class=SensorDeviceClass.WIND_SPEED, ), "Rain": LaCrosseSensorEntityDescription( key="Rain", name="Rain", state_class=SensorStateClass.MEASUREMENT, value_fn=get_value, - native_unit_of_measurement=PRECIPITATION_INCHES, + native_unit_of_measurement=UnitOfPrecipitationDepth.INCHES, + device_class=SensorDeviceClass.PRECIPITATION, ), } @@ -111,7 +111,8 @@ async def async_setup_entry( if description is None: message = ( f"Unsupported sensor field: {field}\nPlease create an issue on " - "GitHub. https://github.com/home-assistant/core/issues/new?assignees=&la" + "GitHub." + " https://github.com/home-assistant/core/issues/new?assignees=&la" "bels=&template=bug_report.yml&integration_name=LaCrosse%20View&integrat" "ion_link=https://www.home-assistant.io/integrations/lacrosse_view/&addi" f"tional_information=Field:%20{field}%0ASensor%20Model:%20{sensor.model}&" diff --git a/homeassistant/components/lacrosse_view/translations/ko.json b/homeassistant/components/lacrosse_view/translations/ko.json new file mode 100644 index 00000000000..4e6af190b61 --- /dev/null +++ b/homeassistant/components/lacrosse_view/translations/ko.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/lacrosse_view/translations/pt.json b/homeassistant/components/lacrosse_view/translations/pt.json index 7af622c544e..dbbefd43356 100644 --- a/homeassistant/components/lacrosse_view/translations/pt.json +++ b/homeassistant/components/lacrosse_view/translations/pt.json @@ -1,7 +1,21 @@ { "config": { + "abort": { + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", + "reauth_successful": "Reautentica\u00e7\u00e3o bem sucedida" + }, "error": { - "no_locations": "Nenhum local encontrado" + "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", + "no_locations": "Nenhum local encontrado", + "unknown": "Erro inesperado" + }, + "step": { + "user": { + "data": { + "password": "Palavra-passe", + "username": "Nome de Utilizador" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/lametric/select.py b/homeassistant/components/lametric/select.py index e15e33facfc..79e3eca693f 100644 --- a/homeassistant/components/lametric/select.py +++ b/homeassistant/components/lametric/select.py @@ -40,8 +40,8 @@ SELECTS = [ name="Brightness mode", icon="mdi:brightness-auto", entity_category=EntityCategory.CONFIG, - device_class="lametric__brightness_mode", options=["auto", "manual"], + translation_key="brightness_mode", current_fn=lambda device: device.display.brightness_mode.value, select_fn=lambda api, opt: api.display(brightness_mode=BrightnessMode(opt)), ), diff --git a/homeassistant/components/lametric/strings.json b/homeassistant/components/lametric/strings.json index 768f8e2b740..f20732c6348 100644 --- a/homeassistant/components/lametric/strings.json +++ b/homeassistant/components/lametric/strings.json @@ -47,7 +47,17 @@ "issues": { "manual_migration": { "title": "Manual migration required for LaMetric", - "description": "The LaMetric integration has been modernized: It is now configured and set up via the user interface and the communcations are now local.\n\nUnfortunately, there is no automatic migration path possible and thus requires you to re-setup your LaMetric with Home Assistant. Please consult the Home Assistant LaMetric integration documentation on how to set it up.\n\nRemove the old LaMetric YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue." + "description": "The LaMetric integration has been modernized: It is now configured and set up via the user interface and the communcations are now local.\n\nUnfortunately, there is no automatic migration path possible and thus requires you to re-set up your LaMetric with Home Assistant. Please consult the Home Assistant LaMetric integration documentation on how to set it up.\n\nRemove the old LaMetric YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue." + } + }, + "entity": { + "select": { + "brightness_mode": { + "state": { + "auto": "Automatic", + "manual": "Manual" + } + } } } } diff --git a/homeassistant/components/lametric/strings.select.json b/homeassistant/components/lametric/strings.select.json deleted file mode 100644 index 1d2ce0a2ce7..00000000000 --- a/homeassistant/components/lametric/strings.select.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "state": { - "lametric__brightness_mode": { - "auto": "Automatic", - "manual": "Manual" - } - } -} diff --git a/homeassistant/components/lametric/translations/ca.json b/homeassistant/components/lametric/translations/ca.json index b5f3d609052..0de69481a67 100644 --- a/homeassistant/components/lametric/translations/ca.json +++ b/homeassistant/components/lametric/translations/ca.json @@ -44,9 +44,19 @@ } } }, + "entity": { + "select": { + "brightness_mode": { + "state": { + "auto": "Autom\u00e0tic", + "manual": "Manual" + } + } + } + }, "issues": { "manual_migration": { - "description": "La integraci\u00f3 de LaMetric s'ha modernitzat: ara es configura a trav\u00e9s de la interf\u00edcie d'usuari (IU) i les comunicacions s\u00f3n locals. \n\nMalauradament, no hi ha cap possibilitat de migraci\u00f3 autom\u00e0tica i, per tant, cal que tornis a configurar el teu dispositiu LaMetric amb Home Assistant. Consulta la documentaci\u00f3 de la integraci\u00f3 LaMetric de Home Assistant per fer-ho. \n\nElimina l'antiga configuraci\u00f3 YAML de LaMetric del fitxer configuration.yaml i reinicia Home Assistant per solucionar aquest problema.", + "description": "La integraci\u00f3 LaMetric s'ha modernitzat: ara es configura a trav\u00e9s de la interf\u00edcie d'usuari (IU) i les comunicacions s\u00f3n locals. \n\nMalauradament, no hi ha cap possibilitat de migraci\u00f3 autom\u00e0tica i, per tant, cal que tornis a configurar el teu dispositiu LaMetric amb Home Assistant. Consulta la documentaci\u00f3 de la integraci\u00f3 LaMetric de Home Assistant per fer-ho. \n\nElimina l'antiga configuraci\u00f3 YAML de LaMetric del fitxer configuration.yaml i reinicia Home Assistant per solucionar aquest problema.", "title": "\u00c9s necess\u00e0ria una migraci\u00f3 manual per a LaMetric" } } diff --git a/homeassistant/components/lametric/translations/de.json b/homeassistant/components/lametric/translations/de.json index 5b7c2534c5d..20d4b417840 100644 --- a/homeassistant/components/lametric/translations/de.json +++ b/homeassistant/components/lametric/translations/de.json @@ -44,6 +44,16 @@ } } }, + "entity": { + "select": { + "brightness_mode": { + "state": { + "auto": "Automatisch", + "manual": "Manuell" + } + } + } + }, "issues": { "manual_migration": { "description": "Die LaMetric-Integration wurde modernisiert: Sie wird nun \u00fcber die Benutzeroberfl\u00e4che konfiguriert und eingerichtet und die Kommunikation erfolgt nun lokal.\n\nLeider gibt es keinen automatischen Migrationspfad, so dass du dein LaMetric mit Home Assistant neu einrichten musst. Bitte konsultiere die Dokumentation zur LaMetric-Integration von Home Assistant, um zu erfahren, wie du diese einrichten kannst.\n\nEntferne die alte LaMetric YAML-Konfiguration aus deiner configuration.yaml-Datei und starte Home Assistant neu, um das Problem zu beheben.", diff --git a/homeassistant/components/lametric/translations/el.json b/homeassistant/components/lametric/translations/el.json index dfd0423ce15..24b48466fee 100644 --- a/homeassistant/components/lametric/translations/el.json +++ b/homeassistant/components/lametric/translations/el.json @@ -44,6 +44,16 @@ } } }, + "entity": { + "select": { + "brightness_mode": { + "state": { + "auto": "\u0391\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03bf", + "manual": "\u03a7\u03b5\u03b9\u03c1\u03bf\u03ba\u03af\u03bd\u03b7\u03c4\u03bf" + } + } + } + }, "issues": { "manual_migration": { "description": "\u0397 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 LaMetric \u03ad\u03c7\u03b5\u03b9 \u03b5\u03ba\u03c3\u03c5\u03b3\u03c7\u03c1\u03bf\u03bd\u03b9\u03c3\u03c4\u03b5\u03af: \u03a4\u03ce\u03c1\u03b1 \u03ad\u03c7\u03b5\u03b9 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af \u03ba\u03b1\u03b9 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af \u03bc\u03ad\u03c3\u03c9 \u03c4\u03b7\u03c2 \u03b4\u03b9\u03b5\u03c0\u03b1\u03c6\u03ae\u03c2 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7 \u03ba\u03b1\u03b9 \u03bf\u03b9 \u03b5\u03c0\u03b9\u03ba\u03bf\u03b9\u03bd\u03c9\u03bd\u03af\u03b5\u03c2 \u03b5\u03af\u03bd\u03b1\u03b9 \u03c0\u03bb\u03ad\u03bf\u03bd \u03c4\u03bf\u03c0\u03b9\u03ba\u03ad\u03c2. \n\n \u0394\u03c5\u03c3\u03c4\u03c5\u03c7\u03ce\u03c2, \u03b4\u03b5\u03bd \u03c5\u03c0\u03ac\u03c1\u03c7\u03b5\u03b9 \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae \u03b4\u03b9\u03b1\u03b4\u03c1\u03bf\u03bc\u03ae \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b7\u03c2 \u03bc\u03b5\u03c4\u03b5\u03b3\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2 \u03ba\u03b1\u03b9, \u03c9\u03c2 \u03b5\u03ba \u03c4\u03bf\u03cd\u03c4\u03bf\u03c5, \u03b1\u03c0\u03b1\u03b9\u03c4\u03b5\u03af \u03b1\u03c0\u03cc \u03b5\u03c3\u03ac\u03c2 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac \u03c4\u03bf LaMetric \u03c3\u03b1\u03c2 \u03bc\u03b5 \u03c4\u03bf Home Assistant. \u03a3\u03c5\u03bc\u03b2\u03bf\u03c5\u03bb\u03b5\u03c5\u03c4\u03b5\u03af\u03c4\u03b5 \u03c4\u03b7\u03bd \u03c4\u03b5\u03ba\u03bc\u03b7\u03c1\u03af\u03c9\u03c3\u03b7 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 Home Assistant LaMetric \u03c3\u03c7\u03b5\u03c4\u03b9\u03ba\u03ac \u03bc\u03b5 \u03c4\u03bf\u03bd \u03c4\u03c1\u03cc\u03c0\u03bf \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5. \n\n \u039a\u03b1\u03c4\u03b1\u03c1\u03b3\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03c0\u03b1\u03bb\u03b9\u03ac \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 YAML LaMetric \u03b1\u03c0\u03cc \u03c4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf configuration.yaml \u03ba\u03b1\u03b9 \u03b5\u03c0\u03b1\u03bd\u03b5\u03ba\u03ba\u03b9\u03bd\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf Home Assistant \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c0\u03c1\u03cc\u03b2\u03bb\u03b7\u03bc\u03b1.", diff --git a/homeassistant/components/lametric/translations/en.json b/homeassistant/components/lametric/translations/en.json index c36b490fcd2..e7e3ad0104e 100644 --- a/homeassistant/components/lametric/translations/en.json +++ b/homeassistant/components/lametric/translations/en.json @@ -44,9 +44,19 @@ } } }, + "entity": { + "select": { + "brightness_mode": { + "state": { + "auto": "Automatic", + "manual": "Manual" + } + } + } + }, "issues": { "manual_migration": { - "description": "The LaMetric integration has been modernized: It is now configured and set up via the user interface and the communcations are now local.\n\nUnfortunately, there is no automatic migration path possible and thus requires you to re-setup your LaMetric with Home Assistant. Please consult the Home Assistant LaMetric integration documentation on how to set it up.\n\nRemove the old LaMetric YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue.", + "description": "The LaMetric integration has been modernized: It is now configured and set up via the user interface and the communcations are now local.\n\nUnfortunately, there is no automatic migration path possible and thus requires you to re-set up your LaMetric with Home Assistant. Please consult the Home Assistant LaMetric integration documentation on how to set it up.\n\nRemove the old LaMetric YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue.", "title": "Manual migration required for LaMetric" } } diff --git a/homeassistant/components/lametric/translations/es.json b/homeassistant/components/lametric/translations/es.json index 47fccfea279..400917038e2 100644 --- a/homeassistant/components/lametric/translations/es.json +++ b/homeassistant/components/lametric/translations/es.json @@ -44,6 +44,16 @@ } } }, + "entity": { + "select": { + "brightness_mode": { + "state": { + "auto": "Autom\u00e1tico", + "manual": "Manual" + } + } + } + }, "issues": { "manual_migration": { "description": "La integraci\u00f3n de LaMetric se ha modernizado: ahora se configura a trav\u00e9s de la interfaz de usuario y las comunicaciones son locales.\n\nDesafortunadamente, no existe una ruta de migraci\u00f3n autom\u00e1tica posible y, por lo tanto, requiere que vuelvas a configurar tu LaMetric con Home Assistant. Consulta la documentaci\u00f3n de la integraci\u00f3n LaMetric en Home Assistant sobre c\u00f3mo configurarlo. \n\nElimina la configuraci\u00f3n antigua de LaMetric YAML de tu archivo configuration.yaml y reinicia Home Assistant para solucionar este problema.", diff --git a/homeassistant/components/lametric/translations/et.json b/homeassistant/components/lametric/translations/et.json index 008b4e875de..dfe199367c1 100644 --- a/homeassistant/components/lametric/translations/et.json +++ b/homeassistant/components/lametric/translations/et.json @@ -44,6 +44,16 @@ } } }, + "entity": { + "select": { + "brightness_mode": { + "state": { + "auto": "Automaatne", + "manual": "K\u00e4sitsi" + } + } + } + }, "issues": { "manual_migration": { "description": "LaMetricu integratsioon on moderniseeritud: see on n\u00fc\u00fcd konfigureeritud ja seadistatud kasutajaliidese kaudu ning \u00fchendused on n\u00fc\u00fcd kohalikud.\n\nKahjuks pole automaatset migreerimisteed v\u00f5imalik ja seega peate oma LaMetricu koduabilisega uuesti seadistama. Palun tutvuge koduabilise LaMetric integratsiooni dokumentatsiooniga, kuidas seda seadistada.\n\nEemaldage vana LaMetric YAML-i konfiguratsioon failist configuration.yaml ja taask\u00e4ivitage selle probleemi lahendamiseks Home Assistant.", diff --git a/homeassistant/components/lametric/translations/he.json b/homeassistant/components/lametric/translations/he.json index 0912073e131..734ecd5ba35 100644 --- a/homeassistant/components/lametric/translations/he.json +++ b/homeassistant/components/lametric/translations/he.json @@ -22,5 +22,15 @@ "title": "\u05d1\u05d7\u05e8 \u05e9\u05d9\u05d8\u05ea \u05d0\u05d9\u05de\u05d5\u05ea" } } + }, + "entity": { + "select": { + "brightness_mode": { + "state": { + "auto": "\u05d0\u05d5\u05d8\u05d5\u05de\u05d8\u05d9", + "manual": "\u05d9\u05d3\u05e0\u05d9\u05ea" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/lametric/translations/hu.json b/homeassistant/components/lametric/translations/hu.json index 748f4bb59be..8506d7fe5df 100644 --- a/homeassistant/components/lametric/translations/hu.json +++ b/homeassistant/components/lametric/translations/hu.json @@ -44,6 +44,16 @@ } } }, + "entity": { + "select": { + "brightness_mode": { + "state": { + "auto": "Automatikus", + "manual": "Manu\u00e1lis" + } + } + } + }, "issues": { "manual_migration": { "description": "A LaMetric integr\u00e1ci\u00f3 moderniz\u00e1l\u00e1sra ker\u00fclt: A konfigur\u00e1l\u00e1s \u00e9s be\u00e1ll\u00edt\u00e1s mostant\u00f3l a felhaszn\u00e1l\u00f3i fel\u00fcleten kereszt\u00fcl t\u00f6rt\u00e9nik, \u00e9s a kommunik\u00e1ci\u00f3 mostant\u00f3l helyi.\n\nSajnos nincs lehet\u0151s\u00e9g automatikus migr\u00e1ci\u00f3s \u00fatvonalra, \u00edgy a LaMetric-et \u00fajra be kell \u00e1ll\u00edtania a Home Assistant seg\u00edts\u00e9g\u00e9vel. K\u00e9rj\u00fck, tekintse meg a Home Assistant LaMetric integr\u00e1ci\u00f3 dokument\u00e1ci\u00f3j\u00e1t a be\u00e1ll\u00edt\u00e1ssal kapcsolatban.\n\nA probl\u00e9ma megold\u00e1s\u00e1hoz t\u00e1vol\u00edtsa el a r\u00e9gi LaMetric YAML konfigur\u00e1ci\u00f3t a configuration.yaml f\u00e1jlb\u00f3l, \u00e9s ind\u00edtsa \u00fajra a Home Assistant-ot.", diff --git a/homeassistant/components/lametric/translations/id.json b/homeassistant/components/lametric/translations/id.json index e6010f08e3d..f49a5611814 100644 --- a/homeassistant/components/lametric/translations/id.json +++ b/homeassistant/components/lametric/translations/id.json @@ -44,6 +44,16 @@ } } }, + "entity": { + "select": { + "brightness_mode": { + "state": { + "auto": "Otomatis", + "manual": "Manual" + } + } + } + }, "issues": { "manual_migration": { "description": "Integrasi LaMetric telah dimodernisasi: kini integrasinya dikonfigurasi dan disiapkan melalui antarmuka pengguna dan komunikasi menjadi lokal.\n\nSayangnya, tidak ada jalur migrasi otomatis yang mungkin dan oleh sebab itu Anda harus mengatur ulang integrasi LaMetric dengan Home Assistant. Baca dokumentasi integrasi LaMetric Home Assistant tentang cara persiapannya.\n\nHapus konfigurasi YAML LaMetric yang lama dari file configuration.yaml Anda dan mulai ulang Home Assistant untuk memperbaiki masalah ini.", diff --git a/homeassistant/components/lametric/translations/it.json b/homeassistant/components/lametric/translations/it.json index c6b8f6c84df..c29d522dcc7 100644 --- a/homeassistant/components/lametric/translations/it.json +++ b/homeassistant/components/lametric/translations/it.json @@ -44,9 +44,19 @@ } } }, + "entity": { + "select": { + "brightness_mode": { + "state": { + "auto": "Automatico", + "manual": "Manuale" + } + } + } + }, "issues": { "manual_migration": { - "description": "L'integrazione LaMetric \u00e8 stata modernizzata: viene ora configurata e inizializzata tramite l'interfaccia utente e le comunicazioni sono ora locali. \n\nSfortunatamente, la migrazione automatica non \u00e8 disponibile, quindi \u00e8 necessario configurare nuovamente LaMetric con Home Assistant. Consulta la documentazione di Home Assistant sull'integrazione LaMetric per sapere come configurarlo. \n\nRimuovere la vecchia configurazione YAML di LaMetric dal file configuration.yaml e riavviare Home Assistant per risolvere questo problema.", + "description": "L'integrazione di LaMetric \u00e8 stata modernizzata: ora \u00e8 configurata e impostata tramite l'interfaccia utente e le comunicazioni sono ora locali. \n\n Sfortunatamente, non \u00e8 possibile un percorso di migrazione automatica e quindi \u00e8 necessario riconfigurare LaMetric con Home Assistant. Si prega di consultare la documentazione sull'integrazione di Home Assistant LaMetric su come configurarlo. \n\n Rimuovi la vecchia configurazione YAML di LaMetric dal tuo file configuration.yaml e riavvia Home Assistant per risolvere questo problema.", "title": "Migrazione manuale richiesta per LaMetric" } } diff --git a/homeassistant/components/lametric/translations/ko.json b/homeassistant/components/lametric/translations/ko.json new file mode 100644 index 00000000000..0b67152061f --- /dev/null +++ b/homeassistant/components/lametric/translations/ko.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "authorize_url_timeout": "\uc778\uc99d URL \uc0dd\uc131 \uc2dc\uac04\uc774 \ucd08\uacfc\ub418\uc5c8\uc2b5\ub2c8\ub2e4.", + "no_url_available": "\uc0ac\uc6a9 \uac00\ub2a5\ud55c URL\uc774 \uc5c6\uc2b5\ub2c8\ub2e4. \uc774 \uc624\ub958\uc5d0 \ub300\ud55c \uc790\uc138\ud55c \ub0b4\uc6a9\uc740 [\ub3c4\uc6c0\ub9d0 \uc139\uc158]({docs_url}) \uc744(\ub97c) \ucc38\uc870\ud574\uc8fc\uc138\uc694.", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "manual_entry": { + "data": { + "api_key": "API \ud0a4", + "host": "\ud638\uc2a4\ud2b8" + } + }, + "pick_implementation": { + "title": "\uc778\uc99d \ubc29\ubc95 \uc120\ud0dd\ud558\uae30" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/lametric/translations/nl.json b/homeassistant/components/lametric/translations/nl.json index addce23b5c9..d598679de04 100644 --- a/homeassistant/components/lametric/translations/nl.json +++ b/homeassistant/components/lametric/translations/nl.json @@ -33,5 +33,15 @@ } } } + }, + "entity": { + "select": { + "brightness_mode": { + "state": { + "auto": "Automatisch", + "manual": "Handmatig" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/lametric/translations/no.json b/homeassistant/components/lametric/translations/no.json index e1c1f331692..662cfbf3301 100644 --- a/homeassistant/components/lametric/translations/no.json +++ b/homeassistant/components/lametric/translations/no.json @@ -44,9 +44,19 @@ } } }, + "entity": { + "select": { + "brightness_mode": { + "state": { + "auto": "Automatisk", + "manual": "Manuell" + } + } + } + }, "issues": { "manual_migration": { - "description": "LaMetric-integrasjonen er modernisert: Den er n\u00e5 konfigurert og satt opp via brukergrensesnittet og kommunikasjonen er n\u00e5 lokal. \n\n Dessverre er det ingen automatisk migreringsbane mulig og krever derfor at du konfigurerer LaMetric p\u00e5 nytt med Home Assistant. Se integreringsdokumentasjonen for Home Assistant LaMetric for hvordan du setter den opp. \n\n Fjern den gamle LaMetric YAML-konfigurasjonen fra configuration.yaml-filen og start Home Assistant p\u00e5 nytt for \u00e5 fikse dette problemet.", + "description": "LaMetric-integrasjonen er modernisert: Den er n\u00e5 konfigurert og satt opp via brukergrensesnittet og kommunikasjonen er n\u00e5 lokal. \n\n Dessverre er det ingen automatisk migreringsbane mulig og krever derfor at du konfigurerer LaMetric p\u00e5 nytt med Home Assistant. Se integrasjonsdokumentasjonen for Home Assistant LaMetric for hvordan du setter den opp. \n\n Fjern den gamle LaMetric YAML-konfigurasjonen fra configuration.yaml-filen og start Home Assistant p\u00e5 nytt for \u00e5 fikse dette problemet.", "title": "Manuell migrering kreves for LaMetric" } } diff --git a/homeassistant/components/lametric/translations/pl.json b/homeassistant/components/lametric/translations/pl.json index 879ab701be7..094c78394f9 100644 --- a/homeassistant/components/lametric/translations/pl.json +++ b/homeassistant/components/lametric/translations/pl.json @@ -44,6 +44,16 @@ } } }, + "entity": { + "select": { + "brightness_mode": { + "state": { + "auto": "automatyczny", + "manual": "r\u0119czny" + } + } + } + }, "issues": { "manual_migration": { "description": "Integracja LaMetric zosta\u0142a zmodernizowana: jest teraz konfigurowana za pomoc\u0105 interfejsu u\u017cytkownika, a komunikacja odbywa si\u0119 lokalnie. \n\nNiestety nie ma mo\u017cliwo\u015bci automatycznej migracji i dlatego wymaga si\u0119 ponownej konfiguracji LaMetric za pomoc\u0105 Home Assistanta. Zapoznaj si\u0119 z dokumentacj\u0105 integracji Home Assistant LaMetric, aby dowiedzie\u0107 si\u0119, jak to skonfigurowa\u0107. \n\nUsu\u0144 konfiguracj\u0119 LaMetric YAML z pliku configuration.yaml i uruchom ponownie Home Assistanta, aby rozwi\u0105za\u0107 ten problem.", diff --git a/homeassistant/components/lametric/translations/pt-BR.json b/homeassistant/components/lametric/translations/pt-BR.json index 8c419582d9d..8d3e8c706f0 100644 --- a/homeassistant/components/lametric/translations/pt-BR.json +++ b/homeassistant/components/lametric/translations/pt-BR.json @@ -44,9 +44,19 @@ } } }, + "entity": { + "select": { + "brightness_mode": { + "state": { + "auto": "Autom\u00e1tico", + "manual": "Manual" + } + } + } + }, "issues": { "manual_migration": { - "description": "A integra\u00e7\u00e3o LaMetric foi modernizada: agora est\u00e1 configurada e configurada atrav\u00e9s da interface do usu\u00e1rio e as comunica\u00e7\u00f5es agora s\u00e3o locais. \n\n Infelizmente, n\u00e3o h\u00e1 caminho de migra\u00e7\u00e3o autom\u00e1tica poss\u00edvel e, portanto, exige que voc\u00ea reconfigure seu LaMetric com o Home Assistant. Consulte a documenta\u00e7\u00e3o de integra\u00e7\u00e3o do Home Assistant LaMetric sobre como configur\u00e1-lo. \n\n Remova a configura\u00e7\u00e3o antiga do LaMetric YAML do arquivo configuration.yaml e reinicie o Home Assistant para corrigir esse problema.", + "description": "A integra\u00e7\u00e3o LaMetric foi modernizada: agora \u00e9 configurada e configurada atrav\u00e9s da interface do usu\u00e1rio e as comunica\u00e7\u00f5es agora s\u00e3o locais. \n\n Infelizmente, n\u00e3o h\u00e1 um caminho de migra\u00e7\u00e3o autom\u00e1tica poss\u00edvel e, portanto, requer que voc\u00ea reconfigure seu LaMetric com o Home Assistant. Consulte a documenta\u00e7\u00e3o de integra\u00e7\u00e3o LaMetric do Home Assistant para saber como configur\u00e1-lo. \n\n Remova a configura\u00e7\u00e3o antiga do LaMetric YAML do arquivo configuration.yaml e reinicie o Home Assistant para corrigir esse problema.", "title": "Migra\u00e7\u00e3o manual necess\u00e1ria para LaMetric" } } diff --git a/homeassistant/components/lametric/translations/pt.json b/homeassistant/components/lametric/translations/pt.json index 74a501a1ca8..d37499b1bbb 100644 --- a/homeassistant/components/lametric/translations/pt.json +++ b/homeassistant/components/lametric/translations/pt.json @@ -1,11 +1,19 @@ { "config": { "abort": { + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", + "authorize_url_timeout": "Excedido o tempo limite para gerar um URL de autoriza\u00e7\u00e3o", "invalid_discovery_info": "Informa\u00e7\u00f5es de descoberta inv\u00e1lidas recebidas", "link_local_address": "Endere\u00e7os locais de link n\u00e3o s\u00e3o suportados", "missing_configuration": "A integra\u00e7\u00e3o LaMetric n\u00e3o est\u00e1 configurada. Por favor, siga a documenta\u00e7\u00e3o.", "no_devices": "O usu\u00e1rio autorizado n\u00e3o possui dispositivos LaMetric", - "reauth_successful": "Reautentica\u00e7\u00e3o bem sucedida" + "no_url_available": "Nenhum URL est\u00e1 dispon\u00edvel. Para obter informa\u00e7\u00f5es sobre esse erro, [consulte a sec\u00e7\u00e3o de ajuda]({docs_url})", + "reauth_successful": "Reautentica\u00e7\u00e3o bem sucedida", + "unknown": "Erro inesperado" + }, + "error": { + "cannot_connect": "A liga\u00e7\u00e3o falhou", + "unknown": "Erro inesperado" }, "step": { "choice_enter_manual_or_fetch_cloud": { @@ -16,11 +24,18 @@ } }, "manual_entry": { + "data": { + "api_key": "Chave da API", + "host": "Endere\u00e7o" + }, "data_description": { "api_key": "Voc\u00ea pode encontrar essa chave de API em [p\u00e1gina de dispositivos em sua conta de desenvolvedor LaMetric](https://developer.lametric.com/user/devices).", "host": "O endere\u00e7o IP ou nome de host do seu LaMetric TIME em sua rede." } }, + "pick_implementation": { + "title": "Escolha o m\u00e9todo de autentica\u00e7\u00e3o" + }, "user_cloud_select_device": { "data": { "device": "Selecione o dispositivo LaMetric para adicionar" diff --git a/homeassistant/components/lametric/translations/ru.json b/homeassistant/components/lametric/translations/ru.json index df712e4dc65..be1204f594c 100644 --- a/homeassistant/components/lametric/translations/ru.json +++ b/homeassistant/components/lametric/translations/ru.json @@ -44,6 +44,16 @@ } } }, + "entity": { + "select": { + "brightness_mode": { + "state": { + "auto": "\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438", + "manual": "\u0412\u0440\u0443\u0447\u043d\u0443\u044e" + } + } + } + }, "issues": { "manual_migration": { "description": "\u0418\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f LaMetric \u043c\u043e\u0434\u0435\u0440\u043d\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u0430. \u0422\u0435\u043f\u0435\u0440\u044c \u043e\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0438 \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441, \u0430 \u043e\u0431\u043c\u0435\u043d \u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0439 \u0441\u0435\u0442\u0438.\n\n\u041a \u0441\u043e\u0436\u0430\u043b\u0435\u043d\u0438\u044e, \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0438\u043c\u043f\u043e\u0440\u0442 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u043d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u0435\u043d, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0412\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u043d\u043e\u0432\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c LaMetric \u0432 Home Assistant. \u0427\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0432\u0441\u044e \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0443\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e, \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439 \u043f\u043e \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 LaMetric.\n\n\u0423\u0434\u0430\u043b\u0438\u0442\u0435 YAML-\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e LaMetric \u0438\u0437 \u0444\u0430\u0439\u043b\u0430 configuration.yaml \u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 Home Assistant, \u0447\u0442\u043e\u0431\u044b \u0443\u0441\u0442\u0440\u0430\u043d\u0438\u0442\u044c \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443.", diff --git a/homeassistant/components/lametric/translations/select.sk.json b/homeassistant/components/lametric/translations/select.sk.json new file mode 100644 index 00000000000..d527ddfa3a4 --- /dev/null +++ b/homeassistant/components/lametric/translations/select.sk.json @@ -0,0 +1,8 @@ +{ + "state": { + "lametric__brightness_mode": { + "auto": "Automatick\u00e9", + "manual": "Manu\u00e1lne" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/lametric/translations/sk.json b/homeassistant/components/lametric/translations/sk.json index e2a8b9c5051..26895d98f05 100644 --- a/homeassistant/components/lametric/translations/sk.json +++ b/homeassistant/components/lametric/translations/sk.json @@ -5,7 +5,10 @@ "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL.", "invalid_discovery_info": "Prijat\u00e9 neplatn\u00e9 inform\u00e1cie o zis\u0165ovan\u00ed", "link_local_address": "Lok\u00e1lne adresy odkazov nie s\u00fa podporovan\u00e9", + "missing_configuration": "Integr\u00e1cia LaMetric nie je nakonfigurovan\u00e1. Postupujte pod\u013ea dokument\u00e1cie.", + "no_devices": "Autorizovan\u00fd pou\u017e\u00edvate\u013e nem\u00e1 \u017eiadne zariadenia LaMetric", "no_url_available": "Nie je k dispoz\u00edcii \u017eiadna adresa URL. Inform\u00e1cie o tejto chybe n\u00e1jdete [pozrite si sekciu pomocn\u00edka]({docs_url})", + "reauth_device_not_found": "Zariadenie, ktor\u00e9 sa pok\u00fa\u0161ate op\u00e4tovne overi\u0165, sa v tomto \u00fa\u010dte LaMetric nena\u0161lo", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, @@ -15,19 +18,46 @@ }, "step": { "choice_enter_manual_or_fetch_cloud": { + "description": "Zariadenie LaMetric mo\u017eno v aplik\u00e1cii Home Assistant nastavi\u0165 dvoma r\u00f4znymi sp\u00f4sobmi. \n\n V\u0161etky inform\u00e1cie o zariaden\u00ed a tokeny API m\u00f4\u017eete zada\u0165 sami, alebo ich m\u00f4\u017ee Home Assistant importova\u0165 z v\u00e1\u0161ho \u00fa\u010dtu LaMetric.com.", "menu_options": { - "manual_entry": "Zadajte manu\u00e1lne" + "manual_entry": "Zadajte manu\u00e1lne", + "pick_implementation": "Importova\u0165 z LaMetric.com (odpor\u00fa\u010da sa)" } }, "manual_entry": { "data": { "api_key": "API k\u013e\u00fa\u010d", "host": "Hostite\u013e" + }, + "data_description": { + "api_key": "Tento k\u013e\u00fa\u010d API n\u00e1jdete na [str\u00e1nke zariaden\u00ed vo svojom \u00fa\u010dte v\u00fdvoj\u00e1ra LaMetric](https://developer.lametric.com/user/devices).", + "host": "IP adresa alebo n\u00e1zov hostite\u013ea v\u00e1\u0161ho LaMetric TIME vo va\u0161ej sieti." } }, "pick_implementation": { "title": "Vyberte met\u00f3du overenia" + }, + "user_cloud_select_device": { + "data": { + "device": "Vyberte zariadenie LaMetric, ktor\u00e9 chcete prida\u0165" + } } } + }, + "entity": { + "select": { + "brightness_mode": { + "state": { + "auto": "Automatick\u00e9", + "manual": "Manu\u00e1lne" + } + } + } + }, + "issues": { + "manual_migration": { + "description": "Integr\u00e1cia LaMetric bola modernizovan\u00e1: Teraz je nakonfigurovan\u00e1 a nastaven\u00e1 prostredn\u00edctvom pou\u017e\u00edvate\u013esk\u00e9ho rozhrania a komunik\u00e1cia je teraz lok\u00e1lna. \n\n\u017dia\u013e, nie je mo\u017en\u00e1 \u017eiadna cesta automatickej migr\u00e1cie, a preto je potrebn\u00e9, aby ste svoj LaMetric znova nastavili pomocou Home Assistant. Inform\u00e1cie o tom, ako ho nastavi\u0165, n\u00e1jdete v integra\u010dnej dokument\u00e1cii Home Assistant LaMetric. \n\nOdstr\u00e1\u0148te star\u00fa konfigur\u00e1ciu LaMetric YAML zo s\u00faboru configuration.yaml a re\u0161tartujte Home Assistant, aby ste tento probl\u00e9m vyrie\u0161ili.", + "title": "Pre LaMetric je potrebn\u00e1 manu\u00e1lna migr\u00e1cia" + } } } \ No newline at end of file diff --git a/homeassistant/components/lametric/translations/zh-Hant.json b/homeassistant/components/lametric/translations/zh-Hant.json index 56697150747..6ee5ea2f1b4 100644 --- a/homeassistant/components/lametric/translations/zh-Hant.json +++ b/homeassistant/components/lametric/translations/zh-Hant.json @@ -44,6 +44,16 @@ } } }, + "entity": { + "select": { + "brightness_mode": { + "state": { + "auto": "\u81ea\u52d5", + "manual": "\u624b\u52d5" + } + } + } + }, "issues": { "manual_migration": { "description": "LaMetric \u6574\u5408\u5df2\u7d93\u9032\u884c\u66f4\u65b0\uff1a\u73fe\u5728\u53ef\u900f\u904e\u4f7f\u7528\u8005\u4ecb\u9762\u9032\u884c\u8a2d\u5b9a\u3001\u4e26\u4e14\u70ba\u672c\u5730\u7aef\u901a\u8a0a\u65b9\u5f0f\u3002\n\n\u4e0d\u5e78\u7684\u3001\u76ee\u524d\u6c92\u6709\u81ea\u52d5\u8f49\u79fb\u7684\u529f\u80fd\uff0c\u9700\u8981\u65bc Home Assistant \u91cd\u65b0\u8f38\u8a2d\u5b9a LaMetric\u3002\u8acb\u53c3\u95b1 Home Assistant LaMetric \u6574\u5408\u6587\u4ef6\u4ee5\u4e86\u89e3\u5982\u4f55\u9032\u884c\u8a2d\u5b9a\u3002\n\n\u7531 configuration.yaml \u6a94\u6848\u4e2d\u79fb\u9664 LaMetric YAML \u8a2d\u5b9a\u4e26\u91cd\u555f Home Assistant \u4ee5\u4fee\u6b63\u6b64\u554f\u984c\u3002", diff --git a/homeassistant/components/landisgyr_heat_meter/const.py b/homeassistant/components/landisgyr_heat_meter/const.py index 7767a491f3b..aae62f926d8 100644 --- a/homeassistant/components/landisgyr_heat_meter/const.py +++ b/homeassistant/components/landisgyr_heat_meter/const.py @@ -6,13 +6,12 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.const import ( - ENERGY_MEGA_WATT_HOUR, - POWER_KILO_WATT, - TEMP_CELSIUS, - TIME_HOURS, - TIME_MINUTES, - VOLUME_CUBIC_METERS, - VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR, + UnitOfEnergy, + UnitOfPower, + UnitOfTemperature, + UnitOfTime, + UnitOfVolume, + UnitOfVolumeFlowRate, ) from homeassistant.helpers.entity import EntityCategory @@ -26,7 +25,7 @@ HEAT_METER_SENSOR_TYPES = ( key="heat_usage", icon="mdi:fire", name="Heat usage", - native_unit_of_measurement=ENERGY_MEGA_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.MEGA_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL, ), @@ -35,7 +34,7 @@ HEAT_METER_SENSOR_TYPES = ( icon="mdi:fire", name="Volume usage", device_class=SensorDeviceClass.VOLUME, - native_unit_of_measurement=VOLUME_CUBIC_METERS, + native_unit_of_measurement=UnitOfVolume.CUBIC_METERS, state_class=SensorStateClass.TOTAL, ), # Diagnostic entity for debugging, this will match the value in GJ indicated on the meter's display @@ -50,7 +49,8 @@ HEAT_METER_SENSOR_TYPES = ( key="heat_previous_year", icon="mdi:fire", name="Heat usage previous year", - native_unit_of_measurement=ENERGY_MEGA_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.MEGA_WATT_HOUR, + device_class=SensorDeviceClass.ENERGY, entity_category=EntityCategory.DIAGNOSTIC, ), # Diagnostic entity for debugging, this will match the value in GJ of previous year indicated on the meter's display @@ -66,7 +66,7 @@ HEAT_METER_SENSOR_TYPES = ( icon="mdi:fire", name="Volume usage previous year", device_class=SensorDeviceClass.VOLUME, - native_unit_of_measurement=VOLUME_CUBIC_METERS, + native_unit_of_measurement=UnitOfVolume.CUBIC_METERS, entity_category=EntityCategory.DIAGNOSTIC, ), SensorEntityDescription( @@ -91,62 +91,62 @@ HEAT_METER_SENSOR_TYPES = ( key="measurement_period_minutes", name="Measurement period minutes", device_class=SensorDeviceClass.DURATION, - native_unit_of_measurement=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, entity_category=EntityCategory.DIAGNOSTIC, ), SensorEntityDescription( key="power_max_kw", name="Power max", - native_unit_of_measurement=POWER_KILO_WATT, + native_unit_of_measurement=UnitOfPower.KILO_WATT, device_class=SensorDeviceClass.POWER, entity_category=EntityCategory.DIAGNOSTIC, ), SensorEntityDescription( key="power_max_previous_year_kw", name="Power max previous year", - native_unit_of_measurement=POWER_KILO_WATT, + native_unit_of_measurement=UnitOfPower.KILO_WATT, device_class=SensorDeviceClass.POWER, entity_category=EntityCategory.DIAGNOSTIC, ), SensorEntityDescription( key="flowrate_max_m3ph", name="Flowrate max", - native_unit_of_measurement=VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR, + native_unit_of_measurement=UnitOfVolumeFlowRate.CUBIC_METERS_PER_HOUR, icon="mdi:water-outline", entity_category=EntityCategory.DIAGNOSTIC, ), SensorEntityDescription( key="flowrate_max_previous_year_m3ph", name="Flowrate max previous year", - native_unit_of_measurement=VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR, + native_unit_of_measurement=UnitOfVolumeFlowRate.CUBIC_METERS_PER_HOUR, icon="mdi:water-outline", entity_category=EntityCategory.DIAGNOSTIC, ), SensorEntityDescription( key="return_temperature_max_c", name="Return temperature max", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, entity_category=EntityCategory.DIAGNOSTIC, ), SensorEntityDescription( key="return_temperature_max_previous_year_c", name="Return temperature max previous year", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, entity_category=EntityCategory.DIAGNOSTIC, ), SensorEntityDescription( key="flow_temperature_max_c", name="Flow temperature max", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, entity_category=EntityCategory.DIAGNOSTIC, ), SensorEntityDescription( key="flow_temperature_max_previous_year_c", name="Flow temperature max previous year", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, entity_category=EntityCategory.DIAGNOSTIC, ), @@ -154,28 +154,28 @@ HEAT_METER_SENSOR_TYPES = ( key="operating_hours", name="Operating hours", device_class=SensorDeviceClass.DURATION, - native_unit_of_measurement=TIME_HOURS, + native_unit_of_measurement=UnitOfTime.HOURS, entity_category=EntityCategory.DIAGNOSTIC, ), SensorEntityDescription( key="flow_hours", name="Flow hours", device_class=SensorDeviceClass.DURATION, - native_unit_of_measurement=TIME_HOURS, + native_unit_of_measurement=UnitOfTime.HOURS, entity_category=EntityCategory.DIAGNOSTIC, ), SensorEntityDescription( key="fault_hours", name="Fault hours", device_class=SensorDeviceClass.DURATION, - native_unit_of_measurement=TIME_HOURS, + native_unit_of_measurement=UnitOfTime.HOURS, entity_category=EntityCategory.DIAGNOSTIC, ), SensorEntityDescription( key="fault_hours_previous_year", name="Fault hours previous year", device_class=SensorDeviceClass.DURATION, - native_unit_of_measurement=TIME_HOURS, + native_unit_of_measurement=UnitOfTime.HOURS, entity_category=EntityCategory.DIAGNOSTIC, ), SensorEntityDescription( @@ -200,7 +200,7 @@ HEAT_METER_SENSOR_TYPES = ( SensorEntityDescription( key="measuring_range_m3ph", name="Measuring range", - native_unit_of_measurement=VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR, + native_unit_of_measurement=UnitOfVolumeFlowRate.CUBIC_METERS_PER_HOUR, icon="mdi:water-outline", entity_category=EntityCategory.DIAGNOSTIC, ), diff --git a/homeassistant/components/landisgyr_heat_meter/translations/ko.json b/homeassistant/components/landisgyr_heat_meter/translations/ko.json new file mode 100644 index 00000000000..36bffa3e9a6 --- /dev/null +++ b/homeassistant/components/landisgyr_heat_meter/translations/ko.json @@ -0,0 +1,14 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "step": { + "setup_serial_manual_path": { + "data": { + "device": "USB \uc7a5\uce58 \uacbd\ub85c" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/landisgyr_heat_meter/translations/pt.json b/homeassistant/components/landisgyr_heat_meter/translations/pt.json new file mode 100644 index 00000000000..99eb59399fe --- /dev/null +++ b/homeassistant/components/landisgyr_heat_meter/translations/pt.json @@ -0,0 +1,14 @@ +{ + "config": { + "abort": { + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" + }, + "step": { + "setup_serial_manual_path": { + "data": { + "device": "Caminho do Dispositivo USB" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/launch_library/translations/ko.json b/homeassistant/components/launch_library/translations/ko.json new file mode 100644 index 00000000000..416fb8d164e --- /dev/null +++ b/homeassistant/components/launch_library/translations/ko.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/launch_library/translations/sk.json b/homeassistant/components/launch_library/translations/sk.json index c294bc45d7c..6567baaaae9 100644 --- a/homeassistant/components/launch_library/translations/sk.json +++ b/homeassistant/components/launch_library/translations/sk.json @@ -2,6 +2,11 @@ "config": { "abort": { "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, + "step": { + "user": { + "description": "Chcete nakonfigurova\u0165 Launch Library?" + } } } } \ No newline at end of file diff --git a/homeassistant/components/laundrify/binary_sensor.py b/homeassistant/components/laundrify/binary_sensor.py index 2f64d8cee78..fb9bac987bb 100644 --- a/homeassistant/components/laundrify/binary_sensor.py +++ b/homeassistant/components/laundrify/binary_sensor.py @@ -25,7 +25,9 @@ async def async_setup_entry( ) -> None: """Set up sensors from a config entry created in the integrations UI.""" - coordinator = hass.data[DOMAIN][config.entry_id]["coordinator"] + coordinator: LaundrifyUpdateCoordinator = hass.data[DOMAIN][config.entry_id][ + "coordinator" + ] async_add_entities( LaundrifyPowerPlug(coordinator, device) for device in coordinator.data.values() @@ -39,6 +41,7 @@ class LaundrifyPowerPlug( _attr_device_class = BinarySensorDeviceClass.RUNNING _attr_icon = "mdi:washing-machine" + _attr_unique_id: str def __init__( self, coordinator: LaundrifyUpdateCoordinator, device: LaundrifyDevice @@ -63,7 +66,7 @@ class LaundrifyPowerPlug( def available(self) -> bool: """Check if the device is available.""" return ( - self.unique_id in self.coordinator.data + self._attr_unique_id in self.coordinator.data and self.coordinator.last_update_success ) @@ -80,5 +83,5 @@ class LaundrifyPowerPlug( @callback def _handle_coordinator_update(self) -> None: """Handle updated data from the coordinator.""" - self._device = self.coordinator.data[self.unique_id] + self._device = self.coordinator.data[self._attr_unique_id] super()._handle_coordinator_update() diff --git a/homeassistant/components/laundrify/coordinator.py b/homeassistant/components/laundrify/coordinator.py index 31447490506..47728a38983 100644 --- a/homeassistant/components/laundrify/coordinator.py +++ b/homeassistant/components/laundrify/coordinator.py @@ -16,7 +16,7 @@ from .model import LaundrifyDevice _LOGGER = logging.getLogger(__name__) -class LaundrifyUpdateCoordinator(DataUpdateCoordinator): +class LaundrifyUpdateCoordinator(DataUpdateCoordinator[dict[str, LaundrifyDevice]]): """Class to manage fetching laundrify API data.""" def __init__( diff --git a/homeassistant/components/laundrify/translations/ko.json b/homeassistant/components/laundrify/translations/ko.json new file mode 100644 index 00000000000..3bb034dc427 --- /dev/null +++ b/homeassistant/components/laundrify/translations/ko.json @@ -0,0 +1,17 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "reauth_confirm": { + "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/laundrify/translations/sk.json b/homeassistant/components/laundrify/translations/sk.json index f8f52f3f99d..ab676e9ee5c 100644 --- a/homeassistant/components/laundrify/translations/sk.json +++ b/homeassistant/components/laundrify/translations/sk.json @@ -13,9 +13,11 @@ "init": { "data": { "code": "Autoriza\u010dn\u00fd k\u00f3d (xxx-xxx)" - } + }, + "description": "Zadajte svoj osobn\u00fd autoriza\u010dn\u00fd k\u00f3d, ktor\u00fd je zobrazen\u00fd v aplik\u00e1cii laundrify-App." }, "reauth_confirm": { + "description": "Integr\u00e1cia laundrify sa mus\u00ed znovu overi\u0165.", "title": "Znova overi\u0165 integr\u00e1ciu" } } diff --git a/homeassistant/components/lcn/__init__.py b/homeassistant/components/lcn/__init__.py index d4a20c4d849..2e1185fd692 100644 --- a/homeassistant/components/lcn/__init__.py +++ b/homeassistant/components/lcn/__init__.py @@ -99,8 +99,10 @@ async def async_setup_entry( return False except pypck.connection.PchkLicenseError: _LOGGER.warning( - 'Maximum number of connections on PCHK "%s" was ' - "reached. An additional license key is required", + ( + 'Maximum number of connections on PCHK "%s" was ' + "reached. An additional license key is required" + ), config_entry.title, ) return False @@ -269,7 +271,10 @@ class LcnEntity(Entity): def device_info(self) -> DeviceInfo | None: """Return device specific attributes.""" address = f"{'g' if self.address[2] else 'm'}{self.address[0]:03d}{self.address[1]:03d}" - model = f"LCN resource ({get_device_model(self.config[CONF_DOMAIN], self.config[CONF_DOMAIN_DATA])})" + model = ( + "LCN resource" + f" ({get_device_model(self.config[CONF_DOMAIN], self.config[CONF_DOMAIN_DATA])})" + ) return { "identifiers": {(DOMAIN, self.unique_id)}, diff --git a/homeassistant/components/lcn/climate.py b/homeassistant/components/lcn/climate.py index 8d701fbfa2f..4f40bcd25cd 100644 --- a/homeassistant/components/lcn/climate.py +++ b/homeassistant/components/lcn/climate.py @@ -19,8 +19,7 @@ from homeassistant.const import ( CONF_ENTITIES, CONF_SOURCE, CONF_UNIT_OF_MEASUREMENT, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -114,10 +113,10 @@ class LcnClimate(LcnEntity, ClimateEntity): @property def temperature_unit(self) -> str: """Return the unit of measurement.""" - # Config schema only allows for: TEMP_CELSIUS and TEMP_FAHRENHEIT + # Config schema only allows for: UnitOfTemperature.CELSIUS and UnitOfTemperature.FAHRENHEIT if self.unit == pypck.lcn_defs.VarUnit.FAHRENHEIT: - return TEMP_FAHRENHEIT - return TEMP_CELSIUS + return UnitOfTemperature.FAHRENHEIT + return UnitOfTemperature.CELSIUS @property def current_temperature(self) -> float | None: diff --git a/homeassistant/components/lcn/config_flow.py b/homeassistant/components/lcn/config_flow.py index a8c5a311971..09f588b6953 100644 --- a/homeassistant/components/lcn/config_flow.py +++ b/homeassistant/components/lcn/config_flow.py @@ -81,8 +81,10 @@ class LcnFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): return self.async_abort(reason="authentication_error") except pypck.connection.PchkLicenseError: _LOGGER.warning( - 'Maximum number of connections on PCHK "%s" was ' - "reached. An additional license key is required", + ( + 'Maximum number of connections on PCHK "%s" was ' + "reached. An additional license key is required" + ), host_name, ) return self.async_abort(reason="license_error") diff --git a/homeassistant/components/lcn/const.py b/homeassistant/components/lcn/const.py index ef0ad6481f4..bb97658b880 100644 --- a/homeassistant/components/lcn/const.py +++ b/homeassistant/components/lcn/const.py @@ -1,15 +1,7 @@ """Constants for the LCN component.""" from itertools import product -from homeassistant.const import ( - DEGREE, - ELECTRIC_POTENTIAL_VOLT, - PERCENTAGE, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, - TEMP_KELVIN, - Platform, -) +from homeassistant.const import Platform PLATFORMS = [ Platform.BINARY_SENSOR, @@ -166,9 +158,9 @@ VAR_UNITS = [ "", "LCN", "NATIVE", - TEMP_CELSIUS, - TEMP_KELVIN, - TEMP_FAHRENHEIT, + "°C", + "K", + "°F", "LUX_T", "LX_T", "LUX_I", @@ -176,16 +168,16 @@ VAR_UNITS = [ "LX", "M/S", "METERPERSECOND", - PERCENTAGE, + "%", "PERCENT", "PPM", "VOLT", - ELECTRIC_POTENTIAL_VOLT, + "V", "AMPERE", "AMP", "A", "DEGREE", - DEGREE, + "°", ] RELVARREF = ["CURRENT", "PROG"] diff --git a/homeassistant/components/lcn/manifest.json b/homeassistant/components/lcn/manifest.json index eea72a0e508..8a962db3514 100644 --- a/homeassistant/components/lcn/manifest.json +++ b/homeassistant/components/lcn/manifest.json @@ -3,7 +3,7 @@ "name": "LCN", "config_flow": false, "documentation": "https://www.home-assistant.io/integrations/lcn", - "requirements": ["pypck==0.7.15"], + "requirements": ["pypck==0.7.16"], "codeowners": ["@alengwenus"], "iot_class": "local_push", "loggers": ["pypck"] diff --git a/homeassistant/components/lcn/schemas.py b/homeassistant/components/lcn/schemas.py index 290f7dd55f4..bd02c80da66 100644 --- a/homeassistant/components/lcn/schemas.py +++ b/homeassistant/components/lcn/schemas.py @@ -17,8 +17,7 @@ from homeassistant.const import ( CONF_SWITCHES, CONF_UNIT_OF_MEASUREMENT, CONF_USERNAME, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, + UnitOfTemperature, ) import homeassistant.helpers.config_validation as cv @@ -74,8 +73,8 @@ DOMAIN_DATA_CLIMATE = { vol.Optional(CONF_MAX_TEMP, default=DEFAULT_MAX_TEMP): vol.Coerce(float), vol.Optional(CONF_MIN_TEMP, default=DEFAULT_MIN_TEMP): vol.Coerce(float), vol.Optional(CONF_LOCKABLE, default=False): vol.Coerce(bool), - vol.Optional(CONF_UNIT_OF_MEASUREMENT, default=TEMP_CELSIUS): vol.In( - TEMP_CELSIUS, TEMP_FAHRENHEIT + vol.Optional(CONF_UNIT_OF_MEASUREMENT, default=UnitOfTemperature.CELSIUS): vol.In( + UnitOfTemperature.CELSIUS, UnitOfTemperature.FAHRENHEIT ), } diff --git a/homeassistant/components/lcn/services.py b/homeassistant/components/lcn/services.py index c55e6586edf..ca07dbe0ef6 100644 --- a/homeassistant/components/lcn/services.py +++ b/homeassistant/components/lcn/services.py @@ -9,7 +9,6 @@ from homeassistant.const import ( CONF_HOST, CONF_STATE, CONF_UNIT_OF_MEASUREMENT, - TIME_SECONDS, ) from homeassistant.core import HomeAssistant, ServiceCall import homeassistant.helpers.config_validation as cv @@ -195,7 +194,7 @@ class VarAbs(LcnServiceCall): vol.Required(CONF_VARIABLE): vol.All( vol.Upper, vol.In(VARIABLES + SETPOINTS) ), - vol.Optional(CONF_VALUE, default=0): cv.positive_int, + vol.Optional(CONF_VALUE, default=0): vol.Coerce(float), vol.Optional(CONF_UNIT_OF_MEASUREMENT, default="native"): vol.All( vol.Upper, vol.In(VAR_UNITS) ), @@ -235,7 +234,7 @@ class VarRel(LcnServiceCall): vol.Required(CONF_VARIABLE): vol.All( vol.Upper, vol.In(VARIABLES + SETPOINTS + THRESHOLDS) ), - vol.Optional(CONF_VALUE, default=0): int, + vol.Optional(CONF_VALUE, default=0): vol.Coerce(float), vol.Optional(CONF_UNIT_OF_MEASUREMENT, default="native"): vol.All( vol.Upper, vol.In(VAR_UNITS) ), @@ -288,7 +287,7 @@ class SendKeys(LcnServiceCall): vol.Upper, vol.In(SENDKEYCOMMANDS) ), vol.Optional(CONF_TIME, default=0): cv.positive_int, - vol.Optional(CONF_TIME_UNIT, default=TIME_SECONDS): vol.All( + vol.Optional(CONF_TIME_UNIT, default="S"): vol.All( vol.Upper, vol.In(TIME_UNITS) ), } @@ -330,7 +329,7 @@ class LockKeys(LcnServiceCall): ), vol.Required(CONF_STATE): is_states_string, vol.Optional(CONF_TIME, default=0): cv.positive_int, - vol.Optional(CONF_TIME_UNIT, default=TIME_SECONDS): vol.All( + vol.Optional(CONF_TIME_UNIT, default="S"): vol.All( vol.Upper, vol.In(TIME_UNITS) ), } diff --git a/homeassistant/components/lcn/translations/pl.json b/homeassistant/components/lcn/translations/pl.json index 2a2415d4138..db85087f6c2 100644 --- a/homeassistant/components/lcn/translations/pl.json +++ b/homeassistant/components/lcn/translations/pl.json @@ -3,7 +3,7 @@ "trigger_type": { "codelock": "otrzymano kod blokady", "fingerprint": "otrzymano kod odcisku palca", - "send_keys": "otrzymano klucze wysy\u0142ania", + "send_keys": "otrzymano klucze", "transmitter": "otrzymano kod nadajnika", "transponder": "otrzymano kod transpondera" } diff --git a/homeassistant/components/lcn/translations/sk.json b/homeassistant/components/lcn/translations/sk.json index 41b4464c18f..ecb0bf6ae64 100644 --- a/homeassistant/components/lcn/translations/sk.json +++ b/homeassistant/components/lcn/translations/sk.json @@ -3,7 +3,9 @@ "trigger_type": { "codelock": "prijat\u00fd k\u00f3d z\u00e1mku", "fingerprint": "prijat\u00fd k\u00f3d odtla\u010dku prsta", - "send_keys": "odosla\u0165 prijat\u00e9 k\u013e\u00fa\u010de" + "send_keys": "odosla\u0165 prijat\u00e9 k\u013e\u00fa\u010de", + "transmitter": "prijat\u00fd k\u00f3d vysiela\u010da", + "transponder": "prijat\u00fd k\u00f3d transpond\u00e9ra" } } } \ No newline at end of file diff --git a/homeassistant/components/led_ble/__init__.py b/homeassistant/components/led_ble/__init__.py index 9dd6529df0d..768300ff534 100644 --- a/homeassistant/components/led_ble/__init__.py +++ b/homeassistant/components/led_ble/__init__.py @@ -6,7 +6,7 @@ from datetime import timedelta import logging import async_timeout -from led_ble import BLEAK_EXCEPTIONS, LEDBLE, get_device +from led_ble import BLEAK_EXCEPTIONS, LEDBLE from homeassistant.components import bluetooth from homeassistant.components.bluetooth.match import ADDRESS, BluetoothCallbackMatcher @@ -27,9 +27,7 @@ _LOGGER = logging.getLogger(__name__) async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up LED BLE from a config entry.""" address: str = entry.data[CONF_ADDRESS] - ble_device = bluetooth.async_ble_device_from_address( - hass, address.upper(), True - ) or await get_device(address) + ble_device = bluetooth.async_ble_device_from_address(hass, address.upper(), True) if not ble_device: raise ConfigEntryNotReady( f"Could not find LED BLE device with address {address}" diff --git a/homeassistant/components/led_ble/config_flow.py b/homeassistant/components/led_ble/config_flow.py index d757b5021af..55b7ed45793 100644 --- a/homeassistant/components/led_ble/config_flow.py +++ b/homeassistant/components/led_ble/config_flow.py @@ -104,7 +104,9 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): { vol.Required(CONF_ADDRESS): vol.In( { - service_info.address: f"{service_info.name} ({service_info.address})" + service_info.address: ( + f"{service_info.name} ({service_info.address})" + ) for service_info in self._discovered_devices.values() } ), diff --git a/homeassistant/components/led_ble/translations/ko.json b/homeassistant/components/led_ble/translations/ko.json new file mode 100644 index 00000000000..5a37c98cbf5 --- /dev/null +++ b/homeassistant/components/led_ble/translations/ko.json @@ -0,0 +1,13 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", + "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/led_ble/translations/pt.json b/homeassistant/components/led_ble/translations/pt.json index c7268106706..dd104d17b68 100644 --- a/homeassistant/components/led_ble/translations/pt.json +++ b/homeassistant/components/led_ble/translations/pt.json @@ -1,9 +1,16 @@ { "config": { "abort": { + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", + "already_in_progress": "O processo de configura\u00e7\u00e3o j\u00e1 est\u00e1 a decorrer", + "no_devices_found": "Nenhum dispositivo encontrado na rede", "no_unconfigured_devices": "Nenhum dispositivo n\u00e3o configurado encontrado.", "not_supported": "Dispositivo n\u00e3o suportado" }, + "error": { + "cannot_connect": "A liga\u00e7\u00e3o falhou", + "unknown": "Erro inesperado" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/lg_soundbar/translations/ca.json b/homeassistant/components/lg_soundbar/translations/ca.json index b49df6df970..565d8f44118 100644 --- a/homeassistant/components/lg_soundbar/translations/ca.json +++ b/homeassistant/components/lg_soundbar/translations/ca.json @@ -4,7 +4,8 @@ "already_configured": "El dispositiu ja est\u00e0 configurat" }, "error": { - "cannot_connect": "Ha fallat la connexi\u00f3" + "cannot_connect": "Ha fallat la connexi\u00f3", + "no_data": "El dispositiu no ha retornat dades necess\u00e0ries per a cap entrada." }, "step": { "user": { diff --git a/homeassistant/components/lg_soundbar/translations/de.json b/homeassistant/components/lg_soundbar/translations/de.json index 4c1588bfcad..0c4b4703b41 100644 --- a/homeassistant/components/lg_soundbar/translations/de.json +++ b/homeassistant/components/lg_soundbar/translations/de.json @@ -4,7 +4,8 @@ "already_configured": "Ger\u00e4t ist bereits konfiguriert" }, "error": { - "cannot_connect": "Verbindung fehlgeschlagen" + "cannot_connect": "Verbindung fehlgeschlagen", + "no_data": "Das Ger\u00e4t hat keine f\u00fcr einen Eintrag erforderlichen Daten zur\u00fcckgegeben." }, "step": { "user": { diff --git a/homeassistant/components/lg_soundbar/translations/el.json b/homeassistant/components/lg_soundbar/translations/el.json index 687f872ea14..066fa9d310c 100644 --- a/homeassistant/components/lg_soundbar/translations/el.json +++ b/homeassistant/components/lg_soundbar/translations/el.json @@ -4,7 +4,8 @@ "already_configured": "\u0397 \u03c5\u03c0\u03b7\u03c1\u03b5\u03c3\u03af\u03b1 \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af" }, "error": { - "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2" + "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", + "no_data": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b4\u03b5\u03bd \u03b5\u03c0\u03ad\u03c3\u03c4\u03c1\u03b5\u03c8\u03b5 \u03ba\u03b1\u03bd\u03ad\u03bd\u03b1 \u03c3\u03c4\u03bf\u03b9\u03c7\u03b5\u03af\u03bf \u03c0\u03bf\u03c5 \u03b1\u03c0\u03b1\u03b9\u03c4\u03b5\u03af\u03c4\u03b1\u03b9 \u03b3\u03b9\u03b1 \u03bc\u03b9\u03b1 \u03ba\u03b1\u03c4\u03b1\u03c7\u03ce\u03c1\u03b9\u03c3\u03b7." }, "step": { "user": { diff --git a/homeassistant/components/lg_soundbar/translations/en.json b/homeassistant/components/lg_soundbar/translations/en.json index 19f5027d470..befcd5749dd 100644 --- a/homeassistant/components/lg_soundbar/translations/en.json +++ b/homeassistant/components/lg_soundbar/translations/en.json @@ -1,11 +1,11 @@ { "config": { "abort": { - "already_configured": "Device is already configured", - "no_uuid": "Device missing unique identification required for discovery." + "already_configured": "Device is already configured" }, "error": { - "cannot_connect": "Failed to connect" + "cannot_connect": "Failed to connect", + "no_data": "Device did not return any data required to an entry." }, "step": { "user": { @@ -15,4 +15,4 @@ } } } -} +} \ No newline at end of file diff --git a/homeassistant/components/lg_soundbar/translations/es.json b/homeassistant/components/lg_soundbar/translations/es.json index b9b39a74d9d..7b3b318edd4 100644 --- a/homeassistant/components/lg_soundbar/translations/es.json +++ b/homeassistant/components/lg_soundbar/translations/es.json @@ -4,7 +4,8 @@ "already_configured": "El dispositivo ya est\u00e1 configurado" }, "error": { - "cannot_connect": "No se pudo conectar" + "cannot_connect": "No se pudo conectar", + "no_data": "El dispositivo no devolvi\u00f3 ning\u00fan dato requerido para una entrada." }, "step": { "user": { diff --git a/homeassistant/components/lg_soundbar/translations/et.json b/homeassistant/components/lg_soundbar/translations/et.json index 2ee852479c9..e798119834e 100644 --- a/homeassistant/components/lg_soundbar/translations/et.json +++ b/homeassistant/components/lg_soundbar/translations/et.json @@ -4,7 +4,8 @@ "already_configured": "Seade on juba h\u00e4\u00e4lestatud" }, "error": { - "cannot_connect": "\u00dchendamine nurjus" + "cannot_connect": "\u00dchendamine nurjus", + "no_data": "Seade ei tagastanud kande jaoks vajalikke andmeid." }, "step": { "user": { diff --git a/homeassistant/components/lg_soundbar/translations/hu.json b/homeassistant/components/lg_soundbar/translations/hu.json index 382c4f2d8d3..e560fba262e 100644 --- a/homeassistant/components/lg_soundbar/translations/hu.json +++ b/homeassistant/components/lg_soundbar/translations/hu.json @@ -4,7 +4,8 @@ "already_configured": "A szolg\u00e1ltat\u00e1s m\u00e1r konfigur\u00e1lva van" }, "error": { - "cannot_connect": "Sikertelen csatlakoz\u00e1s" + "cannot_connect": "Sikertelen csatlakoz\u00e1s", + "no_data": "Az eszk\u00f6z nem adott vissza semmilyen entit\u00e1shoz haszn\u00e1lhat\u00f3 adatot." }, "step": { "user": { diff --git a/homeassistant/components/lg_soundbar/translations/id.json b/homeassistant/components/lg_soundbar/translations/id.json index c8236f5ec73..23ea630c3b3 100644 --- a/homeassistant/components/lg_soundbar/translations/id.json +++ b/homeassistant/components/lg_soundbar/translations/id.json @@ -4,7 +4,8 @@ "already_configured": "Perangkat sudah dikonfigurasi" }, "error": { - "cannot_connect": "Gagal terhubung" + "cannot_connect": "Gagal terhubung", + "no_data": "Perangkat tidak mengembalikan data apa pun yang diperlukan ke entri." }, "step": { "user": { diff --git a/homeassistant/components/lg_soundbar/translations/it.json b/homeassistant/components/lg_soundbar/translations/it.json index 9cb86e4ee5a..15bab635df2 100644 --- a/homeassistant/components/lg_soundbar/translations/it.json +++ b/homeassistant/components/lg_soundbar/translations/it.json @@ -4,7 +4,8 @@ "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato" }, "error": { - "cannot_connect": "Impossibile connettersi" + "cannot_connect": "Impossibile connettersi", + "no_data": "Il dispositivo non ha restituito alcun dato richiesto per una voce." }, "step": { "user": { diff --git a/homeassistant/components/lg_soundbar/translations/ko.json b/homeassistant/components/lg_soundbar/translations/ko.json new file mode 100644 index 00000000000..667944d1591 --- /dev/null +++ b/homeassistant/components/lg_soundbar/translations/ko.json @@ -0,0 +1,18 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "no_data": "\uae30\uae30\uac00 \uad6c\uc131\uc694\uc18c\uc5d0 \ud544\uc694\ud55c \ub370\uc774\ud130\ub97c \ubc18\ud658\ud558\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4." + }, + "step": { + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/lg_soundbar/translations/no.json b/homeassistant/components/lg_soundbar/translations/no.json index 7457ffad8a4..9769b91ebd9 100644 --- a/homeassistant/components/lg_soundbar/translations/no.json +++ b/homeassistant/components/lg_soundbar/translations/no.json @@ -4,7 +4,8 @@ "already_configured": "Enheten er allerede konfigurert" }, "error": { - "cannot_connect": "Tilkobling mislyktes" + "cannot_connect": "Tilkobling mislyktes", + "no_data": "Enheten returnerte ingen data som kreves til en oppf\u00f8ring." }, "step": { "user": { diff --git a/homeassistant/components/lg_soundbar/translations/pl.json b/homeassistant/components/lg_soundbar/translations/pl.json index 1b4e47469aa..f1705815f83 100644 --- a/homeassistant/components/lg_soundbar/translations/pl.json +++ b/homeassistant/components/lg_soundbar/translations/pl.json @@ -4,7 +4,8 @@ "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane" }, "error": { - "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia" + "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", + "no_data": "Urz\u0105dzenie nie zwr\u00f3ci\u0142o \u017cadnych danych wymaganych do wpisu." }, "step": { "user": { diff --git a/homeassistant/components/lg_soundbar/translations/pt-BR.json b/homeassistant/components/lg_soundbar/translations/pt-BR.json index fb9b1f4c79e..ef7b548e101 100644 --- a/homeassistant/components/lg_soundbar/translations/pt-BR.json +++ b/homeassistant/components/lg_soundbar/translations/pt-BR.json @@ -4,7 +4,8 @@ "already_configured": "Dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha ao conectar" + "cannot_connect": "Falha ao conectar", + "no_data": "O dispositivo n\u00e3o retornou nenhum dado necess\u00e1rio para uma entrada." }, "step": { "user": { diff --git a/homeassistant/components/lg_soundbar/translations/pt.json b/homeassistant/components/lg_soundbar/translations/pt.json index 91ff56e73f8..6d81843965d 100644 --- a/homeassistant/components/lg_soundbar/translations/pt.json +++ b/homeassistant/components/lg_soundbar/translations/pt.json @@ -1,15 +1,15 @@ { "config": { "abort": { - "already_configured": "O servi\u00e7o j\u00e1 est\u00e1 configurado" + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "user": { "data": { - "host": "Servidor" + "host": "Endere\u00e7o" } } } diff --git a/homeassistant/components/lg_soundbar/translations/ru.json b/homeassistant/components/lg_soundbar/translations/ru.json index 2475f72e796..0473a2cb470 100644 --- a/homeassistant/components/lg_soundbar/translations/ru.json +++ b/homeassistant/components/lg_soundbar/translations/ru.json @@ -4,7 +4,8 @@ "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant." }, "error": { - "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f." + "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", + "no_data": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043d\u0435 \u0432\u0435\u0440\u043d\u0443\u043b\u043e \u043d\u0438\u043a\u0430\u043a\u0438\u0445 \u0434\u0430\u043d\u043d\u044b\u0445, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0445 \u0434\u043b\u044f \u0437\u0430\u043f\u0438\u0441\u0438." }, "step": { "user": { diff --git a/homeassistant/components/lg_soundbar/translations/sk.json b/homeassistant/components/lg_soundbar/translations/sk.json index c8e33ffce5e..3fe149be234 100644 --- a/homeassistant/components/lg_soundbar/translations/sk.json +++ b/homeassistant/components/lg_soundbar/translations/sk.json @@ -4,7 +4,8 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { - "cannot_connect": "Nepodarilo sa pripoji\u0165" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "no_data": "Zariadenie nevr\u00e1tilo \u017eiadne po\u017eadovan\u00e9 \u00fadaje k z\u00e1znamu." }, "step": { "user": { diff --git a/homeassistant/components/lg_soundbar/translations/zh-Hant.json b/homeassistant/components/lg_soundbar/translations/zh-Hant.json index 7582133c92f..c53351f237c 100644 --- a/homeassistant/components/lg_soundbar/translations/zh-Hant.json +++ b/homeassistant/components/lg_soundbar/translations/zh-Hant.json @@ -4,7 +4,8 @@ "already_configured": "\u670d\u52d9\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210" }, "error": { - "cannot_connect": "\u9023\u7dda\u5931\u6557" + "cannot_connect": "\u9023\u7dda\u5931\u6557", + "no_data": "\u88dd\u7f6e\u4e26\u672a\u56de\u50b3\u5be6\u9ad4\u6240\u9700\u7684\u8cc7\u6599\u3002" }, "step": { "user": { diff --git a/homeassistant/components/lidarr/const.py b/homeassistant/components/lidarr/const.py index 08e284b9b31..feadedb6d49 100644 --- a/homeassistant/components/lidarr/const.py +++ b/homeassistant/components/lidarr/const.py @@ -2,35 +2,25 @@ import logging from typing import Final -from homeassistant.const import ( - DATA_BYTES, - DATA_EXABYTES, - DATA_GIGABYTES, - DATA_KILOBYTES, - DATA_MEGABYTES, - DATA_PETABYTES, - DATA_TERABYTES, - DATA_YOTTABYTES, - DATA_ZETTABYTES, -) +from homeassistant.const import UnitOfInformation BYTE_SIZES = [ - DATA_BYTES, - DATA_KILOBYTES, - DATA_MEGABYTES, - DATA_GIGABYTES, - DATA_TERABYTES, - DATA_PETABYTES, - DATA_EXABYTES, - DATA_ZETTABYTES, - DATA_YOTTABYTES, + UnitOfInformation.BYTES, + UnitOfInformation.KILOBYTES, + UnitOfInformation.MEGABYTES, + UnitOfInformation.GIGABYTES, + UnitOfInformation.TERABYTES, + UnitOfInformation.PETABYTES, + UnitOfInformation.EXABYTES, + UnitOfInformation.ZETTABYTES, + UnitOfInformation.YOTTABYTES, ] # Defaults DEFAULT_DAYS = "1" DEFAULT_HOST = "localhost" DEFAULT_NAME = "Lidarr" -DEFAULT_UNIT = DATA_GIGABYTES +DEFAULT_UNIT = UnitOfInformation.GIGABYTES DEFAULT_MAX_RECORDS = 20 DOMAIN: Final = "lidarr" diff --git a/homeassistant/components/lidarr/coordinator.py b/homeassistant/components/lidarr/coordinator.py index c02d6525871..e4554685c8c 100644 --- a/homeassistant/components/lidarr/coordinator.py +++ b/homeassistant/components/lidarr/coordinator.py @@ -1,7 +1,7 @@ """Data update coordinator for the Lidarr integration.""" from __future__ import annotations -from abc import abstractmethod +from abc import ABC, abstractmethod from datetime import timedelta from typing import Generic, TypeVar, Union, cast @@ -19,7 +19,7 @@ from .const import DEFAULT_MAX_RECORDS, DOMAIN, LOGGER T = TypeVar("T", bound=Union[list[LidarrRootFolder], LidarrQueue, str, LidarrAlbum]) -class LidarrDataUpdateCoordinator(DataUpdateCoordinator[T], Generic[T]): +class LidarrDataUpdateCoordinator(DataUpdateCoordinator[T], Generic[T], ABC): """Data update coordinator for the Lidarr integration.""" config_entry: ConfigEntry diff --git a/homeassistant/components/lidarr/sensor.py b/homeassistant/components/lidarr/sensor.py index 2e5f9bb710f..96ab2e5f7a2 100644 --- a/homeassistant/components/lidarr/sensor.py +++ b/homeassistant/components/lidarr/sensor.py @@ -9,12 +9,13 @@ from typing import Any, Generic from aiopyarr import LidarrQueue, LidarrQueueItem, LidarrRootFolder from homeassistant.components.sensor import ( + SensorDeviceClass, SensorEntity, SensorEntityDescription, SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import DATA_GIGABYTES +from homeassistant.const import UnitOfInformation from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -29,7 +30,9 @@ def get_space(data: list[LidarrRootFolder], name: str) -> str: for mount in data: if name in mount.path: mount.freeSpace = mount.freeSpace if mount.accessible else 0 - space.append(mount.freeSpace / 1024 ** BYTE_SIZES.index(DATA_GIGABYTES)) + space.append( + mount.freeSpace / 1024 ** BYTE_SIZES.index(UnitOfInformation.GIGABYTES) + ) return f"{space[0]:.2f}" @@ -68,7 +71,8 @@ SENSOR_TYPES: dict[str, LidarrSensorEntityDescription[Any]] = { "disk_space": LidarrSensorEntityDescription( key="disk_space", name="Disk space", - native_unit_of_measurement=DATA_GIGABYTES, + native_unit_of_measurement=UnitOfInformation.GIGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:harddisk", value_fn=get_space, state_class=SensorStateClass.TOTAL, diff --git a/homeassistant/components/lidarr/translations/ko.json b/homeassistant/components/lidarr/translations/ko.json new file mode 100644 index 00000000000..9125d8643e6 --- /dev/null +++ b/homeassistant/components/lidarr/translations/ko.json @@ -0,0 +1,28 @@ +{ + "config": { + "abort": { + "already_configured": "\uc11c\ube44\uc2a4\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "API \ud0a4" + }, + "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" + }, + "user": { + "data": { + "api_key": "API \ud0a4", + "url": "URL \uc8fc\uc18c", + "verify_ssl": "SSL \uc778\uc99d\uc11c \ud655\uc778" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/lidarr/translations/pt.json b/homeassistant/components/lidarr/translations/pt.json new file mode 100644 index 00000000000..0d29c60c8b0 --- /dev/null +++ b/homeassistant/components/lidarr/translations/pt.json @@ -0,0 +1,28 @@ +{ + "config": { + "abort": { + "already_configured": "O servi\u00e7o j\u00e1 est\u00e1 configurado", + "reauth_successful": "Reautentica\u00e7\u00e3o bem sucedida" + }, + "error": { + "cannot_connect": "A liga\u00e7\u00e3o falhou", + "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", + "unknown": "Erro inesperado" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "Chave da API" + }, + "title": "Reautenticar integra\u00e7\u00e3o" + }, + "user": { + "data": { + "api_key": "Chave da API", + "url": "", + "verify_ssl": "Verificar o certificado SSL" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/lidarr/translations/sk.json b/homeassistant/components/lidarr/translations/sk.json index 824b22a1b5d..12e471f39e6 100644 --- a/homeassistant/components/lidarr/translations/sk.json +++ b/homeassistant/components/lidarr/translations/sk.json @@ -16,6 +16,7 @@ "data": { "api_key": "API k\u013e\u00fa\u010d" }, + "description": "Integr\u00e1cia Lidarr mus\u00ed by\u0165 manu\u00e1lne op\u00e4tovne overen\u00e1 pomocou Lidarr API", "title": "Znova overi\u0165 integr\u00e1ciu" }, "user": { @@ -23,7 +24,8 @@ "api_key": "API k\u013e\u00fa\u010d", "url": "URL", "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" - } + }, + "description": "API k\u013e\u00fa\u010d je mo\u017en\u00e9 z\u00edska\u0165 automaticky, ak v aplik\u00e1cii neboli nastaven\u00e9 prihlasovacie \u00fadaje.\n V\u00e1\u0161 k\u013e\u00fa\u010d API n\u00e1jdete v \u010dasti Nastavenia > V\u0161eobecn\u00e9 vo webovom pou\u017e\u00edvate\u013eskom rozhran\u00ed Lidarr." } } } diff --git a/homeassistant/components/life360/coordinator.py b/homeassistant/components/life360/coordinator.py index b7121cc7fdb..5ea64d3f81d 100644 --- a/homeassistant/components/life360/coordinator.py +++ b/homeassistant/components/life360/coordinator.py @@ -11,12 +11,7 @@ from typing import Any from life360 import Life360, Life360Error, LoginError from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - LENGTH_FEET, - LENGTH_KILOMETERS, - LENGTH_METERS, - LENGTH_MILES, -) +from homeassistant.const import UnitOfLength from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryAuthFailed from homeassistant.helpers.aiohttp_client import async_get_clientsession @@ -211,7 +206,7 @@ class Life360DataUpdateCoordinator(DataUpdateCoordinator[Life360Data]): speed = max(0, float(loc["speed"]) * SPEED_FACTOR_MPH) if self._hass.config.units is METRIC_SYSTEM: speed = DistanceConverter.convert( - speed, LENGTH_MILES, LENGTH_KILOMETERS + speed, UnitOfLength.MILES, UnitOfLength.KILOMETERS ) data.members[member_id] = Life360Member( @@ -225,7 +220,9 @@ class Life360DataUpdateCoordinator(DataUpdateCoordinator[Life360Data]): # gps_accuracy in meters. round( DistanceConverter.convert( - float(loc["accuracy"]), LENGTH_FEET, LENGTH_METERS + float(loc["accuracy"]), + UnitOfLength.FEET, + UnitOfLength.METERS, ) ), dt_util.utc_from_timestamp(int(loc["timestamp"])), diff --git a/homeassistant/components/life360/device_tracker.py b/homeassistant/components/life360/device_tracker.py index 74ab63f88a1..f16e7215a22 100644 --- a/homeassistant/components/life360/device_tracker.py +++ b/homeassistant/components/life360/device_tracker.py @@ -152,16 +152,20 @@ class Life360DeviceTracker( if bad_last_seen or bad_accuracy: if bad_last_seen: LOGGER.warning( - "%s: Ignoring location update because " - "last_seen (%s) < previous last_seen (%s)", + ( + "%s: Ignoring location update because " + "last_seen (%s) < previous last_seen (%s)" + ), self.entity_id, last_seen, prev_seen, ) if bad_accuracy: LOGGER.warning( - "%s: Ignoring location update because " - "expected GPS accuracy (%0.1f) is not met: %i", + ( + "%s: Ignoring location update because " + "expected GPS accuracy (%0.1f) is not met: %i" + ), self.entity_id, max_gps_acc, self.location_accuracy, diff --git a/homeassistant/components/life360/translations/de.json b/homeassistant/components/life360/translations/de.json index e084616c413..92e12de0afb 100644 --- a/homeassistant/components/life360/translations/de.json +++ b/homeassistant/components/life360/translations/de.json @@ -23,7 +23,7 @@ "password": "Passwort", "username": "Benutzername" }, - "title": "Life360-Konto konfigurieren" + "title": "Life360 Konto konfigurieren" } } }, diff --git a/homeassistant/components/life360/translations/ko.json b/homeassistant/components/life360/translations/ko.json index eb00b434591..4ee7fef0f7e 100644 --- a/homeassistant/components/life360/translations/ko.json +++ b/homeassistant/components/life360/translations/ko.json @@ -1,14 +1,23 @@ { "config": { "abort": { - "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" }, "error": { "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" }, "step": { + "reauth_confirm": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638" + }, + "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" + }, "user": { "data": { "password": "\ube44\ubc00\ubc88\ud638", diff --git a/homeassistant/components/life360/translations/pt.json b/homeassistant/components/life360/translations/pt.json index f0b8db82092..bc601b32b89 100644 --- a/homeassistant/components/life360/translations/pt.json +++ b/homeassistant/components/life360/translations/pt.json @@ -7,7 +7,7 @@ }, "error": { "already_configured": "Conta j\u00e1 configurada", - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, diff --git a/homeassistant/components/life360/translations/sk.json b/homeassistant/components/life360/translations/sk.json index da9c32711a1..37bd6c3c104 100644 --- a/homeassistant/components/life360/translations/sk.json +++ b/homeassistant/components/life360/translations/sk.json @@ -31,9 +31,11 @@ "step": { "init": { "data": { + "driving": "Zobrazi\u0165 jazdu ako stav", "driving_speed": "R\u00fdchlos\u0165 jazdy", "limit_gps_acc": "Obmedzenie presnosti GPS", - "max_gps_accuracy": "Maxim\u00e1lna presnos\u0165 GPS (v metroch)" + "max_gps_accuracy": "Maxim\u00e1lna presnos\u0165 GPS (v metroch)", + "set_drive_speed": "Nastavenie prahovej r\u00fdchlosti jazdy" }, "title": "Mo\u017enosti \u00fa\u010dtu" } diff --git a/homeassistant/components/lifx/__init__.py b/homeassistant/components/lifx/__init__.py index abe35c0a0d8..b2265d81da9 100644 --- a/homeassistant/components/lifx/__init__.py +++ b/homeassistant/components/lifx/__init__.py @@ -143,7 +143,10 @@ class LIFXDiscoveryManager: if migration_complete and migrating_was_in_progress: self.migrating = False _LOGGER.debug( - "LIFX migration complete, switching to normal discovery interval: %s", + ( + "LIFX migration complete, switching to normal discovery" + " interval: %s" + ), DISCOVERY_INTERVAL, ) self.async_setup_discovery_interval() diff --git a/homeassistant/components/lifx/config_flow.py b/homeassistant/components/lifx/config_flow.py index fa83ff5c427..56a88c89806 100644 --- a/homeassistant/components/lifx/config_flow.py +++ b/homeassistant/components/lifx/config_flow.py @@ -119,9 +119,10 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): assert self._discovered_device is not None discovered = self._discovered_device _LOGGER.debug( - "Confirming discovery: %s with serial %s", + "Confirming discovery of %s (%s) [%s]", discovered.label, - self.unique_id, + discovered.group, + discovered.mac_addr, ) if user_input is not None or self._async_discovered_pending_migration(): return self._async_create_entry_from_device(discovered) @@ -130,8 +131,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): self._set_confirm_only() placeholders = { "label": discovered.label, - "host": discovered.ip_addr, - "serial": self.unique_id, + "group": discovered.group, } self.context["title_placeholders"] = placeholders return self.async_show_form( @@ -224,11 +224,13 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): # get_hostfirmware required for MAC address offset # get_version required for lifx_features() # get_label required to log the name of the device + # get_group required to populate suggested areas messages = await asyncio.gather( *[ async_execute_lifx(device.get_hostfirmware), async_execute_lifx(device.get_version), async_execute_lifx(device.get_label), + async_execute_lifx(device.get_group), ] ) except asyncio.TimeoutError: @@ -237,7 +239,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): connection.async_stop() if ( messages is None - or len(messages) != 3 + or len(messages) != 4 or lifx_features(device)["relays"] is True or device.host_firmware_version is None ): diff --git a/homeassistant/components/lifx/coordinator.py b/homeassistant/components/lifx/coordinator.py index 9343c3b7dad..952a4574d2d 100644 --- a/homeassistant/components/lifx/coordinator.py +++ b/homeassistant/components/lifx/coordinator.py @@ -64,7 +64,7 @@ class FirmwareEffect(IntEnum): FLAME = 3 -class LIFXUpdateCoordinator(DataUpdateCoordinator): +class LIFXUpdateCoordinator(DataUpdateCoordinator[None]): """DataUpdateCoordinator to gather data for a specific lifx device.""" def __init__( @@ -173,6 +173,8 @@ class LIFXUpdateCoordinator(DataUpdateCoordinator): self.device.get_hostfirmware() if self.device.product is None: self.device.get_version() + if self.device.group is None: + self.device.get_group() response = await async_execute_lifx(self.device.get_color) diff --git a/homeassistant/components/lifx/entity.py b/homeassistant/components/lifx/entity.py index a500e353fbf..63996d60027 100644 --- a/homeassistant/components/lifx/entity.py +++ b/homeassistant/components/lifx/entity.py @@ -25,6 +25,7 @@ class LIFXEntity(CoordinatorEntity[LIFXUpdateCoordinator]): name=coordinator.label, model=products.product_map.get(self.bulb.product, "LIFX Bulb"), sw_version=self.bulb.host_firmware_version, + suggested_area=self.bulb.group, ) @@ -42,4 +43,5 @@ class LIFXSensorEntity(CoordinatorEntity[LIFXSensorUpdateCoordinator]): name=coordinator.parent.label, model=products.product_map.get(self.bulb.product, "LIFX Bulb"), sw_version=self.bulb.host_firmware_version, + suggested_area=self.bulb.group, ) diff --git a/homeassistant/components/lifx/light.py b/homeassistant/components/lifx/light.py index 7b23e1d34c4..eb62cb8016e 100644 --- a/homeassistant/components/lifx/light.py +++ b/homeassistant/components/lifx/light.py @@ -221,7 +221,10 @@ class LIFXLight(LIFXEntity, LightEntity): Platform.SELECT, INFRARED_BRIGHTNESS ) _LOGGER.warning( - "The 'infrared' attribute of 'lifx.set_state' is deprecated: call 'select.select_option' targeting '%s' instead", + ( + "The 'infrared' attribute of 'lifx.set_state' is deprecated:" + " call 'select.select_option' targeting '%s' instead" + ), infrared_entity_id, ) bulb.set_infrared(convert_8_to_16(kwargs[ATTR_INFRARED])) diff --git a/homeassistant/components/lifx/manifest.json b/homeassistant/components/lifx/manifest.json index 8adbd18f264..f4247f97ecf 100644 --- a/homeassistant/components/lifx/manifest.json +++ b/homeassistant/components/lifx/manifest.json @@ -6,7 +6,7 @@ "requirements": [ "aiolifx==0.8.7", "aiolifx_effects==0.3.1", - "aiolifx_themes==0.2.0" + "aiolifx_themes==0.4.0" ], "quality_scale": "platinum", "dependencies": ["network"], diff --git a/homeassistant/components/lifx/strings.json b/homeassistant/components/lifx/strings.json index b83ae9c1609..93d3bd62abe 100644 --- a/homeassistant/components/lifx/strings.json +++ b/homeassistant/components/lifx/strings.json @@ -1,6 +1,6 @@ { "config": { - "flow_title": "{label} ({host}) {serial}", + "flow_title": "{label} ({group})", "step": { "user": { "description": "If you leave the host empty, discovery will be used to find devices.", @@ -14,7 +14,7 @@ } }, "discovery_confirm": { - "description": "Do you want to setup {label} ({host}) {serial}?" + "description": "Do you want to set up {label} ({group})?" } }, "error": { diff --git a/homeassistant/components/lifx/translations/ca.json b/homeassistant/components/lifx/translations/ca.json index 57c706e19df..25f2b25b504 100644 --- a/homeassistant/components/lifx/translations/ca.json +++ b/homeassistant/components/lifx/translations/ca.json @@ -8,10 +8,10 @@ "error": { "cannot_connect": "Ha fallat la connexi\u00f3" }, - "flow_title": "{label} ({host}) {serial}", + "flow_title": "{label} ({group})", "step": { "discovery_confirm": { - "description": "Vols configurar {label} ({host}) {serial}?" + "description": "Vols configurar {label} ({group})?" }, "pick_device": { "data": { diff --git a/homeassistant/components/lifx/translations/de.json b/homeassistant/components/lifx/translations/de.json index ae056f136d7..bc691edb76d 100644 --- a/homeassistant/components/lifx/translations/de.json +++ b/homeassistant/components/lifx/translations/de.json @@ -8,10 +8,10 @@ "error": { "cannot_connect": "Verbindung fehlgeschlagen" }, - "flow_title": "{label} ({host}) {serial}", + "flow_title": "{label} ({group})", "step": { "discovery_confirm": { - "description": "M\u00f6chtest du {label} ({host}) {serial} einrichten?" + "description": "M\u00f6chtest du {label} ({group}) einrichten?" }, "pick_device": { "data": { diff --git a/homeassistant/components/lifx/translations/en.json b/homeassistant/components/lifx/translations/en.json index 119259457a7..f5526c38410 100644 --- a/homeassistant/components/lifx/translations/en.json +++ b/homeassistant/components/lifx/translations/en.json @@ -8,10 +8,10 @@ "error": { "cannot_connect": "Failed to connect" }, - "flow_title": "{label} ({host}) {serial}", + "flow_title": "{label} ({group})", "step": { "discovery_confirm": { - "description": "Do you want to setup {label} ({host}) {serial}?" + "description": "Do you want to set up {label} ({group})?" }, "pick_device": { "data": { diff --git a/homeassistant/components/lifx/translations/es.json b/homeassistant/components/lifx/translations/es.json index 227e9fb3cdd..66e9a5b7004 100644 --- a/homeassistant/components/lifx/translations/es.json +++ b/homeassistant/components/lifx/translations/es.json @@ -8,10 +8,10 @@ "error": { "cannot_connect": "No se pudo conectar" }, - "flow_title": "{label} ({host}) {serial}", + "flow_title": "{label} ({group})", "step": { "discovery_confirm": { - "description": "\u00bfQuieres configurar {label} ({host}) {serial}?" + "description": "\u00bfQuieres configurar {label} ({group})?" }, "pick_device": { "data": { diff --git a/homeassistant/components/lifx/translations/et.json b/homeassistant/components/lifx/translations/et.json index fe05f4044ba..4540e89a623 100644 --- a/homeassistant/components/lifx/translations/et.json +++ b/homeassistant/components/lifx/translations/et.json @@ -8,10 +8,10 @@ "error": { "cannot_connect": "\u00dchendamine nurjus" }, - "flow_title": "{label} ({host}) {serial}", + "flow_title": "{label} ({group})", "step": { "discovery_confirm": { - "description": "Kas seadistada {label} ( {host} ) {serial} ?" + "description": "Kas seadistada {label} ({group})?" }, "pick_device": { "data": { diff --git a/homeassistant/components/lifx/translations/he.json b/homeassistant/components/lifx/translations/he.json index 9237bf45294..66e8a3a96f9 100644 --- a/homeassistant/components/lifx/translations/he.json +++ b/homeassistant/components/lifx/translations/he.json @@ -8,7 +8,7 @@ "error": { "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4" }, - "flow_title": "{label} ({host}) {serial}", + "flow_title": "{label} ({group})", "step": { "user": { "data": { diff --git a/homeassistant/components/lifx/translations/hu.json b/homeassistant/components/lifx/translations/hu.json index 048509a9952..5a29767dc53 100644 --- a/homeassistant/components/lifx/translations/hu.json +++ b/homeassistant/components/lifx/translations/hu.json @@ -8,10 +8,10 @@ "error": { "cannot_connect": "Sikertelen csatlakoz\u00e1s" }, - "flow_title": "{label} ({host}) {serial}", + "flow_title": "{label} ({group})", "step": { "discovery_confirm": { - "description": "Szeretn\u00e9 be\u00e1ll\u00edtani: {label} ({host}) {serial}?" + "description": "Szeretn\u00e9 be\u00e1ll\u00edtani: {label} ({group})?" }, "pick_device": { "data": { diff --git a/homeassistant/components/lifx/translations/id.json b/homeassistant/components/lifx/translations/id.json index b9b3bb59207..5e328f9eb80 100644 --- a/homeassistant/components/lifx/translations/id.json +++ b/homeassistant/components/lifx/translations/id.json @@ -8,10 +8,10 @@ "error": { "cannot_connect": "Gagal terhubung" }, - "flow_title": "{label} ({host}) {serial}", + "flow_title": "{label} ({group})", "step": { "discovery_confirm": { - "description": "Ingin menyiapkan {label} ({host}) {serial}?" + "description": "Ingin menyiapkan {label} ({group})?" }, "pick_device": { "data": { diff --git a/homeassistant/components/lifx/translations/it.json b/homeassistant/components/lifx/translations/it.json index 8f6172f79ae..24601afdadf 100644 --- a/homeassistant/components/lifx/translations/it.json +++ b/homeassistant/components/lifx/translations/it.json @@ -8,10 +8,10 @@ "error": { "cannot_connect": "Impossibile connettersi" }, - "flow_title": "{label} ({host}) {serial}", + "flow_title": "{label} ({group})", "step": { "discovery_confirm": { - "description": "Vuoi configurare {label} ({host}) {serial}?" + "description": "Vuoi configurare {label} ({group})?" }, "pick_device": { "data": { diff --git a/homeassistant/components/lifx/translations/ko.json b/homeassistant/components/lifx/translations/ko.json index d50e5e705bb..bea6bcf2bbc 100644 --- a/homeassistant/components/lifx/translations/ko.json +++ b/homeassistant/components/lifx/translations/ko.json @@ -1,7 +1,19 @@ { "config": { "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/lifx/translations/no.json b/homeassistant/components/lifx/translations/no.json index 00bcd009eed..b75851ac220 100644 --- a/homeassistant/components/lifx/translations/no.json +++ b/homeassistant/components/lifx/translations/no.json @@ -8,10 +8,10 @@ "error": { "cannot_connect": "Tilkobling mislyktes" }, - "flow_title": "{label} ( {host} ) {serial}", + "flow_title": "{label} ( {group} )", "step": { "discovery_confirm": { - "description": "Vil du sette opp {label} ( {host} ) {serial} ?" + "description": "Vil du sette opp {label} ( {group} )?" }, "pick_device": { "data": { diff --git a/homeassistant/components/lifx/translations/pl.json b/homeassistant/components/lifx/translations/pl.json index 9bf20bc1e40..b9abf646531 100644 --- a/homeassistant/components/lifx/translations/pl.json +++ b/homeassistant/components/lifx/translations/pl.json @@ -8,10 +8,10 @@ "error": { "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia" }, - "flow_title": "{label} ({host}) {serial}", + "flow_title": "{label} ({group})", "step": { "discovery_confirm": { - "description": "Czy chcesz skonfigurowa\u0107 {label} ({host}) {serial}?" + "description": "Czy chcesz skonfigurowa\u0107 {label} ({group})?" }, "pick_device": { "data": { diff --git a/homeassistant/components/lifx/translations/pt-BR.json b/homeassistant/components/lifx/translations/pt-BR.json index 3ae1087f327..8e50f190260 100644 --- a/homeassistant/components/lifx/translations/pt-BR.json +++ b/homeassistant/components/lifx/translations/pt-BR.json @@ -8,10 +8,10 @@ "error": { "cannot_connect": "Falha ao conectar" }, - "flow_title": "{label} ( {host} ) {serial}", + "flow_title": "{label} ({host})", "step": { "discovery_confirm": { - "description": "Deseja configurar {label} ( {host} ) {serial}?" + "description": "Deseja configurar {label} ({group})?" }, "pick_device": { "data": { diff --git a/homeassistant/components/lifx/translations/pt.json b/homeassistant/components/lifx/translations/pt.json index 5d7fdf356ef..76acbc6f49f 100644 --- a/homeassistant/components/lifx/translations/pt.json +++ b/homeassistant/components/lifx/translations/pt.json @@ -1,7 +1,19 @@ { "config": { "abort": { + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", + "already_in_progress": "O processo de configura\u00e7\u00e3o j\u00e1 est\u00e1 a decorrer", "no_devices_found": "Nenhum dispositivo LIFX encontrado na rede." + }, + "error": { + "cannot_connect": "A liga\u00e7\u00e3o falhou" + }, + "step": { + "user": { + "data": { + "host": "Endere\u00e7o" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/lifx/translations/ru.json b/homeassistant/components/lifx/translations/ru.json index cea461cb10d..7e785616c07 100644 --- a/homeassistant/components/lifx/translations/ru.json +++ b/homeassistant/components/lifx/translations/ru.json @@ -8,10 +8,10 @@ "error": { "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f." }, - "flow_title": "{label} ({host}) {serial}", + "flow_title": "{label} ({group})", "step": { "discovery_confirm": { - "description": "\u0425\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c {label} ({host}) {serial}?" + "description": "\u0425\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c {label} ({group})?" }, "pick_device": { "data": { diff --git a/homeassistant/components/lifx/translations/sk.json b/homeassistant/components/lifx/translations/sk.json index f03ef39a4f3..53251673c9f 100644 --- a/homeassistant/components/lifx/translations/sk.json +++ b/homeassistant/components/lifx/translations/sk.json @@ -21,7 +21,8 @@ "user": { "data": { "host": "Hostite\u013e" - } + }, + "description": "Ak nech\u00e1te hostite\u013ea pr\u00e1zdneho, na vyh\u013ead\u00e1vanie zariaden\u00ed sa pou\u017eije zis\u0165ovanie." } } } diff --git a/homeassistant/components/lifx/translations/zh-Hant.json b/homeassistant/components/lifx/translations/zh-Hant.json index 6fc7318a7b1..6dc0101101f 100644 --- a/homeassistant/components/lifx/translations/zh-Hant.json +++ b/homeassistant/components/lifx/translations/zh-Hant.json @@ -8,10 +8,10 @@ "error": { "cannot_connect": "\u9023\u7dda\u5931\u6557" }, - "flow_title": "{label} ({host}) {serial}", + "flow_title": "{label} ({group})", "step": { "discovery_confirm": { - "description": "\u662f\u5426\u8981\u8a2d\u5b9a {label} ({host}) {serial}\uff1f" + "description": "\u662f\u5426\u8981\u8a2d\u5b9a{label} ({group})\uff1f" }, "pick_device": { "data": { diff --git a/homeassistant/components/lifx/util.py b/homeassistant/components/lifx/util.py index fde36d714d5..67190aaa599 100644 --- a/homeassistant/components/lifx/util.py +++ b/homeassistant/components/lifx/util.py @@ -110,7 +110,8 @@ def find_hsbk(hass: HomeAssistant, **kwargs: Any) -> list[float | int | None] | if ATTR_KELVIN in kwargs: _LOGGER.warning( - "The 'kelvin' parameter is deprecated. Please use 'color_temp_kelvin' for all service calls" + "The 'kelvin' parameter is deprecated. Please use 'color_temp_kelvin' for" + " all service calls" ) kelvin = kwargs.pop(ATTR_KELVIN) saturation = 0 diff --git a/homeassistant/components/light/intent.py b/homeassistant/components/light/intent.py index 2ffd73a9792..e85602f763a 100644 --- a/homeassistant/components/light/intent.py +++ b/homeassistant/components/light/intent.py @@ -1,4 +1,6 @@ """Intents for the light integration.""" +from __future__ import annotations + import voluptuous as vol from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_ON diff --git a/homeassistant/components/light/reproduce_state.py b/homeassistant/components/light/reproduce_state.py index 46adcc1fa2e..15141b6d428 100644 --- a/homeassistant/components/light/reproduce_state.py +++ b/homeassistant/components/light/reproduce_state.py @@ -89,8 +89,9 @@ DEPRECATED_GROUP = [ ] DEPRECATION_WARNING = ( - "The use of other attributes than device state attributes is deprecated and will be removed in a future release. " - "Invalid attributes are %s. Read the logs for further details: https://www.home-assistant.io/integrations/scene/" + "The use of other attributes than device state attributes is deprecated and will be" + " removed in a future release. Invalid attributes are %s. Read the logs for further" + " details: https://www.home-assistant.io/integrations/scene/" ) diff --git a/homeassistant/components/lightwave/climate.py b/homeassistant/components/lightwave/climate.py index 834fc7eaeca..60108aba024 100644 --- a/homeassistant/components/lightwave/climate.py +++ b/homeassistant/components/lightwave/climate.py @@ -11,7 +11,7 @@ from homeassistant.components.climate import ( HVACAction, HVACMode, ) -from homeassistant.const import ATTR_TEMPERATURE, CONF_NAME, TEMP_CELSIUS +from homeassistant.const import ATTR_TEMPERATURE, CONF_NAME, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType @@ -49,7 +49,7 @@ class LightwaveTrv(ClimateEntity): _attr_max_temp = DEFAULT_MAX_TEMP _attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE _attr_target_temperature_step = 0.5 - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS def __init__(self, name, device_id, lwlink, serial): """Initialize LightwaveTrv entity.""" diff --git a/homeassistant/components/litejet/translations/sk.json b/homeassistant/components/litejet/translations/sk.json index 70783638575..c19d85e24d8 100644 --- a/homeassistant/components/litejet/translations/sk.json +++ b/homeassistant/components/litejet/translations/sk.json @@ -11,6 +11,7 @@ "data": { "port": "Port" }, + "description": "Pripojte port RS232-2 LiteJet k po\u010d\u00edta\u010du a zadajte cestu k zariadeniu so s\u00e9riov\u00fdm portom. \n\n LiteJet MCP mus\u00ed by\u0165 nakonfigurovan\u00fd na 19,2 K baud, 8 d\u00e1tov\u00fdch bitov, 1 stop bit, bez parity a na prenos 'CR' po ka\u017edej odpovedi.", "title": "Pripoji\u0165 sa k LiteJet" } } @@ -18,6 +19,9 @@ "options": { "step": { "init": { + "data": { + "default_transition": "Predvolen\u00fd prechod (sekundy)" + }, "title": "Nakonfigurujte LiteJet" } } diff --git a/homeassistant/components/litterrobot/__init__.py b/homeassistant/components/litterrobot/__init__.py index 3d8f8487b33..45483f99e5b 100644 --- a/homeassistant/components/litterrobot/__init__.py +++ b/homeassistant/components/litterrobot/__init__.py @@ -1,7 +1,7 @@ """The Litter-Robot integration.""" from __future__ import annotations -from pylitterbot import FeederRobot, LitterRobot, LitterRobot3, Robot +from pylitterbot import FeederRobot, LitterRobot, LitterRobot3, LitterRobot4, Robot from homeassistant.config_entries import ConfigEntry from homeassistant.const import Platform @@ -19,6 +19,7 @@ PLATFORMS_BY_TYPE = { ), LitterRobot: (Platform.VACUUM,), LitterRobot3: (Platform.BUTTON,), + LitterRobot4: (Platform.UPDATE,), FeederRobot: (Platform.BUTTON,), } diff --git a/homeassistant/components/litterrobot/manifest.json b/homeassistant/components/litterrobot/manifest.json index 889c7edfd9c..f81e663f302 100644 --- a/homeassistant/components/litterrobot/manifest.json +++ b/homeassistant/components/litterrobot/manifest.json @@ -3,7 +3,7 @@ "name": "Litter-Robot", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/litterrobot", - "requirements": ["pylitterbot==2022.11.0"], + "requirements": ["pylitterbot==2022.12.0"], "codeowners": ["@natekspencer", "@tkdrob"], "dhcp": [{ "hostname": "litter-robot4" }], "iot_class": "cloud_push", diff --git a/homeassistant/components/litterrobot/select.py b/homeassistant/components/litterrobot/select.py index d384e94a092..0edfd5b0646 100644 --- a/homeassistant/components/litterrobot/select.py +++ b/homeassistant/components/litterrobot/select.py @@ -14,7 +14,7 @@ from homeassistant.components.select import ( SelectEntityDescription, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import TIME_MINUTES +from homeassistant.const import UnitOfTime from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -48,7 +48,7 @@ LITTER_ROBOT_SELECT = RobotSelectEntityDescription[LitterRobot, int]( key="cycle_delay", name="Clean Cycle Wait Time Minutes", icon="mdi:timer-outline", - unit_of_measurement=TIME_MINUTES, + unit_of_measurement=UnitOfTime.MINUTES, current_fn=lambda robot: robot.clean_cycle_wait_time_minutes, options_fn=lambda robot: robot.VALID_WAIT_TIMES, select_fn=lambda robot, option: robot.set_wait_time(int(option)), diff --git a/homeassistant/components/litterrobot/sensor.py b/homeassistant/components/litterrobot/sensor.py index 1857931143d..3b994f4ae9d 100644 --- a/homeassistant/components/litterrobot/sensor.py +++ b/homeassistant/components/litterrobot/sensor.py @@ -13,9 +13,10 @@ from homeassistant.components.sensor import ( SensorDeviceClass, SensorEntity, SensorEntityDescription, + SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import MASS_POUNDS, PERCENTAGE +from homeassistant.const import PERCENTAGE, UnitOfMass from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -73,6 +74,7 @@ ROBOT_SENSOR_MAP: dict[type[Robot], list[RobotSensorEntityDescription]] = { name="Waste Drawer", native_unit_of_measurement=PERCENTAGE, icon_fn=lambda state: icon_for_gauge_level(state, 10), + state_class=SensorStateClass.MEASUREMENT, ), RobotSensorEntityDescription[LitterRobot]( key="sleep_mode_start_time", @@ -95,8 +97,36 @@ ROBOT_SENSOR_MAP: dict[type[Robot], list[RobotSensorEntityDescription]] = { RobotSensorEntityDescription[LitterRobot]( key="status_code", name="Status Code", - device_class="litterrobot__status_code", + translation_key="status_code", entity_category=EntityCategory.DIAGNOSTIC, + device_class=SensorDeviceClass.ENUM, + options=[ + "br", + "ccc", + "ccp", + "cd", + "csf", + "csi", + "cst", + "df1", + "df2", + "dfs", + "dhf", + "dpf", + "ec", + "hpf", + "off", + "offline", + "otf", + "p", + "pd", + "pwrd", + "pwru", + "rdy", + "scf", + "sdf", + "spf", + ], ), ], LitterRobot4: [ @@ -105,12 +135,14 @@ ROBOT_SENSOR_MAP: dict[type[Robot], list[RobotSensorEntityDescription]] = { name="Litter level", native_unit_of_measurement=PERCENTAGE, icon_fn=lambda state: icon_for_gauge_level(state, 10), + state_class=SensorStateClass.MEASUREMENT, ), RobotSensorEntityDescription[LitterRobot4]( key="pet_weight", name="Pet weight", - native_unit_of_measurement=MASS_POUNDS, + native_unit_of_measurement=UnitOfMass.POUNDS, device_class=SensorDeviceClass.WEIGHT, + state_class=SensorStateClass.MEASUREMENT, ), ], FeederRobot: [ @@ -119,6 +151,7 @@ ROBOT_SENSOR_MAP: dict[type[Robot], list[RobotSensorEntityDescription]] = { name="Food level", native_unit_of_measurement=PERCENTAGE, icon_fn=lambda state: icon_for_gauge_level(state, 10), + state_class=SensorStateClass.MEASUREMENT, ) ], } diff --git a/homeassistant/components/litterrobot/strings.json b/homeassistant/components/litterrobot/strings.json index f2256249b8e..2d40eb6a044 100644 --- a/homeassistant/components/litterrobot/strings.json +++ b/homeassistant/components/litterrobot/strings.json @@ -30,5 +30,38 @@ "title": "Litter-Robot attributes are now their own sensors", "description": "The vacuum entity attributes are now available as diagnostic sensors.\n\nPlease adjust any automations or scripts you may have that use these attributes." } + }, + "entity": { + "sensor": { + "status_code": { + "state": { + "br": "Bonnet Removed", + "ccc": "Clean Cycle Complete", + "ccp": "Clean Cycle In Progress", + "cd": "Cat Detected", + "csf": "Cat Sensor Fault", + "csi": "Cat Sensor Interrupted", + "cst": "Cat Sensor Timing", + "df1": "Drawer Almost Full - 2 Cycles Left", + "df2": "Drawer Almost Full - 1 Cycle Left", + "dfs": "Drawer Full", + "dhf": "Dump + Home Position Fault", + "dpf": "Dump Position Fault", + "ec": "Empty Cycle", + "hpf": "Home Position Fault", + "off": "[%key:common::state::off%]", + "offline": "Offline", + "otf": "Over Torque Fault", + "p": "[%key:common::state::paused%]", + "pd": "Pinch Detect", + "pwrd": "Powering Down", + "pwru": "Powering Up", + "rdy": "Ready", + "scf": "Cat Sensor Fault At Startup", + "sdf": "Drawer Full At Startup", + "spf": "Pinch Detect At Startup" + } + } + } } } diff --git a/homeassistant/components/litterrobot/strings.sensor.json b/homeassistant/components/litterrobot/strings.sensor.json deleted file mode 100644 index 0c901704b02..00000000000 --- a/homeassistant/components/litterrobot/strings.sensor.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "state": { - "litterrobot__status_code": { - "br": "Bonnet Removed", - "ccc": "Clean Cycle Complete", - "ccp": "Clean Cycle In Progress", - "cd": "Cat Detected", - "csf": "Cat Sensor Fault", - "csi": "Cat Sensor Interrupted", - "cst": "Cat Sensor Timing", - "df1": "Drawer Almost Full - 2 Cycles Left", - "df2": "Drawer Almost Full - 1 Cycle Left", - "dfs": "Drawer Full", - "dhf": "Dump + Home Position Fault", - "dpf": "Dump Position Fault", - "ec": "Empty Cycle", - "hpf": "Home Position Fault", - "off": "[%key:common::state::off%]", - "offline": "Offline", - "otf": "Over Torque Fault", - "p": "[%key:common::state::paused%]", - "pd": "Pinch Detect", - "pwrd": "Powering Down", - "pwru": "Powering Up", - "rdy": "Ready", - "scf": "Cat Sensor Fault At Startup", - "sdf": "Drawer Full At Startup", - "spf": "Pinch Detect At Startup" - } - } -} diff --git a/homeassistant/components/litterrobot/translations/bg.json b/homeassistant/components/litterrobot/translations/bg.json index 71ce9e1b4af..0cea600c82d 100644 --- a/homeassistant/components/litterrobot/translations/bg.json +++ b/homeassistant/components/litterrobot/translations/bg.json @@ -21,5 +21,14 @@ } } } + }, + "entity": { + "sensor": { + "status_code": { + "state": { + "off": "\u0418\u0437\u043a\u043b." + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/litterrobot/translations/ca.json b/homeassistant/components/litterrobot/translations/ca.json index 059e0ea6236..c93695a7326 100644 --- a/homeassistant/components/litterrobot/translations/ca.json +++ b/homeassistant/components/litterrobot/translations/ca.json @@ -25,6 +25,39 @@ } } }, + "entity": { + "sensor": { + "status_code": { + "state": { + "br": "Bossa extreta", + "ccc": "Cicle de neteja completat", + "ccp": "Cicle de neteja en curs", + "cd": "Gat detectat", + "csf": "Error del sensor de gat", + "csi": "Sensor de gat interromput", + "cst": "Temps del sensor de gats", + "df1": "Dip\u00f2sit gaireb\u00e9 ple - Queden 2 cicles", + "df2": "Dip\u00f2sit gaireb\u00e9 ple - Queda 1 cicle", + "dfs": "Dip\u00f2sit ple", + "dhf": "Error de posici\u00f3 d'abocament + inici", + "dpf": "Error de posici\u00f3 d'abocament", + "ec": "Cicle de buidatge", + "hpf": "Error de posici\u00f3 d'inici", + "off": "OFF", + "offline": "Fora de l\u00ednia", + "otf": "Error per sobre-parell", + "p": "Pausat/ada", + "pd": "Detecci\u00f3 de pessigada", + "pwrd": "Apagant", + "pwru": "Engegant", + "rdy": "A punt", + "scf": "Error del sensor de gat a l'arrencada", + "sdf": "Dip\u00f2sit ple a l'arrencada", + "spf": "Detecci\u00f3 de pessigada a l'arrencada" + } + } + } + }, "issues": { "migrated_attributes": { "description": "Els atributs de l'entitat aspirador ara estan disponibles com a sensors de diagn\u00f2stic. \n\nActualitza les automatitzacions o scripts que tinguis que utilitzin aquests atributs.", diff --git a/homeassistant/components/litterrobot/translations/de.json b/homeassistant/components/litterrobot/translations/de.json index 15f7a2107b2..66eeb396492 100644 --- a/homeassistant/components/litterrobot/translations/de.json +++ b/homeassistant/components/litterrobot/translations/de.json @@ -25,6 +25,39 @@ } } }, + "entity": { + "sensor": { + "status_code": { + "state": { + "br": "Haube entfernt", + "ccc": "Reinigungszyklus abgeschlossen", + "ccp": "Reinigungszyklus l\u00e4uft", + "cd": "Katze erkannt", + "csf": "Katzensensor Fehler", + "csi": "Katzensensor unterbrochen", + "cst": "Katzensensor Timing", + "df1": "Schublade fast voll - 2 Zyklen \u00fcbrig", + "df2": "Schublade fast voll - 1 Zyklus \u00fcbrig", + "dfs": "Schublade voll", + "dhf": "Fehler in Leerungs- und Grundstellung", + "dpf": "Fehler in der Leerungsstellung", + "ec": "Leerungszyklus", + "hpf": "Fehler in der Grundstellung", + "off": "Aus", + "offline": "Offline", + "otf": "\u00dcberdrehungsfehler", + "p": "Pausiert", + "pd": "Einklemmen erkennen", + "pwrd": "F\u00e4hrt herunter", + "pwru": "F\u00e4hrt hoch", + "rdy": "Bereit", + "scf": "Katzen-Sensorfehler beim Start", + "sdf": "Schublade voll beim Start", + "spf": "Einklemmerkennung beim Start" + } + } + } + }, "issues": { "migrated_attributes": { "description": "Die Staubsaugerentit\u00e4tsattribute sind jetzt als Diagnosesensoren verf\u00fcgbar. \n\nBitte passe eventuell vorhandene Automatisierungen oder Skripte an, die diese Attribute verwenden.", diff --git a/homeassistant/components/litterrobot/translations/el.json b/homeassistant/components/litterrobot/translations/el.json index f965d1be9ca..2ef40c31d3b 100644 --- a/homeassistant/components/litterrobot/translations/el.json +++ b/homeassistant/components/litterrobot/translations/el.json @@ -25,6 +25,39 @@ } } }, + "entity": { + "sensor": { + "status_code": { + "state": { + "br": "\u03a4\u03bf \u03ba\u03b1\u03c0\u03cc \u03b1\u03c6\u03b1\u03b9\u03c1\u03ad\u03b8\u03b7\u03ba\u03b5", + "ccc": "\u039f\u03bb\u03bf\u03ba\u03bb\u03b7\u03c1\u03ce\u03b8\u03b7\u03ba\u03b5 \u03bf \u03ba\u03cd\u03ba\u03bb\u03bf\u03c2 \u03ba\u03b1\u03b8\u03b1\u03c1\u03b9\u03c3\u03bc\u03bf\u03cd", + "ccp": "\u039a\u03b1\u03b8\u03b1\u03c1\u03cc\u03c2 \u03ba\u03cd\u03ba\u03bb\u03bf\u03c2 \u03c3\u03b5 \u03b5\u03be\u03ad\u03bb\u03b9\u03be\u03b7", + "cd": "\u0391\u03bd\u03b9\u03c7\u03bd\u03b5\u03cd\u03c4\u03b7\u03ba\u03b5 \u03b3\u03ac\u03c4\u03b1", + "csf": "\u03a3\u03c6\u03ac\u03bb\u03bc\u03b1 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 \u03b3\u03ac\u03c4\u03b1\u03c2", + "csi": "\u0394\u03b9\u03b1\u03ba\u03bf\u03c0\u03ae \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 \u03b3\u03ac\u03c4\u03b1\u03c2", + "cst": "\u03a7\u03c1\u03bf\u03bd\u03b9\u03c3\u03bc\u03cc\u03c2 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 \u03b3\u03ac\u03c4\u03b1\u03c2", + "df1": "\u03a3\u03c5\u03c1\u03c4\u03ac\u03c1\u03b9 \u03c3\u03c7\u03b5\u03b4\u03cc\u03bd \u03b3\u03b5\u03bc\u03ac\u03c4\u03bf - \u0391\u03c0\u03bf\u03bc\u03ad\u03bd\u03bf\u03c5\u03bd 2 \u03ba\u03cd\u03ba\u03bb\u03bf\u03b9", + "df2": "\u03a3\u03c5\u03c1\u03c4\u03ac\u03c1\u03b9 \u03c3\u03c7\u03b5\u03b4\u03cc\u03bd \u03b3\u03b5\u03bc\u03ac\u03c4\u03bf - \u0391\u03c0\u03bf\u03bc\u03ad\u03bd\u03b5\u03b9 1 \u03ba\u03cd\u03ba\u03bb\u03bf\u03c2", + "dfs": "\u03a3\u03c5\u03c1\u03c4\u03ac\u03c1\u03b9 \u03b3\u03b5\u03bc\u03ac\u03c4\u03bf", + "dhf": "\u0392\u03bb\u03ac\u03b2\u03b7 \u03b8\u03ad\u03c3\u03b7\u03c2 \u03b1\u03c0\u03cc\u03c1\u03c1\u03b9\u03c8\u03b7\u03c2 + \u03b1\u03c1\u03c7\u03b9\u03ba\u03ae\u03c2", + "dpf": "\u0392\u03bb\u03ac\u03b2\u03b7 \u03b8\u03ad\u03c3\u03b7\u03c2 \u03b1\u03c0\u03cc\u03c1\u03c1\u03b9\u03c8\u03b7\u03c2", + "ec": "\u0386\u03b4\u03b5\u03b9\u03bf\u03c2 \u03ba\u03cd\u03ba\u03bb\u03bf\u03c2", + "hpf": "\u0392\u03bb\u03ac\u03b2\u03b7 \u03c4\u03b7\u03c2 \u03b1\u03c1\u03c7\u03b9\u03ba\u03ae\u03c2 \u03b8\u03ad\u03c3\u03b7\u03c2", + "off": "\u0391\u03bd\u03b5\u03bd\u03b5\u03c1\u03b3\u03cc", + "offline": "\u0395\u03ba\u03c4\u03cc\u03c2 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", + "otf": "\u0392\u03bb\u03ac\u03b2\u03b7 \u03c5\u03c0\u03b5\u03c1\u03b2\u03bf\u03bb\u03b9\u03ba\u03ae\u03c2 \u03c1\u03bf\u03c0\u03ae\u03c2", + "p": "\u03a3\u03b5 \u03c0\u03b1\u03cd\u03c3\u03b7", + "pd": "\u0391\u03bd\u03af\u03c7\u03bd\u03b5\u03c5\u03c3\u03b7 \u03c4\u03c3\u03b9\u03bc\u03c0\u03ae\u03bc\u03b1\u03c4\u03bf\u03c2", + "pwrd": "\u0391\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7", + "pwru": "\u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7", + "rdy": "\u0388\u03c4\u03bf\u03b9\u03bc\u03bf", + "scf": "\u03a3\u03c6\u03ac\u03bb\u03bc\u03b1 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 \u03b3\u03ac\u03c4\u03b1\u03c2 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03b5\u03ba\u03ba\u03af\u03bd\u03b7\u03c3\u03b7", + "sdf": "\u03a3\u03c5\u03c1\u03c4\u03ac\u03c1\u03b9 \u03b3\u03b5\u03bc\u03ac\u03c4\u03bf \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03b5\u03ba\u03ba\u03af\u03bd\u03b7\u03c3\u03b7", + "spf": "\u0391\u03bd\u03af\u03c7\u03bd\u03b5\u03c5\u03c3\u03b7 \u03c4\u03c3\u03b9\u03bc\u03c0\u03ae\u03bc\u03b1\u03c4\u03bf\u03c2 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03b5\u03ba\u03ba\u03af\u03bd\u03b7\u03c3\u03b7" + } + } + } + }, "issues": { "migrated_attributes": { "description": "\u03a4\u03b1 \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03b7\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03ac \u03c4\u03b7\u03c2 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03c4\u03b7\u03c2 \u03c3\u03ba\u03bf\u03cd\u03c0\u03b1\u03c2 \u03b5\u03af\u03bd\u03b1\u03b9 \u03c4\u03ce\u03c1\u03b1 \u03b4\u03b9\u03b1\u03b8\u03ad\u03c3\u03b9\u03bc\u03b1 \u03c9\u03c2 \u03b4\u03b9\u03b1\u03b3\u03bd\u03c9\u03c3\u03c4\u03b9\u03ba\u03bf\u03af \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b5\u03c2.\n\n\u03a0\u03b1\u03c1\u03b1\u03ba\u03b1\u03bb\u03bf\u03cd\u03bc\u03b5 \u03c0\u03c1\u03bf\u03c3\u03b1\u03c1\u03bc\u03cc\u03c3\u03c4\u03b5 \u03c4\u03c5\u03c7\u03cc\u03bd \u03b1\u03c5\u03c4\u03bf\u03bc\u03b1\u03c4\u03b9\u03c3\u03bc\u03bf\u03cd\u03c2 \u03ae \u03c3\u03b5\u03bd\u03ac\u03c1\u03b9\u03b1 \u03c0\u03bf\u03c5 \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03ad\u03c7\u03b5\u03c4\u03b5 \u03ba\u03b1\u03b9 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03bf\u03cd\u03bd \u03b1\u03c5\u03c4\u03ac \u03c4\u03b1 \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03b7\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03ac.", diff --git a/homeassistant/components/litterrobot/translations/en.json b/homeassistant/components/litterrobot/translations/en.json index 76c0dcb79c9..c4010c63ecd 100644 --- a/homeassistant/components/litterrobot/translations/en.json +++ b/homeassistant/components/litterrobot/translations/en.json @@ -25,6 +25,39 @@ } } }, + "entity": { + "sensor": { + "status_code": { + "state": { + "br": "Bonnet Removed", + "ccc": "Clean Cycle Complete", + "ccp": "Clean Cycle In Progress", + "cd": "Cat Detected", + "csf": "Cat Sensor Fault", + "csi": "Cat Sensor Interrupted", + "cst": "Cat Sensor Timing", + "df1": "Drawer Almost Full - 2 Cycles Left", + "df2": "Drawer Almost Full - 1 Cycle Left", + "dfs": "Drawer Full", + "dhf": "Dump + Home Position Fault", + "dpf": "Dump Position Fault", + "ec": "Empty Cycle", + "hpf": "Home Position Fault", + "off": "Off", + "offline": "Offline", + "otf": "Over Torque Fault", + "p": "Paused", + "pd": "Pinch Detect", + "pwrd": "Powering Down", + "pwru": "Powering Up", + "rdy": "Ready", + "scf": "Cat Sensor Fault At Startup", + "sdf": "Drawer Full At Startup", + "spf": "Pinch Detect At Startup" + } + } + } + }, "issues": { "migrated_attributes": { "description": "The vacuum entity attributes are now available as diagnostic sensors.\n\nPlease adjust any automations or scripts you may have that use these attributes.", diff --git a/homeassistant/components/litterrobot/translations/es.json b/homeassistant/components/litterrobot/translations/es.json index 6ece2f2c221..4d6827f97a9 100644 --- a/homeassistant/components/litterrobot/translations/es.json +++ b/homeassistant/components/litterrobot/translations/es.json @@ -25,6 +25,39 @@ } } }, + "entity": { + "sensor": { + "status_code": { + "state": { + "br": "Cap\u00f3 eliminado", + "ccc": "Ciclo de limpieza completo", + "ccp": "Ciclo de limpieza en curso", + "cd": "Gato detectado", + "csf": "Fallo del sensor de gatos", + "csi": "Sensor de gatos interrumpido", + "cst": "Sincronizaci\u00f3n del sensor de gatos", + "df1": "Caj\u00f3n casi lleno - Quedan 2 ciclos", + "df2": "Caj\u00f3n casi lleno - Queda 1 ciclo", + "dfs": "Caj\u00f3n lleno", + "dhf": "Fallo de volcado + posici\u00f3n inicial", + "dpf": "Fallo de posici\u00f3n de descarga", + "ec": "Ciclo vac\u00edo", + "hpf": "Fallo de posici\u00f3n inicial", + "off": "Apagado", + "offline": "Sin conexi\u00f3n", + "otf": "Fallo de par excesivo", + "p": "En pausa", + "pd": "Detecci\u00f3n de pellizcos", + "pwrd": "Apagando", + "pwru": "Encendiendo", + "rdy": "Listo", + "scf": "Fallo del sensor de gatos al inicio", + "sdf": "Caj\u00f3n lleno al inicio", + "spf": "Detecci\u00f3n de pellizco al inicio" + } + } + } + }, "issues": { "migrated_attributes": { "description": "Los atributos de la entidad aspiradora ahora est\u00e1n disponibles como sensores de diagn\u00f3stico. \n\nPor favor, ajusta cualquier automatizaci\u00f3n o script que puedas tener que use estos atributos.", diff --git a/homeassistant/components/litterrobot/translations/et.json b/homeassistant/components/litterrobot/translations/et.json index b271a1195d9..25b781912f7 100644 --- a/homeassistant/components/litterrobot/translations/et.json +++ b/homeassistant/components/litterrobot/translations/et.json @@ -25,6 +25,39 @@ } } }, + "entity": { + "sensor": { + "status_code": { + "state": { + "br": "Kaas on lahti", + "ccc": "Puhastusts\u00fckkel on l\u00f5ppenud", + "ccp": "Puhastusts\u00fckkel on pooleli", + "cd": "Kassi on m\u00e4rgatud", + "csf": "Kassianduri viga", + "csi": "Kassianduri h\u00e4iring", + "cst": "Kassianduri ajastus", + "df1": "Mahuti on peaaegu t\u00e4is - 2 ts\u00fcklit j\u00e4\u00e4nud", + "df2": "Mahuti on peaaegu t\u00e4is - 1 ts\u00fckkel j\u00e4\u00e4nud", + "dfs": "Mahuti on t\u00e4is", + "dhf": "T\u00fchjendus- ja algasendi t\u00f5rge", + "dpf": "T\u00fchjendusasendi viga", + "ec": "T\u00fchjendusts\u00fckkel", + "hpf": "Algasendi viga", + "off": "V\u00e4ljas", + "offline": "\u00dchenduseta", + "otf": "\u00dclekoornuse viga", + "p": "Ootel", + "pd": "Pinch Detect", + "pwrd": "Sulgumine", + "pwru": "K\u00e4ivitumine", + "rdy": "Valmis", + "scf": "Kassianduri viga k\u00e4ivitamisel", + "sdf": "Sahtel t\u00e4is k\u00e4ivitamisel", + "spf": "Pinch Detect k\u00e4ivitamisel" + } + } + } + }, "issues": { "migrated_attributes": { "description": "Vaakumseadme atribuudid on n\u00fc\u00fcd saadaval diagnostiliste anduritena.\n\nPalun kohandage k\u00f5iki automatiseerimisi v\u00f5i skripte, mis neid atribuute kasutavad.", diff --git a/homeassistant/components/litterrobot/translations/he.json b/homeassistant/components/litterrobot/translations/he.json index d6636c6f865..cee7ca80cb0 100644 --- a/homeassistant/components/litterrobot/translations/he.json +++ b/homeassistant/components/litterrobot/translations/he.json @@ -23,5 +23,15 @@ } } } + }, + "entity": { + "sensor": { + "status_code": { + "state": { + "off": "\u05db\u05d1\u05d5\u05d9", + "p": "\u05de\u05d5\u05e9\u05d4\u05d4" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/litterrobot/translations/hu.json b/homeassistant/components/litterrobot/translations/hu.json index c35fc251f8a..88a8ba5830e 100644 --- a/homeassistant/components/litterrobot/translations/hu.json +++ b/homeassistant/components/litterrobot/translations/hu.json @@ -25,6 +25,39 @@ } } }, + "entity": { + "sensor": { + "status_code": { + "state": { + "br": "Tet\u0151 elt\u00e1vol\u00edtva", + "ccc": "Tiszt\u00edt\u00e1s befejez\u0151d\u00f6tt", + "ccp": "Tiszt\u00edt\u00e1s folyamatban", + "cd": "Macska \u00e9szlelve", + "csf": "Macska\u00e9rz\u00e9kel\u0151 hiba", + "csi": "Macska\u00e9rz\u00e9kel\u0151 megszak\u00edtva", + "cst": "Macska\u00e9rz\u00e9kel\u0151 id\u0151z\u00edt\u00e9se", + "df1": "A fi\u00f3k majdnem megtelt \u2013 2 ciklus van h\u00e1tra", + "df2": "A fi\u00f3k majdnem megtelt \u2013 1 ciklus maradt", + "dfs": "Fi\u00f3k tele", + "dhf": "Dump + Home poz\u00edci\u00f3 hiba", + "dpf": "Dump poz\u00edci\u00f3 hiba", + "ec": "\u00dcr\u00edt\u00e9si ciklus", + "hpf": "Home poz\u00edci\u00f3 hiba", + "off": "Ki", + "offline": "Offline", + "otf": "T\u00falzott nyomat\u00e9k hiba", + "p": "Sz\u00fcnetel", + "pd": "Csippent\u00e9s\u00e9rz\u00e9kel\u00e9s", + "pwrd": "Kikapcsol\u00e1s", + "pwru": "Bekapcsol\u00e1s", + "rdy": "K\u00e9sz", + "scf": "Macska\u00e9rz\u00e9kel\u0151 hiba ind\u00edt\u00e1skor", + "sdf": "Ind\u00edt\u00e1skor megtelt a fi\u00f3k", + "spf": "Csippent\u00e9s\u00e9rz\u00e9kel\u00e9s ind\u00edt\u00e1skor" + } + } + } + }, "issues": { "migrated_attributes": { "description": "A az entit\u00e1s attrib\u00fatumok mostant\u00f3l diagnosztikai \u00e9rz\u00e9kel\u0151k\u00e9nt \u00e1llnak rendelkez\u00e9sre.\n\nK\u00e9rrem, korrig\u00e1lja azokat az automatizmusokat vagy szkripteket, amelyek ezeket az attrib\u00fatumokat haszn\u00e1lj\u00e1k.", diff --git a/homeassistant/components/litterrobot/translations/id.json b/homeassistant/components/litterrobot/translations/id.json index b8d3d58a8dc..a2177936666 100644 --- a/homeassistant/components/litterrobot/translations/id.json +++ b/homeassistant/components/litterrobot/translations/id.json @@ -25,6 +25,39 @@ } } }, + "entity": { + "sensor": { + "status_code": { + "state": { + "br": "Bonnet Dihapus", + "ccc": "Siklus Pembersihan Selesai", + "ccp": "Siklus Pembersihan Sedang Berlangsung", + "cd": "Kucing Terdeteksi", + "csf": "Kesalahan Sensor Kucing", + "csi": "Sensor Kucing Terganggu", + "cst": "Waktu Sensor Kucing", + "df1": "Laci Hampir Penuh - Tersisa 2 Siklus", + "df2": "Laci Hampir Penuh - Tersisa 1 Siklus", + "dfs": "Laci Penuh", + "dhf": "Kesalahan Posisi Dump + Home", + "dpf": "Kesalahan Posisi Dump", + "ec": "Siklus Pengosongan", + "hpf": "Kesalahan Posisi Home", + "off": "Mati", + "offline": "Luring", + "otf": "Kesalahan Torsi Berlebih", + "p": "Jeda", + "pd": "Deteksi Pinch", + "pwrd": "Mematikan Daya", + "pwru": "Menyalakan", + "rdy": "Siap", + "scf": "Kesalahan Sensor Kucing Saat Mulai", + "sdf": "Laci Penuh Saat Memulai", + "spf": "Deteksi Pinch Saat Memulai" + } + } + } + }, "issues": { "migrated_attributes": { "description": "Atribut entitas vakum sekarang tersedia sebagai sensor diagnostik.\n\nSesuaikan semua otomasi atau skrip yang mungkin Anda miliki yang menggunakan atribut ini.", diff --git a/homeassistant/components/litterrobot/translations/it.json b/homeassistant/components/litterrobot/translations/it.json index 7e09efc0610..af3b325c85c 100644 --- a/homeassistant/components/litterrobot/translations/it.json +++ b/homeassistant/components/litterrobot/translations/it.json @@ -25,6 +25,39 @@ } } }, + "entity": { + "sensor": { + "status_code": { + "state": { + "br": "Coperchio rimosso", + "ccc": "Ciclo di pulizia completato", + "ccp": "Ciclo di pulizia in corso", + "cd": "Gatto rilevato", + "csf": "Errore sensore gatto", + "csi": "Sensore gatto interrotto", + "cst": "Temporizzazione sensore gatto", + "df1": "Cassetto quasi pieno - 2 cicli rimanenti", + "df2": "Cassetto quasi pieno - 1 ciclo rimanente", + "dfs": "Cassetto pieno", + "dhf": "Errore di posizione iniziale e di scarico", + "dpf": "Errore di posizione di scarico", + "ec": "Ciclo vuoto", + "hpf": "Errore di posizione iniziale", + "off": "Spento", + "offline": "Non in linea", + "otf": "Errore di coppia eccessiva", + "p": "In pausa", + "pd": "Rileva pizzicamento", + "pwrd": "Spegnimento", + "pwru": "Accensione", + "rdy": "Pronto", + "scf": "Guasto del sensore gatto all'avvio", + "sdf": "Cassetto pieno all'avvio", + "spf": "Rileva pizzicamento all'avvio" + } + } + } + }, "issues": { "migrated_attributes": { "description": "Gli attributi dell'entit\u00e0 aspirapolvere sono ora disponibili come sensori diagnostici. \n\nModifica eventuali automazioni o script che potresti avere che utilizzano questi attributi.", diff --git a/homeassistant/components/litterrobot/translations/ja.json b/homeassistant/components/litterrobot/translations/ja.json index 0106d4b63c6..4736dae7d0b 100644 --- a/homeassistant/components/litterrobot/translations/ja.json +++ b/homeassistant/components/litterrobot/translations/ja.json @@ -24,5 +24,17 @@ } } } + }, + "entity": { + "sensor": { + "status_code": { + "state": { + "ec": "\u7a7a\u306e\u30b5\u30a4\u30af\u30eb", + "hpf": "\u30db\u30fc\u30e0\u30dd\u30b8\u30b7\u30e7\u30f3\u30a8\u30e9\u30fc", + "off": "\u30aa\u30d5", + "offline": "\u30aa\u30d5\u30e9\u30a4\u30f3" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/litterrobot/translations/ko.json b/homeassistant/components/litterrobot/translations/ko.json index 94261de9637..2b39c5cc04e 100644 --- a/homeassistant/components/litterrobot/translations/ko.json +++ b/homeassistant/components/litterrobot/translations/ko.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" }, "error": { "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", @@ -9,6 +10,12 @@ "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" }, "step": { + "reauth_confirm": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638" + }, + "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" + }, "user": { "data": { "password": "\ube44\ubc00\ubc88\ud638", diff --git a/homeassistant/components/litterrobot/translations/nl.json b/homeassistant/components/litterrobot/translations/nl.json index 09d9c66090d..06991532cd9 100644 --- a/homeassistant/components/litterrobot/translations/nl.json +++ b/homeassistant/components/litterrobot/translations/nl.json @@ -23,5 +23,15 @@ } } } + }, + "entity": { + "sensor": { + "status_code": { + "state": { + "cd": "Kat gedetecteerd", + "p": "Gepauzeerd" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/litterrobot/translations/no.json b/homeassistant/components/litterrobot/translations/no.json index 92853ca057c..6c1c6c174e3 100644 --- a/homeassistant/components/litterrobot/translations/no.json +++ b/homeassistant/components/litterrobot/translations/no.json @@ -25,6 +25,39 @@ } } }, + "entity": { + "sensor": { + "status_code": { + "state": { + "br": "Panser fjernet", + "ccc": "Rengj\u00f8ringssyklus fullf\u00f8rt", + "ccp": "Rengj\u00f8ringssyklus p\u00e5g\u00e5r", + "cd": "Katt oppdaget", + "csf": "Cat Sensor Feil", + "csi": "Cat Sensor avbrutt", + "cst": "Cat Sensor Timing", + "df1": "Skuff nesten full - 2 sykluser igjen", + "df2": "Skuff nesten full - 1 syklus igjen", + "dfs": "Skuff full", + "dhf": "Dump + Hjemmeposisjonsfeil", + "dpf": "Dumpposisjonsfeil", + "ec": "T\u00f8mme syklus", + "hpf": "Feil i hjemmeposisjon", + "off": "Av", + "offline": "Frakoblet", + "otf": "Over dreiemomentfeil", + "p": "Pauset", + "pd": "Knip oppdaget", + "pwrd": "Sl\u00e5r seg av", + "pwru": "Sl\u00e5r seg p\u00e5", + "rdy": "Klar", + "scf": "Cat-sensorfeil ved oppstart", + "sdf": "Skuff full ved oppstart", + "spf": "Knip oppdage ved oppstart" + } + } + } + }, "issues": { "migrated_attributes": { "description": "Vakuumenhetsattributtene er n\u00e5 tilgjengelige som diagnostiske sensorer. \n\n Juster eventuelle automatiseringer eller skript du m\u00e5tte ha som bruker disse attributtene.", diff --git a/homeassistant/components/litterrobot/translations/pl.json b/homeassistant/components/litterrobot/translations/pl.json index aaad705c2a7..584ff096718 100644 --- a/homeassistant/components/litterrobot/translations/pl.json +++ b/homeassistant/components/litterrobot/translations/pl.json @@ -25,6 +25,39 @@ } } }, + "entity": { + "sensor": { + "status_code": { + "state": { + "br": "pokrywa otwarta", + "ccc": "cykl czyszczenia zako\u0144czony", + "ccp": "cykl czyszczenia w toku", + "cd": "wykryto kota", + "csf": "b\u0142\u0105d sensora obecno\u015bci", + "csi": "przerwa w pracy sensora", + "cst": "czas pracy sensora", + "df1": "szuflada prawie pe\u0142na - pozosta\u0142y 2 cykle", + "df2": "szuflada prawie pe\u0142na - pozosta\u0142 1 cykl", + "dfs": "szuflada pe\u0142na", + "dhf": "b\u0142\u0105d pozycji wyj\u015bciowej + odchod\u00f3w", + "dpf": "b\u0142\u0105d pozycji odchod\u00f3w", + "ec": "cykl opr\u00f3\u017cniania", + "hpf": "b\u0142\u0105d pozycji wyj\u015bciowej", + "off": "wy\u0142.", + "offline": "offline", + "otf": "b\u0142\u0105d nadmiernego momentu obrotowego", + "p": "wstrzymany", + "pd": "detektor obecno\u015bci", + "pwrd": "wy\u0142\u0105czanie", + "pwru": "w\u0142\u0105czanie", + "rdy": "gotowy", + "scf": "b\u0142\u0105d sensora obecno\u015bci podczas uruchamiania", + "sdf": "szuflada pe\u0142na podczas uruchamiania", + "spf": "detekcja obecno\u015bci podczas uruchamiania" + } + } + } + }, "issues": { "migrated_attributes": { "description": "Atrybuty encji s\u0105 teraz dost\u0119pne jako sensory diagnostyczne. \n\nDostosuj wszelkie automatyzacje lub skrypty korzystaj\u0105ce z tych atrybut\u00f3w.", diff --git a/homeassistant/components/litterrobot/translations/pt-BR.json b/homeassistant/components/litterrobot/translations/pt-BR.json index dfeb0a9018f..35997bb2fda 100644 --- a/homeassistant/components/litterrobot/translations/pt-BR.json +++ b/homeassistant/components/litterrobot/translations/pt-BR.json @@ -25,6 +25,39 @@ } } }, + "entity": { + "sensor": { + "status_code": { + "state": { + "br": "Cap\u00f4 removido", + "ccc": "Ciclo de Limpeza Completo", + "ccp": "Ciclo de limpeza em andamento", + "cd": "Gato detectado", + "csf": "Falha no sensor do gato", + "csi": "Sensor de Gato Interrompido", + "cst": "Sincroniza\u00e7\u00e3o do sensor do gato", + "df1": "Gaveta quase cheia - 2 ciclos restantes", + "df2": "Gaveta quase cheia - 1 ciclo restante", + "dfs": "Gaveta cheia", + "dhf": "Despejo + falha na posi\u00e7\u00e3o inicial", + "dpf": "Falha na posi\u00e7\u00e3o de despejo", + "ec": "Ciclo vazio", + "hpf": "Falha na posi\u00e7\u00e3o inicial", + "off": "Desligado", + "offline": "Offline", + "otf": "Falha de sobretorque", + "p": "Pausado", + "pd": "Detec\u00e7\u00e3o de aperto", + "pwrd": "Desligando", + "pwru": "Ligando", + "rdy": "Pronto", + "scf": "Falha do sensor Cat na inicializa\u00e7\u00e3o", + "sdf": "Gaveta cheia na inicializa\u00e7\u00e3o", + "spf": "Detec\u00e7\u00e3o de aperto na inicializa\u00e7\u00e3o" + } + } + } + }, "issues": { "migrated_attributes": { "description": "Os atributos da entidade de v\u00e1cuo agora est\u00e3o dispon\u00edveis como sensores de diagn\u00f3stico. \n\n Ajuste quaisquer automa\u00e7\u00f5es ou scripts que voc\u00ea possa ter que usem esses atributos.", diff --git a/homeassistant/components/litterrobot/translations/pt.json b/homeassistant/components/litterrobot/translations/pt.json index c2bf0536ccf..d8590a42ef8 100644 --- a/homeassistant/components/litterrobot/translations/pt.json +++ b/homeassistant/components/litterrobot/translations/pt.json @@ -1,14 +1,21 @@ { "config": { "abort": { - "already_configured": "Conta j\u00e1 configurada" + "already_configured": "Conta j\u00e1 configurada", + "reauth_successful": "Reautentica\u00e7\u00e3o bem sucedida" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, "step": { + "reauth_confirm": { + "data": { + "password": "Palavra-passe" + }, + "title": "Reautenticar integra\u00e7\u00e3o" + }, "user": { "data": { "password": "Palavra-passe", diff --git a/homeassistant/components/litterrobot/translations/ru.json b/homeassistant/components/litterrobot/translations/ru.json index 7df59645cb8..2b2985ae6cb 100644 --- a/homeassistant/components/litterrobot/translations/ru.json +++ b/homeassistant/components/litterrobot/translations/ru.json @@ -25,6 +25,39 @@ } } }, + "entity": { + "sensor": { + "status_code": { + "state": { + "br": "\u041a\u043e\u0436\u0443\u0445 \u0441\u043d\u044f\u0442", + "ccc": "\u0426\u0438\u043a\u043b \u043e\u0447\u0438\u0441\u0442\u043a\u0438 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d", + "ccp": "\u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0446\u0438\u043a\u043b \u043e\u0447\u0438\u0441\u0442\u043a\u0438", + "cd": "\u041e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d \u043a\u043e\u0442", + "csf": "\u041d\u0435\u0438\u0441\u043f\u0440\u0430\u0432\u043d\u043e\u0441\u0442\u044c \u0434\u0430\u0442\u0447\u0438\u043a\u0430", + "csi": "\u0414\u0430\u0442\u0447\u0438\u043a \u043f\u0435\u0440\u0435\u043a\u0440\u044b\u0442", + "cst": "\u0412\u0440\u0435\u043c\u044f \u0441\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u043d\u0438\u044f \u0434\u0430\u0442\u0447\u0438\u043a\u0430", + "df1": "\u042f\u0449\u0438\u043a \u043f\u043e\u0447\u0442\u0438 \u043f\u043e\u043b\u043e\u043d \u2014 \u043e\u0441\u0442\u0430\u043b\u043e\u0441\u044c 2 \u0446\u0438\u043a\u043b\u0430", + "df2": "\u042f\u0449\u0438\u043a \u043f\u043e\u0447\u0442\u0438 \u043f\u043e\u043b\u043e\u043d \u2014 \u043e\u0441\u0442\u0430\u043b\u0441\u044f 1 \u0446\u0438\u043a\u043b", + "dfs": "\u042f\u0449\u0438\u043a \u043f\u043e\u043b\u043d\u044b\u0439", + "dhf": "\u0421\u0431\u0440\u043e\u0441 + \u043e\u0448\u0438\u0431\u043a\u0430 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f", + "dpf": "\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0441\u0431\u0440\u043e\u0441\u0430", + "ec": "\u041f\u0443\u0441\u0442\u043e\u0439 \u0446\u0438\u043a\u043b", + "hpf": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f", + "off": "\u0412\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u043e", + "offline": "\u041d\u0435 \u0432 \u0441\u0435\u0442\u0438", + "otf": "\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0435\u0432\u044b\u0448\u0435\u043d\u0438\u044f \u043a\u0440\u0443\u0442\u044f\u0449\u0435\u0433\u043e \u043c\u043e\u043c\u0435\u043d\u0442\u0430", + "p": "\u041f\u0430\u0443\u0437\u0430", + "pd": "\u041e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u0435 \u0437\u0430\u0449\u0435\u043c\u043b\u0435\u043d\u0438\u044f", + "pwrd": "\u041e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043f\u0438\u0442\u0430\u043d\u0438\u044f", + "pwru": "\u0412\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043f\u0438\u0442\u0430\u043d\u0438\u044f", + "rdy": "\u0413\u043e\u0442\u043e\u0432", + "scf": "\u041d\u0435\u0438\u0441\u043f\u0440\u0430\u0432\u043d\u043e\u0441\u0442\u044c \u0434\u0430\u0442\u0447\u0438\u043a\u0430 \u043f\u0440\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435", + "sdf": "\u042f\u0449\u0438\u043a \u043f\u043e\u043b\u043d\u044b\u0439 \u043f\u0440\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435", + "spf": "\u041e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u0435 \u0437\u0430\u0449\u0435\u043c\u043b\u0435\u043d\u0438\u044f \u043f\u0440\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435" + } + } + } + }, "issues": { "migrated_attributes": { "description": "\u0410\u0442\u0440\u0438\u0431\u0443\u0442\u044b \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u043f\u044b\u043b\u0435\u0441\u043e\u0441\u0430 \u0442\u0435\u043f\u0435\u0440\u044c \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b \u043a\u0430\u043a \u0441\u0435\u043d\u0441\u043e\u0440\u044b \u0434\u0438\u0430\u0433\u043d\u043e\u0441\u0442\u0438\u043a\u0438.\n\u041e\u0442\u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0443\u0439\u0442\u0435 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u0441\u043a\u0440\u0438\u043f\u0442\u044b, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0449\u0438\u0435 \u044d\u0442\u0438 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b.", diff --git a/homeassistant/components/litterrobot/translations/sensor.sk.json b/homeassistant/components/litterrobot/translations/sensor.sk.json index 4b9941a9477..bed910f9905 100644 --- a/homeassistant/components/litterrobot/translations/sensor.sk.json +++ b/homeassistant/components/litterrobot/translations/sensor.sk.json @@ -1,11 +1,31 @@ { "state": { "litterrobot__status_code": { + "br": "Odstr\u00e1nen\u00fd kryt", + "ccc": "Dokon\u010denie \u010distiaceho cyklu", + "ccp": "Prebieha \u010distiaci cyklus", "cd": "Zisten\u00e1 ma\u010dka", + "csf": "Porucha sn\u00edma\u010da ma\u010dky", + "csi": "Sn\u00edma\u010d ma\u010dky je preru\u0161en\u00fd", + "cst": "\u010casovanie sn\u00edma\u010da ma\u010dky", + "df1": "Z\u00e1suvka takmer pln\u00e1 - zost\u00e1vaj\u00fa 2 cykly", + "df2": "Z\u00e1suvka takmer pln\u00e1 - zost\u00e1va 1 cyklus", + "dfs": "Pln\u00e1 z\u00e1suvka", + "dhf": "Porucha polohy v\u00fdsypky + Home", + "dpf": "Porucha polohy v\u00fdsypky", + "ec": "Pr\u00e1zdny cyklus", + "hpf": "Porucha polohy Home", "off": "Vypnut\u00fd", "offline": "Offline", + "otf": "Porucha nadmern\u00e9ho kr\u00fatiaceho momentu", "p": "Pozastaven\u00fd", - "rdy": "Pripraven\u00fd" + "pd": "Detekcia prek\u00e1\u017eky", + "pwrd": "Vyp\u00edna sa", + "pwru": "Zap\u00edna sa", + "rdy": "Pripraven\u00fd", + "scf": "Porucha sn\u00edma\u010da ma\u010dky pri spusten\u00ed", + "sdf": "Pln\u00e1 z\u00e1suvka pri spusten\u00ed", + "spf": "Zistenie prek\u00e1\u017eky pri spusten\u00ed" } } } \ No newline at end of file diff --git a/homeassistant/components/litterrobot/translations/sk.json b/homeassistant/components/litterrobot/translations/sk.json index 3896b31534b..7d10f29e407 100644 --- a/homeassistant/components/litterrobot/translations/sk.json +++ b/homeassistant/components/litterrobot/translations/sk.json @@ -24,5 +24,44 @@ } } } + }, + "entity": { + "sensor": { + "status_code": { + "state": { + "br": "Odstr\u00e1nen\u00fd kryt", + "ccc": "Dokon\u010denie \u010distiaceho cyklu", + "ccp": "Prebieha \u010distiaci cyklus", + "cd": "Zisten\u00e1 ma\u010dka", + "csf": "Porucha sn\u00edma\u010da ma\u010dky", + "csi": "Sn\u00edma\u010d ma\u010dky je preru\u0161en\u00fd", + "cst": "\u010casovanie sn\u00edma\u010da ma\u010dky", + "df1": "Z\u00e1suvka takmer pln\u00e1 - zost\u00e1vaj\u00fa 2 cykly", + "df2": "Z\u00e1suvka takmer pln\u00e1 - zost\u00e1va 1 cyklus", + "dfs": "Pln\u00e1 z\u00e1suvka", + "dhf": "Porucha polohy v\u00fdsypky + Home", + "dpf": "Porucha polohy v\u00fdsypky", + "ec": "Pr\u00e1zdny cyklus", + "hpf": "Porucha polohy Home", + "off": "Neakt\u00edvny", + "offline": "Offline", + "otf": "Porucha nadmern\u00e9ho kr\u00fatiaceho momentu", + "p": "Pozastaven\u00fd", + "pd": "Detekcia prek\u00e1\u017eky", + "pwrd": "Vyp\u00edna sa", + "pwru": "Zap\u00edna sa", + "rdy": "Pripraven\u00e9", + "scf": "Porucha sn\u00edma\u010da ma\u010dky pri spusten\u00ed", + "sdf": "Pln\u00e1 z\u00e1suvka pri spusten\u00ed", + "spf": "Zistenie prek\u00e1\u017eky pri spusten\u00ed" + } + } + } + }, + "issues": { + "migrated_attributes": { + "description": "Atrib\u00faty v\u00e1kuovej entity s\u00fa teraz dostupn\u00e9 ako diagnostick\u00e9 sn\u00edma\u010de. \n\nUpravte v\u0161etky automatiz\u00e1cie alebo skripty, ktor\u00e9 m\u00f4\u017eete pou\u017e\u00edva\u0165 a pou\u017e\u00edvaj\u00fa tieto atrib\u00faty.", + "title": "Atrib\u00faty Litter-Robot s\u00fa teraz ich vlastn\u00e9 snima\u010de" + } } } \ No newline at end of file diff --git a/homeassistant/components/litterrobot/translations/zh-Hant.json b/homeassistant/components/litterrobot/translations/zh-Hant.json index e3d32a2d75f..c7b0bc1aaa3 100644 --- a/homeassistant/components/litterrobot/translations/zh-Hant.json +++ b/homeassistant/components/litterrobot/translations/zh-Hant.json @@ -25,6 +25,39 @@ } } }, + "entity": { + "sensor": { + "status_code": { + "state": { + "br": "\u4e0a\u84cb\u906d\u958b\u555f", + "ccc": "\u8c93\u7802\u6e05\u7406\u5b8c\u6210", + "ccp": "\u8c93\u7802\u6e05\u7406\u4e2d", + "cd": "\u5075\u6e2c\u5230\u8c93\u54aa", + "csf": "\u8c93\u54aa\u611f\u6e2c\u5668\u6545\u969c", + "csi": "\u8c93\u54aa\u611f\u6e2c\u5668\u906d\u4e2d\u65b7", + "cst": "\u8c93\u54aa\u611f\u6e2c\u5668\u8a08\u6642", + "df1": "\u6392\u5ee2\u76d2\u5feb\u6eff\u4e86 - \u5269\u4e0b 2 \u6b21\u6e05\u7406", + "df2": "\u6392\u5ee2\u76d2\u5feb\u6eff\u4e86 - \u5269\u4e0b 1 \u6b21\u6e05\u7406", + "dfs": "\u6392\u5ee2\u76d2\u5df2\u6eff", + "dhf": "\u50be\u5012\u8207\u539f\u9ede\u4f4d\u7f6e\u6545\u969c", + "dpf": "\u50be\u5012\u4f4d\u7f6e\u6545\u969c", + "ec": "\u6574\u5e73", + "hpf": "\u539f\u9ede\u5b9a\u4f4d\u6545\u969c", + "off": "\u95dc\u9589", + "offline": "\u96e2\u7dda", + "otf": "\u8f49\u52d5\u5931\u6557", + "p": "\u5df2\u66ab\u505c", + "pd": "\u7570\u7269\u5075\u6e2c", + "pwrd": "\u95dc\u6a5f\u4e2d", + "pwru": "\u555f\u52d5\u4e2d", + "rdy": "\u6e96\u5099\u5c31\u7dd2", + "scf": "\u555f\u52d5\u6642\u8c93\u54aa\u611f\u6e2c\u5668\u5931\u6548", + "sdf": "\u555f\u52d5\u6642\u6392\u5ee2\u76d2\u5df2\u6eff", + "spf": "\u555f\u52d5\u6642\u5075\u6e2c\u5230\u7570\u7269" + } + } + } + }, "issues": { "migrated_attributes": { "description": "\u5438\u5875\u5668\u5be6\u9ad4\u5c6c\u6027\u73fe\u5728\u53ef\u4f5c\u70ba\u8a3a\u65b7\u8cc7\u6599\u611f\u6e2c\u5668\u3002\n\n\u5047\u5982\u6709\u81ea\u52d5\u5316\u6216\u8173\u672c\u4f7f\u7528\u5230\u9019\u4e9b\u5c6c\u6027\u3001\u8acb\u9032\u884c\u8abf\u6574\u3002", diff --git a/homeassistant/components/litterrobot/update.py b/homeassistant/components/litterrobot/update.py new file mode 100644 index 00000000000..d6475ea486b --- /dev/null +++ b/homeassistant/components/litterrobot/update.py @@ -0,0 +1,111 @@ +"""Support for Litter-Robot updates.""" +from __future__ import annotations + +from collections.abc import Callable +from datetime import datetime, timedelta +from typing import Any + +from pylitterbot import LitterRobot4 + +from homeassistant.components.update import ( + UpdateDeviceClass, + UpdateEntity, + UpdateEntityDescription, + UpdateEntityFeature, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.event import async_call_later +from homeassistant.helpers.start import async_at_start + +from .const import DOMAIN +from .entity import LitterRobotEntity, LitterRobotHub + +FIRMWARE_UPDATE_ENTITY = UpdateEntityDescription( + key="firmware", + name="Firmware", + device_class=UpdateDeviceClass.FIRMWARE, +) + + +async def async_setup_entry( + hass: HomeAssistant, + entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up Litter-Robot update platform.""" + hub: LitterRobotHub = hass.data[DOMAIN][entry.entry_id] + robots = hub.account.robots + entities = [ + RobotUpdateEntity(robot=robot, hub=hub, description=FIRMWARE_UPDATE_ENTITY) + for robot in robots + if isinstance(robot, LitterRobot4) + ] + async_add_entities(entities) + + +class RobotUpdateEntity(LitterRobotEntity[LitterRobot4], UpdateEntity): + """A class that describes robot update entities.""" + + _attr_supported_features = ( + UpdateEntityFeature.INSTALL | UpdateEntityFeature.PROGRESS + ) + + def __init__( + self, + robot: LitterRobot4, + hub: LitterRobotHub, + description: UpdateEntityDescription, + ) -> None: + """Initialize a Litter-Robot update entity.""" + super().__init__(robot, hub, description) + self._poll_unsub: Callable[[], None] | None = None + + @property + def installed_version(self) -> str: + """Version installed and in use.""" + return self.robot.firmware + + @property + def in_progress(self) -> bool: + """Update installation progress.""" + return self.robot.firmware_update_triggered + + async def _async_update(self, _: HomeAssistant | datetime | None = None) -> None: + """Update the entity.""" + self._poll_unsub = None + + if await self.robot.has_firmware_update(): + latest_version = await self.robot.get_latest_firmware() + else: + latest_version = self.installed_version + + if self._attr_latest_version != self.installed_version: + self._attr_latest_version = latest_version + self.async_write_ha_state() + + self._poll_unsub = async_call_later( + self.hass, timedelta(days=1), self._async_update + ) + + async def async_added_to_hass(self) -> None: + """Set up a listener for the entity.""" + await super().async_added_to_hass() + self.async_on_remove(async_at_start(self.hass, self._async_update)) + + async def async_install( + self, version: str | None, backup: bool, **kwargs: Any + ) -> None: + """Install an update.""" + if await self.robot.has_firmware_update(): + if not await self.robot.update_firmware(): + message = f"Unable to start firmware update on {self.robot.name}" + raise HomeAssistantError(message) + + async def async_will_remove_from_hass(self) -> None: + """Call when entity will be removed.""" + if self._poll_unsub: + self._poll_unsub() + self._poll_unsub = None diff --git a/homeassistant/components/livisi/translations/ko.json b/homeassistant/components/livisi/translations/ko.json new file mode 100644 index 00000000000..4eb64965344 --- /dev/null +++ b/homeassistant/components/livisi/translations/ko.json @@ -0,0 +1,15 @@ +{ + "config": { + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "host": "IP \uc8fc\uc18c", + "password": "\ube44\ubc00\ubc88\ud638" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/livisi/translations/nl.json b/homeassistant/components/livisi/translations/nl.json new file mode 100644 index 00000000000..e02950f786f --- /dev/null +++ b/homeassistant/components/livisi/translations/nl.json @@ -0,0 +1,16 @@ +{ + "config": { + "error": { + "cannot_connect": "Kan geen verbinding maken", + "wrong_password": "Het wachtwoord is onjuist" + }, + "step": { + "user": { + "data": { + "host": "IP-adres", + "password": "Wachtwoord" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/livisi/translations/pt.json b/homeassistant/components/livisi/translations/pt.json new file mode 100644 index 00000000000..f4aa64afbb1 --- /dev/null +++ b/homeassistant/components/livisi/translations/pt.json @@ -0,0 +1,15 @@ +{ + "config": { + "error": { + "cannot_connect": "A liga\u00e7\u00e3o falhou" + }, + "step": { + "user": { + "data": { + "host": "Endere\u00e7o IP", + "password": "Palavra-passe" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/local_calendar/calendar.py b/homeassistant/components/local_calendar/calendar.py index 63a2f2b03d2..be6fb4a17b5 100644 --- a/homeassistant/components/local_calendar/calendar.py +++ b/homeassistant/components/local_calendar/calendar.py @@ -15,11 +15,7 @@ from pydantic import ValidationError import voluptuous as vol from homeassistant.components.calendar import ( - EVENT_DESCRIPTION, - EVENT_END, EVENT_RRULE, - EVENT_START, - EVENT_SUMMARY, CalendarEntity, CalendarEntityFeature, CalendarEvent, @@ -55,7 +51,9 @@ class LocalCalendarEntity(CalendarEntity): _attr_has_entity_name = True _attr_supported_features = ( - CalendarEntityFeature.CREATE_EVENT | CalendarEntityFeature.DELETE_EVENT + CalendarEntityFeature.CREATE_EVENT + | CalendarEntityFeature.DELETE_EVENT + | CalendarEntityFeature.UPDATE_EVENT ) def __init__( @@ -104,22 +102,7 @@ class LocalCalendarEntity(CalendarEntity): async def async_create_event(self, **kwargs: Any) -> None: """Add a new event to calendar.""" - event_data = { - EVENT_SUMMARY: kwargs[EVENT_SUMMARY], - EVENT_START: kwargs[EVENT_START], - EVENT_END: kwargs[EVENT_END], - EVENT_DESCRIPTION: kwargs.get(EVENT_DESCRIPTION), - } - try: - event = Event.parse_obj(event_data) - except ValidationError as err: - _LOGGER.debug( - "Error parsing event input fields: %s (%s)", event_data, str(err) - ) - raise vol.Invalid("Error parsing event input fields") from err - if rrule := kwargs.get(EVENT_RRULE): - event.rrule = Recur.from_rrule(rrule) - + event = _parse_event(kwargs) EventStore(self._calendar).add(event) await self._async_store() await self.async_update_ha_state(force_refresh=True) @@ -142,6 +125,38 @@ class LocalCalendarEntity(CalendarEntity): await self._async_store() await self.async_update_ha_state(force_refresh=True) + async def async_update_event( + self, + uid: str, + event: dict[str, Any], + recurrence_id: str | None = None, + recurrence_range: str | None = None, + ) -> None: + """Update an existing event on the calendar.""" + new_event = _parse_event(event) + range_value: Range = Range.NONE + if recurrence_range == Range.THIS_AND_FUTURE: + range_value = Range.THIS_AND_FUTURE + EventStore(self._calendar).edit( + uid, + new_event, + recurrence_id=recurrence_id, + recurrence_range=range_value, + ) + await self._async_store() + await self.async_update_ha_state(force_refresh=True) + + +def _parse_event(event: dict[str, Any]) -> Event: + """Parse an ical event from a home assistant event dictionary.""" + if rrule := event.get(EVENT_RRULE): + event[EVENT_RRULE] = Recur.from_rrule(rrule) + try: + return Event.parse_obj(event) + except ValidationError as err: + _LOGGER.debug("Error parsing event input fields: %s (%s)", event, str(err)) + raise vol.Invalid("Error parsing event input fields") from err + def _get_calendar_event(event: Event) -> CalendarEvent: """Return a CalendarEvent from an API event.""" diff --git a/homeassistant/components/local_calendar/manifest.json b/homeassistant/components/local_calendar/manifest.json index 21ec76bf967..b277611dfdb 100644 --- a/homeassistant/components/local_calendar/manifest.json +++ b/homeassistant/components/local_calendar/manifest.json @@ -3,7 +3,7 @@ "name": "Local Calendar", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/local_calendar", - "requirements": ["ical==4.2.4"], + "requirements": ["ical==4.2.8"], "codeowners": ["@allenporter"], "iot_class": "local_polling", "loggers": ["ical"] diff --git a/homeassistant/components/local_calendar/translations/bg.json b/homeassistant/components/local_calendar/translations/bg.json new file mode 100644 index 00000000000..309325be7fc --- /dev/null +++ b/homeassistant/components/local_calendar/translations/bg.json @@ -0,0 +1,12 @@ +{ + "config": { + "step": { + "user": { + "data": { + "calendar_name": "\u0418\u043c\u0435 \u043d\u0430 \u043a\u0430\u043b\u0435\u043d\u0434\u0430\u0440\u0430" + }, + "description": "\u041c\u043e\u043b\u044f, \u0438\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u0438\u043c\u0435 \u0437\u0430 \u0432\u0430\u0448\u0438\u044f \u043d\u043e\u0432 \u043a\u0430\u043b\u0435\u043d\u0434\u0430\u0440" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/local_calendar/translations/ca.json b/homeassistant/components/local_calendar/translations/ca.json new file mode 100644 index 00000000000..a740c433dbc --- /dev/null +++ b/homeassistant/components/local_calendar/translations/ca.json @@ -0,0 +1,12 @@ +{ + "config": { + "step": { + "user": { + "data": { + "calendar_name": "Nom del calendari" + }, + "description": "Trieu un nom per al nou calendari" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/local_calendar/translations/de.json b/homeassistant/components/local_calendar/translations/de.json new file mode 100644 index 00000000000..f64f436a82d --- /dev/null +++ b/homeassistant/components/local_calendar/translations/de.json @@ -0,0 +1,12 @@ +{ + "config": { + "step": { + "user": { + "data": { + "calendar_name": "Kalendername" + }, + "description": "Bitte w\u00e4hle einen Namen f\u00fcr deinen neuen Kalender" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/local_calendar/translations/el.json b/homeassistant/components/local_calendar/translations/el.json new file mode 100644 index 00000000000..7cf485b6fc1 --- /dev/null +++ b/homeassistant/components/local_calendar/translations/el.json @@ -0,0 +1,12 @@ +{ + "config": { + "step": { + "user": { + "data": { + "calendar_name": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03b7\u03bc\u03b5\u03c1\u03bf\u03bb\u03bf\u03b3\u03af\u03bf\u03c5" + }, + "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03ad\u03bd\u03b1 \u03cc\u03bd\u03bf\u03bc\u03b1 \u03b3\u03b9\u03b1 \u03c4\u03bf \u03bd\u03ad\u03bf \u03c3\u03b1\u03c2 \u03b7\u03bc\u03b5\u03c1\u03bf\u03bb\u03cc\u03b3\u03b9\u03bf" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/local_calendar/translations/es.json b/homeassistant/components/local_calendar/translations/es.json new file mode 100644 index 00000000000..ec1904defd9 --- /dev/null +++ b/homeassistant/components/local_calendar/translations/es.json @@ -0,0 +1,12 @@ +{ + "config": { + "step": { + "user": { + "data": { + "calendar_name": "Nombre del calendario" + }, + "description": "Por favor, elige un nombre para tu nuevo calendario." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/local_calendar/translations/et.json b/homeassistant/components/local_calendar/translations/et.json new file mode 100644 index 00000000000..6d9f20bc673 --- /dev/null +++ b/homeassistant/components/local_calendar/translations/et.json @@ -0,0 +1,12 @@ +{ + "config": { + "step": { + "user": { + "data": { + "calendar_name": "Kalendri nimi" + }, + "description": "Vali oma uuele kalendrile nimi" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/local_calendar/translations/hu.json b/homeassistant/components/local_calendar/translations/hu.json new file mode 100644 index 00000000000..d8efec9ec9e --- /dev/null +++ b/homeassistant/components/local_calendar/translations/hu.json @@ -0,0 +1,12 @@ +{ + "config": { + "step": { + "user": { + "data": { + "calendar_name": "Napt\u00e1r elnevez\u00e9se" + }, + "description": "K\u00e9rj\u00fck, v\u00e1lasszon nevet az \u00faj napt\u00e1r\u00e1nak" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/local_calendar/translations/id.json b/homeassistant/components/local_calendar/translations/id.json new file mode 100644 index 00000000000..2ab0f6b959f --- /dev/null +++ b/homeassistant/components/local_calendar/translations/id.json @@ -0,0 +1,12 @@ +{ + "config": { + "step": { + "user": { + "data": { + "calendar_name": "Nama Kalender" + }, + "description": "Tentukan nama untuk kalender baru Anda" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/local_calendar/translations/it.json b/homeassistant/components/local_calendar/translations/it.json new file mode 100644 index 00000000000..665dd9b1623 --- /dev/null +++ b/homeassistant/components/local_calendar/translations/it.json @@ -0,0 +1,12 @@ +{ + "config": { + "step": { + "user": { + "data": { + "calendar_name": "Nome del calendario" + }, + "description": "Scegli un nome per il tuo nuovo calendario" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/local_calendar/translations/ko.json b/homeassistant/components/local_calendar/translations/ko.json new file mode 100644 index 00000000000..e0d65a1a54a --- /dev/null +++ b/homeassistant/components/local_calendar/translations/ko.json @@ -0,0 +1,12 @@ +{ + "config": { + "step": { + "user": { + "data": { + "calendar_name": "\uce98\ub9b0\ub354 \uc774\ub984" + }, + "description": "\uc0c8 \uce98\ub9b0\ub354\uc758 \uc774\ub984\uc744 \uc120\ud0dd\ud558\uc138\uc694." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/local_calendar/translations/nl.json b/homeassistant/components/local_calendar/translations/nl.json new file mode 100644 index 00000000000..45dd83b9a80 --- /dev/null +++ b/homeassistant/components/local_calendar/translations/nl.json @@ -0,0 +1,12 @@ +{ + "config": { + "step": { + "user": { + "data": { + "calendar_name": "Naam agenda" + }, + "description": "Kies een naam voor je nieuwe agenda" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/local_calendar/translations/no.json b/homeassistant/components/local_calendar/translations/no.json new file mode 100644 index 00000000000..3af0213f16b --- /dev/null +++ b/homeassistant/components/local_calendar/translations/no.json @@ -0,0 +1,12 @@ +{ + "config": { + "step": { + "user": { + "data": { + "calendar_name": "Kalendernavn" + }, + "description": "Velg et navn for din nye kalender" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/local_calendar/translations/pl.json b/homeassistant/components/local_calendar/translations/pl.json new file mode 100644 index 00000000000..c5397838aef --- /dev/null +++ b/homeassistant/components/local_calendar/translations/pl.json @@ -0,0 +1,12 @@ +{ + "config": { + "step": { + "user": { + "data": { + "calendar_name": "Nazwa kalendarza" + }, + "description": "Wybierz nazw\u0119 nowego kalendarza" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/local_calendar/translations/pt-BR.json b/homeassistant/components/local_calendar/translations/pt-BR.json new file mode 100644 index 00000000000..5fdb0aab423 --- /dev/null +++ b/homeassistant/components/local_calendar/translations/pt-BR.json @@ -0,0 +1,12 @@ +{ + "config": { + "step": { + "user": { + "data": { + "calendar_name": "Nome do calend\u00e1rio" + }, + "description": "Escolha um nome para o seu novo calend\u00e1rio" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/local_calendar/translations/ru.json b/homeassistant/components/local_calendar/translations/ru.json new file mode 100644 index 00000000000..68659854e68 --- /dev/null +++ b/homeassistant/components/local_calendar/translations/ru.json @@ -0,0 +1,12 @@ +{ + "config": { + "step": { + "user": { + "data": { + "calendar_name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043a\u0430\u043b\u0435\u043d\u0434\u0430\u0440\u044f" + }, + "description": "\u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0434\u043b\u044f \u0412\u0430\u0448\u0435\u0433\u043e \u043d\u043e\u0432\u043e\u0433\u043e \u043a\u0430\u043b\u0435\u043d\u0434\u0430\u0440\u044f" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/local_calendar/translations/sk.json b/homeassistant/components/local_calendar/translations/sk.json new file mode 100644 index 00000000000..e627bdaaefc --- /dev/null +++ b/homeassistant/components/local_calendar/translations/sk.json @@ -0,0 +1,12 @@ +{ + "config": { + "step": { + "user": { + "data": { + "calendar_name": "N\u00e1zov kalend\u00e1ra" + }, + "description": "Vyberte n\u00e1zov pre svoj nov\u00fd kalend\u00e1r" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/local_calendar/translations/zh-Hant.json b/homeassistant/components/local_calendar/translations/zh-Hant.json new file mode 100644 index 00000000000..f6c53f8c991 --- /dev/null +++ b/homeassistant/components/local_calendar/translations/zh-Hant.json @@ -0,0 +1,12 @@ +{ + "config": { + "step": { + "user": { + "data": { + "calendar_name": "\u884c\u4e8b\u66c6\u540d\u7a31" + }, + "description": "\u70ba\u884c\u4e8b\u66c6\u9078\u64c7\u4e00\u500b\u540d\u7a31" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/local_ip/translations/en.json b/homeassistant/components/local_ip/translations/en.json index eae07e0931b..edb4e01ab83 100644 --- a/homeassistant/components/local_ip/translations/en.json +++ b/homeassistant/components/local_ip/translations/en.json @@ -5,7 +5,7 @@ }, "step": { "user": { - "description": "Do you want to start set up?", + "description": "Do you want to start setup?", "title": "Local IP Address" } } diff --git a/homeassistant/components/local_ip/translations/it.json b/homeassistant/components/local_ip/translations/it.json index 525b75715fd..aa99c0332cb 100644 --- a/homeassistant/components/local_ip/translations/it.json +++ b/homeassistant/components/local_ip/translations/it.json @@ -5,7 +5,7 @@ }, "step": { "user": { - "description": "Vuoi iniziare la configurazione?", + "description": "Vuoi avviare la configurazione?", "title": "Indirizzo IP locale" } } diff --git a/homeassistant/components/local_ip/translations/pt.json b/homeassistant/components/local_ip/translations/pt.json index c5a4032636c..6587d1e1f57 100644 --- a/homeassistant/components/local_ip/translations/pt.json +++ b/homeassistant/components/local_ip/translations/pt.json @@ -5,7 +5,7 @@ }, "step": { "user": { - "description": "Quer dar inicio \u00e0 configura\u00e7\u00e3o?" + "description": "Quer dar in\u00edcio \u00e0 configura\u00e7\u00e3o?" } } }, diff --git a/homeassistant/components/locative/strings.json b/homeassistant/components/locative/strings.json index ff1999306f4..7cc53f18428 100644 --- a/homeassistant/components/locative/strings.json +++ b/homeassistant/components/locative/strings.json @@ -12,7 +12,7 @@ "webhook_not_internet_accessible": "[%key:common::config_flow::abort::webhook_not_internet_accessible%]" }, "create_entry": { - "default": "To send locations to Home Assistant, you will need to setup the webhook feature in the Locative app.\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n\nSee [the documentation]({docs_url}) for further details." + "default": "To send locations to Home Assistant, you will need to set up the webhook feature in the Locative app.\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n\nSee [the documentation]({docs_url}) for further details." } } } diff --git a/homeassistant/components/locative/translations/de.json b/homeassistant/components/locative/translations/de.json index 801ec910745..a7abaeca94f 100644 --- a/homeassistant/components/locative/translations/de.json +++ b/homeassistant/components/locative/translations/de.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "Deine Home Assistant-Instanz muss \u00fcber das Internet erreichbar sein, um Webhook-Nachrichten empfangen zu k\u00f6nnen." }, "create_entry": { - "default": "Um Standorte Home Assistant zu senden, muss das Webhook Feature in der Locative App konfiguriert werden.\n\n F\u00fcge die folgenden Informationen ein: \n\n - URL: ` {webhook_url} ` \n - Methode: POST \n \n Weitere Informationen finden sich in der [Dokumentation]({docs_url})." + "default": "Um Standorte an Home Assistant zu senden, muss das Webhook Feature in der Locative App konfiguriert werden.\n\nF\u00fcge die folgenden Informationen ein: \n\n- URL: `{webhook_url}` \n- Methode: POST \n \nWeitere Informationen finden sich in der [Dokumentation]({docs_url})." }, "step": { "user": { diff --git a/homeassistant/components/locative/translations/en.json b/homeassistant/components/locative/translations/en.json index 91710293751..fa5ca4b6897 100644 --- a/homeassistant/components/locative/translations/en.json +++ b/homeassistant/components/locative/translations/en.json @@ -6,11 +6,11 @@ "webhook_not_internet_accessible": "Your Home Assistant instance needs to be accessible from the internet to receive webhook messages." }, "create_entry": { - "default": "To send locations to Home Assistant, you will need to setup the webhook feature in the Locative app.\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n\nSee [the documentation]({docs_url}) for further details." + "default": "To send locations to Home Assistant, you will need to set up the webhook feature in the Locative app.\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n\nSee [the documentation]({docs_url}) for further details." }, "step": { "user": { - "description": "Do you want to start set up?", + "description": "Do you want to start setup?", "title": "Set up the Locative Webhook" } } diff --git a/homeassistant/components/locative/translations/es.json b/homeassistant/components/locative/translations/es.json index 676c1863b2e..ce622cd1d59 100644 --- a/homeassistant/components/locative/translations/es.json +++ b/homeassistant/components/locative/translations/es.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "Tu instancia de Home Assistant debe estar accesible desde Internet para recibir mensajes webhook." }, "create_entry": { - "default": "Para enviar ubicaciones a Home Assistant, es necesario configurar la caracter\u00edstica webhook en la app de Locative.\n\nCompleta la siguiente informaci\u00f3n:\n\n- URL: `{webhook_url}`\n- M\u00e9todo: POST\n\nConsulta [la documentaci\u00f3n]({docs_url}) para m\u00e1s detalles." + "default": "Para enviar ubicaciones a Home Assistant, necesitar\u00e1s configurar la funci\u00f3n de webhook en la app de Locative.\n\nCompleta la siguiente informaci\u00f3n:\n\n- URL: `{webhook_url}`\n- M\u00e9todo: POST\n\nConsulta [la documentaci\u00f3n]({docs_url}) para obtener m\u00e1s detalles." }, "step": { "user": { diff --git a/homeassistant/components/locative/translations/it.json b/homeassistant/components/locative/translations/it.json index 7cdb9b2170d..85363569b69 100644 --- a/homeassistant/components/locative/translations/it.json +++ b/homeassistant/components/locative/translations/it.json @@ -6,11 +6,11 @@ "webhook_not_internet_accessible": "L'istanza di Home Assistant deve essere accessibile da Internet per ricevere messaggi webhook." }, "create_entry": { - "default": "Per inviare localit\u00e0 a Home Assistant, dovrai configurare la funzionalit\u00e0 webhook nell'app Locative.\n\n Compila le seguenti informazioni: \n\n - URL: `{webhook_url}` \n - Metodo: POST \n\n Vedi [la documentazione]({docs_url}) per ulteriori dettagli." + "default": "Per inviare posizioni a Home Assistant, dovrai configurare la funzione webhook nell'app Locative. \n\n Compila le seguenti informazioni: \n\n - URL: `{webhook_url}`\n - Metodo: POST \n\n Consulta [la documentazione]({docs_url}) per ulteriori dettagli." }, "step": { "user": { - "description": "Vuoi iniziare la configurazione?", + "description": "Vuoi avviare la configurazione?", "title": "Configura il webhook di Locative" } } diff --git a/homeassistant/components/locative/translations/ko.json b/homeassistant/components/locative/translations/ko.json index 50652f76fc5..7a8478570d3 100644 --- a/homeassistant/components/locative/translations/ko.json +++ b/homeassistant/components/locative/translations/ko.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "cloud_not_connected": "Home Assistant \ud074\ub77c\uc6b0\ub4dc\uc5d0 \uc5f0\uacb0\ub418\uc5b4 \uc788\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.", "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.", "webhook_not_internet_accessible": "\uc6f9 \ud6c5 \uba54\uc2dc\uc9c0\ub97c \ubc1b\uc73c\ub824\uba74 \uc778\ud130\ub137\uc5d0\uc11c Home Assistant \uc778\uc2a4\ud134\uc2a4\uc5d0 \uc811\uadfc\ud560 \uc218 \uc788\uc5b4\uc57c \ud569\ub2c8\ub2e4." }, diff --git a/homeassistant/components/locative/translations/no.json b/homeassistant/components/locative/translations/no.json index 0982c627526..798f4c960f8 100644 --- a/homeassistant/components/locative/translations/no.json +++ b/homeassistant/components/locative/translations/no.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "Home Assistant forekomsten din m\u00e5 v\u00e6re tilgjengelig fra internett for \u00e5 kunne motta webhook meldinger" }, "create_entry": { - "default": "For \u00e5 kunne sende plasseringer til Home Assistant, m\u00e5 du sette opp webhook-funksjonen i Locative appen. \n\n Fyll ut f\u00f8lgende informasjon: \n\n - URL: `{webhook_url}` \n - Metode: POST \n\nSe [dokumentasjonen]({docs_url}) for ytterligere detaljer." + "default": "For \u00e5 sende posisjoner til Home Assistant, m\u00e5 du sette opp webhook-funksjonen i Locative-appen. \n\n Fyll inn f\u00f8lgende informasjon: \n\n - URL: ` {webhook_url} `\n - Metode: POST \n\n Se [dokumentasjonen]( {docs_url} ) for ytterligere detaljer." }, "step": { "user": { diff --git a/homeassistant/components/locative/translations/pt-BR.json b/homeassistant/components/locative/translations/pt-BR.json index d134a5113f4..f3d2cacbafe 100644 --- a/homeassistant/components/locative/translations/pt-BR.json +++ b/homeassistant/components/locative/translations/pt-BR.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "Sua inst\u00e2ncia do Home Assistant precisa estar acess\u00edvel pela Internet para receber mensagens de webhook." }, "create_entry": { - "default": "Para enviar locais para o Home Assistant, voc\u00ea precisar\u00e1 configurar o recurso webhook no aplicativo Locative. \n\n Preencha as seguintes informa\u00e7\u00f5es: \n\n - URL: ` {webhook_url} ` \n - M\u00e9todo: POST \n\n Veja [a documenta\u00e7\u00e3o] ( {docs_url} ) para mais detalhes." + "default": "Para enviar locais para o Home Assistant, voc\u00ea precisar\u00e1 configurar o recurso webhook no aplicativo Locative. \n\n Preencha as seguintes informa\u00e7\u00f5es: \n\n - URL: `{webhook_url}` \n - M\u00e9todo: POST \n\n Veja [a documenta\u00e7\u00e3o] ({docs_url}) para mais detalhes." }, "step": { "user": { diff --git a/homeassistant/components/locative/translations/sk.json b/homeassistant/components/locative/translations/sk.json index 04cb32a1c4e..2bf5e20ca57 100644 --- a/homeassistant/components/locative/translations/sk.json +++ b/homeassistant/components/locative/translations/sk.json @@ -5,9 +5,13 @@ "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia.", "webhook_not_internet_accessible": "Va\u0161a in\u0161tancia Home Assistant mus\u00ed by\u0165 pr\u00edstupn\u00e1 z internetu, aby ste mohli prij\u00edma\u0165 spr\u00e1vy webhooku." }, + "create_entry": { + "default": "Ak chcete odosla\u0165 polohy dom\u00e1cemu asistentovi, budete musie\u0165 nastavi\u0165 funkciu webhooku v aplik\u00e1cii Locative. \n\nVypl\u0148te nasleduj\u00face inform\u00e1cie: \n\n - URL: `{webhook_url}`\n - Met\u00f3da: POST \n\n\u010eal\u0161ie podrobnosti n\u00e1jdete v [dokument\u00e1cii]({docs_url})." + }, "step": { "user": { - "description": "Chcete za\u010da\u0165 nastavova\u0165?" + "description": "Chcete za\u010da\u0165 nastavova\u0165?", + "title": "Nastavte Locative Webhook" } } } diff --git a/homeassistant/components/logi_circle/__init__.py b/homeassistant/components/logi_circle/__init__.py index cabf6342fac..7e5d0df0259 100644 --- a/homeassistant/components/logi_circle/__init__.py +++ b/homeassistant/components/logi_circle/__init__.py @@ -144,8 +144,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: persistent_notification.create( hass, ( - f"Error: The cached access tokens are missing from {DEFAULT_CACHEDB}.
" - f"Please unload then re-add the Logi Circle integration to resolve." + "Error: The cached access tokens are missing from" + f" {DEFAULT_CACHEDB}.
Please unload then re-add the Logi Circle" + " integration to resolve." ), title=NOTIFICATION_TITLE, notification_id=NOTIFICATION_ID, @@ -160,10 +161,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: except AuthorizationFailed: persistent_notification.create( hass, - "Error: Failed to obtain an access token from the cached " - "refresh token.
" - "Token may have expired or been revoked.
" - "Please unload then re-add the Logi Circle integration to resolve", + ( + "Error: Failed to obtain an access token from the cached " + "refresh token.
" + "Token may have expired or been revoked.
" + "Please unload then re-add the Logi Circle integration to resolve" + ), title=NOTIFICATION_TITLE, notification_id=NOTIFICATION_ID, ) diff --git a/homeassistant/components/logi_circle/translations/de.json b/homeassistant/components/logi_circle/translations/de.json index fba8106c937..6a2ae59c643 100644 --- a/homeassistant/components/logi_circle/translations/de.json +++ b/homeassistant/components/logi_circle/translations/de.json @@ -13,7 +13,7 @@ }, "step": { "auth": { - "description": "Folge dem Link unten und dr\u00fccke **Akzeptieren** um auf dein Logi Circle-Konto zuzugreifen. Kehre dann zur\u00fcck und dr\u00fccke unten auf **Senden**. \n\n [Link] ({authorization_url})", + "description": "Folge dem Link unten und dr\u00fccke **Akzeptieren** um auf dein Logi Circle Konto zuzugreifen. Kehre dann zur\u00fcck und dr\u00fccke unten auf **Senden**. \n\n [Link] ({authorization_url})", "title": "Authentifizierung mit Logi Circle" }, "user": { diff --git a/homeassistant/components/logi_circle/translations/en_GB.json b/homeassistant/components/logi_circle/translations/en-GB.json similarity index 100% rename from homeassistant/components/logi_circle/translations/en_GB.json rename to homeassistant/components/logi_circle/translations/en-GB.json diff --git a/homeassistant/components/logi_circle/translations/pt.json b/homeassistant/components/logi_circle/translations/pt.json index 38375801547..b1d80db707e 100644 --- a/homeassistant/components/logi_circle/translations/pt.json +++ b/homeassistant/components/logi_circle/translations/pt.json @@ -5,7 +5,7 @@ "missing_configuration": "O componente n\u00e3o est\u00e1 configurado. Por favor, siga a documenta\u00e7\u00e3o." }, "error": { - "authorize_url_timeout": "Tempo excedido a gerar um URL de autoriza\u00e7\u00e3o", + "authorize_url_timeout": "Excedido o tempo limite para gerar um URL de autoriza\u00e7\u00e3o", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida" }, "step": { diff --git a/homeassistant/components/logi_circle/translations/sk.json b/homeassistant/components/logi_circle/translations/sk.json index da1f4892e03..f2cf2a5c74b 100644 --- a/homeassistant/components/logi_circle/translations/sk.json +++ b/homeassistant/components/logi_circle/translations/sk.json @@ -2,11 +2,27 @@ "config": { "abort": { "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", + "external_error": "V\u00fdnimka nastala z in\u00e9ho toku.", + "external_setup": "Logi Circle \u00faspe\u0161ne nakonfigurovan\u00fd in\u00fdm sp\u00f4sobom.", "missing_configuration": "Komponent nie je nakonfigurovan\u00fd. Postupujte pod\u013ea dokument\u00e1cie." }, "error": { "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL.", + "follow_link": "Pred stla\u010den\u00edm Odosla\u0165 kliknite na odkaz a overte sa.", "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "auth": { + "description": "Kliknite na odkaz ni\u017e\u0161ie a **Prijmite** pr\u00edstup k svojmu \u00fa\u010dtu Logi Circle, potom sa vr\u00e1\u0165te a stla\u010dte ni\u017e\u0161ie **Odosla\u0165**. \n\n[Odkaz]({authorization_url})", + "title": "Overte sa pomocou Logi Circle" + }, + "user": { + "data": { + "flow_impl": "Poskytovate\u013e" + }, + "description": "Vyberte si, prostredn\u00edctvom ktor\u00e9ho poskytovate\u013ea autentifik\u00e1cie sa chcete autentifikova\u0165 pomocou Logi Circle.", + "title": "Poskytovate\u013e overovania" + } } } } \ No newline at end of file diff --git a/homeassistant/components/london_air/sensor.py b/homeassistant/components/london_air/sensor.py index 33fe1d4d7fb..45f585da39d 100644 --- a/homeassistant/components/london_air/sensor.py +++ b/homeassistant/components/london_air/sensor.py @@ -51,10 +51,7 @@ AUTHORITIES = [ "Westminster", ] -URL = ( - "http://api.erg.kcl.ac.uk/AirQuality/Hourly/" - "MonitoringIndex/GroupName=London/Json" -) +URL = "http://api.erg.kcl.ac.uk/AirQuality/Hourly/MonitoringIndex/GroupName=London/Json" PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( { diff --git a/homeassistant/components/lookin/__init__.py b/homeassistant/components/lookin/__init__.py index 5ab4078eb20..a8f22fb17ca 100644 --- a/homeassistant/components/lookin/__init__.py +++ b/homeassistant/components/lookin/__init__.py @@ -101,18 +101,19 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: push_coordinator = LookinPushCoordinator(entry.title) - meteo_coordinator: LookinDataUpdateCoordinator = LookinDataUpdateCoordinator( - hass, - push_coordinator, - name=entry.title, - update_method=lookin_protocol.get_meteo_sensor, - update_interval=timedelta( - minutes=5 - ), # Updates are pushed (fallback is polling) - ) - await meteo_coordinator.async_config_entry_first_refresh() + if lookin_device.model >= 2: + meteo_coordinator = LookinDataUpdateCoordinator[MeteoSensor]( + hass, + push_coordinator, + name=entry.title, + update_method=lookin_protocol.get_meteo_sensor, + update_interval=timedelta( + minutes=5 + ), # Updates are pushed (fallback is polling) + ) + await meteo_coordinator.async_config_entry_first_refresh() - device_coordinators: dict[str, LookinDataUpdateCoordinator] = {} + device_coordinators: dict[str, LookinDataUpdateCoordinator[Remote]] = {} for remote in devices: if (platform := TYPE_TO_PLATFORM.get(remote["Type"])) is None: continue @@ -148,17 +149,18 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: lookin_udp_subs = await manager.async_get_subscriptions() - entry.async_on_unload( - lookin_udp_subs.subscribe_event( - lookin_device.id, UDPCommandType.meteo, None, _async_meteo_push_update + if lookin_device.model >= 2: + entry.async_on_unload( + lookin_udp_subs.subscribe_event( + lookin_device.id, UDPCommandType.meteo, None, _async_meteo_push_update + ) ) - ) hass.data[DOMAIN][entry.entry_id] = LookinData( host=host, lookin_udp_subs=lookin_udp_subs, lookin_device=lookin_device, - meteo_coordinator=meteo_coordinator, + meteo_coordinator=meteo_coordinator if lookin_device.model >= 2 else None, devices=devices, lookin_protocol=lookin_protocol, device_coordinators=device_coordinators, diff --git a/homeassistant/components/lookin/climate.py b/homeassistant/components/lookin/climate.py index aa3ba0c3614..f09bedab201 100644 --- a/homeassistant/components/lookin/climate.py +++ b/homeassistant/components/lookin/climate.py @@ -4,7 +4,7 @@ from __future__ import annotations import logging from typing import Any, Final, cast -from aiolookin import Climate, MeteoSensor +from aiolookin import Climate, MeteoSensor, Remote from aiolookin.models import UDPCommandType, UDPEvent from homeassistant.components.climate import ( @@ -23,8 +23,8 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( ATTR_TEMPERATURE, PRECISION_WHOLE, - TEMP_CELSIUS, Platform, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -75,7 +75,7 @@ async def async_setup_entry( continue uuid = remote["UUID"] coordinator = lookin_data.device_coordinators[uuid] - device: Climate = coordinator.data + device = cast(Climate, coordinator.data) entities.append( ConditionerEntity( uuid=uuid, @@ -92,7 +92,7 @@ class ConditionerEntity(LookinCoordinatorEntity, ClimateEntity): """An aircon or heat pump.""" _attr_current_humidity: float | None = None # type: ignore[assignment] - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS _attr_supported_features = ( ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.FAN_MODE @@ -110,7 +110,7 @@ class ConditionerEntity(LookinCoordinatorEntity, ClimateEntity): uuid: str, device: Climate, lookin_data: LookinData, - coordinator: LookinDataUpdateCoordinator, + coordinator: LookinDataUpdateCoordinator[Remote], ) -> None: """Init the ConditionerEntity.""" super().__init__(coordinator, uuid, device, lookin_data) @@ -146,13 +146,18 @@ class ConditionerEntity(LookinCoordinatorEntity, ClimateEntity): # or cool otherwise we set auto since we don't have a way to make # an educated guess. # - meteo_data: MeteoSensor = self._meteo_coordinator.data - if not (current_temp := meteo_data.temperature): - self._climate.hvac_mode = lookin_index.index(HVACMode.AUTO) - elif current_temp >= self._climate.temp_celsius: - self._climate.hvac_mode = lookin_index.index(HVACMode.COOL) + + if self._meteo_coordinator: + meteo_data: MeteoSensor = self._meteo_coordinator.data + if not (current_temp := meteo_data.temperature): + self._climate.hvac_mode = lookin_index.index(HVACMode.AUTO) + elif current_temp >= self._climate.temp_celsius: + self._climate.hvac_mode = lookin_index.index(HVACMode.COOL) + else: + self._climate.hvac_mode = lookin_index.index(HVACMode.HEAT) else: - self._climate.hvac_mode = lookin_index.index(HVACMode.HEAT) + self._climate.hvac_mode = lookin_index.index(HVACMode.AUTO) + await self._async_update_conditioner() async def async_set_fan_mode(self, fan_mode: str) -> None: @@ -172,14 +177,20 @@ class ConditionerEntity(LookinCoordinatorEntity, ClimateEntity): async def _async_update_conditioner(self) -> None: """Update the conditioner state from the climate data.""" self.coordinator.async_set_updated_data(self._climate) - await self._lookin_protocol.update_conditioner(climate=self._climate) + await self._lookin_protocol.update_conditioner( + uuid=self._attr_unique_id, status=self._climate.to_status + ) def _async_update_from_data(self) -> None: """Update attrs from data.""" - meteo_data: MeteoSensor = self._meteo_coordinator.data + if self._meteo_coordinator: + temperature = self._meteo_coordinator.data.temperature + humidity = int(self._meteo_coordinator.data.humidity) + else: + temperature = humidity = None - self._attr_current_temperature = meteo_data.temperature - self._attr_current_humidity = int(meteo_data.humidity) + self._attr_current_temperature = temperature + self._attr_current_humidity = humidity self._attr_target_temperature = self._climate.temp_celsius self._attr_fan_mode = LOOKIN_FAN_MODE_IDX_TO_HASS[self._climate.fan_mode] self._attr_swing_mode = LOOKIN_SWING_MODE_IDX_TO_HASS[self._climate.swing_mode] diff --git a/homeassistant/components/lookin/coordinator.py b/homeassistant/components/lookin/coordinator.py index 522132cdea6..94c4a70f3ca 100644 --- a/homeassistant/components/lookin/coordinator.py +++ b/homeassistant/components/lookin/coordinator.py @@ -5,12 +5,13 @@ from collections.abc import Awaitable, Callable from datetime import timedelta import logging import time -from typing import cast +from typing import TypeVar from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.update_coordinator import DataUpdateCoordinator _LOGGER = logging.getLogger(__name__) +_DataT = TypeVar("_DataT") NEVER_TIME = -120.0 # Time that will never match time.monotonic() ACTIVE_UPDATES_INTERVAL = 3 # Consider active for 3x the update interval @@ -43,7 +44,7 @@ class LookinPushCoordinator: return is_active -class LookinDataUpdateCoordinator(DataUpdateCoordinator): +class LookinDataUpdateCoordinator(DataUpdateCoordinator[_DataT]): """DataUpdateCoordinator to gather data for a specific lookin devices.""" def __init__( @@ -52,7 +53,7 @@ class LookinDataUpdateCoordinator(DataUpdateCoordinator): push_coordinator: LookinPushCoordinator, name: str, update_interval: timedelta | None = None, - update_method: Callable[[], Awaitable[dict]] | None = None, + update_method: Callable[[], Awaitable[_DataT]] | None = None, ) -> None: """Initialize DataUpdateCoordinator to gather data for specific device.""" self.push_coordinator = push_coordinator @@ -65,12 +66,12 @@ class LookinDataUpdateCoordinator(DataUpdateCoordinator): ) @callback - def async_set_updated_data(self, data: dict) -> None: + def async_set_updated_data(self, data: _DataT) -> None: """Manually update data, notify listeners and reset refresh interval, and remember.""" self.push_coordinator.update() super().async_set_updated_data(data) - async def _async_update_data(self) -> dict: + async def _async_update_data(self) -> _DataT: """Fetch data only if we have not received a push inside the interval.""" interval = self.update_interval if ( @@ -82,4 +83,4 @@ class LookinDataUpdateCoordinator(DataUpdateCoordinator): data = self.data else: data = await super()._async_update_data() - return cast(dict, data) + return data diff --git a/homeassistant/components/lookin/entity.py b/homeassistant/components/lookin/entity.py index 1c641b76f32..35de968cf2f 100644 --- a/homeassistant/components/lookin/entity.py +++ b/homeassistant/components/lookin/entity.py @@ -3,9 +3,15 @@ from __future__ import annotations from abc import abstractmethod import logging -from typing import cast -from aiolookin import POWER_CMD, POWER_OFF_CMD, POWER_ON_CMD, Climate, Remote +from aiolookin import ( + POWER_CMD, + POWER_OFF_CMD, + POWER_ON_CMD, + Climate, + MeteoSensor, + Remote, +) from aiolookin.models import Device, UDPCommandType, UDPEvent from homeassistant.helpers.entity import DeviceInfo @@ -53,7 +59,7 @@ class LookinDeviceMixIn: class LookinDeviceCoordinatorEntity( - LookinDeviceMixIn, CoordinatorEntity[LookinDataUpdateCoordinator] + LookinDeviceMixIn, CoordinatorEntity[LookinDataUpdateCoordinator[MeteoSensor]] ): """A lookin device entity on the device itself that uses the coordinator.""" @@ -61,6 +67,7 @@ class LookinDeviceCoordinatorEntity( def __init__(self, lookin_data: LookinData) -> None: """Init the lookin device entity.""" + assert lookin_data.meteo_coordinator is not None super().__init__(lookin_data.meteo_coordinator) self._set_lookin_device_attrs(lookin_data) self._attr_device_info = _lookin_device_to_device_info( @@ -85,7 +92,9 @@ class LookinEntityMixIn: class LookinCoordinatorEntity( - LookinDeviceMixIn, LookinEntityMixIn, CoordinatorEntity[LookinDataUpdateCoordinator] + LookinDeviceMixIn, + LookinEntityMixIn, + CoordinatorEntity[LookinDataUpdateCoordinator[Remote]], ): """A lookin device entity for an external device that uses the coordinator.""" @@ -94,7 +103,7 @@ class LookinCoordinatorEntity( def __init__( self, - coordinator: LookinDataUpdateCoordinator, + coordinator: LookinDataUpdateCoordinator[Remote], uuid: str, device: Remote | Climate, lookin_data: LookinData, @@ -109,10 +118,10 @@ class LookinCoordinatorEntity( self._attr_unique_id = uuid self._attr_name = device.name - async def _async_send_command(self, command: str) -> None: + async def _async_send_command(self, command: str, signal: str = "FF") -> None: """Send command from saved IR device.""" await self._lookin_protocol.send_command( - uuid=self._uuid, command=command, signal="FF" + uuid=self._uuid, command=command, signal=signal ) @@ -121,7 +130,7 @@ class LookinPowerEntity(LookinCoordinatorEntity): def __init__( self, - coordinator: LookinDataUpdateCoordinator, + coordinator: LookinDataUpdateCoordinator[Remote], uuid: str, device: Remote | Climate, lookin_data: LookinData, @@ -141,7 +150,7 @@ class LookinPowerPushRemoteEntity(LookinPowerEntity): def __init__( self, - coordinator: LookinDataUpdateCoordinator, + coordinator: LookinDataUpdateCoordinator[Remote], uuid: str, device: Remote, lookin_data: LookinData, @@ -153,7 +162,7 @@ class LookinPowerPushRemoteEntity(LookinPowerEntity): @property def _remote(self) -> Remote: - return cast(Remote, self.coordinator.data) + return self.coordinator.data @abstractmethod def _update_from_status(self, status: str) -> None: diff --git a/homeassistant/components/lookin/light.py b/homeassistant/components/lookin/light.py index d7ef4e62f2a..c4b263cbad1 100644 --- a/homeassistant/components/lookin/light.py +++ b/homeassistant/components/lookin/light.py @@ -4,8 +4,6 @@ from __future__ import annotations import logging from typing import Any -from aiolookin import Remote - from homeassistant.components.light import ColorMode, LightEntity from homeassistant.config_entries import ConfigEntry from homeassistant.const import Platform @@ -33,7 +31,7 @@ async def async_setup_entry( continue uuid = remote["UUID"] coordinator = lookin_data.device_coordinators[uuid] - device: Remote = coordinator.data + device = coordinator.data entities.append( LookinLightEntity( coordinator=coordinator, diff --git a/homeassistant/components/lookin/manifest.json b/homeassistant/components/lookin/manifest.json index b58eb254f8f..d6e1cd13686 100644 --- a/homeassistant/components/lookin/manifest.json +++ b/homeassistant/components/lookin/manifest.json @@ -3,7 +3,7 @@ "name": "LOOKin", "documentation": "https://www.home-assistant.io/integrations/lookin/", "codeowners": ["@ANMalko", "@bdraco"], - "requirements": ["aiolookin==0.1.1"], + "requirements": ["aiolookin==1.0.0"], "zeroconf": ["_lookin._tcp.local."], "config_flow": true, "iot_class": "local_push", diff --git a/homeassistant/components/lookin/media_player.py b/homeassistant/components/lookin/media_player.py index 9e925836e11..f7ae457cbff 100644 --- a/homeassistant/components/lookin/media_player.py +++ b/homeassistant/components/lookin/media_player.py @@ -36,6 +36,7 @@ _FUNCTION_NAME_TO_FEATURE = { "volup": MediaPlayerEntityFeature.VOLUME_STEP, "chup": MediaPlayerEntityFeature.NEXT_TRACK, "chdown": MediaPlayerEntityFeature.PREVIOUS_TRACK, + "mode": MediaPlayerEntityFeature.SELECT_SOURCE, } @@ -78,7 +79,7 @@ class LookinMedia(LookinPowerPushRemoteEntity, MediaPlayerEntity): uuid: str, device: Remote, lookin_data: LookinData, - device_class: str, + device_class: MediaPlayerDeviceClass, ) -> None: """Init the lookin media player.""" self._attr_device_class = device_class @@ -86,6 +87,29 @@ class LookinMedia(LookinPowerPushRemoteEntity, MediaPlayerEntity): for function_name, feature in _FUNCTION_NAME_TO_FEATURE.items(): if function_name in self._function_names: self._attr_supported_features |= feature + self._source_list: dict[str, str] | None = None + + @property + def source_list(self) -> list[str]: + """List of available input sources.""" + return list(self._source_list.keys()) if self._source_list else [] + + async def async_select_source(self, source: str) -> None: + """Choose an available playlist and play it.""" + if not self._source_list: + return + await self._async_send_command(command="mode", signal=self._source_list[source]) + + async def async_added_to_hass(self) -> None: + """Get list of available input sources.""" + if self._source_list is None and "mode" in self._function_names: + if sources := await self._lookin_protocol.get_media_sources( + uuid=self._uuid + ): + self._source_list = { + f"INPUT_{index}": f"{index:02x}" for index in range(len(sources)) + } + await super().async_added_to_hass() async def async_volume_up(self) -> None: """Turn volume up for media player.""" diff --git a/homeassistant/components/lookin/models.py b/homeassistant/components/lookin/models.py index f0dffe66ec0..2de3a7ee761 100644 --- a/homeassistant/components/lookin/models.py +++ b/homeassistant/components/lookin/models.py @@ -4,7 +4,13 @@ from __future__ import annotations from dataclasses import dataclass from typing import Any -from aiolookin import Device, LookInHttpProtocol, LookinUDPSubscriptions +from aiolookin import ( + Device, + LookInHttpProtocol, + LookinUDPSubscriptions, + MeteoSensor, + Remote, +) from .coordinator import LookinDataUpdateCoordinator @@ -16,7 +22,7 @@ class LookinData: host: str lookin_udp_subs: LookinUDPSubscriptions lookin_device: Device - meteo_coordinator: LookinDataUpdateCoordinator + meteo_coordinator: LookinDataUpdateCoordinator[MeteoSensor] | None devices: list[dict[str, Any]] lookin_protocol: LookInHttpProtocol - device_coordinators: dict[str, LookinDataUpdateCoordinator] + device_coordinators: dict[str, LookinDataUpdateCoordinator[Remote]] diff --git a/homeassistant/components/lookin/sensor.py b/homeassistant/components/lookin/sensor.py index 6dee2f2c0ac..822c28fda30 100644 --- a/homeassistant/components/lookin/sensor.py +++ b/homeassistant/components/lookin/sensor.py @@ -10,7 +10,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import PERCENTAGE, TEMP_CELSIUS +from homeassistant.const import PERCENTAGE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -25,7 +25,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="temperature", name="Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), diff --git a/homeassistant/components/lookin/strings.json b/homeassistant/components/lookin/strings.json index 641e1f45de7..e8ec44addc2 100644 --- a/homeassistant/components/lookin/strings.json +++ b/homeassistant/components/lookin/strings.json @@ -13,7 +13,7 @@ } }, "discovery_confirm": { - "description": "Do you want to setup {name} ({host})?" + "description": "Do you want to set up {name} ({host})?" } }, "error": { diff --git a/homeassistant/components/lookin/translations/en.json b/homeassistant/components/lookin/translations/en.json index ddb8c310408..00c124a5bd9 100644 --- a/homeassistant/components/lookin/translations/en.json +++ b/homeassistant/components/lookin/translations/en.json @@ -19,7 +19,7 @@ } }, "discovery_confirm": { - "description": "Do you want to setup {name} ({host})?" + "description": "Do you want to set up {name} ({host})?" }, "user": { "data": { diff --git a/homeassistant/components/lookin/translations/ko.json b/homeassistant/components/lookin/translations/ko.json new file mode 100644 index 00000000000..e5a5042af90 --- /dev/null +++ b/homeassistant/components/lookin/translations/ko.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "device_name": { + "data": { + "name": "\uc774\ub984" + } + }, + "user": { + "data": { + "ip_address": "IP \uc8fc\uc18c" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/lookin/translations/no.json b/homeassistant/components/lookin/translations/no.json index 0f3aa19bd41..25534d627cd 100644 --- a/homeassistant/components/lookin/translations/no.json +++ b/homeassistant/components/lookin/translations/no.json @@ -19,7 +19,7 @@ } }, "discovery_confirm": { - "description": "Vil du konfigurere {name} ({host})?" + "description": "Vil du sette opp {name} ( {host} )?" }, "user": { "data": { diff --git a/homeassistant/components/lovelace/__init__.py b/homeassistant/components/lovelace/__init__.py index c2e709aeb40..485579087a1 100644 --- a/homeassistant/components/lovelace/__init__.py +++ b/homeassistant/components/lovelace/__init__.py @@ -113,7 +113,8 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: if yaml_resources is not None: _LOGGER.warning( - "Lovelace is running in storage mode. Define resources via user interface" + "Lovelace is running in storage mode. Define resources via user" + " interface" ) resource_collection = resources.ResourceStorageCollection(hass, default_config) @@ -220,7 +221,8 @@ async def create_yaml_resource_col(hass, yaml_resources): else: if CONF_RESOURCES in ll_conf: _LOGGER.warning( - "Resources need to be specified in your configuration.yaml. Please see the docs" + "Resources need to be specified in your configuration.yaml. Please" + " see the docs" ) yaml_resources = ll_conf[CONF_RESOURCES] diff --git a/homeassistant/components/lovelace/translations/sk.json b/homeassistant/components/lovelace/translations/sk.json new file mode 100644 index 00000000000..858894ba531 --- /dev/null +++ b/homeassistant/components/lovelace/translations/sk.json @@ -0,0 +1,10 @@ +{ + "system_health": { + "info": { + "dashboards": "Ovl\u00e1dacie panely", + "mode": "Re\u017eim", + "resources": "Zdroje", + "views": "Zobrazenia" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/luftdaten/sensor.py b/homeassistant/components/luftdaten/sensor.py index 5333d86a708..67672759706 100644 --- a/homeassistant/components/luftdaten/sensor.py +++ b/homeassistant/components/luftdaten/sensor.py @@ -16,8 +16,8 @@ from homeassistant.const import ( CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, CONF_SHOW_ON_MAP, PERCENTAGE, - PRESSURE_PA, - TEMP_CELSIUS, + UnitOfPressure, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo @@ -33,7 +33,7 @@ SENSORS: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="temperature", name="Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), @@ -47,14 +47,14 @@ SENSORS: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="pressure", name="Pressure", - native_unit_of_measurement=PRESSURE_PA, + native_unit_of_measurement=UnitOfPressure.PA, device_class=SensorDeviceClass.PRESSURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="pressure_at_sealevel", name="Pressure at sealevel", - native_unit_of_measurement=PRESSURE_PA, + native_unit_of_measurement=UnitOfPressure.PA, device_class=SensorDeviceClass.PRESSURE, state_class=SensorStateClass.MEASUREMENT, ), @@ -116,7 +116,9 @@ class SensorCommunitySensor(CoordinatorEntity, SensorEntity): ATTR_SENSOR_ID: sensor_id, } self._attr_device_info = DeviceInfo( - configuration_url=f"https://devices.sensor.community/sensors/{sensor_id}/settings", + configuration_url=( + f"https://devices.sensor.community/sensors/{sensor_id}/settings" + ), identifiers={(DOMAIN, str(sensor_id))}, name=f"Sensor {sensor_id}", manufacturer="Sensor.Community", diff --git a/homeassistant/components/luftdaten/translations/pt.json b/homeassistant/components/luftdaten/translations/pt.json index 12e4c078d8c..1ea7629b209 100644 --- a/homeassistant/components/luftdaten/translations/pt.json +++ b/homeassistant/components/luftdaten/translations/pt.json @@ -2,7 +2,7 @@ "config": { "error": { "already_configured": "O servi\u00e7o j\u00e1 est\u00e1 configurado", - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_sensor": "Sensor n\u00e3o dispon\u00edvel ou inv\u00e1lido" }, "step": { diff --git a/homeassistant/components/luftdaten/translations/zh-Hant.json b/homeassistant/components/luftdaten/translations/zh-Hant.json index 7fc2a8725e8..a1a896b3253 100644 --- a/homeassistant/components/luftdaten/translations/zh-Hant.json +++ b/homeassistant/components/luftdaten/translations/zh-Hant.json @@ -3,13 +3,13 @@ "error": { "already_configured": "\u670d\u52d9\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", "cannot_connect": "\u9023\u7dda\u5931\u6557", - "invalid_sensor": "\u7121\u6cd5\u4f7f\u7528\u6216\u7121\u6548\u7684\u611f\u61c9\u5668" + "invalid_sensor": "\u7121\u6cd5\u4f7f\u7528\u6216\u7121\u6548\u7684\u611f\u6e2c\u5668" }, "step": { "user": { "data": { "show_on_map": "\u65bc\u5730\u5716\u986f\u793a", - "station_id": "\u611f\u61c9\u5668 ID" + "station_id": "\u611f\u6e2c\u5668 ID" } } } diff --git a/homeassistant/components/lupusec/manifest.json b/homeassistant/components/lupusec/manifest.json index 74593977eab..76d8ec2a53d 100644 --- a/homeassistant/components/lupusec/manifest.json +++ b/homeassistant/components/lupusec/manifest.json @@ -2,7 +2,7 @@ "domain": "lupusec", "name": "Lupus Electronics LUPUSEC", "documentation": "https://www.home-assistant.io/integrations/lupusec", - "requirements": ["lupupy==0.2.3"], + "requirements": ["lupupy==0.2.4"], "codeowners": ["@majuss"], "iot_class": "local_polling", "loggers": ["lupupy"] diff --git a/homeassistant/components/lutron_caseta/__init__.py b/homeassistant/components/lutron_caseta/__init__.py index 5ee64a687bf..b9418373b38 100644 --- a/homeassistant/components/lutron_caseta/__init__.py +++ b/homeassistant/components/lutron_caseta/__init__.py @@ -499,7 +499,7 @@ def _async_subscribe_keypad_events( async def async_unload_entry( hass: HomeAssistant, entry: config_entries.ConfigEntry ) -> bool: - """Unload the bridge bridge from a config entry.""" + """Unload the bridge from a config entry.""" data: LutronCasetaData = hass.data[DOMAIN][entry.entry_id] await data.bridge.close() if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): diff --git a/homeassistant/components/lutron_caseta/logbook.py b/homeassistant/components/lutron_caseta/logbook.py index ccefaff2a78..18c46405ed0 100644 --- a/homeassistant/components/lutron_caseta/logbook.py +++ b/homeassistant/components/lutron_caseta/logbook.py @@ -51,7 +51,9 @@ def async_describe_events( if rev_button_map is None: return { LOGBOOK_ENTRY_NAME: f"{data[ATTR_AREA_NAME]} {data[ATTR_DEVICE_NAME]}", - LOGBOOK_ENTRY_MESSAGE: f"{data[ATTR_ACTION]} Error retrieving button description", + LOGBOOK_ENTRY_MESSAGE: ( + f"{data[ATTR_ACTION]} Error retrieving button description" + ), } button_description = rev_button_map.get(leap_button_number) diff --git a/homeassistant/components/lutron_caseta/strings.json b/homeassistant/components/lutron_caseta/strings.json index 0c6ec06005c..bc546321da3 100644 --- a/homeassistant/components/lutron_caseta/strings.json +++ b/homeassistant/components/lutron_caseta/strings.json @@ -4,7 +4,7 @@ "step": { "import_failed": { "title": "Failed to import Caséta bridge configuration.", - "description": "Couldn’t setup bridge (host: {host}) imported from configuration.yaml." + "description": "Couldn’t set up bridge (host: {host}) imported from configuration.yaml." }, "user": { "title": "Automatically connect to the bridge", diff --git a/homeassistant/components/lutron_caseta/translations/en.json b/homeassistant/components/lutron_caseta/translations/en.json index b0ddf459194..ddb0271cd47 100644 --- a/homeassistant/components/lutron_caseta/translations/en.json +++ b/homeassistant/components/lutron_caseta/translations/en.json @@ -11,7 +11,7 @@ "flow_title": "{name} ({host})", "step": { "import_failed": { - "description": "Couldn\u2019t setup bridge (host: {host}) imported from configuration.yaml.", + "description": "Couldn\u2019t set up bridge (host: {host}) imported from configuration.yaml.", "title": "Failed to import Cas\u00e9ta bridge configuration." }, "link": { diff --git a/homeassistant/components/lutron_caseta/translations/it.json b/homeassistant/components/lutron_caseta/translations/it.json index c013d08b575..893eed3c6e9 100644 --- a/homeassistant/components/lutron_caseta/translations/it.json +++ b/homeassistant/components/lutron_caseta/translations/it.json @@ -11,7 +11,7 @@ "flow_title": "{name} ({host})", "step": { "import_failed": { - "description": "Impossibile impostare il bridge (host: {host}) importato da configuration.yaml.", + "description": "Impossibile configurare il bridge (host: {host}) importato da configuration.yaml.", "title": "Impossibile importare la configurazione del bridge Cas\u00e9ta." }, "link": { diff --git a/homeassistant/components/lutron_caseta/translations/no.json b/homeassistant/components/lutron_caseta/translations/no.json index e5f8e72330d..b4834683a35 100644 --- a/homeassistant/components/lutron_caseta/translations/no.json +++ b/homeassistant/components/lutron_caseta/translations/no.json @@ -11,7 +11,7 @@ "flow_title": "{name} ({host})", "step": { "import_failed": { - "description": "Kunne ikke konfigurere bridge (host: {host} ) importert fra configuration.yaml.", + "description": "Kunne ikke konfigurere broen (vert: {host} ) importert fra configuration.yaml.", "title": "Kan ikke importere Cas\u00e9ta bridge-konfigurasjon." }, "link": { diff --git a/homeassistant/components/lutron_caseta/translations/pt.json b/homeassistant/components/lutron_caseta/translations/pt.json index 4ae57417d6f..5d50b715c47 100644 --- a/homeassistant/components/lutron_caseta/translations/pt.json +++ b/homeassistant/components/lutron_caseta/translations/pt.json @@ -2,15 +2,15 @@ "config": { "abort": { "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "user": { "data": { - "host": "Servidor" + "host": "Endere\u00e7o" } } } diff --git a/homeassistant/components/lutron_caseta/translations/sk.json b/homeassistant/components/lutron_caseta/translations/sk.json index 11557dadc82..eac14c88454 100644 --- a/homeassistant/components/lutron_caseta/translations/sk.json +++ b/homeassistant/components/lutron_caseta/translations/sk.json @@ -10,6 +10,14 @@ }, "flow_title": "{name} ({host})", "step": { + "import_failed": { + "description": "Nepodarilo sa nastavi\u0165 most (hostite\u013e: {host}) importovan\u00fd z konfigura\u010dn\u00e9ho s\u00faboru.yaml.", + "title": "Import konfigur\u00e1cie bridge Cas\u00e9ta zlyhal." + }, + "link": { + "description": "Ak chcete sp\u00e1rova\u0165 s {name} ({host}), po odoslan\u00ed tohto formul\u00e1ra stla\u010dte \u010dierne tla\u010didlo na zadnej strane bridge.", + "title": "Sp\u00e1rujte s bridge" + }, "user": { "data": { "host": "Hostite\u013e" @@ -28,9 +36,40 @@ "button_5": "Piate tla\u010didlo", "button_6": "\u0160ieste tla\u010didlo", "button_7": "Siedme tla\u010didlo", + "close_1": "Zavrie\u0165 1", + "close_2": "Zavrie\u0165 2", + "close_3": "Zavrie\u0165 3", + "close_4": "Zavrie\u0165 4", + "close_all": "Zavrie\u0165 v\u0161etko", + "group_1_button_1": "Prv\u00e1 skupina prv\u00e9 tla\u010didlo", "group_1_button_2": "Prv\u00e1 skupina druh\u00e9 tla\u010didlo", "group_2_button_1": "Druh\u00e1 skupina prv\u00e9 tla\u010didlo", - "group_2_button_2": "Druh\u00e1 skupina druh\u00e9 tla\u010didlo" + "group_2_button_2": "Druh\u00e1 skupina druh\u00e9 tla\u010didlo", + "lower": "Zn\u00ed\u017ei\u0165", + "lower_1": "Zn\u00ed\u017ei\u0165 1", + "lower_2": "Zn\u00ed\u017ei\u0165 2", + "lower_3": "Zn\u00ed\u017ei\u0165 3", + "lower_4": "Zn\u00ed\u017ei\u0165 4", + "lower_all": "Zn\u00ed\u017ete v\u0161etko", + "off": "Vypnut\u00e9", + "on": "Zapnut\u00fd", + "open_1": "Otvori\u0165 1", + "open_2": "Otvori\u0165 2", + "open_3": "Otvori\u0165 3", + "open_4": "Otvori\u0165 4", + "open_all": "Otvori\u0165 v\u0161etky", + "raise": "Zv\u00fd\u0161i\u0165", + "raise_1": "Zv\u00fd\u0161i\u0165 1", + "raise_2": "Zv\u00fd\u0161i\u0165 2", + "raise_3": "Zv\u00fd\u0161i\u0165 3", + "raise_4": "Zv\u00fd\u0161i\u0165 4", + "raise_all": "Zv\u00fd\u0161i\u0165 v\u0161etko", + "stop": "Stop (ob\u013e\u00faben\u00e9)", + "stop_1": "Stop 1", + "stop_2": "Stop 2", + "stop_3": "Stop 3", + "stop_4": "Stop 4", + "stop_all": "Stop v\u0161etko" }, "trigger_type": { "press": "\"{subtype}\" stla\u010den\u00e9", diff --git a/homeassistant/components/lyric/__init__.py b/homeassistant/components/lyric/__init__.py index 8a93591b037..228cf4c1d5d 100644 --- a/homeassistant/components/lyric/__init__.py +++ b/homeassistant/components/lyric/__init__.py @@ -105,7 +105,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: except (LyricException, ClientResponseError) as exception: raise UpdateFailed(exception) from exception - coordinator = DataUpdateCoordinator( + coordinator = DataUpdateCoordinator[Lyric]( hass, _LOGGER, # Name of the data. For logging purposes. @@ -133,12 +133,12 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return unload_ok -class LyricEntity(CoordinatorEntity): +class LyricEntity(CoordinatorEntity[DataUpdateCoordinator[Lyric]]): """Defines a base Honeywell Lyric entity.""" def __init__( self, - coordinator: DataUpdateCoordinator, + coordinator: DataUpdateCoordinator[Lyric], location: LyricLocation, device: LyricDevice, key: str, diff --git a/homeassistant/components/lyric/climate.py b/homeassistant/components/lyric/climate.py index 8339c4dad45..4de413c45dc 100644 --- a/homeassistant/components/lyric/climate.py +++ b/homeassistant/components/lyric/climate.py @@ -6,6 +6,7 @@ import logging from time import localtime, strftime, time from typing import Any +from aiolyric import Lyric from aiolyric.objects.device import LyricDevice from aiolyric.objects.location import LyricLocation import voluptuous as vol @@ -97,7 +98,7 @@ async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback ) -> None: """Set up the Honeywell Lyric climate platform based on a config entry.""" - coordinator: DataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] + coordinator: DataUpdateCoordinator[Lyric] = hass.data[DOMAIN][entry.entry_id] entities = [] @@ -130,12 +131,12 @@ async def async_setup_entry( class LyricClimate(LyricDeviceEntity, ClimateEntity): """Defines a Honeywell Lyric climate entity.""" - coordinator: DataUpdateCoordinator + coordinator: DataUpdateCoordinator[Lyric] entity_description: ClimateEntityDescription def __init__( self, - coordinator: DataUpdateCoordinator, + coordinator: DataUpdateCoordinator[Lyric], description: ClimateEntityDescription, location: LyricLocation, device: LyricDevice, @@ -277,7 +278,8 @@ class LyricClimate(LyricDeviceEntity, ClimateEntity): if device.changeableValues.autoChangeoverActive: if target_temp_low is None or target_temp_high is None: raise HomeAssistantError( - "Could not find target_temp_low and/or target_temp_high in arguments" + "Could not find target_temp_low and/or target_temp_high in" + " arguments" ) _LOGGER.debug("Set temperature: %s - %s", target_temp_low, target_temp_high) try: diff --git a/homeassistant/components/lyric/sensor.py b/homeassistant/components/lyric/sensor.py index 528161f3d6e..1201a675a5d 100644 --- a/homeassistant/components/lyric/sensor.py +++ b/homeassistant/components/lyric/sensor.py @@ -6,6 +6,7 @@ from dataclasses import dataclass from datetime import datetime, timedelta from typing import cast +from aiolyric import Lyric from aiolyric.objects.device import LyricDevice from aiolyric.objects.location import LyricLocation @@ -63,7 +64,7 @@ async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback ) -> None: """Set up the Honeywell Lyric sensor platform based on a config entry.""" - coordinator: DataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] + coordinator: DataUpdateCoordinator[Lyric] = hass.data[DOMAIN][entry.entry_id] entities = [] @@ -179,12 +180,12 @@ async def async_setup_entry( class LyricSensor(LyricDeviceEntity, SensorEntity): """Define a Honeywell Lyric sensor.""" - coordinator: DataUpdateCoordinator + coordinator: DataUpdateCoordinator[Lyric] entity_description: LyricSensorEntityDescription def __init__( self, - coordinator: DataUpdateCoordinator, + coordinator: DataUpdateCoordinator[Lyric], description: LyricSensorEntityDescription, location: LyricLocation, device: LyricDevice, diff --git a/homeassistant/components/lyric/translations/de.json b/homeassistant/components/lyric/translations/de.json index 1ef7e65fbf4..b5b84a07080 100644 --- a/homeassistant/components/lyric/translations/de.json +++ b/homeassistant/components/lyric/translations/de.json @@ -13,7 +13,7 @@ "title": "W\u00e4hle die Authentifizierungsmethode" }, "reauth_confirm": { - "description": "Die Lyric-Integration muss dein Konto neu authentifizieren.", + "description": "Die Lyric Integration muss dein Konto neu authentifizieren.", "title": "Integration erneut authentifizieren" } } diff --git a/homeassistant/components/lyric/translations/en_GB.json b/homeassistant/components/lyric/translations/en-GB.json similarity index 100% rename from homeassistant/components/lyric/translations/en_GB.json rename to homeassistant/components/lyric/translations/en-GB.json diff --git a/homeassistant/components/lyric/translations/pt.json b/homeassistant/components/lyric/translations/pt.json index 002029ae6f7..63db8981bda 100644 --- a/homeassistant/components/lyric/translations/pt.json +++ b/homeassistant/components/lyric/translations/pt.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "authorize_url_timeout": "Tempo excedido a gerar um URL de autoriza\u00e7\u00e3o", + "authorize_url_timeout": "Excedido o tempo limite para gerar um URL de autoriza\u00e7\u00e3o", "missing_configuration": "O componente n\u00e3o est\u00e1 configurado. Por favor, siga a documenta\u00e7\u00e3o." }, "step": { diff --git a/homeassistant/components/lyric/translations/sk.json b/homeassistant/components/lyric/translations/sk.json index 6a514a50f33..a7c314c815b 100644 --- a/homeassistant/components/lyric/translations/sk.json +++ b/homeassistant/components/lyric/translations/sk.json @@ -13,8 +13,15 @@ "title": "Vyberte met\u00f3du overenia" }, "reauth_confirm": { + "description": "Integr\u00e1cia Lyric mus\u00ed znova overi\u0165 v\u00e1\u0161 \u00fa\u010det.", "title": "Znova overi\u0165 integr\u00e1ciu" } } + }, + "issues": { + "removed_yaml": { + "description": "Konfigur\u00e1cia Honeywell Lyric pomocou YAML bola odstr\u00e1nen\u00e1. \n\n Asistent dom\u00e1cnosti nepou\u017e\u00edva va\u0161u existuj\u00facu konfigur\u00e1ciu YAML. \n\n Ak chcete tento probl\u00e9m vyrie\u0161i\u0165, odstr\u00e1\u0148te konfigur\u00e1ciu YAML zo s\u00faboru configuration.yaml a re\u0161tartujte aplik\u00e1ciu Home Assistant.", + "title": "Konfigur\u00e1cia Honeywell Lyric YAML bola odstr\u00e1nen\u00e1" + } } } \ No newline at end of file diff --git a/homeassistant/components/magicseaweed/sensor.py b/homeassistant/components/magicseaweed/sensor.py index 79b77a362c4..0fb2df6aaca 100644 --- a/homeassistant/components/magicseaweed/sensor.py +++ b/homeassistant/components/magicseaweed/sensor.py @@ -168,12 +168,18 @@ class MagicSeaweedSensor(SensorEntity): elif sensor_type == "max_breaking_swell": self._attr_native_value = forecast.swell_maxBreakingHeight elif sensor_type == "swell_forecast": - summary = f"{forecast.swell_minBreakingHeight} - {forecast.swell_maxBreakingHeight}" + summary = ( + f"{forecast.swell_minBreakingHeight} -" + f" {forecast.swell_maxBreakingHeight}" + ) self._attr_native_value = summary if self.hour is None: for hour, data in self.data.hourly.items(): occurs = hour - hr_summary = f"{data.swell_minBreakingHeight} - {data.swell_maxBreakingHeight} {data.swell_unit}" + hr_summary = ( + f"{data.swell_minBreakingHeight} -" + f" {data.swell_maxBreakingHeight} {data.swell_unit}" + ) self._attr_extra_state_attributes[occurs] = hr_summary if sensor_type != "swell_forecast": diff --git a/homeassistant/components/mailgun/config_flow.py b/homeassistant/components/mailgun/config_flow.py index 6fe87e7cbf4..bfeaed5ae5b 100644 --- a/homeassistant/components/mailgun/config_flow.py +++ b/homeassistant/components/mailgun/config_flow.py @@ -7,7 +7,9 @@ config_entry_flow.register_webhook_flow( DOMAIN, "Mailgun Webhook", { - "mailgun_url": "https://documentation.mailgun.com/en/latest/user_manual.html#webhooks", + "mailgun_url": ( + "https://documentation.mailgun.com/en/latest/user_manual.html#webhooks" + ), "docs_url": "https://www.home-assistant.io/integrations/mailgun/", }, ) diff --git a/homeassistant/components/mailgun/strings.json b/homeassistant/components/mailgun/strings.json index 03446b50adf..0c44dc63aae 100644 --- a/homeassistant/components/mailgun/strings.json +++ b/homeassistant/components/mailgun/strings.json @@ -12,7 +12,7 @@ "webhook_not_internet_accessible": "[%key:common::config_flow::abort::webhook_not_internet_accessible%]" }, "create_entry": { - "default": "To send events to Home Assistant, you will need to setup [Webhooks with Mailgun]({mailgun_url}).\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n- Content Type: application/json\n\nSee [the documentation]({docs_url}) on how to configure automations to handle incoming data." + "default": "To send events to Home Assistant, you will need to set up [Webhooks with Mailgun]({mailgun_url}).\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n- Content Type: application/json\n\nSee [the documentation]({docs_url}) on how to configure automations to handle incoming data." } } } diff --git a/homeassistant/components/mailgun/translations/ca.json b/homeassistant/components/mailgun/translations/ca.json index 5959fce55c2..7dc93e6e158 100644 --- a/homeassistant/components/mailgun/translations/ca.json +++ b/homeassistant/components/mailgun/translations/ca.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "La teva inst\u00e0ncia de Home Assistant ha de ser accessible des d'Internet per poder rebre missatges webhook." }, "create_entry": { - "default": "Per enviar esdeveniments a Home Assistant, haur\u00e0s de configurar [Webhooks amb Mailgun]({mailgun_url}). \n\nCompleta la seg\u00fcent informaci\u00f3: \n\n- URL: `{webhook_url}` \n- M\u00e8tode: POST \n- Tipus de contingut: application/json\n\nConsulta la [documentaci\u00f3]({docs_url}) sobre com configurar les automatitzacions per gestionar dades entrants." + "default": "Per enviar esdeveniments a Home Assistant, has de configurar [Webhooks amb Mailgun]({mailgun_url}). \n\nCompleta la seg\u00fcent informaci\u00f3: \n\n- URL: `{webhook_url}` \n- M\u00e8tode: POST \n- Tipus de contingut: application/json\n\nConsulta la [documentaci\u00f3]({docs_url}) sobre com configurar les automatitzacions per gestionar dades entrants." }, "step": { "user": { diff --git a/homeassistant/components/mailgun/translations/de.json b/homeassistant/components/mailgun/translations/de.json index 34c251770cc..d2cfb44566d 100644 --- a/homeassistant/components/mailgun/translations/de.json +++ b/homeassistant/components/mailgun/translations/de.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "Deine Home Assistant-Instanz muss \u00fcber das Internet erreichbar sein, um Webhook-Nachrichten empfangen zu k\u00f6nnen." }, "create_entry": { - "default": "Um Ereignisse an Home Assistant zu senden, musst du [Webhooks mit Mailgun]({mailgun_url}) einrichten. \n\n F\u00fclle die folgenden Informationen aus: \n\n - URL: `{webhook_url}` \n - Methode: POST \n - Inhaltstyp: application/json \n\nLies in der [Dokumentation]({docs_url}), wie du Automationen f\u00fcr die Verarbeitung eingehender Daten konfigurierst." + "default": "Um Ereignisse an Home Assistant zu senden, muss [Webhooks mit Mailgun]({mailgun_url}) eingerichtet werden. \n\nF\u00fcge die folgenden Informationen ein: \n\n- URL: `{webhook_url}` \n- Methode: POST \n- Inhaltstyp: application/json \n\nLies in der [Dokumentation]({docs_url}), wie du Automationen f\u00fcr die Verarbeitung eingehender Daten konfigurierst." }, "step": { "user": { diff --git a/homeassistant/components/mailgun/translations/en.json b/homeassistant/components/mailgun/translations/en.json index 928ab40e1af..f9d89d43dfd 100644 --- a/homeassistant/components/mailgun/translations/en.json +++ b/homeassistant/components/mailgun/translations/en.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "Your Home Assistant instance needs to be accessible from the internet to receive webhook messages." }, "create_entry": { - "default": "To send events to Home Assistant, you will need to setup [Webhooks with Mailgun]({mailgun_url}).\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n- Content Type: application/json\n\nSee [the documentation]({docs_url}) on how to configure automations to handle incoming data." + "default": "To send events to Home Assistant, you will need to set up [Webhooks with Mailgun]({mailgun_url}).\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n- Content Type: application/json\n\nSee [the documentation]({docs_url}) on how to configure automations to handle incoming data." }, "step": { "user": { diff --git a/homeassistant/components/mailgun/translations/es.json b/homeassistant/components/mailgun/translations/es.json index bf632ad9700..bbe0031bbfa 100644 --- a/homeassistant/components/mailgun/translations/es.json +++ b/homeassistant/components/mailgun/translations/es.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "Tu instancia de Home Assistant debe estar accesible desde Internet para recibir mensajes webhook." }, "create_entry": { - "default": "Para enviar eventos a Home Assistant debes configurar los [Webhooks con Mailgun]({mailgun_url}). \n\n Completa la siguiente informaci\u00f3n: \n\n- URL: `{webhook_url}` \n- M\u00e9todo: POST \n- Tipo de contenido: application/json \n\nConsulta [la documentaci\u00f3n]({docs_url}) sobre c\u00f3mo configurar las automatizaciones para manejar los datos entrantes." + "default": "Para enviar eventos a Home Assistant, necesitar\u00e1s configurar los [Webhooks con Mailgun]({mailgun_url}). \n\nCompleta la siguiente informaci\u00f3n: \n\n- URL: `{webhook_url}`\n- M\u00e9todo: POST\n- Tipo de contenido: application/json\n\nConsulta [la documentaci\u00f3n]({docs_url}) sobre c\u00f3mo configurar las automatizaciones para manejar los datos entrantes." }, "step": { "user": { diff --git a/homeassistant/components/mailgun/translations/et.json b/homeassistant/components/mailgun/translations/et.json index 7e659cfe8ac..a38a7443f19 100644 --- a/homeassistant/components/mailgun/translations/et.json +++ b/homeassistant/components/mailgun/translations/et.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "Veebikonksu s\u00f5numite vastuv\u00f5tmiseks peab Home Assistant olema Interneti kaudu juurdep\u00e4\u00e4setav." }, "create_entry": { - "default": "S\u00fcndmuste saatmiseks Home Assistantile pead seadistama [Webhooks with Mailgun] ( {dialogflow_url} ). \n\n Sisesta j\u00e4rgmine teave: \n\n - URL: \" {webhook_url} \" \n - Meetod: POST \n - Sisu t\u00fc\u00fcp: application/json \n\n Lisateavet leiad [documentation] ( {docs_url} )." + "default": "S\u00fcndmuste saatmiseks Home Assistantile pead seadistama [Webhooks with Mailgun] ( {mailgun_url} ). \n\n Sisesta j\u00e4rgmine teave: \n\n - URL: \" {webhook_url} \" \n - Meetod: POST \n - Sisu t\u00fc\u00fcp: application/json \n\n Lisateavet leiad [documentation] ( {docs_url} )." }, "step": { "user": { diff --git a/homeassistant/components/mailgun/translations/it.json b/homeassistant/components/mailgun/translations/it.json index 0131b39c22b..b667d516e69 100644 --- a/homeassistant/components/mailgun/translations/it.json +++ b/homeassistant/components/mailgun/translations/it.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "L'istanza di Home Assistant deve essere accessibile da Internet per ricevere messaggi webhook." }, "create_entry": { - "default": "Per inviare eventi a Home Assistant, dovrai configurare [Webhook con Mailgun]({mailgun_url})\n\n Compila le seguenti informazioni: \n\n - URL: `{webhook_url}` \n - Metodo: POST \n - Tipo di contenuto: application/json\n\n Vedi [la documentazione]({docs_url}) su come configurare le automazioni per gestire i dati in arrivo." + "default": "Per inviare eventi a Home Assistant, dovrai configurare [Webhook con Mailgun]({mailgun_url}). \n\n Compila le seguenti informazioni: \n\n - URL: `{webhook_url}`\n - Metodo: POST\n - Tipo di contenuto: applicazione/json \n\n Consulta [la documentazione]({docs_url}) su come configurare le automazioni per gestire i dati in entrata." }, "step": { "user": { diff --git a/homeassistant/components/mailgun/translations/ko.json b/homeassistant/components/mailgun/translations/ko.json index 2a296303d58..4fd1322a1ea 100644 --- a/homeassistant/components/mailgun/translations/ko.json +++ b/homeassistant/components/mailgun/translations/ko.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "cloud_not_connected": "Home Assistant \ud074\ub77c\uc6b0\ub4dc\uc5d0 \uc5f0\uacb0\ub418\uc5b4 \uc788\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.", "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.", "webhook_not_internet_accessible": "\uc6f9 \ud6c5 \uba54\uc2dc\uc9c0\ub97c \ubc1b\uc73c\ub824\uba74 \uc778\ud130\ub137\uc5d0\uc11c Home Assistant \uc778\uc2a4\ud134\uc2a4\uc5d0 \uc811\uadfc\ud560 \uc218 \uc788\uc5b4\uc57c \ud569\ub2c8\ub2e4." }, diff --git a/homeassistant/components/mailgun/translations/no.json b/homeassistant/components/mailgun/translations/no.json index 77576133365..f5530f5b840 100644 --- a/homeassistant/components/mailgun/translations/no.json +++ b/homeassistant/components/mailgun/translations/no.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "Home Assistant forekomsten din m\u00e5 v\u00e6re tilgjengelig fra internett for \u00e5 kunne motta webhook meldinger" }, "create_entry": { - "default": "For \u00e5 sende hendelser til Home Assistant, m\u00e5 du sette opp [Webhooks with Mailgun]({mailgun_url}).\n\nFyll ut f\u00f8lgende informasjon:\n\n- URL: `{webhook_url}`\n- Metode: POST\n- Innholdstype: application/json\n\nSe [dokumentasjonen]({docs_url}) om hvordan du konfigurerer automasjoner for \u00e5 h\u00e5ndtere innkommende data." + "default": "For \u00e5 sende hendelser til Home Assistant, m\u00e5 du sette opp [Webhooks with Mailgun]( {mailgun_url} ). \n\n Fyll inn f\u00f8lgende informasjon: \n\n - URL: ` {webhook_url} `\n - Metode: POST\n - Innholdstype: application/json \n\n Se [dokumentasjonen]( {docs_url} ) om hvordan du konfigurerer automatiseringer for \u00e5 h\u00e5ndtere innkommende data." }, "step": { "user": { diff --git a/homeassistant/components/mailgun/translations/sk.json b/homeassistant/components/mailgun/translations/sk.json index 27eb9ec5b61..ece5c930f18 100644 --- a/homeassistant/components/mailgun/translations/sk.json +++ b/homeassistant/components/mailgun/translations/sk.json @@ -5,6 +5,9 @@ "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia.", "webhook_not_internet_accessible": "Va\u0161a in\u0161tancia Home Assistant mus\u00ed by\u0165 pr\u00edstupn\u00e1 z internetu, aby ste mohli prij\u00edma\u0165 spr\u00e1vy webhooku." }, + "create_entry": { + "default": "Chcete odosla\u0165 udalosti do Home Assistant, budete musie\u0165 nastavi\u0165 [Webhooks with Mailgun]({mailgun_url}). \n\nVypl\u0148te nasleduj\u00face inform\u00e1cie: \n\n - URL: `{webhook_url}`\n - Met\u00f3da: POST \n - Typ obsahu: application/json\n\nVi\u010f [dokument\u00e1cia]({docs_url}), ako konfigurova\u0165 automatiz\u00e1cie pre spracovanie prich\u00e1dzaj\u00facich d\u00e1t." + }, "step": { "user": { "description": "Naozaj chcete nastavi\u0165 Mailgun?", diff --git a/homeassistant/components/manual/alarm_control_panel.py b/homeassistant/components/manual/alarm_control_panel.py index d96ada6e139..08dcba23272 100644 --- a/homeassistant/components/manual/alarm_control_panel.py +++ b/homeassistant/components/manual/alarm_control_panel.py @@ -32,7 +32,7 @@ from homeassistant.const import ( from homeassistant.core import HomeAssistant, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.event import track_point_in_time +from homeassistant.helpers.event import async_track_point_in_time from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType import homeassistant.util.dt as dt_util @@ -284,61 +284,61 @@ class ManualAlarm(alarm.AlarmControlPanelEntity, RestoreEntity): return alarm.CodeFormat.NUMBER return alarm.CodeFormat.TEXT - def alarm_disarm(self, code: str | None = None) -> None: + async def async_alarm_disarm(self, code: str | None = None) -> None: """Send disarm command.""" - if not self._validate_code(code, STATE_ALARM_DISARMED): + if not self._async_validate_code(code, STATE_ALARM_DISARMED): return self._state = STATE_ALARM_DISARMED self._state_ts = dt_util.utcnow() - self.schedule_update_ha_state() + self.async_write_ha_state() - def alarm_arm_home(self, code: str | None = None) -> None: + async def async_alarm_arm_home(self, code: str | None = None) -> None: """Send arm home command.""" - if self.code_arm_required and not self._validate_code( + if self.code_arm_required and not self._async_validate_code( code, STATE_ALARM_ARMED_HOME ): return - self._update_state(STATE_ALARM_ARMED_HOME) + self._async_update_state(STATE_ALARM_ARMED_HOME) - def alarm_arm_away(self, code: str | None = None) -> None: + async def async_alarm_arm_away(self, code: str | None = None) -> None: """Send arm away command.""" - if self.code_arm_required and not self._validate_code( + if self.code_arm_required and not self._async_validate_code( code, STATE_ALARM_ARMED_AWAY ): return - self._update_state(STATE_ALARM_ARMED_AWAY) + self._async_update_state(STATE_ALARM_ARMED_AWAY) - def alarm_arm_night(self, code: str | None = None) -> None: + async def async_alarm_arm_night(self, code: str | None = None) -> None: """Send arm night command.""" - if self.code_arm_required and not self._validate_code( + if self.code_arm_required and not self._async_validate_code( code, STATE_ALARM_ARMED_NIGHT ): return - self._update_state(STATE_ALARM_ARMED_NIGHT) + self._async_update_state(STATE_ALARM_ARMED_NIGHT) - def alarm_arm_vacation(self, code: str | None = None) -> None: + async def async_alarm_arm_vacation(self, code: str | None = None) -> None: """Send arm vacation command.""" - if self.code_arm_required and not self._validate_code( + if self.code_arm_required and not self._async_validate_code( code, STATE_ALARM_ARMED_VACATION ): return - self._update_state(STATE_ALARM_ARMED_VACATION) + self._async_update_state(STATE_ALARM_ARMED_VACATION) - def alarm_arm_custom_bypass(self, code: str | None = None) -> None: + async def async_alarm_arm_custom_bypass(self, code: str | None = None) -> None: """Send arm custom bypass command.""" - if self.code_arm_required and not self._validate_code( + if self.code_arm_required and not self._async_validate_code( code, STATE_ALARM_ARMED_CUSTOM_BYPASS ): return - self._update_state(STATE_ALARM_ARMED_CUSTOM_BYPASS) + self._async_update_state(STATE_ALARM_ARMED_CUSTOM_BYPASS) - def alarm_trigger(self, code: str | None = None) -> None: + async def async_alarm_trigger(self, code: str | None = None) -> None: """ Send alarm trigger command. @@ -347,9 +347,9 @@ class ManualAlarm(alarm.AlarmControlPanelEntity, RestoreEntity): """ if not self._trigger_time_by_state[self._active_state]: return - self._update_state(STATE_ALARM_TRIGGERED) + self._async_update_state(STATE_ALARM_TRIGGERED) - def _update_state(self, state: str) -> None: + def _async_update_state(self, state: str) -> None: """Update the state.""" if self._state == state: return @@ -357,16 +357,19 @@ class ManualAlarm(alarm.AlarmControlPanelEntity, RestoreEntity): self._previous_state = self._state self._state = state self._state_ts = dt_util.utcnow() - self.schedule_update_ha_state() + self.async_write_ha_state() + self._async_set_state_update_events() + def _async_set_state_update_events(self) -> None: + state = self._state if state == STATE_ALARM_TRIGGERED: pending_time = self._pending_time(state) - track_point_in_time( + async_track_point_in_time( self._hass, self.async_scheduled_update, self._state_ts + pending_time ) trigger_time = self._trigger_time_by_state[self._previous_state] - track_point_in_time( + async_track_point_in_time( self._hass, self.async_scheduled_update, self._state_ts + pending_time + trigger_time, @@ -374,20 +377,20 @@ class ManualAlarm(alarm.AlarmControlPanelEntity, RestoreEntity): elif state in SUPPORTED_ARMING_STATES: arming_time = self._arming_time(state) if arming_time: - track_point_in_time( + async_track_point_in_time( self._hass, self.async_scheduled_update, self._state_ts + arming_time, ) - def _validate_code(self, code, state): + def _async_validate_code(self, code, state): """Validate given code.""" if self._code is None: return True if isinstance(self._code, str): alarm_code = self._code else: - alarm_code = self._code.render( + alarm_code = self._code.async_render( parse_result=False, from_state=self._state, to_state=state ) check = not alarm_code or code == alarm_code @@ -403,6 +406,10 @@ class ManualAlarm(alarm.AlarmControlPanelEntity, RestoreEntity): ATTR_PREVIOUS_STATE: self._previous_state, ATTR_NEXT_STATE: self._state, } + if self.state == STATE_ALARM_TRIGGERED: + return { + ATTR_PREVIOUS_STATE: self._previous_state, + } return {} @callback @@ -414,14 +421,14 @@ class ManualAlarm(alarm.AlarmControlPanelEntity, RestoreEntity): """Run when entity about to be added to hass.""" await super().async_added_to_hass() if state := await self.async_get_last_state(): - if ( - state.state in (STATE_ALARM_PENDING, STATE_ALARM_ARMING) - and hasattr(state, "attributes") - and state.attributes[ATTR_PREVIOUS_STATE] - ): - # If in arming or pending state, we return to the ATTR_PREVIOUS_STATE - self._state = state.attributes[ATTR_PREVIOUS_STATE] - self._state_ts = dt_util.utcnow() + self._state_ts = state.last_updated + if hasattr(state, "attributes") and ATTR_NEXT_STATE in state.attributes: + # If in arming or pending state we record the transition, + # not the current state + self._state = state.attributes[ATTR_NEXT_STATE] else: self._state = state.state - self._state_ts = state.last_updated + + if hasattr(state, "attributes") and ATTR_PREVIOUS_STATE in state.attributes: + self._previous_state = state.attributes[ATTR_PREVIOUS_STATE] + self._async_set_state_update_events() diff --git a/homeassistant/components/manual_mqtt/alarm_control_panel.py b/homeassistant/components/manual_mqtt/alarm_control_panel.py index 66c75b5ed0e..93a1eeb2cb6 100644 --- a/homeassistant/components/manual_mqtt/alarm_control_panel.py +++ b/homeassistant/components/manual_mqtt/alarm_control_panel.py @@ -21,18 +21,20 @@ from homeassistant.const import ( CONF_PLATFORM, CONF_TRIGGER_TIME, STATE_ALARM_ARMED_AWAY, + STATE_ALARM_ARMED_CUSTOM_BYPASS, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, + STATE_ALARM_ARMED_VACATION, STATE_ALARM_DISARMED, STATE_ALARM_PENDING, STATE_ALARM_TRIGGERED, ) -from homeassistant.core import HomeAssistant +from homeassistant.core import HomeAssistant, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.event import ( + async_track_point_in_time, async_track_state_change_event, - track_point_in_time, ) from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType import homeassistant.util.dt as dt_util @@ -46,6 +48,8 @@ CONF_PAYLOAD_DISARM = "payload_disarm" CONF_PAYLOAD_ARM_HOME = "payload_arm_home" CONF_PAYLOAD_ARM_AWAY = "payload_arm_away" CONF_PAYLOAD_ARM_NIGHT = "payload_arm_night" +CONF_PAYLOAD_ARM_VACATION = "payload_arm_vacation" +CONF_PAYLOAD_ARM_CUSTOM_BYPASS = "payload_arm_custom_bypass" DEFAULT_ALARM_NAME = "HA Alarm" DEFAULT_DELAY_TIME = datetime.timedelta(seconds=0) @@ -55,6 +59,8 @@ DEFAULT_DISARM_AFTER_TRIGGER = False DEFAULT_ARM_AWAY = "ARM_AWAY" DEFAULT_ARM_HOME = "ARM_HOME" DEFAULT_ARM_NIGHT = "ARM_NIGHT" +DEFAULT_ARM_VACATION = "ARM_VACATION" +DEFAULT_ARM_CUSTOM_BYPASS = "ARM_CUSTOM_BYPASS" DEFAULT_DISARM = "DISARM" SUPPORTED_STATES = [ @@ -62,6 +68,8 @@ SUPPORTED_STATES = [ STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, + STATE_ALARM_ARMED_VACATION, + STATE_ALARM_ARMED_CUSTOM_BYPASS, STATE_ALARM_TRIGGERED, ] @@ -138,6 +146,12 @@ PLATFORM_SCHEMA = vol.Schema( vol.Optional(STATE_ALARM_ARMED_NIGHT, default={}): _state_schema( STATE_ALARM_ARMED_NIGHT ), + vol.Optional(STATE_ALARM_ARMED_VACATION, default={}): _state_schema( + STATE_ALARM_ARMED_VACATION + ), + vol.Optional( + STATE_ALARM_ARMED_CUSTOM_BYPASS, default={} + ): _state_schema(STATE_ALARM_ARMED_CUSTOM_BYPASS), vol.Optional(STATE_ALARM_DISARMED, default={}): _state_schema( STATE_ALARM_DISARMED ), @@ -156,6 +170,12 @@ PLATFORM_SCHEMA = vol.Schema( vol.Optional( CONF_PAYLOAD_ARM_NIGHT, default=DEFAULT_ARM_NIGHT ): cv.string, + vol.Optional( + CONF_PAYLOAD_ARM_VACATION, default=DEFAULT_ARM_VACATION + ): cv.string, + vol.Optional( + CONF_PAYLOAD_ARM_CUSTOM_BYPASS, default=DEFAULT_ARM_CUSTOM_BYPASS + ): cv.string, vol.Optional(CONF_PAYLOAD_DISARM, default=DEFAULT_DISARM): cv.string, } ), @@ -187,6 +207,8 @@ def setup_platform( config.get(CONF_PAYLOAD_ARM_HOME), config.get(CONF_PAYLOAD_ARM_AWAY), config.get(CONF_PAYLOAD_ARM_NIGHT), + config.get(CONF_PAYLOAD_ARM_VACATION), + config.get(CONF_PAYLOAD_ARM_CUSTOM_BYPASS), config, ) ] @@ -210,7 +232,9 @@ class ManualMQTTAlarm(alarm.AlarmControlPanelEntity): AlarmControlPanelEntityFeature.ARM_HOME | AlarmControlPanelEntityFeature.ARM_AWAY | AlarmControlPanelEntityFeature.ARM_NIGHT + | AlarmControlPanelEntityFeature.ARM_VACATION | AlarmControlPanelEntityFeature.TRIGGER + | AlarmControlPanelEntityFeature.ARM_CUSTOM_BYPASS ) def __init__( @@ -228,6 +252,8 @@ class ManualMQTTAlarm(alarm.AlarmControlPanelEntity): payload_arm_home, payload_arm_away, payload_arm_night, + payload_arm_vacation, + payload_arm_custom_bypass, config, ): """Init the manual MQTT alarm panel.""" @@ -264,6 +290,8 @@ class ManualMQTTAlarm(alarm.AlarmControlPanelEntity): self._payload_arm_home = payload_arm_home self._payload_arm_away = payload_arm_away self._payload_arm_night = payload_arm_night + self._payload_arm_vacation = payload_arm_vacation + self._payload_arm_custom_bypass = payload_arm_custom_bypass @property def state(self) -> str: @@ -314,43 +342,61 @@ class ManualMQTTAlarm(alarm.AlarmControlPanelEntity): return alarm.CodeFormat.NUMBER return alarm.CodeFormat.TEXT - def alarm_disarm(self, code: str | None = None) -> None: + async def async_alarm_disarm(self, code: str | None = None) -> None: """Send disarm command.""" - if not self._validate_code(code, STATE_ALARM_DISARMED): + if not self._async_validate_code(code, STATE_ALARM_DISARMED): return self._state = STATE_ALARM_DISARMED self._state_ts = dt_util.utcnow() - self.schedule_update_ha_state() + self.async_schedule_update_ha_state() - def alarm_arm_home(self, code: str | None = None) -> None: + async def async_alarm_arm_home(self, code: str | None = None) -> None: """Send arm home command.""" - if self.code_arm_required and not self._validate_code( + if self.code_arm_required and not self._async_validate_code( code, STATE_ALARM_ARMED_HOME ): return - self._update_state(STATE_ALARM_ARMED_HOME) + self._async_update_state(STATE_ALARM_ARMED_HOME) - def alarm_arm_away(self, code: str | None = None) -> None: + async def async_alarm_arm_away(self, code: str | None = None) -> None: """Send arm away command.""" - if self.code_arm_required and not self._validate_code( + if self.code_arm_required and not self._async_validate_code( code, STATE_ALARM_ARMED_AWAY ): return - self._update_state(STATE_ALARM_ARMED_AWAY) + self._async_update_state(STATE_ALARM_ARMED_AWAY) - def alarm_arm_night(self, code: str | None = None) -> None: + async def async_alarm_arm_night(self, code: str | None = None) -> None: """Send arm night command.""" - if self.code_arm_required and not self._validate_code( + if self.code_arm_required and not self._async_validate_code( code, STATE_ALARM_ARMED_NIGHT ): return - self._update_state(STATE_ALARM_ARMED_NIGHT) + self._async_update_state(STATE_ALARM_ARMED_NIGHT) - def alarm_trigger(self, code: str | None = None) -> None: + async def async_alarm_arm_vacation(self, code: str | None = None) -> None: + """Send arm vacation command.""" + if self.code_arm_required and not self._async_validate_code( + code, STATE_ALARM_ARMED_VACATION + ): + return + + self._async_update_state(STATE_ALARM_ARMED_VACATION) + + async def async_alarm_arm_custom_bypass(self, code: str | None = None) -> None: + """Send arm custom bypass command.""" + if self.code_arm_required and not self._async_validate_code( + code, STATE_ALARM_ARMED_CUSTOM_BYPASS + ): + return + + self._async_update_state(STATE_ALARM_ARMED_CUSTOM_BYPASS) + + async def async_alarm_trigger(self, code: str | None = None) -> None: """ Send alarm trigger command. @@ -359,9 +405,9 @@ class ManualMQTTAlarm(alarm.AlarmControlPanelEntity): """ if not self._trigger_time_by_state[self._active_state]: return - self._update_state(STATE_ALARM_TRIGGERED) + self._async_update_state(STATE_ALARM_TRIGGERED) - def _update_state(self, state): + def _async_update_state(self, state: str) -> None: """Update the state.""" if self._state == state: return @@ -369,33 +415,33 @@ class ManualMQTTAlarm(alarm.AlarmControlPanelEntity): self._previous_state = self._state self._state = state self._state_ts = dt_util.utcnow() - self.schedule_update_ha_state() + self.async_write_ha_state() pending_time = self._pending_time(state) if state == STATE_ALARM_TRIGGERED: - track_point_in_time( - self._hass, self.async_update_ha_state, self._state_ts + pending_time + async_track_point_in_time( + self._hass, self.async_scheduled_update, self._state_ts + pending_time ) trigger_time = self._trigger_time_by_state[self._previous_state] - track_point_in_time( + async_track_point_in_time( self._hass, - self.async_update_ha_state, + self.async_scheduled_update, self._state_ts + pending_time + trigger_time, ) elif state in SUPPORTED_PENDING_STATES and pending_time: - track_point_in_time( - self._hass, self.async_update_ha_state, self._state_ts + pending_time + async_track_point_in_time( + self._hass, self.async_scheduled_update, self._state_ts + pending_time ) - def _validate_code(self, code, state): + def _async_validate_code(self, code, state): """Validate given code.""" if self._code is None: return True if isinstance(self._code, str): alarm_code = self._code else: - alarm_code = self._code.render( + alarm_code = self._code.async_render( from_state=self._state, to_state=state, parse_result=False ) check = not alarm_code or code == alarm_code @@ -413,6 +459,11 @@ class ManualMQTTAlarm(alarm.AlarmControlPanelEntity): ATTR_POST_PENDING_STATE: self._state, } + @callback + def async_scheduled_update(self, now): + """Update state at a scheduled point in time.""" + self.async_write_ha_state() + async def async_added_to_hass(self) -> None: """Subscribe to MQTT events.""" async_track_state_change_event( @@ -429,6 +480,10 @@ class ManualMQTTAlarm(alarm.AlarmControlPanelEntity): await self.async_alarm_arm_away(self._code) elif msg.payload == self._payload_arm_night: await self.async_alarm_arm_night(self._code) + elif msg.payload == self._payload_arm_vacation: + await self.async_alarm_arm_vacation(self._code) + elif msg.payload == self._payload_arm_custom_bypass: + await self.async_alarm_arm_custom_bypass(self._code) else: _LOGGER.warning("Received unexpected payload: %s", msg.payload) return diff --git a/homeassistant/components/mastodon/__init__.py b/homeassistant/components/mastodon/__init__.py index 123d23afb80..6a9f074a9ba 100644 --- a/homeassistant/components/mastodon/__init__.py +++ b/homeassistant/components/mastodon/__init__.py @@ -1 +1 @@ -"""The mastodon component.""" +"""The Mastodon integration.""" diff --git a/homeassistant/components/mastodon/const.py b/homeassistant/components/mastodon/const.py new file mode 100644 index 00000000000..6fe9552f991 --- /dev/null +++ b/homeassistant/components/mastodon/const.py @@ -0,0 +1,9 @@ +"""Constants for the Mastodon integration.""" + +import logging +from typing import Final + +LOGGER = logging.getLogger(__name__) + +CONF_BASE_URL: Final = "base_url" +DEFAULT_URL: Final = "https://mastodon.social" diff --git a/homeassistant/components/mastodon/notify.py b/homeassistant/components/mastodon/notify.py index 058137393f5..8f259bc3433 100644 --- a/homeassistant/components/mastodon/notify.py +++ b/homeassistant/components/mastodon/notify.py @@ -1,5 +1,7 @@ """Mastodon platform for notify component.""" -import logging +from __future__ import annotations + +from typing import Any from mastodon import Mastodon from mastodon.Mastodon import MastodonAPIError, MastodonUnauthorizedError @@ -7,13 +9,11 @@ import voluptuous as vol from homeassistant.components.notify import PLATFORM_SCHEMA, BaseNotificationService from homeassistant.const import CONF_ACCESS_TOKEN, CONF_CLIENT_ID, CONF_CLIENT_SECRET -import homeassistant.helpers.config_validation as cv +from homeassistant.core import HomeAssistant +from homeassistant.helpers import config_validation as cv +from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -_LOGGER = logging.getLogger(__name__) - -CONF_BASE_URL = "base_url" - -DEFAULT_URL = "https://mastodon.social" +from .const import CONF_BASE_URL, DEFAULT_URL, LOGGER PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( { @@ -25,7 +25,11 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( ) -def get_service(hass, config, discovery_info=None): +def get_service( + hass: HomeAssistant, + config: ConfigType, + discovery_info: DiscoveryInfoType | None = None, +) -> MastodonNotificationService | None: """Get the Mastodon notification service.""" client_id = config.get(CONF_CLIENT_ID) client_secret = config.get(CONF_CLIENT_SECRET) @@ -41,7 +45,7 @@ def get_service(hass, config, discovery_info=None): ) mastodon.account_verify_credentials() except MastodonUnauthorizedError: - _LOGGER.warning("Authentication failed") + LOGGER.warning("Authentication failed") return None return MastodonNotificationService(mastodon) @@ -50,13 +54,13 @@ def get_service(hass, config, discovery_info=None): class MastodonNotificationService(BaseNotificationService): """Implement the notification service for Mastodon.""" - def __init__(self, api): + def __init__(self, api: Mastodon) -> None: """Initialize the service.""" self._api = api - def send_message(self, message="", **kwargs): + def send_message(self, message: str = "", **kwargs: Any) -> None: """Send a message to a user.""" try: self._api.toot(message) except MastodonAPIError: - _LOGGER.error("Unable to send message") + LOGGER.error("Unable to send message") diff --git a/homeassistant/components/matter/__init__.py b/homeassistant/components/matter/__init__.py index 845d48ea883..b1470ecc422 100644 --- a/homeassistant/components/matter/__init__.py +++ b/homeassistant/components/matter/__init__.py @@ -2,7 +2,6 @@ from __future__ import annotations import asyncio -from typing import cast import async_timeout from matter_server.client import MatterClient @@ -11,10 +10,11 @@ from matter_server.client.exceptions import ( FailedCommand, InvalidServerVersion, ) +from matter_server.common.models.error import MatterError import voluptuous as vol from homeassistant.components.hassio import AddonError, AddonManager, AddonState -from homeassistant.config_entries import ConfigEntry +from homeassistant.config_entries import ConfigEntry, ConfigEntryState from homeassistant.const import CONF_URL, EVENT_HOMEASSISTANT_STOP from homeassistant.core import Event, HomeAssistant, ServiceCall, callback from homeassistant.exceptions import ConfigEntryNotReady, HomeAssistantError @@ -32,6 +32,10 @@ from .addon import get_addon_manager from .api import async_register_api from .const import CONF_INTEGRATION_CREATED_ADDON, CONF_USE_ADDON, DOMAIN, LOGGER from .device_platform import DEVICE_PLATFORM +from .helpers import MatterEntryData, get_matter + +CONNECT_TIMEOUT = 10 +LISTEN_READY_TIMEOUT = 30 async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: @@ -41,8 +45,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: matter_client = MatterClient(entry.data[CONF_URL], async_get_clientsession(hass)) try: - await matter_client.connect() - except CannotConnect as err: + async with async_timeout.timeout(CONNECT_TIMEOUT): + await matter_client.connect() + except (CannotConnect, asyncio.TimeoutError) as err: raise ConfigEntryNotReady("Failed to connect to matter server") from err except InvalidServerVersion as err: if use_addon: @@ -60,7 +65,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: raise ConfigEntryNotReady(f"Invalid server version: {err}") from err except Exception as err: - matter_client.logger.exception("Failed to connect to matter server") + LOGGER.exception("Failed to connect to matter server") raise ConfigEntryNotReady( "Unknown error connecting to the Matter server" ) from err @@ -75,16 +80,17 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, on_hass_stop) ) - # register websocket api async_register_api(hass) # launch the matter client listen task in the background - # use the init_ready event to keep track if it did initialize successfully + # use the init_ready event to wait until initialization is done init_ready = asyncio.Event() - listen_task = asyncio.create_task(matter_client.start_listening(init_ready)) + listen_task = asyncio.create_task( + _client_listen(hass, entry, matter_client, init_ready) + ) try: - async with async_timeout.timeout(30): + async with async_timeout.timeout(LISTEN_READY_TIMEOUT): await init_ready.wait() except asyncio.TimeoutError as err: listen_task.cancel() @@ -94,27 +100,58 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass.data[DOMAIN] = {} _async_init_services(hass) - # we create an intermediate layer (adapter) which keeps track of our nodes - # and discovery of platform entities from the node's attributes + # create an intermediate layer (adapter) which keeps track of the nodes + # and discovery of platform entities from the node attributes matter = MatterAdapter(hass, matter_client, entry) - hass.data[DOMAIN][entry.entry_id] = matter + hass.data[DOMAIN][entry.entry_id] = MatterEntryData(matter, listen_task) - # forward platform setup to all platforms in the discovery schema await hass.config_entries.async_forward_entry_setups(entry, DEVICE_PLATFORM) + await matter.setup_nodes() - # start discovering of node entities as task - asyncio.create_task(matter.setup_nodes()) + # If the listen task is already failed, we need to raise ConfigEntryNotReady + if listen_task.done() and (listen_error := listen_task.exception()) is not None: + await hass.config_entries.async_unload_platforms(entry, DEVICE_PLATFORM) + hass.data[DOMAIN].pop(entry.entry_id) + try: + await matter_client.disconnect() + finally: + raise ConfigEntryNotReady(listen_error) from listen_error return True +async def _client_listen( + hass: HomeAssistant, + entry: ConfigEntry, + matter_client: MatterClient, + init_ready: asyncio.Event, +) -> None: + """Listen with the client.""" + try: + await matter_client.start_listening(init_ready) + except MatterError as err: + if entry.state != ConfigEntryState.LOADED: + raise + LOGGER.error("Failed to listen: %s", err) + except Exception as err: # pylint: disable=broad-except + # We need to guard against unknown exceptions to not crash this task. + LOGGER.exception("Unexpected exception: %s", err) + if entry.state != ConfigEntryState.LOADED: + raise + + if not hass.is_stopping: + LOGGER.debug("Disconnected from server. Reloading integration") + hass.async_create_task(hass.config_entries.async_reload(entry.entry_id)) + + async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Unload a config entry.""" unload_ok = await hass.config_entries.async_unload_platforms(entry, DEVICE_PLATFORM) if unload_ok: - matter: MatterAdapter = hass.data[DOMAIN].pop(entry.entry_id) - await matter.matter_client.disconnect() + matter_entry_data: MatterEntryData = hass.data[DOMAIN].pop(entry.entry_id) + matter_entry_data.listen_task.cancel() + await matter_entry_data.adapter.matter_client.disconnect() if entry.data.get(CONF_USE_ADDON) and entry.disabled_by: addon_manager: AddonManager = get_addon_manager(hass) @@ -165,102 +202,21 @@ async def async_remove_config_entry_device( if not unique_id: return True - matter: MatterAdapter = hass.data[DOMAIN][config_entry.entry_id] + matter_entry_data: MatterEntryData = hass.data[DOMAIN][config_entry.entry_id] + matter_client = matter_entry_data.adapter.matter_client - for node in await matter.matter_client.get_nodes(): + for node in await matter_client.get_nodes(): if node.unique_id == unique_id: - await matter.matter_client.remove_node(node.node_id) + await matter_client.remove_node(node.node_id) break return True -@callback -def get_matter(hass: HomeAssistant) -> MatterAdapter: - """Return MatterAdapter instance.""" - # NOTE: This assumes only one Matter connection/fabric can exist. - # Shall we support connecting to multiple servers in the client or by config entries? - # In case of the config entry we need to fix this. - matter: MatterAdapter = next(iter(hass.data[DOMAIN].values())) - return matter - - @callback def _async_init_services(hass: HomeAssistant) -> None: """Init services.""" - async def commission(call: ServiceCall) -> None: - """Handle commissioning.""" - matter_client = get_matter(hass).matter_client - try: - await matter_client.commission_with_code(call.data["code"]) - except FailedCommand as err: - raise HomeAssistantError(str(err)) from err - - async_register_admin_service( - hass, - DOMAIN, - "commission", - commission, - vol.Schema({"code": str}), - ) - - async def accept_shared_device(call: ServiceCall) -> None: - """Accept a shared device.""" - matter_client = get_matter(hass).matter_client - try: - await matter_client.commission_on_network(call.data["pin"]) - except FailedCommand as err: - raise HomeAssistantError(str(err)) from err - - async_register_admin_service( - hass, - DOMAIN, - "accept_shared_device", - accept_shared_device, - vol.Schema({"pin": vol.Coerce(int)}), - ) - - async def set_wifi(call: ServiceCall) -> None: - """Handle set wifi creds.""" - matter_client = get_matter(hass).matter_client - try: - await matter_client.set_wifi_credentials( - call.data["ssid"], call.data["password"] - ) - except FailedCommand as err: - raise HomeAssistantError(str(err)) from err - - async_register_admin_service( - hass, - DOMAIN, - "set_wifi", - set_wifi, - vol.Schema( - { - "ssid": str, - "password": str, - } - ), - ) - - async def set_thread_dataset(call: ServiceCall) -> None: - """Handle set Thread creds.""" - matter_client = get_matter(hass).matter_client - thread_dataset = bytes.fromhex(call.data["dataset"]) - try: - await matter_client.set_thread_operational_dataset(thread_dataset) - except FailedCommand as err: - raise HomeAssistantError(str(err)) from err - - async_register_admin_service( - hass, - DOMAIN, - "set_thread", - set_thread_dataset, - vol.Schema({"dataset": str}), - ) - async def _node_id_from_ha_device_id(ha_device_id: str) -> int | None: """Get node id from ha device id.""" dev_reg = dr.async_get(hass) @@ -288,7 +244,7 @@ def _async_init_services(hass: HomeAssistant) -> None: # This could be more efficient for node in await matter_client.get_nodes(): if node.unique_id == unique_id: - return cast(int, node.node_id) + return node.node_id return None diff --git a/homeassistant/components/matter/adapter.py b/homeassistant/components/matter/adapter.py index c2ad11cb10f..b573ed0a3fc 100644 --- a/homeassistant/components/matter/adapter.py +++ b/homeassistant/components/matter/adapter.py @@ -1,11 +1,10 @@ """Matter to Home Assistant adapter.""" from __future__ import annotations -import asyncio -import logging from typing import TYPE_CHECKING from chip.clusters import Objects as all_clusters +from matter_server.common.models.events import EventType from matter_server.common.models.node_device import AbstractMatterNodeDevice from homeassistant.config_entries import ConfigEntry @@ -14,7 +13,7 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry as dr from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .const import DOMAIN +from .const import DOMAIN, LOGGER from .device_platform import DEVICE_PLATFORM if TYPE_CHECKING: @@ -35,32 +34,40 @@ class MatterAdapter: self.matter_client = matter_client self.hass = hass self.config_entry = config_entry - self.logger = logging.getLogger(__name__) self.platform_handlers: dict[Platform, AddEntitiesCallback] = {} - self._platforms_set_up = asyncio.Event() def register_platform_handler( self, platform: Platform, add_entities: AddEntitiesCallback ) -> None: """Register a platform handler.""" self.platform_handlers[platform] = add_entities - if len(self.platform_handlers) == len(DEVICE_PLATFORM): - self._platforms_set_up.set() async def setup_nodes(self) -> None: - """Set up all existing nodes.""" - await self._platforms_set_up.wait() + """Set up all existing nodes and subscribe to new nodes.""" for node in await self.matter_client.get_nodes(): - await self._setup_node(node) + self._setup_node(node) - async def _setup_node(self, node: MatterNode) -> None: + def node_added_callback(event: EventType, node: MatterNode | None) -> None: + """Handle node added event.""" + if node is None: + # We can clean this up when we've improved the typing in the library. + # https://github.com/home-assistant-libs/python-matter-server/pull/153 + raise RuntimeError("Node added event without node") + self._setup_node(node) + + self.config_entry.async_on_unload( + self.matter_client.subscribe(node_added_callback, EventType.NODE_ADDED) + ) + + def _setup_node(self, node: MatterNode) -> None: """Set up an node.""" - self.logger.debug("Setting up entities for node %s", node.node_id) + LOGGER.debug("Setting up entities for node %s", node.node_id) bridge_unique_id: str | None = None - if node.aggregator_device_type_instance is not None: - node_info = node.root_device_type_instance.get_cluster(all_clusters.Basic) + if node.aggregator_device_type_instance is not None and ( + node_info := node.root_device_type_instance.get_cluster(all_clusters.Basic) + ): self._create_device_registry( node_info, node_info.nodeLabel or "Hub device", None ) @@ -115,7 +122,7 @@ class MatterAdapter: entities = [] for entity_description in entity_descriptions: - self.logger.debug( + LOGGER.debug( "Creating %s entity for %s (%s)", platform, instance.device_type.__name__, @@ -134,7 +141,7 @@ class MatterAdapter: created = True if not created: - self.logger.warning( + LOGGER.warning( "Found unsupported device %s (%s)", type(instance).__name__, hex(instance.device_type.device_type), diff --git a/homeassistant/components/matter/api.py b/homeassistant/components/matter/api.py index 36cf83fd0da..b1c2d9effb1 100644 --- a/homeassistant/components/matter/api.py +++ b/homeassistant/components/matter/api.py @@ -13,7 +13,7 @@ from homeassistant.components.websocket_api import ActiveConnection from homeassistant.core import HomeAssistant, callback from .adapter import MatterAdapter -from .const import DOMAIN +from .helpers import get_matter ID = "id" TYPE = "type" @@ -36,7 +36,7 @@ def async_get_matter_adapter(func: Callable) -> Callable: hass: HomeAssistant, connection: ActiveConnection, msg: dict ) -> None: """Provide the Matter client to the function.""" - matter: MatterAdapter = next(iter(hass.data[DOMAIN].values())) + matter = get_matter(hass) await func(hass, connection, msg, matter) diff --git a/homeassistant/components/matter/binary_sensor.py b/homeassistant/components/matter/binary_sensor.py index a4ca54920fb..15ad13d25ad 100644 --- a/homeassistant/components/matter/binary_sensor.py +++ b/homeassistant/components/matter/binary_sensor.py @@ -3,7 +3,6 @@ from __future__ import annotations from dataclasses import dataclass from functools import partial -from typing import TYPE_CHECKING from chip.clusters import Objects as clusters from matter_server.common.models import device_types @@ -18,11 +17,8 @@ from homeassistant.const import Platform from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .const import DOMAIN from .entity import MatterEntity, MatterEntityDescriptionBaseClass - -if TYPE_CHECKING: - from .adapter import MatterAdapter +from .helpers import get_matter async def async_setup_entry( @@ -31,7 +27,7 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up Matter binary sensor from Config Entry.""" - matter: MatterAdapter = hass.data[DOMAIN][config_entry.entry_id] + matter = get_matter(hass) matter.register_platform_handler(Platform.BINARY_SENSOR, async_add_entities) @@ -43,9 +39,8 @@ class MatterBinarySensor(MatterEntity, BinarySensorEntity): @callback def _update_from_device(self) -> None: """Update from device.""" - self._attr_is_on = self._device_type_instance.get_cluster( - clusters.BooleanState - ).stateValue + cluster = self._device_type_instance.get_cluster(clusters.BooleanState) + self._attr_is_on = cluster.stateValue if cluster else None class MatterOccupancySensor(MatterBinarySensor): @@ -56,11 +51,9 @@ class MatterOccupancySensor(MatterBinarySensor): @callback def _update_from_device(self) -> None: """Update from device.""" - occupancy = self._device_type_instance.get_cluster( - clusters.OccupancySensing - ).occupancy + cluster = self._device_type_instance.get_cluster(clusters.OccupancySensing) # The first bit = if occupied - self._attr_is_on = occupancy & 1 == 1 + self._attr_is_on = cluster.occupancy & 1 == 1 if cluster else None @dataclass diff --git a/homeassistant/components/matter/config_flow.py b/homeassistant/components/matter/config_flow.py index f570b0cf14c..6e25370d86a 100644 --- a/homeassistant/components/matter/config_flow.py +++ b/homeassistant/components/matter/config_flow.py @@ -20,6 +20,7 @@ from homeassistant.components.hassio import ( from homeassistant.const import CONF_URL from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import AbortFlow, FlowResult +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import aiohttp_client from .addon import get_addon_manager @@ -34,6 +35,7 @@ from .const import ( ADDON_SETUP_TIMEOUT = 5 ADDON_SETUP_TIMEOUT_ROUNDS = 40 DEFAULT_URL = "ws://localhost:5580/ws" +DEFAULT_TITLE = "Matter" ON_SUPERVISOR_SCHEMA = vol.Schema({vol.Optional(CONF_USE_ADDON, default=True): bool}) @@ -130,7 +132,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): try: await self.start_task - except (CannotConnect, AddonError, AbortFlow) as err: + except (FailedConnect, AddonError, AbortFlow) as err: self.start_task = None LOGGER.error(err) return self.async_show_progress_done(next_step_id="start_failed") @@ -169,7 +171,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): else: break else: - raise CannotConnect("Failed to start Matter Server add-on: timeout") + raise FailedConnect("Failed to start Matter Server add-on: timeout") finally: # Continue the flow after show progress when the task is done. self.hass.async_create_task( @@ -306,7 +308,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): CONF_USE_ADDON: self.use_addon, CONF_INTEGRATION_CREATED_ADDON: self.integration_created_addon, }, - title=self.ws_address, + title=DEFAULT_TITLE, ) await self.hass.config_entries.async_reload(config_entry.entry_id) raise AbortFlow("reconfiguration_successful") @@ -316,10 +318,14 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): self.hass.config_entries.flow.async_abort(progress["flow_id"]) return self.async_create_entry( - title=self.ws_address, + title=DEFAULT_TITLE, data={ CONF_URL: self.ws_address, CONF_USE_ADDON: self.use_addon, CONF_INTEGRATION_CREATED_ADDON: self.integration_created_addon, }, ) + + +class FailedConnect(HomeAssistantError): + """Failed to connect to the Matter Server.""" diff --git a/homeassistant/components/matter/entity.py b/homeassistant/components/matter/entity.py index 019631750f4..4f28c1d2369 100644 --- a/homeassistant/components/matter/entity.py +++ b/homeassistant/components/matter/entity.py @@ -59,7 +59,15 @@ class MatterEntity(Entity): self._unsubscribes: list[Callable] = [] # for fast lookups we create a mapping to the attribute paths self._attributes_map: dict[type, str] = {} - self._attr_unique_id = f"{matter_client.server_info.compressed_fabric_id}-{node.unique_id}-{device_type_instance.endpoint}-{device_type_instance.device_type.device_type}" + server_info = matter_client.server_info + # The server info is set when the client connects to the server. + assert server_info is not None + self._attr_unique_id = ( + f"{server_info.compressed_fabric_id}-" + f"{node.unique_id}-" + f"{device_type_instance.endpoint}-" + f"{device_type_instance.device_type.device_type}" + ) @property def device_info(self) -> DeviceInfo | None: diff --git a/homeassistant/components/matter/helpers.py b/homeassistant/components/matter/helpers.py new file mode 100644 index 00000000000..479b1d824ad --- /dev/null +++ b/homeassistant/components/matter/helpers.py @@ -0,0 +1,31 @@ +"""Provide integration helpers that are aware of the matter integration.""" +from __future__ import annotations + +import asyncio +from dataclasses import dataclass +from typing import TYPE_CHECKING + +from homeassistant.core import HomeAssistant, callback + +from .const import DOMAIN + +if TYPE_CHECKING: + from .adapter import MatterAdapter + + +@dataclass +class MatterEntryData: + """Hold Matter data for the config entry.""" + + adapter: MatterAdapter + listen_task: asyncio.Task + + +@callback +def get_matter(hass: HomeAssistant) -> MatterAdapter: + """Return MatterAdapter instance.""" + # NOTE: This assumes only one Matter connection/fabric can exist. + # Shall we support connecting to multiple servers in the client or by config entries? + # In case of the config entry we need to fix this. + matter_entry_data: MatterEntryData = next(iter(hass.data[DOMAIN].values())) + return matter_entry_data.adapter diff --git a/homeassistant/components/matter/light.py b/homeassistant/components/matter/light.py index 37454e3005c..0136b74f32e 100644 --- a/homeassistant/components/matter/light.py +++ b/homeassistant/components/matter/light.py @@ -3,7 +3,7 @@ from __future__ import annotations from dataclasses import dataclass from functools import partial -from typing import TYPE_CHECKING, Any +from typing import Any from chip.clusters import Objects as clusters from matter_server.common.models import device_types @@ -19,13 +19,10 @@ from homeassistant.const import Platform from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .const import DOMAIN from .entity import MatterEntity, MatterEntityDescriptionBaseClass +from .helpers import get_matter from .util import renormalize -if TYPE_CHECKING: - from .adapter import MatterAdapter - async def async_setup_entry( hass: HomeAssistant, @@ -33,7 +30,7 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up Matter Light from Config Entry.""" - matter: MatterAdapter = hass.data[DOMAIN][config_entry.entry_id] + matter = get_matter(hass) matter.register_platform_handler(Platform.LIGHT, async_add_entities) @@ -60,6 +57,9 @@ class MatterLight(MatterEntity, LightEntity): return level_control = self._device_type_instance.get_cluster(clusters.LevelControl) + # We check above that the device supports brightness, ie level control. + assert level_control is not None + level = round( renormalize( kwargs[ATTR_BRIGHTNESS], @@ -89,20 +89,20 @@ class MatterLight(MatterEntity, LightEntity): @callback def _update_from_device(self) -> None: """Update from device.""" - if self._attr_supported_color_modes is None: - if self._supports_brightness(): - self._attr_supported_color_modes = {ColorMode.BRIGHTNESS} + supports_brigthness = self._supports_brightness() + + if self._attr_supported_color_modes is None and supports_brigthness: + self._attr_supported_color_modes = {ColorMode.BRIGHTNESS} if attr := self.get_matter_attribute(clusters.OnOff.Attributes.OnOff): self._attr_is_on = attr.value - if ( - clusters.LevelControl.Attributes.CurrentLevel - in self.entity_description.subscribe_attributes - ): + if supports_brigthness: level_control = self._device_type_instance.get_cluster( clusters.LevelControl ) + # We check above that the device supports brightness, ie level control. + assert level_control is not None # Convert brightness to Home Assistant = 0..255 self._attr_brightness = round( diff --git a/homeassistant/components/matter/manifest.json b/homeassistant/components/matter/manifest.json index 280a6b38da5..129110ba519 100644 --- a/homeassistant/components/matter/manifest.json +++ b/homeassistant/components/matter/manifest.json @@ -3,8 +3,8 @@ "name": "Matter (BETA)", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/matter", - "requirements": ["python-matter-server==1.0.7"], + "requirements": ["python-matter-server==1.0.8"], "dependencies": ["websocket_api"], - "codeowners": ["@MartinHjelmare", "@marcelveldt"], + "codeowners": ["@home-assistant/matter"], "iot_class": "local_push" } diff --git a/homeassistant/components/matter/sensor.py b/homeassistant/components/matter/sensor.py index 2b659fc1304..38a701e779a 100644 --- a/homeassistant/components/matter/sensor.py +++ b/homeassistant/components/matter/sensor.py @@ -4,7 +4,7 @@ from __future__ import annotations from collections.abc import Callable from dataclasses import dataclass from functools import partial -from typing import TYPE_CHECKING, Any +from typing import Any from chip.clusters import Objects as clusters from chip.clusters.Types import Nullable, NullValue @@ -21,19 +21,16 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( LIGHT_LUX, PERCENTAGE, - VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR, Platform, UnitOfPressure, UnitOfTemperature, + UnitOfVolumeFlowRate, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .const import DOMAIN from .entity import MatterEntity, MatterEntityDescriptionBaseClass - -if TYPE_CHECKING: - from .adapter import MatterAdapter +from .helpers import get_matter async def async_setup_entry( @@ -42,7 +39,7 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up Matter sensors from Config Entry.""" - matter: MatterAdapter = hass.data[DOMAIN][config_entry.entry_id] + matter = get_matter(hass) matter.register_platform_handler(Platform.SENSOR, async_add_entities) @@ -142,7 +139,7 @@ DEVICE_ENTITY: dict[ name="Flow", measurement_to_ha=lambda x: x / 10, subscribe_attributes=(clusters.FlowMeasurement.Attributes.MeasuredValue,), - native_unit_of_measurement=VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR, + native_unit_of_measurement=UnitOfVolumeFlowRate.CUBIC_METERS_PER_HOUR, ), device_types.HumiditySensor: MatterSensorEntityDescriptionFactory( key=device_types.HumiditySensor, diff --git a/homeassistant/components/matter/services.yaml b/homeassistant/components/matter/services.yaml index 18bb4be6452..ff59a7efe63 100644 --- a/homeassistant/components/matter/services.yaml +++ b/homeassistant/components/matter/services.yaml @@ -1,57 +1,3 @@ -commission: - name: Commission device - description: > - Add a new device to your Matter network. - fields: - code: - name: Pairing code - description: The pairing code for the device. - required: true - selector: - text: -accept_shared_device: - name: Accept shared device - description: > - Add a shared device to your Matter network. - fields: - pin: - name: Pin code - description: The pin code for the device. - required: true - selector: - text: - -set_wifi: - name: Set Wi-Fi credentials - description: > - The Wi-Fi credentials will be sent as part of commissioning to a Matter device so it can connect to the Wi-Fi network. - fields: - ssid: - name: Network name - description: The SSID network name. - required: true - selector: - text: - password: - name: Password - description: The Wi-Fi network password. - required: true - selector: - text: - type: password -set_thread: - name: Set Thread network operational dataset - description: > - The Thread keys will be used as part of commissioning to let a Matter device join the Thread network. - - Get keys by running `ot-ctl dataset active -x` on the Open Thread Border Router. - fields: - thread_operation_dataset: - name: Thread Operational Dataset - description: The Thread Operational Dataset to set. - required: true - selector: - text: open_commissioning_window: name: Open Commissioning Window description: > diff --git a/homeassistant/components/matter/switch.py b/homeassistant/components/matter/switch.py index b7c2b999918..f86a7cbb023 100644 --- a/homeassistant/components/matter/switch.py +++ b/homeassistant/components/matter/switch.py @@ -3,7 +3,7 @@ from __future__ import annotations from dataclasses import dataclass from functools import partial -from typing import TYPE_CHECKING, Any +from typing import Any from chip.clusters import Objects as clusters from matter_server.common.models import device_types @@ -18,11 +18,8 @@ from homeassistant.const import Platform from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .const import DOMAIN from .entity import MatterEntity, MatterEntityDescriptionBaseClass - -if TYPE_CHECKING: - from .adapter import MatterAdapter +from .helpers import get_matter async def async_setup_entry( @@ -31,7 +28,7 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up Matter switches from Config Entry.""" - matter: MatterAdapter = hass.data[DOMAIN][config_entry.entry_id] + matter = get_matter(hass) matter.register_platform_handler(Platform.SWITCH, async_add_entities) @@ -59,7 +56,8 @@ class MatterSwitch(MatterEntity, SwitchEntity): @callback def _update_from_device(self) -> None: """Update from device.""" - self._attr_is_on = self._device_type_instance.get_cluster(clusters.OnOff).onOff + cluster = self._device_type_instance.get_cluster(clusters.OnOff) + self._attr_is_on = cluster.onOff if cluster else None @dataclass diff --git a/homeassistant/components/matter/translations/bg.json b/homeassistant/components/matter/translations/bg.json new file mode 100644 index 00000000000..ad53db4720a --- /dev/null +++ b/homeassistant/components/matter/translations/bg.json @@ -0,0 +1,38 @@ +{ + "config": { + "abort": { + "addon_info_failed": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0432\u0430\u043d\u0435 \u043d\u0430 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u0437\u0430 \u0434\u043e\u0431\u0430\u0432\u043a\u0430\u0442\u0430 Matter Server.", + "addon_install_failed": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0438\u043d\u0441\u0442\u0430\u043b\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u0434\u043e\u0431\u0430\u0432\u043a\u0430\u0442\u0430 Matter Server.", + "addon_start_failed": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0442\u0430\u0440\u0442\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u0434\u043e\u0431\u0430\u0432\u043a\u0430\u0442\u0430 Matter Server.", + "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", + "not_matter_addon": "\u041e\u0442\u043a\u0440\u0438\u0442\u0430\u0442\u0430 \u0434\u043e\u0431\u0430\u0432\u043a\u0430 \u043d\u0435 \u0435 \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u043d\u0430\u0442\u0430 \u0434\u043e\u0431\u0430\u0432\u043a\u0430 Matter Server.", + "reconfiguration_successful": "\u0423\u0441\u043f\u0435\u0448\u043d\u043e \u043f\u0440\u0435\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f\u0442\u0430 Matter." + }, + "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", + "invalid_server_version": "\u0421\u044a\u0440\u0432\u044a\u0440\u044a\u0442 Matter \u043d\u0435 \u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u043d\u0430\u0442\u0430 \u0432\u0435\u0440\u0441\u0438\u044f", + "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" + }, + "flow_title": "{name}", + "progress": { + "install_addon": "\u041c\u043e\u043b\u044f, \u0438\u0437\u0447\u0430\u043a\u0430\u0439\u0442\u0435, \u0434\u043e\u043a\u0430\u0442\u043e \u0437\u0430\u0432\u044a\u0440\u0448\u0438 \u0438\u043d\u0441\u0442\u0430\u043b\u0438\u0440\u0430\u043d\u0435\u0442\u043e \u043d\u0430 \u0434\u043e\u0431\u0430\u0432\u043a\u0430\u0442\u0430 Matter Server. \u0422\u043e\u0432\u0430 \u043c\u043e\u0436\u0435 \u0434\u0430 \u043e\u0442\u043d\u0435\u043c\u0435 \u043d\u044f\u043a\u043e\u043b\u043a\u043e \u043c\u0438\u043d\u0443\u0442\u0438.", + "start_addon": "\u041c\u043e\u043b\u044f, \u0438\u0437\u0447\u0430\u043a\u0430\u0439\u0442\u0435, \u0434\u043e\u043a\u0430\u0442\u043e \u0441\u0442\u0430\u0440\u0442\u0438\u0440\u0430 \u0434\u043e\u0431\u0430\u0432\u043a\u0430\u0442\u0430 Matter Server. \u0422\u0430\u0437\u0438 \u0434\u043e\u0431\u0430\u0432\u043a\u0430 \u0435 \u0442\u043e\u0432\u0430, \u043a\u043e\u0435\u0442\u043e \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0432\u0430 Matter \u0432 Home Assistant. \u0422\u043e\u0432\u0430 \u043c\u043e\u0436\u0435 \u0434\u0430 \u043e\u0442\u043d\u0435\u043c\u0435 \u043d\u044f\u043a\u043e\u043b\u043a\u043e \u0441\u0435\u043a\u0443\u043d\u0434\u0438." + }, + "step": { + "install_addon": { + "title": "\u0418\u043d\u0441\u0442\u0430\u043b\u0438\u0440\u0430\u043d\u0435\u0442\u043e \u043d\u0430 \u0434\u043e\u0431\u0430\u0432\u043a\u0430\u0442\u0430 \u0437\u0430\u043f\u043e\u0447\u043d\u0430" + }, + "manual": { + "data": { + "url": "URL" + } + }, + "on_supervisor": { + "title": "\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u043c\u0435\u0442\u043e\u0434 \u043d\u0430 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" + }, + "start_addon": { + "title": "\u0421\u0442\u0430\u0440\u0442\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u0434\u043e\u0431\u0430\u0432\u043a\u0430\u0442\u0430." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/matter/translations/ca.json b/homeassistant/components/matter/translations/ca.json new file mode 100644 index 00000000000..d0e13073e10 --- /dev/null +++ b/homeassistant/components/matter/translations/ca.json @@ -0,0 +1,47 @@ +{ + "config": { + "abort": { + "addon_get_discovery_info_failed": "No s'ha pogut obtenir la informaci\u00f3 de descobriment del complement Servidor Matter.", + "addon_info_failed": "No s'ha pogut obtenir la informaci\u00f3 del complement Servidor Matter.", + "addon_install_failed": "No s'ha pogut instal\u00b7lar el complement Servidor Matter.", + "addon_start_failed": "No s'ha pogut iniciar el complement Servidor Matter.", + "already_configured": "El dispositiu ja est\u00e0 configurat", + "already_in_progress": "El flux de configuraci\u00f3 ja est\u00e0 en curs", + "not_matter_addon": "El complement descobert no \u00e9s el Servidor Matter oficial.", + "reconfiguration_successful": "Integraci\u00f3 Matter reconfigurada correctament." + }, + "error": { + "cannot_connect": "Ha fallat la connexi\u00f3", + "invalid_server_version": "El servidor de Matter no t\u00e9 la versi\u00f3 correcta", + "unknown": "Error inesperat" + }, + "flow_title": "{name}", + "progress": { + "install_addon": "Espera mentre finalitza la instal\u00b7laci\u00f3 del complement Servidor Matter. Pot tardar uns minuts.", + "start_addon": "Espera mentre es completa la inicialitzaci\u00f3 del complement Servidor Matter. Aquest complement \u00e9s el que permet utilitzar Matter dins Home Assistant. Espera uns segons." + }, + "step": { + "hassio_confirm": { + "title": "Configuraci\u00f3 de la integraci\u00f3 Matter amb el complement Servidor Matter" + }, + "install_addon": { + "title": "Ha comen\u00e7at la instal\u00b7laci\u00f3 del complement" + }, + "manual": { + "data": { + "url": "URL" + } + }, + "on_supervisor": { + "data": { + "use_addon": "Utilitza el complement oficial del Supervisor, Servidor Matter" + }, + "description": "Vols utilitzar el complement oficial Servidor Matter Supervisor? \n\nSi ja est\u00e0s executant el Servidor Matter en un altre complement, contenidor personalitzat, o de manera nativa, etc. NO seleccionis aquesta opci\u00f3.", + "title": "Selecci\u00f3 del m\u00e8tode de connexi\u00f3" + }, + "start_addon": { + "title": "Iniciant complement." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/matter/translations/cs.json b/homeassistant/components/matter/translations/cs.json new file mode 100644 index 00000000000..bda99809565 --- /dev/null +++ b/homeassistant/components/matter/translations/cs.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno", + "already_in_progress": "Konfigurace ji\u017e prob\u00edh\u00e1" + }, + "error": { + "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", + "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" + }, + "step": { + "manual": { + "data": { + "url": "URL" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/matter/translations/de.json b/homeassistant/components/matter/translations/de.json new file mode 100644 index 00000000000..7c5c040b77d --- /dev/null +++ b/homeassistant/components/matter/translations/de.json @@ -0,0 +1,47 @@ +{ + "config": { + "abort": { + "addon_get_discovery_info_failed": "Matter Server Add-on Erkennungsinformationen konnten nicht abgerufen werden.", + "addon_info_failed": "Matter Server Add-On Informationen konnten nicht abgerufen werden.", + "addon_install_failed": "Das Matter Server Add-on konnte nicht installiert werden.", + "addon_start_failed": "Das Matter Server Add-on konnte nicht gestartet werden.", + "already_configured": "Ger\u00e4t ist bereits konfiguriert", + "already_in_progress": "Der Konfigurationsablauf wird bereits ausgef\u00fchrt", + "not_matter_addon": "Das entdeckte Add-on ist nicht das offizielle Matter Server Add-on.", + "reconfiguration_successful": "Matter-Integration erfolgreich neu konfiguriert." + }, + "error": { + "cannot_connect": "Verbindung fehlgeschlagen", + "invalid_server_version": "Der Matter-Server ist nicht die richtige Version", + "unknown": "Unerwarteter Fehler" + }, + "flow_title": "{name}", + "progress": { + "install_addon": "Bitte warte, w\u00e4hrend die Installation des Matter Server Add-Ons abgeschlossen wird. Dies kann einige Minuten dauern.", + "start_addon": "Bitte warten, w\u00e4hrend das Matter Server Add-on startet. Dieses Add-on ist das, was Matter in Home Assistant antreibt. Dies kann einige Sekunden dauern." + }, + "step": { + "hassio_confirm": { + "title": "Einrichten der Matter-Integration mit dem Matter Server Add-on" + }, + "install_addon": { + "title": "Die Installation des Add-ons hat begonnen" + }, + "manual": { + "data": { + "url": "URL" + } + }, + "on_supervisor": { + "data": { + "use_addon": "Verwende das offizielle Matter Server Supervisor Add-on" + }, + "description": "M\u00f6chtest du das offizielle Matter Server Supervisor Add-on verwenden?\n\nWenn du den Matter Server bereits in einem anderen Add-on, in einem benutzerdefinierten Container, nativ etc. betreibst, dann w\u00e4hle diese Option nicht.", + "title": "Verbindungsmethode w\u00e4hlen" + }, + "start_addon": { + "title": "Add-on starten." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/matter/translations/el.json b/homeassistant/components/matter/translations/el.json new file mode 100644 index 00000000000..8ae15708cf3 --- /dev/null +++ b/homeassistant/components/matter/translations/el.json @@ -0,0 +1,47 @@ +{ + "config": { + "abort": { + "addon_get_discovery_info_failed": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03bb\u03ae\u03c8\u03b7\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03b9\u03ce\u03bd \u03b5\u03cd\u03c1\u03b5\u03c3\u03b7\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf\u03c5 \u03b4\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ae Matter.", + "addon_info_failed": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03bb\u03ae\u03c8\u03b7\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03b9\u03ce\u03bd \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf\u03c5 \u03b4\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ae Matter.", + "addon_install_failed": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03b5\u03b3\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 Matter Server \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf\u03c5.", + "addon_start_failed": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03b5\u03ba\u03ba\u03af\u03bd\u03b7\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 Matter Server \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf\u03c5.", + "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", + "already_in_progress": "\u0394\u03b9\u03b1\u03b4\u03b9\u03ba\u03b1\u03c3\u03af\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7\u03c2 \u03b2\u03c1\u03af\u03c3\u03ba\u03b5\u03c4\u03b1\u03b9 \u03ae\u03b4\u03b7 \u03c3\u03b5 \u03b5\u03be\u03ad\u03bb\u03b9\u03be\u03b7", + "not_matter_addon": "\u03a4\u03bf \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf \u03c0\u03bf\u03c5 \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b5 \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03c4\u03bf \u03b5\u03c0\u03af\u03c3\u03b7\u03bc\u03bf Matter Server \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf", + "reconfiguration_successful": "\u0397 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 Matter \u03b1\u03bd\u03b1\u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03ce\u03c6\u03b7\u03ba\u03b5 \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03ce\u03c2" + }, + "error": { + "cannot_connect": "\u0397 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03b4\u03b5\u03bd \u03ae\u03c4\u03b1\u03bd \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae", + "invalid_server_version": "\u039f \u03b4\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ae\u03c2 Matter \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b7 \u03c3\u03c9\u03c3\u03c4\u03ae \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7", + "unknown": "\u039c\u03b7 \u03b1\u03bd\u03b1\u03bc\u03b5\u03bd\u03cc\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" + }, + "flow_title": "{name}", + "progress": { + "install_addon": "\u03a0\u03b5\u03c1\u03b9\u03bc\u03ad\u03bd\u03b5\u03c4\u03b5 \u03bc\u03ad\u03c7\u03c1\u03b9 \u03bd\u03b1 \u03bf\u03bb\u03bf\u03ba\u03bb\u03b7\u03c1\u03c9\u03b8\u03b5\u03af \u03b7 \u03b5\u03b3\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf\u03c5 Matter Server. \u0391\u03c5\u03c4\u03cc \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03b4\u03b9\u03b1\u03c1\u03ba\u03ad\u03c3\u03b5\u03b9 \u03b1\u03c1\u03ba\u03b5\u03c4\u03ac \u03bb\u03b5\u03c0\u03c4\u03ac.", + "start_addon": "\u03a0\u03b5\u03c1\u03b9\u03bc\u03ad\u03bd\u03b5\u03c4\u03b5 \u03bc\u03ad\u03c7\u03c1\u03b9 \u03bd\u03b1 \u03be\u03b5\u03ba\u03b9\u03bd\u03ae\u03c3\u03b5\u03b9 \u03c4\u03bf \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf Matter Server. \u0391\u03c5\u03c4\u03cc \u03c4\u03bf \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf \u03b5\u03af\u03bd\u03b1\u03b9 \u03b1\u03c5\u03c4\u03cc \u03c0\u03bf\u03c5 \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03bf\u03c4\u03b5\u03af \u03c4\u03bf Matter in Home Assistant. \u0391\u03c5\u03c4\u03cc \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03b4\u03b9\u03b1\u03c1\u03ba\u03ad\u03c3\u03b5\u03b9 \u03bc\u03b5\u03c1\u03b9\u03ba\u03ac \u03b4\u03b5\u03c5\u03c4\u03b5\u03c1\u03cc\u03bb\u03b5\u03c0\u03c4\u03b1." + }, + "step": { + "hassio_confirm": { + "title": "\u03a1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 Matter \u03bc\u03b5 \u03c4\u03bf \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf Matter Server" + }, + "install_addon": { + "title": "\u0397 \u03b5\u03b3\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf\u03c5 \u03be\u03b5\u03ba\u03af\u03bd\u03b7\u03c3\u03b5" + }, + "manual": { + "data": { + "url": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL" + } + }, + "on_supervisor": { + "data": { + "use_addon": "\u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf \u03b5\u03c0\u03af\u03c3\u03b7\u03bc\u03bf \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf Matter Server Supervisor" + }, + "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf \u03b5\u03c0\u03af\u03c3\u03b7\u03bc\u03bf \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf Matter Server Supervisor; \n\n \u0395\u03ac\u03bd \u03b5\u03ba\u03c4\u03b5\u03bb\u03b5\u03af\u03c4\u03b5 \u03ae\u03b4\u03b7 \u03c4\u03bf\u03bd Matter Server \u03c3\u03b5 \u03ac\u03bb\u03bb\u03bf \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf, \u03c3\u03b5 \u03c0\u03c1\u03bf\u03c3\u03b1\u03c1\u03bc\u03bf\u03c3\u03bc\u03ad\u03bd\u03bf \u03ba\u03bf\u03bd\u03c4\u03ad\u03b9\u03bd\u03b5\u03c1, \u03b5\u03b3\u03b3\u03b5\u03bd\u03ce\u03c2 \u03ba.\u03bb\u03c0., \u03c4\u03cc\u03c4\u03b5 \u03bc\u03b7\u03bd \u03bf\u03c1\u03af\u03c3\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03ae\u03bd \u03c4\u03b7\u03bd \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae.", + "title": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03bc\u03ad\u03b8\u03bf\u03b4\u03bf \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2" + }, + "start_addon": { + "title": "\u0388\u03bd\u03b1\u03c1\u03be\u03b7 \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf\u03c5." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/matter/translations/es.json b/homeassistant/components/matter/translations/es.json new file mode 100644 index 00000000000..3341397c114 --- /dev/null +++ b/homeassistant/components/matter/translations/es.json @@ -0,0 +1,47 @@ +{ + "config": { + "abort": { + "addon_get_discovery_info_failed": "No se pudo obtener la informaci\u00f3n de descubrimiento del complemento Matter Server.", + "addon_info_failed": "No se pudo obtener la informaci\u00f3n del complemento Matter Server.", + "addon_install_failed": "No se pudo instalar el complemento Matter Server.", + "addon_start_failed": "No se pudo iniciar el complemento Matter Server.", + "already_configured": "El dispositivo ya est\u00e1 configurado", + "already_in_progress": "El flujo de configuraci\u00f3n ya est\u00e1 en curso", + "not_matter_addon": "El complemento descubierto no es el complemento oficial Matter Server.", + "reconfiguration_successful": "Se volvi\u00f3 a configurar con \u00e9xito la integraci\u00f3n de Matter." + }, + "error": { + "cannot_connect": "No se pudo conectar", + "invalid_server_version": "El servidor de Matter no es la versi\u00f3n correcta", + "unknown": "Error inesperado" + }, + "flow_title": "{name}", + "progress": { + "install_addon": "Por favor, espera mientras finaliza la instalaci\u00f3n del complemento Matter Server. Esto puede tardar varios minutos.", + "start_addon": "Por favor, espera mientras se inicia el complemento Matter Server. Este complemento es lo que alimenta a Matter en Home Assistant. Esto puede tardar unos segundos." + }, + "step": { + "hassio_confirm": { + "title": "Configurar la integraci\u00f3n de Matter con el complemento Matter Server" + }, + "install_addon": { + "title": "La instalaci\u00f3n del complemento ha comenzado." + }, + "manual": { + "data": { + "url": "URL" + } + }, + "on_supervisor": { + "data": { + "use_addon": "Utiliza el complemento oficial del supervisor Matter Server" + }, + "description": "\u00bfQuieres utilizar el complemento oficial del supervisor Matter Server? \n\nSi ya est\u00e1s ejecutando Matter Server en otro complemento, en un contenedor personalizado, de forma nativa, etc., no selecciones esta opci\u00f3n.", + "title": "Selecciona el m\u00e9todo de conexi\u00f3n" + }, + "start_addon": { + "title": "Iniciando el complemento." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/matter/translations/et.json b/homeassistant/components/matter/translations/et.json new file mode 100644 index 00000000000..4f064f50931 --- /dev/null +++ b/homeassistant/components/matter/translations/et.json @@ -0,0 +1,47 @@ +{ + "config": { + "abort": { + "addon_get_discovery_info_failed": "Matter Serveri lisandmooduli tuvastusteabe hankimine nurjus.", + "addon_info_failed": "Matter Serveri lisandmooduli teabe hankimine nurjus.", + "addon_install_failed": "Matter Serveri lisandmooduli paigaldamine nurjus.", + "addon_start_failed": "Matter Serveri lisandmooduli k\u00e4ivitamine nurjus.", + "already_configured": "Seade on juba h\u00e4\u00e4lestatud", + "already_in_progress": "Seadistamine on juba k\u00e4imas", + "not_matter_addon": "Avastatud lisandmoodul ei ole ametlik Matter Serveri lisandmoodul.", + "reconfiguration_successful": "Matteri sidumise uuesti seadistamine \u00f5nnestus." + }, + "error": { + "cannot_connect": "\u00dchendamine nurjus", + "invalid_server_version": "Matter'i server ei ole \u00f5ige versioon", + "unknown": "Ootamatu t\u00f5rge" + }, + "flow_title": "{name}", + "progress": { + "install_addon": "Oota kuni Matter Serveri lisandmooduli paigaldamine l\u00f5peb. See v\u00f5ib v\u00f5tta mitu minutit.", + "start_addon": "Oota kuni Matter Serveri lisandmoodul k\u00e4ivitub. See lisandmoodul annab Home Assistantile Matter funktsiooni. See v\u00f5ib v\u00f5tta m\u00f5ne sekundi." + }, + "step": { + "hassio_confirm": { + "title": "Seadista Matter'i sidumine Matter Serveri lisandmooduliga" + }, + "install_addon": { + "title": "Lisandmooduli paigaldamine on alanud" + }, + "manual": { + "data": { + "url": "URL" + } + }, + "on_supervisor": { + "data": { + "use_addon": "Kasuta ametlikku Matter Server Supervisori lisandmoodulit" + }, + "description": "Kas soovid kasutada ametlikku Matter Server Supervisori lisandmoodulit? \n\n Kui kasutad Matter Serverit juba m\u00f5nes teises lisandmoodulis, kohandatud konteineris, algselt jne, siis \u00e4ra seda valikut vali.", + "title": "Vali \u00fchendusviis" + }, + "start_addon": { + "title": "Lisandmooduli k\u00e4ivitamine." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/matter/translations/he.json b/homeassistant/components/matter/translations/he.json new file mode 100644 index 00000000000..449107febb0 --- /dev/null +++ b/homeassistant/components/matter/translations/he.json @@ -0,0 +1,47 @@ +{ + "config": { + "abort": { + "addon_get_discovery_info_failed": "\u05db\u05d9\u05e9\u05dc\u05d5\u05df \u05d1\u05e7\u05d1\u05dc\u05ea \u05de\u05d9\u05d3\u05e2 \u05e2\u05dc \u05d2\u05d9\u05dc\u05d5\u05d9 \u05d4\u05d4\u05e8\u05d7\u05d1\u05d4 \u05e9\u05e8\u05ea Matter.", + "addon_info_failed": "\u05db\u05d9\u05e9\u05dc\u05d5\u05df \u05d1\u05e7\u05d1\u05dc\u05ea \u05de\u05d9\u05d3\u05e2 \u05e2\u05dc \u05d4\u05d4\u05e8\u05d7\u05d1\u05d4 \u05e9\u05e8\u05ea Matter.", + "addon_install_failed": "\u05d4\u05ea\u05e7\u05e0\u05ea \u05d4\u05d4\u05e8\u05d7\u05d1\u05d4 \u05e9\u05e8\u05ea Matter \u05e0\u05db\u05e9\u05dc\u05d4.", + "addon_start_failed": "\u05d4\u05e4\u05e2\u05dc\u05ea \u05d4\u05d4\u05e8\u05d7\u05d1\u05d4 \u05e9\u05e8\u05ea Matter \u05e0\u05db\u05e9\u05dc\u05d4.", + "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", + "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", + "not_matter_addon": "\u05d4\u05ea\u05d5\u05e1\u05e3 \u05e9\u05d4\u05ea\u05d2\u05dc\u05d4 \u05d0\u05d9\u05e0\u05d5 \u05d4\u05ea\u05d5\u05e1\u05e3 \u05d4\u05e8\u05e9\u05de\u05d9 \u05e9\u05dc \u05e9\u05e8\u05ea Matter.", + "reconfiguration_successful": "\u05d4\u05d2\u05d3\u05d9\u05e8 \u05de\u05d7\u05d3\u05e9 \u05d1\u05d4\u05e6\u05dc\u05d7\u05d4 \u05d0\u05ea \u05e9\u05d9\u05dc\u05d5\u05d1 Matter." + }, + "error": { + "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", + "invalid_server_version": "\u05e9\u05e8\u05ea Matter \u05d0\u05d9\u05e0\u05d5 \u05d4\u05d2\u05e8\u05e1\u05d4 \u05d4\u05e0\u05db\u05d5\u05e0\u05d4", + "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" + }, + "flow_title": "{name}", + "progress": { + "install_addon": "\u05d9\u05e9 \u05dc\u05d4\u05de\u05ea\u05d9\u05df \u05e2\u05d3 \u05e9\u05d4\u05ea\u05e7\u05e0\u05ea \u05d4\u05ea\u05d5\u05e1\u05e3 \u05e9\u05dc \u05e9\u05e8\u05ea Matter \u05ea\u05e1\u05ea\u05d9\u05d9\u05dd. \u05e4\u05e2\u05d5\u05dc\u05d4 \u05d6\u05d5 \u05e2\u05e9\u05d5\u05d9\u05d4 \u05dc\u05d4\u05d9\u05de\u05e9\u05da \u05de\u05e1\u05e4\u05e8 \u05d3\u05e7\u05d5\u05ea.", + "start_addon": "\u05d9\u05e9 \u05dc\u05d4\u05de\u05ea\u05d9\u05df \u05d1\u05d6\u05de\u05df \u05e9\u05d4\u05ea\u05d5\u05e1\u05e3 \u05e9\u05e8\u05ea Matter \u05de\u05d5\u05e4\u05e2\u05dc. \u05d4\u05e8\u05d7\u05d1\u05d4 \u05d6\u05d5 \u05d4\u05d9\u05d0 \u05de\u05d4 \u05e9\u05de\u05e4\u05e2\u05d9\u05dc \u05d0\u05ea Matter \u05d1-Home Assistant. \u05e4\u05e2\u05d5\u05dc\u05d4 \u05d6\u05d5 \u05e2\u05e9\u05d5\u05d9\u05d4 \u05dc\u05d4\u05d9\u05de\u05e9\u05da \u05de\u05e1\u05e4\u05e8 \u05e9\u05e0\u05d9\u05d5\u05ea." + }, + "step": { + "hassio_confirm": { + "title": "\u05d4\u05d2\u05d3\u05e8\u05ea \u05e9\u05d9\u05dc\u05d5\u05d1 Matter \u05e2\u05dd \u05d4\u05ea\u05d5\u05e1\u05e3 \u05e9\u05e8\u05ea Matter" + }, + "install_addon": { + "title": "\u05d4\u05ea\u05e7\u05e0\u05ea \u05d4\u05ea\u05d5\u05e1\u05e3 \u05d4\u05d7\u05dc\u05d4" + }, + "manual": { + "data": { + "url": "\u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8" + } + }, + "on_supervisor": { + "data": { + "use_addon": "\u05d9\u05e9 \u05dc\u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05ea\u05d5\u05e1\u05e3 \u05d4\u05e8\u05e9\u05de\u05d9 \u05e9\u05dc \u05de\u05e4\u05e7\u05d7 \u05e9\u05e8\u05ea Matter" + }, + "description": "\u05d4\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05ea\u05d5\u05e1\u05e3 \u05d4\u05e8\u05e9\u05de\u05d9 \u05e9\u05dc \u05de\u05e4\u05e7\u05d7 \u05e9\u05e8\u05ea Matter?\n\n\u05d0\u05dd \u05e9\u05e8\u05ea Matter \u05db\u05d1\u05e8 \u05de\u05d5\u05e4\u05e2\u05dc \u05d1\u05d4\u05e8\u05d7\u05d1\u05d4 \u05d0\u05d7\u05e8\u05ea, \u05d1\u05d2\u05d5\u05e8\u05dd \u05de\u05db\u05d9\u05dc \u05de\u05d5\u05ea\u05d0\u05dd \u05d0\u05d9\u05e9\u05d9\u05ea, \u05d1\u05d0\u05d5\u05e4\u05df \u05de\u05e7\u05d5\u05e8\u05d9 \u05d5\u05db\u05d5', \u05d0\u05d9\u05df \u05dc\u05d1\u05d7\u05d5\u05e8 \u05d1\u05d0\u05e4\u05e9\u05e8\u05d5\u05ea \u05d6\u05d5.", + "title": "\u05d1\u05d7\u05d9\u05e8\u05ea \u05e9\u05d9\u05d8\u05ea \u05d7\u05d9\u05d1\u05d5\u05e8" + }, + "start_addon": { + "title": "\u05d4\u05e4\u05e2\u05dc\u05ea \u05d4\u05e8\u05d7\u05d1\u05d4." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/matter/translations/hu.json b/homeassistant/components/matter/translations/hu.json new file mode 100644 index 00000000000..d343c5f41d9 --- /dev/null +++ b/homeassistant/components/matter/translations/hu.json @@ -0,0 +1,47 @@ +{ + "config": { + "abort": { + "addon_get_discovery_info_failed": "Nem siker\u00fclt lek\u00e9rni a Matter Server b\u0151v\u00edtm\u00e9ny felder\u00edt\u00e9si adatait.", + "addon_info_failed": "Nem siker\u00fclt lek\u00e9rni a Matter Server b\u0151v\u00edtm\u00e9ny adatait.", + "addon_install_failed": "Nem siker\u00fclt telep\u00edteni a Matter Server b\u0151v\u00edtm\u00e9nyt.", + "addon_start_failed": "Nem siker\u00fclt elind\u00edtani a Matter Server b\u0151v\u00edtm\u00e9nyt.", + "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", + "not_matter_addon": "A felfedezett b\u0151v\u00edtm\u00e9ny nem a hivatalos Matter Server b\u0151v\u00edtm\u00e9ny.", + "reconfiguration_successful": "A Matter integr\u00e1ci\u00f3 sikeresen \u00fajrakonfigur\u00e1lva." + }, + "error": { + "cannot_connect": "Sikertelen csatlakoz\u00e1s", + "invalid_server_version": "A Matter szerver verzi\u00f3ja nem a megfelel\u0151", + "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" + }, + "flow_title": "{name}", + "progress": { + "install_addon": "K\u00e9rj\u00fck, v\u00e1rjon, am\u00edg a Matter Server b\u0151v\u00edtm\u00e9ny telep\u00edt\u00e9se befejez\u0151dik. Ez t\u00f6bb percig is eltarthat.", + "start_addon": "K\u00e9rj\u00fck, v\u00e1rjon, am\u00edg a Matter Server b\u0151v\u00edtm\u00e9ny elindul. Ez a kieg\u00e9sz\u00edt\u0151 az, ami a Matter-t m\u0171k\u00f6dteti a Home Assistant-ben. Ez eltarthat n\u00e9h\u00e1ny egy kis ideig." + }, + "step": { + "hassio_confirm": { + "title": "A Matter integr\u00e1ci\u00f3j\u00e1nak be\u00e1ll\u00edt\u00e1sa a Matter Server b\u0151v\u00edtm\u00e9nnyel" + }, + "install_addon": { + "title": "A b\u0151v\u00edtm\u00e9ny telep\u00edt\u00e9se megkezd\u0151d\u00f6tt" + }, + "manual": { + "data": { + "url": "URL" + } + }, + "on_supervisor": { + "data": { + "use_addon": "A hivatalos Matter Server Supervisor kieg\u00e9sz\u00edt\u0151 haszn\u00e1lata" + }, + "description": "Szeretn\u00e9 haszn\u00e1lni a hivatalos Matter Server Supervisor kieg\u00e9sz\u00edt\u0151t?\n\nHa a Matter Server-t m\u00e1r egy m\u00e1sik b\u0151v\u00edtm\u00e9nyben, egy\u00e9ni kont\u00e9nerben, nat\u00edvan stb. futtatja, akkor ne v\u00e1lassza ezt a lehet\u0151s\u00e9get.", + "title": "V\u00e1lassza ki a csatlakoz\u00e1si m\u00f3dot" + }, + "start_addon": { + "title": "B\u0151v\u00edtm\u00e9ny ind\u00edt\u00e1sa." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/matter/translations/id.json b/homeassistant/components/matter/translations/id.json new file mode 100644 index 00000000000..0daebbc2c62 --- /dev/null +++ b/homeassistant/components/matter/translations/id.json @@ -0,0 +1,47 @@ +{ + "config": { + "abort": { + "addon_get_discovery_info_failed": "Gagal mendapatkan info penemuan add-on Matter Server.", + "addon_info_failed": "Gagal mendapatkan info add-on Matter Server.", + "addon_install_failed": "Gagal menginstal add-on Matter Server.", + "addon_start_failed": "Gagal memulai add-on Matter Server.", + "already_configured": "Perangkat sudah dikonfigurasi", + "already_in_progress": "Alur konfigurasi sedang berlangsung", + "not_matter_addon": "Add-on yang ditemukan bukanlah add-on Matter Server resmi.", + "reconfiguration_successful": "Berhasil mengonfigurasi ulang integrasi Matter." + }, + "error": { + "cannot_connect": "Gagal terhubung", + "invalid_server_version": "Matter Server bukan versi yang benar", + "unknown": "Kesalahan yang tidak diharapkan" + }, + "flow_title": "{name}", + "progress": { + "install_addon": "Harap tunggu hingga penginstalan add-on Matter Server selesai. Ini bisa memakan waktu beberapa saat.", + "start_addon": "Harap tunggu hingga add-on Matter Server memulai. Add-on ini adalah add-on yang mendukung Matter dalam Home Assistant. Ini mungkin perlu waktu beberapa saat." + }, + "step": { + "hassio_confirm": { + "title": "Siapkan integrasi Matter dengan add-on Matter Server" + }, + "install_addon": { + "title": "Instalasi add-on telah dimulai" + }, + "manual": { + "data": { + "url": "URL" + } + }, + "on_supervisor": { + "data": { + "use_addon": "Gunakan add-on Supervisor Matter Server resmi" + }, + "description": "Ingin menggunakan add-on resmi Supervisor Matter Server?\n\nJika Anda sudah menjalankan Matter Server di add-on lain, dalam kontainer khusus, secara native, atau lainnya., jangan pilih opsi ini.", + "title": "Pilih metode koneksi" + }, + "start_addon": { + "title": "Memulai add-on." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/matter/translations/it.json b/homeassistant/components/matter/translations/it.json new file mode 100644 index 00000000000..79989a92456 --- /dev/null +++ b/homeassistant/components/matter/translations/it.json @@ -0,0 +1,47 @@ +{ + "config": { + "abort": { + "addon_get_discovery_info_failed": "Impossibile ottenere informazioni sul rilevamento del componente aggiuntivo Matter Server.", + "addon_info_failed": "Impossibile ottenere le informazioni sul componente aggiuntivo di Matter Server.", + "addon_install_failed": "Impossibile installare il componente aggiuntivo Matter Server.", + "addon_start_failed": "Impossibile avviare il componente aggiuntivo Matter Server.", + "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato", + "already_in_progress": "Il flusso di configurazione \u00e8 gi\u00e0 in corso", + "not_matter_addon": "Il componente aggiuntivo rilevato non \u00e8 il componente aggiuntivo ufficiale di Matter Server.", + "reconfiguration_successful": "Riconfigurata con successo l'integrazione di Matter." + }, + "error": { + "cannot_connect": "Impossibile connettersi", + "invalid_server_version": "Il server Matter non \u00e8 la versione corretta", + "unknown": "Errore imprevisto" + }, + "flow_title": "{name}", + "progress": { + "install_addon": "Attendere il completamento dell'installazione del componente aggiuntivo Matter Server. Questo pu\u00f2 richiedere diversi minuti.", + "start_addon": "Attendi l'avvio del componente aggiuntivo Matter Server. Questo componente aggiuntivo \u00e8 ci\u00f2 che alimenta Matter in Home Assistant. Questo potrebbe richiedere alcuni secondi." + }, + "step": { + "hassio_confirm": { + "title": "Configurare l'integrazione di Matter con il componente aggiuntivo Matter Server" + }, + "install_addon": { + "title": "L'installazione del componente aggiuntivo \u00e8 iniziata" + }, + "manual": { + "data": { + "url": "URL" + } + }, + "on_supervisor": { + "data": { + "use_addon": "Utilizza il componente aggiuntivo ufficiale Matter Server Supervisor" + }, + "description": "Vuoi utilizzare il componente aggiuntivo ufficiale di Matter Server Supervisor?\n\nSe stai gi\u00e0 eseguendo il Matter Server in un altro componente aggiuntivo, in un contenitore personalizzato, in modo nativo ecc., Non selezionare questa opzione.", + "title": "Seleziona il metodo di connessione" + }, + "start_addon": { + "title": "Avvio del componente aggiuntivo." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/matter/translations/ko.json b/homeassistant/components/matter/translations/ko.json new file mode 100644 index 00000000000..f8cb7dec12f --- /dev/null +++ b/homeassistant/components/matter/translations/ko.json @@ -0,0 +1,47 @@ +{ + "config": { + "abort": { + "addon_get_discovery_info_failed": "Matter Server \uc560\ub4dc\uc628 \uac80\uc0c9 \uc815\ubcf4\ub97c \uac00\uc838\uc624\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4.", + "addon_info_failed": "Matter Server \uc560\ub4dc\uc628 \uc815\ubcf4\ub97c \uac00\uc838\uc624\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4.", + "addon_install_failed": "Matter Server \uc560\ub4dc\uc628\uc744 \uc124\uce58\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4.", + "addon_start_failed": "Matter Server \uc560\ub4dc\uc628\uc744 \uc2dc\uc791\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4.", + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", + "not_matter_addon": "\uac80\uc0c9\ub41c \uc560\ub4dc\uc628\uc740 \uacf5\uc2dd Matter Server \uc560\ub4dc\uc628\uc774 \uc544\ub2d9\ub2c8\ub2e4.", + "reconfiguration_successful": "Matter \ud1b5\ud569\uad6c\uc131\uc694\uc18c\ub97c \uc131\uacf5\uc801\uc73c\ub85c \uc7ac\uad6c\uc131\ud588\uc2b5\ub2c8\ub2e4." + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_server_version": "Matter \uc11c\ubc84\uc758 \ubc84\uc804\uc774 \ub9de\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "flow_title": "{name}", + "progress": { + "install_addon": "Matter Server \uc560\ub4dc\uc628 \uc124\uce58\uac00 \uc644\ub8cc\ub418\ub294 \ub3d9\uc548 \uc7a0\uc2dc \uae30\ub2e4\ub824 \uc8fc\uc2ed\uc2dc\uc624. \uba87 \ubd84\uc774 \uac78\ub9b4 \uc218 \uc788\uc2b5\ub2c8\ub2e4.", + "start_addon": "Matter Server \uc560\ub4dc\uc628\uc774 \uc2dc\uc791\ub418\ub294 \ub3d9\uc548 \uc7a0\uc2dc \uae30\ub2e4\ub824 \uc8fc\uc2ed\uc2dc\uc624. \uc774 \uc560\ub4dc\uc628\uc740 Home Assistant\uc5d0\uc11c Matter\ub97c \uc0ac\uc6a9\ud558\uac8c \ud569\ub2c8\ub2e4. \uba87 \ucd08 \uc815\ub3c4 \uac78\ub9b4 \uc218 \uc788\uc2b5\ub2c8\ub2e4." + }, + "step": { + "hassio_confirm": { + "title": "Matter Server \uc560\ub4dc\uc628\uc73c\ub85c Matter \ud1b5\ud569\uad6c\uc131\uc694\uc18c \uc124\uc815 \ud558\uae30" + }, + "install_addon": { + "title": "\uc560\ub4dc\uc628\uc744 \uc124\uce58 \uc911\uc785\ub2c8\ub2e4" + }, + "manual": { + "data": { + "url": "URL \uc8fc\uc18c" + } + }, + "on_supervisor": { + "data": { + "use_addon": "\uacf5\uc2dd Matt Server Supervisor \uc560\ub4dc\uc628 \uc0ac\uc6a9" + }, + "description": "\uacf5\uc2dd Matter Server Supervisor \uc560\ub4dc\uc628\uc744 \uc0ac\uc6a9\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c? \n\n\ub2e4\ub978 \uc560\ub4dc\uc628, \uc0ac\uc6a9\uc790 \uc815\uc758 \ucee8\ud14c\uc774\ub108 \ub4f1\uc5d0\uc11c Matter Server\ub97c \uc774\ubbf8 \uc2e4\ud589 \uc911\uc778 \uacbd\uc6b0 \uc774 \uc635\uc158\uc744 \uc120\ud0dd\ud558\uc9c0 \ub9c8\uc2ed\uc2dc\uc624.", + "title": "\uc5f0\uacb0 \ubc29\ubc95 \uc120\ud0dd" + }, + "start_addon": { + "title": "\uc560\ub4dc\uc628 \uc2dc\uc791" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/matter/translations/nl.json b/homeassistant/components/matter/translations/nl.json new file mode 100644 index 00000000000..6e9c1caf781 --- /dev/null +++ b/homeassistant/components/matter/translations/nl.json @@ -0,0 +1,47 @@ +{ + "config": { + "abort": { + "addon_get_discovery_info_failed": "Het ophalen van de Matter Server add-on detectie informatie is mislukt.", + "addon_info_failed": "Het ophalen van de Matter Server add-on informatie is mislukt.", + "addon_install_failed": "Installatie van de Matter Server add-on is mislukt.", + "addon_start_failed": "De Matter Server add-on is installatie is mislukt.", + "already_configured": "Apparaat is al geconfigureerd", + "already_in_progress": "De configuratie is momenteel al bezig", + "not_matter_addon": "De ontdekte add-on is niet de offici\u00eble Matter Server add-on.", + "reconfiguration_successful": "De Matter integratie is succesvol geconfigureerd." + }, + "error": { + "cannot_connect": "Kan geen verbinding maken", + "invalid_server_version": "De versie van Matter server is niet correct", + "unknown": "Onverwachte fout" + }, + "flow_title": "{name}", + "progress": { + "install_addon": "Een moment geduld, de Matter Server add-on voltooid de installatie. Dit kan enkele minuten duren.", + "start_addon": "Een moment geduld, terwijl de Matter Server add-on aan het opstarten is. Deze add-on maakt Matter mogelijk binnen Home Assistant. Het opstarten kan een paar seconden duren." + }, + "step": { + "hassio_confirm": { + "title": "Instellen van de Matter integratie met de Matter Server add-on" + }, + "install_addon": { + "title": "De add-on installatie is gestart" + }, + "manual": { + "data": { + "url": "URL" + } + }, + "on_supervisor": { + "data": { + "use_addon": "Gebruik de offici\u00eble Matter Server Supervisor add-on" + }, + "description": "Wil je de offici\u00eble Matter Server add-on gebruiken?\n\nWanneer je al een Matter Server uitvoert in via een andere add-on, native etc,, gebruik dan niet deze optie.", + "title": "Selecteer de verbindingsmethode" + }, + "start_addon": { + "title": "De add-on wordt gestart" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/matter/translations/no.json b/homeassistant/components/matter/translations/no.json new file mode 100644 index 00000000000..1475006b56d --- /dev/null +++ b/homeassistant/components/matter/translations/no.json @@ -0,0 +1,47 @@ +{ + "config": { + "abort": { + "addon_get_discovery_info_failed": "Kunne ikke hente oppdagelsesinformasjon for Matter Server-tillegg.", + "addon_info_failed": "Kunne ikke hente tilleggsinformasjon om Matter Server.", + "addon_install_failed": "Kunne ikke installere Matter Server-tillegget.", + "addon_start_failed": "Kunne ikke starte Matter Server-tillegget.", + "already_configured": "Enheten er allerede konfigurert", + "already_in_progress": "Konfigurasjonsflyten p\u00e5g\u00e5r allerede", + "not_matter_addon": "Discovered add-on er ikke det offisielle Matter Server-tillegget.", + "reconfiguration_successful": "Har konfigurert Matter-integrasjonen p\u00e5 nytt." + }, + "error": { + "cannot_connect": "Tilkobling mislyktes", + "invalid_server_version": "Matter-serveren er ikke riktig versjon", + "unknown": "Uventet feil" + }, + "flow_title": "{name}", + "progress": { + "install_addon": "Vent mens installasjonen av Matter Server-tillegget fullf\u00f8res. Dette kan ta flere minutter.", + "start_addon": "Vennligst vent mens Matter Server-tillegget starter. Dette tillegget er det som driver Matter i Home Assistant. Dette kan ta noen sekunder." + }, + "step": { + "hassio_confirm": { + "title": "Sett opp Matter-integrasjonen med Matter Server-tillegget" + }, + "install_addon": { + "title": "Tilleggsinstallasjonen har startet" + }, + "manual": { + "data": { + "url": "URL" + } + }, + "on_supervisor": { + "data": { + "use_addon": "Bruk det offisielle Matter Server Supervisor-tillegget" + }, + "description": "Vil du bruke det offisielle Matter Server Supervisor-tillegget? \n\n Hvis du allerede kj\u00f8rer Matter Server i et annet tillegg, i en egendefinert beholder, native etc., s\u00e5 ikke velg dette alternativet.", + "title": "Velg tilkoblingsmetode" + }, + "start_addon": { + "title": "Starter tillegg." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/matter/translations/pl.json b/homeassistant/components/matter/translations/pl.json new file mode 100644 index 00000000000..97878acd59d --- /dev/null +++ b/homeassistant/components/matter/translations/pl.json @@ -0,0 +1,47 @@ +{ + "config": { + "abort": { + "addon_get_discovery_info_failed": "Nie uda\u0142o si\u0119 uzyska\u0107 informacji o wykrywaniu dodatku Matter Server.", + "addon_info_failed": "Nie uda\u0142o si\u0119 uzyska\u0107 informacji o dodatku Matter Server.", + "addon_install_failed": "Nie uda\u0142o si\u0119 zainstalowa\u0107 dodatku Matter Server.", + "addon_start_failed": "Nie uda\u0142o si\u0119 uruchomi\u0107 dodatku Matter Server.", + "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane", + "already_in_progress": "Konfiguracja jest ju\u017c w toku", + "not_matter_addon": "Wykryty dodatek nie jest oficjalnym dodatkiem Matter Server.", + "reconfiguration_successful": "Pomy\u015blnie zmieniono konfiguracj\u0119 integracji Matter." + }, + "error": { + "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", + "invalid_server_version": "Serwer Matter nie ma prawid\u0142owej wersji", + "unknown": "Nieoczekiwany b\u0142\u0105d" + }, + "flow_title": "{name}", + "progress": { + "install_addon": "Poczekaj, a\u017c zako\u0144czy si\u0119 instalacja dodatku Matter Server. Mo\u017ce to potrwa\u0107 kilka minut.", + "start_addon": "Poczekaj na uruchomienie dodatku Matter Server. Ten dodatek nap\u0119dza Matter w Home Assistant. Mo\u017ce to potrwa\u0107 kilka sekund." + }, + "step": { + "hassio_confirm": { + "title": "Konfiguracja integracji Matter z dodatkiem Matter Server" + }, + "install_addon": { + "title": "Rozpocz\u0119\u0142a si\u0119 instalacja dodatku Matter Server" + }, + "manual": { + "data": { + "url": "URL" + } + }, + "on_supervisor": { + "data": { + "use_addon": "U\u017cyj oficjalnego dodatku Matter Server Supervisor" + }, + "description": "Czy chcesz skorzysta\u0107 z oficjalnego dodatku Matter Server Supervisor? \n\nJe\u015bli ju\u017c uruchamiasz Matter Server w innym dodatku, w niestandardowym kontenerze, natywnie itp., nie wybieraj tej opcji.", + "title": "Wybierz metod\u0119 po\u0142\u0105czenia" + }, + "start_addon": { + "title": "Uruchamianie dodatku" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/matter/translations/pt-BR.json b/homeassistant/components/matter/translations/pt-BR.json new file mode 100644 index 00000000000..767968e8e0b --- /dev/null +++ b/homeassistant/components/matter/translations/pt-BR.json @@ -0,0 +1,47 @@ +{ + "config": { + "abort": { + "addon_get_discovery_info_failed": "Falha ao obter informa\u00e7\u00f5es de descoberta de add-on do Matter Server.", + "addon_info_failed": "Falha ao obter informa\u00e7\u00f5es do add-on Matter Server.", + "addon_install_failed": "Falha ao instalar o add-on Matter Server.", + "addon_start_failed": "Falha ao iniciar o add-on Matter Server.", + "already_configured": "Dispositivo j\u00e1 est\u00e1 configurado", + "already_in_progress": "O fluxo de configura\u00e7\u00e3o j\u00e1 est\u00e1 em andamento", + "not_matter_addon": "O add-on descoberto n\u00e3o \u00e9 o add-on oficial do Matter Server.", + "reconfiguration_successful": "Reconfigura\u00e7\u00e3o bem-sucedida da integra\u00e7\u00e3o do Matter." + }, + "error": { + "cannot_connect": "Falha ao conectar", + "invalid_server_version": "O servidor Matter n\u00e3o \u00e9 a vers\u00e3o correta", + "unknown": "Erro inesperado" + }, + "flow_title": "{name}", + "progress": { + "install_addon": "Aguarde enquanto a instala\u00e7\u00e3o do add-on do Matter Server \u00e9 conclu\u00edda. Isso pode levar v\u00e1rios minutos.", + "start_addon": "Aguarde enquanto o add-on Matter Server \u00e9 iniciado. Este add-on \u00e9 o que capacita o Matter no Home Assistant. Isso pode levar alguns segundos." + }, + "step": { + "hassio_confirm": { + "title": "Configurar a integra\u00e7\u00e3o do Matter com o add-on Matter Server" + }, + "install_addon": { + "title": "A instala\u00e7\u00e3o do add-on foi iniciada" + }, + "manual": { + "data": { + "url": "URL" + } + }, + "on_supervisor": { + "data": { + "use_addon": "Use o add-on oficial Matter Server Supervisor" + }, + "description": "Deseja usar o add-on oficial Matter Server Supervisor? \n\n Se voc\u00ea j\u00e1 estiver executando o Matter Server em outro add-on, em um cont\u00eainer personalizado, nativamente etc., n\u00e3o selecione esta op\u00e7\u00e3o.", + "title": "Selecione o m\u00e9todo de conex\u00e3o" + }, + "start_addon": { + "title": "Iniciando add-on." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/matter/translations/pt.json b/homeassistant/components/matter/translations/pt.json new file mode 100644 index 00000000000..07d00b2527c --- /dev/null +++ b/homeassistant/components/matter/translations/pt.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", + "already_in_progress": "O processo de configura\u00e7\u00e3o j\u00e1 est\u00e1 a decorrer" + }, + "error": { + "cannot_connect": "A liga\u00e7\u00e3o falhou", + "unknown": "Erro inesperado" + }, + "step": { + "manual": { + "data": { + "url": "" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/matter/translations/ru.json b/homeassistant/components/matter/translations/ru.json new file mode 100644 index 00000000000..7d2fa272680 --- /dev/null +++ b/homeassistant/components/matter/translations/ru.json @@ -0,0 +1,47 @@ +{ + "config": { + "abort": { + "addon_get_discovery_info_failed": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u0434\u043b\u044f \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f Matter Server.", + "addon_info_failed": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0438 Matter Server.", + "addon_install_failed": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 Matter Server.", + "addon_start_failed": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 Matter Server.", + "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant.", + "already_in_progress": "\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f.", + "not_matter_addon": "\u041e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u043e \u043d\u0435\u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u0435 \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 Matter Server.", + "reconfiguration_successful": "\u041f\u0435\u0440\u0435\u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 Matter \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e." + }, + "error": { + "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", + "invalid_server_version": "\u041d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f \u0441\u0435\u0440\u0432\u0435\u0440\u0430 Matter", + "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." + }, + "flow_title": "{name}", + "progress": { + "install_addon": "\u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f Matter Server. \u042d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u043d\u044f\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043c\u0438\u043d\u0443\u0442.", + "start_addon": "\u041e\u0436\u0438\u0434\u0430\u0439\u0442\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f Matter Server. \u042d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u043d\u044f\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u0435\u043a\u0443\u043d\u0434." + }, + "step": { + "hassio_confirm": { + "title": "\u0418\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u0430 Matter \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f Matter Server" + }, + "install_addon": { + "title": "\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043d\u0430\u0447\u0430\u043b\u0430\u0441\u044c" + }, + "manual": { + "data": { + "url": "URL-\u0430\u0434\u0440\u0435\u0441" + } + }, + "on_supervisor": { + "data": { + "use_addon": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u0435 \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 Matter Server" + }, + "description": "\u0412\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u0435 \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 Matter Server?\n\n\u041d\u0435 \u0432\u044b\u0431\u0438\u0440\u0430\u0439\u0442\u0435 \u044d\u0442\u0443 \u043e\u043f\u0446\u0438\u044e \u0435\u0441\u043b\u0438 Matter Server \u0443\u0436\u0435 \u0437\u0430\u043f\u0443\u0449\u0435\u043d \u0432 \u0434\u0440\u0443\u0433\u043e\u043c \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0438, \u0432 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u043c \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0435, \u043d\u0430\u0442\u0438\u0432\u043d\u043e \u0438 \u0442.\u0434.", + "title": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u043f\u043e\u0441\u043e\u0431 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f" + }, + "start_addon": { + "title": "\u0417\u0430\u043f\u0443\u0441\u043a \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/matter/translations/sk.json b/homeassistant/components/matter/translations/sk.json new file mode 100644 index 00000000000..a83497baac5 --- /dev/null +++ b/homeassistant/components/matter/translations/sk.json @@ -0,0 +1,47 @@ +{ + "config": { + "abort": { + "addon_get_discovery_info_failed": "Nepodarilo sa z\u00edska\u0165 inform\u00e1cie o zis\u0165ovan\u00ed doplnku Matter Server.", + "addon_info_failed": "Nepodarilo sa z\u00edska\u0165 inform\u00e1cie o doplnku Matter Server.", + "addon_install_failed": "Nepodarilo sa nain\u0161talova\u0165 doplnok Matter Server.", + "addon_start_failed": "Nepodarilo sa spusti\u0165 doplnok Matter Server.", + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "not_matter_addon": "Doplnok Discovered nie je ofici\u00e1lnym doplnkom Matter Server.", + "reconfiguration_successful": "\u00daspe\u0161ne prekonfigurovan\u00e1 integr\u00e1cia Matter." + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_server_version": "Server Matter nie je spr\u00e1vna verzia", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "flow_title": "{name}", + "progress": { + "install_addon": "Po\u010dkajte, k\u00fdm sa dokon\u010d\u00ed in\u0161tal\u00e1cia doplnku Matter Server. M\u00f4\u017ee to trva\u0165 nieko\u013eko min\u00fat.", + "start_addon": "Po\u010dkajte, k\u00fdm sa spust\u00ed doplnok Matter Server. Tento doplnok je to, \u010do poh\u00e1\u0148a Matter v Home Assistant. M\u00f4\u017ee to trva\u0165 nieko\u013eko sek\u00fand." + }, + "step": { + "hassio_confirm": { + "title": "Nastavte integr\u00e1ciu Matter s doplnkom Matter Server" + }, + "install_addon": { + "title": "In\u0161tal\u00e1cia doplnku sa za\u010dala" + }, + "manual": { + "data": { + "url": "URL" + } + }, + "on_supervisor": { + "data": { + "use_addon": "Pou\u017eite ofici\u00e1lny doplnok Matter Server Supervisor" + }, + "description": "Chcete pou\u017ei\u0165 ofici\u00e1lny doplnok Matter Server Supervisor? \n\n Ak u\u017e Matter Server sp\u00fa\u0161\u0165ate v inom doplnku, vo vlastnom kontajneri, nat\u00edvne at\u010f., t\u00fato mo\u017enos\u0165 nevyberajte.", + "title": "Vyberte sp\u00f4sob pripojenia" + }, + "start_addon": { + "title": "Sp\u00fa\u0161\u0165a sa doplnok." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/matter/translations/zh-Hant.json b/homeassistant/components/matter/translations/zh-Hant.json new file mode 100644 index 00000000000..fa683640d64 --- /dev/null +++ b/homeassistant/components/matter/translations/zh-Hant.json @@ -0,0 +1,47 @@ +{ + "config": { + "abort": { + "addon_get_discovery_info_failed": "\u53d6\u5f97 Matter \u4f3a\u670d\u5668\u9644\u52a0\u5143\u4ef6\u641c\u7d22\u8cc7\u8a0a\u5931\u6557\u3002", + "addon_info_failed": "\u53d6\u5f97 Matter \u4f3a\u670d\u5668\u9644\u52a0\u5143\u4ef6\u8cc7\u8a0a\u5931\u6557\u3002", + "addon_install_failed": "\u5b89\u88dd Matter \u4f3a\u670d\u5668\u9644\u52a0\u5143\u4ef6\u5931\u6557", + "addon_start_failed": "\u555f\u52d5 Matter \u4f3a\u670d\u5668\u9644\u52a0\u5143\u4ef6\u5931\u6557", + "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", + "already_in_progress": "\u8a2d\u5b9a\u5df2\u7d93\u9032\u884c\u4e2d", + "not_matter_addon": "\u767c\u73fe\u4e4b\u9644\u52a0\u5143\u4ef6\u4e26\u975e\u5b98\u65b9 Matter \u4f3a\u670d\u5668\u9644\u52a0\u5143\u4ef6\u3002", + "reconfiguration_successful": "\u91cd\u65b0\u8a2d\u5b9a Matter \u6574\u5408\u6210\u529f\u3002" + }, + "error": { + "cannot_connect": "\u9023\u7dda\u5931\u6557", + "invalid_server_version": "Matter \u4f3a\u670d\u5668\u7248\u672c\u932f\u8aa4", + "unknown": "\u672a\u9810\u671f\u932f\u8aa4" + }, + "flow_title": "{name}", + "progress": { + "install_addon": "\u8acb\u7a0d\u7b49 Matter \u4f3a\u670d\u5668\u9644\u52a0\u5143\u4ef6\u5b89\u88dd\u5b8c\u6210\uff0c\u53ef\u80fd\u6703\u9700\u8981\u5e7e\u5206\u9418\u3002", + "start_addon": "\u8acb\u7a0d\u7b49 Matter \u4f3a\u670d\u5668\u9644\u52a0\u5143\u4ef6\u555f\u59cb\u5b8c\u6210\uff0c\u9644\u52a0\u5143\u4ef6\u70ba Home Assistant \u65b0\u589e Matter \u6838\u5fc3\u529f\u80fd\u3002\u53ef\u80fd\u6703\u9700\u8981\u5e7e\u5206\u9418\u3002" + }, + "step": { + "hassio_confirm": { + "title": "\u4ee5 Matter \u4f3a\u670d\u5668\u9644\u52a0\u5143\u4ef6\u8a2d\u5b9a Matter \u6574\u5408" + }, + "install_addon": { + "title": "\u9644\u52a0\u5143\u4ef6\u5b89\u88dd\u5df2\u555f\u52d5" + }, + "manual": { + "data": { + "url": "\u7db2\u5740" + } + }, + "on_supervisor": { + "data": { + "use_addon": "\u4f7f\u7528\u5b98\u65b9 Matter \u4f3a\u670d\u5668 Supervisor \u9644\u52a0\u5143\u4ef6" + }, + "description": "\u662f\u5426\u8981\u4f7f\u7528\u5b98\u65b9 Matter \u4f3a\u670d\u5668 Supervisor \u9644\u52a0\u5143\u4ef6\uff1f\n\n\u5047\u5982\u5df2\u7d93\u4f7f\u7528\u5176\u4ed6\u9644\u52a0\u5143\u4ef6\u3001Container \u6216\u539f\u751f\u65b9\u5f0f\u57f7\u884c Matter \u4f3a\u670d\u5668\u4e2d\uff0c\u8acb\u4e0d\u8981\u9078\u64c7\u6b64\u9078\u9805\u3002", + "title": "\u9078\u64c7\u9023\u7dda\u985e\u5225" + }, + "start_addon": { + "title": "\u555f\u52d5\u9644\u52a0\u5143\u4ef6\u3002" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/maxcube/__init__.py b/homeassistant/components/maxcube/__init__.py index 04a5ee97935..f8899ea082f 100644 --- a/homeassistant/components/maxcube/__init__.py +++ b/homeassistant/components/maxcube/__init__.py @@ -69,7 +69,10 @@ def setup(hass: HomeAssistant, config: ConfigType) -> bool: _LOGGER.error("Unable to connect to Max!Cube gateway: %s", str(ex)) persistent_notification.create( hass, - f"Error: {ex}
You will need to restart Home Assistant after fixing.", + ( + f"Error: {ex}
You will need to restart Home Assistant after" + " fixing." + ), title=NOTIFICATION_TITLE, notification_id=NOTIFICATION_ID, ) diff --git a/homeassistant/components/maxcube/climate.py b/homeassistant/components/maxcube/climate.py index 733736606f6..828696a9ff0 100644 --- a/homeassistant/components/maxcube/climate.py +++ b/homeassistant/components/maxcube/climate.py @@ -23,7 +23,7 @@ from homeassistant.components.climate import ( HVACAction, HVACMode, ) -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType @@ -79,7 +79,7 @@ class MaxCubeClimate(ClimateEntity): self._device = device self._attr_should_poll = True self._attr_unique_id = self._device.serial - self._attr_temperature_unit = TEMP_CELSIUS + self._attr_temperature_unit = UnitOfTemperature.CELSIUS self._attr_preset_modes = [ PRESET_NONE, PRESET_BOOST, diff --git a/homeassistant/components/mazda/__init__.py b/homeassistant/components/mazda/__init__.py index b62725e5b1b..403627147f0 100644 --- a/homeassistant/components/mazda/__init__.py +++ b/homeassistant/components/mazda/__init__.py @@ -33,13 +33,14 @@ from homeassistant.helpers.update_coordinator import ( UpdateFailed, ) -from .const import DATA_CLIENT, DATA_COORDINATOR, DATA_VEHICLES, DOMAIN +from .const import DATA_CLIENT, DATA_COORDINATOR, DATA_REGION, DATA_VEHICLES, DOMAIN _LOGGER = logging.getLogger(__name__) PLATFORMS = [ Platform.BINARY_SENSOR, Platform.BUTTON, + Platform.CLIMATE, Platform.DEVICE_TRACKER, Platform.LOCK, Platform.SENSOR, @@ -161,6 +162,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: vehicle["evStatus"] = await with_timeout( mazda_client.get_ev_vehicle_status(vehicle["id"]) ) + vehicle["hvacSetting"] = await with_timeout( + mazda_client.get_hvac_setting(vehicle["id"]) + ) hass.data[DOMAIN][entry.entry_id][DATA_VEHICLES] = vehicles @@ -185,6 +189,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass.data[DOMAIN][entry.entry_id] = { DATA_CLIENT: mazda_client, DATA_COORDINATOR: coordinator, + DATA_REGION: region, DATA_VEHICLES: [], } diff --git a/homeassistant/components/mazda/climate.py b/homeassistant/components/mazda/climate.py new file mode 100644 index 00000000000..e2028885c34 --- /dev/null +++ b/homeassistant/components/mazda/climate.py @@ -0,0 +1,187 @@ +"""Platform for Mazda climate integration.""" +from __future__ import annotations + +from typing import Any + +from pymazda import Client as MazdaAPIClient + +from homeassistant.components.climate import ( + ClimateEntity, + ClimateEntityFeature, + HVACMode, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import ( + ATTR_TEMPERATURE, + PRECISION_HALVES, + PRECISION_WHOLE, + UnitOfTemperature, +) +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator +from homeassistant.util.temperature import convert as convert_temperature + +from . import MazdaEntity +from .const import DATA_CLIENT, DATA_COORDINATOR, DATA_REGION, DOMAIN + +PRESET_DEFROSTER_OFF = "Defroster Off" +PRESET_DEFROSTER_FRONT = "Front Defroster" +PRESET_DEFROSTER_REAR = "Rear Defroster" +PRESET_DEFROSTER_FRONT_AND_REAR = "Front and Rear Defroster" + + +def _front_defroster_enabled(preset_mode: str | None) -> bool: + return preset_mode in [ + PRESET_DEFROSTER_FRONT_AND_REAR, + PRESET_DEFROSTER_FRONT, + ] + + +def _rear_defroster_enabled(preset_mode: str | None) -> bool: + return preset_mode in [ + PRESET_DEFROSTER_FRONT_AND_REAR, + PRESET_DEFROSTER_REAR, + ] + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up the climate platform.""" + entry_data = hass.data[DOMAIN][config_entry.entry_id] + client = entry_data[DATA_CLIENT] + coordinator = entry_data[DATA_COORDINATOR] + region = entry_data[DATA_REGION] + + async_add_entities( + MazdaClimateEntity(client, coordinator, index, region) + for index, data in enumerate(coordinator.data) + if data["isElectric"] + ) + + +class MazdaClimateEntity(MazdaEntity, ClimateEntity): + """Class for a Mazda climate entity.""" + + _attr_name = "Climate" + _attr_supported_features = ( + ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE + ) + _attr_hvac_modes = [HVACMode.HEAT_COOL, HVACMode.OFF] + _attr_preset_modes = [ + PRESET_DEFROSTER_OFF, + PRESET_DEFROSTER_FRONT, + PRESET_DEFROSTER_REAR, + PRESET_DEFROSTER_FRONT_AND_REAR, + ] + + def __init__( + self, + client: MazdaAPIClient, + coordinator: DataUpdateCoordinator, + index: int, + region: str, + ) -> None: + """Initialize Mazda climate entity.""" + super().__init__(client, coordinator, index) + + self.region = region + self._attr_unique_id = self.vin + + if self.data["hvacSetting"]["temperatureUnit"] == "F": + self._attr_precision = PRECISION_WHOLE + self._attr_temperature_unit = UnitOfTemperature.FAHRENHEIT + self._attr_min_temp = 61.0 + self._attr_max_temp = 83.0 + else: + self._attr_precision = PRECISION_HALVES + self._attr_temperature_unit = UnitOfTemperature.CELSIUS + if region == "MJO": + self._attr_min_temp = 18.5 + self._attr_max_temp = 31.5 + else: + self._attr_min_temp = 15.5 + self._attr_max_temp = 28.5 + + self._update_state_attributes() + + @callback + def _handle_coordinator_update(self) -> None: + """Update attributes when the coordinator data updates.""" + self._update_state_attributes() + + super()._handle_coordinator_update() + + def _update_state_attributes(self) -> None: + # Update the HVAC mode + hvac_on = self.client.get_assumed_hvac_mode(self.vehicle_id) + self._attr_hvac_mode = HVACMode.HEAT_COOL if hvac_on else HVACMode.OFF + + # Update the target temperature + hvac_setting = self.client.get_assumed_hvac_setting(self.vehicle_id) + self._attr_target_temperature = hvac_setting.get("temperature") + + # Update the current temperature + current_temperature_celsius = self.data["evStatus"]["hvacInfo"][ + "interiorTemperatureCelsius" + ] + if self.data["hvacSetting"]["temperatureUnit"] == "F": + self._attr_current_temperature = convert_temperature( + current_temperature_celsius, + UnitOfTemperature.CELSIUS, + UnitOfTemperature.FAHRENHEIT, + ) + else: + self._attr_current_temperature = current_temperature_celsius + + # Update the preset mode based on the state of the front and rear defrosters + front_defroster = hvac_setting.get("frontDefroster") + rear_defroster = hvac_setting.get("rearDefroster") + if front_defroster and rear_defroster: + self._attr_preset_mode = PRESET_DEFROSTER_FRONT_AND_REAR + elif front_defroster: + self._attr_preset_mode = PRESET_DEFROSTER_FRONT + elif rear_defroster: + self._attr_preset_mode = PRESET_DEFROSTER_REAR + else: + self._attr_preset_mode = PRESET_DEFROSTER_OFF + + async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None: + """Set a new HVAC mode.""" + if hvac_mode == HVACMode.HEAT_COOL: + await self.client.turn_on_hvac(self.vehicle_id) + elif hvac_mode == HVACMode.OFF: + await self.client.turn_off_hvac(self.vehicle_id) + + self._handle_coordinator_update() + + async def async_set_temperature(self, **kwargs: Any) -> None: + """Set a new target temperature.""" + if (temperature := kwargs.get(ATTR_TEMPERATURE)) is not None: + precision = self.precision + rounded_temperature = round(temperature / precision) * precision + + await self.client.set_hvac_setting( + self.vehicle_id, + rounded_temperature, + self.data["hvacSetting"]["temperatureUnit"], + _front_defroster_enabled(self._attr_preset_mode), + _rear_defroster_enabled(self._attr_preset_mode), + ) + + self._handle_coordinator_update() + + async def async_set_preset_mode(self, preset_mode: str) -> None: + """Turn on/off the front/rear defrosters according to the chosen preset mode.""" + await self.client.set_hvac_setting( + self.vehicle_id, + self._attr_target_temperature, + self.data["hvacSetting"]["temperatureUnit"], + _front_defroster_enabled(preset_mode), + _rear_defroster_enabled(preset_mode), + ) + + self._handle_coordinator_update() diff --git a/homeassistant/components/mazda/const.py b/homeassistant/components/mazda/const.py index 58ca2183a56..ebfa7f05301 100644 --- a/homeassistant/components/mazda/const.py +++ b/homeassistant/components/mazda/const.py @@ -4,6 +4,7 @@ DOMAIN = "mazda" DATA_CLIENT = "mazda_client" DATA_COORDINATOR = "coordinator" +DATA_REGION = "region" DATA_VEHICLES = "vehicles" MAZDA_REGIONS = {"MNAO": "North America", "MME": "Europe", "MJO": "Japan"} diff --git a/homeassistant/components/mazda/sensor.py b/homeassistant/components/mazda/sensor.py index 212d646051c..7ae59572b30 100644 --- a/homeassistant/components/mazda/sensor.py +++ b/homeassistant/components/mazda/sensor.py @@ -12,12 +12,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - LENGTH_KILOMETERS, - LENGTH_MILES, - PERCENTAGE, - PRESSURE_PSI, -) +from homeassistant.const import PERCENTAGE, UnitOfLength, UnitOfPressure from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import StateType @@ -52,8 +47,8 @@ class MazdaSensorEntityDescription( def _get_distance_unit(unit_system: UnitSystem) -> str: """Return the distance unit for the given unit system.""" if unit_system is US_CUSTOMARY_SYSTEM: - return LENGTH_MILES - return LENGTH_KILOMETERS + return UnitOfLength.MILES + return UnitOfLength.KILOMETERS def _fuel_remaining_percentage_supported(data): @@ -109,14 +104,18 @@ def _ev_remaining_range_supported(data): def _fuel_distance_remaining_value(data, unit_system): """Get the fuel distance remaining value.""" return round( - unit_system.length(data["status"]["fuelDistanceRemainingKm"], LENGTH_KILOMETERS) + unit_system.length( + data["status"]["fuelDistanceRemainingKm"], UnitOfLength.KILOMETERS + ) ) def _odometer_value(data, unit_system): """Get the odometer value.""" # In order to match the behavior of the Mazda mobile app, we always round down - return int(unit_system.length(data["status"]["odometerKm"], LENGTH_KILOMETERS)) + return int( + unit_system.length(data["status"]["odometerKm"], UnitOfLength.KILOMETERS) + ) def _front_left_tire_pressure_value(data, unit_system): @@ -148,7 +147,7 @@ def _ev_remaining_range_value(data, unit_system): """Get the remaining range value.""" return round( unit_system.length( - data["evStatus"]["chargeInfo"]["drivingRangeKm"], LENGTH_KILOMETERS + data["evStatus"]["chargeInfo"]["drivingRangeKm"], UnitOfLength.KILOMETERS ) ) @@ -186,7 +185,7 @@ SENSOR_ENTITIES = [ name="Front left tire pressure", icon="mdi:car-tire-alert", device_class=SensorDeviceClass.PRESSURE, - native_unit_of_measurement=PRESSURE_PSI, + native_unit_of_measurement=UnitOfPressure.PSI, state_class=SensorStateClass.MEASUREMENT, is_supported=_front_left_tire_pressure_supported, value=_front_left_tire_pressure_value, @@ -196,7 +195,7 @@ SENSOR_ENTITIES = [ name="Front right tire pressure", icon="mdi:car-tire-alert", device_class=SensorDeviceClass.PRESSURE, - native_unit_of_measurement=PRESSURE_PSI, + native_unit_of_measurement=UnitOfPressure.PSI, state_class=SensorStateClass.MEASUREMENT, is_supported=_front_right_tire_pressure_supported, value=_front_right_tire_pressure_value, @@ -206,7 +205,7 @@ SENSOR_ENTITIES = [ name="Rear left tire pressure", icon="mdi:car-tire-alert", device_class=SensorDeviceClass.PRESSURE, - native_unit_of_measurement=PRESSURE_PSI, + native_unit_of_measurement=UnitOfPressure.PSI, state_class=SensorStateClass.MEASUREMENT, is_supported=_rear_left_tire_pressure_supported, value=_rear_left_tire_pressure_value, @@ -216,7 +215,7 @@ SENSOR_ENTITIES = [ name="Rear right tire pressure", icon="mdi:car-tire-alert", device_class=SensorDeviceClass.PRESSURE, - native_unit_of_measurement=PRESSURE_PSI, + native_unit_of_measurement=UnitOfPressure.PSI, state_class=SensorStateClass.MEASUREMENT, is_supported=_rear_right_tire_pressure_supported, value=_rear_right_tire_pressure_value, diff --git a/homeassistant/components/mazda/translations/pt.json b/homeassistant/components/mazda/translations/pt.json index 70ac9ae7bf8..615ab3d7cf6 100644 --- a/homeassistant/components/mazda/translations/pt.json +++ b/homeassistant/components/mazda/translations/pt.json @@ -4,7 +4,7 @@ "reauth_successful": "Reautentica\u00e7\u00e3o bem sucedida" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" } } } \ No newline at end of file diff --git a/homeassistant/components/mazda/translations/sk.json b/homeassistant/components/mazda/translations/sk.json index 9b00c29386f..373d319dd6e 100644 --- a/homeassistant/components/mazda/translations/sk.json +++ b/homeassistant/components/mazda/translations/sk.json @@ -16,7 +16,8 @@ "email": "Email", "password": "Heslo", "region": "Oblas\u0165" - } + }, + "description": "Zadajte e-mailov\u00fa adresu a heslo, ktor\u00e9 pou\u017e\u00edvate na prihl\u00e1senie do mobilnej aplik\u00e1cie MyMazda." } } } diff --git a/homeassistant/components/meater/sensor.py b/homeassistant/components/meater/sensor.py index 8322b9a0202..0a1240c7471 100644 --- a/homeassistant/components/meater/sensor.py +++ b/homeassistant/components/meater/sensor.py @@ -14,7 +14,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import TEMP_CELSIUS +from homeassistant.const import UnitOfTemperature from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import ( @@ -65,7 +65,7 @@ SENSOR_TYPES = ( key="ambient", device_class=SensorDeviceClass.TEMPERATURE, name="Ambient", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, available=lambda probe: probe is not None, value=lambda probe: probe.ambient_temperature, @@ -75,7 +75,7 @@ SENSOR_TYPES = ( key="internal", device_class=SensorDeviceClass.TEMPERATURE, name="Internal", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, available=lambda probe: probe is not None, value=lambda probe: probe.internal_temperature, @@ -100,7 +100,7 @@ SENSOR_TYPES = ( key="cook_target_temp", device_class=SensorDeviceClass.TEMPERATURE, name="Target", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, available=lambda probe: probe is not None and probe.cook is not None, value=lambda probe: probe.cook.target_temperature @@ -112,7 +112,7 @@ SENSOR_TYPES = ( key="cook_peak_temp", device_class=SensorDeviceClass.TEMPERATURE, name="Peak", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, available=lambda probe: probe is not None and probe.cook is not None, value=lambda probe: probe.cook.peak_temperature diff --git a/homeassistant/components/meater/translations/sk.json b/homeassistant/components/meater/translations/sk.json index e2216e824b3..d972d67ca62 100644 --- a/homeassistant/components/meater/translations/sk.json +++ b/homeassistant/components/meater/translations/sk.json @@ -9,7 +9,8 @@ "reauth_confirm": { "data": { "password": "Heslo" - } + }, + "description": "Potvr\u010fte heslo pre \u00fa\u010det Meater Cloud {username}." }, "user": { "data": { diff --git a/homeassistant/components/media_player/__init__.py b/homeassistant/components/media_player/__init__.py index 2337990933a..fa2c5465443 100644 --- a/homeassistant/components/media_player/__init__.py +++ b/homeassistant/components/media_player/__init__.py @@ -393,12 +393,14 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: elif value[ATTR_MEDIA_ENQUEUE] is True: value[ATTR_MEDIA_ENQUEUE] = MediaPlayerEnqueue.ADD _LOGGER.warning( - "Playing media with enqueue set to True is deprecated. Use 'add' instead" + "Playing media with enqueue set to True is deprecated. Use 'add'" + " instead" ) elif value[ATTR_MEDIA_ENQUEUE] is False: value[ATTR_MEDIA_ENQUEUE] = MediaPlayerEnqueue.PLAY _LOGGER.warning( - "Playing media with enqueue set to False is deprecated. Use 'play' instead" + "Playing media with enqueue set to False is deprecated. Use 'play'" + " instead" ) return value @@ -453,7 +455,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: class MediaPlayerEntityDescription(EntityDescription): """A class that describes media player entities.""" - device_class: MediaPlayerDeviceClass | str | None = None + device_class: MediaPlayerDeviceClass | None = None class MediaPlayerEntity(Entity): @@ -464,7 +466,7 @@ class MediaPlayerEntity(Entity): _attr_app_id: str | None = None _attr_app_name: str | None = None - _attr_device_class: MediaPlayerDeviceClass | str | None + _attr_device_class: MediaPlayerDeviceClass | None _attr_group_members: list[str] | None = None _attr_is_volume_muted: bool | None = None _attr_media_album_artist: str | None = None @@ -497,7 +499,7 @@ class MediaPlayerEntity(Entity): # Implement these for your media player @property - def device_class(self) -> MediaPlayerDeviceClass | str | None: + def device_class(self) -> MediaPlayerDeviceClass | None: """Return the class of this entity.""" if hasattr(self, "_attr_device_class"): return self._attr_device_class diff --git a/homeassistant/components/media_player/translations/sk.json b/homeassistant/components/media_player/translations/sk.json index a2d2813ea7b..1a69273794c 100644 --- a/homeassistant/components/media_player/translations/sk.json +++ b/homeassistant/components/media_player/translations/sk.json @@ -1,20 +1,26 @@ { "device_automation": { "condition_type": { + "is_buffering": "{entity_name} sa uklad\u00e1 do vyrovn\u00e1vacej pam\u00e4te", "is_idle": "{entity_name} je ne\u010dinn\u00e9", "is_off": "{entity_name} je vypnut\u00e9", "is_on": "{entity_name} je zapnut\u00e9", - "is_paused": "{entity_name} je pozastaven\u00e9" + "is_paused": "{entity_name} je pozastaven\u00e9", + "is_playing": "{entity_name} prehr\u00e1va" }, "trigger_type": { + "buffering": "{entity_name} spust\u00ed ukladanie do vyrovn\u00e1vacej pam\u00e4te", "changed_states": "{entity_name} zmenil stav", + "idle": "{entity_name} sa st\u00e1va ne\u010dinn\u00fdm", "paused": "{entity_name} je pozastaven\u00e9", + "playing": "{entity_name} za\u010d\u00edna hra\u0165", "turned_off": "{entity_name} vypnut\u00e1", "turned_on": "{entity_name} zapnut\u00e1" } }, "state": { "_": { + "buffering": "Ukladanie do vyrovn\u00e1vacej pam\u00e4te", "idle": "Ne\u010dinn\u00fd", "off": "Vypnut\u00fd", "on": "Zapnut\u00fd", diff --git a/homeassistant/components/melcloud/climate.py b/homeassistant/components/melcloud/climate.py index 9fc455893f6..2cac1abcf88 100644 --- a/homeassistant/components/melcloud/climate.py +++ b/homeassistant/components/melcloud/climate.py @@ -23,7 +23,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers import config_validation as cv, entity_platform from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -98,7 +98,7 @@ async def async_setup_entry( class MelCloudClimate(ClimateEntity): """Base climate device.""" - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS def __init__(self, device: MelCloudDevice) -> None: """Initialize the climate.""" @@ -240,7 +240,8 @@ class AtaDeviceClimate(MelCloudClimate): """Set horizontal vane position.""" if position not in self._device.vane_horizontal_positions: raise ValueError( - f"Invalid horizontal vane position {position}. Valid positions: [{self._device.vane_horizontal_positions}]." + f"Invalid horizontal vane position {position}. Valid positions:" + f" [{self._device.vane_horizontal_positions}]." ) await self._device.set({ata.PROPERTY_VANE_HORIZONTAL: position}) @@ -248,7 +249,8 @@ class AtaDeviceClimate(MelCloudClimate): """Set vertical vane position.""" if position not in self._device.vane_vertical_positions: raise ValueError( - f"Invalid vertical vane position {position}. Valid positions: [{self._device.vane_vertical_positions}]." + f"Invalid vertical vane position {position}. Valid positions:" + f" [{self._device.vane_vertical_positions}]." ) await self._device.set({ata.PROPERTY_VANE_VERTICAL: position}) diff --git a/homeassistant/components/melcloud/sensor.py b/homeassistant/components/melcloud/sensor.py index 3d407b0916b..74187948d7d 100644 --- a/homeassistant/components/melcloud/sensor.py +++ b/homeassistant/components/melcloud/sensor.py @@ -15,7 +15,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ENERGY_KILO_WATT_HOUR, TEMP_CELSIUS +from homeassistant.const import UnitOfEnergy, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -43,7 +43,7 @@ ATA_SENSORS: tuple[MelcloudSensorEntityDescription, ...] = ( key="room_temperature", name="Room Temperature", icon="mdi:thermometer", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, value_fn=lambda x: x.device.room_temperature, enabled=lambda x: True, @@ -52,7 +52,7 @@ ATA_SENSORS: tuple[MelcloudSensorEntityDescription, ...] = ( key="energy", name="Energy", icon="mdi:factory", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, value_fn=lambda x: x.device.total_energy_consumed, enabled=lambda x: x.device.has_energy_consumed_meter, @@ -61,7 +61,7 @@ ATA_SENSORS: tuple[MelcloudSensorEntityDescription, ...] = ( key="daily_energy", name="Daily Energy Consumed", icon="mdi:factory", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, value_fn=lambda x: x.device.daily_energy_consumed, enabled=lambda x: True, @@ -72,7 +72,7 @@ ATW_SENSORS: tuple[MelcloudSensorEntityDescription, ...] = ( key="outside_temperature", name="Outside Temperature", icon="mdi:thermometer", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, value_fn=lambda x: x.device.outside_temperature, enabled=lambda x: True, @@ -81,7 +81,7 @@ ATW_SENSORS: tuple[MelcloudSensorEntityDescription, ...] = ( key="tank_temperature", name="Tank Temperature", icon="mdi:thermometer", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, value_fn=lambda x: x.device.tank_temperature, enabled=lambda x: True, @@ -90,7 +90,7 @@ ATW_SENSORS: tuple[MelcloudSensorEntityDescription, ...] = ( key="daily_energy", name="Daily Energy Consumed", icon="mdi:factory", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, value_fn=lambda x: x.device.daily_energy_consumed, enabled=lambda x: True, @@ -101,7 +101,7 @@ ATW_ZONE_SENSORS: tuple[MelcloudSensorEntityDescription, ...] = ( key="room_temperature", name="Room Temperature", icon="mdi:thermometer", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, value_fn=lambda zone: zone.room_temperature, enabled=lambda x: True, @@ -110,7 +110,7 @@ ATW_ZONE_SENSORS: tuple[MelcloudSensorEntityDescription, ...] = ( key="flow_temperature", name="Flow Temperature", icon="mdi:thermometer", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, value_fn=lambda zone: zone.flow_temperature, enabled=lambda x: True, @@ -119,7 +119,7 @@ ATW_ZONE_SENSORS: tuple[MelcloudSensorEntityDescription, ...] = ( key="return_temperature", name="Flow Return Temperature", icon="mdi:thermometer", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, value_fn=lambda zone: zone.return_temperature, enabled=lambda x: True, diff --git a/homeassistant/components/melcloud/translations/pt.json b/homeassistant/components/melcloud/translations/pt.json index 67f59434d46..59bde69b2dd 100644 --- a/homeassistant/components/melcloud/translations/pt.json +++ b/homeassistant/components/melcloud/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "Integra\u00e7\u00e3o com o MELCloud j\u00e1 configurada para este email. O token de acesso foi atualizado." }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, @@ -12,7 +12,7 @@ "user": { "data": { "password": "Palavra-passe MELCloud", - "username": "Email" + "username": "" } } } diff --git a/homeassistant/components/melcloud/water_heater.py b/homeassistant/components/melcloud/water_heater.py index 49c642d562d..511518279cb 100644 --- a/homeassistant/components/melcloud/water_heater.py +++ b/homeassistant/components/melcloud/water_heater.py @@ -17,7 +17,7 @@ from homeassistant.components.water_heater import ( WaterHeaterEntityFeature, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import TEMP_CELSIUS +from homeassistant.const import UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -89,7 +89,7 @@ class AtwWaterHeater(WaterHeaterEntity): @property def temperature_unit(self) -> str: """Return the unit of measurement used by the platform.""" - return TEMP_CELSIUS + return UnitOfTemperature.CELSIUS @property def current_operation(self) -> str | None: diff --git a/homeassistant/components/melissa/climate.py b/homeassistant/components/melissa/climate.py index bfe1a4929d0..9facb18ed05 100644 --- a/homeassistant/components/melissa/climate.py +++ b/homeassistant/components/melissa/climate.py @@ -13,7 +13,7 @@ from homeassistant.components.climate import ( ClimateEntityFeature, HVACMode, ) -from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, TEMP_CELSIUS +from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType @@ -59,7 +59,7 @@ class MelissaClimate(ClimateEntity): _attr_supported_features = ( ClimateEntityFeature.FAN_MODE | ClimateEntityFeature.TARGET_TEMPERATURE ) - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS def __init__(self, api, serial_number, init_data): """Initialize the climate device.""" diff --git a/homeassistant/components/met/__init__.py b/homeassistant/components/met/__init__.py index fc79ac4c60f..64734d5faaf 100644 --- a/homeassistant/components/met/__init__.py +++ b/homeassistant/components/met/__init__.py @@ -16,9 +16,8 @@ from homeassistant.const import ( CONF_LATITUDE, CONF_LONGITUDE, EVENT_CORE_CONFIG_UPDATE, - LENGTH_FEET, - LENGTH_METERS, Platform, + UnitOfLength, ) from homeassistant.core import Event, HomeAssistant from homeassistant.exceptions import HomeAssistantError @@ -161,7 +160,11 @@ class MetWeatherData: if not self._is_metric: elevation = int( - round(DistanceConverter.convert(elevation, LENGTH_FEET, LENGTH_METERS)) + round( + DistanceConverter.convert( + elevation, UnitOfLength.FEET, UnitOfLength.METERS + ) + ) ) coordinates = { diff --git a/homeassistant/components/met/weather.py b/homeassistant/components/met/weather.py index 222d4a040e3..ba8ccadfca5 100644 --- a/homeassistant/components/met/weather.py +++ b/homeassistant/components/met/weather.py @@ -36,8 +36,7 @@ from . import MetDataUpdateCoordinator from .const import ATTR_MAP, CONDITIONS_MAP, CONF_TRACK_HOME, DOMAIN, FORECAST_MAP ATTRIBUTION = ( - "Weather forecast from met.no, delivered by the Norwegian " - "Meteorological Institute." + "Weather forecast from met.no, delivered by the Norwegian Meteorological Institute." ) DEFAULT_NAME = "Met.no" diff --git a/homeassistant/components/met_eireann/translations/sk.json b/homeassistant/components/met_eireann/translations/sk.json index c631fc4b440..b3976737f46 100644 --- a/homeassistant/components/met_eireann/translations/sk.json +++ b/homeassistant/components/met_eireann/translations/sk.json @@ -11,6 +11,7 @@ "longitude": "Zemepisn\u00e1 d\u013a\u017eka", "name": "N\u00e1zov" }, + "description": "Ak chcete pou\u017ei\u0165 \u00fadaje o po\u010das\u00ed z rozhrania Met \u00c9ireann Public Weather Forecast API, zadajte svoju polohu", "title": "Umiestnenie" } } diff --git a/homeassistant/components/meteo_france/__init__.py b/homeassistant/components/meteo_france/__init__.py index 1d36746413e..6c9af12a322 100644 --- a/homeassistant/components/meteo_france/__init__.py +++ b/homeassistant/components/meteo_france/__init__.py @@ -139,13 +139,19 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass.data[DOMAIN][department] = True else: _LOGGER.warning( - "Weather alert for department %s won't be added with city %s, as it has already been added within another city", + ( + "Weather alert for department %s won't be added with city %s, as it" + " has already been added within another city" + ), department, entry.title, ) else: _LOGGER.warning( - "Weather alert not available: The city %s is not in metropolitan France or Andorre", + ( + "Weather alert not available: The city %s is not in metropolitan France" + " or Andorre" + ), entry.title, ) @@ -172,7 +178,10 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: ].data.position.get("dept") hass.data[DOMAIN][department] = False _LOGGER.debug( - "Weather alert for depatment %s unloaded and released. It can be added now by another city", + ( + "Weather alert for depatment %s unloaded and released. It can be added" + " now by another city" + ), department, ) diff --git a/homeassistant/components/meteo_france/const.py b/homeassistant/components/meteo_france/const.py index d6cbd34d88d..fad1a33e25c 100644 --- a/homeassistant/components/meteo_france/const.py +++ b/homeassistant/components/meteo_france/const.py @@ -1,13 +1,6 @@ """Meteo-France component constants.""" from __future__ import annotations -from dataclasses import dataclass - -from homeassistant.components.sensor import ( - SensorDeviceClass, - SensorEntityDescription, - SensorStateClass, -) from homeassistant.components.weather import ( ATTR_CONDITION_CLEAR_NIGHT, ATTR_CONDITION_CLOUDY, @@ -25,15 +18,7 @@ from homeassistant.components.weather import ( ATTR_CONDITION_WINDY, ATTR_CONDITION_WINDY_VARIANT, ) -from homeassistant.const import ( - LENGTH_MILLIMETERS, - PERCENTAGE, - PRESSURE_HPA, - SPEED_KILOMETERS_PER_HOUR, - TEMP_CELSIUS, - UV_INDEX, - Platform, -) +from homeassistant.const import Platform DOMAIN = "meteo_france" PLATFORMS = [Platform.SENSOR, Platform.WEATHER] @@ -54,135 +39,6 @@ ATTR_NEXT_RAIN_1_HOUR_FORECAST = "1_hour_forecast" ATTR_NEXT_RAIN_DT_REF = "forecast_time_ref" -@dataclass -class MeteoFranceRequiredKeysMixin: - """Mixin for required keys.""" - - data_path: str - - -@dataclass -class MeteoFranceSensorEntityDescription( - SensorEntityDescription, MeteoFranceRequiredKeysMixin -): - """Describes Meteo-France sensor entity.""" - - -SENSOR_TYPES: tuple[MeteoFranceSensorEntityDescription, ...] = ( - MeteoFranceSensorEntityDescription( - key="pressure", - name="Pressure", - native_unit_of_measurement=PRESSURE_HPA, - device_class=SensorDeviceClass.PRESSURE, - state_class=SensorStateClass.MEASUREMENT, - entity_registry_enabled_default=False, - data_path="current_forecast:sea_level", - ), - MeteoFranceSensorEntityDescription( - key="wind_gust", - name="Wind gust", - native_unit_of_measurement=SPEED_KILOMETERS_PER_HOUR, - state_class=SensorStateClass.MEASUREMENT, - icon="mdi:weather-windy-variant", - entity_registry_enabled_default=False, - data_path="current_forecast:wind:gust", - ), - MeteoFranceSensorEntityDescription( - key="wind_speed", - name="Wind speed", - native_unit_of_measurement=SPEED_KILOMETERS_PER_HOUR, - state_class=SensorStateClass.MEASUREMENT, - icon="mdi:weather-windy", - entity_registry_enabled_default=False, - data_path="current_forecast:wind:speed", - ), - MeteoFranceSensorEntityDescription( - key="temperature", - name="Temperature", - native_unit_of_measurement=TEMP_CELSIUS, - device_class=SensorDeviceClass.TEMPERATURE, - state_class=SensorStateClass.MEASUREMENT, - entity_registry_enabled_default=False, - data_path="current_forecast:T:value", - ), - MeteoFranceSensorEntityDescription( - key="uv", - name="UV", - native_unit_of_measurement=UV_INDEX, - icon="mdi:sunglasses", - data_path="today_forecast:uv", - ), - MeteoFranceSensorEntityDescription( - key="precipitation", - name="Daily precipitation", - native_unit_of_measurement=LENGTH_MILLIMETERS, - icon="mdi:cup-water", - data_path="today_forecast:precipitation:24h", - ), - MeteoFranceSensorEntityDescription( - key="cloud", - name="Cloud cover", - native_unit_of_measurement=PERCENTAGE, - icon="mdi:weather-partly-cloudy", - data_path="current_forecast:clouds", - ), - MeteoFranceSensorEntityDescription( - key="original_condition", - name="Original condition", - entity_registry_enabled_default=False, - data_path="current_forecast:weather:desc", - ), - MeteoFranceSensorEntityDescription( - key="daily_original_condition", - name="Daily original condition", - entity_registry_enabled_default=False, - data_path="today_forecast:weather12H:desc", - ), -) - -SENSOR_TYPES_RAIN: tuple[MeteoFranceSensorEntityDescription, ...] = ( - MeteoFranceSensorEntityDescription( - key="next_rain", - name="Next rain", - device_class=SensorDeviceClass.TIMESTAMP, - data_path="", - ), -) - -SENSOR_TYPES_ALERT: tuple[MeteoFranceSensorEntityDescription, ...] = ( - MeteoFranceSensorEntityDescription( - key="weather_alert", - name="Weather alert", - icon="mdi:weather-cloudy-alert", - data_path="", - ), -) - -SENSOR_TYPES_PROBABILITY: tuple[MeteoFranceSensorEntityDescription, ...] = ( - MeteoFranceSensorEntityDescription( - key="rain_chance", - name="Rain chance", - native_unit_of_measurement=PERCENTAGE, - icon="mdi:weather-rainy", - data_path="probability_forecast:rain:3h", - ), - MeteoFranceSensorEntityDescription( - key="snow_chance", - name="Snow chance", - native_unit_of_measurement=PERCENTAGE, - icon="mdi:weather-snowy", - data_path="probability_forecast:snow:3h", - ), - MeteoFranceSensorEntityDescription( - key="freeze_chance", - name="Freeze chance", - native_unit_of_measurement=PERCENTAGE, - icon="mdi:snowflake", - data_path="probability_forecast:freezing", - ), -) - - CONDITION_CLASSES: dict[str, list[str]] = { ATTR_CONDITION_CLEAR_NIGHT: ["Nuit Claire", "Nuit claire"], ATTR_CONDITION_CLOUDY: ["Très nuageux", "Couvert"], diff --git a/homeassistant/components/meteo_france/sensor.py b/homeassistant/components/meteo_france/sensor.py index b3a0ad498d9..22a9b195d2d 100644 --- a/homeassistant/components/meteo_france/sensor.py +++ b/homeassistant/components/meteo_france/sensor.py @@ -1,13 +1,32 @@ """Support for Meteo-France raining forecast sensor.""" from __future__ import annotations +from dataclasses import dataclass +from typing import Any, TypeVar, Union + from meteofrance_api.helpers import ( get_warning_text_status_from_indice_color, readeable_phenomenoms_dict, ) +from meteofrance_api.model.forecast import Forecast +from meteofrance_api.model.rain import Rain +from meteofrance_api.model.warning import CurrentPhenomenons -from homeassistant.components.sensor import SensorEntity +from homeassistant.components.sensor import ( + SensorDeviceClass, + SensorEntity, + SensorEntityDescription, + SensorStateClass, +) from homeassistant.config_entries import ConfigEntry +from homeassistant.const import ( + PERCENTAGE, + UV_INDEX, + UnitOfPrecipitationDepth, + UnitOfPressure, + UnitOfSpeed, + UnitOfTemperature, +) from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceEntryType from homeassistant.helpers.entity import DeviceInfo @@ -28,11 +47,138 @@ from .const import ( DOMAIN, MANUFACTURER, MODEL, - SENSOR_TYPES, - SENSOR_TYPES_ALERT, - SENSOR_TYPES_PROBABILITY, - SENSOR_TYPES_RAIN, - MeteoFranceSensorEntityDescription, +) + +_DataT = TypeVar("_DataT", bound=Union[Rain, Forecast, CurrentPhenomenons]) + + +@dataclass +class MeteoFranceRequiredKeysMixin: + """Mixin for required keys.""" + + data_path: str + + +@dataclass +class MeteoFranceSensorEntityDescription( + SensorEntityDescription, MeteoFranceRequiredKeysMixin +): + """Describes Meteo-France sensor entity.""" + + +SENSOR_TYPES: tuple[MeteoFranceSensorEntityDescription, ...] = ( + MeteoFranceSensorEntityDescription( + key="pressure", + name="Pressure", + native_unit_of_measurement=UnitOfPressure.HPA, + device_class=SensorDeviceClass.PRESSURE, + state_class=SensorStateClass.MEASUREMENT, + entity_registry_enabled_default=False, + data_path="current_forecast:sea_level", + ), + MeteoFranceSensorEntityDescription( + key="wind_gust", + name="Wind gust", + native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, + device_class=SensorDeviceClass.WIND_SPEED, + state_class=SensorStateClass.MEASUREMENT, + icon="mdi:weather-windy-variant", + entity_registry_enabled_default=False, + data_path="current_forecast:wind:gust", + ), + MeteoFranceSensorEntityDescription( + key="wind_speed", + name="Wind speed", + native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, + device_class=SensorDeviceClass.WIND_SPEED, + state_class=SensorStateClass.MEASUREMENT, + entity_registry_enabled_default=False, + data_path="current_forecast:wind:speed", + ), + MeteoFranceSensorEntityDescription( + key="temperature", + name="Temperature", + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + entity_registry_enabled_default=False, + data_path="current_forecast:T:value", + ), + MeteoFranceSensorEntityDescription( + key="uv", + name="UV", + native_unit_of_measurement=UV_INDEX, + icon="mdi:sunglasses", + data_path="today_forecast:uv", + ), + MeteoFranceSensorEntityDescription( + key="precipitation", + name="Daily precipitation", + native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + device_class=SensorDeviceClass.PRECIPITATION, + data_path="today_forecast:precipitation:24h", + ), + MeteoFranceSensorEntityDescription( + key="cloud", + name="Cloud cover", + native_unit_of_measurement=PERCENTAGE, + icon="mdi:weather-partly-cloudy", + data_path="current_forecast:clouds", + ), + MeteoFranceSensorEntityDescription( + key="original_condition", + name="Original condition", + entity_registry_enabled_default=False, + data_path="current_forecast:weather:desc", + ), + MeteoFranceSensorEntityDescription( + key="daily_original_condition", + name="Daily original condition", + entity_registry_enabled_default=False, + data_path="today_forecast:weather12H:desc", + ), +) + +SENSOR_TYPES_RAIN: tuple[MeteoFranceSensorEntityDescription, ...] = ( + MeteoFranceSensorEntityDescription( + key="next_rain", + name="Next rain", + device_class=SensorDeviceClass.TIMESTAMP, + data_path="", + ), +) + +SENSOR_TYPES_ALERT: tuple[MeteoFranceSensorEntityDescription, ...] = ( + MeteoFranceSensorEntityDescription( + key="weather_alert", + name="Weather alert", + icon="mdi:weather-cloudy-alert", + data_path="", + ), +) + +SENSOR_TYPES_PROBABILITY: tuple[MeteoFranceSensorEntityDescription, ...] = ( + MeteoFranceSensorEntityDescription( + key="rain_chance", + name="Rain chance", + native_unit_of_measurement=PERCENTAGE, + icon="mdi:weather-rainy", + data_path="probability_forecast:rain:3h", + ), + MeteoFranceSensorEntityDescription( + key="snow_chance", + name="Snow chance", + native_unit_of_measurement=PERCENTAGE, + icon="mdi:weather-snowy", + data_path="probability_forecast:snow:3h", + ), + MeteoFranceSensorEntityDescription( + key="freeze_chance", + name="Freeze chance", + native_unit_of_measurement=PERCENTAGE, + icon="mdi:snowflake", + data_path="probability_forecast:freezing", + ), ) @@ -40,11 +186,14 @@ async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback ) -> None: """Set up the Meteo-France sensor platform.""" - coordinator_forecast = hass.data[DOMAIN][entry.entry_id][COORDINATOR_FORECAST] - coordinator_rain = hass.data[DOMAIN][entry.entry_id][COORDINATOR_RAIN] - coordinator_alert = hass.data[DOMAIN][entry.entry_id][COORDINATOR_ALERT] + data = hass.data[DOMAIN][entry.entry_id] + coordinator_forecast: DataUpdateCoordinator[Forecast] = data[COORDINATOR_FORECAST] + coordinator_rain: DataUpdateCoordinator[Rain] | None = data[COORDINATOR_RAIN] + coordinator_alert: DataUpdateCoordinator[CurrentPhenomenons] | None = data[ + COORDINATOR_ALERT + ] - entities = [ + entities: list[MeteoFranceSensor[Any]] = [ MeteoFranceSensor(coordinator_forecast, description) for description in SENSOR_TYPES ] @@ -76,7 +225,7 @@ async def async_setup_entry( async_add_entities(entities, False) -class MeteoFranceSensor(CoordinatorEntity, SensorEntity): +class MeteoFranceSensor(CoordinatorEntity[DataUpdateCoordinator[_DataT]], SensorEntity): """Representation of a Meteo-France sensor.""" entity_description: MeteoFranceSensorEntityDescription @@ -84,7 +233,7 @@ class MeteoFranceSensor(CoordinatorEntity, SensorEntity): def __init__( self, - coordinator: DataUpdateCoordinator, + coordinator: DataUpdateCoordinator[_DataT], description: MeteoFranceSensorEntityDescription, ) -> None: """Initialize the Meteo-France sensor.""" @@ -138,7 +287,7 @@ class MeteoFranceSensor(CoordinatorEntity, SensorEntity): return value -class MeteoFranceRainSensor(MeteoFranceSensor): +class MeteoFranceRainSensor(MeteoFranceSensor[Rain]): """Representation of a Meteo-France rain sensor.""" @property @@ -164,12 +313,12 @@ class MeteoFranceRainSensor(MeteoFranceSensor): } -class MeteoFranceAlertSensor(MeteoFranceSensor): +class MeteoFranceAlertSensor(MeteoFranceSensor[CurrentPhenomenons]): """Representation of a Meteo-France alert sensor.""" def __init__( self, - coordinator: DataUpdateCoordinator, + coordinator: DataUpdateCoordinator[CurrentPhenomenons], description: MeteoFranceSensorEntityDescription, ) -> None: """Initialize the Meteo-France sensor.""" diff --git a/homeassistant/components/meteo_france/translations/sk.json b/homeassistant/components/meteo_france/translations/sk.json index 402d5f65bc7..e011693380c 100644 --- a/homeassistant/components/meteo_france/translations/sk.json +++ b/homeassistant/components/meteo_france/translations/sk.json @@ -4,6 +4,9 @@ "already_configured": "Umiestnenie u\u017e je nakonfigurovan\u00e9", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, + "error": { + "empty": "\u017diadny v\u00fdsledok pri h\u013eadan\u00ed mesta: skontrolujte pole mesta" + }, "step": { "cities": { "data": { @@ -14,7 +17,8 @@ "user": { "data": { "city": "Mesto" - } + }, + "description": "Zadajte po\u0161tov\u00e9 smerovacie \u010d\u00edslo (len pre Franc\u00fazsko, odpor\u00fa\u010dan\u00e9) alebo n\u00e1zov mesta" } } }, diff --git a/homeassistant/components/meteo_france/weather.py b/homeassistant/components/meteo_france/weather.py index bf0ff8c469e..95972a95bbe 100644 --- a/homeassistant/components/meteo_france/weather.py +++ b/homeassistant/components/meteo_france/weather.py @@ -2,6 +2,8 @@ import logging import time +from meteofrance_api.model.forecast import Forecast + from homeassistant.components.weather import ( ATTR_FORECAST_CONDITION, ATTR_FORECAST_NATIVE_PRECIPITATION, @@ -56,7 +58,9 @@ async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback ) -> None: """Set up the Meteo-France weather platform.""" - coordinator = hass.data[DOMAIN][entry.entry_id][COORDINATOR_FORECAST] + coordinator: DataUpdateCoordinator[Forecast] = hass.data[DOMAIN][entry.entry_id][ + COORDINATOR_FORECAST + ] async_add_entities( [ @@ -74,7 +78,9 @@ async def async_setup_entry( ) -class MeteoFranceWeather(CoordinatorEntity, WeatherEntity): +class MeteoFranceWeather( + CoordinatorEntity[DataUpdateCoordinator[Forecast]], WeatherEntity +): """Representation of a weather condition.""" _attr_native_temperature_unit = UnitOfTemperature.CELSIUS @@ -82,7 +88,7 @@ class MeteoFranceWeather(CoordinatorEntity, WeatherEntity): _attr_native_pressure_unit = UnitOfPressure.HPA _attr_native_wind_speed_unit = UnitOfSpeed.METERS_PER_SECOND - def __init__(self, coordinator: DataUpdateCoordinator, mode: str) -> None: + def __init__(self, coordinator: DataUpdateCoordinator[Forecast], mode: str) -> None: """Initialise the platform with a data instance and station name.""" super().__init__(coordinator) self._city_name = self.coordinator.data.position["name"] diff --git a/homeassistant/components/meteoclimatic/const.py b/homeassistant/components/meteoclimatic/const.py index 424d89e9382..4de299f1cf7 100644 --- a/homeassistant/components/meteoclimatic/const.py +++ b/homeassistant/components/meteoclimatic/const.py @@ -5,7 +5,6 @@ from datetime import timedelta from meteoclimatic import Condition -from homeassistant.components.sensor import SensorDeviceClass, SensorEntityDescription from homeassistant.components.weather import ( ATTR_CONDITION_CLEAR_NIGHT, ATTR_CONDITION_CLOUDY, @@ -23,15 +22,7 @@ from homeassistant.components.weather import ( ATTR_CONDITION_WINDY, ATTR_CONDITION_WINDY_VARIANT, ) -from homeassistant.const import ( - DEGREE, - LENGTH_MILLIMETERS, - PERCENTAGE, - PRESSURE_HPA, - SPEED_KILOMETERS_PER_HOUR, - TEMP_CELSIUS, - Platform, -) +from homeassistant.const import Platform DOMAIN = "meteoclimatic" PLATFORMS = [Platform.SENSOR, Platform.WEATHER] @@ -45,86 +36,6 @@ CONF_STATION_CODE = "station_code" DEFAULT_WEATHER_CARD = True -SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( - SensorEntityDescription( - key="temp_current", - name="Temperature", - native_unit_of_measurement=TEMP_CELSIUS, - device_class=SensorDeviceClass.TEMPERATURE, - ), - SensorEntityDescription( - key="temp_max", - name="Daily Max Temperature", - native_unit_of_measurement=TEMP_CELSIUS, - device_class=SensorDeviceClass.TEMPERATURE, - ), - SensorEntityDescription( - key="temp_min", - name="Daily Min Temperature", - native_unit_of_measurement=TEMP_CELSIUS, - device_class=SensorDeviceClass.TEMPERATURE, - ), - SensorEntityDescription( - key="humidity_current", - name="Humidity", - native_unit_of_measurement=PERCENTAGE, - device_class=SensorDeviceClass.HUMIDITY, - ), - SensorEntityDescription( - key="humidity_max", - name="Daily Max Humidity", - native_unit_of_measurement=PERCENTAGE, - device_class=SensorDeviceClass.HUMIDITY, - ), - SensorEntityDescription( - key="humidity_min", - name="Daily Min Humidity", - native_unit_of_measurement=PERCENTAGE, - device_class=SensorDeviceClass.HUMIDITY, - ), - SensorEntityDescription( - key="pressure_current", - name="Pressure", - native_unit_of_measurement=PRESSURE_HPA, - device_class=SensorDeviceClass.PRESSURE, - ), - SensorEntityDescription( - key="pressure_max", - name="Daily Max Pressure", - native_unit_of_measurement=PRESSURE_HPA, - device_class=SensorDeviceClass.PRESSURE, - ), - SensorEntityDescription( - key="pressure_min", - name="Daily Min Pressure", - native_unit_of_measurement=PRESSURE_HPA, - device_class=SensorDeviceClass.PRESSURE, - ), - SensorEntityDescription( - key="wind_current", - name="Wind Speed", - native_unit_of_measurement=SPEED_KILOMETERS_PER_HOUR, - device_class="mdi:weather-windy", - ), - SensorEntityDescription( - key="wind_max", - name="Daily Max Wind Speed", - native_unit_of_measurement=SPEED_KILOMETERS_PER_HOUR, - device_class="mdi:weather-windy", - ), - SensorEntityDescription( - key="wind_bearing", - name="Wind Bearing", - native_unit_of_measurement=DEGREE, - device_class="mdi:weather-windy", - ), - SensorEntityDescription( - key="rain", - name="Daily Precipitation", - native_unit_of_measurement=LENGTH_MILLIMETERS, - device_class="mdi:cup-water", - ), -) CONDITION_CLASSES = { ATTR_CONDITION_CLEAR_NIGHT: [Condition.moon, Condition.hazemoon], diff --git a/homeassistant/components/meteoclimatic/sensor.py b/homeassistant/components/meteoclimatic/sensor.py index c5075d4f189..73804c1f77a 100644 --- a/homeassistant/components/meteoclimatic/sensor.py +++ b/homeassistant/components/meteoclimatic/sensor.py @@ -1,6 +1,18 @@ """Support for Meteoclimatic sensor.""" -from homeassistant.components.sensor import SensorEntity, SensorEntityDescription +from homeassistant.components.sensor import ( + SensorDeviceClass, + SensorEntity, + SensorEntityDescription, +) from homeassistant.config_entries import ConfigEntry +from homeassistant.const import ( + DEGREE, + PERCENTAGE, + UnitOfPrecipitationDepth, + UnitOfPressure, + UnitOfSpeed, + UnitOfTemperature, +) from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceEntryType from homeassistant.helpers.entity import DeviceInfo @@ -10,7 +22,88 @@ from homeassistant.helpers.update_coordinator import ( DataUpdateCoordinator, ) -from .const import ATTRIBUTION, DOMAIN, MANUFACTURER, MODEL, SENSOR_TYPES +from .const import ATTRIBUTION, DOMAIN, MANUFACTURER, MODEL + +SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( + SensorEntityDescription( + key="temp_current", + name="Temperature", + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + device_class=SensorDeviceClass.TEMPERATURE, + ), + SensorEntityDescription( + key="temp_max", + name="Daily Max Temperature", + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + device_class=SensorDeviceClass.TEMPERATURE, + ), + SensorEntityDescription( + key="temp_min", + name="Daily Min Temperature", + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + device_class=SensorDeviceClass.TEMPERATURE, + ), + SensorEntityDescription( + key="humidity_current", + name="Humidity", + native_unit_of_measurement=PERCENTAGE, + device_class=SensorDeviceClass.HUMIDITY, + ), + SensorEntityDescription( + key="humidity_max", + name="Daily Max Humidity", + native_unit_of_measurement=PERCENTAGE, + device_class=SensorDeviceClass.HUMIDITY, + ), + SensorEntityDescription( + key="humidity_min", + name="Daily Min Humidity", + native_unit_of_measurement=PERCENTAGE, + device_class=SensorDeviceClass.HUMIDITY, + ), + SensorEntityDescription( + key="pressure_current", + name="Pressure", + native_unit_of_measurement=UnitOfPressure.HPA, + device_class=SensorDeviceClass.PRESSURE, + ), + SensorEntityDescription( + key="pressure_max", + name="Daily Max Pressure", + native_unit_of_measurement=UnitOfPressure.HPA, + device_class=SensorDeviceClass.PRESSURE, + ), + SensorEntityDescription( + key="pressure_min", + name="Daily Min Pressure", + native_unit_of_measurement=UnitOfPressure.HPA, + device_class=SensorDeviceClass.PRESSURE, + ), + SensorEntityDescription( + key="wind_current", + name="Wind Speed", + native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, + device_class=SensorDeviceClass.WIND_SPEED, + ), + SensorEntityDescription( + key="wind_max", + name="Daily Max Wind Speed", + native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, + device_class=SensorDeviceClass.WIND_SPEED, + ), + SensorEntityDescription( + key="wind_bearing", + name="Wind Bearing", + native_unit_of_measurement=DEGREE, + icon="mdi:weather-windy", + ), + SensorEntityDescription( + key="rain", + name="Daily Precipitation", + native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + device_class=SensorDeviceClass.PRECIPITATION, + ), +) async def async_setup_entry( diff --git a/homeassistant/components/meteoclimatic/translations/ko.json b/homeassistant/components/meteoclimatic/translations/ko.json new file mode 100644 index 00000000000..d70f9058a56 --- /dev/null +++ b/homeassistant/components/meteoclimatic/translations/ko.json @@ -0,0 +1,11 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "not_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/meteoclimatic/translations/sk.json b/homeassistant/components/meteoclimatic/translations/sk.json index 67a321910ac..ce6239e6395 100644 --- a/homeassistant/components/meteoclimatic/translations/sk.json +++ b/homeassistant/components/meteoclimatic/translations/sk.json @@ -11,6 +11,9 @@ "user": { "data": { "code": "K\u00f3d stanice" + }, + "data_description": { + "code": "Vyzer\u00e1 ako ESCAT4300000043206B" } } } diff --git a/homeassistant/components/metoffice/sensor.py b/homeassistant/components/metoffice/sensor.py index 3495f7b7c7a..544dabd018a 100644 --- a/homeassistant/components/metoffice/sensor.py +++ b/homeassistant/components/metoffice/sensor.py @@ -12,11 +12,11 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - LENGTH_KILOMETERS, PERCENTAGE, - SPEED_MILES_PER_HOUR, - TEMP_CELSIUS, UV_INDEX, + UnitOfLength, + UnitOfSpeed, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -65,7 +65,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( key="temperature", name="Temperature", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, icon=None, entity_registry_enabled_default=True, ), @@ -73,17 +73,18 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( key="feels_like_temperature", name="Feels like temperature", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, icon=None, entity_registry_enabled_default=False, ), SensorEntityDescription( key="wind_speed", name="Wind speed", - native_unit_of_measurement=SPEED_MILES_PER_HOUR, - suggested_unit_of_measurement=SPEED_MILES_PER_HOUR, - device_class=SensorDeviceClass.SPEED, - icon="mdi:weather-windy", + native_unit_of_measurement=UnitOfSpeed.MILES_PER_HOUR, + # Hint mph because that's the preferred unit for wind speeds in UK + # This can be removed if we add a mixed metric/imperial unit system for UK users + suggested_unit_of_measurement=UnitOfSpeed.MILES_PER_HOUR, + device_class=SensorDeviceClass.WIND_SPEED, entity_registry_enabled_default=True, ), SensorEntityDescription( @@ -95,10 +96,11 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="wind_gust", name="Wind gust", - native_unit_of_measurement=SPEED_MILES_PER_HOUR, - suggested_unit_of_measurement=SPEED_MILES_PER_HOUR, - device_class=SensorDeviceClass.SPEED, - icon="mdi:weather-windy", + native_unit_of_measurement=UnitOfSpeed.MILES_PER_HOUR, + # Hint mph because that's the preferred unit for wind speeds in UK + # This can be removed if we add a mixed metric/imperial unit system for UK users + suggested_unit_of_measurement=UnitOfSpeed.MILES_PER_HOUR, + device_class=SensorDeviceClass.WIND_SPEED, entity_registry_enabled_default=False, ), SensorEntityDescription( @@ -111,7 +113,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="visibility_distance", name="Visibility distance", - native_unit_of_measurement=LENGTH_KILOMETERS, + native_unit_of_measurement=UnitOfLength.KILOMETERS, device_class=SensorDeviceClass.DISTANCE, icon="mdi:eye", entity_registry_enabled_default=False, diff --git a/homeassistant/components/metoffice/translations/pt.json b/homeassistant/components/metoffice/translations/pt.json index ccbecba0aef..1062b2108dc 100644 --- a/homeassistant/components/metoffice/translations/pt.json +++ b/homeassistant/components/metoffice/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "O servi\u00e7o j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "unknown": "Erro inesperado" }, "step": { diff --git a/homeassistant/components/metoffice/translations/sk.json b/homeassistant/components/metoffice/translations/sk.json index d1578eb3042..4f455c61290 100644 --- a/homeassistant/components/metoffice/translations/sk.json +++ b/homeassistant/components/metoffice/translations/sk.json @@ -14,7 +14,8 @@ "latitude": "Zemepisn\u00e1 \u0161\u00edrka", "longitude": "Zemepisn\u00e1 d\u013a\u017eka" }, - "description": "Zemepisn\u00e1 \u0161\u00edrka a d\u013a\u017eka sa pou\u017eije na n\u00e1jdenie najbli\u017e\u0161ej meteorologickej stanice." + "description": "Zemepisn\u00e1 \u0161\u00edrka a d\u013a\u017eka sa pou\u017eije na n\u00e1jdenie najbli\u017e\u0161ej meteorologickej stanice.", + "title": "Pripojenie k Met Office UK" } } } diff --git a/homeassistant/components/mfi/sensor.py b/homeassistant/components/mfi/sensor.py index de7e661d9d2..38f6d973b9d 100644 --- a/homeassistant/components/mfi/sensor.py +++ b/homeassistant/components/mfi/sensor.py @@ -21,7 +21,7 @@ from homeassistant.const import ( CONF_VERIFY_SSL, STATE_OFF, STATE_ON, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv @@ -136,7 +136,7 @@ class MfiSensor(SensorEntity): return "State" if tag == "temperature": - return TEMP_CELSIUS + return UnitOfTemperature.CELSIUS if tag == "active_pwr": return "Watts" if self._port.model == "Input Digital": diff --git a/homeassistant/components/miflora/translations/sk.json b/homeassistant/components/miflora/translations/sk.json index ea1bb7d4d8f..86b7b620314 100644 --- a/homeassistant/components/miflora/translations/sk.json +++ b/homeassistant/components/miflora/translations/sk.json @@ -1,6 +1,7 @@ { "issues": { "replaced": { + "description": "Integr\u00e1cia Mi Flora prestala fungova\u0165 v Home Assistant 2022.7 a bola nahraden\u00e1 integr\u00e1ciou Xiaomi BLE vo verzii 2022.8. \n\n Nie je mo\u017en\u00e1 \u017eiadna migra\u010dn\u00e1 cesta, preto mus\u00edte svoje zariadenie Mi Flora prida\u0165 pomocou novej integr\u00e1cie manu\u00e1lne. \n\n Asistent domova u\u017e nepou\u017e\u00edva va\u0161u existuj\u00facu konfigur\u00e1ciu Mi Flora YAML. Ak chcete tento probl\u00e9m vyrie\u0161i\u0165, odstr\u00e1\u0148te konfigur\u00e1ciu YAML zo s\u00faboru configuration.yaml a re\u0161tartujte aplik\u00e1ciu Home Assistant.", "title": "Integr\u00e1cia Mi Flora bola nahraden\u00e1" } } diff --git a/homeassistant/components/mikrotik/translations/de.json b/homeassistant/components/mikrotik/translations/de.json index 6ecdf66989f..d55c6002db2 100644 --- a/homeassistant/components/mikrotik/translations/de.json +++ b/homeassistant/components/mikrotik/translations/de.json @@ -34,7 +34,7 @@ "step": { "device_tracker": { "data": { - "arp_ping": "ARP-Ping aktivieren", + "arp_ping": "ARP Ping aktivieren", "detection_time": "Heimintervall ber\u00fccksichtigen", "force_dhcp": "Scannen mit DHCP erzwingen" } diff --git a/homeassistant/components/mikrotik/translations/ko.json b/homeassistant/components/mikrotik/translations/ko.json index 05a8f50066c..25ab19d7720 100644 --- a/homeassistant/components/mikrotik/translations/ko.json +++ b/homeassistant/components/mikrotik/translations/ko.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" }, "error": { "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", @@ -9,6 +10,12 @@ "name_exists": "\uc774\ub984\uc774 \uc774\ubbf8 \uc874\uc7ac\ud569\ub2c8\ub2e4" }, "step": { + "reauth_confirm": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638" + }, + "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" + }, "user": { "data": { "host": "\ud638\uc2a4\ud2b8", diff --git a/homeassistant/components/mikrotik/translations/pt.json b/homeassistant/components/mikrotik/translations/pt.json index 72d275069c9..e4d402ad3e6 100644 --- a/homeassistant/components/mikrotik/translations/pt.json +++ b/homeassistant/components/mikrotik/translations/pt.json @@ -1,14 +1,21 @@ { "config": { "abort": { - "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", + "reauth_successful": "Reautentica\u00e7\u00e3o bem sucedida" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "name_exists": "Nome existe" }, "step": { + "reauth_confirm": { + "data": { + "password": "Palavra-passe" + }, + "title": "Reautenticar integra\u00e7\u00e3o" + }, "user": { "data": { "host": "Servidor", diff --git a/homeassistant/components/mikrotik/translations/sk.json b/homeassistant/components/mikrotik/translations/sk.json index 82e54c59aac..6483c03eb2a 100644 --- a/homeassistant/components/mikrotik/translations/sk.json +++ b/homeassistant/components/mikrotik/translations/sk.json @@ -34,7 +34,9 @@ "step": { "device_tracker": { "data": { - "arp_ping": "Povoli\u0165 ARP ping" + "arp_ping": "Povoli\u0165 ARP ping", + "detection_time": "Zv\u00e1\u017ete dom\u00e1ci interval", + "force_dhcp": "Vyn\u00fati\u0165 skenovanie pomocou DHCP" } } } diff --git a/homeassistant/components/mill/climate.py b/homeassistant/components/mill/climate.py index 84f87e79a2d..42b759b3cdf 100644 --- a/homeassistant/components/mill/climate.py +++ b/homeassistant/components/mill/climate.py @@ -19,7 +19,7 @@ from homeassistant.const import ( CONF_USERNAME, PRECISION_HALVES, PRECISION_WHOLE, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant, ServiceCall, callback from homeassistant.helpers import config_validation as cv @@ -28,6 +28,7 @@ from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import CoordinatorEntity +from . import MillDataUpdateCoordinator from .const import ( ATTR_AWAY_TEMP, ATTR_COMFORT_TEMP, @@ -86,15 +87,17 @@ async def async_setup_entry( ) -class MillHeater(CoordinatorEntity, ClimateEntity): +class MillHeater(CoordinatorEntity[MillDataUpdateCoordinator], ClimateEntity): """Representation of a Mill Thermostat device.""" _attr_fan_modes = [FAN_ON, FAN_OFF] _attr_max_temp = MAX_TEMP _attr_min_temp = MIN_TEMP - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS - def __init__(self, coordinator, heater): + def __init__( + self, coordinator: MillDataUpdateCoordinator, heater: mill.Heater + ) -> None: """Initialize the thermostat.""" super().__init__(coordinator) @@ -196,7 +199,7 @@ class MillHeater(CoordinatorEntity, ClimateEntity): self._attr_hvac_mode = HVACMode.OFF -class LocalMillHeater(CoordinatorEntity, ClimateEntity): +class LocalMillHeater(CoordinatorEntity[MillDataUpdateCoordinator], ClimateEntity): """Representation of a Mill Thermostat device.""" _attr_hvac_mode = HVACMode.HEAT @@ -205,9 +208,9 @@ class LocalMillHeater(CoordinatorEntity, ClimateEntity): _attr_min_temp = MIN_TEMP _attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE _attr_target_temperature_step = PRECISION_HALVES - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS - def __init__(self, coordinator): + def __init__(self, coordinator: MillDataUpdateCoordinator) -> None: """Initialize the thermostat.""" super().__init__(coordinator) self._attr_name = coordinator.mill_data_connection.name diff --git a/homeassistant/components/mill/sensor.py b/homeassistant/components/mill/sensor.py index 2ed30315083..776ba1dc632 100644 --- a/homeassistant/components/mill/sensor.py +++ b/homeassistant/components/mill/sensor.py @@ -15,10 +15,10 @@ from homeassistant.const import ( CONCENTRATION_PARTS_PER_MILLION, CONF_IP_ADDRESS, CONF_USERNAME, - ENERGY_KILO_WATT_HOUR, PERCENTAGE, - POWER_WATT, - TEMP_CELSIUS, + UnitOfEnergy, + UnitOfPower, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC @@ -45,14 +45,14 @@ HEATER_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key=CONSUMPTION_YEAR, device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, state_class=SensorStateClass.TOTAL_INCREASING, name="Year consumption", ), SensorEntityDescription( key=CONSUMPTION_TODAY, device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, state_class=SensorStateClass.TOTAL_INCREASING, name="Day consumption", ), @@ -62,7 +62,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key=TEMPERATURE, device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, name="Temperature", state_class=SensorStateClass.MEASUREMENT, ), @@ -105,14 +105,14 @@ LOCAL_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="current_power", device_class=SensorDeviceClass.POWER, - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, name="Current power", state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="raw_ambient_temperature", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, name="Uncalibrated temperature", state_class=SensorStateClass.MEASUREMENT, entity_registry_enabled_default=False, diff --git a/homeassistant/components/mill/translations/ko.json b/homeassistant/components/mill/translations/ko.json index a38c2d98582..aeb34415005 100644 --- a/homeassistant/components/mill/translations/ko.json +++ b/homeassistant/components/mill/translations/ko.json @@ -7,7 +7,16 @@ "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" }, "step": { + "cloud": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + } + }, "local": { + "data": { + "ip_address": "IP \uc8fc\uc18c" + }, "description": "\ub85c\uceec IP \uc8fc\uc18c" } } diff --git a/homeassistant/components/mill/translations/pt.json b/homeassistant/components/mill/translations/pt.json index 63c9b4e0011..fb20892c61b 100644 --- a/homeassistant/components/mill/translations/pt.json +++ b/homeassistant/components/mill/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "Conta j\u00e1 configurada" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" } } } \ No newline at end of file diff --git a/homeassistant/components/mill/translations/sk.json b/homeassistant/components/mill/translations/sk.json index 0331ed11690..58b4313d383 100644 --- a/homeassistant/components/mill/translations/sk.json +++ b/homeassistant/components/mill/translations/sk.json @@ -22,7 +22,8 @@ "user": { "data": { "connection_type": "Vyberte typ pripojenia" - } + }, + "description": "Vyberte typ pripojenia. Miestne vy\u017eaduje ohrieva\u010de gener\u00e1cie 3" } } } diff --git a/homeassistant/components/min_max/translations/ca.json b/homeassistant/components/min_max/translations/ca.json index 4bc9e69f182..b5218d0265b 100644 --- a/homeassistant/components/min_max/translations/ca.json +++ b/homeassistant/components/min_max/translations/ca.json @@ -9,10 +9,10 @@ "type": "Caracter\u00edstica estad\u00edstica" }, "data_description": { - "round_digits": "Controla el nombre de d\u00edgits decimals quan la caracter\u00edstica estad\u00edstica \u00e9s la mitjana o la mediana." + "round_digits": "Controla el nombre de d\u00edgits decimals quan la caracter\u00edstica estad\u00edstica \u00e9s la mitjana, la mediana o la suma." }, - "description": "Crea un sensor que calcula el m\u00ednim, m\u00e0xim, la mitjana o la mediana d'una llista de sensors d'entrada.", - "title": "Afegeix sensor m\u00edn / m\u00e0x / mitjana / mediana" + "description": "Crea un sensor que calcula el m\u00ednim, m\u00e0xim, la mitjana, la mediana o la suma d'una llista de sensors d'entrada.", + "title": "Combina l'estat de diversos sensors" } } }, @@ -25,10 +25,10 @@ "type": "Caracter\u00edstica estad\u00edstica" }, "data_description": { - "round_digits": "Controla el nombre de d\u00edgits decimals quan la caracter\u00edstica estad\u00edstica \u00e9s la mitjana o la mediana." + "round_digits": "Controla el nombre de d\u00edgits decimals quan la caracter\u00edstica estad\u00edstica \u00e9s la mitjana, la mediana o la suma." } } } }, - "title": "Sensor m\u00edn / m\u00e0x / mitjana / mediana" + "title": "Combina l'estat de diversos sensors" } \ No newline at end of file diff --git a/homeassistant/components/min_max/translations/cs.json b/homeassistant/components/min_max/translations/cs.json index 181a5c93f14..4148cbd4d30 100644 --- a/homeassistant/components/min_max/translations/cs.json +++ b/homeassistant/components/min_max/translations/cs.json @@ -4,11 +4,15 @@ "user": { "data": { "entity_ids": "Vstupn\u00ed entity", - "name": "Jm\u00e9no", + "name": "N\u00e1zev", "round_digits": "P\u0159esnost", "type": "Statistika" }, - "description": "P\u0159esnost ur\u010duje po\u010det desetinn\u00fdch m\u00edst, kdy\u017e je statistikou pr\u016fm\u011br nebo medi\u00e1n." + "data_description": { + "round_digits": "\u0158\u00edd\u00ed po\u010det desetinn\u00fdch m\u00edst ve v\u00fdstupu pokud je statistikou pr\u016fm\u011br, medi\u00e1n nebo sou\u010det." + }, + "description": "Vytvo\u0159\u00ed senzor, kter\u00fd vypo\u010d\u00edt\u00e1 minimum, maximum, pr\u016fm\u011br nebo sou\u010det ze seznamu vstupn\u00edch senzor\u016f.", + "title": "Skombinujte stav n\u011bkolika senzor\u016f" } } }, @@ -19,8 +23,12 @@ "entity_ids": "Vstupn\u00ed entity", "round_digits": "P\u0159esnost", "type": "Statistika" + }, + "data_description": { + "round_digits": "\u0158\u00edd\u00ed po\u010det desetinn\u00fdch m\u00edst ve v\u00fdstupu pokud je statistikou pr\u016fm\u011br, medi\u00e1n nebo sou\u010det." } } } - } + }, + "title": "Skombinujte stav n\u011bkolika senzor\u016f" } \ No newline at end of file diff --git a/homeassistant/components/min_max/translations/el.json b/homeassistant/components/min_max/translations/el.json index 53499682941..606d2ccd1b9 100644 --- a/homeassistant/components/min_max/translations/el.json +++ b/homeassistant/components/min_max/translations/el.json @@ -4,15 +4,15 @@ "user": { "data": { "entity_ids": "\u039f\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2 \u03b5\u03b9\u03c3\u03cc\u03b4\u03bf\u03c5", - "name": "\u039f\u03bd\u03bf\u03bc\u03b1", + "name": "\u038c\u03bd\u03bf\u03bc\u03b1", "round_digits": "\u0391\u03ba\u03c1\u03af\u03b2\u03b5\u03b9\u03b1", "type": "\u03a3\u03c4\u03b1\u03c4\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03b7\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc" }, "data_description": { - "round_digits": "\u0395\u03bb\u03ad\u03b3\u03c7\u03b5\u03b9 \u03c4\u03bf\u03bd \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc \u03c4\u03c9\u03bd \u03b4\u03b5\u03ba\u03b1\u03b4\u03b9\u03ba\u03ce\u03bd \u03c8\u03b7\u03c6\u03af\u03c9\u03bd \u03c3\u03c4\u03b7\u03bd \u03ad\u03be\u03bf\u03b4\u03bf \u03cc\u03c4\u03b1\u03bd \u03c4\u03bf \u03c3\u03c4\u03b1\u03c4\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03b7\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03b5\u03af\u03bd\u03b1\u03b9 \u03bc\u03ad\u03c3\u03bf\u03c2 \u03ae \u03b4\u03b9\u03ac\u03bc\u03b5\u03c3\u03bf\u03c2." + "round_digits": "\u0395\u03bb\u03ad\u03b3\u03c7\u03b5\u03b9 \u03c4\u03bf\u03bd \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc \u03c4\u03c9\u03bd \u03b4\u03b5\u03ba\u03b1\u03b4\u03b9\u03ba\u03ce\u03bd \u03c8\u03b7\u03c6\u03af\u03c9\u03bd \u03c4\u03bf\u03c5 \u03bd\u03ad\u03bf\u03c5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 \u03cc\u03c4\u03b1\u03bd \u03c4\u03bf \u03c3\u03c4\u03b1\u03c4\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03b7\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03b5\u03af\u03bd\u03b1\u03b9 \u03bc\u03ad\u03c3\u03bf\u03c2 \u03cc\u03c1\u03bf\u03c2, \u03b4\u03b9\u03ac\u03bc\u03b5\u03c3\u03bf\u03c2 \u03ae \u03ac\u03b8\u03c1\u03bf\u03b9\u03c3\u03bc\u03b1." }, - "description": "\u0397 \u03b1\u03ba\u03c1\u03af\u03b2\u03b5\u03b9\u03b1 \u03b5\u03bb\u03ad\u03b3\u03c7\u03b5\u03b9 \u03c4\u03bf\u03bd \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc \u03c4\u03c9\u03bd \u03b4\u03b5\u03ba\u03b1\u03b4\u03b9\u03ba\u03ce\u03bd \u03c8\u03b7\u03c6\u03af\u03c9\u03bd \u03cc\u03c4\u03b1\u03bd \u03c4\u03bf \u03c3\u03c4\u03b1\u03c4\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03b7\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03b5\u03af\u03bd\u03b1\u03b9 \u03bf \u03bc\u03ad\u03c3\u03bf\u03c2 \u03cc\u03c1\u03bf\u03c2 \u03ae \u03b7 \u03b4\u03b9\u03ac\u03bc\u03b5\u03c3\u03bf\u03c2.", - "title": "\u03a0\u03c1\u03bf\u03c3\u03b8\u03ae\u03ba\u03b7 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 min / max / mean / median" + "description": "\u0394\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03ae\u03c3\u03c4\u03b5 \u03ad\u03bd\u03b1\u03bd \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 \u03c0\u03bf\u03c5 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03af\u03b6\u03b5\u03b9 \u03ad\u03bd\u03b1 \u03b5\u03bb\u03ac\u03c7\u03b9\u03c3\u03c4\u03bf, \u03bc\u03ad\u03b3\u03b9\u03c3\u03c4\u03bf, \u03bc\u03ad\u03c3\u03bf \u03cc\u03c1\u03bf, \u03b4\u03b9\u03ac\u03bc\u03b5\u03c3\u03bf, \u03ae \u03ac\u03b8\u03c1\u03bf\u03b9\u03c3\u03bc\u03b1 \u03b1\u03c0\u03cc \u03bc\u03af\u03b1 \u03bb\u03af\u03c3\u03c4\u03b1 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03c9\u03bd \u03c0\u03bf\u03c5 \u03bf\u03c1\u03af\u03b6\u03b5\u03c4\u03b5.", + "title": "\u03a3\u03c5\u03bd\u03b4\u03c5\u03ac\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7 \u03c0\u03bf\u03bb\u03bb\u03ce\u03bd \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03c9\u03bd" } } }, @@ -25,10 +25,10 @@ "type": "\u03a3\u03c4\u03b1\u03c4\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03b7\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc" }, "data_description": { - "round_digits": "\u0395\u03bb\u03ad\u03b3\u03c7\u03b5\u03b9 \u03c4\u03bf\u03bd \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc \u03c4\u03c9\u03bd \u03b4\u03b5\u03ba\u03b1\u03b4\u03b9\u03ba\u03ce\u03bd \u03c8\u03b7\u03c6\u03af\u03c9\u03bd \u03c3\u03c4\u03b7\u03bd \u03ad\u03be\u03bf\u03b4\u03bf \u03cc\u03c4\u03b1\u03bd \u03c4\u03bf \u03c3\u03c4\u03b1\u03c4\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03b7\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03b5\u03af\u03bd\u03b1\u03b9 \u03bc\u03ad\u03c3\u03bf\u03c2 \u03ae \u03b4\u03b9\u03ac\u03bc\u03b5\u03c3\u03bf\u03c2." + "round_digits": "\u0395\u03bb\u03ad\u03b3\u03c7\u03b5\u03b9 \u03c4\u03bf\u03bd \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc \u03c4\u03c9\u03bd \u03b4\u03b5\u03ba\u03b1\u03b4\u03b9\u03ba\u03ce\u03bd \u03c8\u03b7\u03c6\u03af\u03c9\u03bd \u03c4\u03bf\u03c5 \u03bd\u03ad\u03bf\u03c5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 \u03cc\u03c4\u03b1\u03bd \u03c4\u03bf \u03c3\u03c4\u03b1\u03c4\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03b7\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03b5\u03af\u03bd\u03b1\u03b9 \u03bc\u03ad\u03c3\u03bf\u03c2 \u03cc\u03c1\u03bf\u03c2, \u03b4\u03b9\u03ac\u03bc\u03b5\u03c3\u03bf\u03c2 \u03ae \u03ac\u03b8\u03c1\u03bf\u03b9\u03c3\u03bc\u03b1." } } } }, - "title": "\u0395\u03bb\u03ac\u03c7\u03b9\u03c3\u03c4\u03bf\u03c2 / \u03bc\u03ad\u03b3\u03b9\u03c3\u03c4\u03bf\u03c2 / \u03bc\u03ad\u03c3\u03bf\u03c2 / \u03b4\u03b9\u03ac\u03bc\u03b5\u03c3\u03bf\u03c2 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2" + "title": "\u03a3\u03c5\u03bd\u03b4\u03c5\u03ac\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7 \u03c0\u03bf\u03bb\u03bb\u03ce\u03bd \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03c9\u03bd" } \ No newline at end of file diff --git a/homeassistant/components/min_max/translations/hu.json b/homeassistant/components/min_max/translations/hu.json index 7330df0d3e0..cd4d7e2e92a 100644 --- a/homeassistant/components/min_max/translations/hu.json +++ b/homeassistant/components/min_max/translations/hu.json @@ -9,10 +9,10 @@ "type": "Statisztikai jellemz\u0151" }, "data_description": { - "round_digits": "Szab\u00e1lyozza a tizedesjegyek sz\u00e1m\u00e1t, amikor a statisztikai jellemz\u0151 az \u00e1tlag vagy a medi\u00e1n." + "round_digits": "Szab\u00e1lyozza a tizedesjegyek sz\u00e1m\u00e1t, amikor a statisztikai jellemz\u0151 az \u00e1tlag, medi\u00e1n, vagy \u00f6sszeg." }, - "description": "A pontoss\u00e1g a tizedesjegyek sz\u00e1m\u00e1t szab\u00e1lyozza, ha a statisztikai jellemz\u0151 az \u00e1tlag vagy a medi\u00e1n.", - "title": "Min / max / \u00e1tlag / medi\u00e1n \u00e9rz\u00e9kel\u0151 hozz\u00e1ad\u00e1sa" + "description": "A bemeneti \u00e9rz\u00e9kel\u0151k list\u00e1j\u00e1b\u00f3l min, max, \u00e1tlag, medi\u00e1n vagy \u00f6sszeg \u00e9rt\u00e9ket kisz\u00e1m\u00edt\u00f3 \u00e9rz\u00e9kel\u0151 l\u00e9trehoz\u00e1sa.", + "title": "T\u00f6bb \u00e9rz\u00e9kel\u0151 \u00e1llapot\u00e1nak kombin\u00e1l\u00e1sa" } } }, @@ -25,10 +25,10 @@ "type": "Statisztikai jellemz\u0151" }, "data_description": { - "round_digits": "Szab\u00e1lyozza a tizedesjegyek sz\u00e1m\u00e1t, amikor a statisztikai jellemz\u0151 az \u00e1tlag vagy a medi\u00e1n." + "round_digits": "Szab\u00e1lyozza a tizedesjegyek sz\u00e1m\u00e1t, amikor a statisztikai jellemz\u0151 az \u00e1tlag, medi\u00e1n, vagy \u00f6sszeg." } } } }, - "title": "Min / max / \u00e1tlag / medi\u00e1n \u00e9rz\u00e9kel\u0151" + "title": "T\u00f6bb \u00e9rz\u00e9kel\u0151 \u00e1llapot\u00e1nak kombin\u00e1l\u00e1sa" } \ No newline at end of file diff --git a/homeassistant/components/min_max/translations/it.json b/homeassistant/components/min_max/translations/it.json index 4574b80e863..895c50439f6 100644 --- a/homeassistant/components/min_max/translations/it.json +++ b/homeassistant/components/min_max/translations/it.json @@ -9,10 +9,10 @@ "type": "Caratteristica statistica" }, "data_description": { - "round_digits": "Controlla il numero di cifre decimali nell'output quando la caratteristica statistica \u00e8 media o mediana." + "round_digits": "Controlla il numero di cifre decimali nell'output quando la caratteristica statistica \u00e8 media, mediana o somma." }, - "description": "Crea un sensore che calcoli un valore minimo, massimo, medio o mediano da un elenco di sensori di input.", - "title": "Aggiungi sensore min / max / media / mediana" + "description": "Crea un sensore che calcola un minimo, un massimo, una media, una mediana o una somma da un elenco di sensori di input.", + "title": "Combina lo stato di diversi sensori" } } }, @@ -25,10 +25,10 @@ "type": "Caratteristica statistica" }, "data_description": { - "round_digits": "Controlla il numero di cifre decimali nell'output quando la caratteristica statistica \u00e8 media o mediana." + "round_digits": "Controlla il numero di cifre decimali nell'output quando la caratteristica statistica \u00e8 media, mediana o somma." } } } }, - "title": "Sensore min / max / media / mediana" + "title": "Combina lo stato di diversi sensori" } \ No newline at end of file diff --git a/homeassistant/components/min_max/translations/nl.json b/homeassistant/components/min_max/translations/nl.json index eb2106cc366..84b033d8715 100644 --- a/homeassistant/components/min_max/translations/nl.json +++ b/homeassistant/components/min_max/translations/nl.json @@ -12,7 +12,7 @@ "round_digits": "Regelt het aantal decimale cijfers in de uitvoer wanneer de statistische eigenschap gemiddelde of mediaan is." }, "description": "Maak een sensor die een min, max, gemiddelde of mediaanwaarde berekent uit een lijst van invoersensoren.", - "title": "Voeg min / max / gemiddelde / mediaan sensor toe" + "title": "Voeg min / max / gemiddelde / mediaan / som sensor toe" } } }, diff --git a/homeassistant/components/min_max/translations/pl.json b/homeassistant/components/min_max/translations/pl.json index cf4157eb612..40b8cecd421 100644 --- a/homeassistant/components/min_max/translations/pl.json +++ b/homeassistant/components/min_max/translations/pl.json @@ -9,10 +9,10 @@ "type": "Charakterystyka statystyczna" }, "data_description": { - "round_digits": "Kontroluje liczb\u0119 cyfr dziesi\u0119tnych w danych wyj\u015bciowych, gdy charakterystyka statystyki jest \u015bredni\u0105 lub median\u0105." + "round_digits": "Kontroluje liczb\u0119 cyfr dziesi\u0119tnych w danych wyj\u015bciowych, gdy charakterystyka statystyki jest \u015bredni\u0105, median\u0105 lub sum\u0105." }, - "description": "Utw\u00f3rz sensor, kt\u00f3ry oblicza warto\u015b\u0107 minimaln\u0105, maksymaln\u0105, \u015bredni\u0105 lub median\u0119 z listy sensor\u00f3w wej\u015bciowych.", - "title": "Dodawanie sensora min / maks / \u015brednia / mediana" + "description": "Utw\u00f3rz sensor, kt\u00f3ry oblicza warto\u015b\u0107 minimaln\u0105, maksymaln\u0105, \u015bredni\u0105, median\u0119 lub sum\u0119 z listy sensor\u00f3w wej\u015bciowych.", + "title": "Po\u0142\u0105czenie stanu kilku sensor\u00f3w" } } }, @@ -25,10 +25,10 @@ "type": "Charakterystyka statystyczna" }, "data_description": { - "round_digits": "Kontroluje liczb\u0119 cyfr dziesi\u0119tnych w danych wyj\u015bciowych, gdy charakterystyka statystyki jest \u015bredni\u0105 lub median\u0105." + "round_digits": "Kontroluje liczb\u0119 cyfr dziesi\u0119tnych w danych wyj\u015bciowych, gdy charakterystyka statystyki jest \u015bredni\u0105, median\u0105 lub sum\u0105." } } } }, - "title": "Sensor min./maks./\u015brednia/mediana" + "title": "Po\u0142\u0105czenie stanu kilku sensor\u00f3w" } \ No newline at end of file diff --git a/homeassistant/components/min_max/translations/sk.json b/homeassistant/components/min_max/translations/sk.json index 18f720b8c2b..73104641e11 100644 --- a/homeassistant/components/min_max/translations/sk.json +++ b/homeassistant/components/min_max/translations/sk.json @@ -5,8 +5,13 @@ "data": { "entity_ids": "Vstupn\u00e9 entity", "name": "N\u00e1zov", - "round_digits": "Presnos\u0165" + "round_digits": "Presnos\u0165", + "type": "\u0160tatistika" }, + "data_description": { + "round_digits": "Riadi po\u010det desatinn\u00fdch \u010d\u00edslic vo v\u00fdstupe, ke\u010f je \u0161tatistikou priemer, medi\u00e1n alebo s\u00fa\u010det." + }, + "description": "Vytvorte sn\u00edma\u010d, ktor\u00fd vypo\u010d\u00edta minimum, maximum, priemer alebo s\u00fa\u010det zo zoznamu vstupn\u00fdch sn\u00edma\u010dov.", "title": "Skombinujte stav nieko\u013ek\u00fdch sn\u00edma\u010dov" } } @@ -16,7 +21,11 @@ "init": { "data": { "entity_ids": "Vstupn\u00e9 entity", - "round_digits": "Presnos\u0165" + "round_digits": "Presnos\u0165", + "type": "\u0160tatistika" + }, + "data_description": { + "round_digits": "Riadi po\u010det desatinn\u00fdch \u010d\u00edslic vo v\u00fdstupe, ke\u010f je \u0161tatistikou priemer, medi\u00e1n alebo s\u00fa\u010det." } } } diff --git a/homeassistant/components/minecraft_server/__init__.py b/homeassistant/components/minecraft_server/__init__.py index b2f7698d969..dad8ebe7f11 100644 --- a/homeassistant/components/minecraft_server/__init__.py +++ b/homeassistant/components/minecraft_server/__init__.py @@ -143,7 +143,10 @@ class MinecraftServer: self.online = True except OSError as error: _LOGGER.debug( - "Error occurred while trying to check the connection to '%s:%s' - OSError: %s", + ( + "Error occurred while trying to check the connection to '%s:%s' -" + " OSError: %s" + ), self.host, self.port, error, diff --git a/homeassistant/components/minecraft_server/sensor.py b/homeassistant/components/minecraft_server/sensor.py index f10a359eca0..36d78520565 100644 --- a/homeassistant/components/minecraft_server/sensor.py +++ b/homeassistant/components/minecraft_server/sensor.py @@ -3,7 +3,7 @@ from __future__ import annotations from homeassistant.components.sensor import SensorEntity from homeassistant.config_entries import ConfigEntry -from homeassistant.const import TIME_MILLISECONDS +from homeassistant.const import UnitOfTime from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -114,7 +114,7 @@ class MinecraftServerLatencyTimeSensor(MinecraftServerSensorEntity): server=server, type_name=NAME_LATENCY_TIME, icon=ICON_LATENCY_TIME, - unit=TIME_MILLISECONDS, + unit=UnitOfTime.MILLISECONDS, ) async def async_update(self) -> None: diff --git a/homeassistant/components/minio/__init__.py b/homeassistant/components/minio/__init__.py index 89c5687075f..1f325f3866d 100644 --- a/homeassistant/components/minio/__init__.py +++ b/homeassistant/components/minio/__init__.py @@ -136,8 +136,7 @@ def setup(hass: HomeAssistant, config: ConfigType) -> bool: file_path = _render_service_value(service, ATTR_FILE_PATH) if not hass.config.is_allowed_path(file_path): - _LOGGER.error("Invalid file_path %s", file_path) - return + raise ValueError(f"Invalid file_path {file_path}") minio_client.fput_object(bucket, key, file_path) @@ -148,8 +147,7 @@ def setup(hass: HomeAssistant, config: ConfigType) -> bool: file_path = _render_service_value(service, ATTR_FILE_PATH) if not hass.config.is_allowed_path(file_path): - _LOGGER.error("Invalid file_path %s", file_path) - return + raise ValueError(f"Invalid file_path {file_path}") minio_client.fget_object(bucket, key, file_path) diff --git a/homeassistant/components/minio/manifest.json b/homeassistant/components/minio/manifest.json index f89db2346d9..ce3b7f141d9 100644 --- a/homeassistant/components/minio/manifest.json +++ b/homeassistant/components/minio/manifest.json @@ -2,7 +2,7 @@ "domain": "minio", "name": "Minio", "documentation": "https://www.home-assistant.io/integrations/minio", - "requirements": ["minio==5.0.10"], + "requirements": ["minio==7.1.12"], "codeowners": ["@tkislan"], "iot_class": "cloud_push", "loggers": ["minio"] diff --git a/homeassistant/components/minio/minio_helper.py b/homeassistant/components/minio/minio_helper.py index 4f10da10998..75a8d003aeb 100644 --- a/homeassistant/components/minio/minio_helper.py +++ b/homeassistant/components/minio/minio_helper.py @@ -34,7 +34,9 @@ def create_minio_client( endpoint: str, access_key: str, secret_key: str, secure: bool ) -> Minio: """Create Minio client.""" - return Minio(endpoint, access_key, secret_key, secure) + return Minio( + endpoint=endpoint, access_key=access_key, secret_key=secret_key, secure=secure + ) def get_minio_notification_response( diff --git a/homeassistant/components/mitemp_bt/translations/sk.json b/homeassistant/components/mitemp_bt/translations/sk.json new file mode 100644 index 00000000000..69e604ed965 --- /dev/null +++ b/homeassistant/components/mitemp_bt/translations/sk.json @@ -0,0 +1,8 @@ +{ + "issues": { + "replaced": { + "description": "Integr\u00e1cia sn\u00edma\u010da teploty a vlhkosti Xiaomi Mijia BLE prestala fungova\u0165 v Home Assistant 2022.7 a bola nahraden\u00e1 integr\u00e1ciou Xiaomi BLE vo verzii 2022.8. \n\n Nie je mo\u017en\u00e1 \u017eiadna migra\u010dn\u00e1 cesta, preto mus\u00edte svoje zariadenie Xiaomi Mijia BLE prida\u0165 pomocou novej integr\u00e1cie manu\u00e1lne. \n\n Dom\u00e1ce asistent u\u017e nepou\u017e\u00edva va\u0161u existuj\u00facu konfigur\u00e1ciu YAML sn\u00edma\u010da teploty a vlhkosti Xiaomi Mijia BLE. Ak chcete tento probl\u00e9m vyrie\u0161i\u0165, odstr\u00e1\u0148te konfigur\u00e1ciu YAML zo s\u00faboru configuration.yaml a re\u0161tartujte aplik\u00e1ciu Home Assistant.", + "title": "Integr\u00e1cia sn\u00edma\u010da teploty a vlhkosti Xiaomi Mijia BLE bola nahraden\u00e1" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/mjpeg/translations/ko.json b/homeassistant/components/mjpeg/translations/ko.json new file mode 100644 index 00000000000..8ffe6aad47a --- /dev/null +++ b/homeassistant/components/mjpeg/translations/ko.json @@ -0,0 +1,38 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "name": "\uc774\ub984", + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984", + "verify_ssl": "SSL \uc778\uc99d\uc11c \ud655\uc778" + } + } + } + }, + "options": { + "error": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "step": { + "init": { + "data": { + "name": "\uc774\ub984", + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984", + "verify_ssl": "SSL \uc778\uc99d\uc11c \ud655\uc778" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/mjpeg/translations/sk.json b/homeassistant/components/mjpeg/translations/sk.json index d669ecdfa1c..4939f7e66d2 100644 --- a/homeassistant/components/mjpeg/translations/sk.json +++ b/homeassistant/components/mjpeg/translations/sk.json @@ -13,6 +13,7 @@ "mjpeg_url": "MJPEG URL", "name": "N\u00e1zov", "password": "Heslo", + "still_image_url": "Adresa URL statick\u00e9ho obr\u00e1zka", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno", "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" } @@ -31,6 +32,7 @@ "mjpeg_url": "MJPEG URL", "name": "N\u00e1zov", "password": "Heslo", + "still_image_url": "Adresa URL statick\u00e9ho obr\u00e1zka", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno", "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" } diff --git a/homeassistant/components/moat/sensor.py b/homeassistant/components/moat/sensor.py index 29133e11283..fc83d1fe757 100644 --- a/homeassistant/components/moat/sensor.py +++ b/homeassistant/components/moat/sensor.py @@ -20,10 +20,10 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.const import ( - ELECTRIC_POTENTIAL_VOLT, PERCENTAGE, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, - TEMP_CELSIUS, + UnitOfElectricPotential, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -35,7 +35,7 @@ SENSOR_DESCRIPTIONS = { (DeviceClass.TEMPERATURE, Units.TEMP_CELSIUS): SensorEntityDescription( key=f"{DeviceClass.TEMPERATURE}_{Units.TEMP_CELSIUS}", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, ), (DeviceClass.HUMIDITY, Units.PERCENTAGE): SensorEntityDescription( @@ -53,7 +53,7 @@ SENSOR_DESCRIPTIONS = { (DeviceClass.VOLTAGE, Units.ELECTRIC_POTENTIAL_VOLT): SensorEntityDescription( key=f"{DeviceClass.VOLTAGE}_{Units.ELECTRIC_POTENTIAL_VOLT}", device_class=SensorDeviceClass.VOLTAGE, - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, state_class=SensorStateClass.MEASUREMENT, ), ( diff --git a/homeassistant/components/moat/translations/en.json b/homeassistant/components/moat/translations/en.json index d24df64f135..f99ec0bbe63 100644 --- a/homeassistant/components/moat/translations/en.json +++ b/homeassistant/components/moat/translations/en.json @@ -8,13 +8,13 @@ "flow_title": "{name}", "step": { "bluetooth_confirm": { - "description": "Do you want to setup {name}?" + "description": "Do you want to set up {name}?" }, "user": { "data": { "address": "Device" }, - "description": "Choose a device to setup" + "description": "Choose a device to set up" } } } diff --git a/homeassistant/components/moat/translations/it.json b/homeassistant/components/moat/translations/it.json index 501b5095826..3ede7709c00 100644 --- a/homeassistant/components/moat/translations/it.json +++ b/homeassistant/components/moat/translations/it.json @@ -14,7 +14,7 @@ "data": { "address": "Dispositivo" }, - "description": "Seleziona un dispositivo da configurare" + "description": "Scegli un dispositivo da configurare" } } } diff --git a/homeassistant/components/moat/translations/ko.json b/homeassistant/components/moat/translations/ko.json new file mode 100644 index 00000000000..1a59c05b30c --- /dev/null +++ b/homeassistant/components/moat/translations/ko.json @@ -0,0 +1,9 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", + "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/moat/translations/no.json b/homeassistant/components/moat/translations/no.json index 28ec4582177..0a44ef08d80 100644 --- a/homeassistant/components/moat/translations/no.json +++ b/homeassistant/components/moat/translations/no.json @@ -8,7 +8,7 @@ "flow_title": "{name}", "step": { "bluetooth_confirm": { - "description": "Vil du konfigurere {name}?" + "description": "Vil du sette opp {name} ?" }, "user": { "data": { diff --git a/homeassistant/components/moat/translations/pt.json b/homeassistant/components/moat/translations/pt.json new file mode 100644 index 00000000000..c6a15010072 --- /dev/null +++ b/homeassistant/components/moat/translations/pt.json @@ -0,0 +1,9 @@ +{ + "config": { + "abort": { + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", + "already_in_progress": "O processo de configura\u00e7\u00e3o j\u00e1 est\u00e1 a decorrer", + "no_devices_found": "Nenhum dispositivo encontrado na rede" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/mobile_app/sensor.py b/homeassistant/components/mobile_app/sensor.py index d802dc92b52..0d2c05df518 100644 --- a/homeassistant/components/mobile_app/sensor.py +++ b/homeassistant/components/mobile_app/sensor.py @@ -5,7 +5,7 @@ from typing import Any from homeassistant.components.sensor import RestoreSensor, SensorDeviceClass from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_WEBHOOK_ID, STATE_UNKNOWN, TEMP_CELSIUS +from homeassistant.const import CONF_WEBHOOK_ID, STATE_UNKNOWN, UnitOfTemperature from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import entity_registry as er from homeassistant.helpers.dispatcher import async_dispatcher_connect @@ -92,7 +92,7 @@ class MobileAppSensor(MobileAppEntity, RestoreSensor): self.device_class == SensorDeviceClass.TEMPERATURE and sensor_unique_id == "battery_temperature" ): - self._config[ATTR_SENSOR_UOM] = TEMP_CELSIUS + self._config[ATTR_SENSOR_UOM] = UnitOfTemperature.CELSIUS return self._config[ATTR_SENSOR_STATE] = last_sensor_data.native_value diff --git a/homeassistant/components/mobile_app/translations/de.json b/homeassistant/components/mobile_app/translations/de.json index 721cbc09f8d..c503f2c48c7 100644 --- a/homeassistant/components/mobile_app/translations/de.json +++ b/homeassistant/components/mobile_app/translations/de.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "install_app": "\u00d6ffne die mobile App, um die Integration mit Home Assistant einzurichten. Eine Liste der kompatiblen Apps gibt es hier [the docs] ({apps_url})." + "install_app": "\u00d6ffne die mobile App, um die Integration mit Home Assistant einzurichten. Eine Liste der kompatiblen Apps gibt es [hier] ({apps_url})." }, "step": { "confirm": { diff --git a/homeassistant/components/mobile_app/translations/he.json b/homeassistant/components/mobile_app/translations/he.json index 022accb4abc..38052813284 100644 --- a/homeassistant/components/mobile_app/translations/he.json +++ b/homeassistant/components/mobile_app/translations/he.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "install_app": "\u05d9\u05e9 \u05dc\u05e4\u05ea\u05d5\u05d7 \u05d0\u05ea \u05d4\u05d9\u05d9\u05e9\u05d5\u05dd \u05dc\u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05e0\u05d9\u05d9\u05d3\u05d9\u05dd \u05db\u05d3\u05d9 \u05dc\u05d4\u05d2\u05d3\u05d9\u05e8 \u05d0\u05ea \u05d4\u05e9\u05d9\u05dc\u05d5\u05d1 \u05e2\u05dd Home Assistant. \u05d9\u05e9 \u05dc\u05e2\u05d9\u05d9\u05df [\u05d1\u05de\u05e1\u05de\u05db\u05d9\u05dd]({apps_url}) \u05dc\u05e7\u05d1\u05dc\u05ea \u05e8\u05e9\u05d9\u05de\u05d4 \u05e9\u05dc \u05d9\u05d9\u05e9\u05d5\u05de\u05d9\u05dd \u05ea\u05d5\u05d0\u05de\u05d9\u05dd." + "install_app": "\u05d9\u05e9 \u05dc\u05e4\u05ea\u05d5\u05d7 \u05d0\u05ea \u05d4\u05d9\u05d9\u05e9\u05d5\u05dd \u05dc\u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05e0\u05d9\u05d9\u05d3\u05d9\u05dd \u05db\u05d3\u05d9 \u05dc\u05d4\u05d2\u05d3\u05d9\u05e8 \u05d0\u05ea \u05d4\u05e9\u05d9\u05dc\u05d5\u05d1 \u05e2\u05dd Home Assistant. \u05d9\u05e9 \u05dc\u05e2\u05d9\u05d9\u05df [\u05d1\u05de\u05e1\u05de\u05db\u05d9\u05dd]({apps_url}) \u05dc\u05e7\u05d1\u05dc\u05ea \u05e8\u05e9\u05d9\u05de\u05d4 \u05e9\u05dc \u05d9\u05d9\u05e9\u05d5\u05de\u05d9\u05dd \u05ea\u05d5\u05d0\u05de\u05d9\u05dd." }, "step": { "confirm": { diff --git a/homeassistant/components/mobile_app/webhook.py b/homeassistant/components/mobile_app/webhook.py index 2dd578a3fea..107058352c1 100644 --- a/homeassistant/components/mobile_app/webhook.py +++ b/homeassistant/components/mobile_app/webhook.py @@ -268,8 +268,10 @@ async def webhook_call_service( ) except (vol.Invalid, ServiceNotFound, Exception) as ex: _LOGGER.error( - "Error when calling service during mobile_app " - "webhook (device name: %s): %s", + ( + "Error when calling service during mobile_app " + "webhook (device name: %s): %s" + ), config_entry.data[ATTR_DEVICE_NAME], ex, ) diff --git a/homeassistant/components/modbus/base_platform.py b/homeassistant/components/modbus/base_platform.py index 0727fa81e44..9463847a4ee 100644 --- a/homeassistant/components/modbus/base_platform.py +++ b/homeassistant/components/modbus/base_platform.py @@ -322,7 +322,10 @@ class BaseSwitch(BasePlatform, ToggleEntity, RestoreEntity): self._attr_is_on = False elif value is not None: _LOGGER.error( - "Unexpected response from modbus device slave %s register %s, got 0x%2x", + ( + "Unexpected response from modbus device slave %s register %s," + " got 0x%2x" + ), self._slave, self._verify_address, value, diff --git a/homeassistant/components/modbus/climate.py b/homeassistant/components/modbus/climate.py index 4e6fdf6cae7..5573ef0b7ec 100644 --- a/homeassistant/components/modbus/climate.py +++ b/homeassistant/components/modbus/climate.py @@ -17,8 +17,7 @@ from homeassistant.const import ( CONF_TEMPERATURE_UNIT, PRECISION_TENTHS, PRECISION_WHOLE, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -89,7 +88,9 @@ class ModbusThermostat(BaseStructPlatform, RestoreEntity, ClimateEntity): self._attr_current_temperature = None self._attr_target_temperature = None self._attr_temperature_unit = ( - TEMP_FAHRENHEIT if self._unit == "F" else TEMP_CELSIUS + UnitOfTemperature.FAHRENHEIT + if self._unit == "F" + else UnitOfTemperature.CELSIUS ) self._attr_precision = ( PRECISION_TENTHS if self._precision >= 1 else PRECISION_WHOLE diff --git a/homeassistant/components/modbus/validators.py b/homeassistant/components/modbus/validators.py index 315e138e130..84e2e6d06b5 100644 --- a/homeassistant/components/modbus/validators.py +++ b/homeassistant/components/modbus/validators.py @@ -111,7 +111,7 @@ def struct_validator(config: dict[str, Any]) -> dict[str, Any]: if count < regs_needed or (count % regs_needed) != 0: raise vol.Invalid( f"Error in sensor {name} swap({swap_type}) " - f"not possible due to the registers " + "not possible due to the registers " f"count: {count}, needed: {regs_needed}" ) @@ -153,8 +153,10 @@ def scan_interval_validator(config: dict) -> dict: continue if scan_interval < 5: _LOGGER.warning( - "%s %s scan_interval(%d) is lower than 5 seconds, " - "which may cause Home Assistant stability issues", + ( + "%s %s scan_interval(%d) is lower than 5 seconds, " + "which may cause Home Assistant stability issues" + ), component, entry.get(CONF_NAME), scan_interval, @@ -198,11 +200,17 @@ def duplicate_entity_validator(config: dict) -> dict: addr += "_" + str(entry[CONF_COMMAND_OFF]) addr += "_" + str(entry.get(CONF_SLAVE, 0)) if addr in addresses: - err = f"Modbus {component}/{name} address {addr} is duplicate, second entry not loaded!" + err = ( + f"Modbus {component}/{name} address {addr} is duplicate, second" + " entry not loaded!" + ) _LOGGER.warning(err) errors.append(index) elif name in names: - err = f"Modbus {component}/{name}  is duplicate, second entry not loaded!" + err = ( + f"Modbus {component}/{name}  is duplicate, second entry not" + " loaded!" + ) _LOGGER.warning(err) errors.append(index) else: diff --git a/homeassistant/components/modem_callerid/translations/ko.json b/homeassistant/components/modem_callerid/translations/ko.json new file mode 100644 index 00000000000..6bf24434233 --- /dev/null +++ b/homeassistant/components/modem_callerid/translations/ko.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "name": "\uc774\ub984", + "port": "\ud3ec\ud2b8" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/modem_callerid/translations/sk.json b/homeassistant/components/modem_callerid/translations/sk.json index a11a0c0cc82..79d0d683eef 100644 --- a/homeassistant/components/modem_callerid/translations/sk.json +++ b/homeassistant/components/modem_callerid/translations/sk.json @@ -9,11 +9,15 @@ "cannot_connect": "Nepodarilo sa pripoji\u0165" }, "step": { + "usb_confirm": { + "description": "Toto je integr\u00e1cia pre hovory na pevn\u00fa linku pomocou hlasov\u00e9ho modemu CX93001. M\u00f4\u017eete tak z\u00edska\u0165 inform\u00e1cie o identifik\u00e1cii volaj\u00faceho s mo\u017enos\u0165ou odmietnu\u0165 prich\u00e1dzaj\u00faci hovor." + }, "user": { "data": { "name": "N\u00e1zov", "port": "Port" - } + }, + "description": "Toto je integr\u00e1cia pre hovory na pevn\u00fa linku pomocou hlasov\u00e9ho modemu CX93001. M\u00f4\u017eete tak z\u00edska\u0165 inform\u00e1cie o identifik\u00e1cii volaj\u00faceho s mo\u017enos\u0165ou odmietnu\u0165 prich\u00e1dzaj\u00faci hovor." } } } diff --git a/homeassistant/components/modern_forms/__init__.py b/homeassistant/components/modern_forms/__init__.py index 9b425f61ad0..d00fe793bf8 100644 --- a/homeassistant/components/modern_forms/__init__.py +++ b/homeassistant/components/modern_forms/__init__.py @@ -145,5 +145,8 @@ class ModernFormsDeviceEntity(CoordinatorEntity[ModernFormsDataUpdateCoordinator name=self.coordinator.data.info.device_name, manufacturer="Modern Forms", model=self.coordinator.data.info.fan_type, - sw_version=f"{self.coordinator.data.info.firmware_version} / {self.coordinator.data.info.main_mcu_firmware_version}", + sw_version=( + f"{self.coordinator.data.info.firmware_version} /" + f" {self.coordinator.data.info.main_mcu_firmware_version}" + ), ) diff --git a/homeassistant/components/modern_forms/translations/ko.json b/homeassistant/components/modern_forms/translations/ko.json index 6079edcd0da..b2ca8f24634 100644 --- a/homeassistant/components/modern_forms/translations/ko.json +++ b/homeassistant/components/modern_forms/translations/ko.json @@ -1,5 +1,9 @@ { "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" + }, "error": { "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" }, diff --git a/homeassistant/components/modern_forms/translations/pt.json b/homeassistant/components/modern_forms/translations/pt.json index ce7cbc3f548..37f4b5fedc0 100644 --- a/homeassistant/components/modern_forms/translations/pt.json +++ b/homeassistant/components/modern_forms/translations/pt.json @@ -3,7 +3,7 @@ "step": { "user": { "data": { - "host": "Servidor" + "host": "Endere\u00e7o" } } } diff --git a/homeassistant/components/modern_forms/translations/sk.json b/homeassistant/components/modern_forms/translations/sk.json index ed248c10965..769149e4835 100644 --- a/homeassistant/components/modern_forms/translations/sk.json +++ b/homeassistant/components/modern_forms/translations/sk.json @@ -12,7 +12,12 @@ "user": { "data": { "host": "Hostite\u013e" - } + }, + "description": "Nastavenie ventil\u00e1tora Modern Forms na integr\u00e1ciu s aplik\u00e1ciou Home Assistant." + }, + "zeroconf_confirm": { + "description": "Chcete prida\u0165 ventil\u00e1tor Modern Forms s n\u00e1zvom `{name}` do aplik\u00e1cie Home Assistant?", + "title": "Objaven\u00e9 ventil\u00e1torov\u00e9 zariadenie Modern Forms" } } } diff --git a/homeassistant/components/moehlenhoff_alpha2/climate.py b/homeassistant/components/moehlenhoff_alpha2/climate.py index 5eac2095706..1868be11f67 100644 --- a/homeassistant/components/moehlenhoff_alpha2/climate.py +++ b/homeassistant/components/moehlenhoff_alpha2/climate.py @@ -9,7 +9,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import CoordinatorEntity @@ -45,7 +45,7 @@ class Alpha2Climate(CoordinatorEntity[Alpha2BaseCoordinator], ClimateEntity): ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE ) _attr_hvac_modes = [HVACMode.HEAT, HVACMode.COOL] - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS _attr_preset_modes = [PRESET_AUTO, PRESET_DAY, PRESET_NIGHT] def __init__(self, coordinator: Alpha2BaseCoordinator, heat_area_id: str) -> None: diff --git a/homeassistant/components/moehlenhoff_alpha2/sensor.py b/homeassistant/components/moehlenhoff_alpha2/sensor.py index d26786e1923..e41c6b041f6 100644 --- a/homeassistant/components/moehlenhoff_alpha2/sensor.py +++ b/homeassistant/components/moehlenhoff_alpha2/sensor.py @@ -46,7 +46,10 @@ class Alpha2HeatControlValveOpeningSensor( self._attr_unique_id = f"{heat_control_id}:valve_opening" heat_control = self.coordinator.data["heat_controls"][heat_control_id] heat_area = self.coordinator.data["heat_areas"][heat_control["_HEATAREA_ID"]] - self._attr_name = f"{heat_area['HEATAREA_NAME']} heat control {heat_control['NR']} valve opening" + self._attr_name = ( + f"{heat_area['HEATAREA_NAME']} heat control {heat_control['NR']} valve" + " opening" + ) @property def native_value(self) -> int: diff --git a/homeassistant/components/moehlenhoff_alpha2/translations/ko.json b/homeassistant/components/moehlenhoff_alpha2/translations/ko.json new file mode 100644 index 00000000000..85281856809 --- /dev/null +++ b/homeassistant/components/moehlenhoff_alpha2/translations/ko.json @@ -0,0 +1,18 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/mold_indicator/sensor.py b/homeassistant/components/mold_indicator/sensor.py index f10ddbe1ab0..95cb24fe7ed 100644 --- a/homeassistant/components/mold_indicator/sensor.py +++ b/homeassistant/components/mold_indicator/sensor.py @@ -14,8 +14,7 @@ from homeassistant.const import ( EVENT_HOMEASSISTANT_START, PERCENTAGE, STATE_UNKNOWN, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant, callback import homeassistant.helpers.config_validation as cv @@ -219,16 +218,18 @@ class MoldIndicator(SensorEntity): return None # convert to celsius if necessary - if unit == TEMP_FAHRENHEIT: - return TemperatureConverter.convert(temp, TEMP_FAHRENHEIT, TEMP_CELSIUS) - if unit == TEMP_CELSIUS: + if unit == UnitOfTemperature.FAHRENHEIT: + return TemperatureConverter.convert( + temp, UnitOfTemperature.FAHRENHEIT, UnitOfTemperature.CELSIUS + ) + if unit == UnitOfTemperature.CELSIUS: return temp _LOGGER.error( "Temp sensor %s has unsupported unit: %s (allowed: %s, %s)", state.entity_id, unit, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, + UnitOfTemperature.CELSIUS, + UnitOfTemperature.FAHRENHEIT, ) return None @@ -309,7 +310,7 @@ class MoldIndicator(SensorEntity): * (alpha + math.log(self._indoor_hum / 100.0)) / (beta - math.log(self._indoor_hum / 100.0)) ) - _LOGGER.debug("Dewpoint: %f %s", self._dewpoint, TEMP_CELSIUS) + _LOGGER.debug("Dewpoint: %f %s", self._dewpoint, UnitOfTemperature.CELSIUS) def _calc_moldindicator(self): """Calculate the humidity at the (cold) calibration point.""" @@ -332,7 +333,9 @@ class MoldIndicator(SensorEntity): ) _LOGGER.debug( - "Estimated Critical Temperature: %f %s", self._crit_temp, TEMP_CELSIUS + "Estimated Critical Temperature: %f %s", + self._crit_temp, + UnitOfTemperature.CELSIUS, ) # Then calculate the humidity at this point @@ -387,13 +390,17 @@ class MoldIndicator(SensorEntity): } dewpoint = ( - TemperatureConverter.convert(self._dewpoint, TEMP_CELSIUS, TEMP_FAHRENHEIT) + TemperatureConverter.convert( + self._dewpoint, UnitOfTemperature.CELSIUS, UnitOfTemperature.FAHRENHEIT + ) if self._dewpoint is not None else None ) crit_temp = ( - TemperatureConverter.convert(self._crit_temp, TEMP_CELSIUS, TEMP_FAHRENHEIT) + TemperatureConverter.convert( + self._crit_temp, UnitOfTemperature.CELSIUS, UnitOfTemperature.FAHRENHEIT + ) if self._crit_temp is not None else None ) diff --git a/homeassistant/components/monoprice/translations/pt.json b/homeassistant/components/monoprice/translations/pt.json index d73c17a62cd..2fe5d9b29b9 100644 --- a/homeassistant/components/monoprice/translations/pt.json +++ b/homeassistant/components/monoprice/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "unknown": "Erro inesperado" }, "step": { diff --git a/homeassistant/components/moon/sensor.py b/homeassistant/components/moon/sensor.py index b033dccc296..c244f161471 100644 --- a/homeassistant/components/moon/sensor.py +++ b/homeassistant/components/moon/sensor.py @@ -6,6 +6,7 @@ import voluptuous as vol from homeassistant.components.sensor import ( PLATFORM_SCHEMA as PARENT_PLATFORM_SCHEMA, + SensorDeviceClass, SensorEntity, ) from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry @@ -27,8 +28,8 @@ STATE_LAST_QUARTER = "last_quarter" STATE_NEW_MOON = "new_moon" STATE_WANING_CRESCENT = "waning_crescent" STATE_WANING_GIBBOUS = "waning_gibbous" -STATE_WAXING_GIBBOUS = "waxing_gibbous" STATE_WAXING_CRESCENT = "waxing_crescent" +STATE_WAXING_GIBBOUS = "waxing_gibbous" MOON_ICONS = { STATE_FIRST_QUARTER: "mdi:moon-first-quarter", @@ -83,9 +84,20 @@ async def async_setup_entry( class MoonSensorEntity(SensorEntity): """Representation of a Moon sensor.""" - _attr_device_class = "moon__phase" _attr_has_entity_name = True _attr_name = "Phase" + _attr_device_class = SensorDeviceClass.ENUM + _attr_options = [ + STATE_FIRST_QUARTER, + STATE_FULL_MOON, + STATE_LAST_QUARTER, + STATE_NEW_MOON, + STATE_WANING_CRESCENT, + STATE_WANING_GIBBOUS, + STATE_WAXING_CRESCENT, + STATE_WAXING_GIBBOUS, + ] + _attr_translation_key = "phase" def __init__(self, entry: ConfigEntry) -> None: """Initialize the moon sensor.""" diff --git a/homeassistant/components/moon/strings.json b/homeassistant/components/moon/strings.json index 76ce886ded8..818460bc13d 100644 --- a/homeassistant/components/moon/strings.json +++ b/homeassistant/components/moon/strings.json @@ -15,5 +15,21 @@ "title": "The Moon YAML configuration has been removed", "description": "Configuring Moon using YAML has been removed.\n\nYour existing YAML configuration is not used by Home Assistant.\n\nRemove the YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue." } + }, + "entity": { + "sensor": { + "phase": { + "state": { + "first_quarter": "First quarter", + "full_moon": "Full moon", + "last_quarter": "Last quarter", + "new_moon": "New moon", + "waning_crescent": "Waning crescent", + "waning_gibbous": "Waning gibbous", + "waxing_crescent": "Waxing crescent", + "waxing_gibbous": "Waxing gibbous" + } + } + } } } diff --git a/homeassistant/components/moon/strings.sensor.json b/homeassistant/components/moon/strings.sensor.json deleted file mode 100644 index 3bbff0a776b..00000000000 --- a/homeassistant/components/moon/strings.sensor.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "state": { - "moon__phase": { - "new_moon": "New moon", - "waxing_crescent": "Waxing crescent", - "first_quarter": "First quarter", - "waxing_gibbous": "Waxing gibbous", - "full_moon": "Full moon", - "waning_gibbous": "Waning gibbous", - "last_quarter": "Last quarter", - "waning_crescent": "Waning crescent" - } - } -} diff --git a/homeassistant/components/moon/translations/bg.json b/homeassistant/components/moon/translations/bg.json index 47a9a365db1..0ab1984d0c6 100644 --- a/homeassistant/components/moon/translations/bg.json +++ b/homeassistant/components/moon/translations/bg.json @@ -9,6 +9,18 @@ } } }, + "entity": { + "sensor": { + "phase": { + "state": { + "first_quarter": "\u041f\u044a\u0440\u0432\u0430 \u0447\u0435\u0442\u0432\u044a\u0440\u0442", + "full_moon": "\u041f\u044a\u043b\u043d\u043e\u043b\u0443\u043d\u0438\u0435", + "last_quarter": "\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u0430 \u0447\u0435\u0442\u0432\u044a\u0440\u0442", + "new_moon": "\u041d\u043e\u0432\u043e\u043b\u0443\u043d\u0438\u0435" + } + } + } + }, "issues": { "removed_yaml": { "description": "\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0435\u0442\u043e \u043d\u0430 Moon \u0441 \u043f\u043e\u043c\u043e\u0449\u0442\u0430 \u043d\u0430 YAML \u0435 \u043f\u0440\u0435\u043c\u0430\u0445\u043d\u0430\u0442\u043e.\n\n\u0412\u0430\u0448\u0430\u0442\u0430 \u0441\u044a\u0449\u0435\u0441\u0442\u0432\u0443\u0432\u0430\u0449\u0430 YAML \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043d\u0435 \u0441\u0435 \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430 \u043e\u0442 Home Assistant.\n\n\u041f\u0440\u0435\u043c\u0430\u0445\u043d\u0435\u0442\u0435 YAML \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u043e\u0442 \u0432\u0430\u0448\u0438\u044f \u0444\u0430\u0439\u043b configuration.yaml \u0438 \u0440\u0435\u0441\u0442\u0430\u0440\u0442\u0438\u0440\u0430\u0439\u0442\u0435 Home Assistant, \u0437\u0430 \u0434\u0430 \u043a\u043e\u0440\u0438\u0433\u0438\u0440\u0430\u0442\u0435 \u0442\u043e\u0437\u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c.", diff --git a/homeassistant/components/moon/translations/ca.json b/homeassistant/components/moon/translations/ca.json index ff98a6fea96..c5f5e9ad62d 100644 --- a/homeassistant/components/moon/translations/ca.json +++ b/homeassistant/components/moon/translations/ca.json @@ -9,6 +9,22 @@ } } }, + "entity": { + "sensor": { + "phase": { + "state": { + "first_quarter": "Quart creixent", + "full_moon": "Lluna plena", + "last_quarter": "Quart minvant", + "new_moon": "Lluna nova", + "waning_crescent": "Minvant (Lluna vella)", + "waning_gibbous": "Gibosa minvant", + "waxing_crescent": "Lluna nova visible", + "waxing_gibbous": "Gibosa creixent" + } + } + } + }, "issues": { "removed_yaml": { "description": "La configuraci\u00f3 de la Lluna mitjan\u00e7ant YAML s'ha eliminat.\n\nHome Assistant ja no utilitza la configuraci\u00f3 YAML existent.\n\nElimina la configuraci\u00f3 YAML corresponent del fitxer configuration.yaml i reinicia Home Assistant per solucionar aquest problema.", diff --git a/homeassistant/components/moon/translations/cs.json b/homeassistant/components/moon/translations/cs.json index f1c26a20f6e..63c65ed07ce 100644 --- a/homeassistant/components/moon/translations/cs.json +++ b/homeassistant/components/moon/translations/cs.json @@ -9,5 +9,21 @@ } } }, + "entity": { + "sensor": { + "phase": { + "state": { + "first_quarter": "Prvn\u00ed \u010dtvr\u0165", + "full_moon": "\u00dapln\u011bk", + "last_quarter": "Posledn\u00ed \u010dtvrt", + "new_moon": "Nov", + "waning_crescent": "Couvaj\u00edc\u00ed srpek", + "waning_gibbous": "Couvaj\u00edc\u00ed m\u011bs\u00edc", + "waxing_crescent": "Dor\u016fstaj\u00edc\u00ed srpek", + "waxing_gibbous": "Dor\u016fstaj\u00edc\u00ed m\u011bs\u00edc" + } + } + } + }, "title": "M\u011bs\u00edc" } \ No newline at end of file diff --git a/homeassistant/components/moon/translations/de.json b/homeassistant/components/moon/translations/de.json index 00408fde1b0..b7baf6b444e 100644 --- a/homeassistant/components/moon/translations/de.json +++ b/homeassistant/components/moon/translations/de.json @@ -9,6 +9,22 @@ } } }, + "entity": { + "sensor": { + "phase": { + "state": { + "first_quarter": "Erstes Viertel", + "full_moon": "Vollmond", + "last_quarter": "Letztes Viertel", + "new_moon": "Neumond", + "waning_crescent": "Abnehmende Sichel", + "waning_gibbous": "Drittes Viertel", + "waxing_crescent": "Zunehmende Sichel", + "waxing_gibbous": "Zweites Viertel" + } + } + } + }, "issues": { "removed_yaml": { "description": "Das Konfigurieren von Mond mit YAML wurde entfernt. \n\nDeine vorhandene YAML-Konfiguration wird von Home Assistant nicht verwendet. \n\nEntferne die YAML-Konfiguration aus deiner configuration.yaml-Datei und starte Home Assistant neu, um dieses Problem zu beheben.", diff --git a/homeassistant/components/moon/translations/el.json b/homeassistant/components/moon/translations/el.json index 11791927ad2..d3f6b460dba 100644 --- a/homeassistant/components/moon/translations/el.json +++ b/homeassistant/components/moon/translations/el.json @@ -9,6 +9,22 @@ } } }, + "entity": { + "sensor": { + "phase": { + "state": { + "first_quarter": "\u03a0\u03c1\u03ce\u03c4\u03bf \u03c4\u03ad\u03c4\u03b1\u03c1\u03c4\u03bf", + "full_moon": "\u03a0\u03b1\u03bd\u03c3\u03ad\u03bb\u03b7\u03bd\u03bf\u03c2", + "last_quarter": "\u03a4\u03b5\u03bb\u03b5\u03c5\u03c4\u03b1\u03af\u03bf \u03c4\u03ad\u03c4\u03b1\u03c1\u03c4\u03bf", + "new_moon": "\u039d\u03ad\u03b1 \u03a3\u03b5\u03bb\u03ae\u03bd\u03b7", + "waning_crescent": "\u03a6\u03b8\u03af\u03bd\u03c9\u03bd \u039c\u03b7\u03bd\u03af\u03c3\u03ba\u03bf\u03c2", + "waning_gibbous": "\u03a6\u03b8\u03af\u03bd\u03c9\u03bd \u0391\u03bc\u03c6\u03af\u03ba\u03c5\u03c1\u03c4\u03bf\u03c2", + "waxing_crescent": "\u0391\u03cd\u03be\u03c9\u03bd \u039c\u03b7\u03bd\u03af\u03c3\u03ba\u03bf\u03c2", + "waxing_gibbous": "\u0391\u03cd\u03be\u03c9\u03bd \u0391\u03bc\u03c6\u03af\u03ba\u03c5\u03c1\u03c4\u03bf\u03c2" + } + } + } + }, "issues": { "removed_yaml": { "description": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c4\u03b7\u03c2 Moon \u03bc\u03b5 \u03c7\u03c1\u03ae\u03c3\u03b7 YAML \u03ad\u03c7\u03b5\u03b9 \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b7\u03b8\u03b5\u03af.\n\n\u0397 \u03c5\u03c0\u03ac\u03c1\u03c7\u03bf\u03c5\u03c3\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 YAML \u03b4\u03b5\u03bd \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03bf Home Assistant.\n\n\u0391\u03c6\u03b1\u03b9\u03c1\u03ad\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 YAML \u03b1\u03c0\u03cc \u03c4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf configuration.yaml \u03ba\u03b1\u03b9 \u03b5\u03c0\u03b1\u03bd\u03b5\u03ba\u03ba\u03b9\u03bd\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf Home Assistant \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c0\u03c1\u03cc\u03b2\u03bb\u03b7\u03bc\u03b1.", diff --git a/homeassistant/components/moon/translations/en.json b/homeassistant/components/moon/translations/en.json index 2f6f73a9982..15f11397a75 100644 --- a/homeassistant/components/moon/translations/en.json +++ b/homeassistant/components/moon/translations/en.json @@ -5,7 +5,23 @@ }, "step": { "user": { - "description": "Do you want to start set up?" + "description": "Do you want to start setup?" + } + } + }, + "entity": { + "sensor": { + "phase": { + "state": { + "first_quarter": "First quarter", + "full_moon": "Full moon", + "last_quarter": "Last quarter", + "new_moon": "New moon", + "waning_crescent": "Waning crescent", + "waning_gibbous": "Waning gibbous", + "waxing_crescent": "Waxing crescent", + "waxing_gibbous": "Waxing gibbous" + } } } }, diff --git a/homeassistant/components/moon/translations/es.json b/homeassistant/components/moon/translations/es.json index 2cdf14282e2..ad928ea37c8 100644 --- a/homeassistant/components/moon/translations/es.json +++ b/homeassistant/components/moon/translations/es.json @@ -9,6 +9,22 @@ } } }, + "entity": { + "sensor": { + "phase": { + "state": { + "first_quarter": "Cuarto creciente", + "full_moon": "Luna llena", + "last_quarter": "Cuarto menguante", + "new_moon": "Luna nueva", + "waning_crescent": "Luna menguante", + "waning_gibbous": "Gibosa menguante", + "waxing_crescent": "Nueva visible", + "waxing_gibbous": "Gibosa creciente" + } + } + } + }, "issues": { "removed_yaml": { "description": "Se ha eliminado la configuraci\u00f3n de Moon mediante YAML. \n\nHome Assistant no utiliza tu configuraci\u00f3n YAML existente. \n\nElimina la configuraci\u00f3n YAML de tu archivo configuration.yaml y reinicia Home Assistant para solucionar este problema.", diff --git a/homeassistant/components/moon/translations/et.json b/homeassistant/components/moon/translations/et.json index 5f51cc47ab1..88c1ad6419f 100644 --- a/homeassistant/components/moon/translations/et.json +++ b/homeassistant/components/moon/translations/et.json @@ -9,6 +9,22 @@ } } }, + "entity": { + "sensor": { + "phase": { + "state": { + "first_quarter": "Esimene veerand", + "full_moon": "T\u00e4iskuu", + "last_quarter": "Viimane veerand", + "new_moon": "Kuu loomine", + "waning_crescent": "Vanakuu", + "waning_gibbous": "Kahanev kuu", + "waxing_crescent": "Noorkuu", + "waxing_gibbous": "Kasvav kuu" + } + } + } + }, "issues": { "removed_yaml": { "description": "Mooni seadistamine YAML-i abil on eemaldatud.\n\nHome Assistant ei kasuta teie olemasolevat YAML-i konfiguratsiooni.\n\nEemaldage FAILIST CONFIGURATION.yaml YAML-konfiguratsioon ja taask\u00e4ivitage selle probleemi lahendamiseks Home Assistant.", diff --git a/homeassistant/components/moon/translations/he.json b/homeassistant/components/moon/translations/he.json index e5602d2dc15..84d0509aa13 100644 --- a/homeassistant/components/moon/translations/he.json +++ b/homeassistant/components/moon/translations/he.json @@ -9,5 +9,27 @@ } } }, + "entity": { + "sensor": { + "phase": { + "state": { + "first_quarter": "\u05e8\u05d1\u05e2\u05d5\u05df \u05e8\u05d0\u05e9\u05d5\u05df", + "full_moon": "\u05d9\u05e8\u05d7 \u05de\u05dc\u05d0", + "last_quarter": "\u05e8\u05d1\u05e2\u05d5\u05df \u05d0\u05d7\u05e8\u05d5\u05df", + "new_moon": "\u05d9\u05e8\u05d7 \u05d7\u05d3\u05e9", + "waning_crescent": "\u05e1\u05d4\u05e8 \u05d3\u05d5\u05e2\u05da", + "waning_gibbous": "\u05d2\u05d1\u05e2\u05d5\u05dc\u05d9 \u05e0\u05d5\u05d3\u05d3", + "waxing_crescent": "\u05d7\u05e6\u05d9 \u05e1\u05d4\u05e8 \u05e9\u05e2\u05d5\u05d5\u05d4", + "waxing_gibbous": "\u05e1\u05d9\u05d1\u05d9\u05ea \u05e9\u05e2\u05d5\u05d5\u05d4" + } + } + } + }, + "issues": { + "removed_yaml": { + "description": "\u05d4\u05d2\u05d3\u05e8\u05ea \u05d4\u05d9\u05e8\u05d7 \u05d1\u05d0\u05de\u05e6\u05e2\u05d5\u05ea YAML \u05d4\u05d5\u05e1\u05e8\u05d4.\n\n\u05ea\u05e6\u05d5\u05e8\u05ea YAML \u05d4\u05e7\u05d9\u05d9\u05de\u05ea \u05e9\u05dc\u05da \u05d0\u05d9\u05e0\u05d4 \u05d1\u05e9\u05d9\u05de\u05d5\u05e9 \u05e2\u05dc \u05d9\u05d3\u05d9 Home Assistant.\n\n\u05d9\u05e9 \u05dc\u05d4\u05e1\u05d9\u05e8 \u05d0\u05ea \u05ea\u05e6\u05d5\u05e8\u05ea YAML \u05de\u05e7\u05d5\u05d1\u05e5 configuration.yaml \u05d5\u05dc\u05d4\u05e4\u05e2\u05d9\u05dc \u05de\u05d7\u05d3\u05e9 \u05d0\u05ea Home Assistant \u05db\u05d3\u05d9 \u05dc\u05e4\u05ea\u05d5\u05e8 \u05d1\u05e2\u05d9\u05d4 \u05d6\u05d5.", + "title": "\u05ea\u05e6\u05d5\u05e8\u05ea YAML \u05e9\u05dc \u05d4\u05d9\u05e8\u05d7 \u05d4\u05d5\u05e1\u05e8\u05d4" + } + }, "title": "\u05d9\u05e8\u05d7" } \ No newline at end of file diff --git a/homeassistant/components/moon/translations/hu.json b/homeassistant/components/moon/translations/hu.json index ed7722ed484..cd1b3e56498 100644 --- a/homeassistant/components/moon/translations/hu.json +++ b/homeassistant/components/moon/translations/hu.json @@ -9,6 +9,22 @@ } } }, + "entity": { + "sensor": { + "phase": { + "state": { + "first_quarter": "Els\u0151 negyed", + "full_moon": "Telihold", + "last_quarter": "Utols\u00f3 negyed", + "new_moon": "\u00dajhold", + "waning_crescent": "Fogy\u00f3 holdsarl\u00f3", + "waning_gibbous": "Fogy\u00f3 hold", + "waxing_crescent": "N\u00f6vekv\u0151 holdsarl\u00f3", + "waxing_gibbous": "N\u00f6vekv\u0151 hold" + } + } + } + }, "issues": { "removed_yaml": { "description": "A Moon YAML haszn\u00e1lat\u00e1val t\u00f6rt\u00e9n\u0151 konfigur\u00e1l\u00e1sa elt\u00e1vol\u00edt\u00e1sra ker\u00fclt.\n\nA megl\u00e9v\u0151 YAML konfigur\u00e1ci\u00f3t a Home Assistant nem haszn\u00e1lja.\n\nA probl\u00e9ma megold\u00e1s\u00e1hoz t\u00e1vol\u00edtsa el a YAML konfigur\u00e1ci\u00f3t a configuration.yaml f\u00e1jlb\u00f3l, \u00e9s ind\u00edtsa \u00fajra a Home Assistantot.", diff --git a/homeassistant/components/moon/translations/id.json b/homeassistant/components/moon/translations/id.json index 01c0ce2df14..f6f90ad891b 100644 --- a/homeassistant/components/moon/translations/id.json +++ b/homeassistant/components/moon/translations/id.json @@ -9,6 +9,22 @@ } } }, + "entity": { + "sensor": { + "phase": { + "state": { + "first_quarter": "Seperempat pertama", + "full_moon": "Bulan purnama", + "last_quarter": "Seperempat terakhir", + "new_moon": "Bulan baru", + "waning_crescent": "Bulan sabit tua", + "waning_gibbous": "Bulan cembung tua", + "waxing_crescent": "Bulan sabit muda", + "waxing_gibbous": "Bulan cembung muda" + } + } + } + }, "issues": { "removed_yaml": { "description": "Proses konfigurasi Integrasi Bulan lewat YAML telah dihapus.\n\nKonfigurasi YAML yang ada tidak digunakan oleh Home Assistant.\n\nHapus konfigurasi YAML dari file configuration.yaml dan mulai ulang Home Assistant untuk memperbaiki masalah ini.", diff --git a/homeassistant/components/moon/translations/it.json b/homeassistant/components/moon/translations/it.json index f510b6b5837..0c5fa521e55 100644 --- a/homeassistant/components/moon/translations/it.json +++ b/homeassistant/components/moon/translations/it.json @@ -5,7 +5,23 @@ }, "step": { "user": { - "description": "Vuoi iniziare la configurazione?" + "description": "Vuoi avviare la configurazione?" + } + } + }, + "entity": { + "sensor": { + "phase": { + "state": { + "first_quarter": "Primo quarto", + "full_moon": "Luna piena", + "last_quarter": "Ultimo quarto", + "new_moon": "Novilunio", + "waning_crescent": "Luna calante", + "waning_gibbous": "Gibbosa calante", + "waxing_crescent": "Luna crescente", + "waxing_gibbous": "Gibbosa crescente" + } } } }, diff --git a/homeassistant/components/moon/translations/ko.json b/homeassistant/components/moon/translations/ko.json index 758f3336cd4..251c9777b6c 100644 --- a/homeassistant/components/moon/translations/ko.json +++ b/homeassistant/components/moon/translations/ko.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." + }, "step": { "user": { "description": "\uc124\uc815\uc744 \uc2dc\uc791\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" diff --git a/homeassistant/components/moon/translations/nl.json b/homeassistant/components/moon/translations/nl.json index 0d626abfd7b..241c41ea21e 100644 --- a/homeassistant/components/moon/translations/nl.json +++ b/homeassistant/components/moon/translations/nl.json @@ -9,5 +9,14 @@ } } }, + "entity": { + "sensor": { + "phase": { + "state": { + "full_moon": "Volle maan" + } + } + } + }, "title": "Maan" } \ No newline at end of file diff --git a/homeassistant/components/moon/translations/no.json b/homeassistant/components/moon/translations/no.json index a4010e77787..fb4b7edf113 100644 --- a/homeassistant/components/moon/translations/no.json +++ b/homeassistant/components/moon/translations/no.json @@ -9,6 +9,22 @@ } } }, + "entity": { + "sensor": { + "phase": { + "state": { + "first_quarter": "F\u00f8rste kvarter", + "full_moon": "Fullm\u00e5ne", + "last_quarter": "Siste kvartal", + "new_moon": "Nym\u00e5ne", + "waning_crescent": "Avtagende halvm\u00e5ne", + "waning_gibbous": "Avtagende trekvartm\u00e5ne", + "waxing_crescent": "Voksende halvm\u00e5ne", + "waxing_gibbous": "Voksende m\u00e5ne" + } + } + } + }, "issues": { "removed_yaml": { "description": "Konfigurering av Moon ved hjelp av YAML er fjernet. \n\n Din eksisterende YAML-konfigurasjon brukes ikke av Home Assistant. \n\n Fjern YAML-konfigurasjonen fra configuration.yaml-filen og start Home Assistant p\u00e5 nytt for \u00e5 fikse dette problemet.", diff --git a/homeassistant/components/moon/translations/pl.json b/homeassistant/components/moon/translations/pl.json index c3ebaeca4a8..4da1361a148 100644 --- a/homeassistant/components/moon/translations/pl.json +++ b/homeassistant/components/moon/translations/pl.json @@ -9,6 +9,22 @@ } } }, + "entity": { + "sensor": { + "phase": { + "state": { + "first_quarter": "pierwsza kwadra", + "full_moon": "pe\u0142nia", + "last_quarter": "ostatnia kwadra", + "new_moon": "n\u00f3w", + "waning_crescent": "sierp ubywaj\u0105cy", + "waning_gibbous": "ubywaj\u0105cy garbaty", + "waxing_crescent": "sierp przybywaj\u0105cy", + "waxing_gibbous": "przybywaj\u0105cy garbaty" + } + } + } + }, "issues": { "removed_yaml": { "description": "Konfiguracja Ksi\u0119\u017cyca za pomoc\u0105 YAML zosta\u0142a usuni\u0119ta. \n\nTwoja istniej\u0105ca konfiguracja YAML nie jest u\u017cywana przez Home Assistant. \n\nUsu\u0144 konfiguracj\u0119 YAML z pliku configuration.yaml i uruchom ponownie Home Assistant, aby rozwi\u0105za\u0107 ten problem.", diff --git a/homeassistant/components/moon/translations/pt-BR.json b/homeassistant/components/moon/translations/pt-BR.json index 1e2d6c5c3f0..0f0ee93e559 100644 --- a/homeassistant/components/moon/translations/pt-BR.json +++ b/homeassistant/components/moon/translations/pt-BR.json @@ -9,6 +9,22 @@ } } }, + "entity": { + "sensor": { + "phase": { + "state": { + "first_quarter": "Primeiro quarto", + "full_moon": "Lua cheia", + "last_quarter": "\u00daltimo quarto", + "new_moon": "Lua nova", + "waning_crescent": "Crescente minguante", + "waning_gibbous": "Minguante", + "waxing_crescent": "Quarto crescente", + "waxing_gibbous": "Crescente" + } + } + } + }, "issues": { "removed_yaml": { "description": "A configura\u00e7\u00e3o da Moon usando YAML foi removida. \n\n Sua configura\u00e7\u00e3o YAML existente n\u00e3o \u00e9 usada pelo Home Assistant. \n\n Remova a configura\u00e7\u00e3o YAML do arquivo configuration.yaml e reinicie o Home Assistant para corrigir esse problema.", diff --git a/homeassistant/components/moon/translations/ru.json b/homeassistant/components/moon/translations/ru.json index cc9beaddb52..09b5191d91b 100644 --- a/homeassistant/components/moon/translations/ru.json +++ b/homeassistant/components/moon/translations/ru.json @@ -9,6 +9,22 @@ } } }, + "entity": { + "sensor": { + "phase": { + "state": { + "first_quarter": "\u041f\u0435\u0440\u0432\u0430\u044f \u0447\u0435\u0442\u0432\u0435\u0440\u0442\u044c", + "full_moon": "\u041f\u043e\u043b\u043d\u043e\u043b\u0443\u043d\u0438\u0435", + "last_quarter": "\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u044f\u044f \u0447\u0435\u0442\u0432\u0435\u0440\u0442\u044c", + "new_moon": "\u041d\u043e\u0432\u043e\u043b\u0443\u043d\u0438\u0435", + "waning_crescent": "\u0421\u0442\u0430\u0440\u0430\u044f \u043b\u0443\u043d\u0430", + "waning_gibbous": "\u0423\u0431\u044b\u0432\u0430\u044e\u0449\u0430\u044f \u043b\u0443\u043d\u0430", + "waxing_crescent": "\u041c\u043e\u043b\u043e\u0434\u0430\u044f \u043b\u0443\u043d\u0430", + "waxing_gibbous": "\u041f\u0440\u0438\u0431\u044b\u0432\u0430\u044e\u0449\u0430\u044f \u043b\u0443\u043d\u0430" + } + } + } + }, "issues": { "removed_yaml": { "description": "\u0418\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0441\u0435\u043d\u0441\u043e\u0440\u0430 \u041b\u0443\u043d\u044b \u0442\u0435\u043f\u0435\u0440\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u0430 \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0435\u0440\u0435\u0437 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441.\n\n\u0412\u0430\u0448\u0430 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0430\u044f YAML-\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f Home Assistant. \u0423\u0434\u0430\u043b\u0438\u0442\u0435 \u0435\u0451 \u0438\u0437 \u0444\u0430\u0439\u043b\u0430 configuration.yaml \u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 Home Assistant, \u0447\u0442\u043e\u0431\u044b \u0443\u0441\u0442\u0440\u0430\u043d\u0438\u0442\u044c \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443.", diff --git a/homeassistant/components/moon/translations/sensor.sk.json b/homeassistant/components/moon/translations/sensor.sk.json index 4adfde603cf..1f288127a9b 100644 --- a/homeassistant/components/moon/translations/sensor.sk.json +++ b/homeassistant/components/moon/translations/sensor.sk.json @@ -4,7 +4,11 @@ "first_quarter": "Prv\u00e1 \u0161tvrtina", "full_moon": "Spln", "last_quarter": "Posledn\u00e1 \u0161tvrtina", - "new_moon": "Nov mesiac" + "new_moon": "Nov mesiac", + "waning_crescent": "Ub\u00fadaj\u00faci polmesiac", + "waning_gibbous": "Ub\u00fadaj\u00faci mesiac", + "waxing_crescent": "Dorastaj\u00faci polmesiac", + "waxing_gibbous": "Dorastaj\u00faci mesiac" } } } \ No newline at end of file diff --git a/homeassistant/components/moon/translations/sk.json b/homeassistant/components/moon/translations/sk.json index 6ba11236f08..4db90866d94 100644 --- a/homeassistant/components/moon/translations/sk.json +++ b/homeassistant/components/moon/translations/sk.json @@ -8,5 +8,28 @@ "description": "Chcete za\u010da\u0165 nastavova\u0165?" } } - } + }, + "entity": { + "sensor": { + "phase": { + "state": { + "first_quarter": "Prv\u00e1 \u0161tvrtina", + "full_moon": "Spln", + "last_quarter": "Posledn\u00e1 \u0161tvrtina", + "new_moon": "Nov mesiac", + "waning_crescent": "Ub\u00fadaj\u00faci polmesiac", + "waning_gibbous": "Ub\u00fadaj\u00faci mesiac", + "waxing_crescent": "Dorastaj\u00faci polmesiac", + "waxing_gibbous": "Dorastaj\u00faci mesiac" + } + } + } + }, + "issues": { + "removed_yaml": { + "description": "Konfigur\u00e1cia Moon pomocou YAML bola odstr\u00e1nen\u00e1. \n\n Asistent dom\u00e1cnosti nepou\u017e\u00edva va\u0161u existuj\u00facu konfigur\u00e1ciu YAML. \n\n Ak chcete tento probl\u00e9m vyrie\u0161i\u0165, odstr\u00e1\u0148te konfigur\u00e1ciu YAML zo s\u00faboru configuration.yaml a re\u0161tartujte aplik\u00e1ciu Home Assistant.", + "title": "Konfigur\u00e1cia Moon YAML bola odstr\u00e1nen\u00e1" + } + }, + "title": "Mesiac" } \ No newline at end of file diff --git a/homeassistant/components/moon/translations/zh-Hant.json b/homeassistant/components/moon/translations/zh-Hant.json index 29acac079e1..2c5c5f9c130 100644 --- a/homeassistant/components/moon/translations/zh-Hant.json +++ b/homeassistant/components/moon/translations/zh-Hant.json @@ -9,6 +9,22 @@ } } }, + "entity": { + "sensor": { + "phase": { + "state": { + "first_quarter": "\u4e0a\u5f26\u6708", + "full_moon": "\u6eff\u6708", + "last_quarter": "\u4e0b\u5f26\u6708", + "new_moon": "\u65b0\u6708", + "waning_crescent": "\u6b98\u6708", + "waning_gibbous": "\u8667\u51f8\u6708", + "waxing_crescent": "\u86fe\u7709\u6708", + "waxing_gibbous": "\u76c8\u51f8\u6708" + } + } + } + }, "issues": { "removed_yaml": { "description": "\u4f7f\u7528 YAML \u8a2d\u5b9a Moon \u7684\u529f\u80fd\u5373\u5c07\u79fb\u9664\u3002\n\nHome Assistant \u5c07\u4e0d\u518d\u4f7f\u7528\u73fe\u6709\u7684 YAML \u8a2d\u5b9a\u3002\n\n\u7531 configuration.yaml \u6a94\u6848\u4e2d\u79fb\u9664 YAML \u8a2d\u5b9a\u4e26\u91cd\u555f Home Assistant \u4ee5\u4fee\u6b63\u6b64\u554f\u984c\u3002", diff --git a/homeassistant/components/motion_blinds/__init__.py b/homeassistant/components/motion_blinds/__init__.py index a023fc05d14..378a2f1f03d 100644 --- a/homeassistant/components/motion_blinds/__init__.py +++ b/homeassistant/components/motion_blinds/__init__.py @@ -135,8 +135,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: data = {**entry.data, CONF_INTERFACE: working_interface} hass.config_entries.async_update_entry(entry, data=data) _LOGGER.debug( - "Motion Blinds interface updated from %s to %s, " - "this should only occur after a network change", + ( + "Motion Blinds interface updated from %s to %s, " + "this should only occur after a network change" + ), multicast_interface, working_interface, ) diff --git a/homeassistant/components/motion_blinds/cover.py b/homeassistant/components/motion_blinds/cover.py index e0d02750d6d..53ee4f5b561 100644 --- a/homeassistant/components/motion_blinds/cover.py +++ b/homeassistant/components/motion_blinds/cover.py @@ -352,6 +352,13 @@ class MotionTiltDevice(MotionPositionDevice): return None return self._blind.angle * 100 / 180 + @property + def is_closed(self) -> bool | None: + """Return if the cover is closed or not.""" + if self._blind.position is None: + return None + return self._blind.position >= 95 + async def async_open_cover_tilt(self, **kwargs: Any) -> None: """Open the cover tilt.""" async with self._api_lock: diff --git a/homeassistant/components/motion_blinds/gateway.py b/homeassistant/components/motion_blinds/gateway.py index 96a85246666..ac18840ddeb 100644 --- a/homeassistant/components/motion_blinds/gateway.py +++ b/homeassistant/components/motion_blinds/gateway.py @@ -133,7 +133,10 @@ class ConnectMotionGateway: return interface _LOGGER.error( - "Could not find working interface for Motion Blinds host %s, using interface '%s'", + ( + "Could not find working interface for Motion Blinds host %s, using" + " interface '%s'" + ), host, self._interface, ) diff --git a/homeassistant/components/motion_blinds/manifest.json b/homeassistant/components/motion_blinds/manifest.json index 6d80d31a69d..2ac02df992e 100644 --- a/homeassistant/components/motion_blinds/manifest.json +++ b/homeassistant/components/motion_blinds/manifest.json @@ -3,7 +3,7 @@ "name": "Motion Blinds", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/motion_blinds", - "requirements": ["motionblinds==0.6.13"], + "requirements": ["motionblinds==0.6.15"], "dependencies": ["network"], "dhcp": [ { "registered_devices": true }, diff --git a/homeassistant/components/motion_blinds/translations/pt.json b/homeassistant/components/motion_blinds/translations/pt.json index ccf03b80e43..b8a9b95dde1 100644 --- a/homeassistant/components/motion_blinds/translations/pt.json +++ b/homeassistant/components/motion_blinds/translations/pt.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", "already_in_progress": "O processo de configura\u00e7\u00e3o j\u00e1 est\u00e1 a decorrer", - "connection_error": "Falha na liga\u00e7\u00e3o" + "connection_error": "A liga\u00e7\u00e3o falhou" }, "flow_title": "{short_mac} ({ip_address})", "step": { diff --git a/homeassistant/components/motion_blinds/translations/sk.json b/homeassistant/components/motion_blinds/translations/sk.json index baae7e432f9..9b48318e1b4 100644 --- a/homeassistant/components/motion_blinds/translations/sk.json +++ b/homeassistant/components/motion_blinds/translations/sk.json @@ -5,21 +5,37 @@ "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "connection_error": "Nepodarilo sa pripoji\u0165" }, + "error": { + "discovery_error": "Nepodarilo sa objavi\u0165 Motion br\u00e1nu" + }, "flow_title": "{short_mac} ({ip_address})", "step": { "connect": { "data": { "api_key": "API k\u013e\u00fa\u010d" - } + }, + "description": "Budete potrebova\u0165 16-znakov\u00fd k\u013e\u00fa\u010d API, pokyny n\u00e1jdete na https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key" }, "select": { "data": { "select_ip": "IP adresa" - } + }, + "description": "Ak chcete pripoji\u0165 \u010fal\u0161ie Motion br\u00e1ny, spustite nastavenie znova", + "title": "Vyberte Motion Gateway, ktor\u00fa chcete pripoji\u0165" }, "user": { "data": { "host": "IP adresa" + }, + "description": "Pripojte sa k va\u0161ej br\u00e1ne pohybu, ak nie je nastaven\u00e1 adresa IP, pou\u017eije sa automatick\u00e9 zis\u0165ovanie" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "wait_for_push": "\u010cakanie na multicast push pri aktualiz\u00e1cii" } } } diff --git a/homeassistant/components/motioneye/const.py b/homeassistant/components/motioneye/const.py index 37e751236da..ebe4e24d6cf 100644 --- a/homeassistant/components/motioneye/const.py +++ b/homeassistant/components/motioneye/const.py @@ -89,8 +89,8 @@ SERVICE_SET_TEXT_OVERLAY: Final = "set_text_overlay" SERVICE_ACTION: Final = "action" SERVICE_SNAPSHOT: Final = "snapshot" -SIGNAL_CAMERA_ADD: Final = f"{DOMAIN}_camera_add_signal." "{}" -SIGNAL_CAMERA_REMOVE: Final = f"{DOMAIN}_camera_remove_signal." "{}" +SIGNAL_CAMERA_ADD: Final = f"{DOMAIN}_camera_add_signal.{{}}" +SIGNAL_CAMERA_REMOVE: Final = f"{DOMAIN}_camera_remove_signal.{{}}" TYPE_MOTIONEYE_ACTION_SENSOR = f"{DOMAIN}_action_sensor" TYPE_MOTIONEYE_MJPEG_CAMERA: Final = "motioneye_mjpeg_camera" diff --git a/homeassistant/components/motioneye/translations/en_GB.json b/homeassistant/components/motioneye/translations/en-GB.json similarity index 100% rename from homeassistant/components/motioneye/translations/en_GB.json rename to homeassistant/components/motioneye/translations/en-GB.json diff --git a/homeassistant/components/motioneye/translations/sk.json b/homeassistant/components/motioneye/translations/sk.json index f3eb264871f..169ffb75a1f 100644 --- a/homeassistant/components/motioneye/translations/sk.json +++ b/homeassistant/components/motioneye/translations/sk.json @@ -11,14 +11,30 @@ "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { + "hassio_confirm": { + "description": "Chcete nakonfigurova\u0165 Home Assistant na pripojenie k slu\u017ebe motionEye poskytovanej doplnkom: {addon}?", + "title": "motionEye cez doplnok Home Assistant" + }, "user": { "data": { "admin_password": "Spr\u00e1vca Heslo", "admin_username": "Admin Pou\u017e\u00edvate\u013esk\u00e9 meno", "surveillance_password": "Doh\u013ead Heslo", + "surveillance_username": "Pou\u017e\u00edvate\u013esk\u00e9 meno pre doh\u013ead", "url": "URL" } } } + }, + "options": { + "step": { + "init": { + "data": { + "stream_url_template": "\u0160abl\u00f3na webovej adresy streamu", + "webhook_set": "Nakonfigurujte webhooky motionEye na hl\u00e1senie udalost\u00ed dom\u00e1cemu asistentovi", + "webhook_set_overwrite": "Prep\u00ed\u0161te nerozpoznan\u00e9 webhooky" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 1c0df640c08..3f6f2122757 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -240,8 +240,10 @@ def _filter_entry_config(hass: HomeAssistant, entry: ConfigEntry) -> None: } if entry.data.keys() != filtered_data.keys(): _LOGGER.warning( - "The following unsupported configuration options were removed from the " - "MQTT config entry: %s", + ( + "The following unsupported configuration options were removed from the " + "MQTT config entry: %s" + ), entry.data.keys() - filtered_data.keys(), ) hass.config_entries.async_update_entry(entry, data=filtered_data) @@ -329,8 +331,10 @@ async def async_fetch_config( override[CONF_CLIENT_KEY] = "-----PRIVATE KEY-----" if override: _LOGGER.warning( - "Deprecated configuration settings found in configuration.yaml. " - "These settings from your configuration entry will override: %s", + ( + "Deprecated configuration settings found in configuration.yaml. " + "These settings from your configuration entry will override: %s" + ), override, ) # Register a repair issue @@ -375,10 +379,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_publish_service(call: ServiceCall) -> None: """Handle MQTT publish service calls.""" - msg_topic = call.data.get(ATTR_TOPIC) - msg_topic_template = call.data.get(ATTR_TOPIC_TEMPLATE) - payload = call.data.get(ATTR_PAYLOAD) - payload_template = call.data.get(ATTR_PAYLOAD_TEMPLATE) + msg_topic: str | None = call.data.get(ATTR_TOPIC) + msg_topic_template: str | None = call.data.get(ATTR_TOPIC_TEMPLATE) + payload: PublishPayloadType = call.data.get(ATTR_PAYLOAD) + payload_template: str | None = call.data.get(ATTR_PAYLOAD_TEMPLATE) qos: int = call.data[ATTR_QOS] retain: bool = call.data[ATTR_RETAIN] if msg_topic_template is not None: @@ -389,16 +393,20 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: msg_topic = valid_publish_topic(rendered_topic) except (jinja2.TemplateError, TemplateError) as exc: _LOGGER.error( - "Unable to publish: rendering topic template of %s " - "failed because %s", + ( + "Unable to publish: rendering topic template of %s " + "failed because %s" + ), msg_topic_template, exc, ) return except vol.Invalid as err: _LOGGER.error( - "Unable to publish: topic template '%s' produced an " - "invalid topic '%s' after rendering (%s)", + ( + "Unable to publish: topic template '%s' produced an " + "invalid topic '%s' after rendering (%s)" + ), msg_topic_template, rendered_topic, err, @@ -412,8 +420,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: ).async_render() except (jinja2.TemplateError, TemplateError) as exc: _LOGGER.error( - "Unable to publish to %s: rendering payload template of " - "%s failed because %s", + ( + "Unable to publish to %s: rendering payload template of " + "%s failed because %s" + ), msg_topic, payload_template, exc, @@ -573,6 +583,7 @@ def websocket_mqtt_info( { vol.Required("type"): "mqtt/subscribe", vol.Required("topic"): valid_subscribe_topic, + vol.Optional("qos"): valid_qos_schema, } ) @websocket_api.async_response @@ -606,8 +617,9 @@ async def websocket_subscribe( ) # Perform UTF-8 decoding directly in callback routine + qos: int = msg["qos"] if "qos" in msg else DEFAULT_QOS connection.subscriptions[msg["id"]] = await async_subscribe( - hass, msg["topic"], forward_messages, encoding=None + hass, msg["topic"], forward_messages, encoding=None, qos=qos ) connection.send_message(websocket_api.result_message(msg["id"])) diff --git a/homeassistant/components/mqtt/binary_sensor.py b/homeassistant/components/mqtt/binary_sensor.py index bbbcb97ada6..5ed9fdfb76f 100644 --- a/homeassistant/components/mqtt/binary_sensor.py +++ b/homeassistant/components/mqtt/binary_sensor.py @@ -59,7 +59,7 @@ CONF_EXPIRE_AFTER = "expire_after" PLATFORM_SCHEMA_MODERN = MQTT_RO_SCHEMA.extend( { - vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA, + vol.Optional(CONF_DEVICE_CLASS): vol.Any(DEVICE_CLASSES_SCHEMA, None), vol.Optional(CONF_EXPIRE_AFTER): cv.positive_int, vol.Optional(CONF_FORCE_UPDATE, default=DEFAULT_FORCE_UPDATE): cv.boolean, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, @@ -106,6 +106,7 @@ class MqttBinarySensor(MqttEntity, BinarySensorEntity, RestoreEntity): _entity_id_format = binary_sensor.ENTITY_ID_FORMAT _expired: bool | None + _expire_after: int | None def __init__( self, @@ -123,8 +124,7 @@ class MqttBinarySensor(MqttEntity, BinarySensorEntity, RestoreEntity): async def mqtt_async_added_to_hass(self) -> None: """Restore state for entities with expire_after set.""" if ( - (expire_after := self._config.get(CONF_EXPIRE_AFTER)) is not None - and expire_after > 0 + self._expire_after and (last_state := await self.async_get_last_state()) is not None and last_state.state not in [STATE_UNKNOWN, STATE_UNAVAILABLE] # We might have set up a trigger already after subscribing from @@ -132,7 +132,7 @@ class MqttBinarySensor(MqttEntity, BinarySensorEntity, RestoreEntity): and not self._expiration_trigger ): expiration_at: datetime = last_state.last_changed + timedelta( - seconds=expire_after + seconds=self._expire_after ) if expiration_at < (time_now := dt_util.utcnow()): # Skip reactivating the binary_sensor @@ -145,7 +145,10 @@ class MqttBinarySensor(MqttEntity, BinarySensorEntity, RestoreEntity): self.hass, self._value_is_expired, expiration_at ) _LOGGER.debug( - "State recovered after reload for %s, remaining time before expiring %s", + ( + "State recovered after reload for %s, remaining time before" + " expiring %s" + ), self.entity_id, expiration_at - time_now, ) @@ -167,8 +170,8 @@ class MqttBinarySensor(MqttEntity, BinarySensorEntity, RestoreEntity): def _setup_from_config(self, config: ConfigType) -> None: """(Re)Setup the entity.""" - expire_after: int | None = config.get(CONF_EXPIRE_AFTER) - if expire_after is not None and expire_after > 0: + self._expire_after = config.get(CONF_EXPIRE_AFTER) + if self._expire_after: self._expired = True else: self._expired = None @@ -195,9 +198,7 @@ class MqttBinarySensor(MqttEntity, BinarySensorEntity, RestoreEntity): def state_message_received(msg: ReceiveMessage) -> None: """Handle a new received MQTT state message.""" # auto-expire enabled? - expire_after: int | None = self._config.get(CONF_EXPIRE_AFTER) - - if expire_after is not None and expire_after > 0: + if self._expire_after: # When expire_after is set, and we receive a message, assume device is # not expired since it has to be to receive the message @@ -208,7 +209,7 @@ class MqttBinarySensor(MqttEntity, BinarySensorEntity, RestoreEntity): self._expiration_trigger() # Set new trigger - expiration_at = dt_util.utcnow() + timedelta(seconds=expire_after) + expiration_at = dt_util.utcnow() + timedelta(seconds=self._expire_after) self._expiration_trigger = async_track_point_in_utc_time( self.hass, self._value_is_expired, expiration_at @@ -217,7 +218,10 @@ class MqttBinarySensor(MqttEntity, BinarySensorEntity, RestoreEntity): payload = self._value_template(msg.payload) if not payload.strip(): # No output from template, ignore _LOGGER.debug( - "Empty template output for entity: %s with state topic: %s. Payload: '%s', with value template '%s'", + ( + "Empty template output for entity: %s with state topic: %s." + " Payload: '%s', with value template '%s'" + ), self._config[CONF_NAME], self._config[CONF_STATE_TOPIC], msg.payload, @@ -234,9 +238,15 @@ class MqttBinarySensor(MqttEntity, BinarySensorEntity, RestoreEntity): else: # Payload is not for this entity template_info = "" if self._config.get(CONF_VALUE_TEMPLATE) is not None: - template_info = f", template output: '{str(payload)}', with value template '{str(self._config.get(CONF_VALUE_TEMPLATE))}'" + template_info = ( + f", template output: '{str(payload)}', with value template" + f" '{str(self._config.get(CONF_VALUE_TEMPLATE))}'" + ) _LOGGER.info( - "No matching payload found for entity: %s with state topic: %s. Payload: '%s'%s", + ( + "No matching payload found for entity: %s with state topic: %s." + " Payload: '%s'%s" + ), self._config[CONF_NAME], self._config[CONF_STATE_TOPIC], msg.payload, @@ -248,7 +258,7 @@ class MqttBinarySensor(MqttEntity, BinarySensorEntity, RestoreEntity): self._delay_listener() self._delay_listener = None - off_delay = self._config.get(CONF_OFF_DELAY) + off_delay: int | None = self._config.get(CONF_OFF_DELAY) if self._attr_is_on and off_delay is not None: self._delay_listener = evt.async_call_later( self.hass, off_delay, off_delay_listener @@ -284,8 +294,7 @@ class MqttBinarySensor(MqttEntity, BinarySensorEntity, RestoreEntity): @property def available(self) -> bool: """Return true if the device is available and value has not expired.""" - expire_after: int | None = self._config.get(CONF_EXPIRE_AFTER) # mypy doesn't know about fget: https://github.com/python/mypy/issues/6185 return MqttAvailability.available.fget(self) and ( # type: ignore[attr-defined] - expire_after is None or not self._expired + self._expire_after is None or not self._expired ) diff --git a/homeassistant/components/mqtt/button.py b/homeassistant/components/mqtt/button.py index 929e270f300..d50a06a46d8 100644 --- a/homeassistant/components/mqtt/button.py +++ b/homeassistant/components/mqtt/button.py @@ -6,7 +6,7 @@ import functools import voluptuous as vol from homeassistant.components import button -from homeassistant.components.button import ButtonDeviceClass, ButtonEntity +from homeassistant.components.button import DEVICE_CLASSES_SCHEMA, ButtonEntity from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_DEVICE_CLASS, CONF_NAME from homeassistant.core import HomeAssistant @@ -39,7 +39,7 @@ PLATFORM_SCHEMA_MODERN = MQTT_BASE_SCHEMA.extend( { vol.Optional(CONF_COMMAND_TEMPLATE): cv.template, vol.Required(CONF_COMMAND_TOPIC): valid_publish_topic, - vol.Optional(CONF_DEVICE_CLASS): button.DEVICE_CLASSES_SCHEMA, + vol.Optional(CONF_DEVICE_CLASS): vol.Any(DEVICE_CLASSES_SCHEMA, None), vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_PAYLOAD_PRESS, default=DEFAULT_PAYLOAD_PRESS): cv.string, vol.Optional(CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean, @@ -104,6 +104,7 @@ class MqttButton(MqttEntity, ButtonEntity): self._command_template = MqttCommandTemplate( config.get(CONF_COMMAND_TEMPLATE), entity=self ).async_render + self._attr_device_class = self._config.get(CONF_DEVICE_CLASS) def _prepare_subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" @@ -111,11 +112,6 @@ class MqttButton(MqttEntity, ButtonEntity): async def _subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" - @property - def device_class(self) -> ButtonDeviceClass | None: - """Return the device class of the sensor.""" - return self._config.get(CONF_DEVICE_CLASS) - async def async_press(self) -> None: """Turn the device on. diff --git a/homeassistant/components/mqtt/client.py b/homeassistant/components/mqtt/client.py index f55b1066420..7dc6048f2f7 100644 --- a/homeassistant/components/mqtt/client.py +++ b/homeassistant/components/mqtt/client.py @@ -121,7 +121,10 @@ async def async_publish( if not isinstance(payload, bytes): if not encoding: _LOGGER.error( - "Can't pass-through payload for publishing %s on %s with no encoding set, need 'bytes' got %s", + ( + "Can't pass-through payload for publishing %s on %s with no" + " encoding set, need 'bytes' got %s" + ), payload, topic, type(payload), @@ -221,8 +224,10 @@ async def async_subscribe( if non_default == 3: module = inspect.getmodule(msg_callback) _LOGGER.warning( - "Signature of MQTT msg_callback '%s.%s' is deprecated, " - "this will stop working with HA core 2023.2", + ( + "Signature of MQTT msg_callback '%s.%s' is deprecated, " + "this will stop working with HA core 2023.2" + ), module.__name__ if module else "", msg_callback.__name__, ) @@ -303,8 +308,8 @@ class MqttClientSetup: # Enable logging self._client.enable_logger() - username = config.get(CONF_USERNAME) - password = config.get(CONF_PASSWORD) + username: str | None = config.get(CONF_USERNAME) + password: str | None = config.get(CONF_PASSWORD) if username is not None: self._client.username_pw_set(username, password) @@ -317,8 +322,8 @@ class MqttClientSetup: client_cert = get_file_path(CONF_CLIENT_CERT, config.get(CONF_CLIENT_CERT)) tls_insecure = config.get(CONF_TLS_INSECURE) if transport == TRANSPORT_WEBSOCKETS: - ws_path = config.get(CONF_WS_PATH) - ws_headers = config.get(CONF_WS_HEADERS) + ws_path: str = config[CONF_WS_PATH] + ws_headers: dict[str, str] = config[CONF_WS_HEADERS] self._client.ws_set_options(ws_path, ws_headers) if certificate is not None: self._client.tls_set( @@ -340,6 +345,8 @@ class MqttClientSetup: class MQTT: """Home Assistant MQTT client.""" + _mqttc: mqtt.Client + def __init__( self, hass: HomeAssistant, @@ -347,10 +354,6 @@ class MQTT: conf: ConfigType, ) -> None: """Initialize Home Assistant MQTT client.""" - # We don't import on the top because some integrations - # should be able to optionally rely on MQTT. - import paho.mqtt.client as mqtt # pylint: disable=import-outside-toplevel - self._mqtt_data = get_mqtt_data(hass) self.hass = hass @@ -360,7 +363,6 @@ class MQTT: self.connected = False self._ha_started = asyncio.Event() self._last_subscribe = time.time() - self._mqttc: mqtt.Client = None self._cleanup_on_unload: list[Callable[[], None]] = [] self._paho_lock = asyncio.Lock() # Prevents parallel calls to the MQTT client @@ -427,11 +429,12 @@ class MQTT: self._mqttc.publish, topic, payload, qos, retain ) _LOGGER.debug( - "Transmitting%s message on %s: '%s', mid: %s", + "Transmitting%s message on %s: '%s', mid: %s, qos: %s", " retained" if retain else "", topic, payload, msg_info.mid, + qos, ) _raise_on_error(msg_info.rc) await self._wait_for_mid(msg_info.mid) @@ -525,12 +528,9 @@ class MQTT: """ def _client_unsubscribe(topic: str) -> int: - result: int | None = None - mid: int | None = None result, mid = self._mqttc.unsubscribe(topic) _LOGGER.debug("Unsubscribing from %s, mid: %s", topic, mid) _raise_on_error(result) - assert mid return mid if any(other.topic == topic for other in self.subscriptions): @@ -554,7 +554,7 @@ class MQTT: for topic, qos in subscriptions: result, mid = self._mqttc.subscribe(topic, qos) subscribe_result_list.append((result, mid)) - _LOGGER.debug("Subscribing to %s, mid: %s", topic, mid) + _LOGGER.debug("Subscribing to %s, mid: %s, qos: %s", topic, mid, qos) return subscribe_result_list async with self._paho_lock: @@ -562,8 +562,8 @@ class MQTT: _process_client_subscriptions ) - tasks = [] - errors = [] + tasks: list[Coroutine[Any, Any, None]] = [] + errors: list[int] = [] for result, mid in results: if result == 0: tasks.append(self._wait_for_mid(mid)) @@ -657,9 +657,10 @@ class MQTT: @callback def _mqtt_handle_message(self, msg: mqtt.MQTTMessage) -> None: _LOGGER.debug( - "Received%s message on %s: %s", + "Received%s message on %s (qos=%s): %s", " retained" if msg.retain else "", msg.topic, + msg.qos, msg.payload[0:8192], ) timestamp = dt_util.utcnow() @@ -775,7 +776,7 @@ class MQTT: ) -def _raise_on_errors(result_codes: Iterable[int | None]) -> None: +def _raise_on_errors(result_codes: Iterable[int]) -> None: """Raise error if error result.""" # pylint: disable-next=import-outside-toplevel import paho.mqtt.client as mqtt @@ -788,7 +789,7 @@ def _raise_on_errors(result_codes: Iterable[int | None]) -> None: raise HomeAssistantError(f"Error talking to MQTT: {', '.join(messages)}") -def _raise_on_error(result_code: int | None) -> None: +def _raise_on_error(result_code: int) -> None: """Raise error if error result.""" _raise_on_errors((result_code,)) diff --git a/homeassistant/components/mqtt/climate.py b/homeassistant/components/mqtt/climate.py index 7d6d2ff7b2c..64a5908368b 100644 --- a/homeassistant/components/mqtt/climate.py +++ b/homeassistant/components/mqtt/climate.py @@ -749,6 +749,7 @@ class MqttClimate(MqttEntity, ClimateEntity): async def async_set_temperature(self, **kwargs: Any) -> None: """Set new target temperatures.""" + operation_mode: HVACMode | None if (operation_mode := kwargs.get(ATTR_HVAC_MODE)) is not None: await self.async_set_hvac_mode(operation_mode) diff --git a/homeassistant/components/mqtt/config_integration.py b/homeassistant/components/mqtt/config_integration.py index 9db18af0718..4140c5963ca 100644 --- a/homeassistant/components/mqtt/config_integration.py +++ b/homeassistant/components/mqtt/config_integration.py @@ -145,8 +145,7 @@ PLATFORM_CONFIG_SCHEMA_BASE = vol.Schema( CLIENT_KEY_AUTH_MSG = ( - "client_key and client_cert must both be present in " - "the MQTT broker configuration" + "client_key and client_cert must both be present in the MQTT broker configuration" ) CONFIG_SCHEMA_ENTRY = vol.Schema( diff --git a/homeassistant/components/mqtt/const.py b/homeassistant/components/mqtt/const.py index f7fa93a36d0..0cb81dc9f74 100644 --- a/homeassistant/components/mqtt/const.py +++ b/homeassistant/components/mqtt/const.py @@ -27,6 +27,7 @@ CONF_TRANSPORT = "transport" CONF_WS_PATH = "ws_path" CONF_WS_HEADERS = "ws_headers" CONF_WILL_MESSAGE = "will_message" +CONF_PAYLOAD_RESET = "payload_reset" CONF_CERTIFICATE = "certificate" CONF_CLIENT_KEY = "client_key" diff --git a/homeassistant/components/mqtt/cover.py b/homeassistant/components/mqtt/cover.py index 7f9b5dc3e85..e13b704436f 100644 --- a/homeassistant/components/mqtt/cover.py +++ b/homeassistant/components/mqtt/cover.py @@ -120,7 +120,8 @@ def validate_options(config: ConfigType) -> ConfigType: """ if CONF_SET_POSITION_TOPIC in config and CONF_GET_POSITION_TOPIC not in config: raise vol.Invalid( - f"'{CONF_SET_POSITION_TOPIC}' must be set together with '{CONF_GET_POSITION_TOPIC}'." + f"'{CONF_SET_POSITION_TOPIC}' must be set together with" + f" '{CONF_GET_POSITION_TOPIC}'." ) # if templates are set make sure the topic for the template is also set @@ -132,22 +133,26 @@ def validate_options(config: ConfigType) -> ConfigType: if CONF_GET_POSITION_TEMPLATE in config and CONF_GET_POSITION_TOPIC not in config: raise vol.Invalid( - f"'{CONF_GET_POSITION_TEMPLATE}' must be set together with '{CONF_GET_POSITION_TOPIC}'." + f"'{CONF_GET_POSITION_TEMPLATE}' must be set together with" + f" '{CONF_GET_POSITION_TOPIC}'." ) if CONF_SET_POSITION_TEMPLATE in config and CONF_SET_POSITION_TOPIC not in config: raise vol.Invalid( - f"'{CONF_SET_POSITION_TEMPLATE}' must be set together with '{CONF_SET_POSITION_TOPIC}'." + f"'{CONF_SET_POSITION_TEMPLATE}' must be set together with" + f" '{CONF_SET_POSITION_TOPIC}'." ) if CONF_TILT_COMMAND_TEMPLATE in config and CONF_TILT_COMMAND_TOPIC not in config: raise vol.Invalid( - f"'{CONF_TILT_COMMAND_TEMPLATE}' must be set together with '{CONF_TILT_COMMAND_TOPIC}'." + f"'{CONF_TILT_COMMAND_TEMPLATE}' must be set together with" + f" '{CONF_TILT_COMMAND_TOPIC}'." ) if CONF_TILT_STATUS_TEMPLATE in config and CONF_TILT_STATUS_TOPIC not in config: raise vol.Invalid( - f"'{CONF_TILT_STATUS_TEMPLATE}' must be set together with '{CONF_TILT_STATUS_TOPIC}'." + f"'{CONF_TILT_STATUS_TEMPLATE}' must be set together with" + f" '{CONF_TILT_STATUS_TOPIC}'." ) return config @@ -156,7 +161,7 @@ def validate_options(config: ConfigType) -> ConfigType: _PLATFORM_SCHEMA_BASE = MQTT_BASE_SCHEMA.extend( { vol.Optional(CONF_COMMAND_TOPIC): valid_publish_topic, - vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA, + vol.Optional(CONF_DEVICE_CLASS): vol.Any(DEVICE_CLASSES_SCHEMA, None), vol.Optional(CONF_GET_POSITION_TOPIC): valid_subscribe_topic, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean, @@ -388,7 +393,10 @@ class MqttCover(MqttEntity, CoverEntity): self._state = STATE_CLOSED else: _LOGGER.warning( - "Payload is not supported (e.g. open, closed, opening, closing, stopped): %s", + ( + "Payload is not supported (e.g. open, closed, opening, closing," + " stopped): %s" + ), payload, ) return @@ -414,7 +422,8 @@ class MqttCover(MqttEntity, CoverEntity): if payload_dict and isinstance(payload_dict, dict): if "position" not in payload_dict: _LOGGER.warning( - "Template (position_template) returned JSON without position attribute" + "Template (position_template) returned JSON without position" + " attribute" ) return if "tilt_position" in payload_dict: diff --git a/homeassistant/components/mqtt/device_tracker.py b/homeassistant/components/mqtt/device_tracker.py index 26dc016e07e..92f213f4bdf 100644 --- a/homeassistant/components/mqtt/device_tracker.py +++ b/homeassistant/components/mqtt/device_tracker.py @@ -29,7 +29,7 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from . import subscription from .config import MQTT_RO_SCHEMA -from .const import CONF_QOS, CONF_STATE_TOPIC +from .const import CONF_PAYLOAD_RESET, CONF_QOS, CONF_STATE_TOPIC from .debug_info import log_messages from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, @@ -44,6 +44,7 @@ CONF_PAYLOAD_HOME = "payload_home" CONF_PAYLOAD_NOT_HOME = "payload_not_home" CONF_SOURCE_TYPE = "source_type" +DEFAULT_PAYLOAD_RESET = "None" DEFAULT_SOURCE_TYPE = SourceType.GPS PLATFORM_SCHEMA_MODERN = MQTT_RO_SCHEMA.extend( @@ -51,6 +52,7 @@ PLATFORM_SCHEMA_MODERN = MQTT_RO_SCHEMA.extend( vol.Optional(CONF_NAME): cv.string, vol.Optional(CONF_PAYLOAD_HOME, default=STATE_HOME): cv.string, vol.Optional(CONF_PAYLOAD_NOT_HOME, default=STATE_NOT_HOME): cv.string, + vol.Optional(CONF_PAYLOAD_RESET, default=DEFAULT_PAYLOAD_RESET): cv.string, vol.Optional(CONF_SOURCE_TYPE, default=DEFAULT_SOURCE_TYPE): vol.In( SOURCE_TYPES ), @@ -127,6 +129,8 @@ class MqttDeviceTracker(MqttEntity, TrackerEntity): self._location_name = STATE_HOME elif payload == self._config[CONF_PAYLOAD_NOT_HOME]: self._location_name = STATE_NOT_HOME + elif payload == self._config[CONF_PAYLOAD_RESET]: + self._location_name = None else: assert isinstance(msg.payload, str) self._location_name = msg.payload diff --git a/homeassistant/components/mqtt/discovery.py b/homeassistant/components/mqtt/discovery.py index 2d460a69592..b6104a570d4 100644 --- a/homeassistant/components/mqtt/discovery.py +++ b/homeassistant/components/mqtt/discovery.py @@ -108,9 +108,12 @@ async def async_start( # noqa: C901 if not (match := TOPIC_MATCHER.match(topic_trimmed)): if topic_trimmed.endswith("config"): _LOGGER.warning( - "Received message on illegal discovery topic '%s'. The topic contains " - "not allowed characters. For more information see " - "https://www.home-assistant.io/docs/mqtt/discovery/#discovery-topic", + ( + "Received message on illegal discovery topic '%s'. The topic" + " contains " + "not allowed characters. For more information see " + "https://www.home-assistant.io/docs/mqtt/discovery/#discovery-topic" + ), topic, ) return diff --git a/homeassistant/components/mqtt/fan.py b/homeassistant/components/mqtt/fan.py index da061da14e4..82fd0d4063a 100644 --- a/homeassistant/components/mqtt/fan.py +++ b/homeassistant/components/mqtt/fan.py @@ -386,7 +386,10 @@ class MqttFan(MqttEntity, FanEntity): ) except ValueError: _LOGGER.warning( - "'%s' received on topic %s. '%s' is not a valid speed within the speed range", + ( + "'%s' received on topic %s. '%s' is not a valid speed within" + " the speed range" + ), msg.payload, msg.topic, rendered_percentage_payload, @@ -394,7 +397,10 @@ class MqttFan(MqttEntity, FanEntity): return if percentage < 0 or percentage > 100: _LOGGER.warning( - "'%s' received on topic %s. '%s' is not a valid speed within the speed range", + ( + "'%s' received on topic %s. '%s' is not a valid speed within" + " the speed range" + ), msg.payload, msg.topic, rendered_percentage_payload, diff --git a/homeassistant/components/mqtt/mixins.py b/homeassistant/components/mqtt/mixins.py index 6df545d7508..1e6e9989577 100644 --- a/homeassistant/components/mqtt/mixins.py +++ b/homeassistant/components/mqtt/mixins.py @@ -1,7 +1,7 @@ """MQTT component mixins and helpers.""" from __future__ import annotations -from abc import abstractmethod +from abc import ABC, abstractmethod import asyncio from collections.abc import Callable, Coroutine from functools import partial @@ -46,7 +46,6 @@ from homeassistant.helpers.entity import ( ENTITY_CATEGORIES_SCHEMA, DeviceInfo, Entity, - EntityCategory, async_generate_entity_id, ) from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -245,9 +244,11 @@ def warn_for_legacy_schema(domain: str) -> Callable[[ConfigType], ConfigType]: return config _LOGGER.error( - "Manually configured MQTT %s(s) found under platform key '%s', " - "please move to the mqtt integration key, see " - "https://www.home-assistant.io/integrations/%s.mqtt/#new_format", + ( + "Manually configured MQTT %s(s) found under platform key '%s', " + "please move to the mqtt integration key, see " + "https://www.home-assistant.io/integrations/%s.mqtt/#new_format" + ), domain, domain, domain, @@ -317,8 +318,10 @@ async def async_setup_entry_helper( """Discover and add an MQTT entity, automation or tag.""" if not mqtt_config_entry_enabled(hass): _LOGGER.warning( - "MQTT integration is disabled, skipping setup of discovered item " - "MQTT %s, payload %s", + ( + "MQTT integration is disabled, skipping setup of discovered item " + "MQTT %s, payload %s" + ), domain, discovery_payload, ) @@ -662,7 +665,7 @@ async def async_clear_discovery_topic_if_entity_removed( await async_remove_discovery_payload(hass, discovery_data) -class MqttDiscoveryDeviceUpdate: +class MqttDiscoveryDeviceUpdate(ABC): """Add support for auto discovery for platforms without an entity.""" def __init__( @@ -1011,10 +1014,11 @@ class MqttEntity( """Init the MQTT Entity.""" self.hass = hass self._config: ConfigType = config - self._unique_id: str | None = config.get(CONF_UNIQUE_ID) + self._attr_unique_id = config.get(CONF_UNIQUE_ID) self._sub_state: dict[str, EntitySubscription] = {} # Load config + self._setup_common_attributes_from_config(self._config) self._setup_from_config(self._config) # Initialize entity_id from config @@ -1052,6 +1056,7 @@ class MqttEntity( """Handle updated discovery message.""" config: DiscoveryInfoType = self.config_schema()(discovery_payload) self._config = config + self._setup_common_attributes_from_config(self._config) self._setup_from_config(self._config) # Prepare MQTT subscriptions @@ -1100,6 +1105,15 @@ class MqttEntity( def config_schema() -> vol.Schema: """Return the config schema.""" + def _setup_common_attributes_from_config(self, config: ConfigType) -> None: + """(Re)Setup the common attributes for the entity.""" + self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY) + self._attr_entity_registry_enabled_default = bool( + config.get(CONF_ENABLED_BY_DEFAULT) + ) + self._attr_icon = config.get(CONF_ICON) + self._attr_name = config.get(CONF_NAME) + def _setup_from_config(self, config: ConfigType) -> None: """(Re)Setup the entity.""" @@ -1111,31 +1125,6 @@ class MqttEntity( async def _subscribe_topics(self) -> None: """(Re)Subscribe to topics.""" - @property - def entity_registry_enabled_default(self) -> bool: - """Return if the entity should be enabled when first added to the entity registry.""" - return bool(self._config[CONF_ENABLED_BY_DEFAULT]) - - @property - def entity_category(self) -> EntityCategory | None: - """Return the entity category if any.""" - return self._config.get(CONF_ENTITY_CATEGORY) - - @property - def icon(self) -> str | None: - """Return icon of the entity if any.""" - return self._config.get(CONF_ICON) - - @property - def name(self) -> str | None: - """Return the name of the device if any.""" - return self._config.get(CONF_NAME) - - @property - def unique_id(self) -> str | None: - """Return a unique ID.""" - return self._unique_id - def update_device( hass: HomeAssistant, diff --git a/homeassistant/components/mqtt/models.py b/homeassistant/components/mqtt/models.py index aaef5e3e3e8..408e455365e 100644 --- a/homeassistant/components/mqtt/models.py +++ b/homeassistant/components/mqtt/models.py @@ -237,7 +237,10 @@ class MqttValueTemplate: return rendered_payload _LOGGER.debug( - "Rendering incoming payload '%s' with variables %s with default value '%s' and %s", + ( + "Rendering incoming payload '%s' with variables %s with default value" + " '%s' and %s" + ), payload, values, default, diff --git a/homeassistant/components/mqtt/number.py b/homeassistant/components/mqtt/number.py index 6d56354368e..6ec26e9a904 100644 --- a/homeassistant/components/mqtt/number.py +++ b/homeassistant/components/mqtt/number.py @@ -36,6 +36,7 @@ from .const import ( CONF_COMMAND_TEMPLATE, CONF_COMMAND_TOPIC, CONF_ENCODING, + CONF_PAYLOAD_RESET, CONF_QOS, CONF_RETAIN, CONF_STATE_TOPIC, @@ -60,7 +61,6 @@ _LOGGER = logging.getLogger(__name__) CONF_MIN = "min" CONF_MAX = "max" -CONF_PAYLOAD_RESET = "payload_reset" CONF_STEP = "step" DEFAULT_NAME = "MQTT Number" @@ -87,7 +87,7 @@ def validate_config(config: ConfigType) -> ConfigType: _PLATFORM_SCHEMA_BASE = MQTT_RW_SCHEMA.extend( { vol.Optional(CONF_COMMAND_TEMPLATE): cv.template, - vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA, + vol.Optional(CONF_DEVICE_CLASS): vol.Any(DEVICE_CLASSES_SCHEMA, None), vol.Optional(CONF_MAX, default=DEFAULT_MAX_VALUE): vol.Coerce(float), vol.Optional(CONF_MIN, default=DEFAULT_MIN_VALUE): vol.Coerce(float), vol.Optional(CONF_MODE, default=NumberMode.AUTO): vol.Coerce(NumberMode), diff --git a/homeassistant/components/mqtt/select.py b/homeassistant/components/mqtt/select.py index d574cf081ba..ea8a20d1db5 100644 --- a/homeassistant/components/mqtt/select.py +++ b/homeassistant/components/mqtt/select.py @@ -115,6 +115,7 @@ class MqttSelect(MqttEntity, SelectEntity, RestoreEntity): discovery_data: DiscoveryInfoType | None, ) -> None: """Initialize the MQTT select.""" + self._attr_current_option = None SelectEntity.__init__(self) MqttEntity.__init__(self, hass, config, config_entry, discovery_data) @@ -125,7 +126,6 @@ class MqttSelect(MqttEntity, SelectEntity, RestoreEntity): def _setup_from_config(self, config: ConfigType) -> None: """(Re)Setup the entity.""" - self._attr_current_option = None self._optimistic = config[CONF_OPTIMISTIC] self._attr_options = config[CONF_OPTIONS] diff --git a/homeassistant/components/mqtt/sensor.py b/homeassistant/components/mqtt/sensor.py index 092ab88b28d..dbb414921b5 100644 --- a/homeassistant/components/mqtt/sensor.py +++ b/homeassistant/components/mqtt/sensor.py @@ -98,13 +98,13 @@ def validate_options(conf: ConfigType) -> ConfigType: _PLATFORM_SCHEMA_BASE = MQTT_RO_SCHEMA.extend( { - vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA, + vol.Optional(CONF_DEVICE_CLASS): vol.Any(DEVICE_CLASSES_SCHEMA, None), vol.Optional(CONF_EXPIRE_AFTER): cv.positive_int, vol.Optional(CONF_FORCE_UPDATE, default=DEFAULT_FORCE_UPDATE): cv.boolean, vol.Optional(CONF_LAST_RESET_TOPIC): valid_subscribe_topic, vol.Optional(CONF_LAST_RESET_VALUE_TEMPLATE): cv.template, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, - vol.Optional(CONF_STATE_CLASS): STATE_CLASSES_SCHEMA, + vol.Optional(CONF_STATE_CLASS): vol.Any(STATE_CLASSES_SCHEMA, None), vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string, } ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) @@ -198,7 +198,10 @@ class MqttSensor(MqttEntity, RestoreSensor): self.hass, self._value_is_expired, expiration_at ) _LOGGER.debug( - "State recovered after reload for %s, remaining time before expiring %s", + ( + "State recovered after reload for %s, remaining time before" + " expiring %s" + ), self.entity_id, expiration_at - time_now, ) diff --git a/homeassistant/components/mqtt/siren.py b/homeassistant/components/mqtt/siren.py index 350f02427e5..6043773d5d6 100644 --- a/homeassistant/components/mqtt/siren.py +++ b/homeassistant/components/mqtt/siren.py @@ -180,10 +180,10 @@ class MqttSiren(MqttEntity, SirenEntity): def _setup_from_config(self, config: ConfigType) -> None: """(Re)Setup the entity.""" - state_on = config.get(CONF_STATE_ON) + state_on: str | None = config.get(CONF_STATE_ON) self._state_on = state_on if state_on else config[CONF_PAYLOAD_ON] - state_off = config.get(CONF_STATE_OFF) + state_off: str | None = config.get(CONF_STATE_OFF) self._state_off = state_off if state_off else config[CONF_PAYLOAD_OFF] self._attr_extra_state_attributes = {} @@ -249,13 +249,19 @@ class MqttSiren(MqttEntity, SirenEntity): try: json_payload = json_loads(payload) _LOGGER.debug( - "JSON payload detected after processing payload '%s' on topic %s", + ( + "JSON payload detected after processing payload '%s' on" + " topic %s" + ), json_payload, msg.topic, ) except JSON_DECODE_EXCEPTIONS: _LOGGER.warning( - "No valid (JSON) payload detected after processing payload '%s' on topic %s", + ( + "No valid (JSON) payload detected after processing payload" + " '%s' on topic %s" + ), json_payload, msg.topic, ) diff --git a/homeassistant/components/mqtt/switch.py b/homeassistant/components/mqtt/switch.py index 09e72955e63..7c06a587c1d 100644 --- a/homeassistant/components/mqtt/switch.py +++ b/homeassistant/components/mqtt/switch.py @@ -62,7 +62,7 @@ PLATFORM_SCHEMA_MODERN = MQTT_RW_SCHEMA.extend( vol.Optional(CONF_STATE_OFF): cv.string, vol.Optional(CONF_STATE_ON): cv.string, vol.Optional(CONF_VALUE_TEMPLATE): cv.template, - vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA, + vol.Optional(CONF_DEVICE_CLASS): vol.Any(DEVICE_CLASSES_SCHEMA, None), } ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) diff --git a/homeassistant/components/mqtt/translations/he.json b/homeassistant/components/mqtt/translations/he.json index 8a5809c76c1..6355a9b911f 100644 --- a/homeassistant/components/mqtt/translations/he.json +++ b/homeassistant/components/mqtt/translations/he.json @@ -8,10 +8,11 @@ "bad_birth": "\u05e0\u05d5\u05e9\u05d0 \u05dc\u05d9\u05d3\u05d4 \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9", "bad_certificate": "\u05d0\u05d9\u05e9\u05d5\u05e8 \u05d4-CA \u05d0\u05d9\u05e0\u05d5 \u05d7\u05d5\u05e7\u05d9", "bad_client_cert": "\u05d0\u05d9\u05e9\u05d5\u05e8 \u05dc\u05e7\u05d5\u05d7 \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9, \u05d9\u05e9 \u05dc\u05d5\u05d5\u05d3\u05d0 \u05e9\u05e7\u05d5\u05d1\u05e5 \u05de\u05e7\u05d5\u05d3\u05d3 PEM \u05de\u05e1\u05d5\u05e4\u05e7", - "bad_client_cert_key": "\u05d0\u05d9\u05e9\u05d5\u05e8 \u05dc\u05e7\u05d5\u05d7 \u05d5\u05e4\u05e8\u05d8\u05d9 \u05d0\u05d9\u05e0\u05dd \u05d6\u05d5\u05d2 \u05d7\u05d5\u05e7\u05d9", + "bad_client_cert_key": "\u05d0\u05d9\u05e9\u05d5\u05e8 \u05dc\u05e7\u05d5\u05d7 \u05d5\u05de\u05e4\u05ea\u05d7 \u05e4\u05e8\u05d8\u05d9 \u05d0\u05d9\u05e0\u05dd \u05d6\u05d5\u05d2 \u05d7\u05d5\u05e7\u05d9", "bad_client_key": "\u05de\u05e4\u05ea\u05d7 \u05e4\u05e8\u05d8\u05d9 \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9, \u05d9\u05e9 \u05dc\u05d5\u05d5\u05d3\u05d0 \u05e9\u05e7\u05d5\u05d1\u05e5 \u05de\u05e7\u05d5\u05d3\u05d3 PEM \u05de\u05e1\u05d5\u05e4\u05e7 \u05dc\u05dc\u05d0 \u05e1\u05d9\u05e1\u05de\u05d4", "bad_discovery_prefix": "\u05d2\u05d9\u05dc\u05d5\u05d9 \u05dc\u05e4\u05d9 \u05e7\u05d9\u05d3\u05d5\u05de\u05ea \u05d0\u05d9\u05e0\u05d5 \u05d7\u05d5\u05e7\u05d9", "bad_will": "\u05e0\u05d5\u05e9\u05d0 \u05e6\u05d5\u05d5\u05d0\u05d4 \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9\u05ea", + "bad_ws_headers": "\u05d0\u05e1\u05e4\u05e7\u05ea \u05db\u05d5\u05ea\u05e8\u05d5\u05ea HTTP \u05d7\u05d5\u05e7\u05d9\u05d5\u05ea \u05db\u05d0\u05d5\u05d1\u05d9\u05d9\u05e7\u05d8 JSON", "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", "invalid_inclusion": "\u05d9\u05e9 \u05dc\u05d4\u05d2\u05d3\u05d9\u05e8 \u05d0\u05ea \u05d0\u05d9\u05e9\u05d5\u05e8 \u05d4\u05dc\u05e7\u05d5\u05d7 \u05d5\u05d4\u05de\u05e4\u05ea\u05d7 \u05d4\u05e4\u05e8\u05d8\u05d9 \u05d9\u05d7\u05d3" }, @@ -20,10 +21,10 @@ "data": { "advanced_options": "\u05d0\u05e4\u05e9\u05e8\u05d5\u05d9\u05d5\u05ea \u05de\u05ea\u05e7\u05d3\u05de\u05d5\u05ea", "broker": "\u05de\u05ea\u05d5\u05d5\u05da", - "certificate": "\u05e0\u05ea\u05d9\u05d1 \u05dc\u05e7\u05d5\u05d1\u05e5 \u05d0\u05d9\u05e9\u05d5\u05e8 CA \u05de\u05d5\u05ea\u05d0\u05dd \u05d0\u05d9\u05e9\u05d9\u05ea", - "client_cert": "\u05e0\u05ea\u05d9\u05d1 \u05dc\u05e7\u05d5\u05d1\u05e5 \u05d0\u05d9\u05e9\u05d5\u05e8 \u05dc\u05e7\u05d5\u05d7", + "certificate": "\u05d4\u05e2\u05dc\u05d0\u05ea \u05e7\u05d5\u05d1\u05e5 \u05d0\u05d9\u05e9\u05d5\u05e8 CA \u05de\u05d5\u05ea\u05d0\u05dd \u05d0\u05d9\u05e9\u05d9\u05ea", + "client_cert": "\u05d4\u05e2\u05dc\u05d0\u05ea \u05e7\u05d5\u05d1\u05e5 \u05d0\u05d9\u05e9\u05d5\u05e8 \u05dc\u05e7\u05d5\u05d7", "client_id": "\u05de\u05d6\u05d4\u05d4 \u05dc\u05e7\u05d5\u05d7 (\u05dc\u05d4\u05e9\u05d0\u05d9\u05e8 \u05e8\u05d9\u05e7 \u05dc\u05de\u05d6\u05d4\u05d4 \u05e9\u05e0\u05d5\u05e6\u05e8 \u05d1\u05d0\u05d5\u05e4\u05df \u05d0\u05e7\u05e8\u05d0\u05d9)", - "client_key": "\u05e0\u05ea\u05d9\u05d1 \u05dc\u05e7\u05d5\u05d1\u05e5 \u05de\u05e4\u05ea\u05d7 \u05e4\u05e8\u05d8\u05d9", + "client_key": "\u05d4\u05e2\u05dc\u05d0\u05ea \u05e7\u05d5\u05d1\u05e5 \u05de\u05e4\u05ea\u05d7 \u05e4\u05e8\u05d8\u05d9", "keepalive": "\u05d4\u05d6\u05de\u05df \u05e9\u05d1\u05d9\u05df \u05e9\u05dc\u05d9\u05d7\u05ea \u05d4\u05d5\u05d3\u05e2\u05d5\u05ea \u05dc\u05e9\u05de\u05d5\u05e8 \u05d1\u05d7\u05d9\u05d9\u05dd", "password": "\u05e1\u05d9\u05e1\u05de\u05d4", "port": "\u05e4\u05ea\u05d7\u05d4", @@ -31,7 +32,10 @@ "set_ca_cert": "\u05d0\u05d9\u05de\u05d5\u05ea \u05ea\u05e2\u05d5\u05d3\u05ea \u05de\u05ea\u05d5\u05d5\u05da", "set_client_cert": "\u05e9\u05d9\u05de\u05d5\u05e9 \u05d1\u05ea\u05e2\u05d5\u05d3\u05ea \u05dc\u05e7\u05d5\u05d7", "tls_insecure": "\u05d4\u05ea\u05e2\u05dc\u05de\u05d5\u05ea \u05de\u05d0\u05d9\u05de\u05d5\u05ea \u05ea\u05e2\u05d5\u05d3\u05ea \u05d4\u05de\u05ea\u05d5\u05d5\u05da", - "username": "\u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9" + "transport": "\u05ea\u05e2\u05d1\u05d5\u05e8\u05ea MQTT", + "username": "\u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9", + "ws_headers": "\u05db\u05d5\u05ea\u05e8\u05d5\u05ea WebSocket \u05d1\u05ea\u05d1\u05e0\u05d9\u05ea JSON", + "ws_path": "\u05e0\u05ea\u05d9\u05d1 WebSocket" }, "description": "\u05e0\u05d0 \u05dc\u05d4\u05d6\u05d9\u05df \u05d0\u05ea \u05e4\u05e8\u05d8\u05d9 \u05d4\u05d7\u05d9\u05d1\u05d5\u05e8 \u05e9\u05dc \u05de\u05ea\u05d5\u05d5\u05da MQTT \u05e9\u05dc\u05da." }, @@ -81,10 +85,11 @@ "bad_birth": "\u05e0\u05d5\u05e9\u05d0 \u05dc\u05d9\u05d3\u05d4 \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9", "bad_certificate": "\u05d0\u05d9\u05e9\u05d5\u05e8 \u05d4-CA \u05d0\u05d9\u05e0\u05d5 \u05d7\u05d5\u05e7\u05d9", "bad_client_cert": "\u05d0\u05d9\u05e9\u05d5\u05e8 \u05dc\u05e7\u05d5\u05d7 \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9, \u05d9\u05e9 \u05dc\u05d5\u05d5\u05d3\u05d0 \u05e9\u05e7\u05d5\u05d1\u05e5 \u05de\u05e7\u05d5\u05d3\u05d3 PEM \u05de\u05e1\u05d5\u05e4\u05e7", - "bad_client_cert_key": "\u05d0\u05d9\u05e9\u05d5\u05e8 \u05dc\u05e7\u05d5\u05d7 \u05d5\u05e4\u05e8\u05d8\u05d9 \u05d0\u05d9\u05e0\u05dd \u05d6\u05d5\u05d2 \u05d7\u05d5\u05e7\u05d9", + "bad_client_cert_key": "\u05d0\u05d9\u05e9\u05d5\u05e8 \u05dc\u05e7\u05d5\u05d7 \u05d5\u05de\u05e4\u05ea\u05d7 \u05e4\u05e8\u05d8\u05d9 \u05d0\u05d9\u05e0\u05dd \u05d6\u05d5\u05d2 \u05d7\u05d5\u05e7\u05d9", "bad_client_key": "\u05de\u05e4\u05ea\u05d7 \u05e4\u05e8\u05d8\u05d9 \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9, \u05d9\u05e9 \u05dc\u05d5\u05d5\u05d3\u05d0 \u05e9\u05e7\u05d5\u05d1\u05e5 \u05de\u05e7\u05d5\u05d3\u05d3 PEM \u05de\u05e1\u05d5\u05e4\u05e7 \u05dc\u05dc\u05d0 \u05e1\u05d9\u05e1\u05de\u05d4", - "bad_discovery_prefix": "\u05e7\u05d9\u05d3\u05d5\u05de\u05ea \u05d2\u05d9\u05dc\u05d5\u05d9 \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9\u05ea", + "bad_discovery_prefix": "\u05d2\u05d9\u05dc\u05d5\u05d9 \u05dc\u05e4\u05d9 \u05e7\u05d9\u05d3\u05d5\u05de\u05ea \u05d0\u05d9\u05e0\u05d5 \u05d7\u05d5\u05e7\u05d9", "bad_will": "\u05e0\u05d5\u05e9\u05d0 \u05e6\u05d5\u05d5\u05d0\u05d4 \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9\u05ea", + "bad_ws_headers": "\u05d0\u05e1\u05e4\u05e7\u05ea \u05db\u05d5\u05ea\u05e8\u05d5\u05ea HTTP \u05d7\u05d5\u05e7\u05d9\u05d5\u05ea \u05db\u05d0\u05d5\u05d1\u05d9\u05d9\u05e7\u05d8 JSON", "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", "invalid_inclusion": "\u05d9\u05e9 \u05dc\u05d4\u05d2\u05d3\u05d9\u05e8 \u05d0\u05ea \u05d0\u05d9\u05e9\u05d5\u05e8 \u05d4\u05dc\u05e7\u05d5\u05d7 \u05d5\u05d4\u05de\u05e4\u05ea\u05d7 \u05d4\u05e4\u05e8\u05d8\u05d9 \u05d9\u05d7\u05d3" }, @@ -104,7 +109,10 @@ "set_ca_cert": "\u05d0\u05d9\u05de\u05d5\u05ea \u05ea\u05e2\u05d5\u05d3\u05ea \u05de\u05ea\u05d5\u05d5\u05da", "set_client_cert": "\u05e9\u05d9\u05de\u05d5\u05e9 \u05d1\u05ea\u05e2\u05d5\u05d3\u05ea \u05dc\u05e7\u05d5\u05d7", "tls_insecure": "\u05d4\u05ea\u05e2\u05dc\u05de\u05d5\u05ea \u05de\u05d0\u05d9\u05de\u05d5\u05ea \u05ea\u05e2\u05d5\u05d3\u05ea \u05d4\u05de\u05ea\u05d5\u05d5\u05da", - "username": "\u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9" + "transport": "\u05ea\u05e2\u05d1\u05d5\u05e8\u05ea MQTT", + "username": "\u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9", + "ws_headers": "\u05db\u05d5\u05ea\u05e8\u05d5\u05ea WebSocket \u05d1\u05ea\u05d1\u05e0\u05d9\u05ea JSON", + "ws_path": "\u05e0\u05ea\u05d9\u05d1 WebSocket" }, "description": "\u05e0\u05d0 \u05dc\u05d4\u05d6\u05d9\u05df \u05d0\u05ea \u05e4\u05e8\u05d8\u05d9 \u05d4\u05d7\u05d9\u05d1\u05d5\u05e8 \u05e9\u05dc \u05de\u05ea\u05d5\u05d5\u05da MQTT \u05e9\u05dc\u05da.", "title": "\u05d0\u05e4\u05e9\u05e8\u05d5\u05d9\u05d5\u05ea \u05de\u05ea\u05d5\u05d5\u05da" diff --git a/homeassistant/components/mqtt/translations/hu.json b/homeassistant/components/mqtt/translations/hu.json index 1a6c62d7e15..ad26cdbe56f 100644 --- a/homeassistant/components/mqtt/translations/hu.json +++ b/homeassistant/components/mqtt/translations/hu.json @@ -8,10 +8,11 @@ "bad_birth": "\u00c9rv\u00e9nytelen 'birth' topik", "bad_certificate": "A CA-tan\u00fas\u00edtv\u00e1ny \u00e9rv\u00e9nytelen", "bad_client_cert": "\u00c9rv\u00e9nytelen \u00fcgyf\u00e9ltan\u00fas\u00edtv\u00e1ny. Gy\u0151z\u0151dj\u00f6n meg r\u00f3la, hogy PEM k\u00f3dolt f\u00e1jl van megadva", - "bad_client_cert_key": "Az \u00fcgyf\u00e9ltan\u00fas\u00edtv\u00e1ny \u00e9s a priv\u00e1t tan\u00fas\u00edtv\u00e1ny nem \u00e9rv\u00e9nyes p\u00e1r", + "bad_client_cert_key": "Az \u00fcgyf\u00e9ltan\u00fas\u00edtv\u00e1ny \u00e9s a priv\u00e1t kulcs nem \u00e9rv\u00e9nyes p\u00e1r", "bad_client_key": "\u00c9rv\u00e9nytelen priv\u00e1t kulcs, gy\u0151z\u0151dj\u00f6n meg r\u00f3la, hogy PEM k\u00f3dol\u00e1s\u00fa f\u00e1jlt k\u00fcld\u00f6tt jelsz\u00f3 n\u00e9lk\u00fcl", "bad_discovery_prefix": "\u00c9rv\u00e9nytelen felfedez\u00e9si el\u0151tag", "bad_will": "\u00c9rv\u00e9nytelen 'will' topik", + "bad_ws_headers": "\u00c9rv\u00e9nyes HTTP-fejl\u00e9cek megad\u00e1sa JSON-objektumk\u00e9nt", "cannot_connect": "Sikertelen csatlakoz\u00e1s", "invalid_inclusion": "Az \u00fcgyf\u00e9ltan\u00fas\u00edtv\u00e1nyt \u00e9s a mag\u00e1nkulcsot egy\u00fctt kell konfigur\u00e1lni" }, @@ -31,7 +32,10 @@ "set_ca_cert": "Br\u00f3kertan\u00fas\u00edtv\u00e1ny \u00e9rv\u00e9nyes\u00edt\u00e9s", "set_client_cert": "\u00dcgyf\u00e9ltan\u00fas\u00edtv\u00e1ny haszn\u00e1lata", "tls_insecure": "A br\u00f3kertan\u00fas\u00edtv\u00e1ny \u00e9rv\u00e9nyes\u00edt\u00e9s\u00e9nek figyelmen k\u00edv\u00fcl hagy\u00e1sa", - "username": "Felhaszn\u00e1l\u00f3n\u00e9v" + "transport": "MQTT adat\u00e1tvitel", + "username": "Felhaszn\u00e1l\u00f3n\u00e9v", + "ws_headers": "WebSocket fejl\u00e9cek JSON form\u00e1tumban", + "ws_path": "WebSocket el\u00e9r\u00e9si \u00fat" }, "description": "K\u00e9rem, adja meg az MQTT br\u00f3ker kapcsol\u00f3d\u00e1si adatait." }, @@ -81,10 +85,11 @@ "bad_birth": "\u00c9rv\u00e9nytelen 'birth' topik", "bad_certificate": "A CA-tan\u00fas\u00edtv\u00e1ny \u00e9rv\u00e9nytelen", "bad_client_cert": "\u00c9rv\u00e9nytelen \u00fcgyf\u00e9ltan\u00fas\u00edtv\u00e1ny. Gy\u0151z\u0151dj\u00f6n meg r\u00f3la, hogy PEM k\u00f3dolt f\u00e1jl van megadva", - "bad_client_cert_key": "Az \u00fcgyf\u00e9ltan\u00fas\u00edtv\u00e1ny \u00e9s a priv\u00e1t tan\u00fas\u00edtv\u00e1ny nem \u00e9rv\u00e9nyes p\u00e1r", + "bad_client_cert_key": "Az \u00fcgyf\u00e9ltan\u00fas\u00edtv\u00e1ny \u00e9s a priv\u00e1t kulcs nem \u00e9rv\u00e9nyes p\u00e1r", "bad_client_key": "\u00c9rv\u00e9nytelen priv\u00e1t kulcs, gy\u0151z\u0151dj\u00f6n meg r\u00f3la, hogy PEM k\u00f3dol\u00e1s\u00fa f\u00e1jlt k\u00fcld\u00f6tt jelsz\u00f3 n\u00e9lk\u00fcl", "bad_discovery_prefix": "\u00c9rv\u00e9nytelen felfedez\u00e9si el\u0151tag", "bad_will": "\u00c9rv\u00e9nytelen 'will' topik", + "bad_ws_headers": "\u00c9rv\u00e9nyes HTTP-fejl\u00e9cek megad\u00e1sa JSON-objektumk\u00e9nt", "cannot_connect": "Sikertelen csatlakoz\u00e1s", "invalid_inclusion": "Az \u00fcgyf\u00e9ltan\u00fas\u00edtv\u00e1nyt \u00e9s a mag\u00e1nkulcsot egy\u00fctt kell konfigur\u00e1lni" }, @@ -104,7 +109,10 @@ "set_ca_cert": "Br\u00f3kertan\u00fas\u00edtv\u00e1ny \u00e9rv\u00e9nyes\u00edt\u00e9s", "set_client_cert": "\u00dcgyf\u00e9ltan\u00fas\u00edtv\u00e1ny haszn\u00e1lata", "tls_insecure": "A br\u00f3kertan\u00fas\u00edtv\u00e1ny \u00e9rv\u00e9nyes\u00edt\u00e9s\u00e9nek figyelmen k\u00edv\u00fcl hagy\u00e1sa", - "username": "Felhaszn\u00e1l\u00f3n\u00e9v" + "transport": "MQTT adat\u00e1tvitel", + "username": "Felhaszn\u00e1l\u00f3n\u00e9v", + "ws_headers": "WebSocket fejl\u00e9cek JSON form\u00e1tumban", + "ws_path": "WebSocket el\u00e9r\u00e9si \u00fat" }, "description": "K\u00e9rem, adja meg az MQTT br\u00f3ker kapcsol\u00f3d\u00e1si adatait.", "title": "Br\u00f3ker opci\u00f3k" @@ -114,14 +122,14 @@ "birth_enable": "Sz\u00fclet\u00e9si \u00fczenet enged\u00e9lyez\u00e9se", "birth_payload": "Sz\u00fclet\u00e9si \u00fczenet", "birth_qos": "Sz\u00fclet\u00e9si \u00fczenet QoS", - "birth_retain": "A sz\u00fclet\u00e9si \u00fczenet meg\u0151rz\u00e9se", + "birth_retain": "Birth (sz\u00fclet\u00e9si) \u00fczenet tart\u00e1sa", "birth_topic": "Sz\u00fclet\u00e9si \u00fczenet t\u00e9m\u00e1ja", "discovery": "Felfedez\u00e9s enged\u00e9lyez\u00e9se", "discovery_prefix": "Felfedez\u00e9s el\u0151tag", "will_enable": "Enged\u00e9lyez\u00e9si \u00fczenet", "will_payload": "\u00dczenet", "will_qos": "QoS \u00fczenet", - "will_retain": "\u00dczenet megtart\u00e1sa", + "will_retain": "Will (b\u00facs\u00fa) \u00fctzenet tart\u00e1sa", "will_topic": "\u00dczenet t\u00e9m\u00e1ja" }, "description": "Discovery - Ha a felfedez\u00e9s enged\u00e9lyezve van (aj\u00e1nlott), akkor Home Assistant automatikusan felfedezi azokat az eszk\u00f6z\u00f6ket \u00e9s entit\u00e1sokat, amelyek k\u00f6zz\u00e9teszik konfigur\u00e1ci\u00f3jukat az MQTT br\u00f3keren. Ha a felfedez\u00e9s le van tiltva, minden konfigur\u00e1ci\u00f3t manu\u00e1lisan kell elv\u00e9gezni.\nBirth \u00fczenet - A sz\u00fclet\u00e9si \u00fczenet minden alkalommal el lesz k\u00fcldve, amikor Home Assistant (\u00fajra) csatlakozik az MQTT br\u00f3kerhez.\nWill \u00fczenet - Az akarat\u00fczenet minden alkalommal el lesz k\u00fcldve, amikor Home Assistant elvesz\u00edti a kapcsolatot a k\u00f6zvet\u00edt\u0151vel, mind tiszta esetben (pl. Home Assistant le\u00e1ll\u00edt\u00e1sa), mind rendelenes helyzetben (pl. Home Assistant lefagy vagy megszakad a h\u00e1l\u00f3zati kapcsolata).", diff --git a/homeassistant/components/mqtt/translations/it.json b/homeassistant/components/mqtt/translations/it.json index a90a95da470..857ddce0358 100644 --- a/homeassistant/components/mqtt/translations/it.json +++ b/homeassistant/components/mqtt/translations/it.json @@ -8,7 +8,7 @@ "bad_birth": "Argomento di nascita non valido", "bad_certificate": "Il certificato CA non \u00e8 valido", "bad_client_cert": "Certificato client non valido, assicurarsi che venga fornito un file codificato PEM", - "bad_client_cert_key": "Il certificato del client e il certificato privato non sono una coppia valida", + "bad_client_cert_key": "Il certificato client e la chiave privata non sono una coppia valida", "bad_client_key": "Chiave privata non valida, assicurarsi che venga fornito un file codificato PEM senza password", "bad_discovery_prefix": "Prefisso di rilevamento non valido", "bad_will": "Argomento testamento non valido", @@ -85,7 +85,7 @@ "bad_birth": "Argomento di nascita non valido", "bad_certificate": "Il certificato CA non \u00e8 valido", "bad_client_cert": "Certificato client non valido, assicurarsi che venga fornito un file codificato PEM", - "bad_client_cert_key": "Il certificato del client e il certificato privato non sono una coppia valida", + "bad_client_cert_key": "Il certificato client e la chiave privata non sono una coppia valida", "bad_client_key": "Chiave privata non valida, assicurarsi che venga fornito un file codificato PEM senza password", "bad_discovery_prefix": "Prefisso di rilevamento non valido", "bad_will": "Argomento testamento non valido", diff --git a/homeassistant/components/mqtt/translations/ko.json b/homeassistant/components/mqtt/translations/ko.json index 25c5db0edb2..af3ad3aaf99 100644 --- a/homeassistant/components/mqtt/translations/ko.json +++ b/homeassistant/components/mqtt/translations/ko.json @@ -1,9 +1,11 @@ { "config": { "abort": { + "already_configured": "\uc11c\ube44\uc2a4\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." }, "error": { + "bad_ws_headers": "\uc720\ud6a8\ud55c HTTP \ud5e4\ub354\ub97c JSON \uac1d\uccb4\ub85c \uc81c\uacf5", "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" }, "step": { @@ -12,7 +14,10 @@ "broker": "\ube0c\ub85c\ucee4", "password": "\ube44\ubc00\ubc88\ud638", "port": "\ud3ec\ud2b8", - "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + "transport": "MQTT \uc804\uc1a1", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984", + "ws_headers": "JSON \ud615\uc2dd\uc758 WebSocket \ud5e4\ub354", + "ws_path": "WebSocket \uacbd\ub85c" }, "description": "MQTT \ube0c\ub85c\ucee4\uc758 \uc5f0\uacb0 \uc815\ubcf4\ub97c \uc785\ub825\ud574\uc8fc\uc138\uc694." }, @@ -51,6 +56,7 @@ "error": { "bad_birth": "Birth \ud1a0\ud53d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4.", "bad_will": "Will \ud1a0\ud53d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4.", + "bad_ws_headers": "\uc720\ud6a8\ud55c HTTP \ud5e4\ub354\ub97c JSON \uac1d\uccb4\ub85c \uc81c\uacf5", "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" }, "step": { @@ -59,7 +65,10 @@ "broker": "\ube0c\ub85c\ucee4", "password": "\ube44\ubc00\ubc88\ud638", "port": "\ud3ec\ud2b8", - "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + "transport": "MQTT \uc804\uc1a1", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984", + "ws_headers": "JSON \ud615\uc2dd\uc758 WebSocket \ud5e4\ub354", + "ws_path": "WebSocket \uacbd\ub85c" }, "description": "MQTT \ube0c\ub85c\ucee4\uc640\uc758 \uc5f0\uacb0 \uc815\ubcf4\ub97c \uc785\ub825\ud574\uc8fc\uc138\uc694.", "title": "\ube0c\ub85c\ucee4 \uc635\uc158" diff --git a/homeassistant/components/mqtt/translations/nl.json b/homeassistant/components/mqtt/translations/nl.json index 7fe65efd7cf..5e4574299bd 100644 --- a/homeassistant/components/mqtt/translations/nl.json +++ b/homeassistant/components/mqtt/translations/nl.json @@ -114,23 +114,23 @@ "ws_headers": "WebSocket headers in JSON formaat", "ws_path": "WebSocket pad" }, - "description": "Voer de verbindingsgegevens van uw MQTT-broker in.", + "description": "Voer de verbindingsgegevens van je MQTT-broker in.", "title": "Broker opties" }, "options": { "data": { - "birth_enable": "Geboortebericht inschakelen (birth)", + "birth_enable": "Birth bericht inschakelen", "birth_payload": "Birth bericht inhoud", "birth_qos": "Birth bericht QoS", - "birth_retain": "Verbind bericht onthouden", + "birth_retain": "Birth bericht vasthouden", "birth_topic": "Birth bericht topic", "discovery": "Discovery inschakelen", "discovery_prefix": "Discovery-voorvoegsel", - "will_enable": "Offline bericht inschakelen (will)", + "will_enable": "Will bericht inschakelen", "will_payload": "Will bericht inhoud", - "will_qos": "Offline bericht QoS", - "will_retain": "Offline bericht onthouden", - "will_topic": "Offline bericht topic" + "will_qos": "Will bericht QoS", + "will_retain": "Will bericht vasthouden", + "will_topic": "Will bericht topic" }, "description": "Detectie - Als detectie is ingeschakeld (aanbevolen), zal Home Assistant automatisch apparaten en entiteiten detecteren die hun configuratie publiceren op de MQTT-broker. Als detectie is uitgeschakeld, moet alle configuratie handmatig worden uitgevoerd.\n Birth message - Het birth message wordt elke keer dat Home Assistant (opnieuw) verbinding maakt met de MQTT-broker, verzonden.\n Will message - Het will message wordt telkens verzonden wanneer Home Assistant de verbinding met de broker verliest, zowel in het geval van een schone (bijv. Home Assistant wordt uitgeschakeld) als in geval van een onjuiste (bijv. Home Assistant crasht of verliest de netwerkverbinding) verbroken verbinding.", "title": "MQTT-opties" diff --git a/homeassistant/components/mqtt/translations/sk.json b/homeassistant/components/mqtt/translations/sk.json index 25f39bf8191..acd06359037 100644 --- a/homeassistant/components/mqtt/translations/sk.json +++ b/homeassistant/components/mqtt/translations/sk.json @@ -5,8 +5,13 @@ "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." }, "error": { + "bad_birth": "Neplatn\u00e1 t\u00e9ma birth", "bad_certificate": "Certifik\u00e1t CA je neplatn\u00fd", + "bad_client_cert": "Neplatn\u00fd certifik\u00e1t klienta, uistite sa, \u017ee je dodan\u00fd s\u00fabor s k\u00f3dom PEM", "bad_client_cert_key": "Certifik\u00e1t klienta a s\u00fakromn\u00fd k\u013e\u00fa\u010d nie s\u00fa platn\u00fdm p\u00e1rom", + "bad_client_key": "Neplatn\u00fd s\u00fakromn\u00fd k\u013e\u00fa\u010d, uistite sa, \u017ee s\u00fabor s k\u00f3dom PEM je dodan\u00fd bez hesla", + "bad_discovery_prefix": "Neplatn\u00fd prefix zis\u0165ovania", + "bad_will": "Neplatn\u00e1 t\u00e9ma will", "bad_ws_headers": "Zadajte platn\u00e9 hlavi\u010dky HTTP ako objekt JSON", "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_inclusion": "Certifik\u00e1t klienta a s\u00fakromn\u00fd k\u013e\u00fa\u010d musia by\u0165 nakonfigurovan\u00e9 spolo\u010dne" @@ -15,16 +20,29 @@ "broker": { "data": { "advanced_options": "Pokro\u010dil\u00e9 nastavenia", + "broker": "Broker", + "certificate": "Nahrajte s\u00fabor vlastn\u00e9ho certifik\u00e1tu CA", + "client_cert": "Nahrajte s\u00fabor certifik\u00e1tu klienta", + "client_id": "ID klienta (ponechajte pr\u00e1zdne pre n\u00e1hodne vygenerovan\u00e9 \u010d\u00edslo)", "client_key": "Nahrajte s\u00fabor so s\u00fakromn\u00fdm k\u013e\u00fa\u010dom", + "keepalive": "\u010cas medzi odoslan\u00edm spr\u00e1v Keep Alive", "password": "Heslo", "port": "Port", "protocol": "MQTT protokol", + "set_ca_cert": "Overenie platnosti certifik\u00e1tu brokera", "set_client_cert": "Pou\u017eite klientsky certifik\u00e1t", + "tls_insecure": "Ignorova\u0165 overenie certifik\u00e1tu brokera", "transport": "MQTT transport", - "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" - } + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno", + "ws_headers": "Hlavi\u010dky WebSocket vo form\u00e1te JSON", + "ws_path": "Cesta WebSocket" + }, + "description": "Zadajte inform\u00e1cie o pripojen\u00ed v\u00e1\u0161ho sprostredkovate\u013ea protokolu MQTT." }, "hassio_confirm": { + "data": { + "discovery": "Povoli\u0165 zis\u0165ovanie" + }, "description": "Chcete nakonfigurova\u0165 dom\u00e1ceho asistenta na pripojenie k brokerovi MQTT poskytovan\u00e9mu doplnkom {addon}?", "title": "MQTT Broker cez doplnok Home Assistant" } @@ -42,13 +60,35 @@ "turn_on": "Zapn\u00fa\u0165" }, "trigger_type": { - "button_double_press": "\"{subtype}\" kliknut\u00e9 dvakr\u00e1t" + "button_double_press": "\"{subtype}\" kliknut\u00e9 dvakr\u00e1t", + "button_long_press": "Trvalo stla\u010den\u00e9 tla\u010didlo \"{subtype}\"", + "button_long_release": "\"{subtype}\" uvo\u013enen\u00e9 po dlhom stla\u010den\u00ed", + "button_quadruple_press": "\"{subtype}\" \u0161tvorn\u00e1sobne kliknut\u00e9", + "button_quintuple_press": "\"{subtype}\" p\u00e4\u0165n\u00e1sobne kliknut\u00e9", + "button_short_press": "\"{subtype}\" stla\u010den\u00e9", + "button_short_release": "\u201c{subtype}\u201c uvo\u013enen\u00e9", + "button_triple_press": "\"{subtype}\" trojn\u00e1sobne kliknut\u00e9" + } + }, + "issues": { + "deprecated_yaml": { + "description": "Manu\u00e1lne nakonfigurovan\u00fd MQTT {platform}(y) n\u00e1jdete pod k\u013e\u00fa\u010dom platformy `{platform}`. \n\nPresu\u0148te konfigur\u00e1ciu do integra\u010dn\u00e9ho k\u013e\u00fa\u010da `mqtt` a re\u0161tartujte Home Assistant, aby ste tento probl\u00e9m vyrie\u0161ili. \u010eal\u0161ie inform\u00e1cie n\u00e1jdete v [dokument\u00e1cii]({more_info_url}).", + "title": "Va\u0161a manu\u00e1lne nakonfigurovan\u00e1 {platform}(y) MQTT vy\u017eaduje pozornos\u0165" + }, + "deprecated_yaml_broker_settings": { + "description": "Nasleduj\u00face nastavenia n\u00e1jden\u00e9 v `configuration.yaml` boli migrovan\u00e9 do konfigura\u010dnej polo\u017eky MQTT a teraz prep\u00ed\u0161u nastavenia v `configuration.yaml`:\n `{deprecated_settings}` \n\nOdstr\u00e1\u0148te tieto nastavenia z `configuration.yaml` a re\u0161tartujte Home Assistant, aby ste tento probl\u00e9m vyrie\u0161ili. \u010eal\u0161ie inform\u00e1cie n\u00e1jdete v [dokument\u00e1cii]({more_info_url}).", + "title": "V `configuration.yaml` sa na\u0161li zastaran\u00e9 nastavenia MQTT" } }, "options": { "error": { + "bad_birth": "Neplatn\u00e1 t\u00e9ma birth", "bad_certificate": "Certifik\u00e1t CA je neplatn\u00fd", + "bad_client_cert": "Neplatn\u00fd certifik\u00e1t klienta, uistite sa, \u017ee je dodan\u00fd s\u00fabor s k\u00f3dom PEM", "bad_client_cert_key": "Certifik\u00e1t klienta a s\u00fakromn\u00fd k\u013e\u00fa\u010d nie s\u00fa platn\u00fdm p\u00e1rom", + "bad_client_key": "Neplatn\u00fd s\u00fakromn\u00fd k\u013e\u00fa\u010d, uistite sa, \u017ee s\u00fabor s k\u00f3dom PEM je dodan\u00fd bez hesla", + "bad_discovery_prefix": "Neplatn\u00fd prefix zis\u0165ovania", + "bad_will": "Neplatn\u00e1 t\u00e9ma will", "bad_ws_headers": "Zadajte platn\u00e9 hlavi\u010dky HTTP ako objekt JSON", "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_inclusion": "Certifik\u00e1t klienta a s\u00fakromn\u00fd k\u013e\u00fa\u010d musia by\u0165 nakonfigurovan\u00e9 spolo\u010dne" @@ -57,20 +97,42 @@ "broker": { "data": { "advanced_options": "Pokro\u010dil\u00e9 nastavenia", + "broker": "Broker", + "certificate": "Nahrajte s\u00fabor vlastn\u00e9ho certifik\u00e1tu CA", + "client_cert": "Nahrajte s\u00fabor certifik\u00e1tu klienta", + "client_id": "ID klienta (ponechajte pr\u00e1zdne pre n\u00e1hodne vygenerovan\u00e9 \u010d\u00edslo)", "client_key": "Nahrajte s\u00fabor so s\u00fakromn\u00fdm k\u013e\u00fa\u010dom", + "keepalive": "\u010cas medzi odoslan\u00edm spr\u00e1v Keep Alive", "password": "Heslo", "port": "Port", "protocol": "MQTT protokol", + "set_ca_cert": "Overenie platnosti certifik\u00e1tu brokera", "set_client_cert": "Pou\u017eite klientsky certifik\u00e1t", + "tls_insecure": "Ignorova\u0165 overenie certifik\u00e1tu brokera", "transport": "MQTT transport", - "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" - } + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno", + "ws_headers": "Hlavi\u010dky WebSocket vo form\u00e1te JSON", + "ws_path": "Cesta WebSocket" + }, + "description": "Zadajte inform\u00e1cie o pripojen\u00ed v\u00e1\u0161ho sprostredkovate\u013ea protokolu MQTT.", + "title": "Mo\u017enosti brokera" }, "options": { "data": { - "will_payload": "Odo\u0161le z\u00e1\u0165a\u017e", - "will_qos": "Odo\u0161le QoS" + "birth_enable": "Povoli\u0165 spr\u00e1vu birth", + "birth_payload": "Birth spr\u00e1va vy\u0165a\u017eenie", + "birth_qos": "QoS spr\u00e1vy birth", + "birth_retain": "Zachova\u0165 spr\u00e1vu birth", + "birth_topic": "T\u00e9ma spr\u00e1vy birth", + "discovery": "Povoli\u0165 zis\u0165ovanie", + "discovery_prefix": "Discovery prefix", + "will_enable": "Povolenie spr\u00e1v \"will\"", + "will_payload": "Will spr\u00e1va za\u0165a\u017eenie", + "will_qos": "Will spr\u00e1va QoS", + "will_retain": "Will spr\u00e1va sa zachov\u00e1", + "will_topic": "T\u00e9ma spr\u00e1v \"will\"" }, + "description": "Zis\u0165ovanie \u2013 ak je zis\u0165ovanie povolen\u00e9 (odpor\u00fa\u010da sa), Home Assistant automaticky objav\u00ed zariadenia a entity, ktor\u00e9 zverej\u0148uj\u00fa svoju konfigur\u00e1ciu na brokerovi MQTT. Ak je zis\u0165ovanie zak\u00e1zan\u00e9, v\u0161etky konfigur\u00e1cie sa musia vykona\u0165 manu\u00e1lne.\nPredpona zis\u0165ovania \u2013 Predpona, ktorou mus\u00ed za\u010d\u00edna\u0165 t\u00e9ma konfigur\u00e1cie automatick\u00e9ho zis\u0165ovania.\nSpr\u00e1va birth \u2013 Spr\u00e1va birth sa odo\u0161le pri ka\u017edom (op\u00e4tovnom) pripojen\u00ed dom\u00e1ceho asistenta k makl\u00e9rovi MQTT.\nWill message - Spr\u00e1va will bude odoslan\u00e1 v\u017edy, ke\u010f Home Assistant strat\u00ed spojenie s brokerom, a to ako v pr\u00edpade vy\u010distenia (napr. vypnutie Home Assistant), tak aj v pr\u00edpade ne\u010dist\u00e9ho stavu (napr. Home Assistant zlyh\u00e1 alebo strat\u00ed pripojenie k sieti) odpoji\u0165.", "title": "MQTT mo\u017enosti" } } diff --git a/homeassistant/components/mqtt/update.py b/homeassistant/components/mqtt/update.py index 874f6024a37..9ee0690b120 100644 --- a/homeassistant/components/mqtt/update.py +++ b/homeassistant/components/mqtt/update.py @@ -54,7 +54,7 @@ CONF_TITLE = "title" PLATFORM_SCHEMA_MODERN = MQTT_RO_SCHEMA.extend( { vol.Optional(CONF_COMMAND_TOPIC): valid_publish_topic, - vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA, + vol.Optional(CONF_DEVICE_CLASS): vol.Any(DEVICE_CLASSES_SCHEMA, None), vol.Optional(CONF_ENTITY_PICTURE): cv.string, vol.Optional(CONF_LATEST_VERSION_TEMPLATE): cv.template, vol.Optional(CONF_LATEST_VERSION_TOPIC): valid_subscribe_topic, @@ -177,20 +177,29 @@ class MqttUpdate(MqttEntity, UpdateEntity, RestoreEntity): json_payload = json_loads(payload) if isinstance(json_payload, dict): _LOGGER.debug( - "JSON payload detected after processing payload '%s' on topic %s", + ( + "JSON payload detected after processing payload '%s' on" + " topic %s" + ), json_payload, msg.topic, ) else: _LOGGER.debug( - "Non-dictionary JSON payload detected after processing payload '%s' on topic %s", + ( + "Non-dictionary JSON payload detected after processing" + " payload '%s' on topic %s" + ), payload, msg.topic, ) json_payload = {"installed_version": payload} except JSON_DECODE_EXCEPTIONS: _LOGGER.debug( - "No valid (JSON) payload detected after processing payload '%s' on topic %s", + ( + "No valid (JSON) payload detected after processing payload '%s'" + " on topic %s" + ), payload, msg.topic, ) diff --git a/homeassistant/components/mqtt/util.py b/homeassistant/components/mqtt/util.py index 97bb120f842..bf26729470d 100644 --- a/homeassistant/components/mqtt/util.py +++ b/homeassistant/components/mqtt/util.py @@ -84,8 +84,7 @@ def valid_subscribe_topic(topic: Any) -> str: if index != len(validated_topic) - 1: # If there are multiple wildcards, this will also trigger raise vol.Invalid( - "Multi-level wildcard must be the last " - "character in the topic filter." + "Multi-level wildcard must be the last character in the topic filter." ) if len(validated_topic) > 1 and validated_topic[index - 1] != "/": raise vol.Invalid( @@ -177,7 +176,7 @@ async def async_create_certificate_temp_files( await hass.async_add_executor_job(_create_temp_dir_and_files) -def get_file_path(option: str, default: str | None = None) -> Path | str | None: +def get_file_path(option: str, default: str | None = None) -> str | None: """Get file path of a certificate file.""" temp_dir = Path(tempfile.gettempdir()) / TEMP_DIR_NAME if not temp_dir.exists(): @@ -187,7 +186,7 @@ def get_file_path(option: str, default: str | None = None) -> Path | str | None: if not file_path.exists(): return default - return temp_dir / option + return str(temp_dir / option) def migrate_certificate_file_to_content(file_name_or_auto: str) -> str | None: diff --git a/homeassistant/components/mqtt/vacuum/schema_legacy.py b/homeassistant/components/mqtt/vacuum/schema_legacy.py index c8f8afd70df..94053e4fb72 100644 --- a/homeassistant/components/mqtt/vacuum/schema_legacy.py +++ b/homeassistant/components/mqtt/vacuum/schema_legacy.py @@ -220,6 +220,7 @@ class MqttVacuum(MqttEntity, VacuumEntity): _entity_id_format = ENTITY_ID_FORMAT _attributes_extra_blocked = MQTT_LEGACY_VACUUM_ATTRIBUTES_BLOCKED + _command_topic: str | None _encoding: str | None _qos: bool _retain: bool diff --git a/homeassistant/components/mqtt/vacuum/schema_state.py b/homeassistant/components/mqtt/vacuum/schema_state.py index 4e020c630a5..210e189c2fc 100644 --- a/homeassistant/components/mqtt/vacuum/schema_state.py +++ b/homeassistant/components/mqtt/vacuum/schema_state.py @@ -180,6 +180,7 @@ class MqttStateVacuum(MqttEntity, StateVacuumEntity): _entity_id_format = ENTITY_ID_FORMAT _attributes_extra_blocked = MQTT_VACUUM_ATTRIBUTES_BLOCKED + _command_topic: str | None _set_fan_speed_topic: str | None _send_command_topic: str | None _payloads: dict[str, str | None] diff --git a/homeassistant/components/mqtt_json/device_tracker.py b/homeassistant/components/mqtt_json/device_tracker.py index f0330e39e86..2c67751551c 100644 --- a/homeassistant/components/mqtt_json/device_tracker.py +++ b/homeassistant/components/mqtt_json/device_tracker.py @@ -59,8 +59,10 @@ async def async_setup_scanner( data = GPS_JSON_PAYLOAD_SCHEMA(json.loads(msg.payload)) except vol.MultipleInvalid: _LOGGER.error( - "Skipping update for following data " - "because of missing or malformatted data: %s", + ( + "Skipping update for following data " + "because of missing or malformatted data: %s" + ), msg.payload, ) return diff --git a/homeassistant/components/mullvad/translations/pt.json b/homeassistant/components/mullvad/translations/pt.json index 657ce03e544..6ed2059bc95 100644 --- a/homeassistant/components/mullvad/translations/pt.json +++ b/homeassistant/components/mullvad/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "unknown": "Erro inesperado" } } diff --git a/homeassistant/components/mutesync/translations/ko.json b/homeassistant/components/mutesync/translations/ko.json new file mode 100644 index 00000000000..e99fa57ed02 --- /dev/null +++ b/homeassistant/components/mutesync/translations/ko.json @@ -0,0 +1,15 @@ +{ + "config": { + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/mvglive/sensor.py b/homeassistant/components/mvglive/sensor.py index 0f0d32908ef..2f8467a0333 100644 --- a/homeassistant/components/mvglive/sensor.py +++ b/homeassistant/components/mvglive/sensor.py @@ -9,7 +9,7 @@ import MVGLive import voluptuous as vol from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity -from homeassistant.const import CONF_NAME, TIME_MINUTES +from homeassistant.const import CONF_NAME, UnitOfTime from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -141,7 +141,7 @@ class MVGLiveSensor(SensorEntity): @property def native_unit_of_measurement(self): """Return the unit this state is expressed in.""" - return TIME_MINUTES + return UnitOfTime.MINUTES def update(self) -> None: """Get the latest data and update the state.""" diff --git a/homeassistant/components/myq/translations/ko.json b/homeassistant/components/myq/translations/ko.json index 1218ce0e4f4..ab8e00af017 100644 --- a/homeassistant/components/myq/translations/ko.json +++ b/homeassistant/components/myq/translations/ko.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "\uc11c\ube44\uc2a4\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + "already_configured": "\uc11c\ube44\uc2a4\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" }, "error": { "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", @@ -10,6 +11,9 @@ }, "step": { "reauth_confirm": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638" + }, "description": "{username} \uc758 \ube44\ubc00\ubc88\ud638\uac00 \ub354 \uc774\uc0c1 \uc720\ud6a8\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.", "title": "MyQ \uacc4\uc815 \uc7ac\uc778\uc99d" }, diff --git a/homeassistant/components/myq/translations/pt.json b/homeassistant/components/myq/translations/pt.json index 14f1703524c..5eb4358649f 100644 --- a/homeassistant/components/myq/translations/pt.json +++ b/homeassistant/components/myq/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "O servi\u00e7o j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, diff --git a/homeassistant/components/mysensors/__init__.py b/homeassistant/components/mysensors/__init__.py index cc0d918ec05..8316572379c 100644 --- a/homeassistant/components/mysensors/__init__.py +++ b/homeassistant/components/mysensors/__init__.py @@ -181,5 +181,5 @@ def setup_mysensors_platform( if new_devices: _LOGGER.info("Adding new devices: %s", new_devices) if async_add_entities is not None: - async_add_entities(new_devices, True) + async_add_entities(new_devices) return new_devices diff --git a/homeassistant/components/mysensors/climate.py b/homeassistant/components/mysensors/climate.py index 38077768f2e..d207d7ff550 100644 --- a/homeassistant/components/mysensors/climate.py +++ b/homeassistant/components/mysensors/climate.py @@ -11,13 +11,8 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - ATTR_TEMPERATURE, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, - Platform, -) -from homeassistant.core import HomeAssistant +from homeassistant.const import ATTR_TEMPERATURE, Platform, UnitOfTemperature +from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.util.unit_system import METRIC_SYSTEM @@ -96,7 +91,9 @@ class MySensorsHVAC(mysensors.device.MySensorsEntity, ClimateEntity): def temperature_unit(self) -> str: """Return the unit of measurement.""" return ( - TEMP_CELSIUS if self.hass.config.units is METRIC_SYSTEM else TEMP_FAHRENHEIT + UnitOfTemperature.CELSIUS + if self.hass.config.units is METRIC_SYSTEM + else UnitOfTemperature.FAHRENHEIT ) @property @@ -216,7 +213,8 @@ class MySensorsHVAC(mysensors.device.MySensorsEntity, ClimateEntity): self._values[self.value_type] = hvac_mode self.async_write_ha_state() - async def async_update(self) -> None: + @callback + def _async_update(self) -> None: """Update the controller with the latest value from a sensor.""" - await super().async_update() + super()._async_update() self._values[self.value_type] = DICT_MYS_TO_HA[self._values[self.value_type]] diff --git a/homeassistant/components/mysensors/device.py b/homeassistant/components/mysensors/device.py index d9f95fa391f..bb5e0d72327 100644 --- a/homeassistant/components/mysensors/device.py +++ b/homeassistant/components/mysensors/device.py @@ -1,7 +1,7 @@ """Handle MySensors devices.""" from __future__ import annotations -from functools import partial +from abc import ABC, abstractmethod import logging from typing import Any @@ -10,6 +10,7 @@ from mysensors.sensor import ChildSensor from homeassistant.const import ATTR_BATTERY_LEVEL, STATE_OFF, STATE_ON, Platform from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.debounce import Debouncer from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import DeviceInfo, Entity @@ -34,7 +35,7 @@ ATTR_HEARTBEAT = "heartbeat" MYSENSORS_PLATFORM_DEVICES = "mysensors_devices_{}" -class MySensorsDevice: +class MySensorsDevice(ABC): """Representation of a MySensors device.""" hass: HomeAssistant @@ -55,7 +56,7 @@ class MySensorsDevice: self.value_type: int = value_type # value_type as int. string variant can be looked up in gateway consts self.child_type = self._child.type self._values: dict[int, Any] = {} - self._update_scheduled = False + self._debouncer: Debouncer | None = None @property def dev_id(self) -> DevId: @@ -151,7 +152,8 @@ class MySensorsDevice: return attr - async def async_update(self) -> None: + @callback + def _async_update(self) -> None: """Update the controller with the latest value from a sensor.""" node = self.gateway.sensors[self.node_id] child = node.children[self.child_id] @@ -178,28 +180,23 @@ class MySensorsDevice: else: self._values[value_type] = value - async def _async_update_callback(self) -> None: - """Update the device.""" - raise NotImplementedError - @callback - def async_update_callback(self) -> None: + @abstractmethod + def _async_update_callback(self) -> None: + """Update the device.""" + + async def async_update_callback(self) -> None: """Update the device after delay.""" - if self._update_scheduled: - return + if not self._debouncer: + self._debouncer = Debouncer( + self.hass, + _LOGGER, + cooldown=UPDATE_DELAY, + immediate=False, + function=self._async_update_callback, + ) - async def update() -> None: - """Perform update.""" - try: - await self._async_update_callback() - except Exception: # pylint: disable=broad-except - _LOGGER.exception("Error updating %s", self.name) - finally: - self._update_scheduled = False - - self._update_scheduled = True - delayed_update = partial(self.hass.async_create_task, update()) - self.hass.loop.call_later(UPDATE_DELAY, delayed_update) + await self._debouncer.async_call() def get_mysensors_devices( @@ -235,9 +232,11 @@ class MySensorsEntity(MySensorsDevice, Entity): return attr - async def _async_update_callback(self) -> None: + @callback + def _async_update_callback(self) -> None: """Update the entity.""" - await self.async_update_ha_state(True) + self._async_update() + self.async_write_ha_state() async def async_added_to_hass(self) -> None: """Register update callback.""" @@ -255,3 +254,4 @@ class MySensorsEntity(MySensorsDevice, Entity): self.async_update_callback, ) ) + self._async_update() diff --git a/homeassistant/components/mysensors/device_tracker.py b/homeassistant/components/mysensors/device_tracker.py index a3fd86d1b44..f2ea3736d15 100644 --- a/homeassistant/components/mysensors/device_tracker.py +++ b/homeassistant/components/mysensors/device_tracker.py @@ -5,7 +5,7 @@ from typing import Any, cast from homeassistant.components.device_tracker import AsyncSeeCallback from homeassistant.const import Platform -from homeassistant.core import HomeAssistant +from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.util import slugify @@ -74,18 +74,21 @@ class MySensorsDeviceScanner(mysensors.device.MySensorsDevice): self.async_see = async_see self.hass = hass - async def _async_update_callback(self) -> None: + @callback + def _async_update_callback(self) -> None: """Update the device.""" - await self.async_update() + self._async_update() node = self.gateway.sensors[self.node_id] child = node.children[self.child_id] position = child.values[self.value_type] latitude, longitude, _ = position.split(",") - await self.async_see( - dev_id=slugify(self.name), - host_name=self.name, - gps=(latitude, longitude), - battery=node.battery_level, - attributes=self._extra_attributes, + self.hass.async_create_task( + self.async_see( + dev_id=slugify(self.name), + host_name=self.name, + gps=(latitude, longitude), + battery=node.battery_level, + attributes=self._extra_attributes, + ) ) diff --git a/homeassistant/components/mysensors/gateway.py b/homeassistant/components/mysensors/gateway.py index a920faf49fa..635df264f75 100644 --- a/homeassistant/components/mysensors/gateway.py +++ b/homeassistant/components/mysensors/gateway.py @@ -329,6 +329,6 @@ def _gw_callback_factory( if msg_handler is None: return - hass.async_create_task(msg_handler(hass, gateway_id, msg)) + msg_handler(hass, gateway_id, msg) return mysensors_callback diff --git a/homeassistant/components/mysensors/handler.py b/homeassistant/components/mysensors/handler.py index 57ff12fc6f0..8a77d167f8b 100644 --- a/homeassistant/components/mysensors/handler.py +++ b/homeassistant/components/mysensors/handler.py @@ -1,8 +1,7 @@ """Handle MySensors messages.""" from __future__ import annotations -from collections.abc import Callable, Coroutine -from typing import Any +from collections.abc import Callable from mysensors import Message @@ -16,30 +15,31 @@ from .device import get_mysensors_devices from .helpers import discover_mysensors_platform, validate_set_msg HANDLERS: decorator.Registry[ - str, Callable[[HomeAssistant, GatewayId, Message], Coroutine[Any, Any, None]] + str, Callable[[HomeAssistant, GatewayId, Message], None] ] = decorator.Registry() @HANDLERS.register("set") -async def handle_set(hass: HomeAssistant, gateway_id: GatewayId, msg: Message) -> None: +@callback +def handle_set(hass: HomeAssistant, gateway_id: GatewayId, msg: Message) -> None: """Handle a mysensors set message.""" validated = validate_set_msg(gateway_id, msg) _handle_child_update(hass, gateway_id, validated) @HANDLERS.register("internal") -async def handle_internal( - hass: HomeAssistant, gateway_id: GatewayId, msg: Message -) -> None: +@callback +def handle_internal(hass: HomeAssistant, gateway_id: GatewayId, msg: Message) -> None: """Handle a mysensors internal message.""" internal = msg.gateway.const.Internal(msg.sub_type) if (handler := HANDLERS.get(internal.name)) is None: return - await handler(hass, gateway_id, msg) + handler(hass, gateway_id, msg) @HANDLERS.register("I_BATTERY_LEVEL") -async def handle_battery_level( +@callback +def handle_battery_level( hass: HomeAssistant, gateway_id: GatewayId, msg: Message ) -> None: """Handle an internal battery level message.""" @@ -47,15 +47,15 @@ async def handle_battery_level( @HANDLERS.register("I_HEARTBEAT_RESPONSE") -async def handle_heartbeat( - hass: HomeAssistant, gateway_id: GatewayId, msg: Message -) -> None: +@callback +def handle_heartbeat(hass: HomeAssistant, gateway_id: GatewayId, msg: Message) -> None: """Handle an heartbeat.""" _handle_node_update(hass, gateway_id, msg) @HANDLERS.register("I_SKETCH_NAME") -async def handle_sketch_name( +@callback +def handle_sketch_name( hass: HomeAssistant, gateway_id: GatewayId, msg: Message ) -> None: """Handle an internal sketch name message.""" @@ -63,7 +63,8 @@ async def handle_sketch_name( @HANDLERS.register("I_SKETCH_VERSION") -async def handle_sketch_version( +@callback +def handle_sketch_version( hass: HomeAssistant, gateway_id: GatewayId, msg: Message ) -> None: """Handle an internal sketch version message.""" diff --git a/homeassistant/components/mysensors/light.py b/homeassistant/components/mysensors/light.py index e7ccdf8c569..e83002ed870 100644 --- a/homeassistant/components/mysensors/light.py +++ b/homeassistant/components/mysensors/light.py @@ -144,9 +144,10 @@ class MySensorsLightDimmer(MySensorsLight): if self.assumed_state: self.async_write_ha_state() - async def async_update(self) -> None: + @callback + def _async_update(self) -> None: """Update the controller with the latest value from a sensor.""" - await super().async_update() + super()._async_update() self._async_update_light() self._async_update_dimmer() @@ -181,9 +182,10 @@ class MySensorsLightRGB(MySensorsLight): self._attr_rgb_color = new_rgb self._values[self.value_type] = hex_color - async def async_update(self) -> None: + @callback + def _async_update(self) -> None: """Update the controller with the latest value from a sensor.""" - await super().async_update() + super()._async_update() self._async_update_light() self._async_update_dimmer() self._async_update_rgb_or_w() diff --git a/homeassistant/components/mysensors/notify.py b/homeassistant/components/mysensors/notify.py index 9083b325e8d..b19ce7b85ca 100644 --- a/homeassistant/components/mysensors/notify.py +++ b/homeassistant/components/mysensors/notify.py @@ -5,7 +5,7 @@ from typing import Any, cast from homeassistant.components.notify import ATTR_TARGET, BaseNotificationService from homeassistant.const import Platform -from homeassistant.core import HomeAssistant +from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from .. import mysensors @@ -35,6 +35,11 @@ async def async_get_service( class MySensorsNotificationDevice(mysensors.device.MySensorsDevice): """Represent a MySensors Notification device.""" + @callback + def _async_update_callback(self) -> None: + """Update the device.""" + self._async_update() + def send_msg(self, msg: str) -> None: """Send a message.""" for sub_msg in [msg[i : i + 25] for i in range(0, len(msg), 25)]: diff --git a/homeassistant/components/mysensors/sensor.py b/homeassistant/components/mysensors/sensor.py index 5bafed04353..225a75e8c84 100644 --- a/homeassistant/components/mysensors/sensor.py +++ b/homeassistant/components/mysensors/sensor.py @@ -15,22 +15,20 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONDUCTIVITY, DEGREE, - ELECTRIC_CURRENT_AMPERE, - ELECTRIC_POTENTIAL_MILLIVOLT, - ELECTRIC_POTENTIAL_VOLT, - ENERGY_KILO_WATT_HOUR, - FREQUENCY_HERTZ, - LENGTH_METERS, LIGHT_LUX, - MASS_KILOGRAMS, PERCENTAGE, - POWER_VOLT_AMPERE, - POWER_WATT, - SOUND_PRESSURE_DB, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, - VOLUME_CUBIC_METERS, Platform, + UnitOfApparentPower, + UnitOfElectricCurrent, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfFrequency, + UnitOfLength, + UnitOfMass, + UnitOfPower, + UnitOfSoundPressure, + UnitOfTemperature, + UnitOfVolume, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.dispatcher import async_dispatcher_connect @@ -94,12 +92,12 @@ SENSORS: dict[str, SensorEntityDescription] = { ), "V_WEIGHT": SensorEntityDescription( key="V_WEIGHT", - native_unit_of_measurement=MASS_KILOGRAMS, + native_unit_of_measurement=UnitOfMass.KILOGRAMS, device_class=SensorDeviceClass.WEIGHT, ), "V_DISTANCE": SensorEntityDescription( key="V_DISTANCE", - native_unit_of_measurement=LENGTH_METERS, + native_unit_of_measurement=UnitOfLength.METERS, device_class=SensorDeviceClass.DISTANCE, ), "V_IMPEDANCE": SensorEntityDescription( @@ -108,13 +106,13 @@ SENSORS: dict[str, SensorEntityDescription] = { ), "V_WATT": SensorEntityDescription( key="V_WATT", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), "V_KWH": SensorEntityDescription( key="V_KWH", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), @@ -124,22 +122,25 @@ SENSORS: dict[str, SensorEntityDescription] = { icon="mdi:white-balance-sunny", ), "V_FLOW": SensorEntityDescription( + # The documentation on this measurement is inconsistent. + # Better not to set a device class here yet. key="V_FLOW", - native_unit_of_measurement=LENGTH_METERS, + native_unit_of_measurement=UnitOfLength.METERS, icon="mdi:gauge", ), "V_VOLUME": SensorEntityDescription( key="V_VOLUME", - native_unit_of_measurement=VOLUME_CUBIC_METERS, + native_unit_of_measurement=UnitOfVolume.CUBIC_METERS, + device_class=SensorDeviceClass.VOLUME, ), "V_LEVEL_S_SOUND": SensorEntityDescription( key="V_LEVEL_S_SOUND", - native_unit_of_measurement=SOUND_PRESSURE_DB, - icon="mdi:volume-high", + native_unit_of_measurement=UnitOfSoundPressure.DECIBEL, + device_class=SensorDeviceClass.SOUND_PRESSURE, ), "V_LEVEL_S_VIBRATION": SensorEntityDescription( key="V_LEVEL_S_VIBRATION", - native_unit_of_measurement=FREQUENCY_HERTZ, + native_unit_of_measurement=UnitOfFrequency.HERTZ, ), "V_LEVEL_S_LIGHT_LEVEL": SensorEntityDescription( key="V_LEVEL_S_LIGHT_LEVEL", @@ -154,13 +155,13 @@ SENSORS: dict[str, SensorEntityDescription] = { ), "V_VOLTAGE": SensorEntityDescription( key="V_VOLTAGE", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, ), "V_CURRENT": SensorEntityDescription( key="V_CURRENT", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, ), @@ -170,7 +171,8 @@ SENSORS: dict[str, SensorEntityDescription] = { ), "V_ORP": SensorEntityDescription( key="V_ORP", - native_unit_of_measurement=ELECTRIC_POTENTIAL_MILLIVOLT, + native_unit_of_measurement=UnitOfElectricPotential.MILLIVOLT, + device_class=SensorDeviceClass.VOLTAGE, ), "V_EC": SensorEntityDescription( key="V_EC", @@ -182,7 +184,8 @@ SENSORS: dict[str, SensorEntityDescription] = { ), "V_VA": SensorEntityDescription( key="V_VA", - native_unit_of_measurement=POWER_VOLT_AMPERE, + native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE, + device_class=SensorDeviceClass.APPARENT_POWER, ), } @@ -244,8 +247,8 @@ class MySensorsSensor(mysensors.device.MySensorsEntity, SensorEntity): if set_req(self.value_type) == set_req.V_TEMP: if self.hass.config.units is METRIC_SYSTEM: - return TEMP_CELSIUS - return TEMP_FAHRENHEIT + return UnitOfTemperature.CELSIUS + return UnitOfTemperature.FAHRENHEIT if hasattr(self, "entity_description"): return self.entity_description.native_unit_of_measurement diff --git a/homeassistant/components/mysensors/switch.py b/homeassistant/components/mysensors/switch.py index dd79c6819ec..df0e6fe8532 100644 --- a/homeassistant/components/mysensors/switch.py +++ b/homeassistant/components/mysensors/switch.py @@ -8,7 +8,7 @@ import voluptuous as vol from homeassistant.components.switch import SwitchEntity from homeassistant.config_entries import ConfigEntry from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF, STATE_ON, Platform -from homeassistant.core import HomeAssistant, ServiceCall +from homeassistant.core import HomeAssistant, ServiceCall, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -178,7 +178,8 @@ class MySensorsIRSwitch(MySensorsSwitch): self._values[set_req.V_LIGHT] = STATE_OFF self.async_write_ha_state() - async def async_update(self) -> None: + @callback + def _async_update(self) -> None: """Update the controller with the latest value from a sensor.""" - await super().async_update() + super()._async_update() self._ir_code = self._values.get(self.value_type) diff --git a/homeassistant/components/mysensors/translations/hu.json b/homeassistant/components/mysensors/translations/hu.json index 4a921724dd3..154afe15613 100644 --- a/homeassistant/components/mysensors/translations/hu.json +++ b/homeassistant/components/mysensors/translations/hu.json @@ -43,7 +43,7 @@ "gw_mqtt": { "data": { "persistence_file": "perzisztencia f\u00e1jl (hagyja \u00fcresen az automatikus gener\u00e1l\u00e1shoz)", - "retain": "mqtt megtart\u00e1sa", + "retain": "mqtt tart\u00e1s (retain)", "topic_in_prefix": "el\u0151tag a beviteli t\u00e9m\u00e1khoz (topic_in_prefix)", "topic_out_prefix": "el\u0151tag a kimeneti t\u00e9m\u00e1khoz (topic_out_prefix)", "version": "MySensors verzi\u00f3" diff --git a/homeassistant/components/mysensors/translations/nl.json b/homeassistant/components/mysensors/translations/nl.json index ba2eecff5d8..32fa49352e7 100644 --- a/homeassistant/components/mysensors/translations/nl.json +++ b/homeassistant/components/mysensors/translations/nl.json @@ -43,7 +43,7 @@ "gw_mqtt": { "data": { "persistence_file": "persistentiebestand (leeg laten om automatisch te genereren)", - "retain": "MQTT retentie", + "retain": "MQTT data vasthouden (retain)", "topic_in_prefix": "prefix voor inkomende topics (topic_in_prefix)", "topic_out_prefix": "voorvoegsel voor uitgaande topics (topic_out_prefix)", "version": "MySensors-versie" diff --git a/homeassistant/components/mysensors/translations/sk.json b/homeassistant/components/mysensors/translations/sk.json index b73b76616e6..a0c4453cd92 100644 --- a/homeassistant/components/mysensors/translations/sk.json +++ b/homeassistant/components/mysensors/translations/sk.json @@ -8,12 +8,16 @@ "invalid_auth": "Neplatn\u00e9 overenie", "invalid_device": "Neplatn\u00e9 zariadenie", "invalid_ip": "Neplatn\u00e1 IP adresa", + "invalid_persistence_file": "Neplatn\u00fd s\u00fabro persistence", "invalid_port": "Neplatn\u00e9 \u010d\u00edslo portu", + "invalid_publish_topic": "Neplatn\u00e1 t\u00e9ma publikovania", "invalid_serial": "Neplatn\u00fd s\u00e9riov\u00fd port", + "invalid_subscribe_topic": "Neplatn\u00e1 t\u00e9ma odberu", "invalid_version": "Neplatn\u00e1 verzia MySensors", "mqtt_required": "Integr\u00e1cia MQTT nie je nastaven\u00e1", "not_a_number": "Zadajte \u010d\u00edslo", "port_out_of_range": "\u010c\u00edslo portu mus\u00ed by\u0165 minim\u00e1lne 1 a maxim\u00e1lne 65535", + "same_topic": "T\u00e9my odberu a publikovania s\u00fa rovnak\u00e9", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "error": { @@ -24,16 +28,24 @@ "invalid_auth": "Neplatn\u00e9 overenie", "invalid_device": "Neplatn\u00e9 zariadenie", "invalid_ip": "Neplatn\u00e1 IP adresa", + "invalid_persistence_file": "Neplatn\u00fd s\u00fabro persistence", "invalid_port": "Neplatn\u00e9 \u010d\u00edslo portu", + "invalid_publish_topic": "Neplatn\u00e1 t\u00e9ma publikovania", "invalid_serial": "Neplatn\u00fd s\u00e9riov\u00fd port", + "invalid_subscribe_topic": "Neplatn\u00e1 t\u00e9ma odberu", "invalid_version": "Neplatn\u00e1 verzia MySensors", "not_a_number": "Zadajte \u010d\u00edslo", "port_out_of_range": "\u010c\u00edslo portu mus\u00ed by\u0165 minim\u00e1lne 1 a maxim\u00e1lne 65535", + "same_topic": "T\u00e9my predplatn\u00e9ho a publikovania s\u00fa rovnak\u00e9", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "gw_mqtt": { "data": { + "persistence_file": "s\u00fabor perzistence (pre automatick\u00e9 generovanie nechajte pr\u00e1zdne)", + "retain": "Ponecha\u0165 MQTT", + "topic_in_prefix": "prefix pre vstupn\u00e9 t\u00e9my (topic_in_prefix)", + "topic_out_prefix": "prefix pre v\u00fdstupn\u00e9 t\u00e9my (topic_out_prefix)", "version": "Verzia MySensors" }, "description": "Nastavenie br\u00e1ny MQTT" @@ -42,12 +54,15 @@ "data": { "baud_rate": "prenosov\u00e1 r\u00fdchlos\u0165", "device": "S\u00e9riov\u00fd port", + "persistence_file": "s\u00fabor perzistence (pre automatick\u00e9 generovanie nechajte pr\u00e1zdne)", "version": "Verzia MySensors" - } + }, + "description": "Nastavenie s\u00e9riovej br\u00e1ny" }, "gw_tcp": { "data": { "device": "IP adresa br\u00e1ny", + "persistence_file": "s\u00fabor perzistence (pre automatick\u00e9 generovanie nechajte pr\u00e1zdne)", "tcp_port": "port", "version": "Verzia MySensors" }, diff --git a/homeassistant/components/nam/__init__.py b/homeassistant/components/nam/__init__.py index 0fbc9384634..c035184c37b 100644 --- a/homeassistant/components/nam/__init__.py +++ b/homeassistant/components/nam/__init__.py @@ -92,7 +92,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return unload_ok -class NAMDataUpdateCoordinator(DataUpdateCoordinator): +class NAMDataUpdateCoordinator(DataUpdateCoordinator[NAMSensors]): """Class to manage fetching Nettigo Air Monitor data.""" def __init__( diff --git a/homeassistant/components/nam/manifest.json b/homeassistant/components/nam/manifest.json index 70da6555a1e..936c389339d 100644 --- a/homeassistant/components/nam/manifest.json +++ b/homeassistant/components/nam/manifest.json @@ -3,7 +3,7 @@ "name": "Nettigo Air Monitor", "documentation": "https://www.home-assistant.io/integrations/nam", "codeowners": ["@bieniu"], - "requirements": ["nettigo-air-monitor==1.5.0"], + "requirements": ["nettigo-air-monitor==1.6.0"], "zeroconf": [ { "type": "_http._tcp.local.", diff --git a/homeassistant/components/nam/sensor.py b/homeassistant/components/nam/sensor.py index ce3fdbf16a8..13ed5675b3c 100644 --- a/homeassistant/components/nam/sensor.py +++ b/homeassistant/components/nam/sensor.py @@ -17,9 +17,9 @@ from homeassistant.const import ( CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, CONCENTRATION_PARTS_PER_MILLION, PERCENTAGE, - PRESSURE_HPA, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, - TEMP_CELSIUS, + UnitOfPressure, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry @@ -81,42 +81,42 @@ SENSORS: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key=ATTR_BME280_PRESSURE, name="BME280 pressure", - native_unit_of_measurement=PRESSURE_HPA, + native_unit_of_measurement=UnitOfPressure.HPA, device_class=SensorDeviceClass.PRESSURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=ATTR_BME280_TEMPERATURE, name="BME280 temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=ATTR_BMP180_PRESSURE, name="BMP180 pressure", - native_unit_of_measurement=PRESSURE_HPA, + native_unit_of_measurement=UnitOfPressure.HPA, device_class=SensorDeviceClass.PRESSURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=ATTR_BMP180_TEMPERATURE, name="BMP180 temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=ATTR_BMP280_PRESSURE, name="BMP280 pressure", - native_unit_of_measurement=PRESSURE_HPA, + native_unit_of_measurement=UnitOfPressure.HPA, device_class=SensorDeviceClass.PRESSURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=ATTR_BMP280_TEMPERATURE, name="BMP280 temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), @@ -130,7 +130,7 @@ SENSORS: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key=ATTR_HECA_TEMPERATURE, name="HECA temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), @@ -150,7 +150,9 @@ SENSORS: tuple[SensorEntityDescription, ...] = ( key=ATTR_PMSX003_CAQI_LEVEL, name="PMSx003 CAQI level", icon="mdi:air-filter", - device_class="nam__caqi_level", + device_class=SensorDeviceClass.ENUM, + options=["very low", "low", "medium", "high", "very high"], + translation_key="caqi_level", ), SensorEntityDescription( key=ATTR_PMSX003_P0, @@ -182,7 +184,9 @@ SENSORS: tuple[SensorEntityDescription, ...] = ( key=ATTR_SDS011_CAQI_LEVEL, name="SDS011 CAQI level", icon="mdi:air-filter", - device_class="nam__caqi_level", + device_class=SensorDeviceClass.ENUM, + options=["very low", "low", "medium", "high", "very high"], + translation_key="caqi_level", ), SensorEntityDescription( key=ATTR_SDS011_P1, @@ -208,7 +212,7 @@ SENSORS: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key=ATTR_SHT3X_TEMPERATURE, name="SHT3X temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), @@ -221,7 +225,9 @@ SENSORS: tuple[SensorEntityDescription, ...] = ( key=ATTR_SPS30_CAQI_LEVEL, name="SPS30 CAQI level", icon="mdi:air-filter", - device_class="nam__caqi_level", + device_class=SensorDeviceClass.ENUM, + options=["very low", "low", "medium", "high", "very high"], + translation_key="caqi_level", ), SensorEntityDescription( key=ATTR_SPS30_P0, @@ -261,7 +267,7 @@ SENSORS: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key=ATTR_DHT22_TEMPERATURE, name="DHT22 temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), diff --git a/homeassistant/components/nam/strings.json b/homeassistant/components/nam/strings.json index dab6eefb095..a37bcc2983e 100644 --- a/homeassistant/components/nam/strings.json +++ b/homeassistant/components/nam/strings.json @@ -37,5 +37,18 @@ "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]", "reauth_unsuccessful": "Re-authentication was unsuccessful, please remove the integration and set it up again." } + }, + "entity": { + "sensor": { + "caqi_level": { + "state": { + "very low": "Very low", + "low": "Low", + "medium": "Medium", + "high": "High", + "very high": "Very high" + } + } + } } } diff --git a/homeassistant/components/nam/strings.sensor.json b/homeassistant/components/nam/strings.sensor.json deleted file mode 100644 index ee53079d7d0..00000000000 --- a/homeassistant/components/nam/strings.sensor.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "state": { - "nam__caqi_level": { - "very low": "Very low", - "low": "Low", - "medium": "Medium", - "high": "High", - "very high": "Very high" - } - } -} diff --git a/homeassistant/components/nam/translations/ca.json b/homeassistant/components/nam/translations/ca.json index 9b2a11e83fe..de1d88e0b55 100644 --- a/homeassistant/components/nam/translations/ca.json +++ b/homeassistant/components/nam/translations/ca.json @@ -37,5 +37,18 @@ "description": "Configura la integraci\u00f3 Nettigo Air Monitor." } } + }, + "entity": { + "sensor": { + "caqi_level": { + "state": { + "high": "Alt", + "low": "Baix", + "medium": "Mitj\u00e0", + "very high": "Molt alt", + "very low": "Molt baix" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/nam/translations/de.json b/homeassistant/components/nam/translations/de.json index 7c30b441378..1e6307a5117 100644 --- a/homeassistant/components/nam/translations/de.json +++ b/homeassistant/components/nam/translations/de.json @@ -37,5 +37,18 @@ "description": "Einrichten der Nettigo Air Monitor Integration." } } + }, + "entity": { + "sensor": { + "caqi_level": { + "state": { + "high": "Hoch", + "low": "Niedrig", + "medium": "Mittel", + "very high": "Sehr hoch", + "very low": "Sehr niedrig" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/nam/translations/el.json b/homeassistant/components/nam/translations/el.json index 36896d9fa24..fe1bbc61634 100644 --- a/homeassistant/components/nam/translations/el.json +++ b/homeassistant/components/nam/translations/el.json @@ -37,5 +37,18 @@ "description": "\u03a1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 Nettigo Air Monitor." } } + }, + "entity": { + "sensor": { + "caqi_level": { + "state": { + "high": "\u03a5\u03c8\u03b7\u03bb\u03cc", + "low": "\u03a7\u03b1\u03bc\u03b7\u03bb\u03cc", + "medium": "\u039c\u03b5\u03c3\u03b1\u03af\u03bf", + "very high": "\u03a0\u03bf\u03bb\u03cd \u03c5\u03c8\u03b7\u03bb\u03cc", + "very low": "\u03a0\u03bf\u03bb\u03cd \u03c7\u03b1\u03bc\u03b7\u03bb\u03cc" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/nam/translations/en.json b/homeassistant/components/nam/translations/en.json index 4378f8d6c51..4fd31471742 100644 --- a/homeassistant/components/nam/translations/en.json +++ b/homeassistant/components/nam/translations/en.json @@ -37,5 +37,18 @@ "description": "Set up Nettigo Air Monitor integration." } } + }, + "entity": { + "sensor": { + "caqi_level": { + "state": { + "high": "High", + "low": "Low", + "medium": "Medium", + "very high": "Very high", + "very low": "Very low" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/nam/translations/es.json b/homeassistant/components/nam/translations/es.json index 7b9558537a6..e76af9e18bb 100644 --- a/homeassistant/components/nam/translations/es.json +++ b/homeassistant/components/nam/translations/es.json @@ -37,5 +37,18 @@ "description": "Configurar la integraci\u00f3n Nettigo Air Monitor." } } + }, + "entity": { + "sensor": { + "caqi_level": { + "state": { + "high": "Alto", + "low": "Bajo", + "medium": "Medio", + "very high": "Muy alto", + "very low": "Muy bajo" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/nam/translations/et.json b/homeassistant/components/nam/translations/et.json index c8b3040ecbb..81f885df7f5 100644 --- a/homeassistant/components/nam/translations/et.json +++ b/homeassistant/components/nam/translations/et.json @@ -37,5 +37,18 @@ "description": "Seadista Nettigo Air Monitori sidumine." } } + }, + "entity": { + "sensor": { + "caqi_level": { + "state": { + "high": "K\u00f5rge", + "low": "Madal", + "medium": "Keskmine", + "very high": "V\u00e4ga k\u00f5rge", + "very low": "V\u00e4ga madal" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/nam/translations/hu.json b/homeassistant/components/nam/translations/hu.json index e0aa942afd8..d31364681db 100644 --- a/homeassistant/components/nam/translations/hu.json +++ b/homeassistant/components/nam/translations/hu.json @@ -37,5 +37,18 @@ "description": "\u00c1ll\u00edtsa be a Nettigo Air Monitor integr\u00e1ci\u00f3j\u00e1t." } } + }, + "entity": { + "sensor": { + "caqi_level": { + "state": { + "high": "Magas", + "low": "Alacsony", + "medium": "K\u00f6zepes", + "very high": "Nagyon magas", + "very low": "Nagyon alacsony" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/nam/translations/id.json b/homeassistant/components/nam/translations/id.json index 7b31e71e6d1..414c182231c 100644 --- a/homeassistant/components/nam/translations/id.json +++ b/homeassistant/components/nam/translations/id.json @@ -37,5 +37,18 @@ "description": "Siapkan integrasi Nettigo Air Monitor." } } + }, + "entity": { + "sensor": { + "caqi_level": { + "state": { + "high": "Tinggi", + "low": "Rendah", + "medium": "Sedang", + "very high": "Sangat tinggi", + "very low": "Sangat rendah" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/nam/translations/it.json b/homeassistant/components/nam/translations/it.json index 1bbacd849fe..805c2608d56 100644 --- a/homeassistant/components/nam/translations/it.json +++ b/homeassistant/components/nam/translations/it.json @@ -37,5 +37,18 @@ "description": "Configura l'integrazione di Nettigo Air Monitor." } } + }, + "entity": { + "sensor": { + "caqi_level": { + "state": { + "high": "Alto", + "low": "Basso", + "medium": "Medio", + "very high": "Molto alto", + "very low": "Molto basso" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/nam/translations/ko.json b/homeassistant/components/nam/translations/ko.json index 8112e2968c5..b1e2322610f 100644 --- a/homeassistant/components/nam/translations/ko.json +++ b/homeassistant/components/nam/translations/ko.json @@ -2,11 +2,32 @@ "config": { "abort": { "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4", "reauth_unsuccessful": "\uc7ac\uc778\uc99d\uc5d0 \uc2e4\ud328\ud588\uc2b5\ub2c8\ub2e4. \uad6c\uc131\uc744 \uc81c\uac70\ud558\uace0 \ub2e4\uc2dc \uc124\uc815\ud558\uc2ed\uc2dc\uc624." }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, "step": { + "credentials": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + } + }, "reauth_confirm": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + }, "description": "\ud638\uc2a4\ud2b8\uc5d0 \ub300\ud55c \uc62c\ubc14\ub978 \uc0ac\uc6a9\uc790 \uc774\ub984\uacfc \uc554\ud638\ub97c \uc785\ub825\ud558\uc2ed\uc2dc\uc624: {host}" + }, + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8" + } } } } diff --git a/homeassistant/components/nam/translations/nl.json b/homeassistant/components/nam/translations/nl.json index a3d7925479f..04d310adf9f 100644 --- a/homeassistant/components/nam/translations/nl.json +++ b/homeassistant/components/nam/translations/nl.json @@ -37,5 +37,16 @@ "description": "Stel Nettigo Air Monitor integratie in." } } + }, + "entity": { + "sensor": { + "caqi_level": { + "state": { + "high": "Hoog", + "low": "Laag", + "medium": "Medium" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/nam/translations/no.json b/homeassistant/components/nam/translations/no.json index 758ea3c4aba..f190bae5560 100644 --- a/homeassistant/components/nam/translations/no.json +++ b/homeassistant/components/nam/translations/no.json @@ -37,5 +37,18 @@ "description": "Sett opp integrering av Nettigo Air Monitor." } } + }, + "entity": { + "sensor": { + "caqi_level": { + "state": { + "high": "H\u00f8y", + "low": "Lav", + "medium": "Medium", + "very high": "Veldig h\u00f8y", + "very low": "Veldig lav" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/nam/translations/pl.json b/homeassistant/components/nam/translations/pl.json index c9a4623a471..931eb6ebab0 100644 --- a/homeassistant/components/nam/translations/pl.json +++ b/homeassistant/components/nam/translations/pl.json @@ -37,5 +37,18 @@ "description": "Konfiguracja integracji Nettigo Air Monitor" } } + }, + "entity": { + "sensor": { + "caqi_level": { + "state": { + "high": "wysoki", + "low": "niski", + "medium": "\u015bredni", + "very high": "bardzo wysoki", + "very low": "bardzo niski" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/nam/translations/pt-BR.json b/homeassistant/components/nam/translations/pt-BR.json index 270e080701c..c8b86aed38e 100644 --- a/homeassistant/components/nam/translations/pt-BR.json +++ b/homeassistant/components/nam/translations/pt-BR.json @@ -37,5 +37,18 @@ "description": "Configure a integra\u00e7\u00e3o do Nettigo Air Monitor." } } + }, + "entity": { + "sensor": { + "caqi_level": { + "state": { + "high": "Alto", + "low": "Baixo", + "medium": "M\u00e9dio", + "very high": "Muito alto", + "very low": "Muito baixo" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/nam/translations/pt.json b/homeassistant/components/nam/translations/pt.json index 0aa6df94840..031ddd57a10 100644 --- a/homeassistant/components/nam/translations/pt.json +++ b/homeassistant/components/nam/translations/pt.json @@ -4,7 +4,7 @@ "reauth_successful": "Reautentica\u00e7\u00e3o bem sucedida" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, diff --git a/homeassistant/components/nam/translations/ru.json b/homeassistant/components/nam/translations/ru.json index d73f9aa0ee5..e4613f96452 100644 --- a/homeassistant/components/nam/translations/ru.json +++ b/homeassistant/components/nam/translations/ru.json @@ -37,5 +37,18 @@ "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 Home Assistant \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 Nettigo Air Monitor." } } + }, + "entity": { + "sensor": { + "caqi_level": { + "state": { + "high": "\u0412\u044b\u0441\u043e\u043a\u0438\u0439", + "low": "\u041d\u0438\u0437\u043a\u0438\u0439", + "medium": "\u0421\u0440\u0435\u0434\u043d\u0438\u0439", + "very high": "\u041e\u0447\u0435\u043d\u044c \u0432\u044b\u0441\u043e\u043a\u0438\u0439", + "very low": "\u041e\u0447\u0435\u043d\u044c \u043d\u0438\u0437\u043a\u0438\u0439" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/nam/translations/sk.json b/homeassistant/components/nam/translations/sk.json index 78966044e1e..72e23d7485b 100644 --- a/homeassistant/components/nam/translations/sk.json +++ b/homeassistant/components/nam/translations/sk.json @@ -8,10 +8,14 @@ }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "flow_title": "{host}", "step": { + "confirm_discovery": { + "description": "Chcete nastavi\u0165 Nettigo Air Monitor na {host}?" + }, "credentials": { "data": { "password": "Heslo", @@ -29,6 +33,20 @@ "user": { "data": { "host": "Hostite\u013e" + }, + "description": "Nastavte integr\u00e1ciu Nettigo Air Monitor." + } + } + }, + "entity": { + "sensor": { + "caqi_level": { + "state": { + "high": "Vysok\u00fd", + "low": "N\u00edzky", + "medium": "Stredn\u00fd", + "very high": "Ve\u013emi vysok\u00fd", + "very low": "Ve\u013emi n\u00edzky" } } } diff --git a/homeassistant/components/nam/translations/zh-Hant.json b/homeassistant/components/nam/translations/zh-Hant.json index f3c343f515c..d2f79fb92d7 100644 --- a/homeassistant/components/nam/translations/zh-Hant.json +++ b/homeassistant/components/nam/translations/zh-Hant.json @@ -37,5 +37,18 @@ "description": "\u8a2d\u5b9a Nettigo Air Monitor \u6574\u5408\u3002" } } + }, + "entity": { + "sensor": { + "caqi_level": { + "state": { + "high": "\u9ad8", + "low": "\u4f4e", + "medium": "\u4e2d", + "very high": "\u6975\u9ad8", + "very low": "\u6975\u4f4e" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/nanoleaf/translations/ko.json b/homeassistant/components/nanoleaf/translations/ko.json index e540d903647..2ad65b462a7 100644 --- a/homeassistant/components/nanoleaf/translations/ko.json +++ b/homeassistant/components/nanoleaf/translations/ko.json @@ -4,11 +4,19 @@ "already_configured": "\ub514\ubc14\uc774\uc2a4\uac00 \uc774\ubbf8 \uc124\uc815\ub418\uc5b4 \uc788\uc74c", "cannot_connect": "\uc5f0\uacb0 \uc2e4\ud328", "invalid_token": "\uc798\ubabb\ub41c \uc811\uc18d \ud1a0\ud070", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4", "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc5d0\ub7ec" }, "error": { "cannot_connect": "\uc5f0\uacb0 \uc2e4\ud328", "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc5d0\ub7ec" + }, + "step": { + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/nanoleaf/translations/sk.json b/homeassistant/components/nanoleaf/translations/sk.json index 876c29b2d6d..1dc7b58e963 100644 --- a/homeassistant/components/nanoleaf/translations/sk.json +++ b/homeassistant/components/nanoleaf/translations/sk.json @@ -9,10 +9,15 @@ }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", + "not_allowing_new_tokens": "Nanoleaf nepovo\u013euje nov\u00e9 tokeny, postupujte pod\u013ea pokynov vy\u0161\u0161ie.", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "flow_title": "{name}", "step": { + "link": { + "description": "Stla\u010dte a podr\u017ete tla\u010didlo nap\u00e1jania na Nanoleaf na 5 sek\u00fand, k\u00fdm neza\u010dn\u00fa blika\u0165 LED di\u00f3dy tla\u010didiel, a potom do 30 sek\u00fand kliknite na **SUBMIT**.", + "title": "Link Nanoleaf" + }, "user": { "data": { "host": "Hostite\u013e" diff --git a/homeassistant/components/neato/translations/en_GB.json b/homeassistant/components/neato/translations/en-GB.json similarity index 100% rename from homeassistant/components/neato/translations/en_GB.json rename to homeassistant/components/neato/translations/en-GB.json diff --git a/homeassistant/components/neato/translations/en.json b/homeassistant/components/neato/translations/en.json index 7c435c3749b..fbc7d2a312f 100644 --- a/homeassistant/components/neato/translations/en.json +++ b/homeassistant/components/neato/translations/en.json @@ -15,7 +15,7 @@ "title": "Pick Authentication Method" }, "reauth_confirm": { - "title": "Do you want to start set up?" + "title": "Do you want to start setup?" } } } diff --git a/homeassistant/components/neato/translations/it.json b/homeassistant/components/neato/translations/it.json index cda062ceff0..07925805fa7 100644 --- a/homeassistant/components/neato/translations/it.json +++ b/homeassistant/components/neato/translations/it.json @@ -15,7 +15,7 @@ "title": "Scegli il metodo di autenticazione" }, "reauth_confirm": { - "title": "Vuoi iniziare la configurazione?" + "title": "Vuoi avviare la configurazione?" } } } diff --git a/homeassistant/components/neato/translations/pt.json b/homeassistant/components/neato/translations/pt.json index 030211ca1cf..bc2b7bb2c1f 100644 --- a/homeassistant/components/neato/translations/pt.json +++ b/homeassistant/components/neato/translations/pt.json @@ -2,9 +2,9 @@ "config": { "abort": { "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", - "authorize_url_timeout": "Tempo excedido a gerar um URL de autoriza\u00e7\u00e3o", + "authorize_url_timeout": "Excedido o tempo limite para gerar um URL de autoriza\u00e7\u00e3o", "missing_configuration": "O componente n\u00e3o est\u00e1 configurado. Por favor, siga a documenta\u00e7\u00e3o.", - "no_url_available": "Nenhum URL dispon\u00edvel. Para obter informa\u00e7\u00f5es sobre esse erro, [verifique a sec\u00e7\u00e3o de ajuda]({docs_url})", + "no_url_available": "Nenhum URL est\u00e1 dispon\u00edvel. Para obter informa\u00e7\u00f5es sobre esse erro, [consulte a sec\u00e7\u00e3o de ajuda]({docs_url})", "reauth_successful": "Reautentica\u00e7\u00e3o bem sucedida" }, "create_entry": { @@ -15,7 +15,7 @@ "title": "Escolha o m\u00e9todo de autentica\u00e7\u00e3o" }, "reauth_confirm": { - "title": "Quer dar inicio \u00e0 configura\u00e7\u00e3o?" + "title": "Quer dar in\u00edcio \u00e0 configura\u00e7\u00e3o?" } } } diff --git a/homeassistant/components/nest/__init__.py b/homeassistant/components/nest/__init__.py index 99760d4be39..c2e295dbfdf 100644 --- a/homeassistant/components/nest/__init__.py +++ b/homeassistant/components/nest/__init__.py @@ -270,7 +270,9 @@ async def async_import_config(hass: HomeAssistant, entry: ConfigEntry) -> None: severity=IssueSeverity.ERROR, translation_key="removed_app_auth", translation_placeholders={ - "more_info_url": "https://www.home-assistant.io/more-info/nest-auth-deprecation", + "more_info_url": ( + "https://www.home-assistant.io/more-info/nest-auth-deprecation" + ), "documentation_url": "https://www.home-assistant.io/integrations/nest/", }, ) @@ -333,7 +335,10 @@ async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None: await subscriber.delete_subscription() except (AuthException, SubscriberException) as err: _LOGGER.warning( - "Unable to delete subscription '%s'; Will be automatically cleaned up by cloud console: %s", + ( + "Unable to delete subscription '%s'; Will be automatically cleaned up" + " by cloud console: %s" + ), subscriber.subscriber_id, err, ) diff --git a/homeassistant/components/nest/application_credentials.py b/homeassistant/components/nest/application_credentials.py index 7d88bc37322..d9398523a57 100644 --- a/homeassistant/components/nest/application_credentials.py +++ b/homeassistant/components/nest/application_credentials.py @@ -17,7 +17,9 @@ async def async_get_authorization_server(hass: HomeAssistant) -> AuthorizationSe async def async_get_description_placeholders(hass: HomeAssistant) -> dict[str, str]: """Return description placeholders for the credentials dialog.""" return { - "oauth_consent_url": "https://console.cloud.google.com/apis/credentials/consent", + "oauth_consent_url": ( + "https://console.cloud.google.com/apis/credentials/consent" + ), "more_info_url": "https://www.home-assistant.io/integrations/nest/", "oauth_creds_url": "https://console.cloud.google.com/apis/credentials", "redirect_url": "https://my.home-assistant.io/redirect/oauth", diff --git a/homeassistant/components/nest/climate_sdm.py b/homeassistant/components/nest/climate_sdm.py index 4a453c5fd38..7f7d22ae604 100644 --- a/homeassistant/components/nest/climate_sdm.py +++ b/homeassistant/components/nest/climate_sdm.py @@ -29,7 +29,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.entity import DeviceInfo @@ -131,7 +131,7 @@ class ThermostatEntity(ClimateEntity): @property def temperature_unit(self) -> str: """Return the unit of temperature measurement for the system.""" - return TEMP_CELSIUS + return UnitOfTemperature.CELSIUS @property def current_temperature(self) -> float | None: diff --git a/homeassistant/components/nest/config_flow.py b/homeassistant/components/nest/config_flow.py index 1288592be74..a837290249e 100644 --- a/homeassistant/components/nest/config_flow.py +++ b/homeassistant/components/nest/config_flow.py @@ -333,7 +333,8 @@ class NestFlowHandler( project_id = user_input[CONF_PROJECT_ID] if project_id == self._data[CONF_CLOUD_PROJECT_ID]: _LOGGER.error( - "Device Access Project ID and Cloud Project ID must not be the same, see documentation" + "Device Access Project ID and Cloud Project ID must not be the" + " same, see documentation" ) errors[CONF_PROJECT_ID] = "wrong_project_id" else: diff --git a/homeassistant/components/nest/legacy/__init__.py b/homeassistant/components/nest/legacy/__init__.py index b244dea182e..ee660b8062f 100644 --- a/homeassistant/components/nest/legacy/__init__.py +++ b/homeassistant/components/nest/legacy/__init__.py @@ -192,8 +192,10 @@ async def async_setup_legacy_entry(hass: HomeAssistant, entry: ConfigEntry) -> b eta_window = service.data.get(ATTR_ETA_WINDOW, timedelta(minutes=1)) eta_end = eta_begin + eta_window _LOGGER.info( - "Setting ETA for trip: %s, " - "ETA window starts at: %s and ends at: %s", + ( + "Setting ETA for trip: %s, " + "ETA window starts at: %s and ends at: %s" + ), trip_id, eta_begin, eta_end, @@ -221,8 +223,7 @@ async def async_setup_legacy_entry(hass: HomeAssistant, entry: ConfigEntry) -> b structure.cancel_eta(trip_id) else: _LOGGER.info( - "No thermostats found in structure: %s, " - "unable to cancel ETA", + "No thermostats found in structure: %s, unable to cancel ETA", structure.name, ) @@ -333,9 +334,11 @@ class NestLegacyDevice: device.name_long except KeyError: _LOGGER.warning( - "Cannot retrieve device name for [%s]" - ", please check your Nest developer " - "account permission settings", + ( + "Cannot retrieve device name for [%s]" + ", please check your Nest developer " + "account permission settings" + ), device.serial, ) continue diff --git a/homeassistant/components/nest/legacy/climate.py b/homeassistant/components/nest/legacy/climate.py index de6bd0e3b26..5f066e3bc0e 100644 --- a/homeassistant/components/nest/legacy/climate.py +++ b/homeassistant/components/nest/legacy/climate.py @@ -20,12 +20,7 @@ from homeassistant.components.climate import ( HVACAction, HVACMode, ) -from homeassistant.const import ( - ATTR_TEMPERATURE, - CONF_SCAN_INTERVAL, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, -) +from homeassistant.const import ATTR_TEMPERATURE, CONF_SCAN_INTERVAL, UnitOfTemperature from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import DeviceInfo @@ -341,6 +336,6 @@ class NestThermostat(ClimateEntity): self._max_temperature = self.device.max_temperature self._is_locked = self.device.is_locked if self.device.temperature_scale == "C": - self._temperature_scale = TEMP_CELSIUS + self._temperature_scale = UnitOfTemperature.CELSIUS else: - self._temperature_scale = TEMP_FAHRENHEIT + self._temperature_scale = UnitOfTemperature.FAHRENHEIT diff --git a/homeassistant/components/nest/legacy/sensor.py b/homeassistant/components/nest/legacy/sensor.py index bb09a40b15e..86e73114568 100644 --- a/homeassistant/components/nest/legacy/sensor.py +++ b/homeassistant/components/nest/legacy/sensor.py @@ -9,8 +9,7 @@ from homeassistant.const import ( CONF_SENSORS, PERCENTAGE, STATE_OFF, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, + UnitOfTemperature, ) from . import NestSensorDevice @@ -206,9 +205,9 @@ class NestTempSensor(NestSensorDevice, SensorEntity): def update(self): """Retrieve latest state.""" if self.device.temperature_scale == "C": - self._unit = TEMP_CELSIUS + self._unit = UnitOfTemperature.CELSIUS else: - self._unit = TEMP_FAHRENHEIT + self._unit = UnitOfTemperature.FAHRENHEIT if (temp := getattr(self.device, self.variable)) is None: self._state = None diff --git a/homeassistant/components/nest/sensor_sdm.py b/homeassistant/components/nest/sensor_sdm.py index b36e9103196..187ac0ee8c2 100644 --- a/homeassistant/components/nest/sensor_sdm.py +++ b/homeassistant/components/nest/sensor_sdm.py @@ -13,7 +13,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import PERCENTAGE, TEMP_CELSIUS +from homeassistant.const import PERCENTAGE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -78,7 +78,7 @@ class TemperatureSensor(SensorBase): """Representation of a Temperature Sensor.""" _attr_device_class = SensorDeviceClass.TEMPERATURE - _attr_native_unit_of_measurement = TEMP_CELSIUS + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS _attr_name = "Temperature" @property diff --git a/homeassistant/components/nest/translations/de.json b/homeassistant/components/nest/translations/de.json index 7a9f068443c..6ddba661d49 100644 --- a/homeassistant/components/nest/translations/de.json +++ b/homeassistant/components/nest/translations/de.json @@ -37,7 +37,7 @@ "title": "Nest: Cloud-Projekt-ID eingeben" }, "create_cloud_project": { - "description": "Die Nest-Integration erm\u00f6glicht es dir, deine Nest-Thermostate, -Kameras und -T\u00fcrklingeln \u00fcber die Smart Device Management API zu integrieren. Die SDM API **erfordert eine einmalige Einrichtungsgeb\u00fchr von US $5**. Siehe Dokumentation f\u00fcr [weitere Informationen]({more_info_url}).\n\n1. Rufe die [Google Cloud Console]({cloud_console_url}) auf.\n1. Wenn dies dein erstes Projekt ist, dr\u00fccke auf **Projekt erstellen** und dann auf **Neues Projekt**.\n1. Gib deinem Cloud-Projekt einen Namen und dr\u00fccke dann auf **Erstellen**.\n1. Speichere die Cloud Project ID, z. B. *example-project-12345*, da du diese sp\u00e4ter ben\u00f6tigst.\n1. Gehe zur API-Bibliothek f\u00fcr [Smart Device Management API]({sdm_api_url}) und dr\u00fccke auf **Aktivieren**.\n1. Wechsele zur API-Bibliothek f\u00fcr [Cloud Pub/Sub API]({pubsub_api_url}) und dr\u00fccke auf **Aktivieren**.\n\nFahre fort, wenn dein Cloud-Projekt eingerichtet ist.", + "description": "Die Nest-Integration erm\u00f6glicht es dir, deine Nest Thermostate, Kameras und T\u00fcrklingeln \u00fcber die Smart Device Management API zu integrieren. Die SDM API **erfordert eine einmalige Einrichtungsgeb\u00fchr von US $5**. Siehe Dokumentation f\u00fcr [weitere Informationen]({more_info_url}).\n\n1. Rufe die [Google Cloud Console]({cloud_console_url}) auf.\n1. Wenn dies dein erstes Projekt ist, dr\u00fccke auf **Projekt erstellen** und dann auf **Neues Projekt**.\n1. Gib deinem Cloud-Projekt einen Namen und dr\u00fccke dann auf **Erstellen**.\n1. Speichere die Cloud Projekt ID, z. B. *example-project-12345*, da du diese sp\u00e4ter ben\u00f6tigst.\n1. Gehe zur API-Bibliothek f\u00fcr [Smart Device Management API]({sdm_api_url}) und dr\u00fccke auf **Aktivieren**.\n1. Wechsele zur API-Bibliothek f\u00fcr [Cloud Pub/Sub API]({pubsub_api_url}) und dr\u00fccke auf **Aktivieren**.\n\nFahre fort, wenn dein Cloud-Projekt eingerichtet ist.", "title": "Nest: Cloud-Projekt erstellen und konfigurieren" }, "device_project": { @@ -62,7 +62,7 @@ "data": { "code": "PIN-Code" }, - "description": "[Autorisiere dein Konto] ( {url} ), um deinen Nest-Account zu verkn\u00fcpfen.\n\nF\u00fcge anschlie\u00dfend den erhaltenen PIN Code hier ein.", + "description": "[Autorisiere dein Konto] ({url}), um deinen Nest-Account zu verkn\u00fcpfen.\n\nF\u00fcge anschlie\u00dfend den erhaltenen PIN-Code hier ein.", "title": "Nest-Konto verkn\u00fcpfen" }, "pick_implementation": { diff --git a/homeassistant/components/nest/translations/en_GB.json b/homeassistant/components/nest/translations/en-GB.json similarity index 100% rename from homeassistant/components/nest/translations/en_GB.json rename to homeassistant/components/nest/translations/en-GB.json diff --git a/homeassistant/components/nest/translations/ko.json b/homeassistant/components/nest/translations/ko.json index 5e38c522b49..43d89925c41 100644 --- a/homeassistant/components/nest/translations/ko.json +++ b/homeassistant/components/nest/translations/ko.json @@ -1,7 +1,9 @@ { "config": { "abort": { + "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "authorize_url_timeout": "\uc778\uc99d URL \uc0dd\uc131 \uc2dc\uac04\uc774 \ucd08\uacfc\ub418\uc5c8\uc2b5\ub2c8\ub2e4.", + "invalid_access_token": "\uc561\uc138\uc2a4 \ud1a0\ud070\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "missing_configuration": "\uad6c\uc131\uc694\uc18c\uac00 \uad6c\uc131\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4. \uc124\uba85\uc11c\ub97c \ucc38\uace0\ud574\uc8fc\uc138\uc694.", "no_url_available": "\uc0ac\uc6a9 \uac00\ub2a5\ud55c URL\uc774 \uc5c6\uc2b5\ub2c8\ub2e4. \uc774 \uc624\ub958\uc5d0 \ub300\ud55c \uc790\uc138\ud55c \ub0b4\uc6a9\uc740 [\ub3c4\uc6c0\ub9d0 \uc139\uc158]({docs_url}) \uc744(\ub97c) \ucc38\uc870\ud574\uc8fc\uc138\uc694.", "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4", diff --git a/homeassistant/components/nest/translations/lb.json b/homeassistant/components/nest/translations/lb.json index dcf3c42ace5..b0d36bf7658 100644 --- a/homeassistant/components/nest/translations/lb.json +++ b/homeassistant/components/nest/translations/lb.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "authorize_url_timeout": "Z\u00e4it Iwwerschreidung beim gener\u00e9ieren vun der Autorisatiouns URL.", + "authorize_url_timeout": "Z\u00e4itiwwerschreidung beim gener\u00e9iere vun der Autorisatiouns-URL.", "missing_configuration": "Komponent net konfigur\u00e9iert. Folleg w.e.g der Dokumentatioun.", "reauth_successful": "Re-authentifikatioun war erfollegr\u00e4ich", "unknown_authorize_url_generation": "Onbekannte Feeler beim erstellen vun der Authorisatiouns URL." @@ -10,9 +10,9 @@ "default": "Erfollegr\u00e4ich authentifiz\u00e9iert" }, "error": { - "internal_error": "Interne Feeler beim valid\u00e9ieren vum Code", - "invalid_pin": "Ong\u00eblte PIN Code", - "timeout": "Z\u00e4it Iwwerschreidung beim valid\u00e9ieren vum Code", + "internal_error": "Et ass een interne Feeler geschitt", + "invalid_pin": "Ong\u00eblte PIN-Code", + "timeout": "Et ass een Z\u00e4itiwwerschreidungsfeeler pass\u00e9iert", "unknown": "Onerwaarte Feeler" }, "step": { @@ -20,15 +20,15 @@ "data": { "flow_impl": "Ubidder" }, - "description": "Wiel Authentifikatioun's Method aus", - "title": "Authentifikatioun Ubidder" + "description": "Wiel d'Authentifikatiounsmethod aus", + "title": "Authentifikatiounsubidder" }, "link": { "data": { - "code": "PIN code" + "code": "PIN-Code" }, "description": "Fir den Nest Kont ze verbannen, [autoris\u00e9iert \u00e4ren Kont]({url}).\nKop\u00e9iert no der Autorisatioun den Pin hei \u00ebnnendr\u00ebnner", - "title": "Nest Kont verbannen" + "title": "Nest-Kont verbannen" }, "pick_implementation": { "title": "Authentifikatioun's Method auswielen" diff --git a/homeassistant/components/nest/translations/pt.json b/homeassistant/components/nest/translations/pt.json index e1022b86b01..fa3cdc9b760 100644 --- a/homeassistant/components/nest/translations/pt.json +++ b/homeassistant/components/nest/translations/pt.json @@ -2,10 +2,10 @@ "config": { "abort": { "already_configured": "Conta j\u00e1 configurada", - "authorize_url_timeout": "Tempo excedido a gerar um URL de autoriza\u00e7\u00e3o", + "authorize_url_timeout": "Excedido o tempo limite para gerar um URL de autoriza\u00e7\u00e3o", "invalid_access_token": "Token de acesso inv\u00e1lido", "missing_configuration": "O componente n\u00e3o est\u00e1 configurado. Por favor, siga a documenta\u00e7\u00e3o.", - "no_url_available": "Nenhum URL dispon\u00edvel. Para obter informa\u00e7\u00f5es sobre esse erro, [verifique a sec\u00e7\u00e3o de ajuda]({docs_url})", + "no_url_available": "Nenhum URL est\u00e1 dispon\u00edvel. Para obter informa\u00e7\u00f5es sobre esse erro, [consulte a sec\u00e7\u00e3o de ajuda]({docs_url})", "unknown_authorize_url_generation": "Erro desconhecido ao gerar um URL de autoriza\u00e7\u00e3o." }, "create_entry": { diff --git a/homeassistant/components/nest/translations/sk.json b/homeassistant/components/nest/translations/sk.json index c8926d87222..18089ecbde1 100644 --- a/homeassistant/components/nest/translations/sk.json +++ b/homeassistant/components/nest/translations/sk.json @@ -1,4 +1,7 @@ { + "application_credentials": { + "description": "Pod\u013ea [pokynov]({more_info_url}) nakonfigurujte Cloud Console: \n\n1. Prejdite na [obrazovka s\u00fahlasu OAuth]({oauth_consent_url}) a nakonfigurujte\n1. Prejdite na [Poverenia]({oauth_creds_url}) a kliknite na **Vytvori\u0165 poverenia**.\n1. Z rozba\u013eovacieho zoznamu vyberte **ID klienta OAuth**.\n1. Ako typ aplik\u00e1cie vyberte **Web Application**.\n1. Pridajte `{redirect_url}` pod *URI autorizovan\u00e9ho presmerovania*." + }, "config": { "abort": { "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", @@ -13,27 +16,54 @@ "default": "\u00daspe\u0161ne overen\u00e9" }, "error": { + "bad_project_id": "Zadajte platn\u00e9 ID cloudov\u00e9ho projektu (skontrolujte Cloud Console)", "internal_error": "Intern\u00e1 chyba pri overovan\u00ed k\u00f3du", "invalid_pin": "Nespr\u00e1vny PIN", "subscriber_error": "Nezn\u00e1ma chyba odberate\u013ea, pozrite si denn\u00edky", "timeout": "Overovac\u00ed k\u00f3d \u010dasov\u00e9ho limitu", - "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba", + "wrong_project_id": "Zadajte platn\u00e9 ID cloudov\u00e9ho projektu (bolo rovnak\u00e9 ako ID projektu pr\u00edstupu k zariadeniu)" }, "step": { + "auth_upgrade": { + "description": "Google ukon\u010dil podporu App Auth s cie\u013eom zlep\u0161i\u0165 zabezpe\u010denie a mus\u00edte podnikn\u00fa\u0165 kroky vytvoren\u00edm nov\u00fdch poveren\u00ed aplik\u00e1cie. \n\n Otvorte [dokument\u00e1ciu]({more_info_url}) a pokra\u010dujte, preto\u017ee \u010fal\u0161ie kroky v\u00e1s preved\u00fa krokmi, ktor\u00e9 mus\u00edte vykona\u0165 na obnovenie pr\u00edstupu k svojim zariadeniam Nest.", + "title": "Nest: Ukon\u010denie podpory overovania aplik\u00e1cie" + }, "cloud_project": { "data": { "cloud_project_id": "ID projektu Google Cloud" }, + "description": "Ni\u017e\u0161ie zadajte ID cloudov\u00e9ho projektu, napr. *example-project-12345*. Pozrite si [Google Cloud Console]({cloud_console_url}) alebo dokument\u00e1ciu pre [\u010fal\u0161ie inform\u00e1cie]({more_info_url}).", "title": "Hniezdo: Zadajte ID projektu Cloud" }, + "create_cloud_project": { + "description": "Integr\u00e1cia Nest v\u00e1m umo\u017e\u0148uje integrova\u0165 termostaty Nest, kamery a zvon\u010deky pomocou rozhrania Smart Device Management API. SDM API **vy\u017eaduje jednorazov\u00fd poplatok za nastavenie vo v\u00fd\u0161ke 5 USD**. Pozrite si dokument\u00e1ciu pre [viac inform\u00e1ci\u00ed] ({more_info_url}). \n\n 1. Prejdite do [Google Cloud Console] ({cloud_console_url}).\n 1. Ak je toto v\u00e1\u0161 prv\u00fd projekt, kliknite na **Vytvori\u0165 projekt** a potom na **Nov\u00fd projekt**.\n 1. Pomenujte svoj cloudov\u00fd projekt a potom kliknite na **Vytvori\u0165**.\n 1. Ulo\u017ete ID cloudov\u00e9ho projektu, napr. *example-project-12345*, ako ho budete nesk\u00f4r potrebova\u0165\n 1. Prejdite do kni\u017enice API pre [Smart Device Management API]({sdm_api_url}) a kliknite na **Povoli\u0165**.\n 1. Prejdite do kni\u017enice API pre [Cloud Pub/Sub API]({pubsub_api_url}) a kliknite na **Povoli\u0165**. \n\n Pokra\u010dujte, ke\u010f je v\u00e1\u0161 cloudov\u00fd projekt nastaven\u00fd.", + "title": "Nest: Vytvorte a nakonfigurujte cloudov\u00fd projekt" + }, + "device_project": { + "data": { + "project_id": "ID projektu pr\u00edstupu k zariadeniu" + }, + "description": "Vytvorte projekt Nest Device Access, ktor\u00e9ho nastavenie **vy\u017eaduje zaplatenie poplatku 5 USD** Googlu.\n 1. Prejdite do [Konzola pr\u00edstupu k zariadeniu] ({device_access_console_url}) a prejdite platobn\u00fdm tokom.\n 1. Kliknite na **Vytvori\u0165 projekt**\n 1. Pomenujte svoj projekt Device Access a kliknite na **Next**.\n 1. Zadajte svoje ID klienta OAuth\n 1. Povo\u013ete udalosti kliknut\u00edm na **Povoli\u0165** a **Vytvori\u0165 projekt**. \n\n Ni\u017e\u0161ie zadajte svoje ID projektu pr\u00edstupu k zariadeniu ([viac inform\u00e1ci\u00ed]({more_info_url})).\n", + "title": "Nest: Vytvorte projekt pr\u00edstupu k zariadeniu" + }, + "device_project_upgrade": { + "description": "Aktualizujte projekt Nest Device Access Project pomocou svojho nov\u00e9ho ID klienta OAuth ([viac inform\u00e1ci\u00ed]({more_info_url}))\n 1. Prejdite na [Device Access Console] ({device_access_console_url}).\n 1. Kliknite na ikonu ko\u0161a ved\u013ea polo\u017eky *ID klienta OAuth*.\n 1. Kliknite na rozba\u013eovaciu ponuku `...` a *Prida\u0165 ID klienta*.\n 1. Zadajte svoje nov\u00e9 ID klienta OAuth a kliknite na **Prida\u0165**. \n\nVa\u0161e ID klienta OAuth je: `{client_id}`", + "title": "Nest: Aktualizujte projekt pr\u00edstupu k zariadeniu" + }, "init": { - "description": "Vyberte met\u00f3du overenia" + "data": { + "flow_impl": "Poskytovate\u013e" + }, + "description": "Vyberte met\u00f3du overenia", + "title": "Poskytovate\u013e overovania" }, "link": { "data": { "code": "PIN k\u00f3d" }, - "description": "Ak chcete prepoji\u0165 svoje konto Nest, [autorizujte svoje konto]({url}).\n\nPo autoriz\u00e1cii skop\u00edrujte a vlo\u017ete poskytnut\u00fd k\u00f3d PIN uveden\u00fd ni\u017e\u0161ie." + "description": "Ak chcete prepoji\u0165 svoje konto Nest, [autorizujte svoje konto]({url}).\n\nPo autoriz\u00e1cii skop\u00edrujte a vlo\u017ete poskytnut\u00fd k\u00f3d PIN uveden\u00fd ni\u017e\u0161ie.", + "title": "Prepoji\u0165 \u00fa\u010det Nest" }, "pick_implementation": { "title": "Vyberte met\u00f3du overenia" @@ -46,6 +76,7 @@ "title": "Nakonfigurujte Google Cloud" }, "reauth_confirm": { + "description": "Integr\u00e1cia Nestu potrebuje op\u00e4tovn\u00e9 overenie v\u00e1\u0161ho konta", "title": "Znova overi\u0165 integr\u00e1ciu" } } @@ -60,7 +91,12 @@ }, "issues": { "deprecated_yaml": { + "description": "Konfigur\u00e1cia Nest v configuration.yaml sa odstra\u0148uje z Home Assistant 2022.10. \n\n Va\u0161e existuj\u00face poverenia aplik\u00e1cie OAuth a nastavenia pr\u00edstupu sa importovali do pou\u017e\u00edvate\u013esk\u00e9ho rozhrania automaticky. Ak chcete tento probl\u00e9m vyrie\u0161i\u0165, odstr\u00e1\u0148te konfigur\u00e1ciu YAML zo s\u00faboru configuration.yaml a re\u0161tartujte aplik\u00e1ciu Home Assistant.", "title": "Konfigur\u00e1cia Nest YAML sa odstra\u0148uje" + }, + "removed_app_auth": { + "description": "Na zlep\u0161enie zabezpe\u010denia a zn\u00ed\u017eenie rizika phishingu spolo\u010dnos\u0165 Google zak\u00e1zala met\u00f3du overovania pou\u017e\u00edvan\u00fa dom\u00e1cim asistentom. \n\n**Vyrie\u0161enie si vy\u017eaduje va\u0161u akciu** ([viac inform\u00e1ci\u00ed]({more_info_url})) \n\n1. Nav\u0161t\u00edvte str\u00e1nku integr\u00e1cie\n1. Kliknite na Prekonfigurova\u0165 na integr\u00e1cii Nest.\n1. Home Assistant v\u00e1s prevedie krokmi aktualiz\u00e1cie na Web Authentication. \n\nInform\u00e1cie o rie\u0161en\u00ed probl\u00e9mov n\u00e1jdete v [pokynoch na integr\u00e1ciu]({documentation_url}).", + "title": "Overovacie poverenia Nest sa musia aktualizova\u0165" } } } \ No newline at end of file diff --git a/homeassistant/components/netatmo/climate.py b/homeassistant/components/netatmo/climate.py index 15b3e35ce05..94844578d9d 100644 --- a/homeassistant/components/netatmo/climate.py +++ b/homeassistant/components/netatmo/climate.py @@ -23,7 +23,7 @@ from homeassistant.const import ( ATTR_TEMPERATURE, PRECISION_HALVES, STATE_OFF, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import config_validation as cv, entity_platform @@ -134,7 +134,7 @@ class NetatmoThermostat(NetatmoBase, ClimateEntity): _attr_preset_modes = SUPPORT_PRESET _attr_supported_features = SUPPORT_FLAGS _attr_target_temperature_step = PRECISION_HALVES - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS def __init__(self, netatmo_device: NetatmoRoom) -> None: """Initialize the sensor.""" diff --git a/homeassistant/components/netatmo/config_flow.py b/homeassistant/components/netatmo/config_flow.py index acd8965d013..b4e6d838537 100644 --- a/homeassistant/components/netatmo/config_flow.py +++ b/homeassistant/components/netatmo/config_flow.py @@ -10,7 +10,7 @@ from pyatmo.const import ALL_SCOPES import voluptuous as vol from homeassistant import config_entries -from homeassistant.const import CONF_SHOW_ON_MAP +from homeassistant.const import CONF_SHOW_ON_MAP, CONF_UUID from homeassistant.core import callback from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers import config_entry_oauth2_flow, config_validation as cv @@ -23,7 +23,6 @@ from .const import ( CONF_LON_SW, CONF_NEW_AREA, CONF_PUBLIC_MODE, - CONF_UUID, CONF_WEATHER_AREAS, DOMAIN, ) diff --git a/homeassistant/components/netatmo/const.py b/homeassistant/components/netatmo/const.py index 21f821040dd..50578eb8223 100644 --- a/homeassistant/components/netatmo/const.py +++ b/homeassistant/components/netatmo/const.py @@ -50,7 +50,6 @@ CONF_LON_NE = "lon_ne" CONF_LON_SW = "lon_sw" CONF_NEW_AREA = "new_area" CONF_PUBLIC_MODE = "mode" -CONF_UUID = "uuid" CONF_WEATHER_AREAS = "weather_areas" OAUTH2_AUTHORIZE = "https://api.netatmo.com/oauth2/authorize" diff --git a/homeassistant/components/netatmo/cover.py b/homeassistant/components/netatmo/cover.py index 6d755d828d3..41bf84c8334 100644 --- a/homeassistant/components/netatmo/cover.py +++ b/homeassistant/components/netatmo/cover.py @@ -99,7 +99,7 @@ class NetatmoCover(NetatmoBase, CoverEntity): await self._cover.async_set_target_position(kwargs[ATTR_POSITION]) @property - def device_class(self) -> str: + def device_class(self) -> CoverDeviceClass: """Return the device class.""" return CoverDeviceClass.SHUTTER diff --git a/homeassistant/components/netatmo/manifest.json b/homeassistant/components/netatmo/manifest.json index ac478282614..e9f3a99a8a5 100644 --- a/homeassistant/components/netatmo/manifest.json +++ b/homeassistant/components/netatmo/manifest.json @@ -3,7 +3,7 @@ "name": "Netatmo", "integration_type": "hub", "documentation": "https://www.home-assistant.io/integrations/netatmo", - "requirements": ["pyatmo==7.4.0"], + "requirements": ["pyatmo==7.5.0"], "after_dependencies": ["cloud", "media_source"], "dependencies": ["application_credentials", "webhook"], "codeowners": ["@cgtobi"], diff --git a/homeassistant/components/netatmo/sensor.py b/homeassistant/components/netatmo/sensor.py index 1fed7cf5e0e..765f7ab309c 100644 --- a/homeassistant/components/netatmo/sensor.py +++ b/homeassistant/components/netatmo/sensor.py @@ -19,13 +19,13 @@ from homeassistant.const import ( ATTR_LONGITUDE, CONCENTRATION_PARTS_PER_MILLION, DEGREE, - LENGTH_MILLIMETERS, PERCENTAGE, - POWER_WATT, - PRESSURE_MBAR, - SOUND_PRESSURE_DB, - SPEED_KILOMETERS_PER_HOUR, - TEMP_CELSIUS, + UnitOfPower, + UnitOfPrecipitationDepth, + UnitOfPressure, + UnitOfSoundPressure, + UnitOfSpeed, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import device_registry as dr @@ -88,7 +88,7 @@ SENSOR_TYPES: tuple[NetatmoSensorEntityDescription, ...] = ( name="Temperature", netatmo_name="temperature", entity_registry_enabled_default=True, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, device_class=SensorDeviceClass.TEMPERATURE, ), @@ -113,7 +113,7 @@ SENSOR_TYPES: tuple[NetatmoSensorEntityDescription, ...] = ( name="Pressure", netatmo_name="pressure", entity_registry_enabled_default=True, - native_unit_of_measurement=PRESSURE_MBAR, + native_unit_of_measurement=UnitOfPressure.MBAR, state_class=SensorStateClass.MEASUREMENT, device_class=SensorDeviceClass.PRESSURE, ), @@ -129,8 +129,8 @@ SENSOR_TYPES: tuple[NetatmoSensorEntityDescription, ...] = ( name="Noise", netatmo_name="noise", entity_registry_enabled_default=True, - native_unit_of_measurement=SOUND_PRESSURE_DB, - icon="mdi:volume-high", + native_unit_of_measurement=UnitOfSoundPressure.DECIBEL, + device_class=SensorDeviceClass.SOUND_PRESSURE, state_class=SensorStateClass.MEASUREMENT, ), NetatmoSensorEntityDescription( @@ -147,27 +147,27 @@ SENSOR_TYPES: tuple[NetatmoSensorEntityDescription, ...] = ( name="Rain", netatmo_name="rain", entity_registry_enabled_default=True, - native_unit_of_measurement=LENGTH_MILLIMETERS, + native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + device_class=SensorDeviceClass.PRECIPITATION, state_class=SensorStateClass.MEASUREMENT, - icon="mdi:weather-rainy", ), NetatmoSensorEntityDescription( key="sum_rain_1", name="Rain last hour", netatmo_name="sum_rain_1", entity_registry_enabled_default=False, - native_unit_of_measurement=LENGTH_MILLIMETERS, + native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + device_class=SensorDeviceClass.PRECIPITATION, state_class=SensorStateClass.TOTAL, - icon="mdi:weather-rainy", ), NetatmoSensorEntityDescription( key="sum_rain_24", name="Rain today", netatmo_name="sum_rain_24", entity_registry_enabled_default=True, - native_unit_of_measurement=LENGTH_MILLIMETERS, + native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + device_class=SensorDeviceClass.PRECIPITATION, state_class=SensorStateClass.TOTAL_INCREASING, - icon="mdi:weather-rainy", ), NetatmoSensorEntityDescription( key="battery_percent", @@ -200,9 +200,8 @@ SENSOR_TYPES: tuple[NetatmoSensorEntityDescription, ...] = ( name="Wind Strength", netatmo_name="wind_strength", entity_registry_enabled_default=True, - native_unit_of_measurement=SPEED_KILOMETERS_PER_HOUR, - device_class=SensorDeviceClass.SPEED, - icon="mdi:weather-windy", + native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, + device_class=SensorDeviceClass.WIND_SPEED, state_class=SensorStateClass.MEASUREMENT, ), NetatmoSensorEntityDescription( @@ -226,9 +225,8 @@ SENSOR_TYPES: tuple[NetatmoSensorEntityDescription, ...] = ( name="Gust Strength", netatmo_name="gust_strength", entity_registry_enabled_default=False, - native_unit_of_measurement=SPEED_KILOMETERS_PER_HOUR, - device_class=SensorDeviceClass.SPEED, - icon="mdi:weather-windy", + native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, + device_class=SensorDeviceClass.WIND_SPEED, state_class=SensorStateClass.MEASUREMENT, ), NetatmoSensorEntityDescription( @@ -267,7 +265,7 @@ SENSOR_TYPES: tuple[NetatmoSensorEntityDescription, ...] = ( name="Power", netatmo_name="power", entity_registry_enabled_default=True, - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, state_class=SensorStateClass.TOTAL, device_class=SensorDeviceClass.POWER, ), diff --git a/homeassistant/components/netatmo/translations/en_GB.json b/homeassistant/components/netatmo/translations/en-GB.json similarity index 100% rename from homeassistant/components/netatmo/translations/en_GB.json rename to homeassistant/components/netatmo/translations/en-GB.json diff --git a/homeassistant/components/netatmo/translations/ko.json b/homeassistant/components/netatmo/translations/ko.json index fee370ce219..553e7440bd9 100644 --- a/homeassistant/components/netatmo/translations/ko.json +++ b/homeassistant/components/netatmo/translations/ko.json @@ -4,6 +4,7 @@ "authorize_url_timeout": "\uc778\uc99d URL \uc0dd\uc131 \uc2dc\uac04\uc774 \ucd08\uacfc\ub418\uc5c8\uc2b5\ub2c8\ub2e4.", "missing_configuration": "\uad6c\uc131\uc694\uc18c\uac00 \uad6c\uc131\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4. \uc124\uba85\uc11c\ub97c \ucc38\uace0\ud574\uc8fc\uc138\uc694.", "no_url_available": "\uc0ac\uc6a9 \uac00\ub2a5\ud55c URL\uc774 \uc5c6\uc2b5\ub2c8\ub2e4. \uc774 \uc624\ub958\uc5d0 \ub300\ud55c \uc790\uc138\ud55c \ub0b4\uc6a9\uc740 [\ub3c4\uc6c0\ub9d0 \uc139\uc158]({docs_url}) \uc744(\ub97c) \ucc38\uc870\ud574\uc8fc\uc138\uc694.", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4", "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." }, "create_entry": { @@ -12,6 +13,9 @@ "step": { "pick_implementation": { "title": "\uc778\uc99d \ubc29\ubc95 \uc120\ud0dd\ud558\uae30" + }, + "reauth_confirm": { + "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" } } }, diff --git a/homeassistant/components/netatmo/translations/pt.json b/homeassistant/components/netatmo/translations/pt.json index 191337f9812..1e55f95a028 100644 --- a/homeassistant/components/netatmo/translations/pt.json +++ b/homeassistant/components/netatmo/translations/pt.json @@ -1,9 +1,9 @@ { "config": { "abort": { - "authorize_url_timeout": "Tempo excedido a gerar um URL de autoriza\u00e7\u00e3o", + "authorize_url_timeout": "Excedido o tempo limite para gerar um URL de autoriza\u00e7\u00e3o", "missing_configuration": "O componente n\u00e3o est\u00e1 configurado. Por favor, siga a documenta\u00e7\u00e3o.", - "no_url_available": "Nenhum URL dispon\u00edvel. Para obter informa\u00e7\u00f5es sobre esse erro, [verifique a sec\u00e7\u00e3o de ajuda]({docs_url})", + "no_url_available": "Nenhum URL est\u00e1 dispon\u00edvel. Para obter informa\u00e7\u00f5es sobre esse erro, [consulte a sec\u00e7\u00e3o de ajuda]({docs_url})", "reauth_successful": "Reautentica\u00e7\u00e3o bem sucedida", "single_instance_allowed": "J\u00e1 configurado. Apenas uma \u00fanica configura\u00e7\u00e3o \u00e9 poss\u00edvel." }, diff --git a/homeassistant/components/netatmo/translations/sk.json b/homeassistant/components/netatmo/translations/sk.json index 80589b6b4a6..801a8dae54e 100644 --- a/homeassistant/components/netatmo/translations/sk.json +++ b/homeassistant/components/netatmo/translations/sk.json @@ -15,14 +15,21 @@ "title": "Vyberte met\u00f3du overenia" }, "reauth_confirm": { + "description": "Integr\u00e1cia Netatmo potrebuje znova overi\u0165 v\u00e1\u0161 \u00fa\u010det", "title": "Znova overi\u0165 integr\u00e1ciu" } } }, "device_automation": { + "trigger_subtype": { + "away": "pre\u010d", + "hg": "mrazuvzdorn\u00fd", + "schedule": "pl\u00e1n" + }, "trigger_type": { "alarm_started": "{entity_name} rozpoznala alarm", "animal": "{entity_name} rozpoznala zviera", + "cancel_set_point": "{entity_name} obnovila svoj pl\u00e1n", "human": "{entity_name} rozpoznala \u010dloveka", "movement": "{entity_name} rozpoznala pohyb", "outdoor": "{n\u00e1zov_objektu} zistila vonkaj\u0161iu udalos\u0165", @@ -31,7 +38,8 @@ "set_point": "Cie\u013eov\u00e1 teplota {entity_name} nastaven\u00e1 manu\u00e1lne", "therm_mode": "{entity_name} prepnut\u00e9 na \u201e{subtype}\u201c", "turned_off": "{entity_name} vypnut\u00e1", - "turned_on": "{entity_name} zapnut\u00e1" + "turned_on": "{entity_name} zapnut\u00e1", + "vehicle": "{entity_name} rozpoznal vozidlo" } }, "options": { diff --git a/homeassistant/components/netatmo/translations/zh-Hant.json b/homeassistant/components/netatmo/translations/zh-Hant.json index 84bb2dcffa3..669bff60039 100644 --- a/homeassistant/components/netatmo/translations/zh-Hant.json +++ b/homeassistant/components/netatmo/translations/zh-Hant.json @@ -35,7 +35,7 @@ "outdoor": "{entity_name}\u5075\u6e2c\u5230\u6236\u5916\u52d5\u4f5c", "person": "{entity_name}\u5075\u6e2c\u5230\u4eba\u54e1", "person_away": "{entity_name}\u5075\u6e2c\u5230\u4eba\u54e1\u5df2\u96e2\u958b", - "set_point": "\u624b\u52d5\u8a2d\u5b9a{entity_name}\u76ee\u6a19\u6eab\u5ea6", + "set_point": "\u624b\u52d5\u8a2d\u5b9a{entity_name}\u8a2d\u5b9a\u6eab\u5ea6", "therm_mode": "{entity_name}\u5207\u63db\u81f3 \"{subtype}\"", "turned_off": "{entity_name}\u5df2\u95dc\u9589", "turned_on": "{entity_name}\u5df2\u958b\u555f", diff --git a/homeassistant/components/netgear/__init__.py b/homeassistant/components/netgear/__init__.py index ade5a8df6bd..c78ba024813 100644 --- a/homeassistant/components/netgear/__init__.py +++ b/homeassistant/components/netgear/__init__.py @@ -48,8 +48,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: data = {**entry.data, CONF_PORT: router.port, CONF_SSL: router.ssl} hass.config_entries.async_update_entry(entry, data=data) _LOGGER.info( - "Netgear port-SSL combination updated from (%i, %r) to (%i, %r), " - "this should only occur after a firmware update", + ( + "Netgear port-SSL combination updated from (%i, %r) to (%i, %r), " + "this should only occur after a firmware update" + ), port, ssl, router.port, diff --git a/homeassistant/components/netgear/manifest.json b/homeassistant/components/netgear/manifest.json index 92b3065147c..4661e39ad53 100644 --- a/homeassistant/components/netgear/manifest.json +++ b/homeassistant/components/netgear/manifest.json @@ -2,7 +2,7 @@ "domain": "netgear", "name": "NETGEAR", "documentation": "https://www.home-assistant.io/integrations/netgear", - "requirements": ["pynetgear==0.10.8"], + "requirements": ["pynetgear==0.10.9"], "codeowners": ["@hacf-fr", "@Quentame", "@starkillerOG"], "iot_class": "local_polling", "config_flow": true, diff --git a/homeassistant/components/netgear/router.py b/homeassistant/components/netgear/router.py index c6384a44351..3e285f998a1 100644 --- a/homeassistant/components/netgear/router.py +++ b/homeassistant/components/netgear/router.py @@ -132,7 +132,10 @@ class NetgearRouter: if self.method_version == 2 and self.track_devices: if not self.api.get_attached_devices_2(): _LOGGER.error( - "Netgear Model '%s' in MODELS_V2 list, but failed to get attached devices using V2", + ( + "Netgear Model '%s' in MODELS_V2 list, but failed to get" + " attached devices using V2" + ), self.model, ) self.method_version = 1 diff --git a/homeassistant/components/netgear/sensor.py b/homeassistant/components/netgear/sensor.py index dc857e1377a..510b97c37e3 100644 --- a/homeassistant/components/netgear/sensor.py +++ b/homeassistant/components/netgear/sensor.py @@ -16,10 +16,10 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - DATA_MEGABYTES, - DATA_RATE_MEGABITS_PER_SECOND, PERCENTAGE, - TIME_MILLISECONDS, + UnitOfDataRate, + UnitOfInformation, + UnitOfTime, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import EntityCategory @@ -89,35 +89,40 @@ SENSOR_TRAFFIC_TYPES = [ key="NewTodayUpload", name="Upload today", entity_category=EntityCategory.DIAGNOSTIC, - native_unit_of_measurement=DATA_MEGABYTES, + native_unit_of_measurement=UnitOfInformation.MEGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:upload", ), NetgearSensorEntityDescription( key="NewTodayDownload", name="Download today", entity_category=EntityCategory.DIAGNOSTIC, - native_unit_of_measurement=DATA_MEGABYTES, + native_unit_of_measurement=UnitOfInformation.MEGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:download", ), NetgearSensorEntityDescription( key="NewYesterdayUpload", name="Upload yesterday", entity_category=EntityCategory.DIAGNOSTIC, - native_unit_of_measurement=DATA_MEGABYTES, + native_unit_of_measurement=UnitOfInformation.MEGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:upload", ), NetgearSensorEntityDescription( key="NewYesterdayDownload", name="Download yesterday", entity_category=EntityCategory.DIAGNOSTIC, - native_unit_of_measurement=DATA_MEGABYTES, + native_unit_of_measurement=UnitOfInformation.MEGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:download", ), NetgearSensorEntityDescription( key="NewWeekUpload", name="Upload week", entity_category=EntityCategory.DIAGNOSTIC, - native_unit_of_measurement=DATA_MEGABYTES, + native_unit_of_measurement=UnitOfInformation.MEGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:upload", index=0, value=lambda data: data[0], @@ -126,7 +131,8 @@ SENSOR_TRAFFIC_TYPES = [ key="NewWeekUpload", name="Upload week average", entity_category=EntityCategory.DIAGNOSTIC, - native_unit_of_measurement=DATA_MEGABYTES, + native_unit_of_measurement=UnitOfInformation.MEGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:upload", index=1, value=lambda data: data[1], @@ -135,7 +141,8 @@ SENSOR_TRAFFIC_TYPES = [ key="NewWeekDownload", name="Download week", entity_category=EntityCategory.DIAGNOSTIC, - native_unit_of_measurement=DATA_MEGABYTES, + native_unit_of_measurement=UnitOfInformation.MEGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:download", index=0, value=lambda data: data[0], @@ -144,7 +151,8 @@ SENSOR_TRAFFIC_TYPES = [ key="NewWeekDownload", name="Download week average", entity_category=EntityCategory.DIAGNOSTIC, - native_unit_of_measurement=DATA_MEGABYTES, + native_unit_of_measurement=UnitOfInformation.MEGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:download", index=1, value=lambda data: data[1], @@ -153,7 +161,8 @@ SENSOR_TRAFFIC_TYPES = [ key="NewMonthUpload", name="Upload month", entity_category=EntityCategory.DIAGNOSTIC, - native_unit_of_measurement=DATA_MEGABYTES, + native_unit_of_measurement=UnitOfInformation.MEGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:upload", index=0, value=lambda data: data[0], @@ -162,7 +171,8 @@ SENSOR_TRAFFIC_TYPES = [ key="NewMonthUpload", name="Upload month average", entity_category=EntityCategory.DIAGNOSTIC, - native_unit_of_measurement=DATA_MEGABYTES, + native_unit_of_measurement=UnitOfInformation.MEGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:upload", index=1, value=lambda data: data[1], @@ -171,7 +181,8 @@ SENSOR_TRAFFIC_TYPES = [ key="NewMonthDownload", name="Download month", entity_category=EntityCategory.DIAGNOSTIC, - native_unit_of_measurement=DATA_MEGABYTES, + native_unit_of_measurement=UnitOfInformation.MEGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:download", index=0, value=lambda data: data[0], @@ -180,7 +191,8 @@ SENSOR_TRAFFIC_TYPES = [ key="NewMonthDownload", name="Download month average", entity_category=EntityCategory.DIAGNOSTIC, - native_unit_of_measurement=DATA_MEGABYTES, + native_unit_of_measurement=UnitOfInformation.MEGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:download", index=1, value=lambda data: data[1], @@ -189,7 +201,8 @@ SENSOR_TRAFFIC_TYPES = [ key="NewLastMonthUpload", name="Upload last month", entity_category=EntityCategory.DIAGNOSTIC, - native_unit_of_measurement=DATA_MEGABYTES, + native_unit_of_measurement=UnitOfInformation.MEGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:upload", index=0, value=lambda data: data[0], @@ -198,7 +211,8 @@ SENSOR_TRAFFIC_TYPES = [ key="NewLastMonthUpload", name="Upload last month average", entity_category=EntityCategory.DIAGNOSTIC, - native_unit_of_measurement=DATA_MEGABYTES, + native_unit_of_measurement=UnitOfInformation.MEGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:upload", index=1, value=lambda data: data[1], @@ -207,7 +221,8 @@ SENSOR_TRAFFIC_TYPES = [ key="NewLastMonthDownload", name="Download last month", entity_category=EntityCategory.DIAGNOSTIC, - native_unit_of_measurement=DATA_MEGABYTES, + native_unit_of_measurement=UnitOfInformation.MEGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:download", index=0, value=lambda data: data[0], @@ -216,7 +231,8 @@ SENSOR_TRAFFIC_TYPES = [ key="NewLastMonthDownload", name="Download last month average", entity_category=EntityCategory.DIAGNOSTIC, - native_unit_of_measurement=DATA_MEGABYTES, + native_unit_of_measurement=UnitOfInformation.MEGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:download", index=1, value=lambda data: data[1], @@ -228,21 +244,23 @@ SENSOR_SPEED_TYPES = [ key="NewOOKLAUplinkBandwidth", name="Uplink Bandwidth", entity_category=EntityCategory.DIAGNOSTIC, - native_unit_of_measurement=DATA_RATE_MEGABITS_PER_SECOND, + native_unit_of_measurement=UnitOfDataRate.MEGABITS_PER_SECOND, + device_class=SensorDeviceClass.DATA_RATE, icon="mdi:upload", ), NetgearSensorEntityDescription( key="NewOOKLADownlinkBandwidth", name="Downlink Bandwidth", entity_category=EntityCategory.DIAGNOSTIC, - native_unit_of_measurement=DATA_RATE_MEGABITS_PER_SECOND, + native_unit_of_measurement=UnitOfDataRate.MEGABITS_PER_SECOND, + device_class=SensorDeviceClass.DATA_RATE, icon="mdi:download", ), NetgearSensorEntityDescription( key="AveragePing", name="Average Ping", entity_category=EntityCategory.DIAGNOSTIC, - native_unit_of_measurement=TIME_MILLISECONDS, + native_unit_of_measurement=UnitOfTime.MILLISECONDS, icon="mdi:wan", ), ] diff --git a/homeassistant/components/netgear/translations/ko.json b/homeassistant/components/netgear/translations/ko.json new file mode 100644 index 00000000000..d09d8fba1c4 --- /dev/null +++ b/homeassistant/components/netgear/translations/ko.json @@ -0,0 +1,16 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8 (\uc120\ud0dd \uc0ac\ud56d)", + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984 (\uc120\ud0dd \uc0ac\ud56d)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/netgear/translations/lb.json b/homeassistant/components/netgear/translations/lb.json new file mode 100644 index 00000000000..ef9f9e4c2d1 --- /dev/null +++ b/homeassistant/components/netgear/translations/lb.json @@ -0,0 +1,9 @@ +{ + "config": { + "step": { + "user": { + "description": "Standardhost: {host}\nStandardbenotzernumm: {username}" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/netgear/translations/sk.json b/homeassistant/components/netgear/translations/sk.json index ae8937fb96d..dfb7c69797a 100644 --- a/homeassistant/components/netgear/translations/sk.json +++ b/homeassistant/components/netgear/translations/sk.json @@ -16,5 +16,15 @@ "description": "Predvolen\u00fd hostite\u013e: {host}\nPredvolen\u00e9 pou\u017e\u00edvate\u013esk\u00e9 meno: {username}" } } + }, + "options": { + "step": { + "init": { + "data": { + "consider_home": "Zoh\u013eadnite dom\u00e1ci \u010das (sekundy)" + }, + "description": "Zadajte volite\u013en\u00e9 nastavenia" + } + } } } \ No newline at end of file diff --git a/homeassistant/components/netgear_lte/sensor.py b/homeassistant/components/netgear_lte/sensor.py index c27c4f43920..49942306da2 100644 --- a/homeassistant/components/netgear_lte/sensor.py +++ b/homeassistant/components/netgear_lte/sensor.py @@ -1,7 +1,7 @@ """Support for Netgear LTE sensors.""" from __future__ import annotations -from homeassistant.components.sensor import SensorEntity +from homeassistant.components.sensor import SensorDeviceClass, SensorEntity from homeassistant.core import HomeAssistant from homeassistant.exceptions import PlatformNotReady from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -73,8 +73,10 @@ class SMSTotalSensor(LTESensor): class UsageSensor(LTESensor): """Data usage sensor entity.""" + _attr_device_class = SensorDeviceClass.DATA_SIZE + @property - def native_value(self): + def native_value(self) -> float: """Return the state of the sensor.""" return round(self.modem_data.data.usage / 1024**2, 1) diff --git a/homeassistant/components/netgear_lte/sensor_types.py b/homeassistant/components/netgear_lte/sensor_types.py index 45cfb873ef8..01aa267e953 100644 --- a/homeassistant/components/netgear_lte/sensor_types.py +++ b/homeassistant/components/netgear_lte/sensor_types.py @@ -2,9 +2,9 @@ from homeassistant.components.binary_sensor import BinarySensorDeviceClass from homeassistant.const import ( - DATA_MEBIBYTES, PERCENTAGE, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, + UnitOfInformation, ) SENSOR_SMS = "sms" @@ -14,7 +14,7 @@ SENSOR_USAGE = "usage" SENSOR_UNITS = { SENSOR_SMS: "unread", SENSOR_SMS_TOTAL: "messages", - SENSOR_USAGE: DATA_MEBIBYTES, + SENSOR_USAGE: UnitOfInformation.MEBIBYTES, "radio_quality": PERCENTAGE, "rx_level": SIGNAL_STRENGTH_DECIBELS_MILLIWATT, "tx_level": SIGNAL_STRENGTH_DECIBELS_MILLIWATT, diff --git a/homeassistant/components/network/__init__.py b/homeassistant/components/network/__init__.py index 1ad975e1d85..a57334d2531 100644 --- a/homeassistant/components/network/__init__.py +++ b/homeassistant/components/network/__init__.py @@ -51,11 +51,13 @@ async def async_get_source_ip( if not all_ipv4s: _LOGGER.warning( - "Because the system does not have any enabled IPv4 addresses, source address detection may be inaccurate" + "Because the system does not have any enabled IPv4 addresses, source" + " address detection may be inaccurate" ) if source_ip is None: raise HomeAssistantError( - "Could not determine source ip because the system does not have any enabled IPv4 addresses and creating a socket failed" + "Could not determine source ip because the system does not have any" + " enabled IPv4 addresses and creating a socket failed" ) return source_ip diff --git a/homeassistant/components/network/util.py b/homeassistant/components/network/util.py index 4b920e5d927..6f204b05397 100644 --- a/homeassistant/components/network/util.py +++ b/homeassistant/components/network/util.py @@ -142,7 +142,10 @@ def async_get_source_ip(target_ip: str) -> str | None: return cast(str, test_sock.getsockname()[0]) except Exception: # pylint: disable=broad-except _LOGGER.debug( - "The system could not auto detect the source ip for %s on your operating system", + ( + "The system could not auto detect the source ip for %s on your" + " operating system" + ), target_ip, ) return None diff --git a/homeassistant/components/neurio_energy/sensor.py b/homeassistant/components/neurio_energy/sensor.py index a1f6791fa5a..52f6d1d7225 100644 --- a/homeassistant/components/neurio_energy/sensor.py +++ b/homeassistant/components/neurio_energy/sensor.py @@ -14,7 +14,7 @@ from homeassistant.components.sensor import ( SensorEntity, SensorStateClass, ) -from homeassistant.const import CONF_API_KEY, ENERGY_KILO_WATT_HOUR, POWER_WATT +from homeassistant.const import CONF_API_KEY, UnitOfEnergy, UnitOfPower from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -149,11 +149,11 @@ class NeurioEnergy(SensorEntity): self._state = None if sensor_type == ACTIVE_TYPE: - self._unit_of_measurement = POWER_WATT + self._unit_of_measurement = UnitOfPower.WATT self._attr_device_class = SensorDeviceClass.POWER self._attr_state_class = SensorStateClass.MEASUREMENT elif sensor_type == DAILY_TYPE: - self._unit_of_measurement = ENERGY_KILO_WATT_HOUR + self._unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR self._attr_device_class = SensorDeviceClass.ENERGY self._attr_state_class = SensorStateClass.TOTAL_INCREASING diff --git a/homeassistant/components/nexia/climate.py b/homeassistant/components/nexia/climate.py index aa7c55df33f..fe31263a86c 100644 --- a/homeassistant/components/nexia/climate.py +++ b/homeassistant/components/nexia/climate.py @@ -31,7 +31,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS, TEMP_FAHRENHEIT +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_platform import homeassistant.helpers.config_validation as cv @@ -178,7 +178,9 @@ class NexiaZone(NexiaThermostatZoneEntity, ClimateEntity): self._attr_max_humidity = percent_conv(max_humidity) self._attr_min_temp = min_setpoint self._attr_max_temp = max_setpoint - self._attr_temperature_unit = TEMP_CELSIUS if unit == "C" else TEMP_FAHRENHEIT + self._attr_temperature_unit = ( + UnitOfTemperature.CELSIUS if unit == "C" else UnitOfTemperature.FAHRENHEIT + ) self._attr_target_temperature_step = 0.5 if unit == "C" else 1.0 @property diff --git a/homeassistant/components/nexia/coordinator.py b/homeassistant/components/nexia/coordinator.py index d44e1827f5a..b83ebcf9c40 100644 --- a/homeassistant/components/nexia/coordinator.py +++ b/homeassistant/components/nexia/coordinator.py @@ -14,7 +14,7 @@ _LOGGER = logging.getLogger(__name__) DEFAULT_UPDATE_RATE = 120 -class NexiaDataUpdateCoordinator(DataUpdateCoordinator): +class NexiaDataUpdateCoordinator(DataUpdateCoordinator[None]): """DataUpdateCoordinator for nexia homes.""" def __init__( diff --git a/homeassistant/components/nexia/sensor.py b/homeassistant/components/nexia/sensor.py index 3f280581ee7..a67ac681199 100644 --- a/homeassistant/components/nexia/sensor.py +++ b/homeassistant/components/nexia/sensor.py @@ -10,7 +10,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import PERCENTAGE, TEMP_CELSIUS, TEMP_FAHRENHEIT +from homeassistant.const import PERCENTAGE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -86,11 +86,10 @@ async def async_setup_entry( ) # Outdoor Temperature if thermostat.has_outdoor_temperature(): - unit = ( - TEMP_CELSIUS - if thermostat.get_unit() == UNIT_CELSIUS - else TEMP_FAHRENHEIT - ) + if thermostat.get_unit() == UNIT_CELSIUS: + unit = UnitOfTemperature.CELSIUS + else: + unit = UnitOfTemperature.FAHRENHEIT entities.append( NexiaThermostatSensor( coordinator, @@ -120,11 +119,10 @@ async def async_setup_entry( # Zone Sensors for zone_id in thermostat.get_zone_ids(): zone = thermostat.get_zone_by_id(zone_id) - unit = ( - TEMP_CELSIUS - if thermostat.get_unit() == UNIT_CELSIUS - else TEMP_FAHRENHEIT - ) + if thermostat.get_unit() == UNIT_CELSIUS: + unit = UnitOfTemperature.CELSIUS + else: + unit = UnitOfTemperature.FAHRENHEIT # Temperature entities.append( NexiaThermostatZoneSensor( diff --git a/homeassistant/components/nexia/translations/pt.json b/homeassistant/components/nexia/translations/pt.json index 7953cf5625c..6e1218d3245 100644 --- a/homeassistant/components/nexia/translations/pt.json +++ b/homeassistant/components/nexia/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, diff --git a/homeassistant/components/nexia/translations/sk.json b/homeassistant/components/nexia/translations/sk.json index 0c9a112e32e..7bd7f5dfd3e 100644 --- a/homeassistant/components/nexia/translations/sk.json +++ b/homeassistant/components/nexia/translations/sk.json @@ -11,6 +11,7 @@ "step": { "user": { "data": { + "brand": "Zna\u010dka", "password": "Heslo", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } diff --git a/homeassistant/components/nextdns/switch.py b/homeassistant/components/nextdns/switch.py index a0e8f15e44d..3856ac0a2a2 100644 --- a/homeassistant/components/nextdns/switch.py +++ b/homeassistant/components/nextdns/switch.py @@ -587,7 +587,8 @@ class NextDnsSwitch(CoordinatorEntity[NextDnsSettingsUpdateCoordinator], SwitchE ClientError, ) as err: raise HomeAssistantError( - f"NextDNS API returned an error calling set_setting for {self.entity_id}: {err}" + "NextDNS API returned an error calling set_setting for" + f" {self.entity_id}: {err}" ) from err if result: diff --git a/homeassistant/components/nextdns/translations/ko.json b/homeassistant/components/nextdns/translations/ko.json new file mode 100644 index 00000000000..d41f2440c20 --- /dev/null +++ b/homeassistant/components/nextdns/translations/ko.json @@ -0,0 +1,16 @@ +{ + "config": { + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_api_key": "API \ud0a4\uac00 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "api_key": "API \ud0a4" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/nextdns/translations/pt.json b/homeassistant/components/nextdns/translations/pt.json index cd774c0c6cd..12063017e4f 100644 --- a/homeassistant/components/nextdns/translations/pt.json +++ b/homeassistant/components/nextdns/translations/pt.json @@ -1,7 +1,7 @@ { "config": { "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_api_key": "Chave de API inv\u00e1lida", "unknown": "Erro inesperado" }, diff --git a/homeassistant/components/nextdns/translations/sk.json b/homeassistant/components/nextdns/translations/sk.json index a748bf158fb..4a8d6aa6f3d 100644 --- a/homeassistant/components/nextdns/translations/sk.json +++ b/homeassistant/components/nextdns/translations/sk.json @@ -20,5 +20,10 @@ } } } + }, + "system_health": { + "info": { + "can_reach_server": "Dosiahnu\u0165 server" + } } } \ No newline at end of file diff --git a/homeassistant/components/nfandroidtv/translations/ko.json b/homeassistant/components/nfandroidtv/translations/ko.json new file mode 100644 index 00000000000..773be9e2285 --- /dev/null +++ b/homeassistant/components/nfandroidtv/translations/ko.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8", + "name": "\uc774\ub984" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/nfandroidtv/translations/pt.json b/homeassistant/components/nfandroidtv/translations/pt.json index e3233d5779f..b0aa25592e2 100644 --- a/homeassistant/components/nfandroidtv/translations/pt.json +++ b/homeassistant/components/nfandroidtv/translations/pt.json @@ -6,7 +6,7 @@ "step": { "user": { "data": { - "host": "Servidor", + "host": "Endere\u00e7o", "name": "Nome" } } diff --git a/homeassistant/components/nibe_heatpump/__init__.py b/homeassistant/components/nibe_heatpump/__init__.py index 68e16871549..ed907a7ce6a 100644 --- a/homeassistant/components/nibe_heatpump/__init__.py +++ b/homeassistant/components/nibe_heatpump/__init__.py @@ -11,7 +11,7 @@ from typing import Any, Generic, TypeVar from nibe.coil import Coil from nibe.connection import Connection from nibe.connection.modbus import Modbus -from nibe.connection.nibegw import NibeGW +from nibe.connection.nibegw import NibeGW, ProductInfo from nibe.exceptions import CoilNotFoundException, CoilReadException from nibe.heatpump import HeatPump, Model, Series @@ -48,6 +48,8 @@ from .const import ( PLATFORMS: list[Platform] = [ Platform.BINARY_SENSOR, + Platform.BUTTON, + Platform.CLIMATE, Platform.NUMBER, Platform.SELECT, Platform.SENSOR, @@ -98,14 +100,25 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: data[entry.entry_id] = coordinator reg = dr.async_get(hass) - reg.async_get_or_create( + device_entry = reg.async_get_or_create( config_entry_id=entry.entry_id, identifiers={(DOMAIN, entry.unique_id or entry.entry_id)}, manufacturer="NIBE Energy Systems", - model=heatpump.model.name, name=heatpump.model.name, ) + def _on_product_info(product_info: ProductInfo): + reg.async_update_device( + device_id=device_entry.id, + model=product_info.model, + sw_version=str(product_info.firmware_version), + ) + + if isinstance(connection, NibeGW): + connection.subscribe(connection.PRODUCT_INFO_EVENT, _on_product_info) + else: + reg.async_update_device(device_id=device_entry.id, model=heatpump.model.name) + await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) # Trigger a refresh again now that all platforms have registered @@ -156,6 +169,8 @@ class ContextCoordinator( self, update_callback: CALLBACK_TYPE, context: Any = None ) -> Callable[[], None]: """Wrap standard function to prune cached callback database.""" + assert isinstance(context, set) + context -= {None} release = super().async_add_listener(update_callback, context) self.__dict__.pop("context_callbacks", None) @@ -238,6 +253,10 @@ class Coordinator(ContextCoordinator[dict[int, Coil], int]): self.async_update_context_listeners([coil.address]) + async def async_read_coil(self, coil: Coil) -> Coil: + """Read coil and update state using callbacks.""" + return await self.connection.read_coil(coil) + async def _async_update_data(self) -> dict[int, Coil]: self.task = asyncio.current_task() try: @@ -268,7 +287,11 @@ class Coordinator(ContextCoordinator[dict[int, Coil], int]): result[coil.address] = coil self.seed.pop(coil.address, None) except CoilReadException as exception: - raise UpdateFailed(f"Failed to update: {exception}") from exception + if not result: + raise UpdateFailed(f"Failed to update: {exception}") from exception + self.logger.debug( + "Some coils failed to update, and may be unsupported: %s", exception + ) return result diff --git a/homeassistant/components/nibe_heatpump/button.py b/homeassistant/components/nibe_heatpump/button.py new file mode 100644 index 00000000000..e8bb3026e47 --- /dev/null +++ b/homeassistant/components/nibe_heatpump/button.py @@ -0,0 +1,62 @@ +"""The Nibe Heat Pump sensors.""" +from __future__ import annotations + +from nibe.coil_groups import UNIT_COILGROUPS, UnitCoilGroup +from nibe.exceptions import CoilNotFoundException + +from homeassistant.components.button import ButtonEntity +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import EntityCategory +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.update_coordinator import CoordinatorEntity + +from . import DOMAIN, LOGGER, Coordinator + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up platform.""" + + coordinator: Coordinator = hass.data[DOMAIN][config_entry.entry_id] + + def reset_buttons(): + if unit := UNIT_COILGROUPS.get(coordinator.series, {}).get("main"): + try: + yield NibeAlarmResetButton(coordinator, unit) + except CoilNotFoundException as exception: + LOGGER.debug("Skipping button %r", exception) + + async_add_entities(reset_buttons()) + + +class NibeAlarmResetButton(CoordinatorEntity[Coordinator], ButtonEntity): + """Sensor entity.""" + + _attr_has_entity_name = True + _attr_entity_category = EntityCategory.DIAGNOSTIC + + def __init__(self, coordinator: Coordinator, unit: UnitCoilGroup) -> None: + """Initialize entity.""" + self._reset_coil = coordinator.heatpump.get_coil_by_address(unit.alarm_reset) + self._alarm_coil = coordinator.heatpump.get_coil_by_address(unit.alarm) + super().__init__(coordinator, {self._alarm_coil.address}) + self._attr_name = self._reset_coil.title + self._attr_unique_id = f"{coordinator.unique_id}-alarm_reset" + self._attr_device_info = coordinator.device_info + + async def async_press(self) -> None: + """Execute the command.""" + await self.coordinator.async_write_coil(self._reset_coil, 1) + await self.coordinator.async_read_coil(self._alarm_coil) + + @property + def available(self) -> bool: + """Return if entity is available.""" + if coil := self.coordinator.data.get(self._alarm_coil.address): + return coil.value != 0 + + return False diff --git a/homeassistant/components/nibe_heatpump/climate.py b/homeassistant/components/nibe_heatpump/climate.py new file mode 100644 index 00000000000..9c7d8641b6e --- /dev/null +++ b/homeassistant/components/nibe_heatpump/climate.py @@ -0,0 +1,223 @@ +"""The Nibe Heat Pump climate.""" +from __future__ import annotations + +from typing import Any + +from nibe.coil import Coil +from nibe.coil_groups import ( + CLIMATE_COILGROUPS, + UNIT_COILGROUPS, + ClimateCoilGroup, + UnitCoilGroup, +) +from nibe.exceptions import CoilNotFoundException + +from homeassistant.components.climate import ( + ATTR_HVAC_MODE, + ATTR_TARGET_TEMP_HIGH, + ATTR_TARGET_TEMP_LOW, + ATTR_TEMPERATURE, + ClimateEntity, + ClimateEntityFeature, + HVACAction, + HVACMode, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.update_coordinator import CoordinatorEntity + +from . import Coordinator +from .const import ( + DOMAIN, + LOGGER, + VALUES_MIXING_VALVE_CLOSED_STATE, + VALUES_PRIORITY_COOLING, + VALUES_PRIORITY_HEATING, +) + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up platform.""" + + coordinator: Coordinator = hass.data[DOMAIN][config_entry.entry_id] + + main_unit = UNIT_COILGROUPS.get(coordinator.series, {}).get("main") + if not main_unit: + LOGGER.debug("Skipping climates - no main unit found") + return + + def climate_systems(): + for key, group in CLIMATE_COILGROUPS.get(coordinator.series, ()).items(): + try: + yield NibeClimateEntity(coordinator, key, main_unit, group) + except CoilNotFoundException as exception: + LOGGER.debug("Skipping climate: %s due to %s", key, exception) + + async_add_entities(climate_systems()) + + +class NibeClimateEntity(CoordinatorEntity[Coordinator], ClimateEntity): + """Climate entity.""" + + _attr_entity_category = None + _attr_supported_features = ( + ClimateEntityFeature.TARGET_TEMPERATURE_RANGE + | ClimateEntityFeature.TARGET_TEMPERATURE + ) + _attr_hvac_modes = [HVACMode.HEAT_COOL, HVACMode.OFF, HVACMode.HEAT] + _attr_target_temperature_step = 0.5 + _attr_max_temp = 35.0 + _attr_min_temp = 5.0 + + def __init__( + self, + coordinator: Coordinator, + key: str, + unit: UnitCoilGroup, + climate: ClimateCoilGroup, + ) -> None: + """Initialize entity.""" + super().__init__( + coordinator, + { + unit.prio, + unit.cooling_with_room_sensor, + climate.current, + climate.setpoint_heat, + climate.setpoint_cool, + climate.mixing_valve_state, + climate.active_accessory, + climate.use_room_sensor, + }, + ) + self._attr_available = False + self._attr_name = climate.name + self._attr_unique_id = f"{coordinator.unique_id}-{key}" + self._attr_device_info = coordinator.device_info + self._attr_hvac_action = HVACAction.IDLE + self._attr_hvac_mode = HVACMode.OFF + self._attr_target_temperature_high = None + self._attr_target_temperature_low = None + self._attr_target_temperature = None + self._attr_entity_registry_enabled_default = climate.active_accessory is None + + def _get(address: int) -> Coil: + return coordinator.heatpump.get_coil_by_address(address) + + self._coil_current = _get(climate.current) + self._coil_setpoint_heat = _get(climate.setpoint_heat) + self._coil_setpoint_cool = _get(climate.setpoint_cool) + self._coil_prio = _get(unit.prio) + self._coil_mixing_valve_state = _get(climate.mixing_valve_state) + if climate.active_accessory is None: + self._coil_active_accessory = None + else: + self._coil_active_accessory = _get(climate.active_accessory) + self._coil_use_room_sensor = _get(climate.use_room_sensor) + self._coil_cooling_with_room_sensor = _get(unit.cooling_with_room_sensor) + + if self._coil_current: + self._attr_temperature_unit = self._coil_current.unit + + @callback + def _handle_coordinator_update(self) -> None: + if not self.coordinator.data: + return + + def _get_value(coil: Coil) -> int | str | float | None: + return self.coordinator.get_coil_value(coil) + + def _get_float(coil: Coil) -> float | None: + return self.coordinator.get_coil_float(coil) + + self._attr_current_temperature = _get_float(self._coil_current) + + mode = HVACMode.OFF + if _get_value(self._coil_use_room_sensor) == "ON": + if _get_value(self._coil_cooling_with_room_sensor) == "ON": + mode = HVACMode.HEAT_COOL + else: + mode = HVACMode.HEAT + self._attr_hvac_mode = mode + + setpoint_heat = _get_float(self._coil_setpoint_heat) + setpoint_cool = _get_float(self._coil_setpoint_cool) + + if mode == HVACMode.HEAT_COOL: + self._attr_target_temperature = None + self._attr_target_temperature_low = setpoint_heat + self._attr_target_temperature_high = setpoint_cool + elif mode == HVACMode.HEAT: + self._attr_target_temperature = setpoint_heat + self._attr_target_temperature_low = None + self._attr_target_temperature_high = None + else: + self._attr_target_temperature = None + self._attr_target_temperature_low = None + self._attr_target_temperature_high = None + + if prio := _get_value(self._coil_prio): + if ( + _get_value(self._coil_mixing_valve_state) + in VALUES_MIXING_VALVE_CLOSED_STATE + ): + self._attr_hvac_action = HVACAction.IDLE + elif prio in VALUES_PRIORITY_HEATING: + self._attr_hvac_action = HVACAction.HEATING + elif prio in VALUES_PRIORITY_COOLING: + self._attr_hvac_action = HVACAction.COOLING + else: + self._attr_hvac_action = HVACAction.IDLE + else: + self._attr_hvac_action = None + + self.async_write_ha_state() + + @property + def available(self) -> bool: + """Return if entity is available.""" + coordinator = self.coordinator + active = self._coil_active_accessory + + if not coordinator.last_update_success: + return False + + if not active: + return True + + if active_accessory := coordinator.get_coil_value(active): + return active_accessory == "ON" + + return False + + async def async_set_temperature(self, **kwargs: Any) -> None: + """Set target temperatures.""" + coordinator = self.coordinator + hvac_mode = kwargs.get(ATTR_HVAC_MODE, self._attr_hvac_mode) + + if (temperature := kwargs.get(ATTR_TEMPERATURE)) is not None: + if hvac_mode == HVACMode.HEAT: + await coordinator.async_write_coil( + self._coil_setpoint_heat, temperature + ) + elif hvac_mode == HVACMode.COOL: + await coordinator.async_write_coil( + self._coil_setpoint_cool, temperature + ) + else: + raise ValueError( + "'set_temperature' requires 'hvac_mode' when passing" + " 'temperature' and 'hvac_mode' is not already set to" + " 'heat' or 'cool'" + ) + + if (temperature := kwargs.get(ATTR_TARGET_TEMP_LOW)) is not None: + await coordinator.async_write_coil(self._coil_setpoint_heat, temperature) + + if (temperature := kwargs.get(ATTR_TARGET_TEMP_HIGH)) is not None: + await coordinator.async_write_coil(self._coil_setpoint_cool, temperature) diff --git a/homeassistant/components/nibe_heatpump/const.py b/homeassistant/components/nibe_heatpump/const.py index 381ad7ba0c2..7d9bf58709c 100644 --- a/homeassistant/components/nibe_heatpump/const.py +++ b/homeassistant/components/nibe_heatpump/const.py @@ -13,3 +13,7 @@ CONF_CONNECTION_TYPE_NIBEGW = "nibegw" CONF_CONNECTION_TYPE_MODBUS = "modbus" CONF_MODBUS_URL = "modbus_url" CONF_MODBUS_UNIT = "modbus_unit" + +VALUES_MIXING_VALVE_CLOSED_STATE = (30, "CLOSED", "SHUNT CLOSED") +VALUES_PRIORITY_HEATING = (30, "HEAT") +VALUES_PRIORITY_COOLING = (60, "COOLING") diff --git a/homeassistant/components/nibe_heatpump/manifest.json b/homeassistant/components/nibe_heatpump/manifest.json index f9276570885..4b1bc81209d 100644 --- a/homeassistant/components/nibe_heatpump/manifest.json +++ b/homeassistant/components/nibe_heatpump/manifest.json @@ -3,7 +3,7 @@ "name": "Nibe Heat Pump", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/nibe_heatpump", - "requirements": ["nibe==1.3.0"], + "requirements": ["nibe==1.6.0"], "codeowners": ["@elupus"], "iot_class": "local_polling" } diff --git a/homeassistant/components/nibe_heatpump/sensor.py b/homeassistant/components/nibe_heatpump/sensor.py index c092464b1bf..310e3ac26ad 100644 --- a/homeassistant/components/nibe_heatpump/sensor.py +++ b/homeassistant/components/nibe_heatpump/sensor.py @@ -12,14 +12,12 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - ELECTRIC_CURRENT_AMPERE, - ELECTRIC_CURRENT_MILLIAMPERE, - ELECTRIC_POTENTIAL_MILLIVOLT, - ELECTRIC_POTENTIAL_VOLT, - TIME_HOURS, + UnitOfElectricCurrent, + UnitOfElectricPotential, UnitOfEnergy, UnitOfPower, UnitOfTemperature, + UnitOfTime, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory @@ -47,28 +45,28 @@ UNIT_DESCRIPTIONS = { entity_category=EntityCategory.DIAGNOSTIC, device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, ), "mA": SensorEntityDescription( key="mA", entity_category=EntityCategory.DIAGNOSTIC, device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=ELECTRIC_CURRENT_MILLIAMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.MILLIAMPERE, ), "V": SensorEntityDescription( key="V", entity_category=EntityCategory.DIAGNOSTIC, - device_class=SensorDeviceClass.CURRENT, + device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, ), "mV": SensorEntityDescription( key="mV", entity_category=EntityCategory.DIAGNOSTIC, - device_class=SensorDeviceClass.CURRENT, + device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=ELECTRIC_POTENTIAL_MILLIVOLT, + native_unit_of_measurement=UnitOfElectricPotential.MILLIVOLT, ), "W": SensorEntityDescription( key="W", @@ -110,7 +108,7 @@ UNIT_DESCRIPTIONS = { entity_category=EntityCategory.DIAGNOSTIC, device_class=SensorDeviceClass.DURATION, state_class=SensorStateClass.TOTAL_INCREASING, - native_unit_of_measurement=TIME_HOURS, + native_unit_of_measurement=UnitOfTime.HOURS, ), } diff --git a/homeassistant/components/nibe_heatpump/translations/hu.json b/homeassistant/components/nibe_heatpump/translations/hu.json index 3f6c9845656..b3ce021ba2c 100644 --- a/homeassistant/components/nibe_heatpump/translations/hu.json +++ b/homeassistant/components/nibe_heatpump/translations/hu.json @@ -3,11 +3,11 @@ "error": { "address": "\u00c9rv\u00e9nytelen t\u00e1voli c\u00edm van megadva. A c\u00edmnek IP-c\u00edmnek vagy feloldhat\u00f3 g\u00e9pn\u00e9vnek kell lennie.", "address_in_use": "A kiv\u00e1lasztott port m\u00e1r haszn\u00e1latban van ezen a rendszeren.", - "model": "\u00dagy t\u0171nik, hogy a kiv\u00e1lasztott modell nem t\u00e1mogatja a modbus40-et", + "model": "\u00dagy t\u0171nik, hogy a kiv\u00e1lasztott modell nem t\u00e1mogatja a MODBUS40 szabv\u00e1nyt.", "read": "Hiba a szivatty\u00fa olvas\u00e1si k\u00e9r\u00e9s\u00e9n\u00e9l. Ellen\u0151rizze a \"T\u00e1voli olvas\u00e1si portot\" vagy a \"T\u00e1voli IP-c\u00edmet\".", "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt", - "url": "A megadott URL-c\u00edm nem j\u00f3l form\u00e1zott \u00e9s t\u00e1mogatott URL-c\u00edm", - "write": "Hiba a h\u0151szivatty\u00fa \u00edr\u00e1si k\u00e9relm\u00e9ben. Ellen\u0151rizze a portot, c\u00edmet." + "url": "A megadott URL nem j\u00f3l form\u00e1zott \u00e9s nem t\u00e1mogatott", + "write": "Hiba a szivatty\u00faba t\u00f6rt\u00e9n\u0151 \u00edr\u00e1si k\u00e9r\u00e9sn\u00e9l. Ellen\u0151rizze a \"T\u00e1voli \u00edr\u00f3portot\" vagy a \"T\u00e1voli c\u00edmet\"." }, "step": { "modbus": { diff --git a/homeassistant/components/nibe_heatpump/translations/ko.json b/homeassistant/components/nibe_heatpump/translations/ko.json new file mode 100644 index 00000000000..3c7d7b7578c --- /dev/null +++ b/homeassistant/components/nibe_heatpump/translations/ko.json @@ -0,0 +1,23 @@ +{ + "config": { + "error": { + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "nibegw": { + "data_description": { + "listening_port": "NibeGW \uc7a5\uce58\uac00 \ub370\uc774\ud130\ub97c \uc804\uc1a1\ud558\ub3c4\ub85d \uad6c\uc131\ub41c \uc774 \uc2dc\uc2a4\ud15c\uc758 \ub85c\uceec \ud3ec\ud2b8\uc785\ub2c8\ub2e4.", + "remote_read_port": "NibeGW \uc7a5\uce58\uac00 \uc77d\uae30 \uc694\uccad\uc744 \uc218\uc2e0\ud558\ub294 \ud3ec\ud2b8\uc785\ub2c8\ub2e4.", + "remote_write_port": "NibeGW \uc7a5\uce58\uac00 \uc4f0\uae30 \uc694\uccad\uc744 \uc218\uc2e0\ud558\ub294 \ud3ec\ud2b8\uc785\ub2c8\ub2e4." + }, + "description": "\ud1b5\ud569\uad6c\uc131\uc694\uc18c \uad6c\uc131\uc744 \uc2dc\ub3c4\ud558\uae30 \uc804\uc5d0 \ub2e4\uc74c\uc744 \ud655\uc778\ud558\uc2ed\uc2dc\uc624.\n - NibeGW \uc7a5\uce58\uac00 \ud788\ud2b8 \ud38c\ud504\uc5d0 \uc5f0\uacb0\ub418\uc5b4\uc788\uc2b5\ub2c8\ub2e4.\n - \ud788\ud2b8 \ud38c\ud504 \uad6c\uc131\uc5d0\uc11c MODBUS40 \uc561\uc138\uc11c\ub9ac\uac00 \ud65c\uc131\ud654\ub418\uc5c8\uc2b5\ub2c8\ub2e4.\n - \ud38c\ud504\uac00 MODBUS40 \uc561\uc138\uc11c\ub9ac \ub204\ub77d\uc5d0 \ub300\ud55c \uacbd\ubcf4 \uc0c1\ud0dc\uac00 \ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4." + }, + "user": { + "menu_options": { + "modbus": "\ubaa8\ub4dc\ubc84\uc2a4", + "nibegw": "NibeGW" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/nibe_heatpump/translations/pt.json b/homeassistant/components/nibe_heatpump/translations/pt.json new file mode 100644 index 00000000000..0c5c7760566 --- /dev/null +++ b/homeassistant/components/nibe_heatpump/translations/pt.json @@ -0,0 +1,7 @@ +{ + "config": { + "error": { + "unknown": "Erro inesperado" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/nibe_heatpump/translations/sk.json b/homeassistant/components/nibe_heatpump/translations/sk.json index 50d472b9c96..ef2a82b3553 100644 --- a/homeassistant/components/nibe_heatpump/translations/sk.json +++ b/homeassistant/components/nibe_heatpump/translations/sk.json @@ -1,25 +1,44 @@ { "config": { "error": { + "address": "Bola zadan\u00e1 neplatn\u00e1 vzdialen\u00e1 adresa. Adresa mus\u00ed by\u0165 IP adresa alebo rozl\u00ed\u0161ite\u013en\u00fd n\u00e1zov hostite\u013ea.", "address_in_use": "Vybran\u00fd port po\u010d\u00favania sa u\u017e v tomto syst\u00e9me pou\u017e\u00edva.", - "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + "model": "Zd\u00e1 sa, \u017ee vybran\u00fd model nepodporuje MODBUS40", + "read": "Chyba pri po\u017eiadavke na \u010d\u00edtanie z pumpy. Overte svoj \u201ePort na \u010d\u00edtanie\u201c alebo \u201eVzdialen\u00e1 adresa\u201c.", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba", + "url": "Zadan\u00e1 adresa URL nie je spr\u00e1vne vytvoren\u00e1 ani podporovan\u00e1", + "write": "Chyba pri \u017eiadosti o z\u00e1pis do pumpy. Overte svoj \u201ePort pre vzdialen\u00fd z\u00e1pis\u201c alebo \u201eVzdialen\u00e1 adresa\u201c." }, "step": { "modbus": { "data": { + "modbus_unit": "Identifik\u00e1tor jednotky Modbus", "modbus_url": "Modbus URL", "model": "Model tepeln\u00e9ho \u010derpadla" + }, + "data_description": { + "modbus_unit": "Identifik\u00e1cia jednotky pre va\u0161e tepeln\u00e9 \u010derpadlo. Zvy\u010dajne mo\u017eno ponecha\u0165 na 0.", + "modbus_url": "Modbus URL, ktor\u00e1 popisuje pripojenie k v\u00e1\u0161mu tepeln\u00e9mu \u010derpadlu alebo jednotke MODBUS40. Malo by to by\u0165 vo formul\u00e1ri:\n - `tcp://[HOST]:[PORT]` pre pripojenie Modbus TCP\n - `serial://[LOCAL DEVICE]` pre miestne pripojenie Modbus RTU\n - `rfc2217://[HOST]:[PORT]` pre vzdialen\u00e9 pripojenie Modbus RTU zalo\u017een\u00e9 na telnete." } }, "nibegw": { "data": { "ip_address": "Vzdialen\u00e1 adresa", + "listening_port": "Miestny port na po\u010d\u00favanie", "model": "Model tepeln\u00e9ho \u010derpadla", "remote_read_port": "Port pre vzdialen\u00e9 \u010d\u00edtanie", "remote_write_port": "Port pre vzdialen\u00fd z\u00e1pis" - } + }, + "data_description": { + "ip_address": "Adresa jednotky NibeGW. Zariadenie by malo by\u0165 nakonfigurovan\u00e9 so statickou adresou.", + "listening_port": "Lok\u00e1lny port na tomto syst\u00e9me, na ktor\u00fd je jednotka NibeGW nakonfigurovan\u00e1 na odosielanie \u00fadajov.", + "remote_read_port": "Port, na ktorom jednotka NibeGW po\u010d\u00fava po\u017eiadavky na \u010d\u00edtanie.", + "remote_write_port": "Port, na ktorom jednotka NibeGW po\u010d\u00fava po\u017eiadavky na z\u00e1pis." + }, + "description": "Pred pokusom o konfigur\u00e1ciu integr\u00e1cie skontrolujte, \u010di:\n - Jednotka NibeGW je pripojen\u00e1 k tepeln\u00e9mu \u010derpadlu.\n - Pr\u00edslu\u0161enstvo MODBUS40 bolo povolen\u00e9 v konfigur\u00e1cii tepeln\u00e9ho \u010derpadla.\n - Pumpa nepre\u0161la do stavu alarmu o ch\u00fdbaj\u00facom pr\u00edslu\u0161enstve MODBUS40." }, "user": { + "description": "Vyberte sp\u00f4sob pripojenia k pumpe. Vo v\u0161eobecnosti \u010derpadl\u00e1 s\u00e9rie F vy\u017eaduj\u00fa vlastn\u00e9 pr\u00edslu\u0161enstvo NibeGW, zatia\u013e \u010do \u010derpadlo s\u00e9rie S m\u00e1 vstavan\u00fa podporu Modbus.", "menu_options": { "modbus": "Modbus", "nibegw": "NibeGW" diff --git a/homeassistant/components/nightscout/translations/de.json b/homeassistant/components/nightscout/translations/de.json index 93524c79689..1b540bed2b0 100644 --- a/homeassistant/components/nightscout/translations/de.json +++ b/homeassistant/components/nightscout/translations/de.json @@ -14,7 +14,7 @@ "api_key": "API-Schl\u00fcssel", "url": "URL" }, - "description": "- URL: die Adresse deiner Nightscout-Instanz. Z.B.: https://myhomeassistant.duckdns.org:5423\n- API-Schl\u00fcssel (optional): Nur verwenden, wenn deine Instanz gesch\u00fctzt ist (auth_default_roles != readable).", + "description": "- URL: die Adresse deiner Nightscout Instanz. z.B.: https://myhomeassistant.duckdns.org:5423\n- API-Schl\u00fcssel (optional): Nur verwenden, wenn deine Instanz gesch\u00fctzt ist (auth_default_roles != readable).", "title": "Gib deine Nightscout-Serverinformationen ein." } } diff --git a/homeassistant/components/nightscout/translations/he.json b/homeassistant/components/nightscout/translations/he.json index eebb1cc0a93..66a49e156d5 100644 --- a/homeassistant/components/nightscout/translations/he.json +++ b/homeassistant/components/nightscout/translations/he.json @@ -13,7 +13,8 @@ "data": { "api_key": "\u05de\u05e4\u05ea\u05d7 API", "url": "\u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8" - } + }, + "description": "- \u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8: \u05d4\u05db\u05ea\u05d5\u05d1\u05ea \u05e9\u05dc \u05de\u05d5\u05e4\u05e2 Nightscout \u05e9\u05dc\u05da. \u05db\u05dc\u05d5\u05de\u05e8: https://myhomeassistant.duckdns.org:5423\n- \u05de\u05e4\u05ea\u05d7 API (\u05d0\u05d5\u05e4\u05e6\u05d9\u05d5\u05e0\u05dc\u05d9): \u05d9\u05e9 \u05dc\u05d4\u05e9\u05ea\u05de\u05e9 \u05e8\u05e7 \u05d0\u05dd \u05d4\u05de\u05d5\u05e4\u05e2 \u05e9\u05dc\u05da \u05de\u05d5\u05d2\u05df (auth_default_roles != readable)." } } } diff --git a/homeassistant/components/nightscout/translations/pt.json b/homeassistant/components/nightscout/translations/pt.json index 97874abe7b5..5b6921ab33e 100644 --- a/homeassistant/components/nightscout/translations/pt.json +++ b/homeassistant/components/nightscout/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, diff --git a/homeassistant/components/nightscout/translations/sk.json b/homeassistant/components/nightscout/translations/sk.json index de3386fca50..0934c42f58b 100644 --- a/homeassistant/components/nightscout/translations/sk.json +++ b/homeassistant/components/nightscout/translations/sk.json @@ -14,6 +14,7 @@ "api_key": "API k\u013e\u00fa\u010d", "url": "URL" }, + "description": "- URL: adresa va\u0161ej in\u0161tancie no\u010dn\u00e9ho prieskumu. Napr\u00edklad: https://myhomeassistant.duckdns.org:5423\n- K\u013e\u00fa\u010d API (volite\u013en\u00e9): Pou\u017eite len vtedy, ak je va\u0161a in\u0161tancia chr\u00e1nen\u00e1 (auth_default_roles != \u010ditate\u013en\u00e9).", "title": "Zadajte inform\u00e1cie o serveri Nightscout." } } diff --git a/homeassistant/components/nilu/air_quality.py b/homeassistant/components/nilu/air_quality.py index 400c9f0ab8e..5c3f9c59460 100644 --- a/homeassistant/components/nilu/air_quality.py +++ b/homeassistant/components/nilu/air_quality.py @@ -97,16 +97,20 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( vol.Exclusive( CONF_AREA, "station_collection", - "Can only configure one specific station or " - "stations in a specific area pr sensor. " - "Please only configure station or area.", + ( + "Can only configure one specific station or " + "stations in a specific area pr sensor. " + "Please only configure station or area." + ), ): vol.All(cv.string, vol.In(CONF_ALLOWED_AREAS)), vol.Exclusive( CONF_STATION, "station_collection", - "Can only configure one specific station or " - "stations in a specific area pr sensor. " - "Please only configure station or area.", + ( + "Can only configure one specific station or " + "stations in a specific area pr sensor. " + "Please only configure station or area." + ), ): vol.All(cv.ensure_list, [cv.string]), vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_SHOW_ON_MAP, default=False): cv.boolean, diff --git a/homeassistant/components/nina/__init__.py b/homeassistant/components/nina/__init__.py index f03a2c765cc..0555f175cfb 100644 --- a/homeassistant/components/nina/__init__.py +++ b/homeassistant/components/nina/__init__.py @@ -2,6 +2,7 @@ from __future__ import annotations from dataclasses import dataclass +from typing import Any from async_timeout import timeout from pynina import ApiError, Nina @@ -56,6 +57,7 @@ class NinaWarningData: description: str sender: str severity: str + recommended_actions: str sent: str start: str expires: str @@ -89,12 +91,38 @@ class NINADataUpdateCoordinator( raise UpdateFailed(err) from err return self._parse_data() + @staticmethod + def _remove_duplicate_warnings( + warnings: dict[str, list[Any]] + ) -> dict[str, list[Any]]: + """Remove warnings with the same title and expires timestamp in a region.""" + all_filtered_warnings: dict[str, list[Any]] = {} + + for region_id, raw_warnings in warnings.items(): + + filtered_warnings: list[Any] = [] + processed_details: list[tuple[str, str]] = [] + + for raw_warn in raw_warnings: + if (raw_warn.headline, raw_warn.expires) in processed_details: + continue + + processed_details.append((raw_warn.headline, raw_warn.expires)) + + filtered_warnings.append(raw_warn) + + all_filtered_warnings[region_id] = filtered_warnings + + return all_filtered_warnings + def _parse_data(self) -> dict[str, list[NinaWarningData]]: """Parse warning data.""" return_data: dict[str, list[NinaWarningData]] = {} - for region_id, raw_warnings in self._nina.warnings.items(): + for region_id, raw_warnings in self._remove_duplicate_warnings( + self._nina.warnings + ).items(): warnings_for_regions: list[NinaWarningData] = [] for raw_warn in raw_warnings: @@ -107,6 +135,7 @@ class NINADataUpdateCoordinator( raw_warn.description, raw_warn.sender, raw_warn.severity, + " ".join([str(action) for action in raw_warn.recommended_actions]), raw_warn.sent or "", raw_warn.start or "", raw_warn.expires or "", diff --git a/homeassistant/components/nina/binary_sensor.py b/homeassistant/components/nina/binary_sensor.py index bdc79c34d92..24d6d35d0e8 100644 --- a/homeassistant/components/nina/binary_sensor.py +++ b/homeassistant/components/nina/binary_sensor.py @@ -18,6 +18,7 @@ from .const import ( ATTR_EXPIRES, ATTR_HEADLINE, ATTR_ID, + ATTR_RECOMMENDED_ACTIONS, ATTR_SENDER, ATTR_SENT, ATTR_SEVERITY, @@ -92,6 +93,7 @@ class NINAMessage(CoordinatorEntity[NINADataUpdateCoordinator], BinarySensorEnti ATTR_DESCRIPTION: data.description, ATTR_SENDER: data.sender, ATTR_SEVERITY: data.severity, + ATTR_RECOMMENDED_ACTIONS: data.recommended_actions, ATTR_ID: data.id, ATTR_SENT: data.sent, ATTR_START: data.start, diff --git a/homeassistant/components/nina/const.py b/homeassistant/components/nina/const.py index 76881501894..8ba7c5ffaa6 100644 --- a/homeassistant/components/nina/const.py +++ b/homeassistant/components/nina/const.py @@ -19,6 +19,7 @@ ATTR_HEADLINE: str = "headline" ATTR_DESCRIPTION: str = "description" ATTR_SENDER: str = "sender" ATTR_SEVERITY: str = "severity" +ATTR_RECOMMENDED_ACTIONS: str = "recommended_actions" ATTR_ID: str = "id" ATTR_SENT: str = "sent" ATTR_START: str = "start" diff --git a/homeassistant/components/nina/manifest.json b/homeassistant/components/nina/manifest.json index 24a8eb7163a..7884d51cdc7 100644 --- a/homeassistant/components/nina/manifest.json +++ b/homeassistant/components/nina/manifest.json @@ -3,7 +3,7 @@ "name": "NINA", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/nina", - "requirements": ["pynina==0.1.8"], + "requirements": ["pynina==0.2.0"], "dependencies": [], "codeowners": ["@DeerMaximum"], "iot_class": "cloud_polling", diff --git a/homeassistant/components/nina/translations/ko.json b/homeassistant/components/nina/translations/ko.json new file mode 100644 index 00000000000..8e37e4fa4e8 --- /dev/null +++ b/homeassistant/components/nina/translations/ko.json @@ -0,0 +1,17 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + } + }, + "options": { + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/nina/translations/pt.json b/homeassistant/components/nina/translations/pt.json index f86b761e487..2cf87c627da 100644 --- a/homeassistant/components/nina/translations/pt.json +++ b/homeassistant/components/nina/translations/pt.json @@ -4,7 +4,7 @@ "single_instance_allowed": "J\u00e1 configurado. Apenas uma \u00fanica configura\u00e7\u00e3o \u00e9 poss\u00edvel." }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "unknown": "Erro inesperado" }, "step": { @@ -15,7 +15,7 @@ }, "options": { "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "unknown": "Erro inesperado" } } diff --git a/homeassistant/components/nissan_leaf/manifest.json b/homeassistant/components/nissan_leaf/manifest.json index 87c29013544..ca932e78476 100644 --- a/homeassistant/components/nissan_leaf/manifest.json +++ b/homeassistant/components/nissan_leaf/manifest.json @@ -2,7 +2,7 @@ "domain": "nissan_leaf", "name": "Nissan Leaf", "documentation": "https://www.home-assistant.io/integrations/nissan_leaf", - "requirements": ["pycarwings2==2.13"], + "requirements": ["pycarwings2==2.14"], "codeowners": ["@filcole"], "iot_class": "cloud_polling", "loggers": ["pycarwings2"] diff --git a/homeassistant/components/nissan_leaf/sensor.py b/homeassistant/components/nissan_leaf/sensor.py index b0da5757db9..5b2f99b997b 100644 --- a/homeassistant/components/nissan_leaf/sensor.py +++ b/homeassistant/components/nissan_leaf/sensor.py @@ -6,7 +6,7 @@ import logging from voluptuous.validators import Number from homeassistant.components.sensor import SensorDeviceClass, SensorEntity -from homeassistant.const import LENGTH_KILOMETERS, LENGTH_MILES, PERCENTAGE +from homeassistant.const import PERCENTAGE, UnitOfLength from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.icon import icon_for_battery_level @@ -117,7 +117,9 @@ class LeafRangeSensor(LeafEntity, SensorEntity): return None if self.car.hass.config.units is US_CUSTOMARY_SYSTEM or self.car.force_miles: - ret = DistanceConverter.convert(ret, LENGTH_KILOMETERS, LENGTH_MILES) + ret = DistanceConverter.convert( + ret, UnitOfLength.KILOMETERS, UnitOfLength.MILES + ) return round(ret) @@ -125,5 +127,5 @@ class LeafRangeSensor(LeafEntity, SensorEntity): def native_unit_of_measurement(self) -> str: """Battery range unit.""" if self.car.hass.config.units is US_CUSTOMARY_SYSTEM or self.car.force_miles: - return LENGTH_MILES - return LENGTH_KILOMETERS + return UnitOfLength.MILES + return UnitOfLength.KILOMETERS diff --git a/homeassistant/components/nmap_tracker/__init__.py b/homeassistant/components/nmap_tracker/__init__.py index e3b83f8abc2..827fb93a012 100644 --- a/homeassistant/components/nmap_tracker/__init__.py +++ b/homeassistant/components/nmap_tracker/__init__.py @@ -343,7 +343,10 @@ class NmapDeviceScanner: return if device.first_offline + self.consider_home > now: _LOGGER.debug( - "Device %s (%s) has NOT been offline (first offline at: %s) long enough to be considered not home: %s", + ( + "Device %s (%s) has NOT been offline (first offline at: %s) long" + " enough to be considered not home: %s" + ), ipv4, formatted_mac, device.first_offline, @@ -351,7 +354,10 @@ class NmapDeviceScanner: ) return _LOGGER.debug( - "Device %s (%s) has been offline (first offline at: %s) long enough to be considered not home: %s", + ( + "Device %s (%s) has been offline (first offline at: %s) long enough to" + " be considered not home: %s" + ), ipv4, formatted_mac, device.first_offline, diff --git a/homeassistant/components/nmap_tracker/translations/de.json b/homeassistant/components/nmap_tracker/translations/de.json index 074bdac0e4a..abd1e8004f0 100644 --- a/homeassistant/components/nmap_tracker/translations/de.json +++ b/homeassistant/components/nmap_tracker/translations/de.json @@ -12,7 +12,7 @@ "exclude": "Netzwerkadressen (kommagetrennt), die von der \u00dcberpr\u00fcfung ausgeschlossen werden sollen", "home_interval": "Mindestanzahl von Minuten zwischen den Scans aktiver Ger\u00e4te (Batterie schonen)", "hosts": "Netzwerkadressen (kommagetrennt) zum Scannen", - "scan_options": "Raw konfigurierbare Scan-Optionen f\u00fcr Nmap" + "scan_options": "RAW konfigurierbare Scan-Optionen f\u00fcr Nmap" }, "description": "Konfiguriere die Hosts, die von Nmap gescannt werden sollen. Netzwerkadresse und Ausschl\u00fcsse k\u00f6nnen IP-Adressen (192.168.1.1), IP-Netzwerke (192.168.0.0/24) oder IP-Bereiche (192.168.1.0-32) sein." } @@ -30,7 +30,7 @@ "home_interval": "Mindestanzahl von Minuten zwischen den Scans aktiver Ger\u00e4te (Batterie schonen)", "hosts": "Netzwerkadressen (kommagetrennt) zum Scannen", "interval_seconds": "Scan Intervall", - "scan_options": "Raw konfigurierbare Scan-Optionen f\u00fcr Nmap" + "scan_options": "RAW konfigurierbare Scan-Optionen f\u00fcr Nmap" }, "description": "Konfiguriere die Hosts, die von Nmap gescannt werden sollen. Netzwerkadresse und Ausschl\u00fcsse k\u00f6nnen IP-Adressen (192.168.1.1), IP-Netzwerke (192.168.0.0/24) oder IP-Bereiche (192.168.1.0-32) sein." } diff --git a/homeassistant/components/nmap_tracker/translations/ko.json b/homeassistant/components/nmap_tracker/translations/ko.json new file mode 100644 index 00000000000..df4ba2bcff9 --- /dev/null +++ b/homeassistant/components/nmap_tracker/translations/ko.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "\uc704\uce58\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/nmap_tracker/translations/sk.json b/homeassistant/components/nmap_tracker/translations/sk.json index e2c968a9e58..92cab449401 100644 --- a/homeassistant/components/nmap_tracker/translations/sk.json +++ b/homeassistant/components/nmap_tracker/translations/sk.json @@ -11,8 +11,10 @@ "data": { "exclude": "Sie\u0165ov\u00e9 adresy (oddelen\u00e9 \u010diarkou), ktor\u00e9 sa maj\u00fa vyl\u00fa\u010di\u0165 z kontroly", "home_interval": "Minim\u00e1lny po\u010det min\u00fat medzi kontrolami akt\u00edvnych zariaden\u00ed (\u0161etrenie bat\u00e9rie)", - "hosts": "Sie\u0165ov\u00e9 adresy (oddelen\u00e9 \u010diarkami), ktor\u00e9 sa maj\u00fa skenova\u0165" - } + "hosts": "Sie\u0165ov\u00e9 adresy (oddelen\u00e9 \u010diarkami), ktor\u00e9 sa maj\u00fa skenova\u0165", + "scan_options": "Raw konfigurovate\u013en\u00e9 mo\u017enosti skenovania pre Nmap" + }, + "description": "Nakonfigurujte hostitelov na skenovanie Nmapom. Sie\u0165ov\u00e9 adresy a vyl\u00fa\u010denia m\u00f4\u017eu by\u0165 adresy IP (192.168.1.1), siete IP (192.168.0.0/24) alebo rozsahy IP (192.168.1.0-32)." } } }, @@ -23,12 +25,16 @@ "step": { "init": { "data": { + "consider_home": "Sekundy \u010dakania, k\u00fdm sa sledova\u010d zariadenia ozna\u010d\u00ed ako nepr\u00edtomn\u00fd po tom, \u010do ho nikto nevidel.", "exclude": "Sie\u0165ov\u00e9 adresy (oddelen\u00e9 \u010diarkou), ktor\u00e9 sa maj\u00fa vyl\u00fa\u010di\u0165 z kontroly", "home_interval": "Minim\u00e1lny po\u010det min\u00fat medzi kontrolami akt\u00edvnych zariaden\u00ed (\u0161etrenie bat\u00e9rie)", "hosts": "Sie\u0165ov\u00e9 adresy (oddelen\u00e9 \u010diarkami), ktor\u00e9 sa maj\u00fa skenova\u0165", - "interval_seconds": "Interval skenovania" - } + "interval_seconds": "Interval skenovania", + "scan_options": "Raw konfigurovate\u013en\u00e9 mo\u017enosti skenovania pre Nmap" + }, + "description": "Nakonfigurujte hostitelov na skenovanie Nmapom. Sie\u0165ov\u00e9 adresy a vyl\u00fa\u010denia m\u00f4\u017eu by\u0165 adresy IP (192.168.1.1), siete IP (192.168.0.0/24) alebo rozsahy IP (192.168.1.0-32)." } } - } + }, + "title": "Nmap Tracker" } \ No newline at end of file diff --git a/homeassistant/components/nmbs/sensor.py b/homeassistant/components/nmbs/sensor.py index 51d2ae3c082..b9a216875f4 100644 --- a/homeassistant/components/nmbs/sensor.py +++ b/homeassistant/components/nmbs/sensor.py @@ -12,7 +12,7 @@ from homeassistant.const import ( ATTR_LONGITUDE, CONF_NAME, CONF_SHOW_ON_MAP, - TIME_MINUTES, + UnitOfTime, ) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv @@ -174,10 +174,10 @@ class NMBSLiveBoard(SensorEntity): class NMBSSensor(SensorEntity): - """Get the the total travel time for a given connection.""" + """Get the total travel time for a given connection.""" _attr_attribution = "https://api.irail.be/" - _attr_native_unit_of_measurement = TIME_MINUTES + _attr_native_unit_of_measurement = UnitOfTime.MINUTES def __init__( self, api_client, name, show_on_map, station_from, station_to, excl_vias @@ -296,8 +296,7 @@ class NMBSSensor(SensorEntity): if self._excl_vias and self.is_via_connection: _LOGGER.debug( - "Skipping update of NMBSSensor \ - because this connection is a via" + "Skipping update of NMBSSensor because this connection is a via" ) return diff --git a/homeassistant/components/nobo_hub/climate.py b/homeassistant/components/nobo_hub/climate.py index d138788fba0..6e4cf13c986 100644 --- a/homeassistant/components/nobo_hub/climate.py +++ b/homeassistant/components/nobo_hub/climate.py @@ -19,22 +19,19 @@ from homeassistant.components.climate import ( from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( ATTR_IDENTIFIERS, - ATTR_MODE, ATTR_NAME, ATTR_SUGGESTED_AREA, ATTR_VIA_DEVICE, PRECISION_TENTHS, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.util import dt from .const import ( - ATTR_OVERRIDE_ALLOWED, ATTR_SERIAL, - ATTR_TARGET_ID, - ATTR_TARGET_TYPE, ATTR_TEMP_COMFORT_C, ATTR_TEMP_ECO_C, CONF_OVERRIDE_TYPE, @@ -84,7 +81,7 @@ class NoboZone(ClimateEntity): _attr_precision = PRECISION_TENTHS _attr_preset_modes = PRESET_MODES _attr_supported_features = SUPPORT_FLAGS - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS _attr_target_temperature_step = 1 # Need to poll to get preset change when in HVACMode.AUTO, so can't set _attr_should_poll = False @@ -118,18 +115,16 @@ class NoboZone(ClimateEntity): """Set new target HVAC mode, if it's supported.""" if hvac_mode not in self.hvac_modes: raise ValueError( - f"Zone {self._id} '{self._attr_name}' called with unsupported HVAC mode '{hvac_mode}'" + f"Zone {self._id} '{self._attr_name}' called with unsupported HVAC mode" + f" '{hvac_mode}'" ) if hvac_mode == HVACMode.AUTO: await self.async_set_preset_mode(PRESET_NONE) elif hvac_mode == HVACMode.HEAT: await self.async_set_preset_mode(PRESET_COMFORT) - self._attr_hvac_mode = hvac_mode async def async_set_preset_mode(self, preset_mode: str) -> None: """Set new zone override.""" - if self._nobo.zones[self._id][ATTR_OVERRIDE_ALLOWED] != "1": - return if preset_mode == PRESET_ECO: mode = nobo.API.OVERRIDE_MODE_ECO elif preset_mode == PRESET_AWAY: @@ -163,7 +158,7 @@ class NoboZone(ClimateEntity): @callback def _read_state(self) -> None: """Read the current state from the hub. These are only local calls.""" - state = self._nobo.get_current_zone_mode(self._id) + state = self._nobo.get_current_zone_mode(self._id, dt.now()) self._attr_hvac_mode = HVACMode.AUTO self._attr_preset_mode = PRESET_NONE @@ -176,17 +171,8 @@ class NoboZone(ClimateEntity): elif state == nobo.API.NAME_COMFORT: self._attr_preset_mode = PRESET_COMFORT - if self._nobo.zones[self._id][ATTR_OVERRIDE_ALLOWED] == "1": - for override in self._nobo.overrides: - if self._nobo.overrides[override][ATTR_MODE] == "0": - continue # "normal" overrides - if ( - self._nobo.overrides[override][ATTR_TARGET_TYPE] - == nobo.API.OVERRIDE_TARGET_ZONE - and self._nobo.overrides[override][ATTR_TARGET_ID] == self._id - ): - self._attr_hvac_mode = HVACMode.HEAT - break + if self._nobo.get_zone_override_mode(self._id) != nobo.API.NAME_NORMAL: + self._attr_hvac_mode = HVACMode.HEAT current_temperature = self._nobo.get_current_zone_temperature(self._id) self._attr_current_temperature = ( diff --git a/homeassistant/components/nobo_hub/const.py b/homeassistant/components/nobo_hub/const.py index ff0f25cfec3..fdffb977201 100644 --- a/homeassistant/components/nobo_hub/const.py +++ b/homeassistant/components/nobo_hub/const.py @@ -14,7 +14,4 @@ ATTR_SOFTWARE_VERSION = "software_version" ATTR_SERIAL = "serial" ATTR_TEMP_COMFORT_C = "temp_comfort_c" ATTR_TEMP_ECO_C = "temp_eco_c" -ATTR_OVERRIDE_ALLOWED = "override_allowed" -ATTR_TARGET_TYPE = "target_type" -ATTR_TARGET_ID = "target_id" ATTR_ZONE_ID = "zone_id" diff --git a/homeassistant/components/nobo_hub/sensor.py b/homeassistant/components/nobo_hub/sensor.py index fe33c6ee83e..3bb1fa373a5 100644 --- a/homeassistant/components/nobo_hub/sensor.py +++ b/homeassistant/components/nobo_hub/sensor.py @@ -16,7 +16,7 @@ from homeassistant.const import ( ATTR_NAME, ATTR_SUGGESTED_AREA, ATTR_VIA_DEVICE, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import DeviceInfo @@ -47,7 +47,7 @@ class NoboTemperatureSensor(SensorEntity): """A Nobø device with a temperature sensor.""" _attr_device_class = SensorDeviceClass.TEMPERATURE - _attr_native_unit_of_measurement = TEMP_CELSIUS + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS _attr_state_class = SensorStateClass.MEASUREMENT _attr_should_poll = False diff --git a/homeassistant/components/nobo_hub/translations/ko.json b/homeassistant/components/nobo_hub/translations/ko.json new file mode 100644 index 00000000000..eeb923a945a --- /dev/null +++ b/homeassistant/components/nobo_hub/translations/ko.json @@ -0,0 +1,17 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "manual": { + "data": { + "ip_address": "IP \uc8fc\uc18c" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/nobo_hub/translations/pt.json b/homeassistant/components/nobo_hub/translations/pt.json index 0749b1fe831..ee9ce63a738 100644 --- a/homeassistant/components/nobo_hub/translations/pt.json +++ b/homeassistant/components/nobo_hub/translations/pt.json @@ -1,13 +1,18 @@ { "config": { + "abort": { + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" + }, "error": { "cannot_connect": "Falha ao conectar - verifique o n\u00famero de s\u00e9rie", "invalid_ip": "Endere\u00e7o IP inv\u00e1lido", - "invalid_serial": "N\u00famero de s\u00e9rie inv\u00e1lido" + "invalid_serial": "N\u00famero de s\u00e9rie inv\u00e1lido", + "unknown": "Erro inesperado" }, "step": { "manual": { "data": { + "ip_address": "Endere\u00e7o IP", "serial": "N\u00famero de s\u00e9rie (12 d\u00edgitos)" }, "description": "Configure um Nob\u00f8 Ecohub n\u00e3o descoberto em sua rede local. Se o seu hub estiver em outra rede, voc\u00ea ainda poder\u00e1 se conectar a ele digitando o n\u00famero de s\u00e9rie completo (12 d\u00edgitos) e seu endere\u00e7o IP." diff --git a/homeassistant/components/nobo_hub/translations/sk.json b/homeassistant/components/nobo_hub/translations/sk.json index f862703bd72..b631cfdab5e 100644 --- a/homeassistant/components/nobo_hub/translations/sk.json +++ b/homeassistant/components/nobo_hub/translations/sk.json @@ -14,7 +14,30 @@ "data": { "ip_address": "IP adresa", "serial": "S\u00e9riov\u00e9 \u010d\u00edslo (12 \u010d\u00edslic)" - } + }, + "description": "Nakonfigurujte Nob\u00f8 Ecohub, ktor\u00fd nebol n\u00e1jden\u00fd vo va\u0161ej lok\u00e1lnej sieti. Ak je v\u00e1\u0161 rozbo\u010dova\u010d v inej sieti, st\u00e1le sa k nemu m\u00f4\u017eete pripoji\u0165 zadan\u00edm \u00fapln\u00e9ho s\u00e9riov\u00e9ho \u010d\u00edsla (12 \u010d\u00edslic) a jeho IP adresy." + }, + "selected": { + "data": { + "serial_suffix": "Pr\u00edpona s\u00e9riov\u00e9ho \u010d\u00edsla (3 \u010d\u00edslice)" + }, + "description": "Konfigur\u00e1cia {hub}. Ak sa chcete pripoji\u0165 k rozbo\u010dova\u010du, mus\u00edte zada\u0165 posledn\u00e9 3 \u010d\u00edslice s\u00e9riov\u00e9ho \u010d\u00edsla hubu." + }, + "user": { + "data": { + "device": "Objaven\u00e9 huby" + }, + "description": "Na konfigur\u00e1ciu vyberte Nob\u00f8 Ecohub." + } + } + }, + "options": { + "step": { + "init": { + "data": { + "override_type": "Typ prep\u00edsania" + }, + "description": "Vyberte typ prepisu \"Teraz\", aby sa prepis dokon\u010dil pri \u010fal\u0161ej zmene profilu v priebehu t\u00fd\u017ed\u0148a." } } } diff --git a/homeassistant/components/notify/legacy.py b/homeassistant/components/notify/legacy.py index 35c1bbe65bf..a5d58451c5e 100644 --- a/homeassistant/components/notify/legacy.py +++ b/homeassistant/components/notify/legacy.py @@ -151,8 +151,8 @@ def check_templates_warn(hass: HomeAssistant, tpl: template.Template) -> None: hass.data["notify_template_warned"] = True LOGGER.warning( - "Passing templates to notify service is deprecated and will be removed in 2021.12. " - "Automations and scripts handle templates automatically" + "Passing templates to notify service is deprecated and will be removed in" + " 2021.12. Automations and scripts handle templates automatically" ) @@ -301,7 +301,10 @@ class BaseNotificationService: # Register the service description service_desc = { CONF_NAME: f"Send a notification via {target_name}", - CONF_DESCRIPTION: f"Sends a notification message using the {target_name} integration.", + CONF_DESCRIPTION: ( + "Sends a notification message using the" + f" {target_name} integration." + ), CONF_FIELDS: self.services_dict[SERVICE_NOTIFY][CONF_FIELDS], } async_set_service_schema(self.hass, DOMAIN, target_name, service_desc) @@ -326,7 +329,9 @@ class BaseNotificationService: # Register the service description service_desc = { CONF_NAME: f"Send a notification with {self._service_name}", - CONF_DESCRIPTION: f"Sends a notification message using the {self._service_name} service.", + CONF_DESCRIPTION: ( + f"Sends a notification message using the {self._service_name} service." + ), CONF_FIELDS: self.services_dict[SERVICE_NOTIFY][CONF_FIELDS], } async_set_service_schema(self.hass, DOMAIN, self._service_name, service_desc) diff --git a/homeassistant/components/notion/config_flow.py b/homeassistant/components/notion/config_flow.py index 917c0f8ebb9..1e4adab2910 100644 --- a/homeassistant/components/notion/config_flow.py +++ b/homeassistant/components/notion/config_flow.py @@ -2,14 +2,16 @@ from __future__ import annotations from collections.abc import Mapping -from typing import TYPE_CHECKING, Any +from typing import Any from aionotion import async_get_client from aionotion.errors import InvalidCredentialsError, NotionError import voluptuous as vol from homeassistant import config_entries +from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_PASSWORD, CONF_USERNAME +from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers import aiohttp_client @@ -21,78 +23,84 @@ AUTH_SCHEMA = vol.Schema( vol.Required(CONF_PASSWORD): str, } ) -RE_AUTH_SCHEMA = vol.Schema( +REAUTH_SCHEMA = vol.Schema( { vol.Required(CONF_PASSWORD): str, } ) +async def async_validate_credentials( + hass: HomeAssistant, username: str, password: str +) -> dict[str, Any]: + """Validate a Notion username and password (returning any errors).""" + session = aiohttp_client.async_get_clientsession(hass) + errors = {} + + try: + await async_get_client(username, password, session=session) + except InvalidCredentialsError: + errors["base"] = "invalid_auth" + except NotionError as err: + LOGGER.error("Unknown Notion error while validation credentials: %s", err) + errors["base"] = "unknown" + except Exception as err: # pylint: disable=broad-except + LOGGER.exception("Unknown error while validation credentials: %s", err) + errors["base"] = "unknown" + + return errors + + class NotionFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): """Handle a Notion config flow.""" VERSION = 1 def __init__(self) -> None: - """Initialize the config flow.""" - self._password: str | None = None - self._username: str | None = None - - async def _async_verify(self, step_id: str, schema: vol.Schema) -> FlowResult: - """Attempt to authenticate the provided credentials.""" - if TYPE_CHECKING: - assert self._username - assert self._password - - errors = {} - session = aiohttp_client.async_get_clientsession(self.hass) - - try: - await async_get_client(self._username, self._password, session=session) - except InvalidCredentialsError: - errors["base"] = "invalid_auth" - except NotionError as err: - LOGGER.error("Unknown Notion error: %s", err) - errors["base"] = "unknown" - - if errors: - return self.async_show_form( - step_id=step_id, - data_schema=schema, - errors=errors, - description_placeholders={CONF_USERNAME: self._username}, - ) - - data = {CONF_USERNAME: self._username, CONF_PASSWORD: self._password} - - if existing_entry := await self.async_set_unique_id(self._username): - self.hass.config_entries.async_update_entry(existing_entry, data=data) - self.hass.async_create_task( - self.hass.config_entries.async_reload(existing_entry.entry_id) - ) - return self.async_abort(reason="reauth_successful") - - return self.async_create_entry(title=self._username, data=data) + """Initialize.""" + self._reauth_entry: ConfigEntry | None = None async def async_step_reauth(self, entry_data: Mapping[str, Any]) -> FlowResult: """Handle configuration by re-auth.""" - self._username = entry_data[CONF_USERNAME] + self._reauth_entry = self.hass.config_entries.async_get_entry( + self.context["entry_id"] + ) return await self.async_step_reauth_confirm() async def async_step_reauth_confirm( self, user_input: dict[str, Any] | None = None ) -> FlowResult: """Handle re-auth completion.""" + assert self._reauth_entry + if not user_input: return self.async_show_form( step_id="reauth_confirm", - data_schema=RE_AUTH_SCHEMA, - description_placeholders={CONF_USERNAME: self._username}, + data_schema=REAUTH_SCHEMA, + description_placeholders={ + CONF_USERNAME: self._reauth_entry.data[CONF_USERNAME] + }, ) - self._password = user_input[CONF_PASSWORD] + if errors := await async_validate_credentials( + self.hass, self._reauth_entry.data[CONF_USERNAME], user_input[CONF_PASSWORD] + ): + return self.async_show_form( + step_id="reauth_confirm", + data_schema=REAUTH_SCHEMA, + errors=errors, + description_placeholders={ + CONF_USERNAME: self._reauth_entry.data[CONF_USERNAME] + }, + ) - return await self._async_verify("reauth_confirm", RE_AUTH_SCHEMA) + self.hass.config_entries.async_update_entry( + self._reauth_entry, data=self._reauth_entry.data | user_input + ) + self.hass.async_create_task( + self.hass.config_entries.async_reload(self._reauth_entry.entry_id) + ) + return self.async_abort(reason="reauth_successful") async def async_step_user( self, user_input: dict[str, str] | None = None @@ -104,7 +112,13 @@ class NotionFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): await self.async_set_unique_id(user_input[CONF_USERNAME]) self._abort_if_unique_id_configured() - self._username = user_input[CONF_USERNAME] - self._password = user_input[CONF_PASSWORD] + if errors := await async_validate_credentials( + self.hass, user_input[CONF_USERNAME], user_input[CONF_PASSWORD] + ): + return self.async_show_form( + step_id="user", + data_schema=AUTH_SCHEMA, + errors=errors, + ) - return await self._async_verify("user", AUTH_SCHEMA) + return self.async_create_entry(title=user_input[CONF_USERNAME], data=user_input) diff --git a/homeassistant/components/notion/sensor.py b/homeassistant/components/notion/sensor.py index ff7ac7d23a6..7881780c4ed 100644 --- a/homeassistant/components/notion/sensor.py +++ b/homeassistant/components/notion/sensor.py @@ -6,7 +6,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import TEMP_CELSIUS +from homeassistant.const import UnitOfTemperature from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -18,7 +18,7 @@ SENSOR_DESCRIPTIONS = ( key=SENSOR_TEMPERATURE, name="Temperature", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, ), ) diff --git a/homeassistant/components/notion/translations/ko.json b/homeassistant/components/notion/translations/ko.json index 991553a1531..1e51f462e9f 100644 --- a/homeassistant/components/notion/translations/ko.json +++ b/homeassistant/components/notion/translations/ko.json @@ -1,14 +1,20 @@ { "config": { "abort": { - "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" }, "error": { - "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" }, "step": { "reauth_confirm": { - "description": "{username} \uc758 \ube44\ubc00\ubc88\ud638\ub97c \ub2e4\uc2dc \uc785\ub825\ud558\uc138\uc694." + "data": { + "password": "\ube44\ubc00\ubc88\ud638" + }, + "description": "{username} \uc758 \ube44\ubc00\ubc88\ud638\ub97c \ub2e4\uc2dc \uc785\ub825\ud558\uc138\uc694.", + "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" }, "user": { "data": { diff --git a/homeassistant/components/nsw_fuel_station/sensor.py b/homeassistant/components/nsw_fuel_station/sensor.py index 0bc2cf12be2..6ebbccc4466 100644 --- a/homeassistant/components/nsw_fuel_station/sensor.py +++ b/homeassistant/components/nsw_fuel_station/sensor.py @@ -6,7 +6,7 @@ import logging import voluptuous as vol from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity -from homeassistant.const import CURRENCY_CENT, VOLUME_LITERS +from homeassistant.const import CURRENCY_CENT, UnitOfVolume from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -127,7 +127,7 @@ class StationPriceSensor( @property def native_unit_of_measurement(self) -> str: """Return the units of measurement.""" - return f"{CURRENCY_CENT}/{VOLUME_LITERS}" + return f"{CURRENCY_CENT}/{UnitOfVolume.LITERS}" def _get_station_name(self): default_name = f"station {self._station_id}" diff --git a/homeassistant/components/nsw_rural_fire_service_feed/geo_location.py b/homeassistant/components/nsw_rural_fire_service_feed/geo_location.py index 553f73a128e..3eb598ffd3e 100644 --- a/homeassistant/components/nsw_rural_fire_service_feed/geo_location.py +++ b/homeassistant/components/nsw_rural_fire_service_feed/geo_location.py @@ -21,7 +21,7 @@ from homeassistant.const import ( CONF_SCAN_INTERVAL, EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, - LENGTH_KILOMETERS, + UnitOfLength, ) from homeassistant.core import Event, HomeAssistant, callback from homeassistant.helpers import aiohttp_client, config_validation as cv @@ -181,7 +181,7 @@ class NswRuralFireServiceLocationEvent(GeolocationEvent): _attr_should_poll = False _attr_source = SOURCE - _attr_unit_of_measurement = LENGTH_KILOMETERS + _attr_unit_of_measurement = UnitOfLength.KILOMETERS def __init__( self, feed_manager: NswRuralFireServiceFeedEntityManager, external_id: str diff --git a/homeassistant/components/nuheat/climate.py b/homeassistant/components/nuheat/climate.py index 0c053a8c67f..aa692153215 100644 --- a/homeassistant/components/nuheat/climate.py +++ b/homeassistant/components/nuheat/climate.py @@ -18,7 +18,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS, TEMP_FAHRENHEIT +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import event as event_helper from homeassistant.helpers.entity import DeviceInfo @@ -93,9 +93,9 @@ class NuHeatThermostat(CoordinatorEntity, ClimateEntity): def temperature_unit(self) -> str: """Return the unit of measurement.""" if self._temperature_unit == "C": - return TEMP_CELSIUS + return UnitOfTemperature.CELSIUS - return TEMP_FAHRENHEIT + return UnitOfTemperature.FAHRENHEIT @property def current_temperature(self): diff --git a/homeassistant/components/nuheat/translations/pt.json b/homeassistant/components/nuheat/translations/pt.json index 16a664b4225..995142a7914 100644 --- a/homeassistant/components/nuheat/translations/pt.json +++ b/homeassistant/components/nuheat/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, diff --git a/homeassistant/components/nuheat/translations/sk.json b/homeassistant/components/nuheat/translations/sk.json index 55ec6c3046c..d96f71c73b2 100644 --- a/homeassistant/components/nuheat/translations/sk.json +++ b/homeassistant/components/nuheat/translations/sk.json @@ -16,6 +16,7 @@ "serial_number": "S\u00e9riov\u00e9 \u010d\u00edslo termostatu.", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" }, + "description": "Budete musie\u0165 z\u00edska\u0165 \u010d\u00edseln\u00e9 s\u00e9riov\u00e9 \u010d\u00edslo alebo ID v\u00e1\u0161ho termostatu po prihl\u00e1sen\u00ed sa na https://MyNuHeat.com a v\u00fdberom termostatu(ov).", "title": "Pripojte sa k NuHeat" } } diff --git a/homeassistant/components/nuki/translations/de.json b/homeassistant/components/nuki/translations/de.json index 2631c7b0588..0c11580b984 100644 --- a/homeassistant/components/nuki/translations/de.json +++ b/homeassistant/components/nuki/translations/de.json @@ -13,7 +13,7 @@ "data": { "token": "Zugangstoken" }, - "description": "Die Nuki-Integration muss sich bei deiner Bridge neu authentifizieren.", + "description": "Die Nuki Integration muss sich bei deiner Bridge neu authentifizieren.", "title": "Integration erneut authentifizieren" }, "user": { diff --git a/homeassistant/components/nuki/translations/pt.json b/homeassistant/components/nuki/translations/pt.json index f681da4210f..ecec847af27 100644 --- a/homeassistant/components/nuki/translations/pt.json +++ b/homeassistant/components/nuki/translations/pt.json @@ -3,7 +3,7 @@ "step": { "user": { "data": { - "host": "Servidor", + "host": "Endere\u00e7o", "port": "Porta" } } diff --git a/homeassistant/components/nuki/translations/sk.json b/homeassistant/components/nuki/translations/sk.json index bd03a6ec700..b40c809865d 100644 --- a/homeassistant/components/nuki/translations/sk.json +++ b/homeassistant/components/nuki/translations/sk.json @@ -13,6 +13,7 @@ "data": { "token": "Pr\u00edstupov\u00fd token" }, + "description": "Integr\u00e1cia Nuki sa mus\u00ed znova overi\u0165 pomocou v\u00e1\u0161ho bridga.", "title": "Znova overi\u0165 integr\u00e1ciu" }, "user": { diff --git a/homeassistant/components/numato/__init__.py b/homeassistant/components/numato/__init__.py index e2a5400e050..7a66ac55d70 100644 --- a/homeassistant/components/numato/__init__.py +++ b/homeassistant/components/numato/__init__.py @@ -209,8 +209,12 @@ class NumatoAPI: f"Port {port} is not set up for numato device {device_id}." ) msg = { - gpio.OUT: f"Trying to write to device {device_id} port {port} set up as input.", - gpio.IN: f"Trying to read from device {device_id} port {port} set up as output.", + gpio.OUT: ( + f"Trying to write to device {device_id} port {port} set up as input." + ), + gpio.IN: ( + f"Trying to read from device {device_id} port {port} set up as output." + ), } if self.ports_registered[(device_id, port)] != direction: raise gpio.NumatoGpioError(msg[direction]) diff --git a/homeassistant/components/numato/binary_sensor.py b/homeassistant/components/numato/binary_sensor.py index c326a45d462..cefec42ba3a 100644 --- a/homeassistant/components/numato/binary_sensor.py +++ b/homeassistant/components/numato/binary_sensor.py @@ -58,7 +58,10 @@ def setup_platform( except NumatoGpioError as err: _LOGGER.error( - "Failed to initialize binary sensor '%s' on Numato device %s port %s: %s", + ( + "Failed to initialize binary sensor '%s' on Numato device %s" + " port %s: %s" + ), port_name, device_id, port, diff --git a/homeassistant/components/number/__init__.py b/homeassistant/components/number/__init__.py index dfd14f5257b..de2580eab75 100644 --- a/homeassistant/components/number/__init__.py +++ b/homeassistant/components/number/__init__.py @@ -14,12 +14,7 @@ import voluptuous as vol from homeassistant.backports.enum import StrEnum from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - ATTR_MODE, - CONF_UNIT_OF_MEASUREMENT, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, -) +from homeassistant.const import ATTR_MODE, CONF_UNIT_OF_MEASUREMENT, UnitOfTemperature from homeassistant.core import HomeAssistant, ServiceCall, callback from homeassistant.helpers.config_validation import ( # noqa: F401 PLATFORM_SCHEMA, @@ -69,6 +64,12 @@ class NumberDeviceClass(StrEnum): Unit of measurement: `None` """ + ATMOSPHERIC_PRESSURE = "atmospheric_pressure" + """Atmospheric pressure. + + Unit of measurement: `UnitOfPressure` units + """ + BATTERY = "battery" """Percentage of battery that is left. @@ -90,7 +91,19 @@ class NumberDeviceClass(StrEnum): CURRENT = "current" """Current. - Unit of measurement: `A` + Unit of measurement: `A`, `mA` + """ + + DATA_RATE = "data_rate" + """Data rate. + + Unit of measurement: UnitOfDataRate + """ + + DATA_SIZE = "data_size" + """Data size. + + Unit of measurement: UnitOfInformation """ DISTANCE = "distance" @@ -116,7 +129,9 @@ class NumberDeviceClass(StrEnum): GAS = "gas" """Gas. - Unit of measurement: `m³`, `ft³` + Unit of measurement: + - SI / metric: `m³` + - USCS / imperial: `ft³`, `CCF` """ HUMIDITY = "humidity" @@ -128,7 +143,15 @@ class NumberDeviceClass(StrEnum): ILLUMINANCE = "illuminance" """Illuminance. - Unit of measurement: `lx`, `lm` + Unit of measurement: `lx` + """ + + IRRADIANCE = "irradiance" + """Irradiance. + + Unit of measurement: + - SI / metric: `W/m²` + - USCS / imperial: `BTU/(h⋅ft²)` """ MOISTURE = "moisture" @@ -202,8 +225,8 @@ class NumberDeviceClass(StrEnum): PRECIPITATION = "precipitation" """Precipitation. - Unit of measurement: - - SI / metric: `mm` + Unit of measurement: UnitOfPrecipitationDepth + - SI / metric: `cm`, `mm` - USCS / imperial: `in` """ @@ -237,6 +260,12 @@ class NumberDeviceClass(StrEnum): Unit of measurement: `dB`, `dBm` """ + SOUND_PRESSURE = "sound_pressure" + """Sound pressure. + + Unit of measurement: `dB`, `dBA` + """ + SPEED = "speed" """Generic speed. @@ -267,7 +296,7 @@ class NumberDeviceClass(StrEnum): VOLTAGE = "voltage" """Voltage. - Unit of measurement: `V` + Unit of measurement: `V`, `mV` """ VOLUME = "volume" @@ -275,7 +304,7 @@ class NumberDeviceClass(StrEnum): Unit of measurement: `VOLUME_*` units - SI / metric: `mL`, `L`, `m³` - - USCS / imperial: `fl. oz.`, `ft³`, `gal` (warning: volumes expressed in + - USCS / imperial: `ft³`, `CCF`, `fl. oz.`, `gal` (warning: volumes expressed in USCS/imperial units are currently assumed to be US volumes) """ @@ -284,7 +313,7 @@ class NumberDeviceClass(StrEnum): Unit of measurement: - SI / metric: `m³`, `L` - - USCS / imperial: `ft³`, `gal` (warning: volumes expressed in + - USCS / imperial: `ft³`, `CCF`, `gal` (warning: volumes expressed in USCS/imperial units are currently assumed to be US volumes) """ @@ -347,7 +376,8 @@ async def async_set_value(entity: NumberEntity, service_call: ServiceCall) -> No value = service_call.data["value"] if value < entity.min_value or value > entity.max_value: raise ValueError( - f"Value {value} for {entity.name} is outside valid range {entity.min_value} - {entity.max_value}" + f"Value {value} for {entity.name} is outside valid range" + f" {entity.min_value} - {entity.max_value}" ) try: native_value = entity.convert_to_native_value(value) @@ -376,6 +406,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: class NumberEntityDescription(EntityDescription): """A class that describes number entities.""" + device_class: NumberDeviceClass | None = None max_value: None = None min_value: None = None native_max_value: float | None = None @@ -406,9 +437,11 @@ class NumberEntityDescription(EntityDescription): "https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue" ) _LOGGER.warning( - "%s is setting deprecated attributes on an instance of " - "NumberEntityDescription, this is not valid and will be unsupported " - "from Home Assistant 2022.10. Please %s", + ( + "%s is setting deprecated attributes on an instance of" + " NumberEntityDescription, this is not valid and will be" + " unsupported from Home Assistant 2022.10. Please %s" + ), module.__name__ if module else self.__class__.__name__, report_issue, ) @@ -437,6 +470,7 @@ class NumberEntity(Entity): """Representation of a Number entity.""" entity_description: NumberEntityDescription + _attr_device_class: NumberDeviceClass | None _attr_max_value: None _attr_min_value: None _attr_mode: NumberMode = NumberMode.AUTO @@ -476,9 +510,11 @@ class NumberEntity(Entity): "https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue" ) _LOGGER.warning( - "%s::%s is overriding deprecated methods on an instance of " - "NumberEntity, this is not valid and will be unsupported " - "from Home Assistant 2022.10. Please %s", + ( + "%s::%s is overriding deprecated methods on an instance of " + "NumberEntity, this is not valid and will be unsupported " + "from Home Assistant 2022.10. Please %s" + ), cls.__module__, cls.__name__, report_issue, @@ -501,6 +537,15 @@ class NumberEntity(Entity): ATTR_MODE: self.mode, } + @property + def device_class(self) -> NumberDeviceClass | None: + """Return the class of this entity.""" + if hasattr(self, "_attr_device_class"): + return self._attr_device_class + if hasattr(self, "entity_description"): + return self.entity_description.device_class + return None + @property def native_min_value(self) -> float: """Return the minimum value.""" @@ -629,7 +674,8 @@ class NumberEntity(Entity): if ( self.device_class == NumberDeviceClass.TEMPERATURE - and native_unit_of_measurement in (TEMP_CELSIUS, TEMP_FAHRENHEIT) + and native_unit_of_measurement + in (UnitOfTemperature.CELSIUS, UnitOfTemperature.FAHRENHEIT) ): return self.hass.config.units.temperature_unit @@ -731,8 +777,10 @@ class NumberEntity(Entity): self._deprecated_number_entity_reported = True report_issue = self._suggest_report_issue() _LOGGER.warning( - "Entity %s (%s) is using deprecated NumberEntity features which will " - "be unsupported from Home Assistant Core 2022.10, please %s", + ( + "Entity %s (%s) is using deprecated NumberEntity features which" + " will be unsupported from Home Assistant Core 2022.10, please %s" + ), self.entity_id, type(self), report_issue, diff --git a/homeassistant/components/number/translations/sk.json b/homeassistant/components/number/translations/sk.json index 641f7658bb2..0cf0d050be3 100644 --- a/homeassistant/components/number/translations/sk.json +++ b/homeassistant/components/number/translations/sk.json @@ -1,3 +1,8 @@ { + "device_automation": { + "action_type": { + "set_value": "Nastavi\u0165 hodnotu pre {entity_name}" + } + }, "title": "\u010c\u00edslo" } \ No newline at end of file diff --git a/homeassistant/components/nut/const.py b/homeassistant/components/nut/const.py index a8591349b56..a96b39e6d78 100644 --- a/homeassistant/components/nut/const.py +++ b/homeassistant/components/nut/const.py @@ -1,26 +1,7 @@ """The nut component.""" - from __future__ import annotations -from typing import Final - -from homeassistant.components.sensor import ( - SensorDeviceClass, - SensorEntityDescription, - SensorStateClass, -) -from homeassistant.const import ( - ELECTRIC_CURRENT_AMPERE, - ELECTRIC_POTENTIAL_VOLT, - FREQUENCY_HERTZ, - PERCENTAGE, - POWER_VOLT_AMPERE, - POWER_WATT, - TEMP_CELSIUS, - TIME_SECONDS, - Platform, -) -from homeassistant.helpers.entity import EntityCategory +from homeassistant.const import Platform DOMAIN = "nut" @@ -39,538 +20,6 @@ DEFAULT_SCAN_INTERVAL = 60 PYNUT_DATA = "data" PYNUT_UNIQUE_ID = "unique_id" -SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = { - "ups.status.display": SensorEntityDescription( - key="ups.status.display", - name="Status", - icon="mdi:information-outline", - ), - "ups.status": SensorEntityDescription( - key="ups.status", - name="Status Data", - icon="mdi:information-outline", - ), - "ups.alarm": SensorEntityDescription( - key="ups.alarm", - name="Alarms", - icon="mdi:alarm", - ), - "ups.temperature": SensorEntityDescription( - key="ups.temperature", - name="UPS Temperature", - native_unit_of_measurement=TEMP_CELSIUS, - device_class=SensorDeviceClass.TEMPERATURE, - state_class=SensorStateClass.MEASUREMENT, - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "ups.load": SensorEntityDescription( - key="ups.load", - name="Load", - native_unit_of_measurement=PERCENTAGE, - icon="mdi:gauge", - state_class=SensorStateClass.MEASUREMENT, - ), - "ups.load.high": SensorEntityDescription( - key="ups.load.high", - name="Overload Setting", - native_unit_of_measurement=PERCENTAGE, - icon="mdi:gauge", - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "ups.id": SensorEntityDescription( - key="ups.id", - name="System identifier", - icon="mdi:information-outline", - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "ups.delay.start": SensorEntityDescription( - key="ups.delay.start", - name="Load Restart Delay", - native_unit_of_measurement=TIME_SECONDS, - device_class=SensorDeviceClass.DURATION, - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "ups.delay.reboot": SensorEntityDescription( - key="ups.delay.reboot", - name="UPS Reboot Delay", - native_unit_of_measurement=TIME_SECONDS, - device_class=SensorDeviceClass.DURATION, - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "ups.delay.shutdown": SensorEntityDescription( - key="ups.delay.shutdown", - name="UPS Shutdown Delay", - native_unit_of_measurement=TIME_SECONDS, - device_class=SensorDeviceClass.DURATION, - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "ups.timer.start": SensorEntityDescription( - key="ups.timer.start", - name="Load Start Timer", - native_unit_of_measurement=TIME_SECONDS, - device_class=SensorDeviceClass.DURATION, - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "ups.timer.reboot": SensorEntityDescription( - key="ups.timer.reboot", - name="Load Reboot Timer", - native_unit_of_measurement=TIME_SECONDS, - device_class=SensorDeviceClass.DURATION, - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "ups.timer.shutdown": SensorEntityDescription( - key="ups.timer.shutdown", - name="Load Shutdown Timer", - native_unit_of_measurement=TIME_SECONDS, - device_class=SensorDeviceClass.DURATION, - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "ups.test.interval": SensorEntityDescription( - key="ups.test.interval", - name="Self-Test Interval", - native_unit_of_measurement=TIME_SECONDS, - device_class=SensorDeviceClass.DURATION, - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "ups.test.result": SensorEntityDescription( - key="ups.test.result", - name="Self-Test Result", - icon="mdi:information-outline", - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "ups.test.date": SensorEntityDescription( - key="ups.test.date", - name="Self-Test Date", - icon="mdi:calendar", - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "ups.display.language": SensorEntityDescription( - key="ups.display.language", - name="Language", - icon="mdi:information-outline", - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "ups.contacts": SensorEntityDescription( - key="ups.contacts", - name="External Contacts", - icon="mdi:information-outline", - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "ups.efficiency": SensorEntityDescription( - key="ups.efficiency", - name="Efficiency", - native_unit_of_measurement=PERCENTAGE, - icon="mdi:gauge", - state_class=SensorStateClass.MEASUREMENT, - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "ups.power": SensorEntityDescription( - key="ups.power", - name="Current Apparent Power", - native_unit_of_measurement=POWER_VOLT_AMPERE, - icon="mdi:flash", - state_class=SensorStateClass.MEASUREMENT, - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "ups.power.nominal": SensorEntityDescription( - key="ups.power.nominal", - name="Nominal Power", - native_unit_of_measurement=POWER_VOLT_AMPERE, - icon="mdi:flash", - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "ups.realpower": SensorEntityDescription( - key="ups.realpower", - name="Current Real Power", - native_unit_of_measurement=POWER_WATT, - device_class=SensorDeviceClass.POWER, - state_class=SensorStateClass.MEASUREMENT, - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "ups.realpower.nominal": SensorEntityDescription( - key="ups.realpower.nominal", - name="Nominal Real Power", - native_unit_of_measurement=POWER_WATT, - device_class=SensorDeviceClass.POWER, - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "ups.beeper.status": SensorEntityDescription( - key="ups.beeper.status", - name="Beeper Status", - icon="mdi:information-outline", - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "ups.type": SensorEntityDescription( - key="ups.type", - name="UPS Type", - icon="mdi:information-outline", - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "ups.watchdog.status": SensorEntityDescription( - key="ups.watchdog.status", - name="Watchdog Status", - icon="mdi:information-outline", - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "ups.start.auto": SensorEntityDescription( - key="ups.start.auto", - name="Start on AC", - icon="mdi:information-outline", - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "ups.start.battery": SensorEntityDescription( - key="ups.start.battery", - name="Start on Battery", - icon="mdi:information-outline", - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "ups.start.reboot": SensorEntityDescription( - key="ups.start.reboot", - name="Reboot on Battery", - icon="mdi:information-outline", - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "ups.shutdown": SensorEntityDescription( - key="ups.shutdown", - name="Shutdown Ability", - icon="mdi:information-outline", - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "battery.charge": SensorEntityDescription( - key="battery.charge", - name="Battery Charge", - native_unit_of_measurement=PERCENTAGE, - device_class=SensorDeviceClass.BATTERY, - state_class=SensorStateClass.MEASUREMENT, - ), - "battery.charge.low": SensorEntityDescription( - key="battery.charge.low", - name="Low Battery Setpoint", - native_unit_of_measurement=PERCENTAGE, - icon="mdi:gauge", - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "battery.charge.restart": SensorEntityDescription( - key="battery.charge.restart", - name="Minimum Battery to Start", - native_unit_of_measurement=PERCENTAGE, - icon="mdi:gauge", - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "battery.charge.warning": SensorEntityDescription( - key="battery.charge.warning", - name="Warning Battery Setpoint", - native_unit_of_measurement=PERCENTAGE, - icon="mdi:gauge", - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "battery.charger.status": SensorEntityDescription( - key="battery.charger.status", - name="Charging Status", - icon="mdi:information-outline", - ), - "battery.voltage": SensorEntityDescription( - key="battery.voltage", - name="Battery Voltage", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, - device_class=SensorDeviceClass.VOLTAGE, - state_class=SensorStateClass.MEASUREMENT, - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "battery.voltage.nominal": SensorEntityDescription( - key="battery.voltage.nominal", - name="Nominal Battery Voltage", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, - device_class=SensorDeviceClass.VOLTAGE, - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "battery.voltage.low": SensorEntityDescription( - key="battery.voltage.low", - name="Low Battery Voltage", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, - device_class=SensorDeviceClass.VOLTAGE, - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "battery.voltage.high": SensorEntityDescription( - key="battery.voltage.high", - name="High Battery Voltage", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, - device_class=SensorDeviceClass.VOLTAGE, - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "battery.capacity": SensorEntityDescription( - key="battery.capacity", - name="Battery Capacity", - native_unit_of_measurement="Ah", - icon="mdi:flash", - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "battery.current": SensorEntityDescription( - key="battery.current", - name="Battery Current", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, - icon="mdi:flash", - state_class=SensorStateClass.MEASUREMENT, - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "battery.current.total": SensorEntityDescription( - key="battery.current.total", - name="Total Battery Current", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, - icon="mdi:flash", - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "battery.temperature": SensorEntityDescription( - key="battery.temperature", - name="Battery Temperature", - native_unit_of_measurement=TEMP_CELSIUS, - device_class=SensorDeviceClass.TEMPERATURE, - state_class=SensorStateClass.MEASUREMENT, - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "battery.runtime": SensorEntityDescription( - key="battery.runtime", - name="Battery Runtime", - native_unit_of_measurement=TIME_SECONDS, - device_class=SensorDeviceClass.DURATION, - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "battery.runtime.low": SensorEntityDescription( - key="battery.runtime.low", - name="Low Battery Runtime", - native_unit_of_measurement=TIME_SECONDS, - device_class=SensorDeviceClass.DURATION, - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "battery.runtime.restart": SensorEntityDescription( - key="battery.runtime.restart", - name="Minimum Battery Runtime to Start", - native_unit_of_measurement=TIME_SECONDS, - device_class=SensorDeviceClass.DURATION, - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "battery.alarm.threshold": SensorEntityDescription( - key="battery.alarm.threshold", - name="Battery Alarm Threshold", - icon="mdi:information-outline", - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "battery.date": SensorEntityDescription( - key="battery.date", - name="Battery Date", - icon="mdi:calendar", - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "battery.mfr.date": SensorEntityDescription( - key="battery.mfr.date", - name="Battery Manuf. Date", - icon="mdi:calendar", - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "battery.packs": SensorEntityDescription( - key="battery.packs", - name="Number of Batteries", - icon="mdi:information-outline", - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "battery.packs.bad": SensorEntityDescription( - key="battery.packs.bad", - name="Number of Bad Batteries", - icon="mdi:information-outline", - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "battery.type": SensorEntityDescription( - key="battery.type", - name="Battery Chemistry", - icon="mdi:information-outline", - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "input.sensitivity": SensorEntityDescription( - key="input.sensitivity", - name="Input Power Sensitivity", - icon="mdi:information-outline", - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "input.transfer.low": SensorEntityDescription( - key="input.transfer.low", - name="Low Voltage Transfer", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, - device_class=SensorDeviceClass.VOLTAGE, - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "input.transfer.high": SensorEntityDescription( - key="input.transfer.high", - name="High Voltage Transfer", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, - device_class=SensorDeviceClass.VOLTAGE, - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "input.transfer.reason": SensorEntityDescription( - key="input.transfer.reason", - name="Voltage Transfer Reason", - icon="mdi:information-outline", - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "input.voltage": SensorEntityDescription( - key="input.voltage", - name="Input Voltage", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, - device_class=SensorDeviceClass.VOLTAGE, - state_class=SensorStateClass.MEASUREMENT, - ), - "input.voltage.nominal": SensorEntityDescription( - key="input.voltage.nominal", - name="Nominal Input Voltage", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, - device_class=SensorDeviceClass.VOLTAGE, - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "input.frequency": SensorEntityDescription( - key="input.frequency", - name="Input Line Frequency", - native_unit_of_measurement=FREQUENCY_HERTZ, - icon="mdi:flash", - state_class=SensorStateClass.MEASUREMENT, - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "input.frequency.nominal": SensorEntityDescription( - key="input.frequency.nominal", - name="Nominal Input Line Frequency", - native_unit_of_measurement=FREQUENCY_HERTZ, - icon="mdi:flash", - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "input.frequency.status": SensorEntityDescription( - key="input.frequency.status", - name="Input Frequency Status", - icon="mdi:information-outline", - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "output.current": SensorEntityDescription( - key="output.current", - name="Output Current", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, - icon="mdi:flash", - state_class=SensorStateClass.MEASUREMENT, - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "output.current.nominal": SensorEntityDescription( - key="output.current.nominal", - name="Nominal Output Current", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, - icon="mdi:flash", - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "output.voltage": SensorEntityDescription( - key="output.voltage", - name="Output Voltage", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, - device_class=SensorDeviceClass.VOLTAGE, - state_class=SensorStateClass.MEASUREMENT, - ), - "output.voltage.nominal": SensorEntityDescription( - key="output.voltage.nominal", - name="Nominal Output Voltage", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, - device_class=SensorDeviceClass.VOLTAGE, - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "output.frequency": SensorEntityDescription( - key="output.frequency", - name="Output Frequency", - native_unit_of_measurement=FREQUENCY_HERTZ, - icon="mdi:flash", - state_class=SensorStateClass.MEASUREMENT, - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "output.frequency.nominal": SensorEntityDescription( - key="output.frequency.nominal", - name="Nominal Output Frequency", - native_unit_of_measurement=FREQUENCY_HERTZ, - icon="mdi:flash", - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - ), - "ambient.humidity": SensorEntityDescription( - key="ambient.humidity", - name="Ambient Humidity", - native_unit_of_measurement=PERCENTAGE, - device_class=SensorDeviceClass.HUMIDITY, - state_class=SensorStateClass.MEASUREMENT, - ), - "ambient.temperature": SensorEntityDescription( - key="ambient.temperature", - name="Ambient Temperature", - native_unit_of_measurement=TEMP_CELSIUS, - device_class=SensorDeviceClass.TEMPERATURE, - state_class=SensorStateClass.MEASUREMENT, - ), - "watts": SensorEntityDescription( - key="watts", - name="Watts", - native_unit_of_measurement=POWER_WATT, - device_class=SensorDeviceClass.POWER, - state_class=SensorStateClass.MEASUREMENT, - ), -} STATE_TYPES = { "OL": "Online", diff --git a/homeassistant/components/nut/manifest.json b/homeassistant/components/nut/manifest.json index 4a07713fa30..1297c5cd509 100644 --- a/homeassistant/components/nut/manifest.json +++ b/homeassistant/components/nut/manifest.json @@ -1,6 +1,7 @@ { "domain": "nut", "name": "Network UPS Tools (NUT)", + "integration_type": "device", "documentation": "https://www.home-assistant.io/integrations/nut", "requirements": ["pynut2==2.1.2"], "codeowners": ["@bdraco", "@ollo69"], diff --git a/homeassistant/components/nut/sensor.py b/homeassistant/components/nut/sensor.py index 6c68f2b6c6b..9aaeb830173 100644 --- a/homeassistant/components/nut/sensor.py +++ b/homeassistant/components/nut/sensor.py @@ -3,18 +3,31 @@ from __future__ import annotations from dataclasses import asdict import logging -from typing import cast +from typing import Final, cast -from homeassistant.components.sensor import SensorEntity, SensorEntityDescription +from homeassistant.components.sensor import ( + SensorDeviceClass, + SensorEntity, + SensorEntityDescription, + SensorStateClass, +) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( ATTR_MANUFACTURER, ATTR_MODEL, ATTR_SW_VERSION, + PERCENTAGE, STATE_UNKNOWN, + UnitOfApparentPower, + UnitOfElectricCurrent, + UnitOfElectricPotential, + UnitOfFrequency, + UnitOfPower, + UnitOfTemperature, + UnitOfTime, ) from homeassistant.core import HomeAssistant -from homeassistant.helpers.entity import DeviceInfo +from homeassistant.helpers.entity import DeviceInfo, EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import ( CoordinatorEntity, @@ -29,7 +42,6 @@ from .const import ( KEY_STATUS_DISPLAY, PYNUT_DATA, PYNUT_UNIQUE_ID, - SENSOR_TYPES, STATE_TYPES, ) @@ -41,6 +53,539 @@ NUT_DEV_INFO_TO_DEV_INFO: dict[str, str] = { _LOGGER = logging.getLogger(__name__) +SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = { + "ups.status.display": SensorEntityDescription( + key="ups.status.display", + name="Status", + icon="mdi:information-outline", + ), + "ups.status": SensorEntityDescription( + key="ups.status", + name="Status Data", + icon="mdi:information-outline", + ), + "ups.alarm": SensorEntityDescription( + key="ups.alarm", + name="Alarms", + icon="mdi:alarm", + ), + "ups.temperature": SensorEntityDescription( + key="ups.temperature", + name="UPS Temperature", + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "ups.load": SensorEntityDescription( + key="ups.load", + name="Load", + native_unit_of_measurement=PERCENTAGE, + icon="mdi:gauge", + state_class=SensorStateClass.MEASUREMENT, + ), + "ups.load.high": SensorEntityDescription( + key="ups.load.high", + name="Overload Setting", + native_unit_of_measurement=PERCENTAGE, + icon="mdi:gauge", + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "ups.id": SensorEntityDescription( + key="ups.id", + name="System identifier", + icon="mdi:information-outline", + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "ups.delay.start": SensorEntityDescription( + key="ups.delay.start", + name="Load Restart Delay", + native_unit_of_measurement=UnitOfTime.SECONDS, + device_class=SensorDeviceClass.DURATION, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "ups.delay.reboot": SensorEntityDescription( + key="ups.delay.reboot", + name="UPS Reboot Delay", + native_unit_of_measurement=UnitOfTime.SECONDS, + device_class=SensorDeviceClass.DURATION, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "ups.delay.shutdown": SensorEntityDescription( + key="ups.delay.shutdown", + name="UPS Shutdown Delay", + native_unit_of_measurement=UnitOfTime.SECONDS, + device_class=SensorDeviceClass.DURATION, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "ups.timer.start": SensorEntityDescription( + key="ups.timer.start", + name="Load Start Timer", + native_unit_of_measurement=UnitOfTime.SECONDS, + device_class=SensorDeviceClass.DURATION, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "ups.timer.reboot": SensorEntityDescription( + key="ups.timer.reboot", + name="Load Reboot Timer", + native_unit_of_measurement=UnitOfTime.SECONDS, + device_class=SensorDeviceClass.DURATION, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "ups.timer.shutdown": SensorEntityDescription( + key="ups.timer.shutdown", + name="Load Shutdown Timer", + native_unit_of_measurement=UnitOfTime.SECONDS, + device_class=SensorDeviceClass.DURATION, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "ups.test.interval": SensorEntityDescription( + key="ups.test.interval", + name="Self-Test Interval", + native_unit_of_measurement=UnitOfTime.SECONDS, + device_class=SensorDeviceClass.DURATION, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "ups.test.result": SensorEntityDescription( + key="ups.test.result", + name="Self-Test Result", + icon="mdi:information-outline", + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "ups.test.date": SensorEntityDescription( + key="ups.test.date", + name="Self-Test Date", + icon="mdi:calendar", + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "ups.display.language": SensorEntityDescription( + key="ups.display.language", + name="Language", + icon="mdi:information-outline", + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "ups.contacts": SensorEntityDescription( + key="ups.contacts", + name="External Contacts", + icon="mdi:information-outline", + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "ups.efficiency": SensorEntityDescription( + key="ups.efficiency", + name="Efficiency", + native_unit_of_measurement=PERCENTAGE, + icon="mdi:gauge", + state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "ups.power": SensorEntityDescription( + key="ups.power", + name="Current Apparent Power", + native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE, + device_class=SensorDeviceClass.APPARENT_POWER, + state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "ups.power.nominal": SensorEntityDescription( + key="ups.power.nominal", + name="Nominal Power", + native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE, + device_class=SensorDeviceClass.APPARENT_POWER, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "ups.realpower": SensorEntityDescription( + key="ups.realpower", + name="Current Real Power", + native_unit_of_measurement=UnitOfPower.WATT, + device_class=SensorDeviceClass.POWER, + state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "ups.realpower.nominal": SensorEntityDescription( + key="ups.realpower.nominal", + name="Nominal Real Power", + native_unit_of_measurement=UnitOfPower.WATT, + device_class=SensorDeviceClass.POWER, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "ups.beeper.status": SensorEntityDescription( + key="ups.beeper.status", + name="Beeper Status", + icon="mdi:information-outline", + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "ups.type": SensorEntityDescription( + key="ups.type", + name="UPS Type", + icon="mdi:information-outline", + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "ups.watchdog.status": SensorEntityDescription( + key="ups.watchdog.status", + name="Watchdog Status", + icon="mdi:information-outline", + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "ups.start.auto": SensorEntityDescription( + key="ups.start.auto", + name="Start on AC", + icon="mdi:information-outline", + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "ups.start.battery": SensorEntityDescription( + key="ups.start.battery", + name="Start on Battery", + icon="mdi:information-outline", + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "ups.start.reboot": SensorEntityDescription( + key="ups.start.reboot", + name="Reboot on Battery", + icon="mdi:information-outline", + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "ups.shutdown": SensorEntityDescription( + key="ups.shutdown", + name="Shutdown Ability", + icon="mdi:information-outline", + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "battery.charge": SensorEntityDescription( + key="battery.charge", + name="Battery Charge", + native_unit_of_measurement=PERCENTAGE, + device_class=SensorDeviceClass.BATTERY, + state_class=SensorStateClass.MEASUREMENT, + ), + "battery.charge.low": SensorEntityDescription( + key="battery.charge.low", + name="Low Battery Setpoint", + native_unit_of_measurement=PERCENTAGE, + icon="mdi:gauge", + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "battery.charge.restart": SensorEntityDescription( + key="battery.charge.restart", + name="Minimum Battery to Start", + native_unit_of_measurement=PERCENTAGE, + icon="mdi:gauge", + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "battery.charge.warning": SensorEntityDescription( + key="battery.charge.warning", + name="Warning Battery Setpoint", + native_unit_of_measurement=PERCENTAGE, + icon="mdi:gauge", + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "battery.charger.status": SensorEntityDescription( + key="battery.charger.status", + name="Charging Status", + icon="mdi:information-outline", + ), + "battery.voltage": SensorEntityDescription( + key="battery.voltage", + name="Battery Voltage", + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + device_class=SensorDeviceClass.VOLTAGE, + state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "battery.voltage.nominal": SensorEntityDescription( + key="battery.voltage.nominal", + name="Nominal Battery Voltage", + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + device_class=SensorDeviceClass.VOLTAGE, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "battery.voltage.low": SensorEntityDescription( + key="battery.voltage.low", + name="Low Battery Voltage", + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + device_class=SensorDeviceClass.VOLTAGE, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "battery.voltage.high": SensorEntityDescription( + key="battery.voltage.high", + name="High Battery Voltage", + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + device_class=SensorDeviceClass.VOLTAGE, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "battery.capacity": SensorEntityDescription( + key="battery.capacity", + name="Battery Capacity", + native_unit_of_measurement="Ah", + icon="mdi:flash", + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "battery.current": SensorEntityDescription( + key="battery.current", + name="Battery Current", + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, + device_class=SensorDeviceClass.CURRENT, + state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "battery.current.total": SensorEntityDescription( + key="battery.current.total", + name="Total Battery Current", + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, + device_class=SensorDeviceClass.CURRENT, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "battery.temperature": SensorEntityDescription( + key="battery.temperature", + name="Battery Temperature", + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "battery.runtime": SensorEntityDescription( + key="battery.runtime", + name="Battery Runtime", + native_unit_of_measurement=UnitOfTime.SECONDS, + device_class=SensorDeviceClass.DURATION, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "battery.runtime.low": SensorEntityDescription( + key="battery.runtime.low", + name="Low Battery Runtime", + native_unit_of_measurement=UnitOfTime.SECONDS, + device_class=SensorDeviceClass.DURATION, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "battery.runtime.restart": SensorEntityDescription( + key="battery.runtime.restart", + name="Minimum Battery Runtime to Start", + native_unit_of_measurement=UnitOfTime.SECONDS, + device_class=SensorDeviceClass.DURATION, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "battery.alarm.threshold": SensorEntityDescription( + key="battery.alarm.threshold", + name="Battery Alarm Threshold", + icon="mdi:information-outline", + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "battery.date": SensorEntityDescription( + key="battery.date", + name="Battery Date", + icon="mdi:calendar", + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "battery.mfr.date": SensorEntityDescription( + key="battery.mfr.date", + name="Battery Manuf. Date", + icon="mdi:calendar", + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "battery.packs": SensorEntityDescription( + key="battery.packs", + name="Number of Batteries", + icon="mdi:information-outline", + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "battery.packs.bad": SensorEntityDescription( + key="battery.packs.bad", + name="Number of Bad Batteries", + icon="mdi:information-outline", + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "battery.type": SensorEntityDescription( + key="battery.type", + name="Battery Chemistry", + icon="mdi:information-outline", + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "input.sensitivity": SensorEntityDescription( + key="input.sensitivity", + name="Input Power Sensitivity", + icon="mdi:information-outline", + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "input.transfer.low": SensorEntityDescription( + key="input.transfer.low", + name="Low Voltage Transfer", + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + device_class=SensorDeviceClass.VOLTAGE, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "input.transfer.high": SensorEntityDescription( + key="input.transfer.high", + name="High Voltage Transfer", + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + device_class=SensorDeviceClass.VOLTAGE, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "input.transfer.reason": SensorEntityDescription( + key="input.transfer.reason", + name="Voltage Transfer Reason", + icon="mdi:information-outline", + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "input.voltage": SensorEntityDescription( + key="input.voltage", + name="Input Voltage", + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + device_class=SensorDeviceClass.VOLTAGE, + state_class=SensorStateClass.MEASUREMENT, + ), + "input.voltage.nominal": SensorEntityDescription( + key="input.voltage.nominal", + name="Nominal Input Voltage", + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + device_class=SensorDeviceClass.VOLTAGE, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "input.frequency": SensorEntityDescription( + key="input.frequency", + name="Input Line Frequency", + native_unit_of_measurement=UnitOfFrequency.HERTZ, + device_class=SensorDeviceClass.FREQUENCY, + state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "input.frequency.nominal": SensorEntityDescription( + key="input.frequency.nominal", + name="Nominal Input Line Frequency", + native_unit_of_measurement=UnitOfFrequency.HERTZ, + device_class=SensorDeviceClass.FREQUENCY, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "input.frequency.status": SensorEntityDescription( + key="input.frequency.status", + name="Input Frequency Status", + icon="mdi:information-outline", + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "output.current": SensorEntityDescription( + key="output.current", + name="Output Current", + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, + device_class=SensorDeviceClass.CURRENT, + state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "output.current.nominal": SensorEntityDescription( + key="output.current.nominal", + name="Nominal Output Current", + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, + device_class=SensorDeviceClass.CURRENT, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "output.voltage": SensorEntityDescription( + key="output.voltage", + name="Output Voltage", + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + device_class=SensorDeviceClass.VOLTAGE, + state_class=SensorStateClass.MEASUREMENT, + ), + "output.voltage.nominal": SensorEntityDescription( + key="output.voltage.nominal", + name="Nominal Output Voltage", + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + device_class=SensorDeviceClass.VOLTAGE, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "output.frequency": SensorEntityDescription( + key="output.frequency", + name="Output Frequency", + native_unit_of_measurement=UnitOfFrequency.HERTZ, + device_class=SensorDeviceClass.FREQUENCY, + state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "output.frequency.nominal": SensorEntityDescription( + key="output.frequency.nominal", + name="Nominal Output Frequency", + native_unit_of_measurement=UnitOfFrequency.HERTZ, + device_class=SensorDeviceClass.FREQUENCY, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + "ambient.humidity": SensorEntityDescription( + key="ambient.humidity", + name="Ambient Humidity", + native_unit_of_measurement=PERCENTAGE, + device_class=SensorDeviceClass.HUMIDITY, + state_class=SensorStateClass.MEASUREMENT, + ), + "ambient.temperature": SensorEntityDescription( + key="ambient.temperature", + name="Ambient Temperature", + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + ), + "watts": SensorEntityDescription( + key="watts", + name="Watts", + native_unit_of_measurement=UnitOfPower.WATT, + device_class=SensorDeviceClass.POWER, + state_class=SensorStateClass.MEASUREMENT, + ), +} + def _get_nut_device_info(data: PyNUTData) -> DeviceInfo: """Return a DeviceInfo object filled with NUT device info.""" diff --git a/homeassistant/components/nut/translations/pt.json b/homeassistant/components/nut/translations/pt.json index 9f6c04e78a3..9e4e757aa95 100644 --- a/homeassistant/components/nut/translations/pt.json +++ b/homeassistant/components/nut/translations/pt.json @@ -4,13 +4,13 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "unknown": "Erro inesperado" }, "step": { "user": { "data": { - "host": "Servidor", + "host": "Endere\u00e7o", "password": "Palavra-passe", "port": "Porta", "username": "Utilizador" diff --git a/homeassistant/components/nws/__init__.py b/homeassistant/components/nws/__init__.py index fe0d0b97c69..051402af9fe 100644 --- a/homeassistant/components/nws/__init__.py +++ b/homeassistant/components/nws/__init__.py @@ -42,7 +42,7 @@ def base_unique_id(latitude, longitude): return f"{latitude}_{longitude}" -class NwsDataUpdateCoordinator(DataUpdateCoordinator): +class NwsDataUpdateCoordinator(DataUpdateCoordinator[None]): """ NWS data update coordinator. @@ -57,7 +57,7 @@ class NwsDataUpdateCoordinator(DataUpdateCoordinator): name: str, update_interval: datetime.timedelta, failed_update_interval: datetime.timedelta, - update_method: Callable[[], Awaitable] | None = None, + update_method: Callable[[], Awaitable[None]] | None = None, request_refresh_debouncer: debounce.Debouncer | None = None, ) -> None: """Initialize NWS coordinator.""" diff --git a/homeassistant/components/nws/const.py b/homeassistant/components/nws/const.py index 707186abebf..7a07c1cf7c0 100644 --- a/homeassistant/components/nws/const.py +++ b/homeassistant/components/nws/const.py @@ -1,14 +1,8 @@ """Constants for National Weather Service Integration.""" from __future__ import annotations -from dataclasses import dataclass from datetime import timedelta -from homeassistant.components.sensor import ( - SensorDeviceClass, - SensorEntityDescription, - SensorStateClass, -) from homeassistant.components.weather import ( ATTR_CONDITION_CLOUDY, ATTR_CONDITION_EXCEPTIONAL, @@ -24,17 +18,6 @@ from homeassistant.components.weather import ( ATTR_CONDITION_WINDY, ATTR_CONDITION_WINDY_VARIANT, ) -from homeassistant.const import ( - DEGREE, - LENGTH_METERS, - LENGTH_MILES, - PERCENTAGE, - PRESSURE_INHG, - PRESSURE_PA, - SPEED_KILOMETERS_PER_HOUR, - SPEED_MILES_PER_HOUR, - TEMP_CELSIUS, -) DOMAIN = "nws" @@ -99,113 +82,3 @@ COORDINATOR_FORECAST_HOURLY = "coordinator_forecast_hourly" OBSERVATION_VALID_TIME = timedelta(minutes=20) FORECAST_VALID_TIME = timedelta(minutes=45) - - -@dataclass -class NWSSensorEntityDescription(SensorEntityDescription): - """Class describing NWSSensor entities.""" - - unit_convert: str | None = None - - -SENSOR_TYPES: tuple[NWSSensorEntityDescription, ...] = ( - NWSSensorEntityDescription( - key="dewpoint", - name="Dew Point", - icon=None, - device_class=SensorDeviceClass.TEMPERATURE, - state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=TEMP_CELSIUS, - unit_convert=TEMP_CELSIUS, - ), - NWSSensorEntityDescription( - key="temperature", - name="Temperature", - icon=None, - device_class=SensorDeviceClass.TEMPERATURE, - state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=TEMP_CELSIUS, - unit_convert=TEMP_CELSIUS, - ), - NWSSensorEntityDescription( - key="windChill", - name="Wind Chill", - icon=None, - device_class=SensorDeviceClass.TEMPERATURE, - state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=TEMP_CELSIUS, - unit_convert=TEMP_CELSIUS, - ), - NWSSensorEntityDescription( - key="heatIndex", - name="Heat Index", - icon=None, - device_class=SensorDeviceClass.TEMPERATURE, - state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=TEMP_CELSIUS, - unit_convert=TEMP_CELSIUS, - ), - NWSSensorEntityDescription( - key="relativeHumidity", - name="Relative Humidity", - icon=None, - device_class=SensorDeviceClass.HUMIDITY, - state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=PERCENTAGE, - unit_convert=PERCENTAGE, - ), - NWSSensorEntityDescription( - key="windSpeed", - name="Wind Speed", - icon="mdi:weather-windy", - device_class=None, - state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=SPEED_KILOMETERS_PER_HOUR, - unit_convert=SPEED_MILES_PER_HOUR, - ), - NWSSensorEntityDescription( - key="windGust", - name="Wind Gust", - icon="mdi:weather-windy", - device_class=None, - state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=SPEED_KILOMETERS_PER_HOUR, - unit_convert=SPEED_MILES_PER_HOUR, - ), - NWSSensorEntityDescription( - key="windDirection", - name="Wind Direction", - icon="mdi:compass-rose", - device_class=None, - state_class=None, # statistics currently doesn't handle circular statistics - native_unit_of_measurement=DEGREE, - unit_convert=DEGREE, - ), - NWSSensorEntityDescription( - key="barometricPressure", - name="Barometric Pressure", - icon=None, - device_class=SensorDeviceClass.PRESSURE, - state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=PRESSURE_PA, - unit_convert=PRESSURE_INHG, - ), - NWSSensorEntityDescription( - key="seaLevelPressure", - name="Sea Level Pressure", - icon=None, - device_class=SensorDeviceClass.PRESSURE, - state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=PRESSURE_PA, - unit_convert=PRESSURE_INHG, - ), - NWSSensorEntityDescription( - key="visibility", - name="Visibility", - icon="mdi:eye", - device_class=None, - state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=LENGTH_METERS, - unit_convert=LENGTH_MILES, - ), -) diff --git a/homeassistant/components/nws/manifest.json b/homeassistant/components/nws/manifest.json index 67ad9255c68..93d04bcfba3 100644 --- a/homeassistant/components/nws/manifest.json +++ b/homeassistant/components/nws/manifest.json @@ -2,7 +2,7 @@ "domain": "nws", "name": "National Weather Service (NWS)", "documentation": "https://www.home-assistant.io/integrations/nws", - "codeowners": ["@MatthewFlamm"], + "codeowners": ["@MatthewFlamm", "@kamiyo"], "requirements": ["pynws==1.4.1"], "quality_scale": "platinum", "config_flow": true, diff --git a/homeassistant/components/nws/sensor.py b/homeassistant/components/nws/sensor.py index 2e7495701a9..06d29c830a0 100644 --- a/homeassistant/components/nws/sensor.py +++ b/homeassistant/components/nws/sensor.py @@ -1,17 +1,26 @@ """Sensors for National Weather Service (NWS).""" -from homeassistant.components.sensor import SensorEntity +from __future__ import annotations + +from dataclasses import dataclass + +from pynws import SimpleNWS + +from homeassistant.components.sensor import ( + SensorDeviceClass, + SensorEntity, + SensorEntityDescription, + SensorStateClass, +) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONF_LATITUDE, CONF_LONGITUDE, - LENGTH_METERS, - LENGTH_MILES, + DEGREE, PERCENTAGE, - PRESSURE_INHG, - PRESSURE_PA, - SPEED_KILOMETERS_PER_HOUR, - SPEED_MILES_PER_HOUR, - TEMP_CELSIUS, + UnitOfLength, + UnitOfPressure, + UnitOfSpeed, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo @@ -25,7 +34,7 @@ from homeassistant.util.unit_conversion import ( ) from homeassistant.util.unit_system import US_CUSTOMARY_SYSTEM -from . import base_unique_id, device_info +from . import NwsDataUpdateCoordinator, base_unique_id, device_info from .const import ( ATTRIBUTION, CONF_STATION, @@ -33,13 +42,110 @@ from .const import ( DOMAIN, NWS_DATA, OBSERVATION_VALID_TIME, - SENSOR_TYPES, - NWSSensorEntityDescription, ) PARALLEL_UPDATES = 0 +@dataclass +class NWSSensorEntityDescription(SensorEntityDescription): + """Class describing NWSSensor entities.""" + + unit_convert: str | None = None + + +SENSOR_TYPES: tuple[NWSSensorEntityDescription, ...] = ( + NWSSensorEntityDescription( + key="dewpoint", + name="Dew Point", + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + unit_convert=UnitOfTemperature.CELSIUS, + ), + NWSSensorEntityDescription( + key="temperature", + name="Temperature", + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + unit_convert=UnitOfTemperature.CELSIUS, + ), + NWSSensorEntityDescription( + key="windChill", + name="Wind Chill", + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + unit_convert=UnitOfTemperature.CELSIUS, + ), + NWSSensorEntityDescription( + key="heatIndex", + name="Heat Index", + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + unit_convert=UnitOfTemperature.CELSIUS, + ), + NWSSensorEntityDescription( + key="relativeHumidity", + name="Relative Humidity", + device_class=SensorDeviceClass.HUMIDITY, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=PERCENTAGE, + unit_convert=PERCENTAGE, + ), + NWSSensorEntityDescription( + key="windSpeed", + name="Wind Speed", + device_class=SensorDeviceClass.WIND_SPEED, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, + unit_convert=UnitOfSpeed.MILES_PER_HOUR, + ), + NWSSensorEntityDescription( + key="windGust", + name="Wind Gust", + device_class=SensorDeviceClass.WIND_SPEED, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, + unit_convert=UnitOfSpeed.MILES_PER_HOUR, + ), + # statistics currently doesn't handle circular statistics + NWSSensorEntityDescription( + key="windDirection", + name="Wind Direction", + icon="mdi:compass-rose", + native_unit_of_measurement=DEGREE, + unit_convert=DEGREE, + ), + NWSSensorEntityDescription( + key="barometricPressure", + name="Barometric Pressure", + device_class=SensorDeviceClass.PRESSURE, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UnitOfPressure.PA, + unit_convert=UnitOfPressure.INHG, + ), + NWSSensorEntityDescription( + key="seaLevelPressure", + name="Sea Level Pressure", + device_class=SensorDeviceClass.PRESSURE, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UnitOfPressure.PA, + unit_convert=UnitOfPressure.INHG, + ), + NWSSensorEntityDescription( + key="visibility", + name="Visibility", + icon="mdi:eye", + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UnitOfLength.METERS, + unit_convert=UnitOfLength.MILES, + ), +) + + async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback ) -> None: @@ -59,7 +165,7 @@ async def async_setup_entry( ) -class NWSSensor(CoordinatorEntity, SensorEntity): +class NWSSensor(CoordinatorEntity[NwsDataUpdateCoordinator], SensorEntity): """An NWS Sensor Entity.""" entity_description: NWSSensorEntityDescription @@ -75,7 +181,7 @@ class NWSSensor(CoordinatorEntity, SensorEntity): ): """Initialise the platform with a data instance.""" super().__init__(hass_data[COORDINATOR_OBSERVATION]) - self._nws = hass_data[NWS_DATA] + self._nws: SimpleNWS = hass_data[NWS_DATA] self._latitude = entry_data[CONF_LATITUDE] self._longitude = entry_data[CONF_LONGITUDE] self.entity_description = description @@ -92,19 +198,26 @@ class NWSSensor(CoordinatorEntity, SensorEntity): return None # Set alias to unit property -> prevent unnecessary hasattr calls unit_of_measurement = self.native_unit_of_measurement - if unit_of_measurement == SPEED_MILES_PER_HOUR: + if unit_of_measurement == UnitOfSpeed.MILES_PER_HOUR: return round( SpeedConverter.convert( - value, SPEED_KILOMETERS_PER_HOUR, SPEED_MILES_PER_HOUR + value, UnitOfSpeed.KILOMETERS_PER_HOUR, UnitOfSpeed.MILES_PER_HOUR ) ) - if unit_of_measurement == LENGTH_MILES: - return round(DistanceConverter.convert(value, LENGTH_METERS, LENGTH_MILES)) - if unit_of_measurement == PRESSURE_INHG: + if unit_of_measurement == UnitOfLength.MILES: return round( - PressureConverter.convert(value, PRESSURE_PA, PRESSURE_INHG), 2 + DistanceConverter.convert( + value, UnitOfLength.METERS, UnitOfLength.MILES + ) ) - if unit_of_measurement == TEMP_CELSIUS: + if unit_of_measurement == UnitOfPressure.INHG: + return round( + PressureConverter.convert( + value, UnitOfPressure.PA, UnitOfPressure.INHG + ), + 2, + ) + if unit_of_measurement == UnitOfTemperature.CELSIUS: return round(value, 1) if unit_of_measurement == PERCENTAGE: return round(value) diff --git a/homeassistant/components/nws/translations/pt.json b/homeassistant/components/nws/translations/pt.json index 3d9fdf5a2d3..5e53796965e 100644 --- a/homeassistant/components/nws/translations/pt.json +++ b/homeassistant/components/nws/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "unknown": "Erro inesperado" }, "step": { diff --git a/homeassistant/components/nws/translations/sk.json b/homeassistant/components/nws/translations/sk.json index 8f8b360f638..65aa05315a7 100644 --- a/homeassistant/components/nws/translations/sk.json +++ b/homeassistant/components/nws/translations/sk.json @@ -4,7 +4,8 @@ "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" }, "error": { - "cannot_connect": "Nepodarilo sa pripoji\u0165" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { @@ -14,6 +15,7 @@ "longitude": "Zemepisn\u00e1 d\u013a\u017eka", "station": "K\u00f3d stanice METAR" }, + "description": "Ak nie je zadan\u00fd k\u00f3d stanice METAR, na n\u00e1jdenie najbli\u017e\u0161ej stanice sa pou\u017eije zemepisn\u00e1 \u0161\u00edrka a d\u013a\u017eka. V s\u00fa\u010dasnosti m\u00f4\u017ee by\u0165 k\u013e\u00fa\u010dom API \u010doko\u013evek. Odpor\u00fa\u010da sa pou\u017ei\u0165 platn\u00fa e-mailov\u00fa adresu.", "title": "Pripojenie k N\u00e1rodnej meteorologickej slu\u017ebe" } } diff --git a/homeassistant/components/nzbget/sensor.py b/homeassistant/components/nzbget/sensor.py index 941c528f544..6d94ef35456 100644 --- a/homeassistant/components/nzbget/sensor.py +++ b/homeassistant/components/nzbget/sensor.py @@ -10,11 +10,7 @@ from homeassistant.components.sensor import ( SensorEntityDescription, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - CONF_NAME, - DATA_MEGABYTES, - DATA_RATE_MEGABYTES_PER_SECOND, -) +from homeassistant.const import CONF_NAME, UnitOfDataRate, UnitOfInformation from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.util.dt import utcnow @@ -29,12 +25,14 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="ArticleCacheMB", name="Article Cache", - native_unit_of_measurement=DATA_MEGABYTES, + native_unit_of_measurement=UnitOfInformation.MEGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, ), SensorEntityDescription( key="AverageDownloadRate", name="Average Speed", - native_unit_of_measurement=DATA_RATE_MEGABYTES_PER_SECOND, + device_class=SensorDeviceClass.DATA_RATE, + native_unit_of_measurement=UnitOfDataRate.MEGABYTES_PER_SECOND, ), SensorEntityDescription( key="DownloadPaused", @@ -43,17 +41,20 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="DownloadRate", name="Speed", - native_unit_of_measurement=DATA_RATE_MEGABYTES_PER_SECOND, + device_class=SensorDeviceClass.DATA_RATE, + native_unit_of_measurement=UnitOfDataRate.MEGABYTES_PER_SECOND, ), SensorEntityDescription( key="DownloadedSizeMB", name="Size", - native_unit_of_measurement=DATA_MEGABYTES, + native_unit_of_measurement=UnitOfInformation.MEGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, ), SensorEntityDescription( key="FreeDiskSpaceMB", name="Disk Free", - native_unit_of_measurement=DATA_MEGABYTES, + native_unit_of_measurement=UnitOfInformation.MEGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, ), SensorEntityDescription( key="PostJobCount", @@ -67,7 +68,8 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="RemainingSizeMB", name="Queue Size", - native_unit_of_measurement=DATA_MEGABYTES, + native_unit_of_measurement=UnitOfInformation.MEGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, ), SensorEntityDescription( key="UpTimeSec", @@ -77,7 +79,8 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="DownloadLimit", name="Speed Limit", - native_unit_of_measurement=DATA_RATE_MEGABYTES_PER_SECOND, + device_class=SensorDeviceClass.DATA_RATE, + native_unit_of_measurement=UnitOfDataRate.MEGABYTES_PER_SECOND, ), ) diff --git a/homeassistant/components/nzbget/translations/pt.json b/homeassistant/components/nzbget/translations/pt.json index 4bbc3a839fd..00006ec61e8 100644 --- a/homeassistant/components/nzbget/translations/pt.json +++ b/homeassistant/components/nzbget/translations/pt.json @@ -5,13 +5,13 @@ "unknown": "Erro inesperado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "flow_title": "NZBGet: {name}", "step": { "user": { "data": { - "host": "Servidor", + "host": "Endere\u00e7o", "name": "Nome", "password": "Palavra-passe", "port": "Porta", diff --git a/homeassistant/components/nzbget/translations/sk.json b/homeassistant/components/nzbget/translations/sk.json index eaaa5cb6d0a..0294306e161 100644 --- a/homeassistant/components/nzbget/translations/sk.json +++ b/homeassistant/components/nzbget/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia.", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "error": { diff --git a/homeassistant/components/octoprint/sensor.py b/homeassistant/components/octoprint/sensor.py index fd140d5c00e..17bea7b8ac5 100644 --- a/homeassistant/components/octoprint/sensor.py +++ b/homeassistant/components/octoprint/sensor.py @@ -12,7 +12,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import PERCENTAGE, TEMP_CELSIUS +from homeassistant.const import PERCENTAGE, UnitOfTemperature from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import CoordinatorEntity @@ -224,7 +224,7 @@ class OctoPrintStartTimeSensor(OctoPrintSensorBase): class OctoPrintTemperatureSensor(OctoPrintSensorBase): """Representation of an OctoPrint sensor.""" - _attr_native_unit_of_measurement = TEMP_CELSIUS + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS _attr_device_class = SensorDeviceClass.TEMPERATURE _attr_state_class = SensorStateClass.MEASUREMENT diff --git a/homeassistant/components/octoprint/translations/de.json b/homeassistant/components/octoprint/translations/de.json index 782920e2959..fd3cfb04695 100644 --- a/homeassistant/components/octoprint/translations/de.json +++ b/homeassistant/components/octoprint/translations/de.json @@ -11,9 +11,9 @@ "cannot_connect": "Verbindung fehlgeschlagen", "unknown": "Unerwarteter Fehler" }, - "flow_title": "OctoPrint-Drucker: {host}", + "flow_title": "OctoPrint Drucker: {host}", "progress": { - "get_api_key": "\u00d6ffne die OctoPrint-Benutzeroberfl\u00e4che und dr\u00fccke bei der Zugriffsanfrage f\u00fcr \"Home Assistant\" auf \"Zulassen\"." + "get_api_key": "\u00d6ffne die OctoPrint Benutzeroberfl\u00e4che und dr\u00fccke bei der Zugriffsanfrage f\u00fcr \"Home Assistant\" auf \"Zulassen\"." }, "step": { "reauth_confirm": { diff --git a/homeassistant/components/octoprint/translations/ko.json b/homeassistant/components/octoprint/translations/ko.json new file mode 100644 index 00000000000..adc7a5b1aad --- /dev/null +++ b/homeassistant/components/octoprint/translations/ko.json @@ -0,0 +1,28 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "reauth_confirm": { + "data": { + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + } + }, + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984", + "verify_ssl": "SSL \uc778\uc99d\uc11c \ud655\uc778" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/octoprint/translations/lb.json b/homeassistant/components/octoprint/translations/lb.json new file mode 100644 index 00000000000..04cd2b78707 --- /dev/null +++ b/homeassistant/components/octoprint/translations/lb.json @@ -0,0 +1,12 @@ +{ + "config": { + "step": { + "user": { + "data": { + "port": "Port-Nummer", + "ssl": "SSL benotzen" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/octoprint/translations/pt.json b/homeassistant/components/octoprint/translations/pt.json index 7fef03088cb..b83e72ab752 100644 --- a/homeassistant/components/octoprint/translations/pt.json +++ b/homeassistant/components/octoprint/translations/pt.json @@ -1,10 +1,18 @@ { "config": { "abort": { + "reauth_successful": "Reautentica\u00e7\u00e3o bem sucedida", "unknown": "Erro inesperado" }, "error": { "unknown": "Erro inesperado" + }, + "step": { + "reauth_confirm": { + "data": { + "username": "Nome de Utilizador" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/octoprint/translations/sk.json b/homeassistant/components/octoprint/translations/sk.json index 4cec5809e7e..3e2c82c30df 100644 --- a/homeassistant/components/octoprint/translations/sk.json +++ b/homeassistant/components/octoprint/translations/sk.json @@ -12,6 +12,9 @@ "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "flow_title": "Tla\u010diare\u0148 OctoPrint: {host}", + "progress": { + "get_api_key": "Otvorte pou\u017e\u00edvate\u013esk\u00e9 rozhranie OctoPrint a kliknite na tla\u010didlo \"Povoli\u0165\" v \u017eiadosti o pr\u00edstup pre \"Home Assistant\"." + }, "step": { "reauth_confirm": { "data": { diff --git a/homeassistant/components/oem/climate.py b/homeassistant/components/oem/climate.py index d6337807fc2..1b600b25d94 100644 --- a/homeassistant/components/oem/climate.py +++ b/homeassistant/components/oem/climate.py @@ -21,7 +21,7 @@ from homeassistant.const import ( CONF_PASSWORD, CONF_PORT, CONF_USERNAME, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv @@ -67,7 +67,7 @@ class ThermostatDevice(ClimateEntity): _attr_hvac_modes = SUPPORT_HVAC _attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS def __init__(self, thermostat, name): """Initialize the device.""" diff --git a/homeassistant/components/omnilogic/common.py b/homeassistant/components/omnilogic/common.py index e28fd53d6fe..4ef78477afe 100644 --- a/homeassistant/components/omnilogic/common.py +++ b/homeassistant/components/omnilogic/common.py @@ -20,7 +20,7 @@ from .const import ALL_ITEM_KINDS, DOMAIN _LOGGER = logging.getLogger(__name__) -class OmniLogicUpdateCoordinator(DataUpdateCoordinator): +class OmniLogicUpdateCoordinator(DataUpdateCoordinator[dict[tuple, dict[str, Any]]]): """Class to manage fetching update data from single endpoint.""" def __init__( diff --git a/homeassistant/components/omnilogic/sensor.py b/homeassistant/components/omnilogic/sensor.py index 04bb1abf3e8..5cb7605b854 100644 --- a/homeassistant/components/omnilogic/sensor.py +++ b/homeassistant/components/omnilogic/sensor.py @@ -5,12 +5,11 @@ from homeassistant.components.sensor import SensorDeviceClass, SensorEntity from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONCENTRATION_PARTS_PER_MILLION, - ELECTRIC_POTENTIAL_MILLIVOLT, - MASS_GRAMS, PERCENTAGE, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, - VOLUME_LITERS, + UnitOfElectricPotential, + UnitOfMass, + UnitOfTemperature, + UnitOfVolume, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -24,7 +23,9 @@ async def async_setup_entry( ) -> None: """Set up the sensor platform.""" - coordinator = hass.data[DOMAIN][entry.entry_id][COORDINATOR] + coordinator: OmniLogicUpdateCoordinator = hass.data[DOMAIN][entry.entry_id][ + COORDINATOR + ] entities = [] for item_id, item in coordinator.data.items(): @@ -108,12 +109,12 @@ class OmniLogicTemperatureSensor(OmnilogicSensor): sensor_data = self.coordinator.data[self._item_id][self._state_key] hayward_state = sensor_data - hayward_unit_of_measure = TEMP_FAHRENHEIT + hayward_unit_of_measure = UnitOfTemperature.FAHRENHEIT state = sensor_data if self._unit_type == "Metric": hayward_state = round((int(hayward_state) - 32) * 5 / 9, 1) - hayward_unit_of_measure = TEMP_CELSIUS + hayward_unit_of_measure = UnitOfTemperature.CELSIUS if int(sensor_data) == -1: hayward_state = None @@ -122,7 +123,7 @@ class OmniLogicTemperatureSensor(OmnilogicSensor): self._attrs["hayward_temperature"] = hayward_state self._attrs["hayward_unit_of_measure"] = hayward_unit_of_measure - self._unit = TEMP_FAHRENHEIT + self._unit = UnitOfTemperature.FAHRENHEIT return state @@ -174,7 +175,7 @@ class OmniLogicSaltLevelSensor(OmnilogicSensor): if self._unit_type == "Metric": salt_return = round(int(salt_return) / 1000, 2) - unit_of_measurement = f"{MASS_GRAMS}/{VOLUME_LITERS}" + unit_of_measurement = f"{UnitOfMass.GRAMS}/{UnitOfVolume.LITERS}" self._unit = unit_of_measurement @@ -259,7 +260,7 @@ SENSOR_TYPES: dict[tuple[int, str], list[dict[str, Any]]] = { "kind": "air_temperature", "device_class": SensorDeviceClass.TEMPERATURE, "icon": None, - "unit": TEMP_FAHRENHEIT, + "unit": UnitOfTemperature.FAHRENHEIT, "guard_condition": [{}], }, ], @@ -270,7 +271,7 @@ SENSOR_TYPES: dict[tuple[int, str], list[dict[str, Any]]] = { "kind": "water_temperature", "device_class": SensorDeviceClass.TEMPERATURE, "icon": None, - "unit": TEMP_FAHRENHEIT, + "unit": UnitOfTemperature.FAHRENHEIT, "guard_condition": [{}], }, ], @@ -351,7 +352,7 @@ SENSOR_TYPES: dict[tuple[int, str], list[dict[str, Any]]] = { "kind": "csad_orp", "device_class": None, "icon": "mdi:gauge", - "unit": ELECTRIC_POTENTIAL_MILLIVOLT, + "unit": UnitOfElectricPotential.MILLIVOLT, "guard_condition": [ {"orp": ""}, ], diff --git a/homeassistant/components/omnilogic/switch.py b/homeassistant/components/omnilogic/switch.py index 2d2ad08d38a..5065007d97e 100644 --- a/homeassistant/components/omnilogic/switch.py +++ b/homeassistant/components/omnilogic/switch.py @@ -23,7 +23,9 @@ async def async_setup_entry( ) -> None: """Set up the light platform.""" - coordinator = hass.data[DOMAIN][entry.entry_id][COORDINATOR] + coordinator: OmniLogicUpdateCoordinator = hass.data[DOMAIN][entry.entry_id][ + COORDINATOR + ] entities = [] for item_id, item in coordinator.data.items(): diff --git a/homeassistant/components/omnilogic/translations/pt.json b/homeassistant/components/omnilogic/translations/pt.json index 3e10b977773..b76a5a9fa32 100644 --- a/homeassistant/components/omnilogic/translations/pt.json +++ b/homeassistant/components/omnilogic/translations/pt.json @@ -4,7 +4,7 @@ "single_instance_allowed": "J\u00e1 configurado. Apenas uma \u00fanica configura\u00e7\u00e3o \u00e9 poss\u00edvel." }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, diff --git a/homeassistant/components/omnilogic/translations/sk.json b/homeassistant/components/omnilogic/translations/sk.json index 2e4ae17997a..29679fff797 100644 --- a/homeassistant/components/omnilogic/translations/sk.json +++ b/homeassistant/components/omnilogic/translations/sk.json @@ -21,7 +21,8 @@ "step": { "init": { "data": { - "ph_offset": "Offset pH (pozit\u00edvny alebo negat\u00edvny)" + "ph_offset": "Offset pH (pozit\u00edvny alebo negat\u00edvny)", + "polling_interval": "Interval dotazovania (v sekund\u00e1ch)" } } } diff --git a/homeassistant/components/oncue/sensor.py b/homeassistant/components/oncue/sensor.py index 84802d67d10..0a7f8910775 100644 --- a/homeassistant/components/oncue/sensor.py +++ b/homeassistant/components/oncue/sensor.py @@ -11,15 +11,14 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - ELECTRIC_CURRENT_AMPERE, - ELECTRIC_POTENTIAL_VOLT, - ENERGY_KILO_WATT_HOUR, - FREQUENCY_HERTZ, PERCENTAGE, - POWER_WATT, - PRESSURE_PSI, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, + UnitOfElectricCurrent, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfFrequency, + UnitOfPower, + UnitOfPressure, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory @@ -47,7 +46,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( ), SensorEntityDescription( key="EngineOilPressure", - native_unit_of_measurement=PRESSURE_PSI, + native_unit_of_measurement=UnitOfPressure.PSI, device_class=SensorDeviceClass.PRESSURE, state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, @@ -60,7 +59,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( ), SensorEntityDescription( key="BatteryVoltage", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, @@ -85,7 +84,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( ), SensorEntityDescription( key="GeneratorTrueTotalPower", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, @@ -97,14 +96,14 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( ), SensorEntityDescription( key="GeneratorVoltageAverageLineToLine", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, ), SensorEntityDescription( key="GeneratorFrequency", - native_unit_of_measurement=FREQUENCY_HERTZ, + native_unit_of_measurement=UnitOfFrequency.HERTZ, device_class=SensorDeviceClass.FREQUENCY, state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, @@ -138,21 +137,21 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( ), SensorEntityDescription( key="Source1VoltageAverageLineToLine", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, ), SensorEntityDescription( key="Source2VoltageAverageLineToLine", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, ), SensorEntityDescription( key="GensetTotalEnergy", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), @@ -163,7 +162,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( ), SensorEntityDescription( key="GeneratorCurrentAverage", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, @@ -173,8 +172,8 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SENSOR_MAP = {description.key: description for description in SENSOR_TYPES} UNIT_MAPPINGS = { - "C": TEMP_CELSIUS, - "F": TEMP_FAHRENHEIT, + "C": UnitOfTemperature.CELSIUS, + "F": UnitOfTemperature.FAHRENHEIT, } diff --git a/homeassistant/components/oncue/translations/ko.json b/homeassistant/components/oncue/translations/ko.json new file mode 100644 index 00000000000..dd9aa05261e --- /dev/null +++ b/homeassistant/components/oncue/translations/ko.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/oncue/translations/pt.json b/homeassistant/components/oncue/translations/pt.json index 3b5850222d9..439e6f27fc1 100644 --- a/homeassistant/components/oncue/translations/pt.json +++ b/homeassistant/components/oncue/translations/pt.json @@ -1,7 +1,7 @@ { "config": { "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" } } } \ No newline at end of file diff --git a/homeassistant/components/ondilo_ico/sensor.py b/homeassistant/components/ondilo_ico/sensor.py index b4c6e02879e..b6a3b25f3f2 100644 --- a/homeassistant/components/ondilo_ico/sensor.py +++ b/homeassistant/components/ondilo_ico/sensor.py @@ -15,9 +15,9 @@ from homeassistant.components.sensor import ( from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONCENTRATION_PARTS_PER_MILLION, - ELECTRIC_POTENTIAL_MILLIVOLT, PERCENTAGE, - TEMP_CELSIUS, + UnitOfElectricPotential, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo @@ -34,24 +34,21 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="temperature", name="Temperature", - native_unit_of_measurement=TEMP_CELSIUS, - icon=None, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="orp", name="Oxydo Reduction Potential", - native_unit_of_measurement=ELECTRIC_POTENTIAL_MILLIVOLT, + native_unit_of_measurement=UnitOfElectricPotential.MILLIVOLT, icon="mdi:pool", - device_class=None, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="ph", name="pH", icon="mdi:pool", - device_class=None, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( @@ -59,14 +56,12 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( name="TDS", native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION, icon="mdi:pool", - device_class=None, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="battery", name="Battery", native_unit_of_measurement=PERCENTAGE, - icon=None, device_class=SensorDeviceClass.BATTERY, state_class=SensorStateClass.MEASUREMENT, ), @@ -74,7 +69,6 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( key="rssi", name="RSSI", native_unit_of_measurement=PERCENTAGE, - icon=None, device_class=SensorDeviceClass.SIGNAL_STRENGTH, state_class=SensorStateClass.MEASUREMENT, ), @@ -83,7 +77,6 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( name="Salt", native_unit_of_measurement="mg/L", icon="mdi:pool", - device_class=None, state_class=SensorStateClass.MEASUREMENT, ), ) diff --git a/homeassistant/components/ondilo_ico/translations/en_GB.json b/homeassistant/components/ondilo_ico/translations/en-GB.json similarity index 100% rename from homeassistant/components/ondilo_ico/translations/en_GB.json rename to homeassistant/components/ondilo_ico/translations/en-GB.json diff --git a/homeassistant/components/ondilo_ico/translations/pt.json b/homeassistant/components/ondilo_ico/translations/pt.json index b7b721c012c..36bba92e154 100644 --- a/homeassistant/components/ondilo_ico/translations/pt.json +++ b/homeassistant/components/ondilo_ico/translations/pt.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "authorize_url_timeout": "Tempo excedido a gerar um URL de autoriza\u00e7\u00e3o", + "authorize_url_timeout": "Excedido o tempo limite para gerar um URL de autoriza\u00e7\u00e3o", "missing_configuration": "O componente n\u00e3o est\u00e1 configurado. Por favor, siga a documenta\u00e7\u00e3o." }, "create_entry": { diff --git a/homeassistant/components/onewire/sensor.py b/homeassistant/components/onewire/sensor.py index f6c88201dc7..89a01496b1f 100644 --- a/homeassistant/components/onewire/sensor.py +++ b/homeassistant/components/onewire/sensor.py @@ -17,12 +17,11 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - ELECTRIC_POTENTIAL_VOLT, LIGHT_LUX, PERCENTAGE, - PRESSURE_CBAR, - PRESSURE_MBAR, - TEMP_CELSIUS, + UnitOfElectricPotential, + UnitOfPressure, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -70,7 +69,7 @@ SIMPLE_TEMPERATURE_SENSOR_DESCRIPTION = OneWireSensorEntityDescription( key="temperature", device_class=SensorDeviceClass.TEMPERATURE, name="Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, read_mode=READ_MODE_FLOAT, state_class=SensorStateClass.MEASUREMENT, ) @@ -86,7 +85,7 @@ DEVICE_SENSORS: dict[str, tuple[OneWireSensorEntityDescription, ...]] = { device_class=SensorDeviceClass.TEMPERATURE, entity_registry_enabled_default=False, name="Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, read_mode=READ_MODE_FLOAT, state_class=SensorStateClass.MEASUREMENT, ), @@ -95,7 +94,7 @@ DEVICE_SENSORS: dict[str, tuple[OneWireSensorEntityDescription, ...]] = { device_class=SensorDeviceClass.PRESSURE, entity_registry_enabled_default=False, name="Pressure", - native_unit_of_measurement=PRESSURE_MBAR, + native_unit_of_measurement=UnitOfPressure.MBAR, read_mode=READ_MODE_FLOAT, state_class=SensorStateClass.MEASUREMENT, ), @@ -153,7 +152,7 @@ DEVICE_SENSORS: dict[str, tuple[OneWireSensorEntityDescription, ...]] = { device_class=SensorDeviceClass.PRESSURE, entity_registry_enabled_default=False, name="Pressure", - native_unit_of_measurement=PRESSURE_MBAR, + native_unit_of_measurement=UnitOfPressure.MBAR, read_mode=READ_MODE_FLOAT, state_class=SensorStateClass.MEASUREMENT, ), @@ -171,7 +170,7 @@ DEVICE_SENSORS: dict[str, tuple[OneWireSensorEntityDescription, ...]] = { device_class=SensorDeviceClass.VOLTAGE, entity_registry_enabled_default=False, name="Voltage VAD", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, read_mode=READ_MODE_FLOAT, state_class=SensorStateClass.MEASUREMENT, ), @@ -180,7 +179,7 @@ DEVICE_SENSORS: dict[str, tuple[OneWireSensorEntityDescription, ...]] = { device_class=SensorDeviceClass.VOLTAGE, entity_registry_enabled_default=False, name="Voltage VDD", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, read_mode=READ_MODE_FLOAT, state_class=SensorStateClass.MEASUREMENT, ), @@ -189,7 +188,7 @@ DEVICE_SENSORS: dict[str, tuple[OneWireSensorEntityDescription, ...]] = { device_class=SensorDeviceClass.VOLTAGE, entity_registry_enabled_default=False, name="vis", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, read_mode=READ_MODE_FLOAT, state_class=SensorStateClass.MEASUREMENT, ), @@ -199,7 +198,7 @@ DEVICE_SENSORS: dict[str, tuple[OneWireSensorEntityDescription, ...]] = { key="temperature", device_class=SensorDeviceClass.TEMPERATURE, name="Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, override_key=_get_sensor_precision_family_28, read_mode=READ_MODE_FLOAT, state_class=SensorStateClass.MEASUREMENT, @@ -212,7 +211,7 @@ DEVICE_SENSORS: dict[str, tuple[OneWireSensorEntityDescription, ...]] = { device_class=SensorDeviceClass.TEMPERATURE, entity_registry_enabled_default=False, name="Thermocouple temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, read_mode=READ_MODE_FLOAT, override_key=lambda d, o: "typeK/temperature", state_class=SensorStateClass.MEASUREMENT, @@ -222,7 +221,7 @@ DEVICE_SENSORS: dict[str, tuple[OneWireSensorEntityDescription, ...]] = { device_class=SensorDeviceClass.VOLTAGE, entity_registry_enabled_default=False, name="Voltage", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, read_mode=READ_MODE_FLOAT, state_class=SensorStateClass.MEASUREMENT, ), @@ -231,7 +230,7 @@ DEVICE_SENSORS: dict[str, tuple[OneWireSensorEntityDescription, ...]] = { device_class=SensorDeviceClass.VOLTAGE, entity_registry_enabled_default=False, name="vis", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, read_mode=READ_MODE_FLOAT, state_class=SensorStateClass.MEASUREMENT, ), @@ -274,7 +273,7 @@ HOBBYBOARD_EF: dict[str, tuple[OneWireSensorEntityDescription, ...]] = { key="humidity/temperature", device_class=SensorDeviceClass.TEMPERATURE, name="Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, read_mode=READ_MODE_FLOAT, state_class=SensorStateClass.MEASUREMENT, ), @@ -284,7 +283,7 @@ HOBBYBOARD_EF: dict[str, tuple[OneWireSensorEntityDescription, ...]] = { key=f"moisture/sensor.{id}", device_class=SensorDeviceClass.PRESSURE, name=f"Moisture {id}", - native_unit_of_measurement=PRESSURE_CBAR, + native_unit_of_measurement=UnitOfPressure.CBAR, read_mode=READ_MODE_FLOAT, state_class=SensorStateClass.MEASUREMENT, ) @@ -300,7 +299,7 @@ EDS_SENSORS: dict[str, tuple[OneWireSensorEntityDescription, ...]] = { key="EDS0066/temperature", device_class=SensorDeviceClass.TEMPERATURE, name="Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, read_mode=READ_MODE_FLOAT, state_class=SensorStateClass.MEASUREMENT, ), @@ -308,7 +307,7 @@ EDS_SENSORS: dict[str, tuple[OneWireSensorEntityDescription, ...]] = { key="EDS0066/pressure", device_class=SensorDeviceClass.PRESSURE, name="Pressure", - native_unit_of_measurement=PRESSURE_MBAR, + native_unit_of_measurement=UnitOfPressure.MBAR, read_mode=READ_MODE_FLOAT, state_class=SensorStateClass.MEASUREMENT, ), @@ -318,7 +317,7 @@ EDS_SENSORS: dict[str, tuple[OneWireSensorEntityDescription, ...]] = { key="EDS0068/temperature", device_class=SensorDeviceClass.TEMPERATURE, name="Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, read_mode=READ_MODE_FLOAT, state_class=SensorStateClass.MEASUREMENT, ), @@ -326,7 +325,7 @@ EDS_SENSORS: dict[str, tuple[OneWireSensorEntityDescription, ...]] = { key="EDS0068/pressure", device_class=SensorDeviceClass.PRESSURE, name="Pressure", - native_unit_of_measurement=PRESSURE_MBAR, + native_unit_of_measurement=UnitOfPressure.MBAR, read_mode=READ_MODE_FLOAT, state_class=SensorStateClass.MEASUREMENT, ), diff --git a/homeassistant/components/onewire/translations/pt.json b/homeassistant/components/onewire/translations/pt.json index e4a7c7c095a..19244bb4bc7 100644 --- a/homeassistant/components/onewire/translations/pt.json +++ b/homeassistant/components/onewire/translations/pt.json @@ -4,12 +4,12 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "user": { "data": { - "host": "Servidor", + "host": "Endere\u00e7o", "port": "Porta" } } diff --git a/homeassistant/components/onewire/translations/sk.json b/homeassistant/components/onewire/translations/sk.json index 636af7a47d1..bf28fc720a0 100644 --- a/homeassistant/components/onewire/translations/sk.json +++ b/homeassistant/components/onewire/translations/sk.json @@ -21,11 +21,28 @@ "device_not_selected": "Vyberte zariadenia, ktor\u00e9 chcete nakonfigurova\u0165" }, "step": { + "ack_no_options": { + "data": { + "few": "Pr\u00e1zdnych", + "many": "Pr\u00e1zdnych", + "one": "Pr\u00e1zdny", + "other": "Pr\u00e1zdny" + } + }, "configure_device": { "data": { "precision": "Presnos\u0165 sn\u00edma\u010da" }, - "description": "Vyberte presnos\u0165 sn\u00edma\u010da pre {sensor_id}" + "description": "Vyberte presnos\u0165 sn\u00edma\u010da pre {sensor_id}", + "title": "Presnos\u0165 sn\u00edma\u010da OneWire" + }, + "device_selection": { + "data": { + "clear_device_options": "Vyma\u017ete v\u0161etky konfigur\u00e1cie zariadenia", + "device_selection": "Vyberte zariadenia, ktor\u00e9 chcete nakonfigurova\u0165" + }, + "description": "Vyberte, ktor\u00e9 kroky konfigur\u00e1cie sa maj\u00fa spracova\u0165", + "title": "Mo\u017enosti zariadenia OneWire" } } } diff --git a/homeassistant/components/onewire/translations/zh-Hant.json b/homeassistant/components/onewire/translations/zh-Hant.json index aa98353ca5a..789e757b0fc 100644 --- a/homeassistant/components/onewire/translations/zh-Hant.json +++ b/homeassistant/components/onewire/translations/zh-Hant.json @@ -25,7 +25,7 @@ "data": { "precision": "\u611f\u6e2c\u5668\u7cbe\u6e96\u5ea6" }, - "description": "\u8a2d\u5b9a {sensor_id} \u50b3\u611f\u5668\u7cbe\u6e96\u5ea6", + "description": "\u8a2d\u5b9a {sensor_id} \u611f\u6e2c\u5668\u7cbe\u6e96\u5ea6", "title": "OneWire \u611f\u6e2c\u5668\u7cbe\u6e96\u5ea6" }, "device_selection": { diff --git a/homeassistant/components/onkyo/media_player.py b/homeassistant/components/onkyo/media_player.py index d9de2730659..9b41a635fe1 100644 --- a/homeassistant/components/onkyo/media_player.py +++ b/homeassistant/components/onkyo/media_player.py @@ -365,7 +365,8 @@ class OnkyoDevice(MediaPlayerEntity): """ # HA_VOL * (MAX VOL / 100) * MAX_RECEIVER_VOL self.command( - f"volume {int(volume * (self._max_volume / 100) * self._receiver_max_volume)}" + "volume" + f" {int(volume * (self._max_volume / 100) * self._receiver_max_volume)}" ) def volume_up(self) -> None: diff --git a/homeassistant/components/onvif/binary_sensor.py b/homeassistant/components/onvif/binary_sensor.py index 2a7f23c61ee..93298c0cc63 100644 --- a/homeassistant/components/onvif/binary_sensor.py +++ b/homeassistant/components/onvif/binary_sensor.py @@ -1,7 +1,12 @@ """Support for ONVIF binary sensors.""" from __future__ import annotations -from homeassistant.components.binary_sensor import BinarySensorEntity +from contextlib import suppress + +from homeassistant.components.binary_sensor import ( + BinarySensorDeviceClass, + BinarySensorEntity, +) from homeassistant.config_entries import ConfigEntry from homeassistant.const import STATE_ON from homeassistant.core import HomeAssistant, callback @@ -61,13 +66,21 @@ class ONVIFBinarySensor(ONVIFBaseEntity, RestoreEntity, BinarySensorEntity): """Initialize the ONVIF binary sensor.""" self._attr_unique_id = uid if entry is not None: - self._attr_device_class = entry.original_device_class + if entry.original_device_class: + with suppress(ValueError): + self._attr_device_class = BinarySensorDeviceClass( + entry.original_device_class + ) self._attr_entity_category = entry.entity_category self._attr_name = entry.name else: event = device.events.get_uid(uid) assert event - self._attr_device_class = event.device_class + if event.device_class: + with suppress(ValueError): + self._attr_device_class = BinarySensorDeviceClass( + event.device_class + ) self._attr_entity_category = event.entity_category self._attr_entity_registry_enabled_default = event.entity_enabled self._attr_name = f"{device.name} {event.name}" diff --git a/homeassistant/components/onvif/config_flow.py b/homeassistant/components/onvif/config_flow.py index 48e5163ced5..a0dd77df53a 100644 --- a/homeassistant/components/onvif/config_flow.py +++ b/homeassistant/components/onvif/config_flow.py @@ -213,7 +213,10 @@ class OnvifFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): raise fault LOGGER.debug( - "Couldn't get network interfaces from ONVIF deivice '%s'. Error: %s", + ( + "Couldn't get network interfaces from ONVIF deivice '%s'." + " Error: %s" + ), self.onvif_config[CONF_NAME], fault, ) diff --git a/homeassistant/components/onvif/device.py b/homeassistant/components/onvif/device.py index 8c000852c48..3398c905d58 100644 --- a/homeassistant/components/onvif/device.py +++ b/homeassistant/components/onvif/device.py @@ -135,8 +135,10 @@ class ONVIFDevice: await self.device.close() except Fault as err: LOGGER.error( - "Couldn't connect to camera '%s', please verify " - "that the credentials are correct. Error: %s", + ( + "Couldn't connect to camera '%s', please verify " + "that the credentials are correct. Error: %s" + ), self.name, err, ) @@ -230,9 +232,11 @@ class ONVIFDevice: if self._dt_diff_seconds > 5: LOGGER.warning( - "The date/time on %s (UTC) is '%s', " - "which is different from the system '%s', " - "this could lead to authentication issues", + ( + "The date/time on %s (UTC) is '%s', " + "which is different from the system '%s', " + "this could lead to authentication issues" + ), self.name, cam_date_utc, system_date, @@ -384,7 +388,10 @@ class ONVIFDevice: speed_val = speed preset_val = preset LOGGER.debug( - "Calling %s PTZ | Pan = %4.2f | Tilt = %4.2f | Zoom = %4.2f | Speed = %4.2f | Preset = %s", + ( + "Calling %s PTZ | Pan = %4.2f | Tilt = %4.2f | Zoom = %4.2f | Speed =" + " %4.2f | Preset = %s" + ), move_mode, pan_val, tilt_val, @@ -461,7 +468,10 @@ class ONVIFDevice: return if preset_val not in profile.ptz.presets: LOGGER.warning( - "PTZ preset '%s' does not exist on device '%s'. Available Presets: %s", + ( + "PTZ preset '%s' does not exist on device '%s'. Available" + " Presets: %s" + ), preset_val, self.name, ", ".join(profile.ptz.presets), diff --git a/homeassistant/components/onvif/event.py b/homeassistant/components/onvif/event.py index 8b09ee99079..4766bf0b002 100644 --- a/homeassistant/components/onvif/event.py +++ b/homeassistant/components/onvif/event.py @@ -126,8 +126,10 @@ class EventManager: await self._subscription.Unsubscribe() except (XMLParseError, *SUBSCRIPTION_ERRORS) as err: LOGGER.debug( - "Failed to unsubscribe ONVIF PullPoint subscription for '%s';" - " This is normal if the device restarted: %s", + ( + "Failed to unsubscribe ONVIF PullPoint subscription for '%s';" + " This is normal if the device restarted: %s" + ), self.unique_id, err, ) @@ -141,8 +143,10 @@ class EventManager: # when we get an XMLParseError LOGGER.log( DEBUG if isinstance(err, XMLParseError) else WARNING, - "Failed to restart ONVIF PullPoint subscription for '%s'; " - "Retrying later: %s", + ( + "Failed to restart ONVIF PullPoint subscription for '%s'; " + "Retrying later: %s" + ), self.unique_id, err, ) @@ -194,7 +198,10 @@ class EventManager: # when we get an XMLParseError LOGGER.log( DEBUG if isinstance(err, XMLParseError) else WARNING, - "Failed to fetch ONVIF PullPoint subscription messages for '%s': %s", + ( + "Failed to fetch ONVIF PullPoint subscription messages for" + " '%s': %s" + ), self.unique_id, err, ) diff --git a/homeassistant/components/onvif/sensor.py b/homeassistant/components/onvif/sensor.py index 4ad4c11f175..0a2932a8094 100644 --- a/homeassistant/components/onvif/sensor.py +++ b/homeassistant/components/onvif/sensor.py @@ -1,10 +1,11 @@ """Support for ONVIF binary sensors.""" from __future__ import annotations +from contextlib import suppress from datetime import date, datetime from decimal import Decimal -from homeassistant.components.sensor import RestoreSensor +from homeassistant.components.sensor import RestoreSensor, SensorDeviceClass from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import entity_registry as er @@ -58,14 +59,20 @@ class ONVIFSensor(ONVIFBaseEntity, RestoreSensor): """Initialize the ONVIF binary sensor.""" self._attr_unique_id = uid if entry is not None: - self._attr_device_class = entry.original_device_class + if entry.original_device_class: + with suppress(ValueError): + self._attr_device_class = SensorDeviceClass( + entry.original_device_class + ) self._attr_entity_category = entry.entity_category self._attr_name = entry.name self._attr_native_unit_of_measurement = entry.unit_of_measurement else: event = device.events.get_uid(uid) assert event - self._attr_device_class = event.device_class + if event.device_class: + with suppress(ValueError): + self._attr_device_class = SensorDeviceClass(event.device_class) self._attr_entity_category = event.entity_category self._attr_entity_registry_enabled_default = event.entity_enabled self._attr_name = f"{device.name} {event.name}" diff --git a/homeassistant/components/onvif/translations/ca.json b/homeassistant/components/onvif/translations/ca.json index 222921a0599..6f808c1cb8c 100644 --- a/homeassistant/components/onvif/translations/ca.json +++ b/homeassistant/components/onvif/translations/ca.json @@ -48,7 +48,8 @@ "onvif_devices": { "data": { "extra_arguments": "Arguments addicionals FFMPEG", - "rtsp_transport": "Mecanisme de transport RTSP" + "rtsp_transport": "Mecanisme de transport RTSP", + "use_wallclock_as_timestamps": "Utilitza el rellotge de paret com a marca de temps" }, "title": "Opcions de dispositiu ONVIF" } diff --git a/homeassistant/components/onvif/translations/he.json b/homeassistant/components/onvif/translations/he.json index 76a421947d7..e4c108bdda2 100644 --- a/homeassistant/components/onvif/translations/he.json +++ b/homeassistant/components/onvif/translations/he.json @@ -5,7 +5,7 @@ "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", "no_h264": "\u05dc\u05d0 \u05d4\u05d9\u05d5 \u05d6\u05e8\u05de\u05d9 H264 \u05d6\u05de\u05d9\u05e0\u05d9\u05dd. \u05d9\u05e9 \u05dc\u05d1\u05d3\u05d5\u05e7 \u05d0\u05ea \u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05e4\u05e8\u05d5\u05e4\u05d9\u05dc \u05d1\u05d4\u05ea\u05e7\u05df \u05e9\u05dc\u05da.", "no_mac": "\u05dc\u05d0 \u05d4\u05d9\u05ea\u05d4 \u05d0\u05e4\u05e9\u05e8\u05d5\u05ea \u05dc\u05e7\u05d1\u05d5\u05e2 \u05ea\u05e6\u05d5\u05e8\u05d4 \u05e9\u05dc \u05de\u05d6\u05d4\u05d4 \u05d9\u05d9\u05d7\u05d5\u05d3\u05d9 \u05e2\u05d1\u05d5\u05e8 \u05d4\u05ea\u05e7\u05df ONVIF.", - "onvif_error": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05d4\u05d2\u05d3\u05e8\u05ea \u05d4\u05ea\u05e7\u05df ONVIF. \u05e0\u05d0 \u05dc\u05d1\u05d3\u05d5\u05e7 \u05d0\u05ea \u05d9\u05d5\u05de\u05e0\u05d9 \u05d4\u05e8\u05d9\u05e9\u05d5\u05dd \u05dc\u05e7\u05d1\u05dc\u05ea \u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3." + "onvif_error": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05d4\u05d2\u05d3\u05e8\u05ea \u05d4\u05ea\u05e7\u05df ONVIF. \u05e0\u05d0 \u05dc\u05d1\u05d3\u05d5\u05e7 \u05d0\u05ea \u05d9\u05d5\u05de\u05e0\u05d9 \u05d4\u05e8\u05d9\u05e9\u05d5\u05dd \u05dc\u05e7\u05d1\u05dc\u05ea \u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3." }, "error": { "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4" diff --git a/homeassistant/components/onvif/translations/ko.json b/homeassistant/components/onvif/translations/ko.json index 5dcbd1df983..8cee990e331 100644 --- a/homeassistant/components/onvif/translations/ko.json +++ b/homeassistant/components/onvif/translations/ko.json @@ -11,6 +11,15 @@ "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" }, "step": { + "configure": { + "data": { + "host": "\ud638\uc2a4\ud2b8", + "name": "\uc774\ub984", + "password": "\ube44\ubc00\ubc88\ud638", + "port": "\ud3ec\ud2b8", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + } + }, "configure_profile": { "data": { "include": "\uce74\uba54\ub77c \uad6c\uc131\uc694\uc18c \ub9cc\ub4e4\uae30" diff --git a/homeassistant/components/onvif/translations/pt.json b/homeassistant/components/onvif/translations/pt.json index 4240578ca47..ebc746ed993 100644 --- a/homeassistant/components/onvif/translations/pt.json +++ b/homeassistant/components/onvif/translations/pt.json @@ -8,12 +8,12 @@ "onvif_error": "Erro ao configurar o dispositivo ONVIF. Verifique os logs para obter mais informa\u00e7\u00f5es." }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "configure": { "data": { - "host": "Servidor" + "host": "Endere\u00e7o" } }, "configure_profile": { diff --git a/homeassistant/components/onvif/translations/sk.json b/homeassistant/components/onvif/translations/sk.json index f0230e2e07c..a5163f11ded 100644 --- a/homeassistant/components/onvif/translations/sk.json +++ b/homeassistant/components/onvif/translations/sk.json @@ -2,7 +2,10 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", - "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "no_h264": "Neboli dostupn\u00e9 \u017eiadne streamy H264. Skontrolujte konfigur\u00e1ciu profilu na svojom zariaden\u00ed.", + "no_mac": "Nepodarilo sa nakonfigurova\u0165 jedine\u010dn\u00e9 ID pre zariadenie ONVIF.", + "onvif_error": "Chyba pri nastavovan\u00ed zariadenia ONVIF. \u010eal\u0161ie inform\u00e1cie n\u00e1jdete v protokoloch." }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165" @@ -15,12 +18,40 @@ "password": "Heslo", "port": "Port", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" - } + }, + "title": "Nakonfigurujte zariadenie ONVIF" + }, + "configure_profile": { + "data": { + "include": "Vytvorenie entity kamery" + }, + "description": "Chcete vytvori\u0165 entitu kamery pre {profile} s rozl\u00ed\u0161en\u00edm {resolution}?", + "title": "Konfigur\u00e1cia profilov" + }, + "device": { + "data": { + "host": "Vyberte n\u00e1jden\u00e9 zariadenie ONVIF" + }, + "title": "Vyberte zariadenie ONVIF" }, "user": { "data": { "auto": "Automatick\u00e9 vyh\u013ead\u00e1vanie" - } + }, + "description": "Kliknut\u00edm na tla\u010didlo Odosla\u0165 vyh\u013ead\u00e1me vo va\u0161ej sieti zariadenia ONVIF, ktor\u00e9 podporuj\u00fa profil S. \n\n Niektor\u00ed v\u00fdrobcovia za\u010dali \u0161tandardne deaktivova\u0165 ONVIF. Uistite sa, \u017ee je v konfigur\u00e1cii fotoapar\u00e1tu zapnut\u00e1 funkcia ONVIF.", + "title": "Nastavenie zariadenia ONVIF" + } + } + }, + "options": { + "step": { + "onvif_devices": { + "data": { + "extra_arguments": "\u010eal\u0161ie argumenty FFMPEG", + "rtsp_transport": "RTSP transportn\u00fd mechanizmus", + "use_wallclock_as_timestamps": "Pou\u017eite n\u00e1stenn\u00e9 hodiny ako \u010dasov\u00e9 pe\u010diatky" + }, + "title": "Mo\u017enosti zariadenia ONVIF" } } } diff --git a/homeassistant/components/open_meteo/translations/sk.json b/homeassistant/components/open_meteo/translations/sk.json index 28ab47956fa..64596dea68c 100644 --- a/homeassistant/components/open_meteo/translations/sk.json +++ b/homeassistant/components/open_meteo/translations/sk.json @@ -2,6 +2,9 @@ "config": { "step": { "user": { + "data": { + "zone": "Z\u00f3na" + }, "description": "Vyberte miesto, ktor\u00e9 chcete pou\u017ei\u0165 na predpove\u010f po\u010dasia" } } diff --git a/homeassistant/components/openalpr_local/translations/sk.json b/homeassistant/components/openalpr_local/translations/sk.json new file mode 100644 index 00000000000..3b25e7762be --- /dev/null +++ b/homeassistant/components/openalpr_local/translations/sk.json @@ -0,0 +1,8 @@ +{ + "issues": { + "pending_removal": { + "description": "Integr\u00e1cia OpenALPR Local \u010dak\u00e1 na odstr\u00e1nenie z Home Assistant a od Home Assistant 2022.10 u\u017e nebude k dispoz\u00edcii. \n\n Ak chcete tento probl\u00e9m vyrie\u0161i\u0165, odstr\u00e1\u0148te konfigur\u00e1ciu YAML zo s\u00faboru configuration.yaml a re\u0161tartujte aplik\u00e1ciu Home Assistant.", + "title": "Lok\u00e1lna integr\u00e1cia OpenALPR sa odstra\u0148uje" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/openevse/sensor.py b/homeassistant/components/openevse/sensor.py index 5bb469a3d99..d7a47b97594 100644 --- a/homeassistant/components/openevse/sensor.py +++ b/homeassistant/components/openevse/sensor.py @@ -17,9 +17,9 @@ from homeassistant.components.sensor import ( from homeassistant.const import ( CONF_HOST, CONF_MONITORED_VARIABLES, - ENERGY_KILO_WATT_HOUR, - TEMP_CELSIUS, - TIME_MINUTES, + UnitOfEnergy, + UnitOfTemperature, + UnitOfTime, ) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv @@ -36,42 +36,42 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="charge_time", name="Charge Time Elapsed", - native_unit_of_measurement=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, device_class=SensorDeviceClass.DURATION, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="ambient_temp", name="Ambient Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="ir_temp", name="IR Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="rtc_temp", name="RTC Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="usage_session", name="Usage this Session", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), SensorEntityDescription( key="usage_total", name="Total Usage", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), diff --git a/homeassistant/components/openexchangerates/translations/ko.json b/homeassistant/components/openexchangerates/translations/ko.json new file mode 100644 index 00000000000..ffe27a4826e --- /dev/null +++ b/homeassistant/components/openexchangerates/translations/ko.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "\uc11c\ube44\uc2a4\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4", + "timeout_connect": "\uc5f0\uacb0 \uc124\uc815 \uc2dc\uac04 \ucd08\uacfc" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "timeout_connect": "\uc5f0\uacb0 \uc124\uc815 \uc2dc\uac04 \ucd08\uacfc", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "api_key": "API \ud0a4" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/openexchangerates/translations/pt.json b/homeassistant/components/openexchangerates/translations/pt.json index 18a9b3af81a..c20809df3b9 100644 --- a/homeassistant/components/openexchangerates/translations/pt.json +++ b/homeassistant/components/openexchangerates/translations/pt.json @@ -1,8 +1,21 @@ { "config": { + "abort": { + "already_configured": "O servi\u00e7o j\u00e1 est\u00e1 configurado", + "cannot_connect": "A liga\u00e7\u00e3o falhou", + "reauth_successful": "Reautentica\u00e7\u00e3o bem sucedida", + "timeout_connect": "Excedido o tempo limite para estabelecer liga\u00e7\u00e3o" + }, + "error": { + "cannot_connect": "A liga\u00e7\u00e3o falhou", + "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", + "timeout_connect": "Excedido o tempo limite para estabelecer liga\u00e7\u00e3o", + "unknown": "Erro inesperado" + }, "step": { "user": { "data": { + "api_key": "Chave da API", "base": "Moeda base" }, "data_description": { diff --git a/homeassistant/components/openexchangerates/translations/sk.json b/homeassistant/components/openexchangerates/translations/sk.json index 7a67ae4ec9b..559aab66cb6 100644 --- a/homeassistant/components/openexchangerates/translations/sk.json +++ b/homeassistant/components/openexchangerates/translations/sk.json @@ -3,17 +3,23 @@ "abort": { "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1", "cannot_connect": "Nepodarilo sa pripoji\u0165", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", "timeout_connect": "\u010casov\u00fd limit na nadviazanie spojenia" }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie", - "timeout_connect": "\u010casov\u00fd limit na nadviazanie spojenia" + "timeout_connect": "\u010casov\u00fd limit na nadviazanie spojenia", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { "data": { - "api_key": "API k\u013e\u00fa\u010d" + "api_key": "API k\u013e\u00fa\u010d", + "base": "Z\u00e1kladn\u00e1 mena" + }, + "data_description": { + "base": "Pou\u017eitie inej z\u00e1kladnej meny ako USD vy\u017eaduje [platen\u00fd pl\u00e1n]({signup})." } } } diff --git a/homeassistant/components/opengarage/sensor.py b/homeassistant/components/opengarage/sensor.py index 5e9591e8b6c..386955dfd47 100644 --- a/homeassistant/components/opengarage/sensor.py +++ b/homeassistant/components/opengarage/sensor.py @@ -11,10 +11,10 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - LENGTH_CENTIMETERS, PERCENTAGE, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, - TEMP_CELSIUS, + UnitOfLength, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import EntityCategory @@ -28,7 +28,7 @@ _LOGGER = logging.getLogger(__name__) SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="dist", - native_unit_of_measurement=LENGTH_CENTIMETERS, + native_unit_of_measurement=UnitOfLength.CENTIMETERS, device_class=SensorDeviceClass.DISTANCE, state_class=SensorStateClass.MEASUREMENT, ), @@ -43,7 +43,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="temp", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( diff --git a/homeassistant/components/opengarage/translations/ko.json b/homeassistant/components/opengarage/translations/ko.json index b297ac33af6..dca7d41596d 100644 --- a/homeassistant/components/opengarage/translations/ko.json +++ b/homeassistant/components/opengarage/translations/ko.json @@ -1,9 +1,20 @@ { "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, "step": { "user": { "data": { - "device_key": "\uc7a5\uce58 \ud0a4" + "device_key": "\uc7a5\uce58 \ud0a4", + "host": "\ud638\uc2a4\ud2b8", + "port": "\ud3ec\ud2b8", + "verify_ssl": "SSL \uc778\uc99d\uc11c \ud655\uc778" } } } diff --git a/homeassistant/components/opengarage/translations/sk.json b/homeassistant/components/opengarage/translations/sk.json index ad1df6303e0..5cde0f9364f 100644 --- a/homeassistant/components/opengarage/translations/sk.json +++ b/homeassistant/components/opengarage/translations/sk.json @@ -5,7 +5,8 @@ }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { diff --git a/homeassistant/components/opensky/sensor.py b/homeassistant/components/opensky/sensor.py index 4c96d88f321..4c96f2575f0 100644 --- a/homeassistant/components/opensky/sensor.py +++ b/homeassistant/components/opensky/sensor.py @@ -14,8 +14,7 @@ from homeassistant.const import ( CONF_LONGITUDE, CONF_NAME, CONF_RADIUS, - LENGTH_KILOMETERS, - LENGTH_METERS, + UnitOfLength, ) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv @@ -107,7 +106,7 @@ class OpenSkySensor(SensorEntity): self._latitude = latitude self._longitude = longitude self._radius = DistanceConverter.convert( - radius, LENGTH_KILOMETERS, LENGTH_METERS + radius, UnitOfLength.KILOMETERS, UnitOfLength.METERS ) self._altitude = altitude self._state = 0 diff --git a/homeassistant/components/opentherm_gw/binary_sensor.py b/homeassistant/components/opentherm_gw/binary_sensor.py index 194e047e50e..1a4247992a7 100644 --- a/homeassistant/components/opentherm_gw/binary_sensor.py +++ b/homeassistant/components/opentherm_gw/binary_sensor.py @@ -70,10 +70,12 @@ async def async_setup_entry( if deprecated_sensors: _LOGGER.warning( - "The following binary_sensor entities are deprecated and may " - "no longer behave as expected. They will be removed in a " - "future version. You can force removal of these entities by " - "disabling them and restarting Home Assistant.\n%s", + ( + "The following binary_sensor entities are deprecated and may " + "no longer behave as expected. They will be removed in a " + "future version. You can force removal of these entities by " + "disabling them and restarting Home Assistant.\n%s" + ), pformat([s.entity_id for s in deprecated_sensors]), ) diff --git a/homeassistant/components/opentherm_gw/climate.py b/homeassistant/components/opentherm_gw/climate.py index 08cfbf68ac8..66223996180 100644 --- a/homeassistant/components/opentherm_gw/climate.py +++ b/homeassistant/components/opentherm_gw/climate.py @@ -22,7 +22,7 @@ from homeassistant.const import ( PRECISION_HALVES, PRECISION_TENTHS, PRECISION_WHOLE, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect @@ -68,7 +68,7 @@ class OpenThermClimate(ClimateEntity): _attr_supported_features = ( ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE ) - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS def __init__(self, gw_dev, options): """Initialize the device.""" @@ -201,7 +201,7 @@ class OpenThermClimate(ClimateEntity): """Return the precision of the system.""" if self.temp_read_precision: return self.temp_read_precision - if self.hass.config.units.temperature_unit == TEMP_CELSIUS: + if self.hass.config.units.temperature_unit == UnitOfTemperature.CELSIUS: return PRECISION_HALVES return PRECISION_WHOLE @@ -247,7 +247,7 @@ class OpenThermClimate(ClimateEntity): """Return the supported step of target temperature.""" if self.temp_set_precision: return self.temp_set_precision - if self.hass.config.units.temperature_unit == TEMP_CELSIUS: + if self.hass.config.units.temperature_unit == UnitOfTemperature.CELSIUS: return PRECISION_HALVES return PRECISION_WHOLE diff --git a/homeassistant/components/opentherm_gw/const.py b/homeassistant/components/opentherm_gw/const.py index d72469759f1..1532b787740 100644 --- a/homeassistant/components/opentherm_gw/const.py +++ b/homeassistant/components/opentherm_gw/const.py @@ -7,10 +7,11 @@ from homeassistant.components.binary_sensor import BinarySensorDeviceClass from homeassistant.components.sensor import SensorDeviceClass from homeassistant.const import ( PERCENTAGE, - PRESSURE_BAR, - TEMP_CELSIUS, - TIME_HOURS, - TIME_MINUTES, + UnitOfPower, + UnitOfPressure, + UnitOfTemperature, + UnitOfTime, + UnitOfVolume, ) ATTR_GW_ID = "gateway_id" @@ -50,9 +51,6 @@ TRANSLATE_SOURCE = { gw_vars.THERMOSTAT: "Thermostat", } -UNIT_KW = "kW" -UNIT_L_MIN = f"L/{TIME_MINUTES}" - BINARY_SENSOR_INFO: dict[str, list] = { # [device_class, friendly_name format, [status source, ...]] gw_vars.DATA_MASTER_CH_ENABLED: [ @@ -219,7 +217,7 @@ SENSOR_INFO: dict[str, list] = { # [device_class, unit, friendly_name, [status source, ...]] gw_vars.DATA_CONTROL_SETPOINT: [ SensorDeviceClass.TEMPERATURE, - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, "Control Setpoint {}", [gw_vars.BOILER, gw_vars.THERMOSTAT], ], @@ -249,13 +247,13 @@ SENSOR_INFO: dict[str, list] = { ], gw_vars.DATA_CONTROL_SETPOINT_2: [ SensorDeviceClass.TEMPERATURE, - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, "Control Setpoint 2 {}", [gw_vars.BOILER, gw_vars.THERMOSTAT], ], gw_vars.DATA_ROOM_SETPOINT_OVRD: [ SensorDeviceClass.TEMPERATURE, - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, "Room Setpoint Override {}", [gw_vars.BOILER, gw_vars.THERMOSTAT], ], @@ -266,8 +264,8 @@ SENSOR_INFO: dict[str, list] = { [gw_vars.BOILER, gw_vars.THERMOSTAT], ], gw_vars.DATA_SLAVE_MAX_CAPACITY: [ - None, - UNIT_KW, + SensorDeviceClass.POWER, + UnitOfPower.KILO_WATT, "Boiler Maximum Capacity {}", [gw_vars.BOILER, gw_vars.THERMOSTAT], ], @@ -279,7 +277,7 @@ SENSOR_INFO: dict[str, list] = { ], gw_vars.DATA_ROOM_SETPOINT: [ SensorDeviceClass.TEMPERATURE, - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, "Room Setpoint {}", [gw_vars.BOILER, gw_vars.THERMOSTAT], ], @@ -290,116 +288,116 @@ SENSOR_INFO: dict[str, list] = { [gw_vars.BOILER, gw_vars.THERMOSTAT], ], gw_vars.DATA_CH_WATER_PRESS: [ - None, - PRESSURE_BAR, + SensorDeviceClass.PRESSURE, + UnitOfPressure.BAR, "Central Heating Water Pressure {}", [gw_vars.BOILER, gw_vars.THERMOSTAT], ], gw_vars.DATA_DHW_FLOW_RATE: [ None, - UNIT_L_MIN, + f"{UnitOfVolume.LITERS}/{UnitOfTime.MINUTES}", "Hot Water Flow Rate {}", [gw_vars.BOILER, gw_vars.THERMOSTAT], ], gw_vars.DATA_ROOM_SETPOINT_2: [ SensorDeviceClass.TEMPERATURE, - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, "Room Setpoint 2 {}", [gw_vars.BOILER, gw_vars.THERMOSTAT], ], gw_vars.DATA_ROOM_TEMP: [ SensorDeviceClass.TEMPERATURE, - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, "Room Temperature {}", [gw_vars.BOILER, gw_vars.THERMOSTAT], ], gw_vars.DATA_CH_WATER_TEMP: [ SensorDeviceClass.TEMPERATURE, - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, "Central Heating Water Temperature {}", [gw_vars.BOILER, gw_vars.THERMOSTAT], ], gw_vars.DATA_DHW_TEMP: [ SensorDeviceClass.TEMPERATURE, - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, "Hot Water Temperature {}", [gw_vars.BOILER, gw_vars.THERMOSTAT], ], gw_vars.DATA_OUTSIDE_TEMP: [ SensorDeviceClass.TEMPERATURE, - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, "Outside Temperature {}", [gw_vars.BOILER, gw_vars.THERMOSTAT], ], gw_vars.DATA_RETURN_WATER_TEMP: [ SensorDeviceClass.TEMPERATURE, - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, "Return Water Temperature {}", [gw_vars.BOILER, gw_vars.THERMOSTAT], ], gw_vars.DATA_SOLAR_STORAGE_TEMP: [ SensorDeviceClass.TEMPERATURE, - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, "Solar Storage Temperature {}", [gw_vars.BOILER, gw_vars.THERMOSTAT], ], gw_vars.DATA_SOLAR_COLL_TEMP: [ SensorDeviceClass.TEMPERATURE, - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, "Solar Collector Temperature {}", [gw_vars.BOILER, gw_vars.THERMOSTAT], ], gw_vars.DATA_CH_WATER_TEMP_2: [ SensorDeviceClass.TEMPERATURE, - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, "Central Heating 2 Water Temperature {}", [gw_vars.BOILER, gw_vars.THERMOSTAT], ], gw_vars.DATA_DHW_TEMP_2: [ SensorDeviceClass.TEMPERATURE, - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, "Hot Water 2 Temperature {}", [gw_vars.BOILER, gw_vars.THERMOSTAT], ], gw_vars.DATA_EXHAUST_TEMP: [ SensorDeviceClass.TEMPERATURE, - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, "Exhaust Temperature {}", [gw_vars.BOILER, gw_vars.THERMOSTAT], ], gw_vars.DATA_SLAVE_DHW_MAX_SETP: [ SensorDeviceClass.TEMPERATURE, - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, "Hot Water Maximum Setpoint {}", [gw_vars.BOILER, gw_vars.THERMOSTAT], ], gw_vars.DATA_SLAVE_DHW_MIN_SETP: [ SensorDeviceClass.TEMPERATURE, - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, "Hot Water Minimum Setpoint {}", [gw_vars.BOILER, gw_vars.THERMOSTAT], ], gw_vars.DATA_SLAVE_CH_MAX_SETP: [ SensorDeviceClass.TEMPERATURE, - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, "Boiler Maximum Central Heating Setpoint {}", [gw_vars.BOILER, gw_vars.THERMOSTAT], ], gw_vars.DATA_SLAVE_CH_MIN_SETP: [ SensorDeviceClass.TEMPERATURE, - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, "Boiler Minimum Central Heating Setpoint {}", [gw_vars.BOILER, gw_vars.THERMOSTAT], ], gw_vars.DATA_DHW_SETPOINT: [ SensorDeviceClass.TEMPERATURE, - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, "Hot Water Setpoint {}", [gw_vars.BOILER, gw_vars.THERMOSTAT], ], gw_vars.DATA_MAX_CH_SETPOINT: [ SensorDeviceClass.TEMPERATURE, - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, "Maximum Central Heating Setpoint {}", [gw_vars.BOILER, gw_vars.THERMOSTAT], ], @@ -434,26 +432,26 @@ SENSOR_INFO: dict[str, list] = { [gw_vars.BOILER, gw_vars.THERMOSTAT], ], gw_vars.DATA_TOTAL_BURNER_HOURS: [ - None, - TIME_HOURS, + SensorDeviceClass.DURATION, + UnitOfTime.HOURS, "Total Burner Hours {}", [gw_vars.BOILER, gw_vars.THERMOSTAT], ], gw_vars.DATA_CH_PUMP_HOURS: [ - None, - TIME_HOURS, + SensorDeviceClass.DURATION, + UnitOfTime.HOURS, "Central Heating Pump Hours {}", [gw_vars.BOILER, gw_vars.THERMOSTAT], ], gw_vars.DATA_DHW_PUMP_HOURS: [ - None, - TIME_HOURS, + SensorDeviceClass.DURATION, + UnitOfTime.HOURS, "Hot Water Pump Hours {}", [gw_vars.BOILER, gw_vars.THERMOSTAT], ], gw_vars.DATA_DHW_BURNER_HOURS: [ - None, - TIME_HOURS, + SensorDeviceClass.DURATION, + UnitOfTime.HOURS, "Hot Water Burner Hours {}", [gw_vars.BOILER, gw_vars.THERMOSTAT], ], @@ -513,7 +511,7 @@ SENSOR_INFO: dict[str, list] = { gw_vars.OTGW_GPIO_B: [None, None, "Gateway GPIO B Mode {}", [gw_vars.OTGW]], gw_vars.OTGW_SB_TEMP: [ SensorDeviceClass.TEMPERATURE, - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, "Gateway Setback Temperature {}", [gw_vars.OTGW], ], diff --git a/homeassistant/components/opentherm_gw/sensor.py b/homeassistant/components/opentherm_gw/sensor.py index 67b4eb138dd..d1f99461b22 100644 --- a/homeassistant/components/opentherm_gw/sensor.py +++ b/homeassistant/components/opentherm_gw/sensor.py @@ -73,10 +73,12 @@ async def async_setup_entry( if deprecated_sensors: _LOGGER.warning( - "The following sensor entities are deprecated and may no " - "longer behave as expected. They will be removed in a future " - "version. You can force removal of these entities by disabling " - "them and restarting Home Assistant.\n%s", + ( + "The following sensor entities are deprecated and may no " + "longer behave as expected. They will be removed in a future " + "version. You can force removal of these entities by disabling " + "them and restarting Home Assistant.\n%s" + ), pformat([s.entity_id for s in deprecated_sensors]), ) diff --git a/homeassistant/components/opentherm_gw/translations/ko.json b/homeassistant/components/opentherm_gw/translations/ko.json index 01d1971ca6f..5d758699b78 100644 --- a/homeassistant/components/opentherm_gw/translations/ko.json +++ b/homeassistant/components/opentherm_gw/translations/ko.json @@ -3,7 +3,8 @@ "error": { "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", - "id_exists": "OpenTherm Gateway id \uac00 \uc774\ubbf8 \uc874\uc7ac\ud569\ub2c8\ub2e4" + "id_exists": "OpenTherm Gateway id \uac00 \uc774\ubbf8 \uc874\uc7ac\ud569\ub2c8\ub2e4", + "timeout_connect": "\uc5f0\uacb0 \uc124\uc815 \uc2dc\uac04 \ucd08\uacfc" }, "step": { "init": { diff --git a/homeassistant/components/opentherm_gw/translations/pt.json b/homeassistant/components/opentherm_gw/translations/pt.json index 94a446dbe71..733ebbe15b7 100644 --- a/homeassistant/components/opentherm_gw/translations/pt.json +++ b/homeassistant/components/opentherm_gw/translations/pt.json @@ -2,7 +2,8 @@ "config": { "error": { "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou", + "timeout_connect": "Excedido o tempo limite para estabelecer liga\u00e7\u00e3o" }, "step": { "init": { diff --git a/homeassistant/components/opentherm_gw/translations/sk.json b/homeassistant/components/opentherm_gw/translations/sk.json index 2de0fd19743..4dae87815b9 100644 --- a/homeassistant/components/opentherm_gw/translations/sk.json +++ b/homeassistant/components/opentherm_gw/translations/sk.json @@ -20,7 +20,10 @@ "step": { "init": { "data": { - "set_precision": "Nastavi\u0165 presnos\u0165" + "floor_temperature": "Teplota podlahy", + "read_precision": "Po\u017eiadavka na presnos\u0165", + "set_precision": "Nastavi\u0165 presnos\u0165", + "temporary_override_mode": "Re\u017eim do\u010dasn\u00e9ho prekro\u010denia nastavenej hodnoty" } } } diff --git a/homeassistant/components/openuv/sensor.py b/homeassistant/components/openuv/sensor.py index dd8d1587f49..44bde8341a0 100644 --- a/homeassistant/components/openuv/sensor.py +++ b/homeassistant/components/openuv/sensor.py @@ -2,13 +2,12 @@ from __future__ import annotations from homeassistant.components.sensor import ( - SensorDeviceClass, SensorEntity, SensorEntityDescription, SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import TIME_MINUTES, UV_INDEX +from homeassistant.const import UV_INDEX, UnitOfTime from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.util.dt import as_local, parse_datetime @@ -51,7 +50,6 @@ SENSOR_DESCRIPTIONS = ( SensorEntityDescription( key=TYPE_CURRENT_OZONE_LEVEL, name="Current ozone level", - device_class=SensorDeviceClass.OZONE, native_unit_of_measurement="du", state_class=SensorStateClass.MEASUREMENT, ), @@ -78,42 +76,42 @@ SENSOR_DESCRIPTIONS = ( key=TYPE_SAFE_EXPOSURE_TIME_1, name="Skin type 1 safe exposure time", icon="mdi:timer-outline", - native_unit_of_measurement=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=TYPE_SAFE_EXPOSURE_TIME_2, name="Skin type 2 safe exposure time", icon="mdi:timer-outline", - native_unit_of_measurement=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=TYPE_SAFE_EXPOSURE_TIME_3, name="Skin type 3 safe exposure time", icon="mdi:timer-outline", - native_unit_of_measurement=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=TYPE_SAFE_EXPOSURE_TIME_4, name="Skin type 4 safe exposure time", icon="mdi:timer-outline", - native_unit_of_measurement=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=TYPE_SAFE_EXPOSURE_TIME_5, name="Skin type 5 safe exposure time", icon="mdi:timer-outline", - native_unit_of_measurement=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=TYPE_SAFE_EXPOSURE_TIME_6, name="Skin type 6 safe exposure time", icon="mdi:timer-outline", - native_unit_of_measurement=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, state_class=SensorStateClass.MEASUREMENT, ), ) diff --git a/homeassistant/components/openuv/translations/cs.json b/homeassistant/components/openuv/translations/cs.json index 5e7d7b2d7cc..16c9d000d44 100644 --- a/homeassistant/components/openuv/translations/cs.json +++ b/homeassistant/components/openuv/translations/cs.json @@ -1,12 +1,19 @@ { "config": { "abort": { - "already_configured": "Um\u00edst\u011bn\u00ed je ji\u017e nastaveno" + "already_configured": "Um\u00edst\u011bn\u00ed je ji\u017e nastaveno", + "reauth_successful": "Op\u011btovn\u00e9 ov\u011b\u0159en\u00ed bylo \u00fasp\u011b\u0161n\u00e9" }, "error": { "invalid_api_key": "Neplatn\u00fd kl\u00ed\u010d API" }, "step": { + "reauth_confirm": { + "data": { + "api_key": "Kl\u00ed\u010d API" + }, + "title": "Znovu ov\u011b\u0159it integraci" + }, "user": { "data": { "api_key": "Kl\u00ed\u010d API", diff --git a/homeassistant/components/openuv/translations/ko.json b/homeassistant/components/openuv/translations/ko.json index 114be1df692..12940b28ddc 100644 --- a/homeassistant/components/openuv/translations/ko.json +++ b/homeassistant/components/openuv/translations/ko.json @@ -1,12 +1,20 @@ { "config": { "abort": { - "already_configured": "\uc704\uce58\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + "already_configured": "\uc704\uce58\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" }, "error": { "invalid_api_key": "API \ud0a4\uac00 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" }, "step": { + "reauth_confirm": { + "data": { + "api_key": "API \ud0a4" + }, + "description": "{latitude} , {longitude} \uc5d0 \ub300\ud55c API \ud0a4\ub97c \ub2e4\uc2dc \uc785\ub825\ud558\uc2ed\uc2dc\uc624.", + "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" + }, "user": { "data": { "api_key": "API \ud0a4", diff --git a/homeassistant/components/openuv/translations/pt-BR.json b/homeassistant/components/openuv/translations/pt-BR.json index c98e76680e7..b8b988ebf4c 100644 --- a/homeassistant/components/openuv/translations/pt-BR.json +++ b/homeassistant/components/openuv/translations/pt-BR.json @@ -10,7 +10,7 @@ "step": { "reauth_confirm": { "data": { - "api_key": "Chave API" + "api_key": "Chave da API" }, "description": "Insira novamente a chave de API para {latitude} , {longitude} .", "title": "Reautenticar Integra\u00e7\u00e3o" diff --git a/homeassistant/components/openuv/translations/pt.json b/homeassistant/components/openuv/translations/pt.json index 64d5de8785a..653a02d175a 100644 --- a/homeassistant/components/openuv/translations/pt.json +++ b/homeassistant/components/openuv/translations/pt.json @@ -1,12 +1,19 @@ { "config": { "abort": { - "already_configured": "A localiza\u00e7\u00e3o j\u00e1 est\u00e1 configurada" + "already_configured": "A localiza\u00e7\u00e3o j\u00e1 est\u00e1 configurada", + "reauth_successful": "Reautentica\u00e7\u00e3o bem sucedida" }, "error": { "invalid_api_key": "Chave de API inv\u00e1lida" }, "step": { + "reauth_confirm": { + "data": { + "api_key": "Chave da API" + }, + "title": "Reautenticar integra\u00e7\u00e3o" + }, "user": { "data": { "api_key": "Chave da API", diff --git a/homeassistant/components/openweathermap/const.py b/homeassistant/components/openweathermap/const.py index 5a370604e1f..4af068f2b84 100644 --- a/homeassistant/components/openweathermap/const.py +++ b/homeassistant/components/openweathermap/const.py @@ -1,11 +1,6 @@ """Consts for the OpenWeatherMap.""" from __future__ import annotations -from homeassistant.components.sensor import ( - SensorDeviceClass, - SensorEntityDescription, - SensorStateClass, -) from homeassistant.components.weather import ( ATTR_CONDITION_CLOUDY, ATTR_CONDITION_EXCEPTIONAL, @@ -22,17 +17,7 @@ from homeassistant.components.weather import ( ATTR_CONDITION_WINDY, ATTR_CONDITION_WINDY_VARIANT, ) -from homeassistant.const import ( - DEGREE, - LENGTH_METERS, - LENGTH_MILLIMETERS, - PERCENTAGE, - PRESSURE_HPA, - SPEED_METERS_PER_SECOND, - TEMP_CELSIUS, - UV_INDEX, - Platform, -) +from homeassistant.const import Platform DOMAIN = "openweathermap" DEFAULT_NAME = "OpenWeatherMap" @@ -168,152 +153,3 @@ CONDITION_CLASSES = { 904, ], } -WEATHER_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( - SensorEntityDescription( - key=ATTR_API_WEATHER, - name="Weather", - ), - SensorEntityDescription( - key=ATTR_API_DEW_POINT, - name="Dew Point", - native_unit_of_measurement=TEMP_CELSIUS, - device_class=SensorDeviceClass.TEMPERATURE, - state_class=SensorStateClass.MEASUREMENT, - ), - SensorEntityDescription( - key=ATTR_API_TEMPERATURE, - name="Temperature", - native_unit_of_measurement=TEMP_CELSIUS, - device_class=SensorDeviceClass.TEMPERATURE, - state_class=SensorStateClass.MEASUREMENT, - ), - SensorEntityDescription( - key=ATTR_API_FEELS_LIKE_TEMPERATURE, - name="Feels like temperature", - native_unit_of_measurement=TEMP_CELSIUS, - device_class=SensorDeviceClass.TEMPERATURE, - state_class=SensorStateClass.MEASUREMENT, - ), - SensorEntityDescription( - key=ATTR_API_WIND_SPEED, - name="Wind speed", - native_unit_of_measurement=SPEED_METERS_PER_SECOND, - state_class=SensorStateClass.MEASUREMENT, - ), - SensorEntityDescription( - key=ATTR_API_WIND_BEARING, - name="Wind bearing", - native_unit_of_measurement=DEGREE, - state_class=SensorStateClass.MEASUREMENT, - ), - SensorEntityDescription( - key=ATTR_API_HUMIDITY, - name="Humidity", - native_unit_of_measurement=PERCENTAGE, - device_class=SensorDeviceClass.HUMIDITY, - state_class=SensorStateClass.MEASUREMENT, - ), - SensorEntityDescription( - key=ATTR_API_PRESSURE, - name="Pressure", - native_unit_of_measurement=PRESSURE_HPA, - device_class=SensorDeviceClass.PRESSURE, - state_class=SensorStateClass.MEASUREMENT, - ), - SensorEntityDescription( - key=ATTR_API_CLOUDS, - name="Cloud coverage", - native_unit_of_measurement=PERCENTAGE, - state_class=SensorStateClass.MEASUREMENT, - ), - SensorEntityDescription( - key=ATTR_API_RAIN, - name="Rain", - native_unit_of_measurement=LENGTH_MILLIMETERS, - state_class=SensorStateClass.MEASUREMENT, - ), - SensorEntityDescription( - key=ATTR_API_SNOW, - name="Snow", - native_unit_of_measurement=LENGTH_MILLIMETERS, - state_class=SensorStateClass.MEASUREMENT, - ), - SensorEntityDescription( - key=ATTR_API_PRECIPITATION_KIND, - name="Precipitation kind", - ), - SensorEntityDescription( - key=ATTR_API_UV_INDEX, - name="UV Index", - native_unit_of_measurement=UV_INDEX, - state_class=SensorStateClass.MEASUREMENT, - ), - SensorEntityDescription( - key=ATTR_API_VISIBILITY_DISTANCE, - name="Visibility", - native_unit_of_measurement=LENGTH_METERS, - state_class=SensorStateClass.MEASUREMENT, - ), - SensorEntityDescription( - key=ATTR_API_CONDITION, - name="Condition", - ), - SensorEntityDescription( - key=ATTR_API_WEATHER_CODE, - name="Weather Code", - ), -) -FORECAST_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( - SensorEntityDescription( - key=ATTR_API_FORECAST_CONDITION, - name="Condition", - ), - SensorEntityDescription( - key=ATTR_API_FORECAST_PRECIPITATION, - name="Precipitation", - native_unit_of_measurement=LENGTH_MILLIMETERS, - ), - SensorEntityDescription( - key=ATTR_API_FORECAST_PRECIPITATION_PROBABILITY, - name="Precipitation probability", - native_unit_of_measurement=PERCENTAGE, - ), - SensorEntityDescription( - key=ATTR_API_FORECAST_PRESSURE, - name="Pressure", - native_unit_of_measurement=PRESSURE_HPA, - device_class=SensorDeviceClass.PRESSURE, - ), - SensorEntityDescription( - key=ATTR_API_FORECAST_TEMP, - name="Temperature", - native_unit_of_measurement=TEMP_CELSIUS, - device_class=SensorDeviceClass.TEMPERATURE, - ), - SensorEntityDescription( - key=ATTR_API_FORECAST_TEMP_LOW, - name="Temperature Low", - native_unit_of_measurement=TEMP_CELSIUS, - device_class=SensorDeviceClass.TEMPERATURE, - ), - SensorEntityDescription( - key=ATTR_API_FORECAST_TIME, - name="Time", - device_class=SensorDeviceClass.TIMESTAMP, - ), - SensorEntityDescription( - key=ATTR_API_WIND_BEARING, - name="Wind bearing", - native_unit_of_measurement=DEGREE, - ), - SensorEntityDescription( - key=ATTR_API_WIND_SPEED, - name="Wind speed", - native_unit_of_measurement=SPEED_METERS_PER_SECOND, - ), - SensorEntityDescription( - key=ATTR_API_CLOUDS, - name="Cloud coverage", - native_unit_of_measurement=PERCENTAGE, - ), -) diff --git a/homeassistant/components/openweathermap/sensor.py b/homeassistant/components/openweathermap/sensor.py index 20ee621b9dc..2cfdd2456ab 100644 --- a/homeassistant/components/openweathermap/sensor.py +++ b/homeassistant/components/openweathermap/sensor.py @@ -7,8 +7,19 @@ from homeassistant.components.sensor import ( SensorDeviceClass, SensorEntity, SensorEntityDescription, + SensorStateClass, ) from homeassistant.config_entries import ConfigEntry +from homeassistant.const import ( + DEGREE, + PERCENTAGE, + UV_INDEX, + UnitOfLength, + UnitOfPrecipitationDepth, + UnitOfPressure, + UnitOfSpeed, + UnitOfTemperature, +) from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceEntryType from homeassistant.helpers.entity import DeviceInfo @@ -18,18 +29,195 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from homeassistant.util import dt as dt_util from .const import ( + ATTR_API_CLOUDS, + ATTR_API_CONDITION, + ATTR_API_DEW_POINT, + ATTR_API_FEELS_LIKE_TEMPERATURE, ATTR_API_FORECAST, + ATTR_API_FORECAST_CONDITION, + ATTR_API_FORECAST_PRECIPITATION, + ATTR_API_FORECAST_PRECIPITATION_PROBABILITY, + ATTR_API_FORECAST_PRESSURE, + ATTR_API_FORECAST_TEMP, + ATTR_API_FORECAST_TEMP_LOW, + ATTR_API_FORECAST_TIME, + ATTR_API_HUMIDITY, + ATTR_API_PRECIPITATION_KIND, + ATTR_API_PRESSURE, + ATTR_API_RAIN, + ATTR_API_SNOW, + ATTR_API_TEMPERATURE, + ATTR_API_UV_INDEX, + ATTR_API_VISIBILITY_DISTANCE, + ATTR_API_WEATHER, + ATTR_API_WEATHER_CODE, + ATTR_API_WIND_BEARING, + ATTR_API_WIND_SPEED, ATTRIBUTION, DEFAULT_NAME, DOMAIN, ENTRY_NAME, ENTRY_WEATHER_COORDINATOR, - FORECAST_SENSOR_TYPES, MANUFACTURER, - WEATHER_SENSOR_TYPES, ) from .weather_update_coordinator import WeatherUpdateCoordinator +WEATHER_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( + SensorEntityDescription( + key=ATTR_API_WEATHER, + name="Weather", + ), + SensorEntityDescription( + key=ATTR_API_DEW_POINT, + name="Dew Point", + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + key=ATTR_API_TEMPERATURE, + name="Temperature", + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + key=ATTR_API_FEELS_LIKE_TEMPERATURE, + name="Feels like temperature", + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + key=ATTR_API_WIND_SPEED, + name="Wind speed", + native_unit_of_measurement=UnitOfSpeed.METERS_PER_SECOND, + device_class=SensorDeviceClass.WIND_SPEED, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + key=ATTR_API_WIND_BEARING, + name="Wind bearing", + native_unit_of_measurement=DEGREE, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + key=ATTR_API_HUMIDITY, + name="Humidity", + native_unit_of_measurement=PERCENTAGE, + device_class=SensorDeviceClass.HUMIDITY, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + key=ATTR_API_PRESSURE, + name="Pressure", + native_unit_of_measurement=UnitOfPressure.HPA, + device_class=SensorDeviceClass.PRESSURE, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + key=ATTR_API_CLOUDS, + name="Cloud coverage", + native_unit_of_measurement=PERCENTAGE, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + key=ATTR_API_RAIN, + name="Rain", + native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + device_class=SensorDeviceClass.PRECIPITATION, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + key=ATTR_API_SNOW, + name="Snow", + native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + device_class=SensorDeviceClass.PRECIPITATION, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + key=ATTR_API_PRECIPITATION_KIND, + name="Precipitation kind", + ), + SensorEntityDescription( + key=ATTR_API_UV_INDEX, + name="UV Index", + native_unit_of_measurement=UV_INDEX, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + key=ATTR_API_VISIBILITY_DISTANCE, + name="Visibility", + native_unit_of_measurement=UnitOfLength.METERS, + device_class=SensorDeviceClass.DISTANCE, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + key=ATTR_API_CONDITION, + name="Condition", + ), + SensorEntityDescription( + key=ATTR_API_WEATHER_CODE, + name="Weather Code", + ), +) +FORECAST_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( + SensorEntityDescription( + key=ATTR_API_FORECAST_CONDITION, + name="Condition", + ), + SensorEntityDescription( + key=ATTR_API_FORECAST_PRECIPITATION, + name="Precipitation", + device_class=SensorDeviceClass.PRECIPITATION, + native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + ), + SensorEntityDescription( + key=ATTR_API_FORECAST_PRECIPITATION_PROBABILITY, + name="Precipitation probability", + native_unit_of_measurement=PERCENTAGE, + ), + SensorEntityDescription( + key=ATTR_API_FORECAST_PRESSURE, + name="Pressure", + native_unit_of_measurement=UnitOfPressure.HPA, + device_class=SensorDeviceClass.PRESSURE, + ), + SensorEntityDescription( + key=ATTR_API_FORECAST_TEMP, + name="Temperature", + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + device_class=SensorDeviceClass.TEMPERATURE, + ), + SensorEntityDescription( + key=ATTR_API_FORECAST_TEMP_LOW, + name="Temperature Low", + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + device_class=SensorDeviceClass.TEMPERATURE, + ), + SensorEntityDescription( + key=ATTR_API_FORECAST_TIME, + name="Time", + device_class=SensorDeviceClass.TIMESTAMP, + ), + SensorEntityDescription( + key=ATTR_API_WIND_BEARING, + name="Wind bearing", + native_unit_of_measurement=DEGREE, + ), + SensorEntityDescription( + key=ATTR_API_WIND_SPEED, + name="Wind speed", + native_unit_of_measurement=UnitOfSpeed.METERS_PER_SECOND, + device_class=SensorDeviceClass.WIND_SPEED, + ), + SensorEntityDescription( + key=ATTR_API_CLOUDS, + name="Cloud coverage", + native_unit_of_measurement=PERCENTAGE, + ), +) + async def async_setup_entry( hass: HomeAssistant, diff --git a/homeassistant/components/openweathermap/translations/he.json b/homeassistant/components/openweathermap/translations/he.json index 4de4cdf9e40..ea3fd12fc9a 100644 --- a/homeassistant/components/openweathermap/translations/he.json +++ b/homeassistant/components/openweathermap/translations/he.json @@ -16,7 +16,8 @@ "longitude": "\u05e7\u05d5 \u05d0\u05d5\u05e8\u05da", "mode": "\u05de\u05e6\u05d1", "name": "\u05e9\u05dd" - } + }, + "description": "\u05db\u05d3\u05d9 \u05dc\u05d9\u05e6\u05d5\u05e8 \u05de\u05e4\u05ea\u05d7 API \u05d9\u05e9 \u05dc\u05e2\u05d1\u05d5\u05e8 \u05d0\u05dc https://openweathermap.org/appid" } } }, diff --git a/homeassistant/components/openweathermap/translations/pt.json b/homeassistant/components/openweathermap/translations/pt.json index cc720bd6535..1ef545be422 100644 --- a/homeassistant/components/openweathermap/translations/pt.json +++ b/homeassistant/components/openweathermap/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "A localiza\u00e7\u00e3o j\u00e1 est\u00e1 configurada" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_api_key": "Chave de API inv\u00e1lida" }, "step": { diff --git a/homeassistant/components/openweathermap/weather_update_coordinator.py b/homeassistant/components/openweathermap/weather_update_coordinator.py index 630250b4701..cedc33c1a8e 100644 --- a/homeassistant/components/openweathermap/weather_update_coordinator.py +++ b/homeassistant/components/openweathermap/weather_update_coordinator.py @@ -9,7 +9,7 @@ from homeassistant.components.weather import ( ATTR_CONDITION_CLEAR_NIGHT, ATTR_CONDITION_SUNNY, ) -from homeassistant.const import TEMP_CELSIUS, TEMP_KELVIN +from homeassistant.const import UnitOfTemperature from homeassistant.helpers import sun from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from homeassistant.util import dt @@ -193,7 +193,10 @@ class WeatherUpdateCoordinator(DataUpdateCoordinator): """Format the dewpoint data.""" if dewpoint is not None: return round( - TemperatureConverter.convert(dewpoint, TEMP_KELVIN, TEMP_CELSIUS), 1 + TemperatureConverter.convert( + dewpoint, UnitOfTemperature.KELVIN, UnitOfTemperature.CELSIUS + ), + 1, ) return None diff --git a/homeassistant/components/opple/light.py b/homeassistant/components/opple/light.py index 80d0f8630dc..0d42b35b83b 100644 --- a/homeassistant/components/opple/light.py +++ b/homeassistant/components/opple/light.py @@ -151,8 +151,7 @@ class OppleLight(LightEntity): _LOGGER.debug("Update light %s success: power off", self._device.ip) else: _LOGGER.debug( - "Update light %s success: power on brightness %s " - "color temperature %s", + "Update light %s success: power on brightness %s color temperature %s", self._device.ip, self._brightness, self._color_temp, diff --git a/homeassistant/components/oralb/sensor.py b/homeassistant/components/oralb/sensor.py index af1edd582f9..124138d7e36 100644 --- a/homeassistant/components/oralb/sensor.py +++ b/homeassistant/components/oralb/sensor.py @@ -18,7 +18,7 @@ from homeassistant.components.sensor import ( SensorEntityDescription, SensorStateClass, ) -from homeassistant.const import SIGNAL_STRENGTH_DECIBELS_MILLIWATT, TIME_SECONDS +from homeassistant.const import SIGNAL_STRENGTH_DECIBELS_MILLIWATT, UnitOfTime from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -32,7 +32,7 @@ SENSOR_DESCRIPTIONS: dict[str, SensorEntityDescription] = { key=OralBSensor.TIME, device_class=SensorDeviceClass.DURATION, state_class=SensorStateClass.TOTAL_INCREASING, - native_unit_of_measurement=TIME_SECONDS, + native_unit_of_measurement=UnitOfTime.SECONDS, ), OralBSensor.SECTOR: SensorEntityDescription( key=OralBSensor.SECTOR, diff --git a/homeassistant/components/oralb/translations/en.json b/homeassistant/components/oralb/translations/en.json index ebd9760c161..afe859ca766 100644 --- a/homeassistant/components/oralb/translations/en.json +++ b/homeassistant/components/oralb/translations/en.json @@ -9,13 +9,13 @@ "flow_title": "{name}", "step": { "bluetooth_confirm": { - "description": "Do you want to setup {name}?" + "description": "Do you want to set up {name}?" }, "user": { "data": { "address": "Device" }, - "description": "Choose a device to setup" + "description": "Choose a device to set up" } } } diff --git a/homeassistant/components/oralb/translations/it.json b/homeassistant/components/oralb/translations/it.json index 7784ed3a240..97113c57103 100644 --- a/homeassistant/components/oralb/translations/it.json +++ b/homeassistant/components/oralb/translations/it.json @@ -15,7 +15,7 @@ "data": { "address": "Dispositivo" }, - "description": "Seleziona un dispositivo da configurare" + "description": "Scegli un dispositivo da configurare" } } } diff --git a/homeassistant/components/oralb/translations/ko.json b/homeassistant/components/oralb/translations/ko.json new file mode 100644 index 00000000000..1a59c05b30c --- /dev/null +++ b/homeassistant/components/oralb/translations/ko.json @@ -0,0 +1,9 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", + "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/oralb/translations/no.json b/homeassistant/components/oralb/translations/no.json index 0bf8b1695ec..38ab3d096f2 100644 --- a/homeassistant/components/oralb/translations/no.json +++ b/homeassistant/components/oralb/translations/no.json @@ -9,7 +9,7 @@ "flow_title": "{name}", "step": { "bluetooth_confirm": { - "description": "Vil du konfigurere {name}?" + "description": "Vil du sette opp {name} ?" }, "user": { "data": { diff --git a/homeassistant/components/oralb/translations/pt.json b/homeassistant/components/oralb/translations/pt.json new file mode 100644 index 00000000000..c6a15010072 --- /dev/null +++ b/homeassistant/components/oralb/translations/pt.json @@ -0,0 +1,9 @@ +{ + "config": { + "abort": { + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", + "already_in_progress": "O processo de configura\u00e7\u00e3o j\u00e1 est\u00e1 a decorrer", + "no_devices_found": "Nenhum dispositivo encontrado na rede" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/oru/sensor.py b/homeassistant/components/oru/sensor.py index 5d99e782fdd..a971a8e461b 100644 --- a/homeassistant/components/oru/sensor.py +++ b/homeassistant/components/oru/sensor.py @@ -7,8 +7,12 @@ import logging from oru import Meter, MeterError import voluptuous as vol -from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity -from homeassistant.const import ENERGY_KILO_WATT_HOUR +from homeassistant.components.sensor import ( + PLATFORM_SCHEMA, + SensorDeviceClass, + SensorEntity, +) +from homeassistant.const import UnitOfEnergy from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -20,9 +24,6 @@ CONF_METER_NUMBER = "meter_number" SCAN_INTERVAL = timedelta(minutes=15) -SENSOR_NAME = "ORU Current Energy Usage" -SENSOR_ICON = "mdi:counter" - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({vol.Required(CONF_METER_NUMBER): cv.string}) @@ -51,12 +52,12 @@ def setup_platform( class CurrentEnergyUsageSensor(SensorEntity): """Representation of the sensor.""" - _attr_icon = SENSOR_ICON - _attr_native_unit_of_measurement = ENERGY_KILO_WATT_HOUR + _attr_device_class = SensorDeviceClass.ENERGY + _attr_name = "ORU Current Energy Usage" + _attr_native_unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR def __init__(self, meter): """Initialize the sensor.""" - self._state = None self._available = None self.meter = meter @@ -65,26 +66,19 @@ class CurrentEnergyUsageSensor(SensorEntity): """Return a unique, Home Assistant friendly identifier for this entity.""" return self.meter.meter_id - @property - def name(self): - """Return the name of the sensor.""" - return SENSOR_NAME - - @property - def native_value(self): - """Return the state of the sensor.""" - return self._state - def update(self) -> None: """Fetch new state data for the sensor.""" try: last_read = self.meter.last_read() - self._state = last_read + self._attr_native_value = last_read self._available = True _LOGGER.debug( - "%s = %s %s", self.name, self._state, self.unit_of_measurement + "%s = %s %s", + self.name, + self.native_value, + self.native_unit_of_measurement, ) except MeterError as err: self._available = False diff --git a/homeassistant/components/osramlightify/light.py b/homeassistant/components/osramlightify/light.py index fa4aca357c1..1c0a134831b 100644 --- a/homeassistant/components/osramlightify/light.py +++ b/homeassistant/components/osramlightify/light.py @@ -412,7 +412,9 @@ class OsramLightifyLight(Luminary): """Update static attributes of the luminary.""" super().update_static_attributes() attrs = { - "device_type": f"{self._luminary.type_id()} ({self._luminary.devicename()})", + "device_type": ( + f"{self._luminary.type_id()} ({self._luminary.devicename()})" + ), "firmware_version": self._luminary.version(), } if self._luminary.devicetype().name == "SENSOR": diff --git a/homeassistant/components/otp/manifest.json b/homeassistant/components/otp/manifest.json index 7916e3cb4b7..c16908d0cc3 100644 --- a/homeassistant/components/otp/manifest.json +++ b/homeassistant/components/otp/manifest.json @@ -2,7 +2,7 @@ "domain": "otp", "name": "One-Time Password (OTP)", "documentation": "https://www.home-assistant.io/integrations/otp", - "requirements": ["pyotp==2.7.0"], + "requirements": ["pyotp==2.8.0"], "codeowners": [], "quality_scale": "internal", "iot_class": "local_polling", diff --git a/homeassistant/components/overkiz/__init__.py b/homeassistant/components/overkiz/__init__.py index a4240bc0550..ab463fc34d9 100644 --- a/homeassistant/components/overkiz/__init__.py +++ b/homeassistant/components/overkiz/__init__.py @@ -88,7 +88,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: if coordinator.is_stateless: LOGGER.debug( - "All devices have an assumed state. Update interval has been reduced to: %s", + ( + "All devices have an assumed state. Update interval has been reduced" + " to: %s" + ), UPDATE_INTERVAL_ALL_ASSUMED_STATE, ) coordinator.update_interval = UPDATE_INTERVAL_ALL_ASSUMED_STATE @@ -102,7 +105,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: # Map Overkiz entities to Home Assistant platform for device in coordinator.data.values(): LOGGER.debug( - "The following device has been retrieved. Report an issue if not supported correctly (%s)", + ( + "The following device has been retrieved. Report an issue if not" + " supported correctly (%s)" + ), device, ) diff --git a/homeassistant/components/overkiz/alarm_control_panel.py b/homeassistant/components/overkiz/alarm_control_panel.py index ae7d16aee9c..b08ede7df10 100644 --- a/homeassistant/components/overkiz/alarm_control_panel.py +++ b/homeassistant/components/overkiz/alarm_control_panel.py @@ -187,7 +187,9 @@ ALARM_DESCRIPTIONS: list[OverkizAlarmDescription] = [ alarm_arm_night=OverkizCommand.ALARM_ZONE_ON, alarm_arm_night_args=f"{OverkizCommandParam.A}, {OverkizCommandParam.B}", alarm_arm_away=OverkizCommand.ALARM_ZONE_ON, - alarm_arm_away_args=f"{OverkizCommandParam.A},{OverkizCommandParam.B},{OverkizCommandParam.C}", + alarm_arm_away_args=( + f"{OverkizCommandParam.A},{OverkizCommandParam.B},{OverkizCommandParam.C}" + ), ), # MyFoxAlarmController OverkizAlarmDescription( diff --git a/homeassistant/components/overkiz/climate_entities/atlantic_electrical_heater.py b/homeassistant/components/overkiz/climate_entities/atlantic_electrical_heater.py index 36d657d5846..bb095436054 100644 --- a/homeassistant/components/overkiz/climate_entities/atlantic_electrical_heater.py +++ b/homeassistant/components/overkiz/climate_entities/atlantic_electrical_heater.py @@ -13,7 +13,7 @@ from homeassistant.components.climate import ( ClimateEntityFeature, HVACMode, ) -from homeassistant.const import TEMP_CELSIUS +from homeassistant.const import UnitOfTemperature from ..entity import OverkizEntity @@ -46,7 +46,7 @@ class AtlanticElectricalHeater(OverkizEntity, ClimateEntity): _attr_hvac_modes = [*HVAC_MODES_TO_OVERKIZ] _attr_preset_modes = [*PRESET_MODES_TO_OVERKIZ] _attr_supported_features = ClimateEntityFeature.PRESET_MODE - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS @property def hvac_mode(self) -> HVACMode: diff --git a/homeassistant/components/overkiz/climate_entities/atlantic_electrical_heater_with_adjustable_temperature_setpoint.py b/homeassistant/components/overkiz/climate_entities/atlantic_electrical_heater_with_adjustable_temperature_setpoint.py index 80c2f418331..3b02523ec20 100644 --- a/homeassistant/components/overkiz/climate_entities/atlantic_electrical_heater_with_adjustable_temperature_setpoint.py +++ b/homeassistant/components/overkiz/climate_entities/atlantic_electrical_heater_with_adjustable_temperature_setpoint.py @@ -14,7 +14,7 @@ from homeassistant.components.climate import ( ClimateEntityFeature, HVACMode, ) -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from ..coordinator import OverkizDataUpdateCoordinator from ..entity import OverkizEntity @@ -24,6 +24,7 @@ PRESET_COMFORT1 = "comfort-1" PRESET_COMFORT2 = "comfort-2" PRESET_FROST_PROTECTION = "frost_protection" PRESET_PROG = "prog" +PRESET_EXTERNAL = "external" # Map Overkiz presets to Home Assistant presets @@ -36,6 +37,7 @@ OVERKIZ_TO_PRESET_MODE: dict[str, str] = { OverkizCommandParam.COMFORT_2: PRESET_COMFORT2, OverkizCommandParam.AUTO: PRESET_AUTO, OverkizCommandParam.BOOST: PRESET_BOOST, + OverkizCommandParam.EXTERNAL: PRESET_EXTERNAL, OverkizCommandParam.INTERNAL: PRESET_PROG, } @@ -48,6 +50,7 @@ OVERKIZ_TO_HVAC_MODE: dict[str, str] = { OverkizCommandParam.AUTO: HVACMode.AUTO, OverkizCommandParam.BASIC: HVACMode.HEAT, OverkizCommandParam.STANDBY: HVACMode.OFF, + OverkizCommandParam.EXTERNAL: HVACMode.AUTO, OverkizCommandParam.INTERNAL: HVACMode.AUTO, } @@ -63,7 +66,7 @@ class AtlanticElectricalHeaterWithAdjustableTemperatureSetpoint( _attr_hvac_modes = [*HVAC_MODE_TO_OVERKIZ] _attr_preset_modes = [*PRESET_MODE_TO_OVERKIZ] - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS _attr_supported_features = ( ClimateEntityFeature.PRESET_MODE | ClimateEntityFeature.TARGET_TEMPERATURE ) @@ -96,15 +99,26 @@ class AtlanticElectricalHeaterWithAdjustableTemperatureSetpoint( @property def preset_mode(self) -> str | None: """Return the current preset mode, e.g., home, away, temp.""" + + states = self.device.states + if ( - state := self.device.states[OverkizState.IO_TARGET_HEATING_LEVEL] + operating_mode := states[OverkizState.CORE_OPERATING_MODE] + ) and operating_mode.value_as_str == OverkizCommandParam.EXTERNAL: + return PRESET_EXTERNAL + + if ( + state := states[OverkizState.IO_TARGET_HEATING_LEVEL] ) and state.value_as_str: return OVERKIZ_TO_PRESET_MODE[state.value_as_str] return None async def async_set_preset_mode(self, preset_mode: str) -> None: """Set new preset mode.""" - if preset_mode in [PRESET_AUTO, PRESET_PROG]: + + if preset_mode == PRESET_EXTERNAL: + command = OverkizCommand.SET_SCHEDULING_TYPE + elif preset_mode in [PRESET_AUTO, PRESET_PROG]: command = OverkizCommand.SET_OPERATING_MODE else: command = OverkizCommand.SET_HEATING_LEVEL diff --git a/homeassistant/components/overkiz/climate_entities/atlantic_electrical_towel_dryer.py b/homeassistant/components/overkiz/climate_entities/atlantic_electrical_towel_dryer.py index 374d7e1164d..c9885ada421 100644 --- a/homeassistant/components/overkiz/climate_entities/atlantic_electrical_towel_dryer.py +++ b/homeassistant/components/overkiz/climate_entities/atlantic_electrical_towel_dryer.py @@ -12,7 +12,7 @@ from homeassistant.components.climate import ( ClimateEntityFeature, HVACMode, ) -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from ..coordinator import OverkizDataUpdateCoordinator from ..entity import OverkizEntity @@ -42,7 +42,7 @@ class AtlanticElectricalTowelDryer(OverkizEntity, ClimateEntity): _attr_hvac_modes = [*HVAC_MODE_TO_OVERKIZ] _attr_preset_modes = [*PRESET_MODE_TO_OVERKIZ] - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS def __init__( self, device_url: str, coordinator: OverkizDataUpdateCoordinator diff --git a/homeassistant/components/overkiz/climate_entities/atlantic_heat_recovery_ventilation.py b/homeassistant/components/overkiz/climate_entities/atlantic_heat_recovery_ventilation.py index 3627aa21c43..7c469518f86 100644 --- a/homeassistant/components/overkiz/climate_entities/atlantic_heat_recovery_ventilation.py +++ b/homeassistant/components/overkiz/climate_entities/atlantic_heat_recovery_ventilation.py @@ -11,7 +11,7 @@ from homeassistant.components.climate import ( ClimateEntityFeature, HVACMode, ) -from homeassistant.const import TEMP_CELSIUS +from homeassistant.const import UnitOfTemperature from ..coordinator import OverkizDataUpdateCoordinator from ..entity import OverkizEntity @@ -45,7 +45,7 @@ class AtlanticHeatRecoveryVentilation(OverkizEntity, ClimateEntity): _attr_hvac_mode = HVACMode.FAN_ONLY _attr_hvac_modes = [HVACMode.FAN_ONLY] _attr_preset_modes = [PRESET_AUTO, PRESET_PROG, PRESET_MANUAL] - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS _attr_supported_features = ( ClimateEntityFeature.PRESET_MODE | ClimateEntityFeature.FAN_MODE ) diff --git a/homeassistant/components/overkiz/climate_entities/atlantic_pass_apc_heating_zone.py b/homeassistant/components/overkiz/climate_entities/atlantic_pass_apc_heating_zone.py index 3d1f9038356..e90edad1133 100644 --- a/homeassistant/components/overkiz/climate_entities/atlantic_pass_apc_heating_zone.py +++ b/homeassistant/components/overkiz/climate_entities/atlantic_pass_apc_heating_zone.py @@ -15,7 +15,7 @@ from homeassistant.components.climate import ( ClimateEntityFeature, HVACMode, ) -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from ..coordinator import OverkizDataUpdateCoordinator from ..entity import OverkizEntity @@ -26,12 +26,16 @@ OVERKIZ_TO_HVAC_MODE: dict[str, str] = { OverkizCommandParam.MANU: HVACMode.HEAT, OverkizCommandParam.HEATING: HVACMode.HEAT, OverkizCommandParam.STOP: HVACMode.OFF, + OverkizCommandParam.EXTERNAL_SCHEDULING: HVACMode.AUTO, OverkizCommandParam.INTERNAL_SCHEDULING: HVACMode.AUTO, OverkizCommandParam.COMFORT: HVACMode.HEAT, } HVAC_MODE_TO_OVERKIZ = {v: k for k, v in OVERKIZ_TO_HVAC_MODE.items()} +PRESET_EXTERNAL = "external" +PRESET_FROST_PROTECTION = "frost_protection" + OVERKIZ_TO_PRESET_MODES: dict[str, str] = { OverkizCommandParam.OFF: PRESET_ECO, OverkizCommandParam.STOP: PRESET_ECO, @@ -39,6 +43,8 @@ OVERKIZ_TO_PRESET_MODES: dict[str, str] = { OverkizCommandParam.COMFORT: PRESET_COMFORT, OverkizCommandParam.ABSENCE: PRESET_AWAY, OverkizCommandParam.ECO: PRESET_ECO, + OverkizCommandParam.FROSTPROTECTION: PRESET_FROST_PROTECTION, + OverkizCommandParam.EXTERNAL_SCHEDULING: PRESET_EXTERNAL, OverkizCommandParam.INTERNAL_SCHEDULING: PRESET_HOME, } @@ -51,6 +57,8 @@ OVERKIZ_TO_PROFILE_MODES: dict[str, str] = { OverkizCommandParam.ABSENCE: PRESET_AWAY, OverkizCommandParam.MANU: PRESET_COMFORT, OverkizCommandParam.DEROGATION: PRESET_COMFORT, + OverkizCommandParam.EXTERNAL_SETPOINT: PRESET_EXTERNAL, + OverkizCommandParam.FROSTPROTECTION: PRESET_FROST_PROTECTION, OverkizCommandParam.COMFORT: PRESET_COMFORT, } @@ -69,7 +77,7 @@ class AtlanticPassAPCHeatingZone(OverkizEntity, ClimateEntity): _attr_supported_features = ( ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE ) - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS def __init__( self, device_url: str, coordinator: OverkizDataUpdateCoordinator diff --git a/homeassistant/components/overkiz/climate_entities/atlantic_pass_apc_zone_control.py b/homeassistant/components/overkiz/climate_entities/atlantic_pass_apc_zone_control.py index ba95785fbc7..33c1f0c4a2a 100644 --- a/homeassistant/components/overkiz/climate_entities/atlantic_pass_apc_zone_control.py +++ b/homeassistant/components/overkiz/climate_entities/atlantic_pass_apc_zone_control.py @@ -4,7 +4,7 @@ from typing import cast from pyoverkiz.enums import OverkizCommand, OverkizCommandParam, OverkizState from homeassistant.components.climate import ClimateEntity, HVACMode -from homeassistant.const import TEMP_CELSIUS +from homeassistant.const import UnitOfTemperature from ..entity import OverkizEntity @@ -22,7 +22,7 @@ class AtlanticPassAPCZoneControl(OverkizEntity, ClimateEntity): """Representation of Atlantic Pass APC Zone Control.""" _attr_hvac_modes = [*HVAC_MODE_TO_OVERKIZ] - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS @property def hvac_mode(self) -> str: diff --git a/homeassistant/components/overkiz/climate_entities/somfy_thermostat.py b/homeassistant/components/overkiz/climate_entities/somfy_thermostat.py index 608b26b8c9d..c3fd7cd964d 100644 --- a/homeassistant/components/overkiz/climate_entities/somfy_thermostat.py +++ b/homeassistant/components/overkiz/climate_entities/somfy_thermostat.py @@ -13,7 +13,7 @@ from homeassistant.components.climate import ( ClimateEntityFeature, HVACMode, ) -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from ..coordinator import OverkizDataUpdateCoordinator from ..entity import OverkizEntity @@ -54,7 +54,7 @@ TEMPERATURE_SENSOR_DEVICE_INDEX = 2 class SomfyThermostat(OverkizEntity, ClimateEntity): """Representation of Somfy Smart Thermostat.""" - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS _attr_supported_features = ( ClimateEntityFeature.PRESET_MODE | ClimateEntityFeature.TARGET_TEMPERATURE ) diff --git a/homeassistant/components/overkiz/cover_entities/vertical_cover.py b/homeassistant/components/overkiz/cover_entities/vertical_cover.py index 1459786c23f..18a9f176593 100644 --- a/homeassistant/components/overkiz/cover_entities/vertical_cover.py +++ b/homeassistant/components/overkiz/cover_entities/vertical_cover.py @@ -65,15 +65,12 @@ class VerticalCover(OverkizGenericCover): return supported_features @property - def device_class(self) -> str: + def device_class(self) -> CoverDeviceClass: """Return the class of the device.""" - return cast( - str, - ( - OVERKIZ_DEVICE_TO_DEVICE_CLASS.get(self.device.widget) - or OVERKIZ_DEVICE_TO_DEVICE_CLASS.get(self.device.ui_class) - or CoverDeviceClass.BLIND - ), + return ( + OVERKIZ_DEVICE_TO_DEVICE_CLASS.get(self.device.widget) + or OVERKIZ_DEVICE_TO_DEVICE_CLASS.get(self.device.ui_class) + or CoverDeviceClass.BLIND ) @property diff --git a/homeassistant/components/overkiz/entity.py b/homeassistant/components/overkiz/entity.py index 85e5a3fdf57..632842d048a 100644 --- a/homeassistant/components/overkiz/entity.py +++ b/homeassistant/components/overkiz/entity.py @@ -1,13 +1,11 @@ """Parent class for every Overkiz device.""" from __future__ import annotations -from enum import unique from typing import cast from pyoverkiz.enums import OverkizAttribute, OverkizState from pyoverkiz.models import Device -from homeassistant.backports.enum import StrEnum from homeassistant.helpers.entity import DeviceInfo, EntityDescription from homeassistant.helpers.update_coordinator import CoordinatorEntity @@ -118,18 +116,3 @@ class OverkizDescriptiveEntity(OverkizEntity): if self.is_sub_device: # In case of sub device, use the provided label and append the name of the type of entity self._attr_name = f"{self.device.label} {description.name}" - - -# Used by state translations for sensor and select entities -@unique -class OverkizDeviceClass(StrEnum): - """Device class for Overkiz specific devices.""" - - BATTERY = "overkiz__battery" - DISCRETE_RSSI_LEVEL = "overkiz__discrete_rssi_level" - MEMORIZED_SIMPLE_VOLUME = "overkiz__memorized_simple_volume" - OPEN_CLOSED_PEDESTRIAN = "overkiz__open_closed_pedestrian" - PRIORITY_LOCK_ORIGINATOR = "overkiz__priority_lock_originator" - SENSOR_DEFECT = "overkiz__sensor_defect" - SENSOR_ROOM = "overkiz__sensor_room" - THREE_WAY_HANDLE_DIRECTION = "overkiz__three_way_handle_direction" diff --git a/homeassistant/components/overkiz/manifest.json b/homeassistant/components/overkiz/manifest.json index 864c34714aa..c5d272b0489 100644 --- a/homeassistant/components/overkiz/manifest.json +++ b/homeassistant/components/overkiz/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "integration_type": "hub", "documentation": "https://www.home-assistant.io/integrations/overkiz", - "requirements": ["pyoverkiz==1.7.2"], + "requirements": ["pyoverkiz==1.7.3"], "zeroconf": [ { "type": "_kizbox._tcp.local.", @@ -17,7 +17,7 @@ "macaddress": "F8811A*" } ], - "codeowners": ["@imicknl", "@vlebourl", "@tetienne"], + "codeowners": ["@imicknl", "@vlebourl", "@tetienne", "@nyroDev"], "iot_class": "cloud_polling", "loggers": ["boto3", "botocore", "pyhumps", "pyoverkiz", "s3transfer"] } diff --git a/homeassistant/components/overkiz/number.py b/homeassistant/components/overkiz/number.py index 3b4107256a5..5937367b951 100644 --- a/homeassistant/components/overkiz/number.py +++ b/homeassistant/components/overkiz/number.py @@ -14,7 +14,7 @@ from homeassistant.components.number import ( NumberEntityDescription, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import TEMP_CELSIUS +from homeassistant.const import UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -106,7 +106,7 @@ NUMBER_DESCRIPTIONS: list[OverkizNumberDescription] = [ device_class=NumberDeviceClass.TEMPERATURE, native_min_value=6, native_max_value=29, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, entity_category=EntityCategory.CONFIG, ), OverkizNumberDescription( @@ -117,7 +117,7 @@ NUMBER_DESCRIPTIONS: list[OverkizNumberDescription] = [ device_class=NumberDeviceClass.TEMPERATURE, native_min_value=7, native_max_value=30, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, entity_category=EntityCategory.CONFIG, ), OverkizNumberDescription( @@ -128,7 +128,7 @@ NUMBER_DESCRIPTIONS: list[OverkizNumberDescription] = [ device_class=NumberDeviceClass.TEMPERATURE, native_min_value=5, native_max_value=15, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, entity_category=EntityCategory.CONFIG, ), # DimmerExteriorHeating (Somfy Terrace Heater) (0 - 100) diff --git a/homeassistant/components/overkiz/select.py b/homeassistant/components/overkiz/select.py index 6460e87b4ee..028936d3155 100644 --- a/homeassistant/components/overkiz/select.py +++ b/homeassistant/components/overkiz/select.py @@ -14,7 +14,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from . import HomeAssistantOverkizData from .const import DOMAIN, IGNORED_OVERKIZ_DEVICES -from .entity import OverkizDescriptiveEntity, OverkizDeviceClass +from .entity import OverkizDescriptiveEntity @dataclass @@ -71,7 +71,7 @@ SELECT_DESCRIPTIONS: list[OverkizSelectDescription] = [ OverkizCommandParam.CLOSED, ], select_option=_select_option_open_closed_pedestrian, - device_class=OverkizDeviceClass.OPEN_CLOSED_PEDESTRIAN, + translation_key="open_closed_pedestrian", ), OverkizSelectDescription( key=OverkizState.IO_MEMORIZED_SIMPLE_VOLUME, @@ -80,7 +80,7 @@ SELECT_DESCRIPTIONS: list[OverkizSelectDescription] = [ options=[OverkizCommandParam.STANDARD, OverkizCommandParam.HIGHEST], select_option=_select_option_memorized_simple_volume, entity_category=EntityCategory.CONFIG, - device_class=OverkizDeviceClass.MEMORIZED_SIMPLE_VOLUME, + translation_key="memorized_simple_volume", ), # SomfyHeatingTemperatureInterface OverkizSelectDescription( diff --git a/homeassistant/components/overkiz/sensor.py b/homeassistant/components/overkiz/sensor.py index e80e08e263a..e6a0e04deb7 100644 --- a/homeassistant/components/overkiz/sensor.py +++ b/homeassistant/components/overkiz/sensor.py @@ -17,15 +17,15 @@ from homeassistant.components.sensor import ( from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONCENTRATION_PARTS_PER_MILLION, - ENERGY_WATT_HOUR, LIGHT_LUX, PERCENTAGE, - POWER_WATT, SIGNAL_STRENGTH_DECIBELS, - TEMP_CELSIUS, - TIME_SECONDS, - VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR, - VOLUME_LITERS, + UnitOfEnergy, + UnitOfPower, + UnitOfTemperature, + UnitOfTime, + UnitOfVolume, + UnitOfVolumeFlowRate, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo, EntityCategory @@ -35,7 +35,7 @@ from homeassistant.helpers.typing import StateType from . import HomeAssistantOverkizData from .const import DOMAIN, IGNORED_OVERKIZ_DEVICES, OVERKIZ_STATE_TO_TRANSLATION from .coordinator import OverkizDataUpdateCoordinator -from .entity import OverkizDescriptiveEntity, OverkizDeviceClass, OverkizEntity +from .entity import OverkizDescriptiveEntity, OverkizEntity @dataclass @@ -60,7 +60,9 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [ name="Battery", entity_category=EntityCategory.DIAGNOSTIC, icon="mdi:battery", - device_class=OverkizDeviceClass.BATTERY, + device_class=SensorDeviceClass.ENUM, + options=["full", "normal", "low", "verylow"], + translation_key="battery", ), OverkizSensorDescription( key=OverkizState.CORE_RSSI_LEVEL, @@ -89,7 +91,7 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [ key=OverkizState.CORE_V40_WATER_VOLUME_ESTIMATION, name="Water volume estimation at 40 °C", icon="mdi:water", - native_unit_of_measurement=VOLUME_LITERS, + native_unit_of_measurement=UnitOfVolume.LITERS, device_class=SensorDeviceClass.VOLUME, entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, @@ -98,7 +100,7 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [ key=OverkizState.CORE_WATER_CONSUMPTION, name="Water consumption", icon="mdi:water", - native_unit_of_measurement=VOLUME_LITERS, + native_unit_of_measurement=UnitOfVolume.LITERS, device_class=SensorDeviceClass.VOLUME, state_class=SensorStateClass.MEASUREMENT, ), @@ -106,7 +108,7 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [ key=OverkizState.IO_OUTLET_ENGINE, name="Outlet engine", icon="mdi:fan-chevron-down", - native_unit_of_measurement=VOLUME_LITERS, + native_unit_of_measurement=UnitOfVolume.LITERS, device_class=SensorDeviceClass.VOLUME, state_class=SensorStateClass.MEASUREMENT, ), @@ -114,7 +116,7 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [ key=OverkizState.IO_INLET_ENGINE, name="Inlet engine", icon="mdi:fan-chevron-up", - native_unit_of_measurement=VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR, + native_unit_of_measurement=UnitOfVolumeFlowRate.CUBIC_METERS_PER_HOUR, state_class=SensorStateClass.MEASUREMENT, ), OverkizSensorDescription( @@ -122,14 +124,14 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [ name="Room temperature", device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, ), OverkizSensorDescription( key=OverkizState.IO_MIDDLE_WATER_TEMPERATURE, name="Middle water temperature", device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, ), OverkizSensorDescription( key=OverkizState.CORE_FOSSIL_ENERGY_CONSUMPTION, @@ -156,21 +158,21 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [ key=OverkizState.CORE_ELECTRIC_ENERGY_CONSUMPTION, name="Electric energy consumption", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_WATT_HOUR, # core:MeasuredValueType = core:ElectricalEnergyInWh (not for modbus:YutakiV2DHWElectricalEnergyConsumptionComponent) + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, # core:MeasuredValueType = core:ElectricalEnergyInWh (not for modbus:YutakiV2DHWElectricalEnergyConsumptionComponent) state_class=SensorStateClass.TOTAL_INCREASING, # core:MeasurementCategory attribute = electric/overall ), OverkizSensorDescription( key=OverkizState.CORE_ELECTRIC_POWER_CONSUMPTION, name="Electric power consumption", device_class=SensorDeviceClass.POWER, - native_unit_of_measurement=POWER_WATT, # core:MeasuredValueType = core:ElectricalEnergyInWh (not for modbus:YutakiV2DHWElectricalEnergyConsumptionComponent) + native_unit_of_measurement=UnitOfPower.WATT, # core:MeasuredValueType = core:ElectricalEnergyInWh (not for modbus:YutakiV2DHWElectricalEnergyConsumptionComponent) state_class=SensorStateClass.MEASUREMENT, ), OverkizSensorDescription( key=OverkizState.CORE_CONSUMPTION_TARIFF1, name="Consumption tariff 1", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_WATT_HOUR, # core:MeasuredValueType = core:ElectricalEnergyInWh + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, # core:MeasuredValueType = core:ElectricalEnergyInWh entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, ), @@ -178,7 +180,7 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [ key=OverkizState.CORE_CONSUMPTION_TARIFF2, name="Consumption tariff 2", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_WATT_HOUR, # core:MeasuredValueType = core:ElectricalEnergyInWh + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, # core:MeasuredValueType = core:ElectricalEnergyInWh entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, ), @@ -186,7 +188,7 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [ key=OverkizState.CORE_CONSUMPTION_TARIFF3, name="Consumption tariff 3", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_WATT_HOUR, # core:MeasuredValueType = core:ElectricalEnergyInWh + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, # core:MeasuredValueType = core:ElectricalEnergyInWh entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, ), @@ -194,7 +196,7 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [ key=OverkizState.CORE_CONSUMPTION_TARIFF4, name="Consumption tariff 4", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_WATT_HOUR, # core:MeasuredValueType = core:ElectricalEnergyInWh + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, # core:MeasuredValueType = core:ElectricalEnergyInWh entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, ), @@ -202,7 +204,7 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [ key=OverkizState.CORE_CONSUMPTION_TARIFF5, name="Consumption tariff 5", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_WATT_HOUR, # core:MeasuredValueType = core:ElectricalEnergyInWh + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, # core:MeasuredValueType = core:ElectricalEnergyInWh entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, ), @@ -210,7 +212,7 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [ key=OverkizState.CORE_CONSUMPTION_TARIFF6, name="Consumption tariff 6", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_WATT_HOUR, # core:MeasuredValueType = core:ElectricalEnergyInWh + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, # core:MeasuredValueType = core:ElectricalEnergyInWh entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, ), @@ -218,7 +220,7 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [ key=OverkizState.CORE_CONSUMPTION_TARIFF7, name="Consumption tariff 7", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_WATT_HOUR, # core:MeasuredValueType = core:ElectricalEnergyInWh + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, # core:MeasuredValueType = core:ElectricalEnergyInWh entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, ), @@ -226,7 +228,7 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [ key=OverkizState.CORE_CONSUMPTION_TARIFF8, name="Consumption tariff 8", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_WATT_HOUR, # core:MeasuredValueType = core:ElectricalEnergyInWh + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, # core:MeasuredValueType = core:ElectricalEnergyInWh entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, ), @@ -234,7 +236,7 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [ key=OverkizState.CORE_CONSUMPTION_TARIFF9, name="Consumption tariff 9", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_WATT_HOUR, # core:MeasuredValueType = core:ElectricalEnergyInWh + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, # core:MeasuredValueType = core:ElectricalEnergyInWh entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, ), @@ -253,7 +255,7 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [ name="Temperature", native_value=lambda value: round(cast(float, value), 2), device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, # core:MeasuredValueType = core:TemperatureInCelcius + native_unit_of_measurement=UnitOfTemperature.CELSIUS, # core:MeasuredValueType = core:TemperatureInCelcius state_class=SensorStateClass.MEASUREMENT, ), # WeatherSensor/WeatherForecastSensor @@ -261,21 +263,21 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [ key=OverkizState.CORE_WEATHER_STATUS, name="Weather status", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, ), OverkizSensorDescription( key=OverkizState.CORE_MINIMUM_TEMPERATURE, name="Minimum temperature", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, ), OverkizSensorDescription( key=OverkizState.CORE_MAXIMUM_TEMPERATURE, name="Maximum temperature", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, ), # AirSensor/COSensor @@ -314,16 +316,18 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [ OverkizSensorDescription( key=OverkizState.IO_SENSOR_ROOM, name="Sensor room", - device_class=OverkizDeviceClass.SENSOR_ROOM, + device_class=SensorDeviceClass.ENUM, + options=["clean", "dirty"], entity_category=EntityCategory.DIAGNOSTIC, icon="mdi:spray-bottle", + translation_key="sensor_room", ), OverkizSensorDescription( key=OverkizState.IO_PRIORITY_LOCK_ORIGINATOR, name="Priority lock originator", - device_class=OverkizDeviceClass.PRIORITY_LOCK_ORIGINATOR, icon="mdi:lock", entity_registry_enabled_default=False, + translation_key="priority_lock_originator", native_value=lambda value: OVERKIZ_STATE_TO_TRANSLATION.get( cast(str, value), cast(str, value) ), @@ -332,7 +336,7 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [ key=OverkizState.CORE_PRIORITY_LOCK_TIMER, name="Priority lock timer", icon="mdi:lock-clock", - native_unit_of_measurement=TIME_SECONDS, + native_unit_of_measurement=UnitOfTime.SECONDS, entity_registry_enabled_default=False, ), OverkizSensorDescription( @@ -340,15 +344,17 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [ name="Discrete RSSI level", entity_registry_enabled_default=False, entity_category=EntityCategory.DIAGNOSTIC, - device_class=OverkizDeviceClass.DISCRETE_RSSI_LEVEL, icon="mdi:wifi", + device_class=SensorDeviceClass.ENUM, + options=["verylow", "low", "normal", "good"], + translation_key="discrete_rssi_level", ), OverkizSensorDescription( key=OverkizState.CORE_SENSOR_DEFECT, name="Sensor defect", entity_registry_enabled_default=False, entity_category=EntityCategory.DIAGNOSTIC, - device_class=OverkizDeviceClass.SENSOR_DEFECT, + translation_key="sensor_defect", native_value=lambda value: OVERKIZ_STATE_TO_TRANSLATION.get( cast(str, value), cast(str, value) ), @@ -359,13 +365,13 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [ name="Heat pump operating time", device_class=SensorDeviceClass.DURATION, entity_category=EntityCategory.DIAGNOSTIC, - native_unit_of_measurement=TIME_SECONDS, + native_unit_of_measurement=UnitOfTime.SECONDS, ), OverkizSensorDescription( key=OverkizState.IO_ELECTRIC_BOOSTER_OPERATING_TIME, name="Electric booster operating time", device_class=SensorDeviceClass.DURATION, - native_unit_of_measurement=TIME_SECONDS, + native_unit_of_measurement=UnitOfTime.SECONDS, entity_category=EntityCategory.DIAGNOSTIC, ), # Cover @@ -379,7 +385,9 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [ OverkizSensorDescription( key=OverkizState.CORE_THREE_WAY_HANDLE_DIRECTION, name="Three way handle direction", - device_class=OverkizDeviceClass.THREE_WAY_HANDLE_DIRECTION, + device_class=SensorDeviceClass.ENUM, + options=["open", "tilt", "close"], + translation_key="three_way_handle_direction", ), ] diff --git a/homeassistant/components/overkiz/strings.json b/homeassistant/components/overkiz/strings.json index 440ed154cfe..5f4f3a04642 100644 --- a/homeassistant/components/overkiz/strings.json +++ b/homeassistant/components/overkiz/strings.json @@ -26,5 +26,78 @@ "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]", "reauth_wrong_account": "You can only reauthenticate this entry with the same Overkiz account and hub" } + }, + "entity": { + "select": { + "open_closed_pedestrian": { + "state": { + "open": "Open", + "pedestrian": "Pedestrian", + "closed": "Closed" + } + }, + "memorized_simple_volume": { + "state": { + "highest": "Highest", + "standard": "Standard" + } + } + }, + "sensor": { + "battery": { + "state": { + "full": "Full", + "low": "Low", + "normal": "Normal", + "verylow": "Very low" + } + }, + "discrete_rssi_level": { + "state": { + "good": "Good", + "low": "Low", + "normal": "Normal", + "verylow": "Very low" + } + }, + "priority_lock_originator": { + "state": { + "lsc": "LSC", + "saac": "SAAC", + "sfc": "SFC", + "ups": "UPS", + "external_gateway": "External gateway", + "local_user": "Local user", + "myself": "Myself", + "rain": "Rain", + "security": "Security", + "temperature": "Temperature", + "timer": "Timer", + "user": "User", + "wind": "Wind" + } + }, + "sensor_room": { + "state": { + "clean": "Clean", + "dirty": "Dirty" + } + }, + "sensor_defect": { + "state": { + "dead": "Dead", + "low_battery": "Low battery", + "maintenance_required": "Maintenance required", + "no_defect": "No defect" + } + }, + "three_way_handle_direction": { + "state": { + "closed": "Closed", + "open": "Open", + "tilt": "Tilt" + } + } + } } } diff --git a/homeassistant/components/overkiz/strings.select.json b/homeassistant/components/overkiz/strings.select.json deleted file mode 100644 index 65abfc3d93b..00000000000 --- a/homeassistant/components/overkiz/strings.select.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "state": { - "overkiz__open_closed_pedestrian": { - "open": "Open", - "pedestrian": "Pedestrian", - "closed": "Closed" - }, - "overkiz__memorized_simple_volume": { - "highest": "Highest", - "standard": "Standard" - } - } -} diff --git a/homeassistant/components/overkiz/strings.sensor.json b/homeassistant/components/overkiz/strings.sensor.json deleted file mode 100644 index fdeaa5b911b..00000000000 --- a/homeassistant/components/overkiz/strings.sensor.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "state": { - "overkiz__battery": { - "full": "Full", - "low": "Low", - "normal": "Normal", - "verylow": "Very low" - }, - "overkiz__discrete_rssi_level": { - "good": "Good", - "low": "Low", - "normal": "Normal", - "verylow": "Very low" - }, - "overkiz__priority_lock_originator": { - "lsc": "LSC", - "saac": "SAAC", - "sfc": "SFC", - "ups": "UPS", - "external_gateway": "External gateway", - "local_user": "Local user", - "myself": "Myself", - "rain": "Rain", - "security": "Security", - "temperature": "Temperature", - "timer": "Timer", - "user": "User", - "wind": "Wind" - }, - "overkiz__sensor_room": { - "clean": "Clean", - "dirty": "Dirty" - }, - "overkiz__sensor_defect": { - "dead": "Dead", - "low_battery": "Low battery", - "maintenance_required": "Maintenance required", - "no_defect": "No defect" - }, - "overkiz__three_way_handle_direction": { - "closed": "Closed", - "open": "Open", - "tilt": "Tilt" - } - } -} diff --git a/homeassistant/components/overkiz/translations/bg.json b/homeassistant/components/overkiz/translations/bg.json index b15966c0221..9f7f0f2cb7b 100644 --- a/homeassistant/components/overkiz/translations/bg.json +++ b/homeassistant/components/overkiz/translations/bg.json @@ -23,5 +23,26 @@ } } } + }, + "entity": { + "sensor": { + "priority_lock_originator": { + "state": { + "local_user": "\u041b\u043e\u043a\u0430\u043b\u0435\u043d \u043f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b", + "lsc": "LSC", + "saac": "SAAC", + "security": "\u0421\u0438\u0433\u0443\u0440\u043d\u043e\u0441\u0442", + "sfc": "SFC", + "temperature": "\u0422\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430", + "ups": "UPS", + "user": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b" + } + }, + "sensor_defect": { + "state": { + "low_battery": "\u0421\u043b\u0430\u0431\u0430 \u0431\u0430\u0442\u0435\u0440\u0438\u044f" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/overkiz/translations/ca.json b/homeassistant/components/overkiz/translations/ca.json index fd2cef81b32..83376f43c25 100644 --- a/homeassistant/components/overkiz/translations/ca.json +++ b/homeassistant/components/overkiz/translations/ca.json @@ -26,5 +26,78 @@ "description": "La plataforma Overkiz \u00e9s utilitzada per diversos venedors com Somfy (Connexoon/TaHoma), Hitachi (Hi Kumo), Rexel (Energeasy Connect) i Atlantic (Cozytouch). Introdueix les credencials d'aplicaci\u00f3 i selecciona el concentrador (hub)." } } + }, + "entity": { + "select": { + "memorized_simple_volume": { + "state": { + "highest": "M\u00e0xim", + "standard": "Est\u00e0ndard" + } + }, + "open_closed_pedestrian": { + "state": { + "closed": "Tancat/da", + "open": "Obert/a", + "pedestrian": "Vianant" + } + } + }, + "sensor": { + "battery": { + "state": { + "full": "Carregada", + "low": "Baixa", + "normal": "Normal", + "verylow": "Molt baixa" + } + }, + "discrete_rssi_level": { + "state": { + "good": "Bo", + "low": "Baix", + "normal": "Normal", + "verylow": "Molt baix" + } + }, + "priority_lock_originator": { + "state": { + "external_gateway": "Passarel\u00b7la externa", + "local_user": "Usuari local", + "lsc": "LSC", + "myself": "Jo", + "rain": "Pluja", + "saac": "SAAC", + "security": "Seguretat", + "sfc": "SFC", + "temperature": "Temperatura", + "timer": "Temporitzador", + "ups": "UPS", + "user": "Usuari", + "wind": "Vent" + } + }, + "sensor_defect": { + "state": { + "dead": "Mort", + "low_battery": "Bateria baixa", + "maintenance_required": "Es necessita manteniment", + "no_defect": "Cap defecte" + } + }, + "sensor_room": { + "state": { + "clean": "Neta", + "dirty": "Brut" + } + }, + "three_way_handle_direction": { + "state": { + "closed": "Tancat/da", + "open": "Obert/a", + "tilt": "Inclinada" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/overkiz/translations/de.json b/homeassistant/components/overkiz/translations/de.json index 7b69582fe25..c9b96f4efc7 100644 --- a/homeassistant/components/overkiz/translations/de.json +++ b/homeassistant/components/overkiz/translations/de.json @@ -26,5 +26,78 @@ "description": "Die Overkiz-Plattform wird von verschiedenen Anbietern wie Somfy (Connexoon / TaHoma), Hitachi (Hi Kumo), Rexel (Energeasy Connect) und Atlantic (Cozytouch) verwendet. Gib deine Anmeldeinformationen ein und w\u00e4hle deinen Hub aus." } } + }, + "entity": { + "select": { + "memorized_simple_volume": { + "state": { + "highest": "H\u00f6chste", + "standard": "Standard" + } + }, + "open_closed_pedestrian": { + "state": { + "closed": "Geschlossen", + "open": "Offen", + "pedestrian": "Fu\u00dfg\u00e4nger" + } + } + }, + "sensor": { + "battery": { + "state": { + "full": "Voll", + "low": "Niedrig", + "normal": "Normal", + "verylow": "Sehr niedrig" + } + }, + "discrete_rssi_level": { + "state": { + "good": "Gut", + "low": "Niedrig", + "normal": "Normal", + "verylow": "Sehr niedrig" + } + }, + "priority_lock_originator": { + "state": { + "external_gateway": "Externes Gateway", + "local_user": "Lokaler Benutzer", + "lsc": "LSC", + "myself": "Mich", + "rain": "Regen", + "saac": "SAAC", + "security": "Sicherheit", + "sfc": "SFC", + "temperature": "Temperatur", + "timer": "Timer", + "ups": "USV", + "user": "Benutzer", + "wind": "Wind" + } + }, + "sensor_defect": { + "state": { + "dead": "Nicht Erreichbar", + "low_battery": "Niedriger Batteriestatus", + "maintenance_required": "Wartung erforderlich", + "no_defect": "Kein Fehler" + } + }, + "sensor_room": { + "state": { + "clean": "Sauber", + "dirty": "Schmutzig" + } + }, + "three_way_handle_direction": { + "state": { + "closed": "Geschlossen", + "open": "Offen", + "tilt": "Kippen" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/overkiz/translations/el.json b/homeassistant/components/overkiz/translations/el.json index 4a61e3ac380..31142c87063 100644 --- a/homeassistant/components/overkiz/translations/el.json +++ b/homeassistant/components/overkiz/translations/el.json @@ -26,5 +26,78 @@ "description": "\u0397 \u03c0\u03bb\u03b1\u03c4\u03c6\u03cc\u03c1\u03bc\u03b1 Overkiz \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03b4\u03b9\u03ac\u03c6\u03bf\u03c1\u03bf\u03c5\u03c2 \u03c0\u03c1\u03bf\u03bc\u03b7\u03b8\u03b5\u03c5\u03c4\u03ad\u03c2 \u03cc\u03c0\u03c9\u03c2 \u03b7 Somfy (Connexoon / TaHoma), \u03b7 Hitachi (Hi Kumo), \u03b7 Rexel (Energeasy Connect) \u03ba\u03b1\u03b9 \u03b7 Atlantic (Cozytouch). \u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b1 \u03b4\u03b9\u03b1\u03c0\u03b9\u03c3\u03c4\u03b5\u03c5\u03c4\u03ae\u03c1\u03b9\u03b1 \u03c4\u03b7\u03c2 \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae\u03c2 \u03c3\u03b1\u03c2 \u03ba\u03b1\u03b9 \u03b5\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c4\u03bf\u03bd \u03ba\u03cc\u03bc\u03b2\u03bf \u03c3\u03b1\u03c2." } } + }, + "entity": { + "select": { + "memorized_simple_volume": { + "state": { + "highest": "\u03a5\u03c8\u03b7\u03bb\u03cc\u03c4\u03b5\u03c1\u03bf", + "standard": "\u03a4\u03c5\u03c0\u03b9\u03ba\u03cc" + } + }, + "open_closed_pedestrian": { + "state": { + "closed": "\u039a\u03bb\u03b5\u03b9\u03c3\u03c4\u03cc", + "open": "\u0391\u03bd\u03bf\u03b9\u03c7\u03c4\u03cc", + "pedestrian": "\u03a0\u03b5\u03b6\u03cc\u03c2" + } + } + }, + "sensor": { + "battery": { + "state": { + "full": "\u03a0\u03bb\u03ae\u03c1\u03b7\u03c2", + "low": "\u03a7\u03b1\u03bc\u03b7\u03bb\u03ae", + "normal": "\u039a\u03b1\u03bd\u03bf\u03bd\u03b9\u03ba\u03ae", + "verylow": "\u03a0\u03bf\u03bb\u03cd \u03c7\u03b1\u03bc\u03b7\u03bb\u03ae" + } + }, + "discrete_rssi_level": { + "state": { + "good": "\u039a\u03b1\u03bb\u03cc", + "low": "\u03a7\u03b1\u03bc\u03b7\u03bb\u03cc", + "normal": "\u039a\u03b1\u03bd\u03bf\u03bd\u03b9\u03ba\u03cc", + "verylow": "\u03a0\u03bf\u03bb\u03cd \u03c7\u03b1\u03bc\u03b7\u03bb\u03cc" + } + }, + "priority_lock_originator": { + "state": { + "external_gateway": "\u0395\u03be\u03c9\u03c4\u03b5\u03c1\u03b9\u03ba\u03ae \u03c0\u03cd\u03bb\u03b7", + "local_user": "\u03a4\u03bf\u03c0\u03b9\u03ba\u03cc\u03c2 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7\u03c2", + "lsc": "LSC", + "myself": "\u0395\u03b3\u03ce \u03bf \u03af\u03b4\u03b9\u03bf\u03c2", + "rain": "\u0392\u03c1\u03bf\u03c7\u03ae", + "saac": "SAAC", + "security": "\u0391\u03c3\u03c6\u03ac\u03bb\u03b5\u03b9\u03b1", + "sfc": "SFC", + "temperature": "\u0398\u03b5\u03c1\u03bc\u03bf\u03ba\u03c1\u03b1\u03c3\u03af\u03b1", + "timer": "\u03a7\u03c1\u03bf\u03bd\u03bf\u03b4\u03b9\u03b1\u03ba\u03cc\u03c0\u03c4\u03b7\u03c2", + "ups": "UPS", + "user": "\u03a7\u03c1\u03ae\u03c3\u03c4\u03b7\u03c2", + "wind": "\u0386\u03bd\u03b5\u03bc\u03bf\u03c2" + } + }, + "sensor_defect": { + "state": { + "dead": "\u039d\u03b5\u03ba\u03c1\u03cc", + "low_battery": "\u03a7\u03b1\u03bc\u03b7\u03bb\u03ae \u03bc\u03c0\u03b1\u03c4\u03b1\u03c1\u03af\u03b1", + "maintenance_required": "\u0391\u03c0\u03b1\u03b9\u03c4\u03b5\u03af\u03c4\u03b1\u03b9 \u03c3\u03c5\u03bd\u03c4\u03ae\u03c1\u03b7\u03c3\u03b7", + "no_defect": "\u03a7\u03c9\u03c1\u03af\u03c2 \u03b5\u03bb\u03ac\u03c4\u03c4\u03c9\u03bc\u03b1" + } + }, + "sensor_room": { + "state": { + "clean": "\u039a\u03b1\u03b8\u03b1\u03c1\u03cc", + "dirty": "\u0392\u03c1\u03ce\u03bc\u03b9\u03ba\u03bf" + } + }, + "three_way_handle_direction": { + "state": { + "closed": "\u039a\u03bb\u03b5\u03b9\u03c3\u03c4\u03cc", + "open": "\u0391\u03bd\u03bf\u03b9\u03c7\u03c4\u03cc", + "tilt": "\u039a\u03bb\u03af\u03c3\u03b7" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/overkiz/translations/en.json b/homeassistant/components/overkiz/translations/en.json index 2c534a64cb6..7232525775c 100644 --- a/homeassistant/components/overkiz/translations/en.json +++ b/homeassistant/components/overkiz/translations/en.json @@ -26,5 +26,78 @@ "description": "The Overkiz platform is used by various vendors like Somfy (Connexoon / TaHoma), Hitachi (Hi Kumo), Rexel (Energeasy Connect) and Atlantic (Cozytouch). Enter your application credentials and select your hub." } } + }, + "entity": { + "select": { + "memorized_simple_volume": { + "state": { + "highest": "Highest", + "standard": "Standard" + } + }, + "open_closed_pedestrian": { + "state": { + "closed": "Closed", + "open": "Open", + "pedestrian": "Pedestrian" + } + } + }, + "sensor": { + "battery": { + "state": { + "full": "Full", + "low": "Low", + "normal": "Normal", + "verylow": "Very low" + } + }, + "discrete_rssi_level": { + "state": { + "good": "Good", + "low": "Low", + "normal": "Normal", + "verylow": "Very low" + } + }, + "priority_lock_originator": { + "state": { + "external_gateway": "External gateway", + "local_user": "Local user", + "lsc": "LSC", + "myself": "Myself", + "rain": "Rain", + "saac": "SAAC", + "security": "Security", + "sfc": "SFC", + "temperature": "Temperature", + "timer": "Timer", + "ups": "UPS", + "user": "User", + "wind": "Wind" + } + }, + "sensor_defect": { + "state": { + "dead": "Dead", + "low_battery": "Low battery", + "maintenance_required": "Maintenance required", + "no_defect": "No defect" + } + }, + "sensor_room": { + "state": { + "clean": "Clean", + "dirty": "Dirty" + } + }, + "three_way_handle_direction": { + "state": { + "closed": "Closed", + "open": "Open", + "tilt": "Tilt" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/overkiz/translations/es.json b/homeassistant/components/overkiz/translations/es.json index aae98a32900..d7b1ebbeb47 100644 --- a/homeassistant/components/overkiz/translations/es.json +++ b/homeassistant/components/overkiz/translations/es.json @@ -26,5 +26,78 @@ "description": "La plataforma Overkiz es usada por varios proveedores como Somfy (Connexoon / TaHoma), Hitachi (Hi Kumo), Rexel (Energeasy Connect) y Atlantic (Cozytouch). Introduce las credenciales de tu aplicaci\u00f3n y selecciona tu concentrador." } } + }, + "entity": { + "select": { + "memorized_simple_volume": { + "state": { + "highest": "El m\u00e1s alto", + "standard": "Est\u00e1ndar" + } + }, + "open_closed_pedestrian": { + "state": { + "closed": "Cerrada", + "open": "Abierta", + "pedestrian": "Peatonal" + } + } + }, + "sensor": { + "battery": { + "state": { + "full": "Completa", + "low": "Baja", + "normal": "Normal", + "verylow": "Muy baja" + } + }, + "discrete_rssi_level": { + "state": { + "good": "Bueno", + "low": "Bajo", + "normal": "Normal", + "verylow": "Muy bajo" + } + }, + "priority_lock_originator": { + "state": { + "external_gateway": "Puerta de enlace externa", + "local_user": "Usuario local", + "lsc": "LSC", + "myself": "Yo mismo", + "rain": "Lluvia", + "saac": "SAAC", + "security": "Seguridad", + "sfc": "SFC", + "temperature": "Temperatura", + "timer": "Temporizador", + "ups": "SAI", + "user": "Usuario", + "wind": "Viento" + } + }, + "sensor_defect": { + "state": { + "dead": "Muerto", + "low_battery": "Bater\u00eda baja", + "maintenance_required": "Requiere mantenimiento", + "no_defect": "Sin defectos" + } + }, + "sensor_room": { + "state": { + "clean": "Limpio", + "dirty": "Sucio" + } + }, + "three_way_handle_direction": { + "state": { + "closed": "Cerrado", + "open": "Abierto", + "tilt": "Inclinaci\u00f3n" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/overkiz/translations/et.json b/homeassistant/components/overkiz/translations/et.json index 1e34227b0d1..d972cfdbd08 100644 --- a/homeassistant/components/overkiz/translations/et.json +++ b/homeassistant/components/overkiz/translations/et.json @@ -26,5 +26,78 @@ "description": "Overkizi platvormi kasutavad erinevad m\u00fc\u00fcjad, nagu Somfy (Connexoon / TaHoma), Hitachi (Hi Kumo), Rexel (Energeasy Connect) ja Atlantic (Cozytouch). Sisesta oma rakenduse mandaadid ja vali hub." } } + }, + "entity": { + "select": { + "memorized_simple_volume": { + "state": { + "highest": "K\u00f5rgeim", + "standard": "Tavaline" + } + }, + "open_closed_pedestrian": { + "state": { + "closed": "Suletud", + "open": "Avatud", + "pedestrian": "Jalak\u00e4ija" + } + } + }, + "sensor": { + "battery": { + "state": { + "full": "T\u00e4is", + "low": "Madal", + "normal": "Tavaline", + "verylow": "V\u00e4ga madal" + } + }, + "discrete_rssi_level": { + "state": { + "good": "Hea", + "low": "Madal", + "normal": "Tavaline", + "verylow": "V\u00e4ga madal" + } + }, + "priority_lock_originator": { + "state": { + "external_gateway": "V\u00e4line l\u00fc\u00fcs", + "local_user": "Kohalik kasutaja", + "lsc": "LSC", + "myself": "Kasutaja", + "rain": "Vihm", + "saac": "SAAC", + "security": "Turvalisus", + "sfc": "SFC", + "temperature": "Temperatuur", + "timer": "Taimer", + "ups": "UPS", + "user": "Kasutaja", + "wind": "Tuul" + } + }, + "sensor_defect": { + "state": { + "dead": "Surnud", + "low_battery": "Madal akutase", + "maintenance_required": "Vajab hooldust", + "no_defect": "Korras" + } + }, + "sensor_room": { + "state": { + "clean": "Puhas", + "dirty": "R\u00e4pane" + } + }, + "three_way_handle_direction": { + "state": { + "closed": "Suletud", + "open": "Avatud", + "tilt": "Kallutamine" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/overkiz/translations/hu.json b/homeassistant/components/overkiz/translations/hu.json index 85827bcee50..331e4cd4e0a 100644 --- a/homeassistant/components/overkiz/translations/hu.json +++ b/homeassistant/components/overkiz/translations/hu.json @@ -26,5 +26,78 @@ "description": "Az Overkiz platformot k\u00fcl\u00f6nb\u00f6z\u0151 gy\u00e1rt\u00f3k haszn\u00e1lj\u00e1k, mint p\u00e9ld\u00e1ul a Somfy (Connexoon / TaHoma), a Hitachi (Hi Kumo), a Rexel (Energeasy Connect) \u00e9s az Atlantic (Cozytouch). Adja meg az alkalmaz\u00e1s hiteles\u00edt\u0151 adatait, \u00e9s v\u00e1lassza ki a hubot." } } + }, + "entity": { + "select": { + "memorized_simple_volume": { + "state": { + "highest": "Legmagasabb", + "standard": "Standard" + } + }, + "open_closed_pedestrian": { + "state": { + "closed": "Z\u00e1rva", + "open": "Nyitva", + "pedestrian": "Gyalogos" + } + } + }, + "sensor": { + "battery": { + "state": { + "full": "Felt\u00f6ltve", + "low": "Alacsony", + "normal": "Norm\u00e1l", + "verylow": "Nagyon alacsony" + } + }, + "discrete_rssi_level": { + "state": { + "good": "J\u00f3", + "low": "Alacsony", + "normal": "Norm\u00e1l", + "verylow": "Nagyon alacsony" + } + }, + "priority_lock_originator": { + "state": { + "external_gateway": "K\u00fcls\u0151 \u00e1tj\u00e1r\u00f3", + "local_user": "Helyi felhaszn\u00e1l\u00f3", + "lsc": "LSC", + "myself": "J\u00f3magam", + "rain": "Es\u0151", + "saac": "SAAC", + "security": "Biztons\u00e1g", + "sfc": "SFC", + "temperature": "H\u0151m\u00e9rs\u00e9klet", + "timer": "Id\u0151z\u00edt\u0151", + "ups": "UPS", + "user": "Felhaszn\u00e1l\u00f3", + "wind": "Sz\u00e9l" + } + }, + "sensor_defect": { + "state": { + "dead": "Nem ad \u00e9letjelet", + "low_battery": "Alacsony t\u00f6lt\u00f6tts\u00e9g\u0171 akkumul\u00e1tor", + "maintenance_required": "Karbantart\u00e1s sz\u00fcks\u00e9ges", + "no_defect": "Nincs hiba" + } + }, + "sensor_room": { + "state": { + "clean": "Tiszta", + "dirty": "Piszkos" + } + }, + "three_way_handle_direction": { + "state": { + "closed": "Z\u00e1rva", + "open": "Nyitva", + "tilt": "D\u00f6ntve" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/overkiz/translations/id.json b/homeassistant/components/overkiz/translations/id.json index 0fa0e0b22bb..c1f46143e62 100644 --- a/homeassistant/components/overkiz/translations/id.json +++ b/homeassistant/components/overkiz/translations/id.json @@ -26,5 +26,78 @@ "description": "Platform Overkiz digunakan oleh berbagai vendor seperti Somfy (Connexoon/TaHoma), Hitachi (Hi Kumo), Rexel (Energeasy Connect), dan Atlantic (Cozytouch). Masukkan kredensial aplikasi Anda dan pilih hub Anda." } } + }, + "entity": { + "select": { + "memorized_simple_volume": { + "state": { + "highest": "Tertinggi", + "standard": "Standar" + } + }, + "open_closed_pedestrian": { + "state": { + "closed": "Tutup", + "open": "Buka", + "pedestrian": "Pejalan Kaki" + } + } + }, + "sensor": { + "battery": { + "state": { + "full": "Penuh", + "low": "Rendah", + "normal": "Normal", + "verylow": "Sangat rendah" + } + }, + "discrete_rssi_level": { + "state": { + "good": "Bagus", + "low": "Rendah", + "normal": "Normal", + "verylow": "Sangat rendah" + } + }, + "priority_lock_originator": { + "state": { + "external_gateway": "Gateway eksternal", + "local_user": "Pengguna lokal", + "lsc": "LSC", + "myself": "Saya sendiri", + "rain": "Hujan", + "saac": "SAAC", + "security": "Keamanan", + "sfc": "SFC", + "temperature": "Suhu", + "timer": "Timer", + "ups": "UPS", + "user": "Pengguna", + "wind": "Angin" + } + }, + "sensor_defect": { + "state": { + "dead": "Mati", + "low_battery": "Baterai lemah", + "maintenance_required": "Diperlukan perawatan", + "no_defect": "Tidak ada cacat" + } + }, + "sensor_room": { + "state": { + "clean": "Bersih", + "dirty": "Kotor" + } + }, + "three_way_handle_direction": { + "state": { + "closed": "Tutup", + "open": "Buka", + "tilt": "Miring" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/overkiz/translations/it.json b/homeassistant/components/overkiz/translations/it.json index 96a337b84e9..b6cef2ec242 100644 --- a/homeassistant/components/overkiz/translations/it.json +++ b/homeassistant/components/overkiz/translations/it.json @@ -26,5 +26,78 @@ "description": "La piattaforma Overkiz \u00e8 utilizzata da vari fornitori come Somfy (Connexoon / TaHoma), Hitachi (Hi Kumo), Rexel (Energeasy Connect) e Atlantic (Cozytouch). Inserisci le credenziali dell'applicazione e seleziona il tuo hub." } } + }, + "entity": { + "select": { + "memorized_simple_volume": { + "state": { + "highest": "Il pi\u00f9 alto", + "standard": "Standard" + } + }, + "open_closed_pedestrian": { + "state": { + "closed": "Chiuso", + "open": "Aperto", + "pedestrian": "Pedone" + } + } + }, + "sensor": { + "battery": { + "state": { + "full": "Piena", + "low": "Bassa", + "normal": "Normale", + "verylow": "Molto bassa" + } + }, + "discrete_rssi_level": { + "state": { + "good": "Buono", + "low": "Basso", + "normal": "Normale", + "verylow": "Molto basso" + } + }, + "priority_lock_originator": { + "state": { + "external_gateway": "Portale esterno", + "local_user": "Utente locale", + "lsc": "LSC", + "myself": "Me stesso", + "rain": "Pioggia", + "saac": "SAAC", + "security": "Sicurezza", + "sfc": "SFC", + "temperature": "Temperatura", + "timer": "Timer", + "ups": "UPS", + "user": "Utente", + "wind": "Vento" + } + }, + "sensor_defect": { + "state": { + "dead": "Morto", + "low_battery": "Batteria scarica", + "maintenance_required": "Manutenzione necessaria", + "no_defect": "Nessun difetto" + } + }, + "sensor_room": { + "state": { + "clean": "Pulito", + "dirty": "Sporco" + } + }, + "three_way_handle_direction": { + "state": { + "closed": "Chiuso", + "open": "Aperto", + "tilt": "Inclinazione" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/overkiz/translations/ko.json b/homeassistant/components/overkiz/translations/ko.json new file mode 100644 index 00000000000..61415a839bc --- /dev/null +++ b/homeassistant/components/overkiz/translations/ko.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8", + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/overkiz/translations/nl.json b/homeassistant/components/overkiz/translations/nl.json index 957ca0862fc..f2cce55db16 100644 --- a/homeassistant/components/overkiz/translations/nl.json +++ b/homeassistant/components/overkiz/translations/nl.json @@ -25,5 +25,46 @@ "description": "Het Overkiz platform wordt gebruikt door fabrikanten zoals Somfy (Connexoon / TaHoma), Hitachi (Hi Kumo), Rexel (Energeasy Connect) en Atlantic (Cozytouch). Voer je inloggegevens in en selecteer je hub." } } + }, + "entity": { + "select": { + "open_closed_pedestrian": { + "state": { + "closed": "Gesloten", + "open": "Open" + } + } + }, + "sensor": { + "battery": { + "state": { + "full": "Vol", + "low": "Laag", + "normal": "Normaal" + } + }, + "discrete_rssi_level": { + "state": { + "good": "Goed" + } + }, + "priority_lock_originator": { + "state": { + "temperature": "Temperatuur", + "user": "Gebruiker" + } + }, + "sensor_room": { + "state": { + "clean": "Schoon" + } + }, + "three_way_handle_direction": { + "state": { + "closed": "Gesloten", + "open": "Open" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/overkiz/translations/no.json b/homeassistant/components/overkiz/translations/no.json index a1c7c03853d..31596ecace3 100644 --- a/homeassistant/components/overkiz/translations/no.json +++ b/homeassistant/components/overkiz/translations/no.json @@ -26,5 +26,78 @@ "description": "Overkiz-plattformen brukes av forskjellige leverand\u00f8rer som Somfy (Connexoon / TaHoma), Hitachi (Hi Kumo), Rexel (Energeasy Connect) og Atlantic (Cozytouch). Skriv inn applikasjonslegitimasjonen din og velg hub." } } + }, + "entity": { + "select": { + "memorized_simple_volume": { + "state": { + "highest": "H\u00f8yest", + "standard": "Standard" + } + }, + "open_closed_pedestrian": { + "state": { + "closed": "Lukket", + "open": "\u00c5pen", + "pedestrian": "Fotgjenger" + } + } + }, + "sensor": { + "battery": { + "state": { + "full": "Full", + "low": "Lav", + "normal": "Normal", + "verylow": "Veldig lav" + } + }, + "discrete_rssi_level": { + "state": { + "good": "Bra", + "low": "Lav", + "normal": "Normal", + "verylow": "Veldig lav" + } + }, + "priority_lock_originator": { + "state": { + "external_gateway": "Ekstern gateway", + "local_user": "Lokal bruker", + "lsc": "LSC", + "myself": "Meg selv", + "rain": "Regn", + "saac": "SAAC", + "security": "Sikkerhet", + "sfc": "SFC", + "temperature": "Temperatur", + "timer": "Timer", + "ups": "UPS", + "user": "Bruker", + "wind": "Vind" + } + }, + "sensor_defect": { + "state": { + "dead": "D\u00f8d", + "low_battery": "Lavt batteriniv\u00e5", + "maintenance_required": "Vedlikehold kreves", + "no_defect": "Ingen feil" + } + }, + "sensor_room": { + "state": { + "clean": "Ren", + "dirty": "Skitten" + } + }, + "three_way_handle_direction": { + "state": { + "closed": "Lukket", + "open": "\u00c5pen", + "tilt": "Tilt" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/overkiz/translations/pl.json b/homeassistant/components/overkiz/translations/pl.json index 87c365c099f..9df39432bc4 100644 --- a/homeassistant/components/overkiz/translations/pl.json +++ b/homeassistant/components/overkiz/translations/pl.json @@ -26,5 +26,78 @@ "description": "Platforma Overkiz jest u\u017cywana przez r\u00f3\u017cnych dostawc\u00f3w, takich jak Somfy (Connexoon / TaHoma), Hitachi (Hi Kumo), Rexel (Energeasy Connect) i Atlantic (Cozytouch). Wprowad\u017a po\u015bwiadczenia aplikacji i wybierz sw\u00f3j hub." } } + }, + "entity": { + "select": { + "memorized_simple_volume": { + "state": { + "highest": "najwy\u017csze", + "standard": "normalnie" + } + }, + "open_closed_pedestrian": { + "state": { + "closed": "zamkni\u0119ta", + "open": "otwarta", + "pedestrian": "pieszy" + } + } + }, + "sensor": { + "battery": { + "state": { + "full": "pe\u0142na", + "low": "s\u0142aba", + "normal": "normalna", + "verylow": "bardzo s\u0142aba" + } + }, + "discrete_rssi_level": { + "state": { + "good": "dobry", + "low": "niski", + "normal": "normalny", + "verylow": "bardzo niski" + } + }, + "priority_lock_originator": { + "state": { + "external_gateway": "bramka zewn\u0119trzna", + "local_user": "u\u017cytkownik lokalny", + "lsc": "LSC", + "myself": "ja", + "rain": "deszcz", + "saac": "SAAC", + "security": "bezpiecze\u0144stwo", + "sfc": "SFC", + "temperature": "temperatura", + "timer": "minutnik", + "ups": "UPS", + "user": "u\u017cytkownik", + "wind": "wiatr" + } + }, + "sensor_defect": { + "state": { + "dead": "martwy", + "low_battery": "niski poziom baterii", + "maintenance_required": "wymagany przegl\u0105d", + "no_defect": "brak uszkodze\u0144" + } + }, + "sensor_room": { + "state": { + "clean": "czysto", + "dirty": "brudno" + } + }, + "three_way_handle_direction": { + "state": { + "closed": "zamkni\u0119ta", + "open": "otwarta", + "tilt": "uchylona" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/overkiz/translations/pt-BR.json b/homeassistant/components/overkiz/translations/pt-BR.json index a7e3f6ff206..b41fbdb9ed2 100644 --- a/homeassistant/components/overkiz/translations/pt-BR.json +++ b/homeassistant/components/overkiz/translations/pt-BR.json @@ -26,5 +26,78 @@ "description": "A plataforma Overkiz \u00e9 utilizada por v\u00e1rios fornecedores como Somfy (Connexoon/TaHoma), Hitachi (Hi Kumo), Rexel (Energeasy Connect) e Atlantic (Cozytouch). Insira suas credenciais de aplicativo e selecione seu hub." } } + }, + "entity": { + "select": { + "memorized_simple_volume": { + "state": { + "highest": "Alt\u00edssimo", + "standard": "Padr\u00e3o" + } + }, + "open_closed_pedestrian": { + "state": { + "closed": "Fechado", + "open": "Aberto", + "pedestrian": "Pedestre" + } + } + }, + "sensor": { + "battery": { + "state": { + "full": "Cheio", + "low": "Baixo", + "normal": "Normal", + "verylow": "Muito baixo" + } + }, + "discrete_rssi_level": { + "state": { + "good": "Bom", + "low": "Baixo", + "normal": "Normal", + "verylow": "Muito baixo" + } + }, + "priority_lock_originator": { + "state": { + "external_gateway": "Gateway externo", + "local_user": "Usu\u00e1rio local", + "lsc": "LSC", + "myself": "Eu mesmo", + "rain": "Chuva", + "saac": "SAAC", + "security": "Seguran\u00e7a", + "sfc": "SFC", + "temperature": "Temperatura", + "timer": "Cron\u00f4metro", + "ups": "UPS", + "user": "Usu\u00e1rio", + "wind": "Vento" + } + }, + "sensor_defect": { + "state": { + "dead": "Morto", + "low_battery": "Bateria fraca", + "maintenance_required": "Manuten\u00e7\u00e3o requerida", + "no_defect": "Sem defeito" + } + }, + "sensor_room": { + "state": { + "clean": "Limpar", + "dirty": "Sujo" + } + }, + "three_way_handle_direction": { + "state": { + "closed": "Fechado", + "open": "Aberto", + "tilt": "Inclinar" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/overkiz/translations/ru.json b/homeassistant/components/overkiz/translations/ru.json index 67ca42a6947..644b14e1e6f 100644 --- a/homeassistant/components/overkiz/translations/ru.json +++ b/homeassistant/components/overkiz/translations/ru.json @@ -26,5 +26,78 @@ "description": "\u041f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430 Overkiz \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u043c\u0438 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044f\u043c\u0438, \u0442\u0430\u043a\u0438\u043c\u0438 \u043a\u0430\u043a Somfy (Connexoon / TaHoma), Hitachi (Hi Kumo), Rexel (Energeasy Connect) \u0438 Atlantic (Cozytouch). \u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0443\u0447\u0435\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0412\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438 \u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0445\u0430\u0431." } } + }, + "entity": { + "select": { + "memorized_simple_volume": { + "state": { + "highest": "\u0421\u0430\u043c\u044b\u0439 \u0432\u044b\u0441\u043e\u043a\u0438\u0439", + "standard": "\u0421\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439" + } + }, + "open_closed_pedestrian": { + "state": { + "closed": "\u0417\u0430\u043a\u0440\u044b\u0442\u043e", + "open": "\u041e\u0442\u043a\u0440\u044b\u0442\u043e", + "pedestrian": "\u041f\u0435\u0448\u0435\u0445\u043e\u0434\u043d\u044b\u0439" + } + } + }, + "sensor": { + "battery": { + "state": { + "full": "\u041f\u043e\u043b\u043d\u044b\u0439", + "low": "\u041d\u0438\u0437\u043a\u0438\u0439", + "normal": "\u041d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u044b\u0439", + "verylow": "\u041e\u0447\u0435\u043d\u044c \u043d\u0438\u0437\u043a\u0438\u0439" + } + }, + "discrete_rssi_level": { + "state": { + "good": "\u0425\u043e\u0440\u043e\u0448\u0438\u0439", + "low": "\u041d\u0438\u0437\u043a\u0438\u0439", + "normal": "\u041d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u044b\u0439", + "verylow": "\u041e\u0447\u0435\u043d\u044c \u043d\u0438\u0437\u043a\u0438\u0439" + } + }, + "priority_lock_originator": { + "state": { + "external_gateway": "\u0412\u043d\u0435\u0448\u043d\u0438\u0439 \u0448\u043b\u044e\u0437", + "local_user": "\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c", + "lsc": "LSC", + "myself": "\u0421\u0435\u0431\u044f", + "rain": "\u0414\u043e\u0436\u0434\u044c", + "saac": "SAAC", + "security": "\u0411\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u044c", + "sfc": "SFC", + "temperature": "\u0422\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430", + "timer": "\u0422\u0430\u0439\u043c\u0435\u0440", + "ups": "\u0418\u0411\u041f", + "user": "\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c", + "wind": "\u0412\u0435\u0442\u0435\u0440" + } + }, + "sensor_defect": { + "state": { + "dead": "\u041d\u0435\u0438\u0441\u043f\u0440\u0430\u0432\u043d\u043e", + "low_battery": "\u041d\u0438\u0437\u043a\u0438\u0439 \u0437\u0430\u0440\u044f\u0434 \u0431\u0430\u0442\u0430\u0440\u0435\u0438", + "maintenance_required": "\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043e\u0431\u0441\u043b\u0443\u0436\u0438\u0432\u0430\u043d\u0438\u0435", + "no_defect": "\u041d\u0435\u0442 \u0434\u0435\u0444\u0435\u043a\u0442\u0430" + } + }, + "sensor_room": { + "state": { + "clean": "\u0427\u0438\u0441\u0442\u043e", + "dirty": "\u0413\u0440\u044f\u0437\u043d\u043e" + } + }, + "three_way_handle_direction": { + "state": { + "closed": "\u0417\u0430\u043a\u0440\u044b\u0442\u043e", + "open": "\u041e\u0442\u043a\u0440\u044b\u0442\u043e", + "tilt": "\u041d\u0430\u043a\u043b\u043e\u043d" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/overkiz/translations/select.sk.json b/homeassistant/components/overkiz/translations/select.sk.json index 460b1d67415..b75517f8a7e 100644 --- a/homeassistant/components/overkiz/translations/select.sk.json +++ b/homeassistant/components/overkiz/translations/select.sk.json @@ -1,6 +1,12 @@ { "state": { + "overkiz__memorized_simple_volume": { + "highest": "Najvy\u0161\u0161ie", + "standard": "\u0160tandardn\u00e9" + }, "overkiz__open_closed_pedestrian": { + "closed": "Zatvoren\u00fd", + "open": "Otvoren\u00fd", "pedestrian": "Chodec" } } diff --git a/homeassistant/components/overkiz/translations/sensor.sk.json b/homeassistant/components/overkiz/translations/sensor.sk.json index 848abd98c72..7452c520411 100644 --- a/homeassistant/components/overkiz/translations/sensor.sk.json +++ b/homeassistant/components/overkiz/translations/sensor.sk.json @@ -13,8 +13,14 @@ "verylow": "Ve\u013emi n\u00edzke" }, "overkiz__priority_lock_originator": { + "external_gateway": "Extern\u00e1 br\u00e1na", "local_user": "Miestny pou\u017e\u00edvate\u013e", + "lsc": "LSC", + "myself": "Ja s\u00e1m", "rain": "D\u00e1\u017e\u010f", + "saac": "SAAC", + "security": "Bezpe\u010dnos\u0165", + "sfc": "SFC", "temperature": "Teplota", "timer": "\u010casova\u010d", "ups": "UPS", @@ -22,11 +28,19 @@ "wind": "Vietor" }, "overkiz__sensor_defect": { - "low_battery": "Slab\u00e1 bat\u00e9ria" + "dead": "Nedostupn\u00e9", + "low_battery": "Slab\u00e1 bat\u00e9ria", + "maintenance_required": "Vy\u017eaduje sa \u00fadr\u017eba", + "no_defect": "Bez defektu" + }, + "overkiz__sensor_room": { + "clean": "\u010cist\u00fd", + "dirty": "\u0160pinav\u00fd" }, "overkiz__three_way_handle_direction": { "closed": "Zatvoren\u00e9", - "open": "Otvori\u0165" + "open": "Otvori\u0165", + "tilt": "Naklonenie" } } } \ No newline at end of file diff --git a/homeassistant/components/overkiz/translations/sk.json b/homeassistant/components/overkiz/translations/sk.json index 2d386b6d45a..0d46931b871 100644 --- a/homeassistant/components/overkiz/translations/sk.json +++ b/homeassistant/components/overkiz/translations/sk.json @@ -2,7 +2,8 @@ "config": { "abort": { "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", - "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", + "reauth_wrong_account": "Tento z\u00e1znam m\u00f4\u017eete op\u00e4tovne overi\u0165 iba pomocou rovnak\u00e9ho \u00fa\u010dtu Overkiz a hubu" }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", @@ -18,8 +19,83 @@ "user": { "data": { "host": "Hostite\u013e", + "hub": "Hub", "password": "Heslo", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" + }, + "description": "Platformu Overkiz pou\u017e\u00edvaj\u00fa r\u00f4zni predajcovia ako Somfy (Connexoon / TaHoma), Hitachi (Hi Kumo), Rexel (Energeasy Connect) a Atlantic (Cozytouch). Zadajte prihlasovacie \u00fadaje aplik\u00e1cie a vyberte svoje centrum." + } + } + }, + "entity": { + "select": { + "memorized_simple_volume": { + "state": { + "highest": "Najvy\u0161\u0161ie", + "standard": "\u0160tandardn\u00e9" + } + }, + "open_closed_pedestrian": { + "state": { + "closed": "Zatvoren\u00fd", + "open": "Otvoren\u00fd", + "pedestrian": "Chodec" + } + } + }, + "sensor": { + "battery": { + "state": { + "full": "Pln\u00e1", + "low": "Slab\u00e1", + "normal": "Norm\u00e1lna", + "verylow": "Ve\u013emi n\u00edzka" + } + }, + "discrete_rssi_level": { + "state": { + "good": "Dobr\u00fd", + "low": "N\u00edzky", + "normal": "Norm\u00e1lny", + "verylow": "Ve\u013emi n\u00edzky" + } + }, + "priority_lock_originator": { + "state": { + "external_gateway": "Extern\u00e1 br\u00e1na", + "local_user": "Miestny pou\u017e\u00edvate\u013e", + "lsc": "LSC", + "myself": "Ja s\u00e1m", + "rain": "D\u00e1\u017e\u010f", + "saac": "SAAC", + "security": "Bezpe\u010dnos\u0165", + "sfc": "SFC", + "temperature": "Teplota", + "timer": "\u010casova\u010d", + "ups": "UPS", + "user": "Pou\u017e\u00edvate\u013e", + "wind": "Vietor" + } + }, + "sensor_defect": { + "state": { + "dead": "Nedostupn\u00e9", + "low_battery": "N\u00edzky stav bat\u00e9rie", + "maintenance_required": "Vy\u017eaduje sa \u00fadr\u017eba", + "no_defect": "Bez defektu" + } + }, + "sensor_room": { + "state": { + "clean": "\u010cist\u00fd", + "dirty": "\u0160pinav\u00fd" + } + }, + "three_way_handle_direction": { + "state": { + "closed": "Zatvoren\u00fd", + "open": "Otvoren\u00fd", + "tilt": "Naklonenie" } } } diff --git a/homeassistant/components/overkiz/translations/zh-Hant.json b/homeassistant/components/overkiz/translations/zh-Hant.json index 072233f23bc..3fbf51cefb6 100644 --- a/homeassistant/components/overkiz/translations/zh-Hant.json +++ b/homeassistant/components/overkiz/translations/zh-Hant.json @@ -26,5 +26,78 @@ "description": "Overkiz \u5e73\u53f0\u7531\u4f8b\u5982 Somfy (Connexoon / TaHoma)\u3001Hitachi (Hi Kumo)\u3001Rexel (Energeasy Connect) \u53ca Atlantic (Cozytouch) \u591a\u500b\u4f9b\u61c9\u5546\u6240\u5ee3\u6cdb\u4f7f\u7528\uff0c\u8f38\u5165\u61c9\u7528\u7a0b\u5f0f\u6191\u8b49\u4e26\u9078\u64c7 Hub\u3002" } } + }, + "entity": { + "select": { + "memorized_simple_volume": { + "state": { + "highest": "\u6700\u9ad8", + "standard": "\u6a19\u6e96" + } + }, + "open_closed_pedestrian": { + "state": { + "closed": "\u95dc\u9589", + "open": "\u958b\u555f", + "pedestrian": "\u9598\u9580" + } + } + }, + "sensor": { + "battery": { + "state": { + "full": "\u5168\u6eff", + "low": "\u4f4e", + "normal": "\u6b63\u5e38", + "verylow": "\u6975\u4f4e" + } + }, + "discrete_rssi_level": { + "state": { + "good": "\u826f\u597d", + "low": "\u4f4e", + "normal": "\u6b63\u5e38", + "verylow": "\u6975\u4f4e" + } + }, + "priority_lock_originator": { + "state": { + "external_gateway": "\u5916\u90e8\u9598\u9053\u5668", + "local_user": "\u672c\u5730\u4f7f\u7528\u8005", + "lsc": "LSC", + "myself": "\u672c\u4eba", + "rain": "\u4e0b\u96e8", + "saac": "SAAC", + "security": "\u5b89\u5168\u6027", + "sfc": "SFC", + "temperature": "\u6eab\u5ea6", + "timer": "\u8a08\u6642\u5668", + "ups": "UPS", + "user": "\u4f7f\u7528\u8005", + "wind": "\u6709\u98a8" + } + }, + "sensor_defect": { + "state": { + "dead": "\u5931\u53bb\u9023\u7dda", + "low_battery": "\u4f4e\u96fb\u91cf", + "maintenance_required": "\u9700\u8981\u9032\u884c\u7dad\u8b77", + "no_defect": "\u7121\u7f3a\u9677" + } + }, + "sensor_room": { + "state": { + "clean": "\u826f\u597d", + "dirty": "\u4e0d\u4f73" + } + }, + "three_way_handle_direction": { + "state": { + "closed": "\u95dc\u9589", + "open": "\u958b\u555f", + "tilt": "\u50be\u659c" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/overkiz/water_heater_entities/atlantic_pass_apc_dhw.py b/homeassistant/components/overkiz/water_heater_entities/atlantic_pass_apc_dhw.py index 7c2ea6ff2d8..d977dd67e4e 100644 --- a/homeassistant/components/overkiz/water_heater_entities/atlantic_pass_apc_dhw.py +++ b/homeassistant/components/overkiz/water_heater_entities/atlantic_pass_apc_dhw.py @@ -12,7 +12,7 @@ from homeassistant.components.water_heater import ( WaterHeaterEntity, WaterHeaterEntityFeature, ) -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from ..entity import OverkizEntity @@ -20,7 +20,7 @@ from ..entity import OverkizEntity class AtlanticPassAPCDHW(OverkizEntity, WaterHeaterEntity): """Representation of Atlantic Pass APC DHW.""" - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS _attr_supported_features = ( WaterHeaterEntityFeature.TARGET_TEMPERATURE | WaterHeaterEntityFeature.OPERATION_MODE diff --git a/homeassistant/components/overkiz/water_heater_entities/domestic_hot_water_production.py b/homeassistant/components/overkiz/water_heater_entities/domestic_hot_water_production.py index 2a8d93bf8eb..2285e2dc3d2 100644 --- a/homeassistant/components/overkiz/water_heater_entities/domestic_hot_water_production.py +++ b/homeassistant/components/overkiz/water_heater_entities/domestic_hot_water_production.py @@ -12,7 +12,7 @@ from homeassistant.components.water_heater import ( WaterHeaterEntity, WaterHeaterEntityFeature, ) -from homeassistant.const import ATTR_TEMPERATURE, STATE_OFF, STATE_ON, TEMP_CELSIUS +from homeassistant.const import ATTR_TEMPERATURE, STATE_OFF, STATE_ON, UnitOfTemperature from ..coordinator import OverkizDataUpdateCoordinator from ..entity import OverkizEntity @@ -29,8 +29,6 @@ OVERKIZ_TO_OPERATION_MODE: dict[str, str] = { OverkizCommandParam.BOOST: STATE_PERFORMANCE, } -OPERATION_MODE_TO_OVERKIZ = {v: k for k, v in OVERKIZ_TO_OPERATION_MODE.items()} - DHWP_AWAY_MODES = [ OverkizCommandParam.ABSENCE, OverkizCommandParam.AWAY, @@ -44,12 +42,11 @@ DEFAULT_MAX_TEMP: float = 70 class DomesticHotWaterProduction(OverkizEntity, WaterHeaterEntity): """Representation of a DomesticHotWaterProduction Water Heater.""" - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS _attr_supported_features = ( WaterHeaterEntityFeature.TARGET_TEMPERATURE | WaterHeaterEntityFeature.OPERATION_MODE ) - _attr_operation_list = [*OPERATION_MODE_TO_OVERKIZ] def __init__( self, device_url: str, coordinator: OverkizDataUpdateCoordinator @@ -58,17 +55,21 @@ class DomesticHotWaterProduction(OverkizEntity, WaterHeaterEntity): super().__init__(device_url, coordinator) # Init operation mode to set for this specific device - self.overkiz_to_operation_mode: dict[str, str] = {} + self.operation_mode_to_overkiz: dict[str, str] = {} + self._attr_operation_list = [] state_mode_definition = self.executor.select_definition_state( OverkizState.IO_DHW_MODE, OverkizState.MODBUSLINK_DHW_MODE ) - if state_mode_definition and state_mode_definition.values: + for param, mode in OVERKIZ_TO_OPERATION_MODE.items(): # Filter only for mode allowed by this device - for param, mode in OVERKIZ_TO_OPERATION_MODE.items(): - if param in state_mode_definition.values: - self.overkiz_to_operation_mode[param] = mode - else: - self.overkiz_to_operation_mode = OVERKIZ_TO_OPERATION_MODE + # or allow all if no mode definition found + if ( + not state_mode_definition + or state_mode_definition.values + and param in state_mode_definition.values + ): + self.operation_mode_to_overkiz[mode] = param + self._attr_operation_list.append(param) @property def _is_boost_mode_on(self) -> bool: @@ -230,7 +231,7 @@ class DomesticHotWaterProduction(OverkizEntity, WaterHeaterEntity): async def async_set_temperature(self, **kwargs: Any) -> None: """Set new target temperature.""" - target_temperature = kwargs.get(ATTR_TEMPERATURE) + target_temperature = kwargs[ATTR_TEMPERATURE] if self.executor.has_command(OverkizCommand.SET_TARGET_TEMPERATURE): await self.executor.async_execute_command( @@ -251,19 +252,21 @@ class DomesticHotWaterProduction(OverkizEntity, WaterHeaterEntity): ) @property - def current_operation(self) -> str: + def current_operation(self) -> str | None: """Return current operation ie. eco, electric, performance, ...""" if self._is_boost_mode_on: return OVERKIZ_TO_OPERATION_MODE[OverkizCommandParam.BOOST] - return OVERKIZ_TO_OPERATION_MODE[ - cast( - str, - self.executor.select_state( - OverkizState.IO_DHW_MODE, OverkizState.MODBUSLINK_DHW_MODE - ), - ) - ] + current_dwh_mode = cast( + str, + self.executor.select_state( + OverkizState.IO_DHW_MODE, OverkizState.MODBUSLINK_DHW_MODE + ), + ) + if current_dwh_mode in OVERKIZ_TO_OPERATION_MODE: + return OVERKIZ_TO_OPERATION_MODE[current_dwh_mode] + + return None async def async_set_operation_mode(self, operation_mode: str) -> None: """Set new target operation mode.""" @@ -299,7 +302,8 @@ class DomesticHotWaterProduction(OverkizEntity, WaterHeaterEntity): return if self._is_boost_mode_on: - # We're setting a non Boost mode and the device is currently in Boost mode, the following code remove all boost operations + # We're setting a non Boost mode and the device is currently in Boost mode + # The following code removes all boost operations if self.executor.has_command(OverkizCommand.SET_BOOST_MODE): await self.executor.async_execute_command( OverkizCommand.SET_BOOST_MODE, OverkizCommand.OFF @@ -320,7 +324,7 @@ class DomesticHotWaterProduction(OverkizEntity, WaterHeaterEntity): ) await self.executor.async_execute_command( - OverkizCommand.SET_DHW_MODE, self.overkiz_to_operation_mode[operation_mode] + OverkizCommand.SET_DHW_MODE, self.operation_mode_to_overkiz[operation_mode] ) if self.executor.has_command(OverkizCommand.REFRESH_BOOST_MODE_DURATION): diff --git a/homeassistant/components/overkiz/water_heater_entities/hitachi_dhw.py b/homeassistant/components/overkiz/water_heater_entities/hitachi_dhw.py index fa8c128e0c1..0dd5b70feb6 100644 --- a/homeassistant/components/overkiz/water_heater_entities/hitachi_dhw.py +++ b/homeassistant/components/overkiz/water_heater_entities/hitachi_dhw.py @@ -1,7 +1,7 @@ """Support for Hitachi DHW.""" from __future__ import annotations -from typing import Any, cast +from typing import Any from pyoverkiz.enums import OverkizCommand, OverkizCommandParam, OverkizState @@ -15,7 +15,7 @@ from homeassistant.const import ( PRECISION_WHOLE, STATE_OFF, STATE_ON, - TEMP_CELSIUS, + UnitOfTemperature, ) from ..entity import OverkizEntity @@ -36,7 +36,7 @@ class HitachiDHW(OverkizEntity, WaterHeaterEntity): _attr_max_temp = 70.0 _attr_precision = PRECISION_WHOLE - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS _attr_supported_features = ( WaterHeaterEntityFeature.TARGET_TEMPERATURE | WaterHeaterEntityFeature.OPERATION_MODE @@ -63,9 +63,10 @@ class HitachiDHW(OverkizEntity, WaterHeaterEntity): async def async_set_temperature(self, **kwargs: Any) -> None: """Set new target temperature.""" - temperature = cast(float, kwargs.get(ATTR_TEMPERATURE)) + await self.executor.async_execute_command( - OverkizCommand.SET_CONTROL_DHW_SETTING_TEMPERATURE, int(temperature) + OverkizCommand.SET_CONTROL_DHW_SETTING_TEMPERATURE, + int(kwargs[ATTR_TEMPERATURE]), ) @property diff --git a/homeassistant/components/ovo_energy/__init__.py b/homeassistant/components/ovo_energy/__init__.py index cc19b0454b1..92d9aa118f0 100644 --- a/homeassistant/components/ovo_energy/__init__.py +++ b/homeassistant/components/ovo_energy/__init__.py @@ -61,7 +61,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: raise ConfigEntryAuthFailed("Not authenticated with OVO Energy") return await client.get_daily_usage(datetime.utcnow().strftime("%Y-%m")) - coordinator = DataUpdateCoordinator( + coordinator = DataUpdateCoordinator[OVODailyUsage]( hass, _LOGGER, # Name of the data. For logging purposes. @@ -96,12 +96,12 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return unload_ok -class OVOEnergyEntity(CoordinatorEntity): +class OVOEnergyEntity(CoordinatorEntity[DataUpdateCoordinator[OVODailyUsage]]): """Defines a base OVO Energy entity.""" def __init__( self, - coordinator: DataUpdateCoordinator, + coordinator: DataUpdateCoordinator[OVODailyUsage], client: OVOEnergy, ) -> None: """Initialize the OVO Energy entity.""" diff --git a/homeassistant/components/ovo_energy/sensor.py b/homeassistant/components/ovo_energy/sensor.py index 532bb25cbc8..2a4005e748f 100644 --- a/homeassistant/components/ovo_energy/sensor.py +++ b/homeassistant/components/ovo_energy/sensor.py @@ -16,7 +16,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ENERGY_KILO_WATT_HOUR +from homeassistant.const import UnitOfEnergy from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import StateType @@ -46,7 +46,7 @@ SENSOR_TYPES_ELECTRICITY: tuple[OVOEnergySensorEntityDescription, ...] = ( name="OVO Last Electricity Reading", device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, value=lambda usage: usage.electricity[-1].consumption, ), OVOEnergySensorEntityDescription( @@ -80,7 +80,7 @@ SENSOR_TYPES_GAS: tuple[OVOEnergySensorEntityDescription, ...] = ( name="OVO Last Gas Reading", device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, icon="mdi:gas-cylinder", value=lambda usage: usage.gas[-1].consumption, ), @@ -115,9 +115,9 @@ async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback ) -> None: """Set up OVO Energy sensor based on a config entry.""" - coordinator: DataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id][ - DATA_COORDINATOR - ] + coordinator: DataUpdateCoordinator[OVODailyUsage] = hass.data[DOMAIN][ + entry.entry_id + ][DATA_COORDINATOR] client: OVOEnergy = hass.data[DOMAIN][entry.entry_id][DATA_CLIENT] entities = [] @@ -152,25 +152,22 @@ async def async_setup_entry( class OVOEnergySensor(OVOEnergyDeviceEntity, SensorEntity): """Define a OVO Energy sensor.""" - coordinator: DataUpdateCoordinator + coordinator: DataUpdateCoordinator[DataUpdateCoordinator[OVODailyUsage]] entity_description: OVOEnergySensorEntityDescription def __init__( self, - coordinator: DataUpdateCoordinator, + coordinator: DataUpdateCoordinator[OVODailyUsage], description: OVOEnergySensorEntityDescription, client: OVOEnergy, ) -> None: """Initialize.""" - super().__init__( - coordinator, - client, - ) + super().__init__(coordinator, client) self._attr_unique_id = f"{DOMAIN}_{client.account_id}_{description.key}" self.entity_description = description @property def native_value(self) -> StateType | datetime: """Return the state.""" - usage: OVODailyUsage = self.coordinator.data + usage = self.coordinator.data return self.entity_description.value(usage) diff --git a/homeassistant/components/ovo_energy/translations/pt.json b/homeassistant/components/ovo_energy/translations/pt.json index 15241a5fb65..6fd615fc0cc 100644 --- a/homeassistant/components/ovo_energy/translations/pt.json +++ b/homeassistant/components/ovo_energy/translations/pt.json @@ -2,7 +2,7 @@ "config": { "error": { "already_configured": "Conta j\u00e1 configurada", - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida" }, "flow_title": "{username}", diff --git a/homeassistant/components/ovo_energy/translations/sk.json b/homeassistant/components/ovo_energy/translations/sk.json index e860c7345ce..95e7ecce00a 100644 --- a/homeassistant/components/ovo_energy/translations/sk.json +++ b/homeassistant/components/ovo_energy/translations/sk.json @@ -11,13 +11,17 @@ "data": { "password": "Heslo" }, - "description": "Autentifik\u00e1cia pre OVO Energy zlyhala. Zadajte svoje aktu\u00e1lne poverenia." + "description": "Autentifik\u00e1cia pre OVO Energy zlyhala. Zadajte svoje aktu\u00e1lne poverenia.", + "title": "Op\u00e4tovn\u00e9 overenie" }, "user": { "data": { + "account": "ID \u00fa\u010dtu OVO (pridajte, iba ak m\u00e1te viacero \u00fa\u010dtov)", "password": "Heslo", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" - } + }, + "description": "Nastavte in\u0161tanciu OVO Energy na pr\u00edstup k va\u0161ej spotrebe energie.", + "title": "Prida\u0165 \u00fa\u010det OVO Energy" } } } diff --git a/homeassistant/components/owntracks/config_flow.py b/homeassistant/components/owntracks/config_flow.py index bdc8323d512..b82a8727388 100644 --- a/homeassistant/components/owntracks/config_flow.py +++ b/homeassistant/components/owntracks/config_flow.py @@ -33,7 +33,10 @@ class OwnTracksFlow(config_entries.ConfigFlow, domain=DOMAIN): secret = secrets.token_hex(16) if supports_encryption(): - secret_desc = f"The encryption key is {secret} (on Android under preferences -> advanced)" + secret_desc = ( + f"The encryption key is {secret} (on Android under preferences ->" + " advanced)" + ) else: secret_desc = "Encryption is not supported because nacl is not installed." diff --git a/homeassistant/components/owntracks/messages.py b/homeassistant/components/owntracks/messages.py index cd474054029..b31fbba8c8e 100644 --- a/homeassistant/components/owntracks/messages.py +++ b/homeassistant/components/owntracks/messages.py @@ -138,7 +138,10 @@ def _decrypt_payload(secret, topic, ciphertext): return message except ValueError: _LOGGER.warning( - "Ignoring encrypted payload because unable to decrypt using key for topic %s", + ( + "Ignoring encrypted payload because unable to decrypt using key for" + " topic %s" + ), topic, ) return None diff --git a/homeassistant/components/owntracks/translations/de.json b/homeassistant/components/owntracks/translations/de.json index b755c152be4..376f6c9fdd3 100644 --- a/homeassistant/components/owntracks/translations/de.json +++ b/homeassistant/components/owntracks/translations/de.json @@ -5,7 +5,7 @@ "single_instance_allowed": "Bereits konfiguriert. Nur eine einzige Konfiguration m\u00f6glich." }, "create_entry": { - "default": "Unter Android \u00f6ffne [die OwnTracks App]({android_url}), gehe zu Einstellungen \u2192 Verbindung. \u00c4ndere die folgenden Einstellungen:\n - Modus: Privat HTTP\n - Host: {webhook_url}\n - Identifikation:\n - Benutzername: `''`\n - Ger\u00e4te-ID: `''`\n\nUnter iOS \u00f6ffne [die OwnTracks App]({ios_url}), tippe auf das (i)-Symbol oben links \u2192 Einstellungen. \u00c4ndere die folgenden Einstellungen:\n - Modus: HTTP\n - URL: {webhook_url}\n - Authentifizierung einschalten\n - UserID: `''`\n\n{secret}\n\nWeitere Informationen findest du in [der Dokumentation]({docs_url})." + "default": "Unter Android \u00f6ffne [die OwnTracks App]({android_url}), gehe zu Einstellungen \u2192 Verbindung. \u00c4ndere die folgenden Einstellungen:\n - Modus: Privat HTTP\n - Host: {webhook_url}\n - Identifikation:\n - Benutzername: `''`\n - Ger\u00e4te-ID: `''`\n\nUnter iOS \u00f6ffne [die OwnTracks App]({ios_url}), tippe auf das (i) Symbol oben links \u2192 Einstellungen. \u00c4ndere die folgenden Einstellungen:\n - Modus: HTTP\n - URL: {webhook_url}\n - Authentifizierung einschalten\n - UserID: `''`\n\n{secret}\n\nWeitere Informationen findest du in [der Dokumentation]({docs_url})." }, "step": { "user": { diff --git a/homeassistant/components/owntracks/translations/ko.json b/homeassistant/components/owntracks/translations/ko.json index a8dd94b7e16..e085df8169a 100644 --- a/homeassistant/components/owntracks/translations/ko.json +++ b/homeassistant/components/owntracks/translations/ko.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "cloud_not_connected": "Home Assistant \ud074\ub77c\uc6b0\ub4dc\uc5d0 \uc5f0\uacb0\ub418\uc5b4 \uc788\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.", "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." }, "create_entry": { diff --git a/homeassistant/components/owntracks/translations/sk.json b/homeassistant/components/owntracks/translations/sk.json index c9ae6fa2722..a5eaaa710be 100644 --- a/homeassistant/components/owntracks/translations/sk.json +++ b/homeassistant/components/owntracks/translations/sk.json @@ -4,9 +4,13 @@ "cloud_not_connected": "Nie je pripojen\u00e9 k Home Assistant Cloud.", "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." }, + "create_entry": { + "default": "\n\n V syst\u00e9me Android otvorte [aplik\u00e1ciu OwnTracks]({android_url}), prejdite na predvo\u013eby - > pripojenie. Zme\u0148te nasleduj\u00face nastavenia:\n - Re\u017eim: S\u00fakromn\u00fd HTTP\n \u2013 Hostite\u013e: {webhook_url}\n - Identifik\u00e1cia:\n - Pou\u017e\u00edvate\u013esk\u00e9 meno: `' '`\n - ID zariadenia: `' '` \n\nV syst\u00e9me iOS otvorte [aplik\u00e1ciu OwnTracks]({ios_url}), klepnite na ikonu (i) v\u013eavo hore - > nastavenia. Zme\u0148te nasleduj\u00face nastavenia:\n - Re\u017eim: HTTP\n \u2013 URL: {webhook_url}\n - Zapnite autentifik\u00e1ciu\n - ID pou\u017e\u00edvate\u013ea: `' '` \n\n{secret}\n\n\u010eal\u0161ie inform\u00e1cie n\u00e1jdete v [dokument\u00e1cii]({docs_url})." + }, "step": { "user": { - "description": "Naozaj chcete nastavi\u0165 OwnTracks?" + "description": "Naozaj chcete nastavi\u0165 OwnTracks?", + "title": "Nastavenie OwnTracks" } } } diff --git a/homeassistant/components/p1_monitor/sensor.py b/homeassistant/components/p1_monitor/sensor.py index 8a1c9d68b5a..f192dd44300 100644 --- a/homeassistant/components/p1_monitor/sensor.py +++ b/homeassistant/components/p1_monitor/sensor.py @@ -14,12 +14,11 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONF_HOST, CURRENCY_EURO, - ELECTRIC_CURRENT_AMPERE, - ELECTRIC_POTENTIAL_VOLT, - ENERGY_KILO_WATT_HOUR, - POWER_WATT, - VOLUME_CUBIC_METERS, - VOLUME_LITERS, + UnitOfElectricCurrent, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfPower, + UnitOfVolume, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceEntryType @@ -42,49 +41,49 @@ SENSORS_SMARTMETER: tuple[SensorEntityDescription, ...] = ( key="gas_consumption", name="Gas Consumption", entity_registry_enabled_default=False, - native_unit_of_measurement=VOLUME_CUBIC_METERS, + native_unit_of_measurement=UnitOfVolume.CUBIC_METERS, device_class=SensorDeviceClass.GAS, state_class=SensorStateClass.TOTAL_INCREASING, ), SensorEntityDescription( key="power_consumption", name="Power Consumption", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="energy_consumption_high", name="Energy Consumption - High Tariff", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), SensorEntityDescription( key="energy_consumption_low", name="Energy Consumption - Low Tariff", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), SensorEntityDescription( key="power_production", name="Power Production", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="energy_production_high", name="Energy Production - High Tariff", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), SensorEntityDescription( key="energy_production_low", name="Energy Production - Low Tariff", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), @@ -99,84 +98,84 @@ SENSORS_PHASES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="voltage_phase_l1", name="Voltage Phase L1", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="voltage_phase_l2", name="Voltage Phase L2", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="voltage_phase_l3", name="Voltage Phase L3", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="current_phase_l1", name="Current Phase L1", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="current_phase_l2", name="Current Phase L2", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="current_phase_l3", name="Current Phase L3", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="power_consumed_phase_l1", name="Power Consumed Phase L1", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="power_consumed_phase_l2", name="Power Consumed Phase L2", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="power_consumed_phase_l3", name="Power Consumed Phase L3", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="power_produced_phase_l1", name="Power Produced Phase L1", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="power_produced_phase_l2", name="Power Produced Phase L2", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="power_produced_phase_l3", name="Power Produced Phase L3", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), @@ -188,31 +187,31 @@ SENSORS_SETTINGS: tuple[SensorEntityDescription, ...] = ( name="Gas Consumption Price", entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=f"{CURRENCY_EURO}/{VOLUME_CUBIC_METERS}", + native_unit_of_measurement=f"{CURRENCY_EURO}/{UnitOfVolume.CUBIC_METERS}", ), SensorEntityDescription( key="energy_consumption_price_low", name="Energy Consumption Price - Low", state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=f"{CURRENCY_EURO}/{ENERGY_KILO_WATT_HOUR}", + native_unit_of_measurement=f"{CURRENCY_EURO}/{UnitOfEnergy.KILO_WATT_HOUR}", ), SensorEntityDescription( key="energy_consumption_price_high", name="Energy Consumption Price - High", state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=f"{CURRENCY_EURO}/{ENERGY_KILO_WATT_HOUR}", + native_unit_of_measurement=f"{CURRENCY_EURO}/{UnitOfEnergy.KILO_WATT_HOUR}", ), SensorEntityDescription( key="energy_production_price_low", name="Energy Production Price - Low", state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=f"{CURRENCY_EURO}/{ENERGY_KILO_WATT_HOUR}", + native_unit_of_measurement=f"{CURRENCY_EURO}/{UnitOfEnergy.KILO_WATT_HOUR}", ), SensorEntityDescription( key="energy_production_price_high", name="Energy Production Price - High", state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=f"{CURRENCY_EURO}/{ENERGY_KILO_WATT_HOUR}", + native_unit_of_measurement=f"{CURRENCY_EURO}/{UnitOfEnergy.KILO_WATT_HOUR}", ), ) @@ -221,14 +220,14 @@ SENSORS_WATERMETER: tuple[SensorEntityDescription, ...] = ( key="consumption_day", name="Consumption Day", state_class=SensorStateClass.TOTAL_INCREASING, - native_unit_of_measurement=VOLUME_LITERS, + native_unit_of_measurement=UnitOfVolume.LITERS, device_class=SensorDeviceClass.WATER, ), SensorEntityDescription( key="consumption_total", name="Consumption Total", state_class=SensorStateClass.TOTAL_INCREASING, - native_unit_of_measurement=VOLUME_CUBIC_METERS, + native_unit_of_measurement=UnitOfVolume.CUBIC_METERS, device_class=SensorDeviceClass.WATER, ), SensorEntityDescription( diff --git a/homeassistant/components/p1_monitor/translations/ko.json b/homeassistant/components/p1_monitor/translations/ko.json index e2d6ae5c199..bcd8bb26e97 100644 --- a/homeassistant/components/p1_monitor/translations/ko.json +++ b/homeassistant/components/p1_monitor/translations/ko.json @@ -2,6 +2,13 @@ "config": { "error": { "cannot_connect": "\uc5f0\uacb0 \uc2e4\ud328" + }, + "step": { + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/p1_monitor/translations/pt.json b/homeassistant/components/p1_monitor/translations/pt.json index 602d9c6d009..5eb92676462 100644 --- a/homeassistant/components/p1_monitor/translations/pt.json +++ b/homeassistant/components/p1_monitor/translations/pt.json @@ -1,12 +1,12 @@ { "config": { "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "user": { "data": { - "host": "Servidor" + "host": "Endere\u00e7o" }, "data_description": { "host": "O endere\u00e7o IP ou nome de host da instala\u00e7\u00e3o do Monitor P1." diff --git a/homeassistant/components/panasonic_viera/__init__.py b/homeassistant/components/panasonic_viera/__init__.py index 79504653a39..6504aad7509 100644 --- a/homeassistant/components/panasonic_viera/__init__.py +++ b/homeassistant/components/panasonic_viera/__init__.py @@ -99,7 +99,8 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b unique_id = config_entry.unique_id if device_info is None: _LOGGER.error( - "Couldn't gather device info; Please restart Home Assistant with your TV turned on and connected to your network" + "Couldn't gather device info; Please restart Home Assistant with your" + " TV turned on and connected to your network" ) else: unique_id = device_info[ATTR_UDN] diff --git a/homeassistant/components/panasonic_viera/strings.json b/homeassistant/components/panasonic_viera/strings.json index 13e3d428df9..a04f942dafa 100644 --- a/homeassistant/components/panasonic_viera/strings.json +++ b/homeassistant/components/panasonic_viera/strings.json @@ -2,7 +2,7 @@ "config": { "step": { "user": { - "title": "Setup your TV", + "title": "Set up your TV", "description": "Enter your Panasonic Viera TV's [%key:common::config_flow::data::ip%]", "data": { "host": "[%key:common::config_flow::data::ip%]", diff --git a/homeassistant/components/panasonic_viera/translations/en.json b/homeassistant/components/panasonic_viera/translations/en.json index f4f7438e32e..43e576938e9 100644 --- a/homeassistant/components/panasonic_viera/translations/en.json +++ b/homeassistant/components/panasonic_viera/translations/en.json @@ -23,7 +23,7 @@ "name": "Name" }, "description": "Enter your Panasonic Viera TV's IP Address", - "title": "Setup your TV" + "title": "Set up your TV" } } } diff --git a/homeassistant/components/panasonic_viera/translations/lb.json b/homeassistant/components/panasonic_viera/translations/lb.json index a92bbc5175c..88fd1c25433 100644 --- a/homeassistant/components/panasonic_viera/translations/lb.json +++ b/homeassistant/components/panasonic_viera/translations/lb.json @@ -7,12 +7,12 @@ }, "error": { "cannot_connect": "Feeler beim verbannen", - "invalid_pin_code": "PIN Code ass ong\u00eblteg" + "invalid_pin_code": "De PIN-Code war ong\u00eblteg" }, "step": { "pairing": { "data": { - "pin": "PIN Code" + "pin": "PIN-Code" }, "description": "PIN uginn deem um Fernseh ugewise g\u00ebtt", "title": "Kopplung" diff --git a/homeassistant/components/panasonic_viera/translations/no.json b/homeassistant/components/panasonic_viera/translations/no.json index 5c0105a762e..93c25225287 100644 --- a/homeassistant/components/panasonic_viera/translations/no.json +++ b/homeassistant/components/panasonic_viera/translations/no.json @@ -23,7 +23,7 @@ "name": "Navn" }, "description": "Skriv inn Panasonic Viera TVs IP adresse", - "title": "Sett opp TV-en din" + "title": "Sett opp TV-en" } } } diff --git a/homeassistant/components/panasonic_viera/translations/pt.json b/homeassistant/components/panasonic_viera/translations/pt.json index 411d3a8610b..3062195802d 100644 --- a/homeassistant/components/panasonic_viera/translations/pt.json +++ b/homeassistant/components/panasonic_viera/translations/pt.json @@ -2,11 +2,11 @@ "config": { "abort": { "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "unknown": "Erro inesperado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_pin_code": "O C\u00f3digo PIN digitado \u00e9 inv\u00e1lido" }, "step": { diff --git a/homeassistant/components/peco/translations/ko.json b/homeassistant/components/peco/translations/ko.json new file mode 100644 index 00000000000..e1300423811 --- /dev/null +++ b/homeassistant/components/peco/translations/ko.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "\uc11c\ube44\uc2a4\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/persistent_notification/__init__.py b/homeassistant/components/persistent_notification/__init__.py index c247a326036..36b496ddde2 100644 --- a/homeassistant/components/persistent_notification/__init__.py +++ b/homeassistant/components/persistent_notification/__init__.py @@ -148,8 +148,10 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: if entity_id not in notifications: _LOGGER.error( - "Marking persistent_notification read failed: " - "Notification ID %s not found", + ( + "Marking persistent_notification read failed: " + "Notification ID %s not found" + ), notification_id, ) return diff --git a/homeassistant/components/person/__init__.py b/homeassistant/components/person/__init__.py index a469a459ace..523d21aa69c 100644 --- a/homeassistant/components/person/__init__.py +++ b/homeassistant/components/person/__init__.py @@ -303,7 +303,8 @@ async def filter_yaml_data(hass: HomeAssistant, persons: list[dict]) -> list[dic person_conf[CONF_ID], ) person_invalid_user.append( - f"- Person {person_conf[CONF_NAME]} (id: {person_conf[CONF_ID]}) points at invalid user {user_id}" + f"- Person {person_conf[CONF_NAME]} (id: {person_conf[CONF_ID]}) points" + f" at invalid user {user_id}" ) continue diff --git a/homeassistant/components/person/manifest.json b/homeassistant/components/person/manifest.json index dc9c76ca103..671da2491e2 100644 --- a/homeassistant/components/person/manifest.json +++ b/homeassistant/components/person/manifest.json @@ -2,7 +2,7 @@ "domain": "person", "name": "Person", "documentation": "https://www.home-assistant.io/integrations/person", - "dependencies": ["image"], + "dependencies": ["image_upload"], "after_dependencies": ["device_tracker"], "codeowners": [], "quality_scale": "internal", diff --git a/homeassistant/components/philips_js/translations/pt.json b/homeassistant/components/philips_js/translations/pt.json index f6815d0a16a..f9145863c9e 100644 --- a/homeassistant/components/philips_js/translations/pt.json +++ b/homeassistant/components/philips_js/translations/pt.json @@ -1,7 +1,7 @@ { "config": { "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_pin": "PIN inv\u00e1lido", "unknown": "Erro inesperado" }, @@ -13,7 +13,7 @@ }, "user": { "data": { - "host": "Servidor" + "host": "Endere\u00e7o" } } } diff --git a/homeassistant/components/pi_hole/__init__.py b/homeassistant/components/pi_hole/__init__.py index ffb3352e282..714547ba961 100644 --- a/homeassistant/components/pi_hole/__init__.py +++ b/homeassistant/components/pi_hole/__init__.py @@ -18,10 +18,10 @@ from homeassistant.const import ( Platform, ) from homeassistant.core import HomeAssistant, callback -from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers import config_validation as cv from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.entity import DeviceInfo +from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.update_coordinator import ( CoordinatorEntity, @@ -70,14 +70,26 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: hass.data[DOMAIN] = {} + if DOMAIN not in config: + return True + + async_create_issue( + hass, + DOMAIN, + "deprecated_yaml", + breaks_in_ha_version="2023.2.0", + is_fixable=False, + severity=IssueSeverity.WARNING, + translation_key="deprecated_yaml", + ) + # import - if DOMAIN in config: - for conf in config[DOMAIN]: - hass.async_create_task( - hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_IMPORT}, data=conf - ) + for conf in config[DOMAIN]: + hass.async_create_task( + hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_IMPORT}, data=conf ) + ) return True @@ -99,21 +111,14 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: _LOGGER.debug("Setting up %s integration with host %s", DOMAIN, host) - try: - session = async_get_clientsession(hass, verify_tls) - api = Hole( - host, - session, - location=location, - tls=use_tls, - api_token=api_key, - ) - await api.get_data() - await api.get_versions() - - except HoleError as ex: - _LOGGER.warning("Failed to connect: %s", ex) - raise ConfigEntryNotReady from ex + session = async_get_clientsession(hass, verify_tls) + api = Hole( + host, + session, + location=location, + tls=use_tls, + api_token=api_key, + ) async def async_update_data() -> None: """Fetch data from API endpoint.""" @@ -135,6 +140,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: DATA_KEY_COORDINATOR: coordinator, } + await coordinator.async_config_entry_first_refresh() + await hass.config_entries.async_forward_entry_setups(entry, _async_platforms(entry)) return True diff --git a/homeassistant/components/pi_hole/manifest.json b/homeassistant/components/pi_hole/manifest.json index f35b7919251..bce7c9f8685 100644 --- a/homeassistant/components/pi_hole/manifest.json +++ b/homeassistant/components/pi_hole/manifest.json @@ -2,7 +2,7 @@ "domain": "pi_hole", "name": "Pi-hole", "documentation": "https://www.home-assistant.io/integrations/pi_hole", - "requirements": ["hole==0.7.0"], + "requirements": ["hole==0.8.0"], "codeowners": ["@johnluetke", "@shenxn"], "config_flow": true, "iot_class": "local_polling", diff --git a/homeassistant/components/pi_hole/strings.json b/homeassistant/components/pi_hole/strings.json index fbf3c5a627b..e911779d5d7 100644 --- a/homeassistant/components/pi_hole/strings.json +++ b/homeassistant/components/pi_hole/strings.json @@ -25,5 +25,11 @@ "abort": { "already_configured": "[%key:common::config_flow::abort::already_configured_service%]" } + }, + "issues": { + "deprecated_yaml": { + "title": "The PI-Hole YAML configuration is being removed", + "description": "Configuring PI-Hole using YAML is being removed.\n\nYour existing YAML configuration has been imported into the UI automatically.\n\nRemove the PI-Hole YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue." + } } } diff --git a/homeassistant/components/pi_hole/translations/de.json b/homeassistant/components/pi_hole/translations/de.json index 40a5db3c21f..831c1daf03e 100644 --- a/homeassistant/components/pi_hole/translations/de.json +++ b/homeassistant/components/pi_hole/translations/de.json @@ -25,5 +25,11 @@ } } } + }, + "issues": { + "deprecated_yaml": { + "description": "Die Konfiguration von PI-Hole mit YAML wird entfernt. \n\nDeine vorhandene YAML-Konfiguration wurde automatisch in die Benutzeroberfl\u00e4che importiert. \n\nEntferne die PI-Hole-YAML-Konfiguration aus deiner configuration.yaml-Datei und starte Home Assistant neu, um dieses Problem zu beheben.", + "title": "Die PI-Hole YAML-Konfiguration wird entfernt" + } } } \ No newline at end of file diff --git a/homeassistant/components/pi_hole/translations/en.json b/homeassistant/components/pi_hole/translations/en.json index 9053a70c18f..4333838ae64 100644 --- a/homeassistant/components/pi_hole/translations/en.json +++ b/homeassistant/components/pi_hole/translations/en.json @@ -25,5 +25,11 @@ } } } + }, + "issues": { + "deprecated_yaml": { + "description": "Configuring PI-Hole using YAML is being removed.\n\nYour existing YAML configuration has been imported into the UI automatically.\n\nRemove the PI-Hole YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue.", + "title": "The PI-Hole YAML configuration is being removed" + } } } \ No newline at end of file diff --git a/homeassistant/components/pi_hole/translations/pt.json b/homeassistant/components/pi_hole/translations/pt.json index e75629c42e0..ff1c0fcd1be 100644 --- a/homeassistant/components/pi_hole/translations/pt.json +++ b/homeassistant/components/pi_hole/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "O servi\u00e7o j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "api_key": { diff --git a/homeassistant/components/picnic/translations/ko.json b/homeassistant/components/picnic/translations/ko.json index fe58774c459..1214bea6d27 100644 --- a/homeassistant/components/picnic/translations/ko.json +++ b/homeassistant/components/picnic/translations/ko.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" }, "error": { "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", diff --git a/homeassistant/components/picnic/translations/pt.json b/homeassistant/components/picnic/translations/pt.json index 79bc83a3f69..db59210c79d 100644 --- a/homeassistant/components/picnic/translations/pt.json +++ b/homeassistant/components/picnic/translations/pt.json @@ -4,7 +4,7 @@ "reauth_successful": "Reautentica\u00e7\u00e3o bem sucedida" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "unknown": "Erro inesperado" }, "step": { diff --git a/homeassistant/components/ping/__init__.py b/homeassistant/components/ping/__init__.py index 6349b07ca12..c3699e0fe2d 100644 --- a/homeassistant/components/ping/__init__.py +++ b/homeassistant/components/ping/__init__.py @@ -32,7 +32,8 @@ def _can_use_icmp_lib_with_privilege() -> None | bool: icmp_ping("127.0.0.1", count=0, timeout=0, privileged=False) except SocketPermissionError: _LOGGER.debug( - "Cannot use icmplib because privileges are insufficient to create the socket" + "Cannot use icmplib because privileges are insufficient to create the" + " socket" ) return None else: diff --git a/homeassistant/components/plaato/__init__.py b/homeassistant/components/plaato/__init__.py index 914d92040d2..efc3a2b4225 100644 --- a/homeassistant/components/plaato/__init__.py +++ b/homeassistant/components/plaato/__init__.py @@ -29,11 +29,9 @@ from homeassistant.const import ( CONF_SCAN_INTERVAL, CONF_TOKEN, CONF_WEBHOOK_ID, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, - VOLUME_GALLONS, - VOLUME_LITERS, Platform, + UnitOfTemperature, + UnitOfVolume, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import aiohttp_client @@ -68,8 +66,12 @@ WEBHOOK_SCHEMA = vol.Schema( { vol.Required(ATTR_DEVICE_NAME): cv.string, vol.Required(ATTR_DEVICE_ID): cv.positive_int, - vol.Required(ATTR_TEMP_UNIT): vol.Any(TEMP_CELSIUS, TEMP_FAHRENHEIT), - vol.Required(ATTR_VOLUME_UNIT): vol.Any(VOLUME_LITERS, VOLUME_GALLONS), + vol.Required(ATTR_TEMP_UNIT): vol.In( + UnitOfTemperature.CELSIUS, UnitOfTemperature.FAHRENHEIT + ), + vol.Required(ATTR_VOLUME_UNIT): vol.In( + UnitOfVolume.LITERS, UnitOfVolume.GALLONS + ), vol.Required(ATTR_BPM): cv.positive_int, vol.Required(ATTR_TEMP): vol.Coerce(float), vol.Required(ATTR_SG): vol.Coerce(float), diff --git a/homeassistant/components/plaato/strings.json b/homeassistant/components/plaato/strings.json index 9a4366da668..934628e82c2 100644 --- a/homeassistant/components/plaato/strings.json +++ b/homeassistant/components/plaato/strings.json @@ -19,7 +19,7 @@ }, "webhook": { "title": "Webhook to use", - "description": "To send events to Home Assistant, you will need to setup the webhook feature in Plaato Airlock.\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n\nSee [the documentation]({docs_url}) for further details." + "description": "To send events to Home Assistant, you will need to set up the webhook feature in Plaato Airlock.\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n\nSee [the documentation]({docs_url}) for further details." } }, "error": { @@ -34,7 +34,7 @@ "already_configured": "[%key:common::config_flow::abort::already_configured_account%]" }, "create_entry": { - "default": "Your Plaato {device_type} with name **{device_name}** was successfully setup!" + "default": "Your Plaato {device_type} with name **{device_name}** was successfully set up!" } }, "options": { diff --git a/homeassistant/components/plaato/translations/ca.json b/homeassistant/components/plaato/translations/ca.json index 4c2959bafa2..5f36ec33aa1 100644 --- a/homeassistant/components/plaato/translations/ca.json +++ b/homeassistant/components/plaato/translations/ca.json @@ -32,7 +32,7 @@ "title": "Configura dispositius Plaato" }, "webhook": { - "description": "Per enviar esdeveniments a Home Assistant, haur\u00e0s de configurar la opci\u00f3 webhook de Plaato Airlock.\n\nCompleta la seg\u00fcent informaci\u00f3:\n\n- URL: `{webhook_url}` \n- M\u00e8tode: POST \n\nConsulta la [documentaci\u00f3]({docs_url}) per a m\u00e9s detalls.", + "description": "Per enviar esdeveniments a Home Assistant, has de configurar l'opci\u00f3 webhook de Plaato Airlock.\n\nCompleta la seg\u00fcent informaci\u00f3:\n\n- URL: `{webhook_url}` \n- M\u00e8tode: POST \n\nConsulta la [documentaci\u00f3]({docs_url}) per a m\u00e9s detalls.", "title": "Webhook a utilitzar" } } diff --git a/homeassistant/components/plaato/translations/de.json b/homeassistant/components/plaato/translations/de.json index fadf91bdbe8..e91c6eec7ec 100644 --- a/homeassistant/components/plaato/translations/de.json +++ b/homeassistant/components/plaato/translations/de.json @@ -32,7 +32,7 @@ "title": "Plaato Ger\u00e4te einrichten" }, "webhook": { - "description": "Um Ereignisse an Home Assistant zu senden, muss das Webhook Feature in Plaato Airlock konfiguriert werden.\n\n F\u00fcge die folgenden Informationen ein: \n\n - URL: ` {webhook_url} ` \n - Methode: POST \n \n Weitere Informationen finden sich in der [Dokumentation]({docs_url}).", + "description": "Um Ereignisse an Home Assistant zu senden, muss das Webhook Feature in Plaato Airlock konfiguriert werden.\n\nF\u00fcge die folgenden Informationen ein: \n\n- URL: `{webhook_url}` \n- Methode: POST \n \nWeitere Informationen finden sich in der [Dokumentation]({docs_url}).", "title": "Zu verwendender Webhook" } } diff --git a/homeassistant/components/plaato/translations/en.json b/homeassistant/components/plaato/translations/en.json index 0eba3a94310..46eef46a34b 100644 --- a/homeassistant/components/plaato/translations/en.json +++ b/homeassistant/components/plaato/translations/en.json @@ -7,7 +7,7 @@ "webhook_not_internet_accessible": "Your Home Assistant instance needs to be accessible from the internet to receive webhook messages." }, "create_entry": { - "default": "Your Plaato {device_type} with name **{device_name}** was successfully setup!" + "default": "Your Plaato {device_type} with name **{device_name}** was successfully set up!" }, "error": { "invalid_webhook_device": "You have selected a device that does not support sending data to a webhook. It is only available for the Airlock", @@ -28,11 +28,11 @@ "device_name": "Name your device", "device_type": "Type of Plaato device" }, - "description": "Do you want to start set up?", + "description": "Do you want to start setup?", "title": "Set up the Plaato devices" }, "webhook": { - "description": "To send events to Home Assistant, you will need to setup the webhook feature in Plaato Airlock.\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n\nSee [the documentation]({docs_url}) for further details.", + "description": "To send events to Home Assistant, you will need to set up the webhook feature in Plaato Airlock.\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n\nSee [the documentation]({docs_url}) for further details.", "title": "Webhook to use" } } diff --git a/homeassistant/components/plaato/translations/es.json b/homeassistant/components/plaato/translations/es.json index e65cf7e82d5..9c9aa85c1e2 100644 --- a/homeassistant/components/plaato/translations/es.json +++ b/homeassistant/components/plaato/translations/es.json @@ -32,7 +32,7 @@ "title": "Configurar los dispositivos Plaato" }, "webhook": { - "description": "Para enviar eventos a Home Assistant, deber\u00e1s configurar la funci\u00f3n de webhook en Plaato Airlock. \n\nCompleta la siguiente informaci\u00f3n: \n\n- URL: `{webhook_url}`\n- M\u00e9todo: POST \n\nConsulta [la documentaci\u00f3n]({docs_url}) para obtener m\u00e1s detalles.", + "description": "Para enviar eventos a Home Assistant, necesitar\u00e1s configurar la funci\u00f3n de webhook en Plaato Airlock. \n\nCompleta la siguiente informaci\u00f3n: \n\n- URL: `{webhook_url}`\n- M\u00e9todo: POST \n\nConsulta [la documentaci\u00f3n]({docs_url}) para obtener m\u00e1s detalles.", "title": "Webhook a usar" } } diff --git a/homeassistant/components/plaato/translations/it.json b/homeassistant/components/plaato/translations/it.json index 26fe409bfc2..1f5922517ca 100644 --- a/homeassistant/components/plaato/translations/it.json +++ b/homeassistant/components/plaato/translations/it.json @@ -28,11 +28,11 @@ "device_name": "Assegna un nome al dispositivo", "device_type": "Tipo di dispositivo Plaato" }, - "description": "Vuoi iniziare la configurazione?", + "description": "Vuoi avviare la configurazione?", "title": "Imposta i dispositivi Plaato" }, "webhook": { - "description": "Per inviare eventi a Home Assistant, dovrai configurare la funzione webhook in Plaato Airlock. \n\n Compila le seguenti informazioni: \n\n - URL: \"{webhook_url}\"\n - Metodo: POST \n\n Vedi [la documentazione] ({docs_url}) per ulteriori dettagli.", + "description": "Per inviare eventi a Home Assistant, \u00e8 necessario configurare la funzione webhook in Plaato Airlock.\n\nInserisci le seguenti informazioni:\n\n- URL: `{webhook_url}`\n- Metodo: POST\n\nVedere [la documentazione]({docs_url}) per ulteriori dettagli.", "title": "Webhook da utilizzare" } } diff --git a/homeassistant/components/plaato/translations/ko.json b/homeassistant/components/plaato/translations/ko.json index fb75bbb7d7c..7623bb86069 100644 --- a/homeassistant/components/plaato/translations/ko.json +++ b/homeassistant/components/plaato/translations/ko.json @@ -2,6 +2,7 @@ "config": { "abort": { "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "cloud_not_connected": "Home Assistant \ud074\ub77c\uc6b0\ub4dc\uc5d0 \uc5f0\uacb0\ub418\uc5b4 \uc788\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.", "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.", "webhook_not_internet_accessible": "\uc6f9 \ud6c5 \uba54\uc2dc\uc9c0\ub97c \ubc1b\uc73c\ub824\uba74 \uc778\ud130\ub137\uc5d0\uc11c Home Assistant \uc778\uc2a4\ud134\uc2a4\uc5d0 \uc811\uadfc\ud560 \uc218 \uc788\uc5b4\uc57c \ud569\ub2c8\ub2e4." }, diff --git a/homeassistant/components/plaato/translations/no.json b/homeassistant/components/plaato/translations/no.json index 8065c4fb471..cb18ab94049 100644 --- a/homeassistant/components/plaato/translations/no.json +++ b/homeassistant/components/plaato/translations/no.json @@ -7,7 +7,7 @@ "webhook_not_internet_accessible": "Home Assistant forekomsten din m\u00e5 v\u00e6re tilgjengelig fra internett for \u00e5 kunne motta webhook meldinger" }, "create_entry": { - "default": "Plaato {device_type} med navnet **{device_name}** ble konfigurert!" + "default": "Din Plaato {device_type} med navn ** {device_name} ** ble konfigurert!" }, "error": { "invalid_webhook_device": "Du har valgt en enhet som ikke st\u00f8tter sending av data til en webhook. Den er bare tilgjengelig for luftsluse", @@ -32,7 +32,7 @@ "title": "Sett opp Plaato-enhetene" }, "webhook": { - "description": "For \u00e5 sende hendelser til Home Assistant, m\u00e5 du sette opp webhook-funksjonen i Plaato Airlock. \n\n Fyll ut f\u00f8lgende informasjon: \n\n - URL: `{webhook_url}` \n - Metode: POST \n\n Se [dokumentasjonen]({docs_url}) for ytterligere detaljer.", + "description": "For \u00e5 sende hendelser til Home Assistant, m\u00e5 du sette opp webhook-funksjonen i Plaato Airlock. \n\n Fyll inn f\u00f8lgende informasjon: \n\n - URL: ` {webhook_url} `\n - Metode: POST \n\n Se [dokumentasjonen]( {docs_url} ) for ytterligere detaljer.", "title": "Webhook \u00e5 bruke" } } diff --git a/homeassistant/components/plaato/translations/pt.json b/homeassistant/components/plaato/translations/pt.json index 7e145c2f063..df342d9a1fc 100644 --- a/homeassistant/components/plaato/translations/pt.json +++ b/homeassistant/components/plaato/translations/pt.json @@ -8,7 +8,7 @@ }, "step": { "user": { - "description": "Quer dar inicio \u00e0 configura\u00e7\u00e3o?" + "description": "Quer dar in\u00edcio \u00e0 configura\u00e7\u00e3o?" } } } diff --git a/homeassistant/components/plaato/translations/sk.json b/homeassistant/components/plaato/translations/sk.json index c8633ec2895..af05e58e84e 100644 --- a/homeassistant/components/plaato/translations/sk.json +++ b/homeassistant/components/plaato/translations/sk.json @@ -10,13 +10,17 @@ "default": "Va\u0161e zariadenie Plaato {device_type} s n\u00e1zvom **{device_name}** bolo \u00faspe\u0161ne nastaven\u00e9!" }, "error": { + "invalid_webhook_device": "Vybrali ste zariadenie, ktor\u00e9 nepodporuje odosielanie \u00fadajov do webhooku. Je k dispoz\u00edcii iba pre Airlock", + "no_api_method": "Mus\u00edte prida\u0165 autoriza\u010dn\u00fd token alebo vybra\u0165 webhook", "no_auth_token": "Mus\u00edte prida\u0165 autoriza\u010dn\u00fd token" }, "step": { "api_method": { "data": { - "token": "Sem vlo\u017ete autoriza\u010dn\u00fd token" + "token": "Sem vlo\u017ete autoriza\u010dn\u00fd token", + "use_webhook": "Pou\u017eite webhook" }, + "description": "Aby ste mohli vyh\u013ead\u00e1va\u0165 API, je potrebn\u00fd \u201eauth_token\u201c, ktor\u00fd mo\u017eno z\u00edska\u0165 pod\u013ea [t\u00fdchto](https://plaato.zendesk.com/hc/en-us/articles/360003234717-Auth-token) pokynov \n\nVybran\u00e9 zariadenie: **{device_type}** \n\nAk rad\u0161ej pou\u017e\u00edvate vstavan\u00fa met\u00f3du webhooku (iba Airlock), za\u010diarknite pol\u00ed\u010dko ni\u017e\u0161ie a ponechajte Auth Token pr\u00e1zdne", "title": "Vyberte met\u00f3du API" }, "user": { @@ -26,6 +30,10 @@ }, "description": "Chcete za\u010da\u0165 nastavova\u0165?", "title": "Nastavte zariadenia Plaato" + }, + "webhook": { + "description": "Ak chcete odosiela\u0165 udalosti dom\u00e1cemu asistentovi, budete musie\u0165 nastavi\u0165 funkciu webhook v Plaato Airlock. \n\nVypl\u0148te nasleduj\u00face inform\u00e1cie: \n\n- URL: `{webhook_url}`\n- Met\u00f3da: POST \n\n\u010eal\u0161ie podrobnosti n\u00e1jdete v [dokument\u00e1cii]({docs_url}).", + "title": "Webhook na pou\u017eitie" } } }, @@ -39,7 +47,8 @@ "title": "Mo\u017enosti pre Plaato" }, "webhook": { - "description": "Inform\u00e1cie o webhooku: \n\n - URL: `{webhook_url}`\n - Met\u00f3da: POST \n\n" + "description": "Inform\u00e1cie o webhooku: \n\n - URL: `{webhook_url}`\n - Met\u00f3da: POST \n\n", + "title": "Mo\u017enosti pre Plaato Airlock" } } } diff --git a/homeassistant/components/plant/__init__.py b/homeassistant/components/plant/__init__.py index 69f440b6859..35273e6fce0 100644 --- a/homeassistant/components/plant/__init__.py +++ b/homeassistant/components/plant/__init__.py @@ -18,7 +18,7 @@ from homeassistant.const import ( STATE_PROBLEM, STATE_UNAVAILABLE, STATE_UNKNOWN, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import HomeAssistantError @@ -138,7 +138,7 @@ class Plant(Entity): "min": CONF_MIN_BATTERY_LEVEL, }, READING_TEMPERATURE: { - ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS, + ATTR_UNIT_OF_MEASUREMENT: UnitOfTemperature.CELSIUS, "min": CONF_MIN_TEMPERATURE, "max": CONF_MAX_TEMPERATURE, }, diff --git a/homeassistant/components/plant/translations/nb.json b/homeassistant/components/plant/translations/nb.json index c8f9e3e1d44..ded8128524a 100644 --- a/homeassistant/components/plant/translations/nb.json +++ b/homeassistant/components/plant/translations/nb.json @@ -1,7 +1,7 @@ { "state": { "_": { - "ok": "", + "ok": "Ok", "problem": "Problem" } }, diff --git a/homeassistant/components/plex/__init__.py b/homeassistant/components/plex/__init__.py index 20f14de56c9..0012a5987e7 100644 --- a/homeassistant/components/plex/__init__.py +++ b/homeassistant/components/plex/__init__.py @@ -142,7 +142,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: raise ConfigEntryNotReady from error except plexapi.exceptions.Unauthorized as ex: raise ConfigEntryAuthFailed( - f"Token not accepted, please reauthenticate Plex server '{entry.data[CONF_SERVER]}'" + "Token not accepted, please reauthenticate Plex server" + f" '{entry.data[CONF_SERVER]}'" ) from ex except ( plexapi.exceptions.BadRequest, diff --git a/homeassistant/components/plex/manifest.json b/homeassistant/components/plex/manifest.json index 8eb1e3d6903..193dab8ebd4 100644 --- a/homeassistant/components/plex/manifest.json +++ b/homeassistant/components/plex/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/plex", "requirements": [ - "plexapi==4.13.1", + "plexapi==4.13.2", "plexauth==0.0.6", "plexwebsocket==0.0.13" ], diff --git a/homeassistant/components/plex/media_search.py b/homeassistant/components/plex/media_search.py index 351ee1444c4..a853bac33a5 100644 --- a/homeassistant/components/plex/media_search.py +++ b/homeassistant/components/plex/media_search.py @@ -89,7 +89,8 @@ def search_media( if len(exact_matches) == 1: return exact_matches[0] raise MediaNotFound( - f"Multiple matches, make content_id more specific or use `allow_multiple`: {results}" + "Multiple matches, make content_id more specific or use `allow_multiple`:" + f" {results}" ) return results[0] diff --git a/homeassistant/components/plex/models.py b/homeassistant/components/plex/models.py index d834012d287..9c274774d07 100644 --- a/homeassistant/components/plex/models.py +++ b/homeassistant/components/plex/models.py @@ -77,7 +77,10 @@ class PlexSession: elif media.librarySectionID and media.librarySectionID < 1: self.media_library_title = UNKNOWN_SECTION _LOGGER.warning( - "Unknown library section ID (%s) for title '%s', please create an issue", + ( + "Unknown library section ID (%s) for title '%s'," + " please create an issue" + ), media.librarySectionID, media.title, ) @@ -92,7 +95,11 @@ class PlexSession: self.media_series_title = media.grandparentTitle if media.index is not None: self.media_episode = media.index - self.sensor_title = f"{self.media_series_title} - {media.seasonEpisode} - {self.media_title}" + self.sensor_title = ( + f"{self.media_series_title} -" + f" {media.seasonEpisode} -" + f" {self.media_title}" + ) elif media.type == "movie": self.media_content_type = MediaType.MOVIE if media.year is not None and media.title is not None: diff --git a/homeassistant/components/plex/server.py b/homeassistant/components/plex/server.py index 8bcb0192cd2..827712889e1 100644 --- a/homeassistant/components/plex/server.py +++ b/homeassistant/components/plex/server.py @@ -198,7 +198,8 @@ class PlexServer: config_entry_update_needed = True else: raise Unauthorized( # pylint: disable=raise-missing-from - "New certificate cannot be validated with provided token" + "New certificate cannot be validated" + " with provided token" ) else: raise @@ -212,7 +213,8 @@ class PlexServer: shared_users = self.account.users() if self.account else [] except Unauthorized: _LOGGER.warning( - "Plex account has limited permissions, shared account filtering will not be available" + "Plex account has limited permissions," + " shared account filtering will not be available" ) else: self._accounts = [] diff --git a/homeassistant/components/plex/services.py b/homeassistant/components/plex/services.py index 0847583635d..cb88e98257d 100644 --- a/homeassistant/components/plex/services.py +++ b/homeassistant/components/plex/services.py @@ -36,8 +36,9 @@ async def async_setup_services(hass): async def async_scan_clients_service(_: ServiceCall) -> None: _LOGGER.warning( - "This service is deprecated in favor of the scan_clients button entity. " - "Service calls will still work for now but the service will be removed in a future release" + "This service is deprecated in favor of the scan_clients button entity." + " Service calls will still work for now but the service will be removed in" + " a future release" ) for server_id in hass.data[DOMAIN][SERVERS]: async_dispatcher_send(hass, PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id)) @@ -103,7 +104,8 @@ def get_plex_server(hass, plex_server_name=None, plex_server_id=None): friendly_names = [x.friendly_name for x in plex_servers] raise HomeAssistantError( - f"Multiple Plex servers configured, choose with 'plex_server' key: {friendly_names}" + "Multiple Plex servers configured, choose with 'plex_server' key:" + f" {friendly_names}" ) diff --git a/homeassistant/components/plex/translations/de.json b/homeassistant/components/plex/translations/de.json index 3eb1c1b7707..3584c481bdd 100644 --- a/homeassistant/components/plex/translations/de.json +++ b/homeassistant/components/plex/translations/de.json @@ -2,7 +2,7 @@ "config": { "abort": { "all_configured": "Alle verkn\u00fcpften Server sind bereits konfiguriert", - "already_configured": "Dieser Plex-Server ist bereits konfiguriert", + "already_configured": "Dieser Plex Server ist bereits konfiguriert", "already_in_progress": "Der Konfigurationsablauf wird bereits ausgef\u00fchrt", "reauth_successful": "Die erneute Authentifizierung war erfolgreich", "token_request_timeout": "Zeit\u00fcberschreitung beim Erhalt des Tokens", @@ -12,7 +12,7 @@ "faulty_credentials": "Autorisierung fehlgeschlagen, Token \u00fcberpr\u00fcfen", "host_or_token": "Es muss mindestens ein Host oder ein Token bereitgestellt werden", "no_servers": "Keine Server mit Plex-Konto verbunden", - "not_found": "Plex-Server nicht gefunden", + "not_found": "Plex Server nicht gefunden", "ssl_error": "SSL-Zertifikatsproblem" }, "flow_title": "{name} ({host})", @@ -25,17 +25,17 @@ "token": "Token (optional)", "verify_ssl": "SSL-Zertifikat \u00fcberpr\u00fcfen" }, - "title": "Manuelle Plex-Konfiguration" + "title": "Manuelle Plex Konfiguration" }, "select_server": { "data": { "server": "Server" }, "description": "Mehrere Server verf\u00fcgbar, w\u00e4hle einen aus:", - "title": "Plex-Server ausw\u00e4hlen" + "title": "Plex Server ausw\u00e4hlen" }, "user": { - "description": "Gehe zu [plex.tv] (https://plex.tv), um einen Plex-Server zu verbinden" + "description": "Gehe zu [plex.tv] (https://plex.tv), um einen Plex Server zu verbinden" }, "user_advanced": { "data": { diff --git a/homeassistant/components/plex/translations/pt.json b/homeassistant/components/plex/translations/pt.json index 6daae889e80..be2cc7b92ee 100644 --- a/homeassistant/components/plex/translations/pt.json +++ b/homeassistant/components/plex/translations/pt.json @@ -12,7 +12,7 @@ "step": { "manual_setup": { "data": { - "host": "Servidor", + "host": "Endere\u00e7o", "port": "Porta", "ssl": "Utiliza um certificado SSL", "verify_ssl": "Verificar o certificado SSL" diff --git a/homeassistant/components/plex/translations/sk.json b/homeassistant/components/plex/translations/sk.json index db7c9aeb581..9cea5e26556 100644 --- a/homeassistant/components/plex/translations/sk.json +++ b/homeassistant/components/plex/translations/sk.json @@ -31,6 +31,7 @@ "data": { "server": "Server" }, + "description": "K dispoz\u00edcii je viacero serverov, vyberte jeden:", "title": "Vyberte server Plex" }, "user": { @@ -42,5 +43,18 @@ } } } + }, + "options": { + "step": { + "plex_mp_settings": { + "data": { + "ignore_new_shared_users": "Ignorova\u0165 nov\u00fdch spravovan\u00fdch/zdie\u013ean\u00fdch pou\u017e\u00edvate\u013eov", + "ignore_plex_web_clients": "Ignorovanie webov\u00fdch klientov Plex", + "monitored_users": "Sledovan\u00ed pou\u017e\u00edvatelia", + "use_episode_art": "Pou\u017eitie grafiky epiz\u00f3d" + }, + "description": "Mo\u017enosti pre prehr\u00e1va\u010de m\u00e9di\u00ed Plex" + } + } } } \ No newline at end of file diff --git a/homeassistant/components/plugwise/climate.py b/homeassistant/components/plugwise/climate.py index 8d0d3578c2c..833ce0d4b22 100644 --- a/homeassistant/components/plugwise/climate.py +++ b/homeassistant/components/plugwise/climate.py @@ -13,7 +13,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -41,8 +41,9 @@ async def async_setup_entry( class PlugwiseClimateEntity(PlugwiseEntity, ClimateEntity): """Representation of an Plugwise thermostat.""" - _attr_temperature_unit = TEMP_CELSIUS _attr_has_entity_name = True + _attr_temperature_unit = UnitOfTemperature.CELSIUS + _attr_translation_key = DOMAIN def __init__( self, diff --git a/homeassistant/components/plugwise/config_flow.py b/homeassistant/components/plugwise/config_flow.py index 3bbd5725b29..45d83e316b1 100644 --- a/homeassistant/components/plugwise/config_flow.py +++ b/homeassistant/components/plugwise/config_flow.py @@ -157,7 +157,9 @@ class PlugwiseConfigFlow(ConfigFlow, domain=DOMAIN): CONF_PORT: discovery_info.port, CONF_USERNAME: self._username, }, - "configuration_url": f"http://{discovery_info.host}:{discovery_info.port}", + "configuration_url": ( + f"http://{discovery_info.host}:{discovery_info.port}" + ), "product": _product, } ) diff --git a/homeassistant/components/plugwise/coordinator.py b/homeassistant/components/plugwise/coordinator.py index 30adca12819..afcd673ef7d 100644 --- a/homeassistant/components/plugwise/coordinator.py +++ b/homeassistant/components/plugwise/coordinator.py @@ -80,7 +80,8 @@ class PlugwiseDataUpdateCoordinator(DataUpdateCoordinator[PlugwiseData]): raise ConfigEntryError("Invalid username or Smile ID") from err except (InvalidXMLError, ResponseError) as err: raise UpdateFailed( - "Invalid XML data, or error indication received for the Plugwise Adam/Smile/Stretch" + "Invalid XML data, or error indication received for the Plugwise" + " Adam/Smile/Stretch" ) from err except UnsupportedDeviceError as err: raise ConfigEntryError("Device with unsupported firmware") from err diff --git a/homeassistant/components/plugwise/number.py b/homeassistant/components/plugwise/number.py index 0b8c7f820b4..836af62a388 100644 --- a/homeassistant/components/plugwise/number.py +++ b/homeassistant/components/plugwise/number.py @@ -13,7 +13,7 @@ from homeassistant.components.number import ( NumberMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import TEMP_CELSIUS +from homeassistant.const import UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -51,7 +51,7 @@ NUMBER_TYPES = ( native_max_value_key="upper_bound", native_min_value_key="lower_bound", native_step_key="resolution", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, native_value_key="setpoint", ), ) diff --git a/homeassistant/components/plugwise/select.py b/homeassistant/components/plugwise/select.py index 73157c6a962..dd36dd69f01 100644 --- a/homeassistant/components/plugwise/select.py +++ b/homeassistant/components/plugwise/select.py @@ -49,20 +49,20 @@ SELECT_TYPES = ( name="Regulation mode", icon="mdi:hvac", entity_category=EntityCategory.CONFIG, + translation_key="regulation_mode", command=lambda api, loc, opt: api.set_regulation_mode(opt), current_option_key="regulation_mode", options_key="regulation_modes", - device_class="plugwise__regulation_mode", ), PlugwiseSelectEntityDescription( key="select_dhw_mode", name="DHW mode", icon="mdi:shower", entity_category=EntityCategory.CONFIG, + translation_key="dhw_mode", command=lambda api, loc, opt: api.set_dhw_mode(opt), current_option_key="dhw_mode", options_key="dhw_modes", - device_class="plugwise__dhw_mode", ), ) diff --git a/homeassistant/components/plugwise/sensor.py b/homeassistant/components/plugwise/sensor.py index a59eb0cfc39..91fd32c92c2 100644 --- a/homeassistant/components/plugwise/sensor.py +++ b/homeassistant/components/plugwise/sensor.py @@ -9,13 +9,12 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - ENERGY_KILO_WATT_HOUR, - ENERGY_WATT_HOUR, PERCENTAGE, - POWER_WATT, - PRESSURE_BAR, - TEMP_CELSIUS, - VOLUME_CUBIC_METERS, + UnitOfEnergy, + UnitOfPower, + UnitOfPressure, + UnitOfTemperature, + UnitOfVolume, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory @@ -29,7 +28,7 @@ SENSORS: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="setpoint", name="Setpoint", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, @@ -37,7 +36,7 @@ SENSORS: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="setpoint_high", name="Cooling setpoint", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, @@ -45,7 +44,7 @@ SENSORS: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="setpoint_low", name="Heating setpoint", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, @@ -53,7 +52,7 @@ SENSORS: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="temperature", name="Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, entity_category=EntityCategory.DIAGNOSTIC, state_class=SensorStateClass.MEASUREMENT, @@ -61,7 +60,7 @@ SENSORS: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="intended_boiler_temperature", name="Intended boiler temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, entity_category=EntityCategory.DIAGNOSTIC, state_class=SensorStateClass.MEASUREMENT, @@ -69,7 +68,7 @@ SENSORS: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="temperature_difference", name="Temperature difference", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, entity_category=EntityCategory.DIAGNOSTIC, state_class=SensorStateClass.MEASUREMENT, @@ -77,14 +76,14 @@ SENSORS: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="outdoor_temperature", name="Outdoor temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="outdoor_air_temperature", name="Outdoor air temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, entity_category=EntityCategory.DIAGNOSTIC, state_class=SensorStateClass.MEASUREMENT, @@ -92,7 +91,7 @@ SENSORS: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="water_temperature", name="Water temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, entity_category=EntityCategory.DIAGNOSTIC, state_class=SensorStateClass.MEASUREMENT, @@ -100,7 +99,7 @@ SENSORS: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="return_temperature", name="Return temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, entity_category=EntityCategory.DIAGNOSTIC, state_class=SensorStateClass.MEASUREMENT, @@ -108,14 +107,14 @@ SENSORS: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="electricity_consumed", name="Electricity consumed", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="electricity_produced", name="Electricity produced", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, entity_registry_enabled_default=False, @@ -123,28 +122,28 @@ SENSORS: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="electricity_consumed_interval", name="Electricity consumed interval", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL, ), SensorEntityDescription( key="electricity_consumed_peak_interval", name="Electricity consumed peak interval", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL, ), SensorEntityDescription( key="electricity_consumed_off_peak_interval", name="Electricity consumed off peak interval", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL, ), SensorEntityDescription( key="electricity_produced_interval", name="Electricity produced interval", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL, entity_registry_enabled_default=False, @@ -152,98 +151,98 @@ SENSORS: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="electricity_produced_peak_interval", name="Electricity produced peak interval", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL, ), SensorEntityDescription( key="electricity_produced_off_peak_interval", name="Electricity produced off peak interval", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL, ), SensorEntityDescription( key="electricity_consumed_off_peak_point", name="Electricity consumed off peak point", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="electricity_consumed_peak_point", name="Electricity consumed peak point", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="electricity_consumed_off_peak_cumulative", name="Electricity consumed off peak cumulative", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), SensorEntityDescription( key="electricity_consumed_peak_cumulative", name="Electricity consumed peak cumulative", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), SensorEntityDescription( key="electricity_produced_off_peak_point", name="Electricity produced off peak point", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="electricity_produced_peak_point", name="Electricity produced peak point", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="electricity_produced_off_peak_cumulative", name="Electricity produced off peak cumulative", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), SensorEntityDescription( key="electricity_produced_peak_cumulative", name="Electricity produced peak cumulative", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), SensorEntityDescription( key="gas_consumed_interval", name="Gas consumed interval", - native_unit_of_measurement=VOLUME_CUBIC_METERS, + native_unit_of_measurement=UnitOfVolume.CUBIC_METERS, device_class=SensorDeviceClass.GAS, state_class=SensorStateClass.TOTAL, ), SensorEntityDescription( key="gas_consumed_cumulative", name="Gas consumed cumulative", - native_unit_of_measurement=VOLUME_CUBIC_METERS, + native_unit_of_measurement=UnitOfVolume.CUBIC_METERS, device_class=SensorDeviceClass.GAS, state_class=SensorStateClass.TOTAL, ), SensorEntityDescription( key="net_electricity_point", name="Net electricity point", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="net_electricity_cumulative", name="Net electricity cumulative", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL, ), @@ -259,7 +258,6 @@ SENSORS: tuple[SensorEntityDescription, ...] = ( key="illuminance", name="Illuminance", native_unit_of_measurement=UNIT_LUMEN, - device_class=SensorDeviceClass.ILLUMINANCE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( @@ -281,7 +279,7 @@ SENSORS: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="water_pressure", name="Water pressure", - native_unit_of_measurement=PRESSURE_BAR, + native_unit_of_measurement=UnitOfPressure.BAR, device_class=SensorDeviceClass.PRESSURE, entity_category=EntityCategory.DIAGNOSTIC, state_class=SensorStateClass.MEASUREMENT, @@ -296,7 +294,7 @@ SENSORS: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="dhw_temperature", name="DHW temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, entity_category=EntityCategory.DIAGNOSTIC, state_class=SensorStateClass.MEASUREMENT, @@ -304,7 +302,7 @@ SENSORS: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="domestic_hot_water_setpoint", name="DHW setpoint", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, entity_category=EntityCategory.DIAGNOSTIC, state_class=SensorStateClass.MEASUREMENT, @@ -312,7 +310,7 @@ SENSORS: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="maximum_boiler_temperature", name="Maximum boiler temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, entity_category=EntityCategory.DIAGNOSTIC, state_class=SensorStateClass.MEASUREMENT, diff --git a/homeassistant/components/plugwise/strings.json b/homeassistant/components/plugwise/strings.json index 781a17a1d10..7d9f32f2651 100644 --- a/homeassistant/components/plugwise/strings.json +++ b/homeassistant/components/plugwise/strings.json @@ -24,5 +24,41 @@ "already_configured": "[%key:common::config_flow::abort::already_configured_service%]", "anna_with_adam": "Both Anna and Adam detected. Add your Adam instead of your Anna" } + }, + "entity": { + "climate": { + "plugwise": { + "state_attributes": { + "preset_mode": { + "state": { + "asleep": "Night", + "away": "Away", + "home": "Home", + "no_frost": "Anti-frost", + "vacation": "Vacation" + } + } + } + } + }, + "select": { + "dhw_mode": { + "state": { + "off": "Off", + "auto": "Auto", + "boost": "Boost", + "comfort": "Comfort" + } + }, + "regulation_mode": { + "state": { + "bleeding_cold": "Bleeding cold", + "bleeding_hot": "Bleeding hot", + "cooling": "Cooling", + "heating": "Heating", + "off": "Off" + } + } + } } } diff --git a/homeassistant/components/plugwise/strings.select.json b/homeassistant/components/plugwise/strings.select.json deleted file mode 100644 index 92098e3bc35..00000000000 --- a/homeassistant/components/plugwise/strings.select.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "state": { - "plugwise__regulation_mode": { - "bleeding_cold": "Bleeding cold", - "bleeding_hot": "Bleeding hot", - "cooling": "Cooling", - "heating": "Heating", - "off": "Off" - }, - "plugwise__dhw_mode": { - "off": "Off", - "auto": "Auto", - "boost": "Boost", - "comfort": "Comfort" - } - } -} diff --git a/homeassistant/components/plugwise/translations/bg.json b/homeassistant/components/plugwise/translations/bg.json index c74eb0a71ac..d8b8c7d10a0 100644 --- a/homeassistant/components/plugwise/translations/bg.json +++ b/homeassistant/components/plugwise/translations/bg.json @@ -18,5 +18,22 @@ "description": "\u041f\u0440\u043e\u0434\u0443\u043a\u0442:" } } + }, + "entity": { + "select": { + "dhw_mode": { + "state": { + "auto": "\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u043d", + "off": "\u0418\u0437\u043a\u043b." + } + }, + "regulation_mode": { + "state": { + "cooling": "\u041e\u0445\u043b\u0430\u0436\u0434\u0430\u043d\u0435", + "heating": "\u041e\u0442\u043e\u043f\u043b\u0435\u043d\u0438\u0435", + "off": "\u0418\u0437\u043a\u043b." + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/plugwise/translations/ca.json b/homeassistant/components/plugwise/translations/ca.json index b018a764124..1c5b3fb67be 100644 --- a/homeassistant/components/plugwise/translations/ca.json +++ b/homeassistant/components/plugwise/translations/ca.json @@ -7,7 +7,8 @@ "error": { "cannot_connect": "Ha fallat la connexi\u00f3", "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida", - "invalid_setup": "Afegeix l'Adam en lloc de l'Anna; consulta la documentaci\u00f3 de la integraci\u00f3 Plugwise de Home Assistant per a m\u00e9s informaci\u00f3.", + "invalid_setup": "Afegeix l'Adam en lloc de l'Anna, consulta la documentaci\u00f3", + "response_error": "Dades XML inv\u00e0lides o s'ha rebut una indicaci\u00f3 d'error", "unknown": "Error inesperat", "unsupported": "Dispositiu amb programari no compatible" }, @@ -23,5 +24,41 @@ "title": "Connexi\u00f3 amb Smile" } } + }, + "entity": { + "climate": { + "plugwise": { + "state_attributes": { + "preset_mode": { + "state": { + "asleep": "Nit", + "away": "A fora", + "home": "A casa", + "no_frost": "Anti-gelades", + "vacation": "Vacances" + } + } + } + } + }, + "select": { + "dhw_mode": { + "state": { + "auto": "Autom\u00e0tic", + "boost": "Incrementat", + "comfort": "Confort", + "off": "OFF" + } + }, + "regulation_mode": { + "state": { + "bleeding_cold": "Molt fred", + "bleeding_hot": "Molt calent", + "cooling": "Refredant", + "heating": "Escalfant", + "off": "OFF" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/plugwise/translations/de.json b/homeassistant/components/plugwise/translations/de.json index 6aeba3e5605..b554c8a689d 100644 --- a/homeassistant/components/plugwise/translations/de.json +++ b/homeassistant/components/plugwise/translations/de.json @@ -24,5 +24,26 @@ "title": "Stelle eine Verbindung zu Smile her" } } + }, + "entity": { + "select": { + "dhw_mode": { + "state": { + "auto": "Automatisch", + "boost": "Boost", + "comfort": "Komfort", + "off": "Aus" + } + }, + "regulation_mode": { + "state": { + "bleeding_cold": "Kalt", + "bleeding_hot": "Hei\u00df", + "cooling": "K\u00fchlen", + "heating": "Heizen", + "off": "Aus" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/plugwise/translations/el.json b/homeassistant/components/plugwise/translations/el.json index 11907c3bd03..385a98fc255 100644 --- a/homeassistant/components/plugwise/translations/el.json +++ b/homeassistant/components/plugwise/translations/el.json @@ -24,5 +24,26 @@ "title": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03b2\u03cd\u03c3\u03bc\u03b1\u03c4\u03bf\u03c2" } } + }, + "entity": { + "select": { + "dhw_mode": { + "state": { + "auto": "\u0391\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03bf", + "boost": "\u0395\u03bd\u03af\u03c3\u03c7\u03c5\u03c3\u03b7", + "comfort": "\u0386\u03bd\u03b5\u03c3\u03b7", + "off": "\u0391\u03bd\u03b5\u03bd\u03b5\u03c1\u03b3\u03cc" + } + }, + "regulation_mode": { + "state": { + "bleeding_cold": "\u0391\u03b9\u03bc\u03bf\u03c1\u03c1\u03b1\u03b3\u03af\u03b1 \u03ba\u03c1\u03cd\u03b1", + "bleeding_hot": "\u0391\u03b9\u03bc\u03bf\u03c1\u03c1\u03b1\u03b3\u03af\u03b1 \u03ba\u03b1\u03c5\u03c4\u03ae", + "cooling": "\u03a8\u03cd\u03be\u03b7", + "heating": "\u0398\u03ad\u03c1\u03bc\u03b1\u03bd\u03c3\u03b7", + "off": "\u0391\u03bd\u03b5\u03bd\u03b5\u03c1\u03b3\u03cc" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/plugwise/translations/en.json b/homeassistant/components/plugwise/translations/en.json index ecc5af9218d..23cc292b1e3 100644 --- a/homeassistant/components/plugwise/translations/en.json +++ b/homeassistant/components/plugwise/translations/en.json @@ -24,5 +24,41 @@ "title": "Connect to the Smile" } } + }, + "entity": { + "climate": { + "plugwise": { + "state_attributes": { + "preset_mode": { + "state": { + "asleep": "Night", + "away": "Away", + "home": "Home", + "no_frost": "Anti-frost", + "vacation": "Vacation" + } + } + } + } + }, + "select": { + "dhw_mode": { + "state": { + "auto": "Auto", + "boost": "Boost", + "comfort": "Comfort", + "off": "Off" + } + }, + "regulation_mode": { + "state": { + "bleeding_cold": "Bleeding cold", + "bleeding_hot": "Bleeding hot", + "cooling": "Cooling", + "heating": "Heating", + "off": "Off" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/plugwise/translations/es.json b/homeassistant/components/plugwise/translations/es.json index 9797cffa43c..30673219132 100644 --- a/homeassistant/components/plugwise/translations/es.json +++ b/homeassistant/components/plugwise/translations/es.json @@ -24,5 +24,41 @@ "title": "Conectar a Smile" } } + }, + "entity": { + "climate": { + "plugwise": { + "state_attributes": { + "preset_mode": { + "state": { + "asleep": "Noche", + "away": "Ausente", + "home": "En casa", + "no_frost": "Antihielo", + "vacation": "Vacaciones" + } + } + } + } + }, + "select": { + "dhw_mode": { + "state": { + "auto": "Autom\u00e1tico", + "boost": "Impulso", + "comfort": "Confort", + "off": "Apagado" + } + }, + "regulation_mode": { + "state": { + "bleeding_cold": "Purgando fr\u00edo", + "bleeding_hot": "Purgando caliente", + "cooling": "Refrigeraci\u00f3n", + "heating": "Calefacci\u00f3n", + "off": "Apagado" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/plugwise/translations/et.json b/homeassistant/components/plugwise/translations/et.json index 3ed7329fbb1..88740292a6f 100644 --- a/homeassistant/components/plugwise/translations/et.json +++ b/homeassistant/components/plugwise/translations/et.json @@ -24,5 +24,26 @@ "title": "Loo \u00fchendus Smile-ga" } } + }, + "entity": { + "select": { + "dhw_mode": { + "state": { + "auto": "Automaatne", + "boost": "Turbo", + "comfort": "Mugav", + "off": "V\u00e4ljas" + } + }, + "regulation_mode": { + "state": { + "bleeding_cold": "Jahutuseat soenemine", + "bleeding_hot": "K\u00fcttest jahtumine", + "cooling": "Jahutus", + "heating": "K\u00fcte", + "off": "V\u00e4ljas" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/plugwise/translations/hu.json b/homeassistant/components/plugwise/translations/hu.json index cdfb76fcc5c..35de97a32ef 100644 --- a/homeassistant/components/plugwise/translations/hu.json +++ b/homeassistant/components/plugwise/translations/hu.json @@ -7,8 +7,10 @@ "error": { "cannot_connect": "Sikertelen csatlakoz\u00e1s", "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s", - "invalid_setup": "Adja hozz\u00e1 Adamot Anna helyett. Tov\u00e1bbi inform\u00e1ci\u00f3\u00e9rt tekintse meg a Home Assistant Plugwise integr\u00e1ci\u00f3s dokument\u00e1ci\u00f3j\u00e1t", - "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" + "invalid_setup": "Add hozz\u00e1 \u00c1d\u00e1mot Anna helyett, l\u00e1sd a dokument\u00e1ci\u00f3t.", + "response_error": "\u00c9rv\u00e9nytelen XML adat, vagy hibajelz\u00e9s \u00e9rkezett", + "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt", + "unsupported": "Nem t\u00e1mogatott firmware-rel rendelkez\u0151 eszk\u00f6z" }, "step": { "user": { @@ -22,5 +24,26 @@ "title": "Csatlakoz\u00e1s a Smile-hoz" } } + }, + "entity": { + "select": { + "dhw_mode": { + "state": { + "auto": "Automatikus", + "boost": "Turb\u00f3", + "comfort": "Komfort", + "off": "Ki" + } + }, + "regulation_mode": { + "state": { + "bleeding_cold": "J\u00e9ghideg", + "bleeding_hot": "T\u0171zforr\u00f3", + "cooling": "H\u0171t\u00e9s", + "heating": "F\u0171t\u00e9s", + "off": "Ki" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/plugwise/translations/id.json b/homeassistant/components/plugwise/translations/id.json index 71578c7462c..cda92a53e7f 100644 --- a/homeassistant/components/plugwise/translations/id.json +++ b/homeassistant/components/plugwise/translations/id.json @@ -24,5 +24,26 @@ "title": "Hubungkan ke Smile" } } + }, + "entity": { + "select": { + "dhw_mode": { + "state": { + "auto": "Otomatis", + "boost": "Kencang", + "comfort": "Nyaman", + "off": "Mati" + } + }, + "regulation_mode": { + "state": { + "bleeding_cold": "Dingin sekali", + "bleeding_hot": "Panas sekali", + "cooling": "Mendinginkan", + "heating": "Memanaskan", + "off": "Mati" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/plugwise/translations/it.json b/homeassistant/components/plugwise/translations/it.json index 8d0555a12a7..8e74f8bfb7c 100644 --- a/homeassistant/components/plugwise/translations/it.json +++ b/homeassistant/components/plugwise/translations/it.json @@ -7,8 +7,10 @@ "error": { "cannot_connect": "Impossibile connettersi", "invalid_auth": "Autenticazione non valida", - "invalid_setup": "Aggiungi il tuo Adam invece di Anna, consulta la documentazione sull'integrazione di Home Assistant Plugwise per ulteriori informazioni", - "unknown": "Errore imprevisto" + "invalid_setup": "Aggiungi il tuo Adam invece della tua Anna, consulta la documentazione", + "response_error": "Dati XML non validi o indicazione di errore ricevuta", + "unknown": "Errore imprevisto", + "unsupported": "Dispositivo con firmware non supportato" }, "step": { "user": { @@ -22,5 +24,26 @@ "title": "Connettiti allo Smile" } } + }, + "entity": { + "select": { + "dhw_mode": { + "state": { + "auto": "Automatico", + "boost": "Velocizza", + "comfort": "Comfort", + "off": "Spento" + } + }, + "regulation_mode": { + "state": { + "bleeding_cold": "Freddissimo", + "bleeding_hot": "Caldissimo", + "cooling": "Raffreddamento", + "heating": "Riscaldamento", + "off": "Spento" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/plugwise/translations/ko.json b/homeassistant/components/plugwise/translations/ko.json index 5873415b066..f2941a16b6f 100644 --- a/homeassistant/components/plugwise/translations/ko.json +++ b/homeassistant/components/plugwise/translations/ko.json @@ -6,10 +6,16 @@ "error": { "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", - "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + "response_error": "\uc798\ubabb\ub41c XML \ub370\uc774\ud130 \ub610\ub294 \uc624\ub958 \ud45c\uc2dc\uac00 \uc218\uc2e0\ub418\uc5c8\uc2b5\ub2c8\ub2e4.", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4", + "unsupported": "\uc9c0\uc6d0\ub418\uc9c0 \uc54a\ub294 \ud38c\uc6e8\uc5b4\uac00 \uc788\ub294 \uae30\uae30" }, "step": { "user": { + "data": { + "host": "IP \uc8fc\uc18c", + "port": "\ud3ec\ud2b8" + }, "description": "\uc81c\ud488:", "title": "Plugwise \uc720\ud615" } diff --git a/homeassistant/components/plugwise/translations/lb.json b/homeassistant/components/plugwise/translations/lb.json index 8c160412faa..5e961da91a9 100644 --- a/homeassistant/components/plugwise/translations/lb.json +++ b/homeassistant/components/plugwise/translations/lb.json @@ -10,6 +10,9 @@ }, "step": { "user": { + "data": { + "username": "Smile-Benotzernumm" + }, "description": "Produkt:", "title": "Typ vu Plugwise" } diff --git a/homeassistant/components/plugwise/translations/nl.json b/homeassistant/components/plugwise/translations/nl.json index 5a7e0dc687f..dc8044ee29f 100644 --- a/homeassistant/components/plugwise/translations/nl.json +++ b/homeassistant/components/plugwise/translations/nl.json @@ -23,5 +23,19 @@ "title": "Plugwise type" } } + }, + "entity": { + "select": { + "dhw_mode": { + "state": { + "off": "Uit" + } + }, + "regulation_mode": { + "state": { + "off": "Uit" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/plugwise/translations/no.json b/homeassistant/components/plugwise/translations/no.json index ea676591361..338b08b6624 100644 --- a/homeassistant/components/plugwise/translations/no.json +++ b/homeassistant/components/plugwise/translations/no.json @@ -24,5 +24,26 @@ "title": "Koble til Smile" } } + }, + "entity": { + "select": { + "dhw_mode": { + "state": { + "auto": "Auto", + "boost": "\u00d8ke", + "comfort": "Komfort", + "off": "Av" + } + }, + "regulation_mode": { + "state": { + "bleeding_cold": "Bl\u00f8dende forkj\u00f8lelse", + "bleeding_hot": "Bl\u00f8dende varmt", + "cooling": "Kj\u00f8ling", + "heating": "Oppvarming", + "off": "Av" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/plugwise/translations/pl.json b/homeassistant/components/plugwise/translations/pl.json index cfbd6009a37..5d0041b0ef2 100644 --- a/homeassistant/components/plugwise/translations/pl.json +++ b/homeassistant/components/plugwise/translations/pl.json @@ -7,8 +7,10 @@ "error": { "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", "invalid_auth": "Niepoprawne uwierzytelnienie", - "invalid_setup": "Dodaj urz\u0105dzenie Adam zamiast Anna. Zobacz dokumentacj\u0119 integracji Plugwise dla Home Assistant, aby uzyska\u0107 wi\u0119cej informacji.", - "unknown": "Nieoczekiwany b\u0142\u0105d" + "invalid_setup": "Dodaj urz\u0105dzenie Adam zamiast Anna. Zobacz dokumentacj\u0119.", + "response_error": "Nieprawid\u0142owe dane XML lub otrzymano informacj\u0119 o b\u0142\u0119dzie", + "unknown": "Nieoczekiwany b\u0142\u0105d", + "unsupported": "Urz\u0105dzenie z nieobs\u0142ugiwanym oprogramowaniem" }, "step": { "user": { @@ -22,5 +24,26 @@ "title": "Po\u0142\u0105czenie ze Smile" } } + }, + "entity": { + "select": { + "dhw_mode": { + "state": { + "auto": "auto", + "boost": "dogrzewanie", + "comfort": "komfortowo", + "off": "wy\u0142\u0105czone" + } + }, + "regulation_mode": { + "state": { + "bleeding_cold": "strasznie zimno", + "bleeding_hot": "strasznie ciep\u0142o", + "cooling": "ch\u0142odzenie", + "heating": "grzanie", + "off": "wy\u0142\u0105czony" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/plugwise/translations/pt-BR.json b/homeassistant/components/plugwise/translations/pt-BR.json index edb638bae7a..fe3378b77ba 100644 --- a/homeassistant/components/plugwise/translations/pt-BR.json +++ b/homeassistant/components/plugwise/translations/pt-BR.json @@ -24,5 +24,26 @@ "title": "Conecte-se ao Smile" } } + }, + "entity": { + "select": { + "dhw_mode": { + "state": { + "auto": "Auto", + "boost": "Impulso", + "comfort": "Conforto", + "off": "Desligado" + } + }, + "regulation_mode": { + "state": { + "bleeding_cold": "Sangramento frio", + "bleeding_hot": "Sangramento quente", + "cooling": "Resfriamento", + "heating": "Aquecimento", + "off": "Desligado" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/plugwise/translations/pt.json b/homeassistant/components/plugwise/translations/pt.json index 3481b1de025..d332abd62ae 100644 --- a/homeassistant/components/plugwise/translations/pt.json +++ b/homeassistant/components/plugwise/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "O servi\u00e7o j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" } diff --git a/homeassistant/components/plugwise/translations/ru.json b/homeassistant/components/plugwise/translations/ru.json index 21b3c00a99a..08bd7f13228 100644 --- a/homeassistant/components/plugwise/translations/ru.json +++ b/homeassistant/components/plugwise/translations/ru.json @@ -24,5 +24,24 @@ "title": "\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a Smile" } } + }, + "entity": { + "select": { + "dhw_mode": { + "state": { + "auto": "\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438", + "boost": "\u0422\u0443\u0440\u0431\u043e", + "comfort": "\u041a\u043e\u043c\u0444\u043e\u0440\u0442", + "off": "\u0412\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u043e" + } + }, + "regulation_mode": { + "state": { + "cooling": "\u041e\u0445\u043b\u0430\u0436\u0434\u0435\u043d\u0438\u0435", + "heating": "\u041e\u0431\u043e\u0433\u0440\u0435\u0432", + "off": "\u0412\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u043e" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/plugwise/translations/select.hu.json b/homeassistant/components/plugwise/translations/select.hu.json index 18480d95f63..22ae7304c83 100644 --- a/homeassistant/components/plugwise/translations/select.hu.json +++ b/homeassistant/components/plugwise/translations/select.hu.json @@ -8,7 +8,7 @@ }, "plugwise__regulation_mode": { "bleeding_cold": "J\u00e9ghideg", - "bleeding_hot": "Forr\u00f3", + "bleeding_hot": "T\u0171zforr\u00f3", "cooling": "H\u0171t\u00e9s", "heating": "F\u0171t\u00e9s", "off": "Ki" diff --git a/homeassistant/components/plugwise/translations/select.sk.json b/homeassistant/components/plugwise/translations/select.sk.json new file mode 100644 index 00000000000..373ea2ae943 --- /dev/null +++ b/homeassistant/components/plugwise/translations/select.sk.json @@ -0,0 +1,17 @@ +{ + "state": { + "plugwise__dhw_mode": { + "auto": "Auto", + "boost": "Turbo", + "comfort": "Komfort", + "off": "Vypnut\u00e9" + }, + "plugwise__regulation_mode": { + "bleeding_cold": "Studen\u00fd", + "bleeding_hot": "Hor\u00faci", + "cooling": "Chladenie", + "heating": "Vykurovanie", + "off": "Vypnut\u00e9" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/plugwise/translations/sk.json b/homeassistant/components/plugwise/translations/sk.json index 12048de0987..cceb334b484 100644 --- a/homeassistant/components/plugwise/translations/sk.json +++ b/homeassistant/components/plugwise/translations/sk.json @@ -1,11 +1,14 @@ { "config": { "abort": { - "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1", + "anna_with_adam": "Anna aj Adam zistili. Pridajte svojho Adama namiesto svojej Anny" }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie", + "invalid_setup": "Pridajte svojho Adama namiesto svojej Anny, pozrite si dokument\u00e1ciu", + "response_error": "Neplatn\u00e9 \u00fadaje XML alebo prijat\u00e9 chybov\u00e9 hl\u00e1senie", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba", "unsupported": "Zariadenie s nepodporovan\u00fdm firmv\u00e9rom" }, @@ -13,9 +16,48 @@ "user": { "data": { "host": "IP adresa", - "port": "Port" + "password": "Smile ID", + "port": "Port", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno Smile" }, - "description": "Pros\u00edm, zadajte" + "description": "Pros\u00edm, zadajte", + "title": "Pripojte sa k Smile" + } + } + }, + "entity": { + "climate": { + "plugwise": { + "state_attributes": { + "preset_mode": { + "state": { + "asleep": "Noc", + "away": "Pre\u010d", + "home": "Doma", + "no_frost": "Ochrana proti mrazu", + "vacation": "Dovolenka" + } + } + } + } + }, + "select": { + "dhw_mode": { + "state": { + "auto": "Auto", + "boost": "Turbo", + "comfort": "Komfort", + "off": "Vypnut\u00e9" + } + }, + "regulation_mode": { + "state": { + "bleeding_cold": "Studen\u00fd", + "bleeding_hot": "Hor\u00faci", + "cooling": "Chladenie", + "heating": "Vykurovanie", + "off": "Vypnut\u00e9" + } } } } diff --git a/homeassistant/components/plugwise/translations/zh-Hant.json b/homeassistant/components/plugwise/translations/zh-Hant.json index 07ebe6e64ef..e7d9f6ca870 100644 --- a/homeassistant/components/plugwise/translations/zh-Hant.json +++ b/homeassistant/components/plugwise/translations/zh-Hant.json @@ -24,5 +24,41 @@ "title": "\u9023\u7dda\u81f3 Smile" } } + }, + "entity": { + "climate": { + "plugwise": { + "state_attributes": { + "preset_mode": { + "state": { + "asleep": "\u591c\u9593\u6a21\u5f0f", + "away": "\u96e2\u5bb6\u6a21\u5f0f", + "home": "\u5728\u5bb6\u6a21\u5f0f", + "no_frost": "\u9632\u971c\u6a21\u5f0f", + "vacation": "\u5ea6\u5047\u6a21\u5f0f" + } + } + } + } + }, + "select": { + "dhw_mode": { + "state": { + "auto": "\u81ea\u52d5", + "boost": "\u5168\u901f\u6a21\u5f0f", + "comfort": "\u8212\u9069\u6a21\u5f0f", + "off": "\u95dc\u9589" + } + }, + "regulation_mode": { + "state": { + "bleeding_cold": "\u5f37\u51b7", + "bleeding_hot": "\u5f37\u6696", + "cooling": "\u51b7\u6c23", + "heating": "\u6696\u6c23", + "off": "\u95dc\u9589" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/plum_lightpad/translations/pt.json b/homeassistant/components/plum_lightpad/translations/pt.json index b72e9284a35..7d23c995e9b 100644 --- a/homeassistant/components/plum_lightpad/translations/pt.json +++ b/homeassistant/components/plum_lightpad/translations/pt.json @@ -4,13 +4,13 @@ "already_configured": "Conta j\u00e1 configurada" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "user": { "data": { "password": "Palavra-passe", - "username": "Email" + "username": "" } } } diff --git a/homeassistant/components/point/sensor.py b/homeassistant/components/point/sensor.py index 619180c0460..cba990b60a6 100644 --- a/homeassistant/components/point/sensor.py +++ b/homeassistant/components/point/sensor.py @@ -11,7 +11,7 @@ from homeassistant.components.sensor import ( SensorEntityDescription, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import PERCENTAGE, SOUND_PRESSURE_WEIGHTED_DBA, TEMP_CELSIUS +from homeassistant.const import PERCENTAGE, UnitOfSoundPressure, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -42,7 +42,7 @@ SENSOR_TYPES: tuple[MinutPointSensorEntityDescription, ...] = ( key="temperature", precision=1, device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, ), MinutPointSensorEntityDescription( key="humidity", @@ -53,8 +53,8 @@ SENSOR_TYPES: tuple[MinutPointSensorEntityDescription, ...] = ( MinutPointSensorEntityDescription( key="sound", precision=1, - icon="mdi:ear-hearing", - native_unit_of_measurement=SOUND_PRESSURE_WEIGHTED_DBA, + device_class=SensorDeviceClass.SOUND_PRESSURE, + native_unit_of_measurement=UnitOfSoundPressure.WEIGHTED_DECIBEL_A, ), ) diff --git a/homeassistant/components/point/translations/de.json b/homeassistant/components/point/translations/de.json index ff9a3bf6c7b..f360dd389b4 100644 --- a/homeassistant/components/point/translations/de.json +++ b/homeassistant/components/point/translations/de.json @@ -16,7 +16,7 @@ }, "step": { "auth": { - "description": "Folge dem Link unten und **Best\u00e4tige** den Zugriff auf dein Minut-Konto. Kehre dann zur\u00fcck und dr\u00fccke unten auf **Senden**. \n\n [Link]({authorization_url})", + "description": "Folge dem Link unten und **Best\u00e4tige** den Zugriff auf dein Minut Konto. Kehre dann zur\u00fcck und dr\u00fccke unten auf **Senden**. \n\n [Link]({authorization_url})", "title": "Point authentifizieren" }, "user": { diff --git a/homeassistant/components/point/translations/en_GB.json b/homeassistant/components/point/translations/en-GB.json similarity index 100% rename from homeassistant/components/point/translations/en_GB.json rename to homeassistant/components/point/translations/en-GB.json diff --git a/homeassistant/components/point/translations/en.json b/homeassistant/components/point/translations/en.json index c41dd93683f..39f7a99a7b7 100644 --- a/homeassistant/components/point/translations/en.json +++ b/homeassistant/components/point/translations/en.json @@ -23,7 +23,7 @@ "data": { "flow_impl": "Provider" }, - "description": "Do you want to start set up?", + "description": "Do you want to start setup?", "title": "Pick Authentication Method" } } diff --git a/homeassistant/components/point/translations/it.json b/homeassistant/components/point/translations/it.json index c527666e132..de98409e432 100644 --- a/homeassistant/components/point/translations/it.json +++ b/homeassistant/components/point/translations/it.json @@ -23,7 +23,7 @@ "data": { "flow_impl": "Provider" }, - "description": "Vuoi iniziare la configurazione?", + "description": "Vuoi avviare la configurazione?", "title": "Scegli il metodo di autenticazione" } } diff --git a/homeassistant/components/point/translations/pt.json b/homeassistant/components/point/translations/pt.json index fdb8b0c2c8f..548ecc4533e 100644 --- a/homeassistant/components/point/translations/pt.json +++ b/homeassistant/components/point/translations/pt.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_setup": "J\u00e1 configurado. Apenas uma \u00fanica configura\u00e7\u00e3o \u00e9 poss\u00edvel.", - "authorize_url_timeout": "Tempo excedido a gerar um URL de autoriza\u00e7\u00e3o", + "authorize_url_timeout": "Excedido o tempo limite para gerar um URL de autoriza\u00e7\u00e3o", "external_setup": "Point configurado com \u00eaxito a partir de outro fluxo.", "no_flows": "\u00c9 necess\u00e1rio configurar o Point antes de poder autenticar com ele. [Por favor, leia as instru\u00e7\u00f5es] (https://www.home-assistant.io/components/point/).", "unknown_authorize_url_generation": "Erro desconhecido ao gerar um URL de autoriza\u00e7\u00e3o." diff --git a/homeassistant/components/point/translations/sk.json b/homeassistant/components/point/translations/sk.json index e650ef4e01f..ad45de1a617 100644 --- a/homeassistant/components/point/translations/sk.json +++ b/homeassistant/components/point/translations/sk.json @@ -3,16 +3,26 @@ "abort": { "already_setup": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia.", "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL.", + "external_setup": "Point \u00faspe\u0161ne nastaven\u00e9 in\u00fdm sp\u00f4sobom.", + "no_flows": "Komponent nie je nakonfigurovan\u00fd. Postupujte pod\u013ea dokument\u00e1cie.", "unknown_authorize_url_generation": "Nezn\u00e1ma chyba pri generovan\u00ed autorizovanej adresy URL." }, "create_entry": { "default": "\u00daspe\u0161ne overen\u00e9" }, "error": { + "follow_link": "Pred stla\u010den\u00edm Odosla\u0165 kliknite na odkaz a overte sa", "no_token": "Neplatn\u00fd pr\u00edstupov\u00fd token" }, "step": { + "auth": { + "description": "Kliknite na odkaz ni\u017e\u0161ie a **Prijmite** pr\u00edstup k svojmu \u00fa\u010dtu Minut, potom sa vr\u00e1\u0165te a stla\u010dte **Odosla\u0165** ni\u017e\u0161ie. \n\n[Odkaz]({authorization_url})", + "title": "Overenie Point" + }, "user": { + "data": { + "flow_impl": "Poskytovate\u013e" + }, "description": "Chcete za\u010da\u0165 nastavova\u0165?", "title": "Vyberte met\u00f3du overenia" } diff --git a/homeassistant/components/poolsense/sensor.py b/homeassistant/components/poolsense/sensor.py index 250229867bf..f8f91620321 100644 --- a/homeassistant/components/poolsense/sensor.py +++ b/homeassistant/components/poolsense/sensor.py @@ -9,9 +9,9 @@ from homeassistant.components.sensor import ( from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONF_EMAIL, - ELECTRIC_POTENTIAL_MILLIVOLT, PERCENTAGE, - TEMP_CELSIUS, + UnitOfElectricPotential, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -22,7 +22,7 @@ from .const import DOMAIN SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="Chlorine", - native_unit_of_measurement=ELECTRIC_POTENTIAL_MILLIVOLT, + native_unit_of_measurement=UnitOfElectricPotential.MILLIVOLT, icon="mdi:pool", name="Chlorine", ), @@ -39,7 +39,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( ), SensorEntityDescription( key="Water Temp", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, icon="mdi:coolant-temperature", name="Temperature", device_class=SensorDeviceClass.TEMPERATURE, @@ -52,13 +52,13 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( ), SensorEntityDescription( key="Chlorine High", - native_unit_of_measurement=ELECTRIC_POTENTIAL_MILLIVOLT, + native_unit_of_measurement=UnitOfElectricPotential.MILLIVOLT, icon="mdi:pool", name="Chlorine High", ), SensorEntityDescription( key="Chlorine Low", - native_unit_of_measurement=ELECTRIC_POTENTIAL_MILLIVOLT, + native_unit_of_measurement=UnitOfElectricPotential.MILLIVOLT, icon="mdi:pool", name="Chlorine Low", ), diff --git a/homeassistant/components/poolsense/translations/pt.json b/homeassistant/components/poolsense/translations/pt.json index cc2666db4c5..7678e7b3bac 100644 --- a/homeassistant/components/poolsense/translations/pt.json +++ b/homeassistant/components/poolsense/translations/pt.json @@ -9,7 +9,7 @@ "step": { "user": { "data": { - "email": "Email", + "email": "", "password": "Palavra-passe" } } diff --git a/homeassistant/components/powerwall/models.py b/homeassistant/components/powerwall/models.py index 522dc2806bf..e048cd559ba 100644 --- a/homeassistant/components/powerwall/models.py +++ b/homeassistant/components/powerwall/models.py @@ -44,7 +44,7 @@ class PowerwallData: class PowerwallRuntimeData(TypedDict): """Run time data for the powerwall.""" - coordinator: DataUpdateCoordinator | None + coordinator: DataUpdateCoordinator[PowerwallData] | None base_info: PowerwallBaseInfo api_changed: bool http_session: Session diff --git a/homeassistant/components/powerwall/sensor.py b/homeassistant/components/powerwall/sensor.py index 573dcab6bcc..cf20e51314f 100644 --- a/homeassistant/components/powerwall/sensor.py +++ b/homeassistant/components/powerwall/sensor.py @@ -14,19 +14,19 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - ELECTRIC_CURRENT_AMPERE, - ELECTRIC_POTENTIAL_VOLT, - ENERGY_KILO_WATT_HOUR, - FREQUENCY_HERTZ, PERCENTAGE, - POWER_KILO_WATT, + UnitOfElectricCurrent, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfFrequency, + UnitOfPower, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import DOMAIN, POWERWALL_COORDINATOR from .entity import PowerWallEntity -from .models import PowerwallData, PowerwallRuntimeData +from .models import PowerwallRuntimeData _METER_DIRECTION_EXPORT = "export" _METER_DIRECTION_IMPORT = "import" @@ -72,7 +72,7 @@ POWERWALL_INSTANT_SENSORS = ( name="Now", state_class=SensorStateClass.MEASUREMENT, device_class=SensorDeviceClass.POWER, - native_unit_of_measurement=POWER_KILO_WATT, + native_unit_of_measurement=UnitOfPower.KILO_WATT, value_fn=_get_meter_power, ), PowerwallSensorEntityDescription( @@ -80,7 +80,7 @@ POWERWALL_INSTANT_SENSORS = ( name="Frequency Now", state_class=SensorStateClass.MEASUREMENT, device_class=SensorDeviceClass.FREQUENCY, - native_unit_of_measurement=FREQUENCY_HERTZ, + native_unit_of_measurement=UnitOfFrequency.HERTZ, entity_registry_enabled_default=False, value_fn=_get_meter_frequency, ), @@ -89,7 +89,7 @@ POWERWALL_INSTANT_SENSORS = ( name="Average Current Now", state_class=SensorStateClass.MEASUREMENT, device_class=SensorDeviceClass.CURRENT, - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, entity_registry_enabled_default=False, value_fn=_get_meter_total_current, ), @@ -98,7 +98,7 @@ POWERWALL_INSTANT_SENSORS = ( name="Average Voltage Now", state_class=SensorStateClass.MEASUREMENT, device_class=SensorDeviceClass.VOLTAGE, - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, entity_registry_enabled_default=False, value_fn=_get_meter_average_voltage, ), @@ -114,7 +114,7 @@ async def async_setup_entry( powerwall_data: PowerwallRuntimeData = hass.data[DOMAIN][config_entry.entry_id] coordinator = powerwall_data[POWERWALL_COORDINATOR] assert coordinator is not None - data: PowerwallData = coordinator.data + data = coordinator.data entities: list[PowerWallEntity] = [ PowerWallChargeSensor(powerwall_data), ] @@ -203,7 +203,7 @@ class PowerWallEnergyDirectionSensor(PowerWallEntity, SensorEntity): """Representation of an Powerwall Direction Energy sensor.""" _attr_state_class = SensorStateClass.TOTAL - _attr_native_unit_of_measurement = ENERGY_KILO_WATT_HOUR + _attr_native_unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR _attr_device_class = SensorDeviceClass.ENERGY def __init__( diff --git a/homeassistant/components/powerwall/strings.json b/homeassistant/components/powerwall/strings.json index f4ece53178f..213b7cc03db 100644 --- a/homeassistant/components/powerwall/strings.json +++ b/homeassistant/components/powerwall/strings.json @@ -19,7 +19,7 @@ }, "confirm_discovery": { "title": "[%key:component::powerwall::config::step::user::title%]", - "description": "Do you want to setup {name} ({ip_address})?" + "description": "Do you want to set up {name} ({ip_address})?" } }, "error": { diff --git a/homeassistant/components/powerwall/translations/en.json b/homeassistant/components/powerwall/translations/en.json index 04279759888..c70f18dfbb5 100644 --- a/homeassistant/components/powerwall/translations/en.json +++ b/homeassistant/components/powerwall/translations/en.json @@ -14,7 +14,7 @@ "flow_title": "{name} ({ip_address})", "step": { "confirm_discovery": { - "description": "Do you want to setup {name} ({ip_address})?", + "description": "Do you want to set up {name} ({ip_address})?", "title": "Connect to the powerwall" }, "reauth_confim": { diff --git a/homeassistant/components/powerwall/translations/ko.json b/homeassistant/components/powerwall/translations/ko.json index d1dd99bbb7a..b4b75a30f03 100644 --- a/homeassistant/components/powerwall/translations/ko.json +++ b/homeassistant/components/powerwall/translations/ko.json @@ -2,6 +2,7 @@ "config": { "abort": { "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" }, "error": { @@ -12,6 +13,11 @@ }, "flow_title": "Tesla Powerwall ({ip_address})", "step": { + "reauth_confim": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638" + } + }, "user": { "data": { "ip_address": "IP \uc8fc\uc18c", diff --git a/homeassistant/components/powerwall/translations/no.json b/homeassistant/components/powerwall/translations/no.json index b6d6c051490..79c6dea3240 100644 --- a/homeassistant/components/powerwall/translations/no.json +++ b/homeassistant/components/powerwall/translations/no.json @@ -14,7 +14,7 @@ "flow_title": "{name} ( {ip_address} )", "step": { "confirm_discovery": { - "description": "Vil du konfigurere {name} ( {ip_address} )?", + "description": "Vil du sette opp {name} ( {ip_address} )?", "title": "Koble til powerwall" }, "reauth_confim": { diff --git a/homeassistant/components/powerwall/translations/pt.json b/homeassistant/components/powerwall/translations/pt.json index 1ded97b1b27..b0d06433ec8 100644 --- a/homeassistant/components/powerwall/translations/pt.json +++ b/homeassistant/components/powerwall/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "unknown": "Erro inesperado" }, "step": { diff --git a/homeassistant/components/powerwall/translations/sk.json b/homeassistant/components/powerwall/translations/sk.json index ae43b2fffa0..907227b9151 100644 --- a/homeassistant/components/powerwall/translations/sk.json +++ b/homeassistant/components/powerwall/translations/sk.json @@ -7,23 +7,30 @@ }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba", + "wrong_version": "V\u00e1\u0161 powerwall pou\u017e\u00edva verziu softv\u00e9ru, ktor\u00e1 nie je podporovan\u00e1. Zv\u00e1\u017ete inov\u00e1ciu alebo nahl\u00e1senie tohto probl\u00e9mu, aby sa dal vyrie\u0161i\u0165." }, "flow_title": "{name} ({ip_address})", "step": { "confirm_discovery": { - "description": "Chcete nastavi\u0165 {name} ({ip_address})?" + "description": "Chcete nastavi\u0165 {name} ({ip_address})?", + "title": "Pripojenie k powerwall" }, "reauth_confim": { "data": { "password": "Heslo" - } + }, + "description": "Heslo je zvy\u010dajne posledn\u00fdch 5 znakov s\u00e9riov\u00e9ho \u010d\u00edsla pre Backup Gateway a mo\u017eno ho n\u00e1js\u0165 v aplik\u00e1cii Tesla alebo posledn\u00fdch 5 znakov hesla, ktor\u00e9 n\u00e1jdete vo dver\u00e1ch pre Backup Gateway 2.", + "title": "Znova overte powerwall" }, "user": { "data": { "ip_address": "IP adresa", "password": "Heslo" - } + }, + "description": "Heslo je zvy\u010dajne posledn\u00fdch 5 znakov s\u00e9riov\u00e9ho \u010d\u00edsla pre Backup Gateway a mo\u017eno ho n\u00e1js\u0165 v aplik\u00e1cii Tesla alebo posledn\u00fdch 5 znakov hesla, ktor\u00e9 n\u00e1jdete vo dver\u00e1ch pre Backup Gateway 2.", + "title": "Pripojenie k powerwall" } } } diff --git a/homeassistant/components/profiler/__init__.py b/homeassistant/components/profiler/__init__.py index 69bbd39a77a..9b4164c9f41 100644 --- a/homeassistant/components/profiler/__init__.py +++ b/homeassistant/components/profiler/__init__.py @@ -68,7 +68,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: persistent_notification.async_create( hass, - "Object growth logging has started. See [the logs](/config/logs) to track the growth of new objects.", + ( + "Object growth logging has started. See [the logs](/config/logs) to" + " track the growth of new objects." + ), title="Object growth logging started", notification_id="profile_object_logging", ) @@ -111,7 +114,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: persistent_notification.create( hass, - f"Objects with type {obj_type} have been dumped to the log. See [the logs](/config/logs) to review the repr of the objects.", + ( + f"Objects with type {obj_type} have been dumped to the log. See [the" + " logs](/config/logs) to review the repr of the objects." + ), title="Object dump completed", notification_id="profile_object_dump", ) @@ -231,7 +237,10 @@ async def _async_generate_profile(hass: HomeAssistant, call: ServiceCall): start_time = int(time.time() * 1000000) persistent_notification.async_create( hass, - "The profile has started. This notification will be updated when it is complete.", + ( + "The profile has started. This notification will be updated when it is" + " complete." + ), title="Profile Started", notification_id=f"profiler_{start_time}", ) @@ -247,7 +256,10 @@ async def _async_generate_profile(hass: HomeAssistant, call: ServiceCall): ) persistent_notification.async_create( hass, - f"Wrote cProfile data to {cprofile_path} and callgrind data to {callgrind_path}", + ( + f"Wrote cProfile data to {cprofile_path} and callgrind data to" + f" {callgrind_path}" + ), title="Profile Complete", notification_id=f"profiler_{start_time}", ) @@ -262,7 +274,10 @@ async def _async_generate_memory_profile(hass: HomeAssistant, call: ServiceCall) start_time = int(time.time() * 1000000) persistent_notification.async_create( hass, - "The memory profile has started. This notification will be updated when it is complete.", + ( + "The memory profile has started. This notification will be updated when it" + " is complete." + ), title="Profile Started", notification_id=f"memory_profiler_{start_time}", ) diff --git a/homeassistant/components/profiler/translations/en.json b/homeassistant/components/profiler/translations/en.json index 285e83585cb..379b9d3c1b9 100644 --- a/homeassistant/components/profiler/translations/en.json +++ b/homeassistant/components/profiler/translations/en.json @@ -5,7 +5,7 @@ }, "step": { "user": { - "description": "Do you want to start set up?" + "description": "Do you want to start setup?" } } } diff --git a/homeassistant/components/profiler/translations/it.json b/homeassistant/components/profiler/translations/it.json index 6e2771360db..779ffba471a 100644 --- a/homeassistant/components/profiler/translations/it.json +++ b/homeassistant/components/profiler/translations/it.json @@ -5,7 +5,7 @@ }, "step": { "user": { - "description": "Vuoi iniziare la configurazione?" + "description": "Vuoi avviare la configurazione?" } } } diff --git a/homeassistant/components/profiler/translations/pt.json b/homeassistant/components/profiler/translations/pt.json index c299020ce9a..9094c60428c 100644 --- a/homeassistant/components/profiler/translations/pt.json +++ b/homeassistant/components/profiler/translations/pt.json @@ -5,7 +5,7 @@ }, "step": { "user": { - "description": "Quer dar inicio \u00e0 configura\u00e7\u00e3o?" + "description": "Quer dar in\u00edcio \u00e0 configura\u00e7\u00e3o?" } } } diff --git a/homeassistant/components/progettihwsw/translations/pt.json b/homeassistant/components/progettihwsw/translations/pt.json index 072d1ea0565..49364e94c84 100644 --- a/homeassistant/components/progettihwsw/translations/pt.json +++ b/homeassistant/components/progettihwsw/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "unknown": "Erro inesperado" }, "step": { @@ -31,7 +31,7 @@ }, "user": { "data": { - "host": "Servidor", + "host": "Endere\u00e7o", "port": "Porta" }, "title": "Configurar placa" diff --git a/homeassistant/components/progettihwsw/translations/sk.json b/homeassistant/components/progettihwsw/translations/sk.json index 1ad2241b9e5..3fe6aa25d53 100644 --- a/homeassistant/components/progettihwsw/translations/sk.json +++ b/homeassistant/components/progettihwsw/translations/sk.json @@ -26,13 +26,15 @@ "relay_7": "Rel\u00e9 7", "relay_8": "Rel\u00e9 8", "relay_9": "Rel\u00e9 9" - } + }, + "title": "Nastavenie rel\u00e9" }, "user": { "data": { "host": "Hostite\u013e", "port": "Port" - } + }, + "title": "Nastavenie panela" } } } diff --git a/homeassistant/components/proliphix/climate.py b/homeassistant/components/proliphix/climate.py index 4ff1ff0906c..5f841441d59 100644 --- a/homeassistant/components/proliphix/climate.py +++ b/homeassistant/components/proliphix/climate.py @@ -19,7 +19,7 @@ from homeassistant.const import ( CONF_PASSWORD, CONF_USERNAME, PRECISION_TENTHS, - TEMP_FAHRENHEIT, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv @@ -59,7 +59,7 @@ class ProliphixThermostat(ClimateEntity): _attr_precision = PRECISION_TENTHS _attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE - _attr_temperature_unit = TEMP_FAHRENHEIT + _attr_temperature_unit = UnitOfTemperature.FAHRENHEIT def __init__(self, pdp): """Initialize the thermostat.""" diff --git a/homeassistant/components/prometheus/__init__.py b/homeassistant/components/prometheus/__init__.py index 954dcfd1eb1..71d668d93dd 100644 --- a/homeassistant/components/prometheus/__init__.py +++ b/homeassistant/components/prometheus/__init__.py @@ -16,6 +16,7 @@ from homeassistant.components.climate import ( ATTR_TARGET_TEMP_LOW, HVACAction, ) +from homeassistant.components.cover import ATTR_POSITION, ATTR_TILT_POSITION from homeassistant.components.http import HomeAssistantView from homeassistant.components.humidifier import ATTR_AVAILABLE_MODES, ATTR_HUMIDITY from homeassistant.const import ( @@ -28,11 +29,14 @@ from homeassistant.const import ( CONTENT_TYPE_TEXT_PLAIN, EVENT_STATE_CHANGED, PERCENTAGE, + STATE_CLOSED, + STATE_CLOSING, STATE_ON, + STATE_OPEN, + STATE_OPENING, STATE_UNAVAILABLE, STATE_UNKNOWN, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers import entityfilter, state as state_helper @@ -347,9 +351,12 @@ class PrometheusMetrics: with suppress(ValueError): value = self.state_as_number(state) - if state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == TEMP_FAHRENHEIT: + if ( + state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) + == UnitOfTemperature.FAHRENHEIT + ): value = TemperatureConverter.convert( - value, TEMP_FAHRENHEIT, TEMP_CELSIUS + value, UnitOfTemperature.FAHRENHEIT, UnitOfTemperature.CELSIUS ) metric.labels(**self._labels(state)).set(value) @@ -369,6 +376,38 @@ class PrometheusMetrics: value = self.state_as_number(state) metric.labels(**self._labels(state)).set(value) + def _handle_cover(self, state): + metric = self._metric( + "cover_state", + self.prometheus_cli.Gauge, + "State of the cover (0/1)", + ["state"], + ) + + cover_states = [STATE_CLOSED, STATE_CLOSING, STATE_OPEN, STATE_OPENING] + for cover_state in cover_states: + metric.labels(**dict(self._labels(state), state=cover_state)).set( + float(cover_state == state.state) + ) + + position = state.attributes.get(ATTR_POSITION) + if position is not None: + position_metric = self._metric( + "cover_position", + self.prometheus_cli.Gauge, + "Position of the cover (0-100)", + ) + position_metric.labels(**self._labels(state)).set(float(position)) + + tilt_position = state.attributes.get(ATTR_TILT_POSITION) + if tilt_position is not None: + tilt_position_metric = self._metric( + "cover_tilt_position", + self.prometheus_cli.Gauge, + "Tilt Position of the cover (0-100)", + ) + tilt_position_metric.labels(**self._labels(state)).set(float(tilt_position)) + def _handle_light(self, state): metric = self._metric( "light_brightness_percent", @@ -395,8 +434,10 @@ class PrometheusMetrics: def _handle_climate_temp(self, state, attr, metric_name, metric_description): if (temp := state.attributes.get(attr)) is not None: - if self._climate_units == TEMP_FAHRENHEIT: - temp = TemperatureConverter.convert(temp, TEMP_FAHRENHEIT, TEMP_CELSIUS) + if self._climate_units == UnitOfTemperature.FAHRENHEIT: + temp = TemperatureConverter.convert( + temp, UnitOfTemperature.FAHRENHEIT, UnitOfTemperature.CELSIUS + ) metric = self._metric( metric_name, self.prometheus_cli.Gauge, @@ -508,9 +549,12 @@ class PrometheusMetrics: try: value = self.state_as_number(state) - if state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == TEMP_FAHRENHEIT: + if ( + state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) + == UnitOfTemperature.FAHRENHEIT + ): value = TemperatureConverter.convert( - value, TEMP_FAHRENHEIT, TEMP_CELSIUS + value, UnitOfTemperature.FAHRENHEIT, UnitOfTemperature.CELSIUS ) _metric.labels(**self._labels(state)).set(value) except ValueError: @@ -559,8 +603,8 @@ class PrometheusMetrics: return units = { - TEMP_CELSIUS: "celsius", - TEMP_FAHRENHEIT: "celsius", # F should go into C metric + UnitOfTemperature.CELSIUS: "celsius", + UnitOfTemperature.FAHRENHEIT: "celsius", # F should go into C metric PERCENTAGE: "percent", } default = unit.replace("/", "_per_") diff --git a/homeassistant/components/prosegur/translations/de.json b/homeassistant/components/prosegur/translations/de.json index aa5667d8d54..61f9c54e984 100644 --- a/homeassistant/components/prosegur/translations/de.json +++ b/homeassistant/components/prosegur/translations/de.json @@ -12,7 +12,7 @@ "step": { "reauth_confirm": { "data": { - "description": "Authentifiziere dich erneut mit deinem Prosegur-Konto.", + "description": "Authentifiziere dich erneut mit deinem Prosegur Konto.", "password": "Passwort", "username": "Benutzername" } diff --git a/homeassistant/components/prosegur/translations/ko.json b/homeassistant/components/prosegur/translations/ko.json new file mode 100644 index 00000000000..48f313cd580 --- /dev/null +++ b/homeassistant/components/prosegur/translations/ko.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + } + }, + "user": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/prosegur/translations/pt.json b/homeassistant/components/prosegur/translations/pt.json index a4b08e68352..df85597e8f2 100644 --- a/homeassistant/components/prosegur/translations/pt.json +++ b/homeassistant/components/prosegur/translations/pt.json @@ -5,7 +5,7 @@ "reauth_successful": "Reautentica\u00e7\u00e3o bem sucedida" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "reauth_confirm": { diff --git a/homeassistant/components/prosegur/translations/sk.json b/homeassistant/components/prosegur/translations/sk.json index 7744a00340e..49d5ae21d65 100644 --- a/homeassistant/components/prosegur/translations/sk.json +++ b/homeassistant/components/prosegur/translations/sk.json @@ -12,6 +12,7 @@ "step": { "reauth_confirm": { "data": { + "description": "Znova sa overte pomocou \u00fa\u010dtu Prosegur.", "password": "Heslo", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } diff --git a/homeassistant/components/proximity/__init__.py b/homeassistant/components/proximity/__init__.py index cf99f4fa503..edd50146559 100644 --- a/homeassistant/components/proximity/__init__.py +++ b/homeassistant/components/proximity/__init__.py @@ -11,11 +11,7 @@ from homeassistant.const import ( CONF_DEVICES, CONF_UNIT_OF_MEASUREMENT, CONF_ZONE, - LENGTH_FEET, - LENGTH_KILOMETERS, - LENGTH_METERS, - LENGTH_MILES, - LENGTH_YARD, + UnitOfLength, ) from homeassistant.core import HomeAssistant, State import homeassistant.helpers.config_validation as cv @@ -42,11 +38,11 @@ DEFAULT_TOLERANCE = 1 DOMAIN = "proximity" UNITS = [ - LENGTH_METERS, - LENGTH_KILOMETERS, - LENGTH_FEET, - LENGTH_YARD, - LENGTH_MILES, + UnitOfLength.METERS, + UnitOfLength.KILOMETERS, + UnitOfLength.FEET, + UnitOfLength.YARDS, + UnitOfLength.MILES, ] ZONE_SCHEMA = vol.Schema( @@ -236,7 +232,7 @@ class Proximity(Entity): continue distances_to_zone[device] = round( DistanceConverter.convert( - proximity, LENGTH_METERS, self.unit_of_measurement + proximity, UnitOfLength.METERS, self.unit_of_measurement ), 1, ) diff --git a/homeassistant/components/proxmoxve/__init__.py b/homeassistant/components/proxmoxve/__init__.py index 1334a66cfa0..4ecc2585bce 100644 --- a/homeassistant/components/proxmoxve/__init__.py +++ b/homeassistant/components/proxmoxve/__init__.py @@ -120,8 +120,10 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: continue except SSLError: _LOGGER.error( - "Unable to verify proxmox server SSL. " - 'Try using "verify_ssl: false" for proxmox instance %s:%d', + ( + "Unable to verify proxmox server SSL. " + 'Try using "verify_ssl: false" for proxmox instance %s:%d' + ), host, port, ) diff --git a/homeassistant/components/proxy/camera.py b/homeassistant/components/proxy/camera.py index bc33a1f7ffc..79e9faf8d70 100644 --- a/homeassistant/components/proxy/camera.py +++ b/homeassistant/components/proxy/camera.py @@ -118,8 +118,10 @@ def _resize_image(image, opts): newimage = imgbuf.getvalue() if not opts.force_resize and len(newimage) >= old_size: _LOGGER.debug( - "Using original image (%d bytes) " - "because resized image (%d bytes) is not smaller", + ( + "Using original image (%d bytes) " + "because resized image (%d bytes) is not smaller" + ), old_size, len(newimage), ) diff --git a/homeassistant/components/prusalink/__init__.py b/homeassistant/components/prusalink/__init__.py index 2b77a6fc524..0172a237da8 100644 --- a/homeassistant/components/prusalink/__init__.py +++ b/homeassistant/components/prusalink/__init__.py @@ -1,7 +1,7 @@ """The PrusaLink integration.""" from __future__ import annotations -from abc import abstractmethod +from abc import ABC, abstractmethod from datetime import timedelta import logging from time import monotonic @@ -60,7 +60,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: T = TypeVar("T", PrinterInfo, JobInfo) -class PrusaLinkUpdateCoordinator(DataUpdateCoordinator, Generic[T]): +class PrusaLinkUpdateCoordinator(DataUpdateCoordinator, Generic[T], ABC): """Update coordinator for the printer.""" config_entry: ConfigEntry diff --git a/homeassistant/components/prusalink/sensor.py b/homeassistant/components/prusalink/sensor.py index f2a4f2fec81..a8cdf0625c9 100644 --- a/homeassistant/components/prusalink/sensor.py +++ b/homeassistant/components/prusalink/sensor.py @@ -15,7 +15,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import PERCENTAGE, TEMP_CELSIUS +from homeassistant.const import PERCENTAGE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import StateType @@ -59,12 +59,14 @@ SENSORS: dict[str, tuple[PrusaLinkSensorEntityDescription, ...]] = { if flags["printing"] else "idle" ), - device_class="prusalink__printer_state", + device_class=SensorDeviceClass.ENUM, + options=["cancelling", "idle", "paused", "pausing", "printing"], + translation_key="printer_state", ), PrusaLinkSensorEntityDescription[PrinterInfo]( key="printer.telemetry.temp-bed", name="Heatbed", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, value_fn=lambda data: cast(float, data["telemetry"]["temp-bed"]), @@ -73,7 +75,7 @@ SENSORS: dict[str, tuple[PrusaLinkSensorEntityDescription, ...]] = { PrusaLinkSensorEntityDescription[PrinterInfo]( key="printer.telemetry.temp-nozzle", name="Nozzle Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, value_fn=lambda data: cast(float, data["telemetry"]["temp-nozzle"]), diff --git a/homeassistant/components/prusalink/strings.json b/homeassistant/components/prusalink/strings.json index 24835324e18..9d6a1a2ed53 100644 --- a/homeassistant/components/prusalink/strings.json +++ b/homeassistant/components/prusalink/strings.json @@ -14,5 +14,18 @@ "unknown": "[%key:common::config_flow::error::unknown%]", "not_supported": "Only PrusaLink API v2 is supported" } + }, + "entity": { + "sensor": { + "printer_state": { + "state": { + "cancelling": "Cancelling", + "idle": "Idle", + "paused": "Paused", + "pausing": "Pausing", + "printing": "Printing" + } + } + } } } diff --git a/homeassistant/components/prusalink/strings.sensor.json b/homeassistant/components/prusalink/strings.sensor.json deleted file mode 100644 index 6e1fe62e7f5..00000000000 --- a/homeassistant/components/prusalink/strings.sensor.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "state": { - "prusalink__printer_state": { - "pausing": "Pausing", - "cancelling": "Cancelling", - "paused": "Paused", - "printing": "Printing", - "idle": "Idle" - } - } -} diff --git a/homeassistant/components/prusalink/translations/ca.json b/homeassistant/components/prusalink/translations/ca.json index aaabfa27677..cc7fe8377bc 100644 --- a/homeassistant/components/prusalink/translations/ca.json +++ b/homeassistant/components/prusalink/translations/ca.json @@ -14,5 +14,18 @@ } } } + }, + "entity": { + "sensor": { + "printer_state": { + "state": { + "cancelling": "S'est\u00e0 cancel\u00b7lant", + "idle": "Inactiva", + "paused": "Pausada", + "pausing": "Aturant", + "printing": "Imprimint" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/prusalink/translations/de.json b/homeassistant/components/prusalink/translations/de.json index bf30cbc3f28..28e58e97d1c 100644 --- a/homeassistant/components/prusalink/translations/de.json +++ b/homeassistant/components/prusalink/translations/de.json @@ -14,5 +14,18 @@ } } } + }, + "entity": { + "sensor": { + "printer_state": { + "state": { + "cancelling": "Abbrechen", + "idle": "Inaktiv", + "paused": "Angehalten", + "pausing": "Anhalten", + "printing": "Druckt" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/prusalink/translations/el.json b/homeassistant/components/prusalink/translations/el.json index 8116be1ad89..6459709e6d3 100644 --- a/homeassistant/components/prusalink/translations/el.json +++ b/homeassistant/components/prusalink/translations/el.json @@ -14,5 +14,18 @@ } } } + }, + "entity": { + "sensor": { + "printer_state": { + "state": { + "cancelling": "\u0391\u03ba\u03cd\u03c1\u03c9\u03c3\u03b7", + "idle": "\u03a3\u03b5 \u03b1\u03b4\u03c1\u03ac\u03bd\u03b5\u03b9\u03b1", + "paused": "\u03a3\u03b5 \u03c0\u03b1\u03cd\u03c3\u03b7", + "pausing": "\u03a0\u03b1\u03cd\u03c3\u03b7", + "printing": "\u0395\u03ba\u03c4\u03cd\u03c0\u03c9\u03c3\u03b7" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/prusalink/translations/en.json b/homeassistant/components/prusalink/translations/en.json index ad52d4082de..6edf12e1001 100644 --- a/homeassistant/components/prusalink/translations/en.json +++ b/homeassistant/components/prusalink/translations/en.json @@ -14,5 +14,18 @@ } } } + }, + "entity": { + "sensor": { + "printer_state": { + "state": { + "cancelling": "Cancelling", + "idle": "Idle", + "paused": "Paused", + "pausing": "Pausing", + "printing": "Printing" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/prusalink/translations/es.json b/homeassistant/components/prusalink/translations/es.json index 094840cf785..059fb382733 100644 --- a/homeassistant/components/prusalink/translations/es.json +++ b/homeassistant/components/prusalink/translations/es.json @@ -14,5 +14,18 @@ } } } + }, + "entity": { + "sensor": { + "printer_state": { + "state": { + "cancelling": "Cancelando", + "idle": "Inactivo", + "paused": "En pausa", + "pausing": "Pausando", + "printing": "Imprimiendo" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/prusalink/translations/et.json b/homeassistant/components/prusalink/translations/et.json index 250183a30cf..9f89ee994a8 100644 --- a/homeassistant/components/prusalink/translations/et.json +++ b/homeassistant/components/prusalink/translations/et.json @@ -14,5 +14,18 @@ } } } + }, + "entity": { + "sensor": { + "printer_state": { + "state": { + "cancelling": "Loobumine", + "idle": "Ootel", + "paused": "Peatatud", + "pausing": "Pausi tegemine", + "printing": "Tr\u00fckkimine" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/prusalink/translations/fr.json b/homeassistant/components/prusalink/translations/fr.json index 372360dbde8..16c351f61e6 100644 --- a/homeassistant/components/prusalink/translations/fr.json +++ b/homeassistant/components/prusalink/translations/fr.json @@ -14,5 +14,18 @@ } } } + }, + "entity": { + "sensor": { + "printer_state": { + "state": { + "cancelling": "Annulation", + "idle": "Inactive", + "paused": "En pause", + "pausing": "Mise en pause", + "printing": "Impression" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/prusalink/translations/hu.json b/homeassistant/components/prusalink/translations/hu.json index 7fe5692714a..1be1d4ff3f2 100644 --- a/homeassistant/components/prusalink/translations/hu.json +++ b/homeassistant/components/prusalink/translations/hu.json @@ -14,5 +14,18 @@ } } } + }, + "entity": { + "sensor": { + "printer_state": { + "state": { + "cancelling": "\u00c9rv\u00e9nytelen\u00edt\u00e9sben", + "idle": "T\u00e9tlen", + "paused": "Sz\u00fcneteltetve", + "pausing": "Sz\u00fcneteltet\u00e9sben", + "printing": "Nyomtat\u00e1sban" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/prusalink/translations/id.json b/homeassistant/components/prusalink/translations/id.json index 3582565c801..011f82623dc 100644 --- a/homeassistant/components/prusalink/translations/id.json +++ b/homeassistant/components/prusalink/translations/id.json @@ -14,5 +14,18 @@ } } } + }, + "entity": { + "sensor": { + "printer_state": { + "state": { + "cancelling": "Membatalkan", + "idle": "Siaga", + "paused": "Dijeda", + "pausing": "Jeda", + "printing": "Mencetak" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/prusalink/translations/it.json b/homeassistant/components/prusalink/translations/it.json index 02b2b8a53aa..229684403e2 100644 --- a/homeassistant/components/prusalink/translations/it.json +++ b/homeassistant/components/prusalink/translations/it.json @@ -14,5 +14,18 @@ } } } + }, + "entity": { + "sensor": { + "printer_state": { + "state": { + "cancelling": "Annullamento", + "idle": "Inattivo", + "paused": "In pausa", + "pausing": "Pausa", + "printing": "Stampa" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/prusalink/translations/ko.json b/homeassistant/components/prusalink/translations/ko.json new file mode 100644 index 00000000000..537ebf68669 --- /dev/null +++ b/homeassistant/components/prusalink/translations/ko.json @@ -0,0 +1,17 @@ +{ + "config": { + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "api_key": "API \ud0a4", + "host": "\ud638\uc2a4\ud2b8" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/prusalink/translations/no.json b/homeassistant/components/prusalink/translations/no.json index 4a81bfb490f..6ec4129570a 100644 --- a/homeassistant/components/prusalink/translations/no.json +++ b/homeassistant/components/prusalink/translations/no.json @@ -14,5 +14,18 @@ } } } + }, + "entity": { + "sensor": { + "printer_state": { + "state": { + "cancelling": "Avbryter", + "idle": "Inaktiv", + "paused": "Pauset", + "pausing": "Setter p\u00e5 pause", + "printing": "Printing" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/prusalink/translations/pl.json b/homeassistant/components/prusalink/translations/pl.json index 2cbbaeec07d..8de57505d5c 100644 --- a/homeassistant/components/prusalink/translations/pl.json +++ b/homeassistant/components/prusalink/translations/pl.json @@ -14,5 +14,18 @@ } } } + }, + "entity": { + "sensor": { + "printer_state": { + "state": { + "cancelling": "anulowanie", + "idle": "bezczynna", + "paused": "wstrzymana", + "pausing": "wstrzymywanie", + "printing": "drukowanie" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/prusalink/translations/pt-BR.json b/homeassistant/components/prusalink/translations/pt-BR.json index 2959fa6fe99..e6c3e233928 100644 --- a/homeassistant/components/prusalink/translations/pt-BR.json +++ b/homeassistant/components/prusalink/translations/pt-BR.json @@ -14,5 +14,18 @@ } } } + }, + "entity": { + "sensor": { + "printer_state": { + "state": { + "cancelling": "Cancelando", + "idle": "Ocioso", + "paused": "Pausado", + "pausing": "Pausando", + "printing": "Impress\u00e3o" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/prusalink/translations/pt.json b/homeassistant/components/prusalink/translations/pt.json index 5003d44e3d9..9a8de2cfa41 100644 --- a/homeassistant/components/prusalink/translations/pt.json +++ b/homeassistant/components/prusalink/translations/pt.json @@ -1,7 +1,18 @@ { "config": { "error": { - "not_supported": "Apenas a API PrusaLink v2 \u00e9 suportada" + "cannot_connect": "A liga\u00e7\u00e3o falhou", + "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", + "not_supported": "Apenas a API PrusaLink v2 \u00e9 suportada", + "unknown": "Erro inesperado" + }, + "step": { + "user": { + "data": { + "api_key": "Chave da API", + "host": "Endere\u00e7o" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/prusalink/translations/ru.json b/homeassistant/components/prusalink/translations/ru.json index 7bde8101310..916fe953933 100644 --- a/homeassistant/components/prusalink/translations/ru.json +++ b/homeassistant/components/prusalink/translations/ru.json @@ -14,5 +14,18 @@ } } } + }, + "entity": { + "sensor": { + "printer_state": { + "state": { + "cancelling": "\u041e\u0442\u043c\u0435\u043d\u0430", + "idle": "\u0411\u0435\u0437\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435", + "paused": "\u041f\u0440\u0438\u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d", + "pausing": "\u041f\u0440\u0438\u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430", + "printing": "\u041f\u0435\u0447\u0430\u0442\u044c" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/prusalink/translations/sensor.sk.json b/homeassistant/components/prusalink/translations/sensor.sk.json index d35150afe2e..de0749c24ad 100644 --- a/homeassistant/components/prusalink/translations/sensor.sk.json +++ b/homeassistant/components/prusalink/translations/sensor.sk.json @@ -2,6 +2,9 @@ "state": { "prusalink__printer_state": { "cancelling": "Prebieha ru\u0161enie", + "idle": "Ne\u010dinn\u00fd", + "paused": "Pozastaven\u00e9", + "pausing": "Pozastavenie", "printing": "Tla\u010d" } } diff --git a/homeassistant/components/prusalink/translations/sk.json b/homeassistant/components/prusalink/translations/sk.json index 1ae350ddfd3..95bc9c1ad55 100644 --- a/homeassistant/components/prusalink/translations/sk.json +++ b/homeassistant/components/prusalink/translations/sk.json @@ -14,5 +14,18 @@ } } } + }, + "entity": { + "sensor": { + "printer_state": { + "state": { + "cancelling": "Prebieha ru\u0161enie", + "idle": "Ne\u010dinn\u00fd", + "paused": "Pozastaven\u00fd", + "pausing": "Pozastavenie", + "printing": "Tla\u010d" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/prusalink/translations/zh-Hant.json b/homeassistant/components/prusalink/translations/zh-Hant.json index 43d34d20540..50128c9417b 100644 --- a/homeassistant/components/prusalink/translations/zh-Hant.json +++ b/homeassistant/components/prusalink/translations/zh-Hant.json @@ -14,5 +14,18 @@ } } } + }, + "entity": { + "sensor": { + "printer_state": { + "state": { + "cancelling": "\u53d6\u6d88\u4e2d", + "idle": "\u9592\u7f6e", + "paused": "\u5df2\u66ab\u505c", + "pausing": "\u66ab\u505c\u4e2d", + "printing": "\u5217\u5370\u4e2d" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/ps4/__init__.py b/homeassistant/components/ps4/__init__.py index 9a5744de2fd..3ab1136c7d7 100644 --- a/homeassistant/components/ps4/__init__.py +++ b/homeassistant/components/ps4/__init__.py @@ -108,8 +108,7 @@ async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: version = entry.version = 2 config_entries.async_update_entry(entry, data=data) _LOGGER.info( - "PlayStation 4 Config Updated: \ - Region changed to: %s", + "PlayStation 4 Config Updated: Region changed to: %s", country, ) @@ -140,8 +139,7 @@ async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: ) entry.version = 3 _LOGGER.info( - "PlayStation 4 identifier for entity: %s \ - has changed", + "PlayStation 4 identifier for entity: %s has changed", entity_id, ) config_entries.async_update_entry(entry) diff --git a/homeassistant/components/ps4/translations/de.json b/homeassistant/components/ps4/translations/de.json index cb3b7c328bb..08855ee8dcc 100644 --- a/homeassistant/components/ps4/translations/de.json +++ b/homeassistant/components/ps4/translations/de.json @@ -5,7 +5,7 @@ "credential_error": "Fehler beim Abrufen der Anmeldeinformationen.", "no_devices_found": "Keine Ger\u00e4te im Netzwerk gefunden", "port_987_bind_error": "Konnte sich nicht an Port 987 binden. Weitere Informationen findest du in der [Dokumentation] (https://www.home-assistant.io/components/ps4/).", - "port_997_bind_error": "Bind to Port 997 nicht m\u00f6glich. Weitere Informationen findest du in der [Dokumentation](https://www.home-assistant.io/components/ps4/)" + "port_997_bind_error": "Bind auf Port 997 nicht m\u00f6glich. Weitere Informationen findest du in der [Dokumentation](https://www.home-assistant.io/components/ps4/)" }, "error": { "cannot_connect": "Verbindung fehlgeschlagen", @@ -15,7 +15,7 @@ }, "step": { "creds": { - "description": "Anmeldeinformationen ben\u00f6tigt. Dr\u00fccke auf \"Senden\" und dann in der PS4 2nd Screen App, aktualisiere die Ger\u00e4te und w\u00e4hle das \"Home-Assistant\"-Ger\u00e4t aus, um fortzufahren." + "description": "Anmeldeinformationen ben\u00f6tigt. Dr\u00fccke auf \"Senden\" und dann in der PS4 2nd Screen App, aktualisiere die Ger\u00e4te und w\u00e4hle das \"Home-Assistant\" Ger\u00e4t aus, um fortzufahren." }, "link": { "data": { diff --git a/homeassistant/components/ps4/translations/lb.json b/homeassistant/components/ps4/translations/lb.json index b093b2bdd8e..ebbb0087d22 100644 --- a/homeassistant/components/ps4/translations/lb.json +++ b/homeassistant/components/ps4/translations/lb.json @@ -10,7 +10,7 @@ "error": { "cannot_connect": "Feeler beim verbannen", "credential_timeout": "Z\u00e4it Iwwerschreidung beim Service vun den Umeldungsinformatiounen. Dr\u00e9ck op ofsch\u00e9cke fir nach emol ze starten.", - "login_failed": "Feeler beim verbanne mat der Playstation 4. Iwwerpr\u00e9if op de PIN Code korrekt ass.", + "login_failed": "Feeler beim verbanne mat der Playstation 4. Iwwerpr\u00e9if op de PIN-Code korrekt ass.", "no_ipaddress": "G\u00ebff d'IP Adresse vun der Playstation 4 d\u00e9i soll konfigur\u00e9iert ginn an:" }, "step": { @@ -19,7 +19,7 @@ }, "link": { "data": { - "code": "PIN Code", + "code": "PIN-Code", "ip_address": "IP Adresse", "name": "Numm", "region": "Regioun" diff --git a/homeassistant/components/ps4/translations/pt.json b/homeassistant/components/ps4/translations/pt.json index ac585a23994..762285e4815 100644 --- a/homeassistant/components/ps4/translations/pt.json +++ b/homeassistant/components/ps4/translations/pt.json @@ -8,7 +8,7 @@ "port_997_bind_error": "N\u00e3o foi poss\u00edvel ligar-se \u00e0 porta 997. Consulte a [documenta\u00e7\u00e3o](https://www.home-assistant.io/components/ps4/) para obter mais informa\u00e7\u00f5es." }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "credential_timeout": "O servi\u00e7o de credencial expirou. Pressione enviar para reiniciar.", "login_failed": "Falha ao emparelhar com a PlayStation 4. Verifique se o PIN est\u00e1 correto." }, diff --git a/homeassistant/components/ps4/translations/sk.json b/homeassistant/components/ps4/translations/sk.json index bb9cdddbaac..0fab44cdf25 100644 --- a/homeassistant/components/ps4/translations/sk.json +++ b/homeassistant/components/ps4/translations/sk.json @@ -2,6 +2,7 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "credential_error": "Chyba pri na\u010d\u00edtavan\u00ed poveren\u00ed.", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", "port_987_bind_error": "Nepodarilo sa naviaza\u0165 na port 987. \u010eal\u0161ie inform\u00e1cie n\u00e1jdete v [dokument\u00e1cii](https://www.home-assistant.io/components/ps4/).", "port_997_bind_error": "Nepodarilo sa naviaza\u0165 na port 997. \u010eal\u0161ie inform\u00e1cie n\u00e1jdete v [dokument\u00e1cii](https://www.home-assistant.io/components/ps4/)." @@ -13,16 +14,24 @@ "no_ipaddress": "Zadajte IP adresa konzoly PlayStation 4, ktor\u00fa chcete nakonfigurova\u0165." }, "step": { + "creds": { + "description": "Potrebn\u00e9 poverenia. Stla\u010dte 'Odosla\u0165' a potom v aplik\u00e1cii PS4 2nd Screen App obnovte zariadenia a pokra\u010dujte v\u00fdberom zariadenia 'Home-Assistant'." + }, "link": { "data": { "code": "PIN k\u00f3d", "ip_address": "IP adresa", - "name": "N\u00e1zov" + "name": "N\u00e1zov", + "region": "Regi\u00f3n" + }, + "data_description": { + "code": "Na konzole PlayStation 4 prejdite do \u010dasti Nastavenia. Potom prejdite na \u201eNastavenia pripojenia k mobilnej aplik\u00e1cii\u201c a vyberte \u201ePrida\u0165 zariadenie\u201c, aby ste z\u00edskali PIN." } }, "mode": { "data": { - "ip_address": "IP adresa (Ak pou\u017e\u00edvate automatick\u00e9 zis\u0165ovanie, nechajte pr\u00e1zdne)." + "ip_address": "IP adresa (Ak pou\u017e\u00edvate automatick\u00e9 zis\u0165ovanie, nechajte pr\u00e1zdne).", + "mode": "Konfigura\u010dn\u00fd re\u017eim" }, "data_description": { "ip_address": "Ak vyberiete automatick\u00e9 zis\u0165ovanie, ponechajte pr\u00e1zdne." diff --git a/homeassistant/components/pure_energie/sensor.py b/homeassistant/components/pure_energie/sensor.py index 8b07a2818f8..7d584c7c1a8 100644 --- a/homeassistant/components/pure_energie/sensor.py +++ b/homeassistant/components/pure_energie/sensor.py @@ -12,7 +12,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_HOST, ENERGY_KILO_WATT_HOUR, POWER_WATT +from homeassistant.const import CONF_HOST, UnitOfEnergy, UnitOfPower from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -40,7 +40,7 @@ SENSORS: tuple[PureEnergieSensorEntityDescription, ...] = ( PureEnergieSensorEntityDescription( key="power_flow", name="Power Flow", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, value_fn=lambda data: data.smartbridge.power_flow, @@ -48,7 +48,7 @@ SENSORS: tuple[PureEnergieSensorEntityDescription, ...] = ( PureEnergieSensorEntityDescription( key="energy_consumption_total", name="Energy Consumption", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, value_fn=lambda data: data.smartbridge.energy_consumption_total, @@ -56,7 +56,7 @@ SENSORS: tuple[PureEnergieSensorEntityDescription, ...] = ( PureEnergieSensorEntityDescription( key="energy_production_total", name="Energy Production", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, value_fn=lambda data: data.smartbridge.energy_production_total, diff --git a/homeassistant/components/pure_energie/translations/ko.json b/homeassistant/components/pure_energie/translations/ko.json index b4373b03a25..7a58b0ec1d3 100644 --- a/homeassistant/components/pure_energie/translations/ko.json +++ b/homeassistant/components/pure_energie/translations/ko.json @@ -1,6 +1,18 @@ { "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" + }, "step": { + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8" + } + }, "zeroconf_confirm": { "title": "Pure Energie Meter \uae30\uae30 \ubc1c\uacac" } diff --git a/homeassistant/components/pure_energie/translations/pt.json b/homeassistant/components/pure_energie/translations/pt.json index aad646d8e8c..5e5a5aa6127 100644 --- a/homeassistant/components/pure_energie/translations/pt.json +++ b/homeassistant/components/pure_energie/translations/pt.json @@ -3,7 +3,7 @@ "step": { "user": { "data": { - "host": "Servidor" + "host": "Endere\u00e7o" }, "data_description": { "host": "O endere\u00e7o IP ou nome de host do seu Medidor Pure Energie." diff --git a/homeassistant/components/pure_energie/translations/sk.json b/homeassistant/components/pure_energie/translations/sk.json index 42e2c99e2f3..785419c6561 100644 --- a/homeassistant/components/pure_energie/translations/sk.json +++ b/homeassistant/components/pure_energie/translations/sk.json @@ -16,6 +16,10 @@ "data_description": { "host": "IP adresa alebo n\u00e1zov hostite\u013ea v\u00e1\u0161ho mera\u010da energie Pure." } + }, + "zeroconf_confirm": { + "description": "Chcete prida\u0165 Pure Energie Meter (`{model}`) do Home Assistant?", + "title": "Objaven\u00e9 zariadenie Pure Energie Meter" } } } diff --git a/homeassistant/components/purpleair/__init__.py b/homeassistant/components/purpleair/__init__.py new file mode 100644 index 00000000000..c90f4c9031c --- /dev/null +++ b/homeassistant/components/purpleair/__init__.py @@ -0,0 +1,77 @@ +"""The PurpleAir integration.""" +from __future__ import annotations + +from aiopurpleair.models.sensors import SensorModel + +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import ATTR_LATITUDE, ATTR_LONGITUDE, Platform +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import DeviceInfo +from homeassistant.helpers.update_coordinator import CoordinatorEntity + +from .const import DOMAIN +from .coordinator import PurpleAirDataUpdateCoordinator + +PLATFORMS = [Platform.SENSOR] + + +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Set up PurpleAir from a config entry.""" + coordinator = PurpleAirDataUpdateCoordinator(hass, entry) + await coordinator.async_config_entry_first_refresh() + hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator + + await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) + + entry.async_on_unload(entry.add_update_listener(async_handle_entry_update)) + + return True + + +async def async_handle_entry_update(hass: HomeAssistant, entry: ConfigEntry) -> None: + """Handle an options update.""" + await hass.config_entries.async_reload(entry.entry_id) + + +async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Unload a config entry.""" + if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): + hass.data[DOMAIN].pop(entry.entry_id) + + return unload_ok + + +class PurpleAirEntity(CoordinatorEntity[PurpleAirDataUpdateCoordinator]): + """Define a base PurpleAir entity.""" + + _attr_has_entity_name = True + + def __init__( + self, + coordinator: PurpleAirDataUpdateCoordinator, + entry: ConfigEntry, + sensor_index: int, + ) -> None: + """Initialize.""" + super().__init__(coordinator) + + self._sensor_index = sensor_index + + self._attr_device_info = DeviceInfo( + configuration_url=self.coordinator.async_get_map_url(sensor_index), + hw_version=self.sensor_data.hardware, + identifiers={(DOMAIN, str(self._sensor_index))}, + manufacturer="PurpleAir, Inc.", + model=self.sensor_data.model, + name=self.sensor_data.name, + sw_version=self.sensor_data.firmware_version, + ) + self._attr_extra_state_attributes = { + ATTR_LATITUDE: self.sensor_data.latitude, + ATTR_LONGITUDE: self.sensor_data.longitude, + } + + @property + def sensor_data(self) -> SensorModel: + """Define a property to get this entity's SensorModel object.""" + return self.coordinator.data.data[self._sensor_index] diff --git a/homeassistant/components/purpleair/config_flow.py b/homeassistant/components/purpleair/config_flow.py new file mode 100644 index 00000000000..0b1be019350 --- /dev/null +++ b/homeassistant/components/purpleair/config_flow.py @@ -0,0 +1,439 @@ +"""Config flow for PurpleAir integration.""" +from __future__ import annotations + +import asyncio +from collections.abc import Mapping +from copy import deepcopy +from dataclasses import dataclass, field +from typing import Any, cast + +from aiopurpleair import API +from aiopurpleair.endpoints.sensors import NearbySensorResult +from aiopurpleair.errors import InvalidApiKeyError, PurpleAirError +import voluptuous as vol + +from homeassistant import config_entries +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE +from homeassistant.core import Event, HomeAssistant, callback +from homeassistant.data_entry_flow import FlowResult +from homeassistant.helpers import ( + aiohttp_client, + config_validation as cv, + device_registry as dr, + entity_registry as er, +) +from homeassistant.helpers.event import async_track_state_change_event +from homeassistant.helpers.selector import ( + SelectOptionDict, + SelectSelector, + SelectSelectorConfig, + SelectSelectorMode, +) + +from .const import CONF_SENSOR_INDICES, DOMAIN, LOGGER + +CONF_DISTANCE = "distance" +CONF_NEARBY_SENSOR_OPTIONS = "nearby_sensor_options" +CONF_SENSOR_DEVICE_ID = "sensor_device_id" +CONF_SENSOR_INDEX = "sensor_index" + +DEFAULT_DISTANCE = 5 + +API_KEY_SCHEMA = vol.Schema( + { + vol.Required(CONF_API_KEY): cv.string, + } +) + + +@callback +def async_get_api(hass: HomeAssistant, api_key: str) -> API: + """Get an aiopurpleair API object.""" + session = aiohttp_client.async_get_clientsession(hass) + return API(api_key, session=session) + + +@callback +def async_get_coordinates_schema(hass: HomeAssistant) -> vol.Schema: + """Define a schema for searching for sensors near a coordinate pair.""" + return vol.Schema( + { + vol.Inclusive( + CONF_LATITUDE, "coords", default=hass.config.latitude + ): cv.latitude, + vol.Inclusive( + CONF_LONGITUDE, "coords", default=hass.config.longitude + ): cv.longitude, + vol.Optional(CONF_DISTANCE, default=DEFAULT_DISTANCE): cv.positive_int, + } + ) + + +@callback +def async_get_nearby_sensors_options( + nearby_sensor_results: list[NearbySensorResult], +) -> list[SelectOptionDict]: + """Return a set of nearby sensors as SelectOptionDict objects.""" + return [ + SelectOptionDict( + value=str(result.sensor.sensor_index), label=cast(str, result.sensor.name) + ) + for result in nearby_sensor_results + ] + + +@callback +def async_get_nearby_sensors_schema(options: list[SelectOptionDict]) -> vol.Schema: + """Define a schema for selecting a sensor from a list.""" + return vol.Schema( + { + vol.Required(CONF_SENSOR_INDEX): SelectSelector( + SelectSelectorConfig(options=options, mode=SelectSelectorMode.DROPDOWN) + ) + } + ) + + +@callback +def async_get_remove_sensor_options( + hass: HomeAssistant, config_entry: ConfigEntry +) -> list[SelectOptionDict]: + """Return a set of already-configured sensors as SelectOptionDict objects.""" + device_registry = dr.async_get(hass) + return [ + SelectOptionDict(value=device_entry.id, label=cast(str, device_entry.name)) + for device_entry in device_registry.devices.values() + if config_entry.entry_id in device_entry.config_entries + ] + + +@callback +def async_get_remove_sensor_schema(sensors: list[SelectOptionDict]) -> vol.Schema: + """Define a schema removing a sensor.""" + return vol.Schema( + { + vol.Required(CONF_SENSOR_DEVICE_ID): SelectSelector( + SelectSelectorConfig(options=sensors, mode=SelectSelectorMode.DROPDOWN) + ) + } + ) + + +@dataclass +class ValidationResult: + """Define a validation result.""" + + data: Any = None + errors: dict[str, Any] = field(default_factory=dict) + + +async def async_validate_api_key(hass: HomeAssistant, api_key: str) -> ValidationResult: + """Validate an API key. + + This method returns a dictionary of errors (if appropriate). + """ + api = async_get_api(hass, api_key) + errors = {} + + try: + await api.async_check_api_key() + except InvalidApiKeyError: + errors["base"] = "invalid_api_key" + except PurpleAirError as err: + LOGGER.error("PurpleAir error while checking API key: %s", err) + errors["base"] = "unknown" + except Exception as err: # pylint: disable=broad-except + LOGGER.exception("Unexpected exception while checking API key: %s", err) + errors["base"] = "unknown" + + if errors: + return ValidationResult(errors=errors) + + return ValidationResult(data=None) + + +async def async_validate_coordinates( + hass: HomeAssistant, + api_key: str, + latitude: float, + longitude: float, + distance: float, +) -> ValidationResult: + """Validate coordinates.""" + api = async_get_api(hass, api_key) + errors = {} + + try: + nearby_sensor_results = await api.sensors.async_get_nearby_sensors( + ["name"], latitude, longitude, distance, limit_results=5 + ) + except PurpleAirError as err: + LOGGER.error("PurpleAir error while getting nearby sensors: %s", err) + errors["base"] = "unknown" + except Exception as err: # pylint: disable=broad-except + LOGGER.exception("Unexpected exception while getting nearby sensors: %s", err) + errors["base"] = "unknown" + else: + if not nearby_sensor_results: + errors["base"] = "no_sensors_near_coordinates" + + if errors: + return ValidationResult(errors=errors) + + return ValidationResult(data=nearby_sensor_results) + + +class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): + """Handle a config flow for PurpleAir.""" + + VERSION = 1 + + def __init__(self) -> None: + """Initialize.""" + self._flow_data: dict[str, Any] = {} + self._reauth_entry: ConfigEntry | None = None + + @staticmethod + @callback + def async_get_options_flow( + config_entry: ConfigEntry, + ) -> PurpleAirOptionsFlowHandler: + """Define the config flow to handle options.""" + return PurpleAirOptionsFlowHandler(config_entry) + + async def async_step_by_coordinates( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle the discovery of sensors near a latitude/longitude.""" + if user_input is None: + return self.async_show_form( + step_id="by_coordinates", + data_schema=async_get_coordinates_schema(self.hass), + ) + + validation = await async_validate_coordinates( + self.hass, + self._flow_data[CONF_API_KEY], + user_input[CONF_LATITUDE], + user_input[CONF_LONGITUDE], + user_input[CONF_DISTANCE], + ) + if validation.errors: + return self.async_show_form( + step_id="by_coordinates", + data_schema=async_get_coordinates_schema(self.hass), + errors=validation.errors, + ) + + self._flow_data[CONF_NEARBY_SENSOR_OPTIONS] = async_get_nearby_sensors_options( + validation.data + ) + + return await self.async_step_choose_sensor() + + async def async_step_choose_sensor( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle the selection of a sensor.""" + if user_input is None: + options = self._flow_data.pop(CONF_NEARBY_SENSOR_OPTIONS) + return self.async_show_form( + step_id="choose_sensor", + data_schema=async_get_nearby_sensors_schema(options), + ) + + return self.async_create_entry( + title=self._flow_data[CONF_API_KEY][:5], + data=self._flow_data, + # Note that we store the sensor indices in options so that later on, we can + # add/remove additional sensors via an options flow: + options={CONF_SENSOR_INDICES: [int(user_input[CONF_SENSOR_INDEX])]}, + ) + + async def async_step_reauth(self, entry_data: Mapping[str, Any]) -> FlowResult: + """Handle configuration by re-auth.""" + self._reauth_entry = self.hass.config_entries.async_get_entry( + self.context["entry_id"] + ) + return await self.async_step_reauth_confirm() + + async def async_step_reauth_confirm( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle the re-auth step.""" + if user_input is None: + return self.async_show_form( + step_id="reauth_confirm", data_schema=API_KEY_SCHEMA + ) + + api_key = user_input[CONF_API_KEY] + + validation = await async_validate_api_key(self.hass, api_key) + if validation.errors: + return self.async_show_form( + step_id="reauth_confirm", + data_schema=API_KEY_SCHEMA, + errors=validation.errors, + ) + + assert self._reauth_entry + + self.hass.config_entries.async_update_entry( + self._reauth_entry, data={CONF_API_KEY: api_key} + ) + self.hass.async_create_task( + self.hass.config_entries.async_reload(self._reauth_entry.entry_id) + ) + return self.async_abort(reason="reauth_successful") + + async def async_step_user( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle the initial step.""" + if user_input is None: + return self.async_show_form(step_id="user", data_schema=API_KEY_SCHEMA) + + api_key = user_input[CONF_API_KEY] + + self._async_abort_entries_match({CONF_API_KEY: api_key}) + + validation = await async_validate_api_key(self.hass, api_key) + if validation.errors: + return self.async_show_form( + step_id="user", + data_schema=API_KEY_SCHEMA, + errors=validation.errors, + ) + + self._flow_data = {CONF_API_KEY: api_key} + return await self.async_step_by_coordinates() + + +class PurpleAirOptionsFlowHandler(config_entries.OptionsFlow): + """Handle a PurpleAir options flow.""" + + def __init__(self, config_entry: ConfigEntry) -> None: + """Initialize.""" + self._flow_data: dict[str, Any] = {} + self.config_entry = config_entry + + async def async_step_add_sensor( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Add a sensor.""" + if user_input is None: + return self.async_show_form( + step_id="add_sensor", + data_schema=async_get_coordinates_schema(self.hass), + ) + + validation = await async_validate_coordinates( + self.hass, + self.config_entry.data[CONF_API_KEY], + user_input[CONF_LATITUDE], + user_input[CONF_LONGITUDE], + user_input[CONF_DISTANCE], + ) + + if validation.errors: + return self.async_show_form( + step_id="add_sensor", + data_schema=async_get_coordinates_schema(self.hass), + errors=validation.errors, + ) + + self._flow_data[CONF_NEARBY_SENSOR_OPTIONS] = async_get_nearby_sensors_options( + validation.data + ) + + return await self.async_step_choose_sensor() + + async def async_step_choose_sensor( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle the selection of a sensor.""" + if user_input is None: + options = self._flow_data.pop(CONF_NEARBY_SENSOR_OPTIONS) + return self.async_show_form( + step_id="choose_sensor", + data_schema=async_get_nearby_sensors_schema(options), + ) + + sensor_index = int(user_input[CONF_SENSOR_INDEX]) + + if sensor_index in self.config_entry.options[CONF_SENSOR_INDICES]: + return self.async_abort(reason="already_configured") + + options = deepcopy({**self.config_entry.options}) + options[CONF_SENSOR_INDICES].append(sensor_index) + return self.async_create_entry(title="", data=options) + + async def async_step_init( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Manage the options.""" + return self.async_show_menu( + step_id="init", + menu_options=["add_sensor", "remove_sensor"], + ) + + async def async_step_remove_sensor( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Add a sensor.""" + if user_input is None: + return self.async_show_form( + step_id="remove_sensor", + data_schema=async_get_remove_sensor_schema( + async_get_remove_sensor_options(self.hass, self.config_entry) + ), + ) + + device_registry = dr.async_get(self.hass) + entity_registry = er.async_get(self.hass) + + device_id = user_input[CONF_SENSOR_DEVICE_ID] + device_entry = cast(dr.DeviceEntry, device_registry.async_get(device_id)) + + # Determine the entity entries that belong to this device. + entity_entries = er.async_entries_for_device( + entity_registry, device_id, include_disabled_entities=True + ) + + device_entities_removed_event = asyncio.Event() + + @callback + def async_device_entity_state_changed(_: Event) -> None: + """Listen and respond when all device entities are removed.""" + if all( + self.hass.states.get(entity_entry.entity_id) is None + for entity_entry in entity_entries + ): + device_entities_removed_event.set() + + # Track state changes for this device's entities and when they're removed, + # finish the flow: + cancel_state_track = async_track_state_change_event( + self.hass, + [entity_entry.entity_id for entity_entry in entity_entries], + async_device_entity_state_changed, + ) + device_registry.async_update_device( + device_id, remove_config_entry_id=self.config_entry.entry_id + ) + await device_entities_removed_event.wait() + + # Once we're done, we can cancel the state change tracker callback: + cancel_state_track() + + # Build new config entry options: + removed_sensor_index = next( + sensor_index + for sensor_index in self.config_entry.options[CONF_SENSOR_INDICES] + if (DOMAIN, str(sensor_index)) in device_entry.identifiers + ) + options = deepcopy({**self.config_entry.options}) + options[CONF_SENSOR_INDICES].remove(removed_sensor_index) + + return self.async_create_entry(title="", data=options) diff --git a/homeassistant/components/purpleair/const.py b/homeassistant/components/purpleair/const.py new file mode 100644 index 00000000000..60f51a9e7dd --- /dev/null +++ b/homeassistant/components/purpleair/const.py @@ -0,0 +1,9 @@ +"""Constants for the PurpleAir integration.""" +import logging + +DOMAIN = "purpleair" + +LOGGER = logging.getLogger(__package__) + +CONF_READ_KEY = "read_key" +CONF_SENSOR_INDICES = "sensor_indices" diff --git a/homeassistant/components/purpleair/coordinator.py b/homeassistant/components/purpleair/coordinator.py new file mode 100644 index 00000000000..9982db667f2 --- /dev/null +++ b/homeassistant/components/purpleair/coordinator.py @@ -0,0 +1,78 @@ +"""Define a PurpleAir DataUpdateCoordinator.""" +from __future__ import annotations + +from datetime import timedelta + +from aiopurpleair import API +from aiopurpleair.errors import InvalidApiKeyError, PurpleAirError +from aiopurpleair.models.sensors import GetSensorsResponse + +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_API_KEY +from homeassistant.core import HomeAssistant, callback +from homeassistant.exceptions import ConfigEntryAuthFailed +from homeassistant.helpers import aiohttp_client +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed + +from .const import CONF_SENSOR_INDICES, LOGGER + +SENSOR_FIELDS_TO_RETRIEVE = [ + "0.3_um_count", + "0.5_um_count", + "1.0_um_count", + "10.0_um_count", + "2.5_um_count", + "5.0_um_count", + "altitude", + "firmware_version", + "hardware", + "humidity", + "latitude", + "location_type", + "longitude", + "model", + "name", + "pm1.0", + "pm10.0", + "pm2.5", + "pressure", + "rssi", + "temperature", + "uptime", + "voc", +] + +UPDATE_INTERVAL = timedelta(minutes=2) + + +class PurpleAirDataUpdateCoordinator(DataUpdateCoordinator[GetSensorsResponse]): + """Define a PurpleAir-specific coordinator.""" + + def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None: + """Initialize.""" + self._entry = entry + self._api = API( + entry.data[CONF_API_KEY], + session=aiohttp_client.async_get_clientsession(hass), + ) + + super().__init__( + hass, LOGGER, name=entry.title, update_interval=UPDATE_INTERVAL + ) + + async def _async_update_data(self) -> GetSensorsResponse: + """Get the latest sensor information.""" + try: + return await self._api.sensors.async_get_sensors( + SENSOR_FIELDS_TO_RETRIEVE, + sensor_indices=self._entry.options[CONF_SENSOR_INDICES], + ) + except InvalidApiKeyError as err: + raise ConfigEntryAuthFailed("Invalid API key") from err + except PurpleAirError as err: + raise UpdateFailed(f"Error while fetching data: {err}") from err + + @callback + def async_get_map_url(self, sensor_index: int) -> str: + """Get the map URL for a sensor index.""" + return self._api.get_map_url(sensor_index) diff --git a/homeassistant/components/purpleair/diagnostics.py b/homeassistant/components/purpleair/diagnostics.py new file mode 100644 index 00000000000..0c52879b7a7 --- /dev/null +++ b/homeassistant/components/purpleair/diagnostics.py @@ -0,0 +1,42 @@ +"""Diagnostics support for PurpleAir.""" +from __future__ import annotations + +from typing import Any + +from homeassistant.components.diagnostics import async_redact_data +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import ( + CONF_API_KEY, + CONF_LATITUDE, + CONF_LONGITUDE, + CONF_UNIQUE_ID, +) +from homeassistant.core import HomeAssistant + +from .const import DOMAIN +from .coordinator import PurpleAirDataUpdateCoordinator + +CONF_TITLE = "title" + +TO_REDACT = { + CONF_API_KEY, + CONF_LATITUDE, + CONF_LONGITUDE, + # Config entry title and unique ID contain the API key (whole or part): + CONF_TITLE, + CONF_UNIQUE_ID, +} + + +async def async_get_config_entry_diagnostics( + hass: HomeAssistant, entry: ConfigEntry +) -> dict[str, Any]: + """Return diagnostics for a config entry.""" + coordinator: PurpleAirDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] + return async_redact_data( + { + "entry": entry.as_dict(), + "data": coordinator.data.dict(), + }, + TO_REDACT, + ) diff --git a/homeassistant/components/purpleair/manifest.json b/homeassistant/components/purpleair/manifest.json new file mode 100644 index 00000000000..e796404a142 --- /dev/null +++ b/homeassistant/components/purpleair/manifest.json @@ -0,0 +1,9 @@ +{ + "domain": "purpleair", + "name": "PurpleAir", + "config_flow": true, + "documentation": "https://www.home-assistant.io/integrations/purpleair", + "requirements": ["aiopurpleair==2022.12.1"], + "codeowners": ["@bachya"], + "iot_class": "cloud_polling" +} diff --git a/homeassistant/components/purpleair/sensor.py b/homeassistant/components/purpleair/sensor.py new file mode 100644 index 00000000000..44fa63b2fbc --- /dev/null +++ b/homeassistant/components/purpleair/sensor.py @@ -0,0 +1,219 @@ +"""Support for PurpleAir sensors.""" +from __future__ import annotations + +from collections.abc import Callable +from dataclasses import dataclass + +from aiopurpleair.models.sensors import SensorModel + +from homeassistant.components.sensor import ( + SensorDeviceClass, + SensorEntity, + SensorEntityDescription, + SensorStateClass, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import ( + CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + PERCENTAGE, + SIGNAL_STRENGTH_DECIBELS_MILLIWATT, + UnitOfPressure, + UnitOfTemperature, + UnitOfTime, + UnitOfVolume, +) +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import EntityCategory +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from . import PurpleAirEntity +from .const import CONF_SENSOR_INDICES, DOMAIN +from .coordinator import PurpleAirDataUpdateCoordinator + +CONCENTRATION_IAQ = "iaq" +CONCENTRATION_PARTICLES_PER_100_MILLILITERS = f"particles/100{UnitOfVolume.MILLILITERS}" + + +@dataclass +class PurpleAirSensorEntityDescriptionMixin: + """Define a description mixin for PurpleAir sensor entities.""" + + value_fn: Callable[[SensorModel], float | str | None] + + +@dataclass +class PurpleAirSensorEntityDescription( + SensorEntityDescription, PurpleAirSensorEntityDescriptionMixin +): + """Define an object to describe PurpleAir sensor entities.""" + + +SENSOR_DESCRIPTIONS = [ + PurpleAirSensorEntityDescription( + key="humidity", + name="Humidity", + device_class=SensorDeviceClass.HUMIDITY, + native_unit_of_measurement=PERCENTAGE, + state_class=SensorStateClass.MEASUREMENT, + value_fn=lambda sensor: sensor.humidity, + ), + PurpleAirSensorEntityDescription( + key="pm0.3_count_concentration", + name="PM0.3 count concentration", + entity_registry_enabled_default=False, + icon="mdi:blur", + native_unit_of_measurement=CONCENTRATION_PARTICLES_PER_100_MILLILITERS, + state_class=SensorStateClass.MEASUREMENT, + value_fn=lambda sensor: sensor.pm0_3_um_count, + ), + PurpleAirSensorEntityDescription( + key="pm0.5_count_concentration", + name="PM0.5 count concentration", + entity_registry_enabled_default=False, + icon="mdi:blur", + native_unit_of_measurement=CONCENTRATION_PARTICLES_PER_100_MILLILITERS, + state_class=SensorStateClass.MEASUREMENT, + value_fn=lambda sensor: sensor.pm0_5_um_count, + ), + PurpleAirSensorEntityDescription( + key="pm1.0_count_concentration", + name="PM1.0 count concentration", + entity_registry_enabled_default=False, + icon="mdi:blur", + native_unit_of_measurement=CONCENTRATION_PARTICLES_PER_100_MILLILITERS, + state_class=SensorStateClass.MEASUREMENT, + value_fn=lambda sensor: sensor.pm1_0_um_count, + ), + PurpleAirSensorEntityDescription( + key="pm1.0_mass_concentration", + name="PM1.0 mass concentration", + device_class=SensorDeviceClass.PM1, + native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + state_class=SensorStateClass.MEASUREMENT, + value_fn=lambda sensor: sensor.pm1_0, + ), + PurpleAirSensorEntityDescription( + key="pm10.0_count_concentration", + name="PM10.0 count concentration", + entity_registry_enabled_default=False, + icon="mdi:blur", + native_unit_of_measurement=CONCENTRATION_PARTICLES_PER_100_MILLILITERS, + state_class=SensorStateClass.MEASUREMENT, + value_fn=lambda sensor: sensor.pm10_0_um_count, + ), + PurpleAirSensorEntityDescription( + key="pm10.0_mass_concentration", + name="PM10.0 mass concentration", + device_class=SensorDeviceClass.PM10, + native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + state_class=SensorStateClass.MEASUREMENT, + value_fn=lambda sensor: sensor.pm10_0, + ), + PurpleAirSensorEntityDescription( + key="pm2.5_count_concentration", + name="PM2.5 count concentration", + entity_registry_enabled_default=False, + icon="mdi:blur", + native_unit_of_measurement=CONCENTRATION_PARTICLES_PER_100_MILLILITERS, + state_class=SensorStateClass.MEASUREMENT, + value_fn=lambda sensor: sensor.pm2_5_um_count, + ), + PurpleAirSensorEntityDescription( + key="pm2.5_mass_concentration", + name="PM2.5 mass concentration", + device_class=SensorDeviceClass.PM25, + native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + state_class=SensorStateClass.MEASUREMENT, + value_fn=lambda sensor: sensor.pm2_5, + ), + PurpleAirSensorEntityDescription( + key="pm5.0_count_concentration", + name="PM5.0 count concentration", + entity_registry_enabled_default=False, + icon="mdi:blur", + native_unit_of_measurement=CONCENTRATION_PARTICLES_PER_100_MILLILITERS, + state_class=SensorStateClass.MEASUREMENT, + value_fn=lambda sensor: sensor.pm5_0_um_count, + ), + PurpleAirSensorEntityDescription( + key="pressure", + name="Pressure", + device_class=SensorDeviceClass.PRESSURE, + native_unit_of_measurement=UnitOfPressure.MBAR, + state_class=SensorStateClass.MEASUREMENT, + value_fn=lambda sensor: sensor.pressure, + ), + PurpleAirSensorEntityDescription( + key="rssi", + name="RSSI", + device_class=SensorDeviceClass.SIGNAL_STRENGTH, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS_MILLIWATT, + state_class=SensorStateClass.MEASUREMENT, + value_fn=lambda sensor: sensor.pressure, + ), + PurpleAirSensorEntityDescription( + key="temperature", + name="Temperature", + device_class=SensorDeviceClass.TEMPERATURE, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, + state_class=SensorStateClass.MEASUREMENT, + value_fn=lambda sensor: sensor.temperature, + ), + PurpleAirSensorEntityDescription( + key="uptime", + name="Uptime", + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + device_class=SensorDeviceClass.DURATION, + native_unit_of_measurement=UnitOfTime.MINUTES, + state_class=SensorStateClass.TOTAL_INCREASING, + value_fn=lambda sensor: sensor.uptime, + ), + PurpleAirSensorEntityDescription( + key="voc", + name="VOC", + device_class=SensorDeviceClass.AQI, + state_class=SensorStateClass.MEASUREMENT, + value_fn=lambda sensor: sensor.voc, + ), +] + + +async def async_setup_entry( + hass: HomeAssistant, + entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up PurpleAir sensors based on a config entry.""" + coordinator: PurpleAirDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] + async_add_entities( + PurpleAirSensorEntity(coordinator, entry, sensor_index, description) + for sensor_index in entry.options[CONF_SENSOR_INDICES] + for description in SENSOR_DESCRIPTIONS + ) + + +class PurpleAirSensorEntity(PurpleAirEntity, SensorEntity): + """Define a representation of a PurpleAir sensor.""" + + entity_description: PurpleAirSensorEntityDescription + + def __init__( + self, + coordinator: PurpleAirDataUpdateCoordinator, + entry: ConfigEntry, + sensor_index: int, + description: PurpleAirSensorEntityDescription, + ) -> None: + """Initialize.""" + super().__init__(coordinator, entry, sensor_index) + + self._attr_unique_id = f"{self._sensor_index}-{description.key}" + self.entity_description = description + + @property + def native_value(self) -> float | str | None: + """Return the sensor value.""" + return self.entity_description.value_fn(self.sensor_data) diff --git a/homeassistant/components/purpleair/strings.json b/homeassistant/components/purpleair/strings.json new file mode 100644 index 00000000000..3d18fef3906 --- /dev/null +++ b/homeassistant/components/purpleair/strings.json @@ -0,0 +1,104 @@ +{ + "config": { + "step": { + "by_coordinates": { + "description": "Search for a PurpleAir sensor within a certain distance of a latitude/longitude.", + "data": { + "latitude": "[%key:common::config_flow::data::latitude%]", + "longitude": "[%key:common::config_flow::data::longitude%]", + "distance": "Search Radius" + }, + "data_description": { + "latitude": "The latitude around which to search for sensors", + "longitude": "The longitude around which to search for sensors", + "distance": "The radius (in kilometers) of the circle to search within" + } + }, + "choose_sensor": { + "description": "Which of the nearby sensors would you like to track?", + "data": { + "sensor_index": "Sensor" + }, + "data_description": { + "sensor_index": "The sensor to track" + } + }, + "reauth_confirm": { + "data": { + "api_key": "[%key:common::config_flow::data::api_key%]" + }, + "data_description": { + "api_key": "[%key:component::purpleair::config::step::user::data_description::api_key%]" + } + }, + "user": { + "data": { + "api_key": "[%key:common::config_flow::data::api_key%]" + }, + "data_description": { + "api_key": "Your PurpleAir API key (if you have both read and write keys, use the read key)" + } + } + }, + "error": { + "invalid_api_key": "[%key:common::config_flow::error::invalid_api_key%]", + "no_sensors_near_coordinates": "No sensors found near coordinates (within distance)", + "unknown": "[%key:common::config_flow::error::unknown%]" + }, + "abort": { + "already_configured": "[%key:common::config_flow::abort::already_configured_device%]", + "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]" + } + }, + "options": { + "step": { + "add_sensor": { + "title": "Add Sensor", + "description": "[%key:component::purpleair::config::step::by_coordinates::description%]", + "data": { + "latitude": "[%key:component::purpleair::config::step::by_coordinates::data::latitude%]", + "longitude": "[%key:component::purpleair::config::step::by_coordinates::data::longitude%]", + "distance": "[%key:component::purpleair::config::step::by_coordinates::data::distance%]" + }, + "data_description": { + "latitude": "[%key:component::purpleair::config::step::by_coordinates::data_description::latitude%]", + "longitude": "[%key:component::purpleair::config::step::by_coordinates::data_description::longitude%]", + "distance": "[%key:component::purpleair::config::step::by_coordinates::data_description::distance%]" + } + }, + "choose_sensor": { + "title": "Choose Sensor to Add", + "description": "[%key:component::purpleair::config::step::choose_sensor::description%]", + "data": { + "sensor_index": "[%key:component::purpleair::config::step::choose_sensor::data::sensor_index%]" + }, + "data_description": { + "sensor_index": "[%key:component::purpleair::config::step::choose_sensor::data_description::sensor_index%]" + } + }, + "init": { + "menu_options": { + "add_sensor": "Add sensor", + "remove_sensor": "Remove sensor" + } + }, + "remove_sensor": { + "title": "Remove Sensor", + "data": { + "sensor_device_id": "Sensor Name" + }, + "data_description": { + "sensor_device_id": "The sensor to remove" + } + } + }, + "error": { + "invalid_api_key": "[%key:common::config_flow::error::invalid_api_key%]", + "no_sensors_near_coordinates": "[%key:component::purpleair::config::error::no_sensors_near_coordinates%]", + "unknown": "[%key:common::config_flow::error::unknown%]" + }, + "abort": { + "already_configured": "[%key:common::config_flow::abort::already_configured_device%]" + } + } +} diff --git a/homeassistant/components/purpleair/translations/bg.json b/homeassistant/components/purpleair/translations/bg.json new file mode 100644 index 00000000000..f971a766b89 --- /dev/null +++ b/homeassistant/components/purpleair/translations/bg.json @@ -0,0 +1,51 @@ +{ + "config": { + "abort": { + "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u0442\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u0430" + }, + "error": { + "invalid_api_key": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u0435\u043d API \u043a\u043b\u044e\u0447", + "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" + }, + "step": { + "by_coordinates": { + "data": { + "latitude": "\u0413\u0435\u043e\u0433\u0440\u0430\u0444\u0441\u043a\u0430 \u0448\u0438\u0440\u0438\u043d\u0430", + "longitude": "\u0413\u0435\u043e\u0433\u0440\u0430\u0444\u0441\u043a\u0430 \u0434\u044a\u043b\u0436\u0438\u043d\u0430" + } + }, + "choose_sensor": { + "data": { + "sensor_index": "\u0421\u0435\u043d\u0437\u043e\u0440" + } + }, + "reauth_confirm": { + "data": { + "api_key": "API \u043a\u043b\u044e\u0447" + } + }, + "user": { + "data": { + "api_key": "API \u043a\u043b\u044e\u0447" + } + } + } + }, + "options": { + "abort": { + "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e" + }, + "error": { + "invalid_api_key": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u0435\u043d API \u043a\u043b\u044e\u0447", + "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" + }, + "step": { + "choose_sensor": { + "data": { + "sensor_index": "\u0421\u0435\u043d\u0437\u043e\u0440" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/purpleair/translations/ca.json b/homeassistant/components/purpleair/translations/ca.json new file mode 100644 index 00000000000..95979489379 --- /dev/null +++ b/homeassistant/components/purpleair/translations/ca.json @@ -0,0 +1,104 @@ +{ + "config": { + "abort": { + "already_configured": "El dispositiu ja est\u00e0 configurat", + "reauth_successful": "Re-autenticaci\u00f3 realitzada correctament" + }, + "error": { + "invalid_api_key": "Clau API inv\u00e0lida", + "no_sensors_near_coordinates": "No s'han trobat sensors a prop de coordenades (dins la dist\u00e0ncia)", + "unknown": "Error inesperat" + }, + "step": { + "by_coordinates": { + "data": { + "distance": "Radi de cerca", + "latitude": "Latitud", + "longitude": "Longitud" + }, + "data_description": { + "distance": "Radi (en quil\u00f2metres) del cercle dins del qual cercar", + "latitude": "Latitud al voltant de la qual cercar sensors", + "longitude": "Longitud al voltant de la qual cercar sensors" + }, + "description": "Cerca un sensor PurpleAir a una dist\u00e0ncia determinada d'una latitud/longitud." + }, + "choose_sensor": { + "data": { + "sensor_index": "Sensor" + }, + "data_description": { + "sensor_index": "Sensor al qual fer-li seguiment" + }, + "description": "Quins dels sensors propers t'agradaria seguir?" + }, + "reauth_confirm": { + "data": { + "api_key": "Clau API" + }, + "data_description": { + "api_key": "Clau API de PurpleAir (si tens claus de lectura i escriptura, utilitza la de lectura)" + } + }, + "user": { + "data": { + "api_key": "Clau API" + }, + "data_description": { + "api_key": "Clau API de PurpleAir (si tens claus de lectura i escriptura, utilitza la de lectura)" + } + } + } + }, + "options": { + "abort": { + "already_configured": "El dispositiu ja est\u00e0 configurat" + }, + "error": { + "invalid_api_key": "Clau API inv\u00e0lida", + "no_sensors_near_coordinates": "No s'han trobat sensors a prop de coordenades (dins la dist\u00e0ncia)", + "unknown": "Error inesperat" + }, + "step": { + "add_sensor": { + "data": { + "distance": "Radi de cerca", + "latitude": "Latitud", + "longitude": "Longitud" + }, + "data_description": { + "distance": "Radi (en quil\u00f2metres) del cercle dins del qual cercar", + "latitude": "Latitud al voltant de la qual cercar sensors", + "longitude": "Longitud al voltant de la qual cercar sensors" + }, + "description": "Cerca un sensor PurpleAir a una dist\u00e0ncia determinada d'una latitud/longitud.", + "title": "Afegeix sensor" + }, + "choose_sensor": { + "data": { + "sensor_index": "Sensor" + }, + "data_description": { + "sensor_index": "Sensor al qual fer-li seguiment" + }, + "description": "Quins dels sensors propers t'agradaria seguir?", + "title": "Tria sensor a afegir" + }, + "init": { + "menu_options": { + "add_sensor": "Afegeix sensor", + "remove_sensor": "Elimina sensor" + } + }, + "remove_sensor": { + "data": { + "sensor_device_id": "Nom del sensor" + }, + "data_description": { + "sensor_device_id": "Sensor a eliminar" + }, + "title": "Elimina sensor" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/purpleair/translations/de.json b/homeassistant/components/purpleair/translations/de.json new file mode 100644 index 00000000000..3d5e762e8ed --- /dev/null +++ b/homeassistant/components/purpleair/translations/de.json @@ -0,0 +1,104 @@ +{ + "config": { + "abort": { + "already_configured": "Ger\u00e4t ist bereits konfiguriert", + "reauth_successful": "Die erneute Authentifizierung war erfolgreich" + }, + "error": { + "invalid_api_key": "Ung\u00fcltiger API-Schl\u00fcssel", + "no_sensors_near_coordinates": "Keine Sensoren in der N\u00e4he der Koordinaten gefunden (innerhalb der Entfernung)", + "unknown": "Unerwarteter Fehler" + }, + "step": { + "by_coordinates": { + "data": { + "distance": "Suchradius", + "latitude": "Breitengrad", + "longitude": "L\u00e4ngengrad" + }, + "data_description": { + "distance": "Der Radius (in Kilometern) des Kreises, in dem gesucht werden soll", + "latitude": "Der Breitengrad, um den herum nach Sensoren gesucht werden soll", + "longitude": "Der L\u00e4ngengrad, um den herum nach Sensoren gesucht werden soll" + }, + "description": "Suche nach einem PurpleAir-Sensor innerhalb einer bestimmten Entfernung zu einem Breitengrad/L\u00e4ngengrad." + }, + "choose_sensor": { + "data": { + "sensor_index": "Sensor" + }, + "data_description": { + "sensor_index": "Der zu verfolgende Sensor" + }, + "description": "Welche der Sensoren in der N\u00e4he m\u00f6chtest du verfolgen?" + }, + "reauth_confirm": { + "data": { + "api_key": "API-Schl\u00fcssel" + }, + "data_description": { + "api_key": "Dein PurpleAir-API-Schl\u00fcssel (wenn du sowohl Lese- als auch Schreibschl\u00fcssel hast, verwende den Leseschl\u00fcssel)" + } + }, + "user": { + "data": { + "api_key": "API-Schl\u00fcssel" + }, + "data_description": { + "api_key": "Dein PurpleAir-API-Schl\u00fcssel (wenn du sowohl Lese- als auch Schreibschl\u00fcssel hast, verwende den Leseschl\u00fcssel)" + } + } + } + }, + "options": { + "abort": { + "already_configured": "Ger\u00e4t ist bereits konfiguriert" + }, + "error": { + "invalid_api_key": "Ung\u00fcltiger API-Schl\u00fcssel", + "no_sensors_near_coordinates": "Keine Sensoren in der N\u00e4he der Koordinaten gefunden (innerhalb der Entfernung)", + "unknown": "Unerwarteter Fehler" + }, + "step": { + "add_sensor": { + "data": { + "distance": "Suchradius", + "latitude": "Breitengrad", + "longitude": "L\u00e4ngengrad" + }, + "data_description": { + "distance": "Der Radius (in Kilometern) des Kreises, in dem gesucht werden soll", + "latitude": "Der Breitengrad, um den herum nach Sensoren gesucht werden soll", + "longitude": "Der L\u00e4ngengrad, um den herum nach Sensoren gesucht werden soll" + }, + "description": "Suche nach einem PurpleAir-Sensor innerhalb einer bestimmten Entfernung zu einem Breitengrad/L\u00e4ngengrad.", + "title": "Sensor hinzuf\u00fcgen" + }, + "choose_sensor": { + "data": { + "sensor_index": "Sensor" + }, + "data_description": { + "sensor_index": "Der zu verfolgende Sensor" + }, + "description": "Welche der Sensoren in der N\u00e4he m\u00f6chtest du verfolgen?", + "title": "Sensor zum Hinzuf\u00fcgen ausw\u00e4hlen" + }, + "init": { + "menu_options": { + "add_sensor": "Sensor hinzuf\u00fcgen", + "remove_sensor": "Sensor entfernen" + } + }, + "remove_sensor": { + "data": { + "sensor_device_id": "Sensorname" + }, + "data_description": { + "sensor_device_id": "Der zu entfernende Sensor" + }, + "title": "Sensor entfernen" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/purpleair/translations/el.json b/homeassistant/components/purpleair/translations/el.json new file mode 100644 index 00000000000..1004cce3a31 --- /dev/null +++ b/homeassistant/components/purpleair/translations/el.json @@ -0,0 +1,102 @@ +{ + "config": { + "abort": { + "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", + "reauth_successful": "\u039f \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ae\u03c4\u03b1\u03bd \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2" + }, + "error": { + "invalid_api_key": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API", + "no_sensors_near_coordinates": "\u0394\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b1\u03bd \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b5\u03c2 \u03ba\u03bf\u03bd\u03c4\u03ac \u03c3\u03b5 \u03c3\u03c5\u03bd\u03c4\u03b5\u03c4\u03b1\u03b3\u03bc\u03ad\u03bd\u03b5\u03c2 (\u03b5\u03bd\u03c4\u03cc\u03c2 \u03b1\u03c0\u03cc\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2)", + "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" + }, + "step": { + "by_coordinates": { + "data": { + "distance": "\u0391\u03ba\u03c4\u03af\u03bd\u03b1 \u03b1\u03bd\u03b1\u03b6\u03ae\u03c4\u03b7\u03c3\u03b7\u03c2", + "latitude": "\u0393\u03b5\u03c9\u03b3\u03c1\u03b1\u03c6\u03b9\u03ba\u03cc \u03c0\u03bb\u03ac\u03c4\u03bf\u03c2", + "longitude": "\u0393\u03b5\u03c9\u03b3\u03c1\u03b1\u03c6\u03b9\u03ba\u03cc \u03bc\u03ae\u03ba\u03bf\u03c2" + }, + "data_description": { + "distance": "\u0397 \u03b1\u03ba\u03c4\u03af\u03bd\u03b1 (\u03c3\u03b5 \u03c7\u03b9\u03bb\u03b9\u03cc\u03bc\u03b5\u03c4\u03c1\u03b1) \u03c4\u03bf\u03c5 \u03ba\u03cd\u03ba\u03bb\u03bf\u03c5 \u03c0\u03c1\u03bf\u03c2 \u03b1\u03bd\u03b1\u03b6\u03ae\u03c4\u03b7\u03c3\u03b7", + "latitude": "\u03a4\u03bf \u03b3\u03b5\u03c9\u03b3\u03c1\u03b1\u03c6\u03b9\u03ba\u03cc \u03c0\u03bb\u03ac\u03c4\u03bf\u03c2 \u03b3\u03cd\u03c1\u03c9 \u03b1\u03c0\u03cc \u03c4\u03bf \u03bf\u03c0\u03bf\u03af\u03bf \u03bc\u03c0\u03bf\u03c1\u03b5\u03af\u03c4\u03b5 \u03bd\u03b1 \u03b1\u03bd\u03b1\u03b6\u03b7\u03c4\u03ae\u03c3\u03b5\u03c4\u03b5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b5\u03c2", + "longitude": "\u03a4\u03bf \u03b3\u03b5\u03c9\u03b3\u03c1\u03b1\u03c6\u03b9\u03ba\u03cc \u03bc\u03ae\u03ba\u03bf\u03c2 \u03b3\u03cd\u03c1\u03c9 \u03b1\u03c0\u03cc \u03c4\u03bf \u03bf\u03c0\u03bf\u03af\u03bf \u03bc\u03c0\u03bf\u03c1\u03b5\u03af\u03c4\u03b5 \u03bd\u03b1 \u03b1\u03bd\u03b1\u03b6\u03b7\u03c4\u03ae\u03c3\u03b5\u03c4\u03b5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b5\u03c2" + }, + "description": "\u0391\u03bd\u03b1\u03b6\u03b7\u03c4\u03ae\u03c3\u03c4\u03b5 \u03ad\u03bd\u03b1\u03bd \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 PurpleAir \u03c3\u03b5 \u03bc\u03b9\u03b1 \u03bf\u03c1\u03b9\u03c3\u03bc\u03ad\u03bd\u03b7 \u03b1\u03c0\u03cc\u03c3\u03c4\u03b1\u03c3\u03b7 \u03b3\u03b5\u03c9\u03b3\u03c1\u03b1\u03c6\u03b9\u03ba\u03bf\u03cd \u03c0\u03bb\u03ac\u03c4\u03bf\u03c5\u03c2/\u03bc\u03ae\u03ba\u03bf\u03c5\u03c2." + }, + "choose_sensor": { + "data": { + "sensor_index": "\u0391\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2" + }, + "data_description": { + "sensor_index": "\u039f \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2 \u03b3\u03b9\u03b1 \u03c0\u03b1\u03c1\u03b1\u03ba\u03bf\u03bb\u03bf\u03cd\u03b8\u03b7\u03c3\u03b7" + }, + "description": "\u03a0\u03bf\u03b9\u03bf\u03c5\u03c2 \u03b1\u03c0\u03cc \u03c4\u03bf\u03c5\u03c2 \u03ba\u03bf\u03bd\u03c4\u03b9\u03bd\u03bf\u03cd\u03c2 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b5\u03c2 \u03b8\u03b1 \u03b8\u03ad\u03bb\u03b1\u03c4\u03b5 \u03bd\u03b1 \u03c0\u03b1\u03c1\u03b1\u03ba\u03bf\u03bb\u03bf\u03c5\u03b8\u03ae\u03c3\u03b5\u03c4\u03b5;" + }, + "reauth_confirm": { + "data": { + "api_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af API" + }, + "data_description": { + "api_key": "\u03a4\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API PurpleAir (\u03b5\u03ac\u03bd \u03ad\u03c7\u03b5\u03c4\u03b5 \u03ba\u03b1\u03b9 \u03ba\u03bb\u03b5\u03b9\u03b4\u03b9\u03ac \u03b1\u03bd\u03ac\u03b3\u03bd\u03c9\u03c3\u03b7\u03c2 \u03ba\u03b1\u03b9 \u03b5\u03b3\u03b3\u03c1\u03b1\u03c6\u03ae\u03c2, \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af \u03b1\u03bd\u03ac\u03b3\u03bd\u03c9\u03c3\u03b7\u03c2)" + } + }, + "user": { + "data": { + "api_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af API" + }, + "data_description": { + "api_key": "\u03a4\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API PurpleAir (\u03b5\u03ac\u03bd \u03ad\u03c7\u03b5\u03c4\u03b5 \u03ba\u03b1\u03b9 \u03ba\u03bb\u03b5\u03b9\u03b4\u03b9\u03ac \u03b1\u03bd\u03ac\u03b3\u03bd\u03c9\u03c3\u03b7\u03c2 \u03ba\u03b1\u03b9 \u03b5\u03b3\u03b3\u03c1\u03b1\u03c6\u03ae\u03c2, \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af \u03b1\u03bd\u03ac\u03b3\u03bd\u03c9\u03c3\u03b7\u03c2)" + } + } + } + }, + "options": { + "abort": { + "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af" + }, + "error": { + "invalid_api_key": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API", + "no_sensors_near_coordinates": "\u0394\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b1\u03bd \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b5\u03c2 \u03ba\u03bf\u03bd\u03c4\u03ac \u03c3\u03b5 \u03c3\u03c5\u03bd\u03c4\u03b5\u03c4\u03b1\u03b3\u03bc\u03ad\u03bd\u03b5\u03c2 (\u03b5\u03bd\u03c4\u03cc\u03c2 \u03b1\u03c0\u03cc\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2)", + "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" + }, + "step": { + "add_sensor": { + "data": { + "distance": "\u0391\u03ba\u03c4\u03af\u03bd\u03b1 \u03b1\u03bd\u03b1\u03b6\u03ae\u03c4\u03b7\u03c3\u03b7\u03c2" + }, + "data_description": { + "distance": "\u0397 \u03b1\u03ba\u03c4\u03af\u03bd\u03b1 (\u03c3\u03b5 \u03c7\u03b9\u03bb\u03b9\u03cc\u03bc\u03b5\u03c4\u03c1\u03b1) \u03c4\u03bf\u03c5 \u03ba\u03cd\u03ba\u03bb\u03bf\u03c5 \u03c0\u03c1\u03bf\u03c2 \u03b1\u03bd\u03b1\u03b6\u03ae\u03c4\u03b7\u03c3\u03b7", + "latitude": "\u03a4\u03bf \u03b3\u03b5\u03c9\u03b3\u03c1\u03b1\u03c6\u03b9\u03ba\u03cc \u03c0\u03bb\u03ac\u03c4\u03bf\u03c2 \u03b3\u03cd\u03c1\u03c9 \u03b1\u03c0\u03cc \u03c4\u03bf \u03bf\u03c0\u03bf\u03af\u03bf \u03bc\u03c0\u03bf\u03c1\u03b5\u03af\u03c4\u03b5 \u03bd\u03b1 \u03b1\u03bd\u03b1\u03b6\u03b7\u03c4\u03ae\u03c3\u03b5\u03c4\u03b5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b5\u03c2", + "longitude": "\u03a4\u03bf \u03b3\u03b5\u03c9\u03b3\u03c1\u03b1\u03c6\u03b9\u03ba\u03cc \u03bc\u03ae\u03ba\u03bf\u03c2 \u03b3\u03cd\u03c1\u03c9 \u03b1\u03c0\u03cc \u03c4\u03bf \u03bf\u03c0\u03bf\u03af\u03bf \u03bc\u03c0\u03bf\u03c1\u03b5\u03af\u03c4\u03b5 \u03bd\u03b1 \u03b1\u03bd\u03b1\u03b6\u03b7\u03c4\u03ae\u03c3\u03b5\u03c4\u03b5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b5\u03c2" + }, + "description": "\u0391\u03bd\u03b1\u03b6\u03b7\u03c4\u03ae\u03c3\u03c4\u03b5 \u03ad\u03bd\u03b1\u03bd \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 PurpleAir \u03c3\u03b5 \u03bc\u03b9\u03b1 \u03bf\u03c1\u03b9\u03c3\u03bc\u03ad\u03bd\u03b7 \u03b1\u03c0\u03cc\u03c3\u03c4\u03b1\u03c3\u03b7 \u03b3\u03b5\u03c9\u03b3\u03c1\u03b1\u03c6\u03b9\u03ba\u03bf\u03cd \u03c0\u03bb\u03ac\u03c4\u03bf\u03c5\u03c2/\u03bc\u03ae\u03ba\u03bf\u03c5\u03c2.", + "title": "\u03a0\u03c1\u03bf\u03c3\u03b8\u03ae\u03ba\u03b7 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1" + }, + "choose_sensor": { + "data": { + "sensor_index": "\u0391\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2" + }, + "data_description": { + "sensor_index": "\u039f \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2 \u03b3\u03b9\u03b1 \u03c0\u03b1\u03c1\u03b1\u03ba\u03bf\u03bb\u03bf\u03cd\u03b8\u03b7\u03c3\u03b7" + }, + "description": "\u03a0\u03bf\u03b9\u03bf\u03c5\u03c2 \u03b1\u03c0\u03cc \u03c4\u03bf\u03c5\u03c2 \u03ba\u03bf\u03bd\u03c4\u03b9\u03bd\u03bf\u03cd\u03c2 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b5\u03c2 \u03b8\u03b1 \u03b8\u03ad\u03bb\u03b1\u03c4\u03b5 \u03bd\u03b1 \u03c0\u03b1\u03c1\u03b1\u03ba\u03bf\u03bb\u03bf\u03c5\u03b8\u03ae\u03c3\u03b5\u03c4\u03b5;", + "title": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 \u03b3\u03b9\u03b1 \u03c0\u03c1\u03bf\u03c3\u03b8\u03ae\u03ba\u03b7" + }, + "init": { + "menu_options": { + "add_sensor": "\u03a0\u03c1\u03bf\u03c3\u03b8\u03ae\u03ba\u03b7 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1", + "remove_sensor": "\u039a\u03b1\u03c4\u03ac\u03c1\u03b3\u03b7\u03c3\u03b7 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1" + } + }, + "remove_sensor": { + "data": { + "sensor_device_id": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1" + }, + "data_description": { + "sensor_device_id": "\u039f \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2 \u03b3\u03b9\u03b1 \u03b1\u03c6\u03b1\u03af\u03c1\u03b5\u03c3\u03b7" + }, + "title": "\u039a\u03b1\u03c4\u03ac\u03c1\u03b3\u03b7\u03c3\u03b7 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/purpleair/translations/en.json b/homeassistant/components/purpleair/translations/en.json new file mode 100644 index 00000000000..d769f788a55 --- /dev/null +++ b/homeassistant/components/purpleair/translations/en.json @@ -0,0 +1,104 @@ +{ + "config": { + "abort": { + "already_configured": "Device is already configured", + "reauth_successful": "Re-authentication was successful" + }, + "error": { + "invalid_api_key": "Invalid API key", + "no_sensors_near_coordinates": "No sensors found near coordinates (within distance)", + "unknown": "Unexpected error" + }, + "step": { + "by_coordinates": { + "data": { + "distance": "Search Radius", + "latitude": "Latitude", + "longitude": "Longitude" + }, + "data_description": { + "distance": "The radius (in kilometers) of the circle to search within", + "latitude": "The latitude around which to search for sensors", + "longitude": "The longitude around which to search for sensors" + }, + "description": "Search for a PurpleAir sensor within a certain distance of a latitude/longitude." + }, + "choose_sensor": { + "data": { + "sensor_index": "Sensor" + }, + "data_description": { + "sensor_index": "The sensor to track" + }, + "description": "Which of the nearby sensors would you like to track?" + }, + "reauth_confirm": { + "data": { + "api_key": "API Key" + }, + "data_description": { + "api_key": "Your PurpleAir API key (if you have both read and write keys, use the read key)" + } + }, + "user": { + "data": { + "api_key": "API Key" + }, + "data_description": { + "api_key": "Your PurpleAir API key (if you have both read and write keys, use the read key)" + } + } + } + }, + "options": { + "abort": { + "already_configured": "Device is already configured" + }, + "error": { + "invalid_api_key": "Invalid API key", + "no_sensors_near_coordinates": "No sensors found near coordinates (within distance)", + "unknown": "Unexpected error" + }, + "step": { + "add_sensor": { + "data": { + "distance": "Search Radius", + "latitude": "Latitude", + "longitude": "Longitude" + }, + "data_description": { + "distance": "The radius (in kilometers) of the circle to search within", + "latitude": "The latitude around which to search for sensors", + "longitude": "The longitude around which to search for sensors" + }, + "description": "Search for a PurpleAir sensor within a certain distance of a latitude/longitude.", + "title": "Add Sensor" + }, + "choose_sensor": { + "data": { + "sensor_index": "Sensor" + }, + "data_description": { + "sensor_index": "The sensor to track" + }, + "description": "Which of the nearby sensors would you like to track?", + "title": "Choose Sensor to Add" + }, + "init": { + "menu_options": { + "add_sensor": "Add sensor", + "remove_sensor": "Remove sensor" + } + }, + "remove_sensor": { + "data": { + "sensor_device_id": "Sensor Name" + }, + "data_description": { + "sensor_device_id": "The sensor to remove" + }, + "title": "Remove Sensor" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/purpleair/translations/es.json b/homeassistant/components/purpleair/translations/es.json new file mode 100644 index 00000000000..c730d620782 --- /dev/null +++ b/homeassistant/components/purpleair/translations/es.json @@ -0,0 +1,104 @@ +{ + "config": { + "abort": { + "already_configured": "El dispositivo ya est\u00e1 configurado", + "reauth_successful": "La autenticaci\u00f3n se volvi\u00f3 a realizar correctamente" + }, + "error": { + "invalid_api_key": "Clave API no v\u00e1lida", + "no_sensors_near_coordinates": "No se encontraron sensores cerca de las coordenadas (dentro de la distancia)", + "unknown": "Error inesperado" + }, + "step": { + "by_coordinates": { + "data": { + "distance": "Radio de b\u00fasqueda", + "latitude": "Latitud", + "longitude": "Longitud" + }, + "data_description": { + "distance": "El radio (en kil\u00f3metros) del c\u00edrculo para buscar dentro", + "latitude": "La latitud alrededor de la cual buscar sensores", + "longitude": "La longitud alrededor de la cual buscar sensores" + }, + "description": "Buscar un sensor PurpleAir dentro de una cierta distancia de una latitud/longitud." + }, + "choose_sensor": { + "data": { + "sensor_index": "Sensor" + }, + "data_description": { + "sensor_index": "El sensor para rastrear" + }, + "description": "\u00bfCu\u00e1l de los sensores cercanos te gustar\u00eda rastrear?" + }, + "reauth_confirm": { + "data": { + "api_key": "Clave API" + }, + "data_description": { + "api_key": "Tu clave API de PurpleAir (si tienes claves de lectura y escritura, usa la clave de lectura)" + } + }, + "user": { + "data": { + "api_key": "Clave API" + }, + "data_description": { + "api_key": "Tu clave API de PurpleAir (si tienes claves de lectura y escritura, usa la clave de lectura)" + } + } + } + }, + "options": { + "abort": { + "already_configured": "El dispositivo ya est\u00e1 configurado" + }, + "error": { + "invalid_api_key": "Clave API no v\u00e1lida", + "no_sensors_near_coordinates": "No se encontraron sensores cerca de las coordenadas (dentro de la distancia)", + "unknown": "Error inesperado" + }, + "step": { + "add_sensor": { + "data": { + "distance": "Radio de b\u00fasqueda", + "latitude": "Latitud", + "longitude": "Longitud" + }, + "data_description": { + "distance": "El radio (en kil\u00f3metros) del c\u00edrculo para buscar dentro", + "latitude": "La latitud alrededor de la cual buscar sensores", + "longitude": "La longitud alrededor de la cual buscar sensores" + }, + "description": "Buscar un sensor PurpleAir dentro de una cierta distancia de una latitud/longitud.", + "title": "A\u00f1adir sensor" + }, + "choose_sensor": { + "data": { + "sensor_index": "Sensor" + }, + "data_description": { + "sensor_index": "El sensor para rastrear" + }, + "description": "\u00bfCu\u00e1l de los sensores cercanos te gustar\u00eda rastrear?", + "title": "Elige el sensor para a\u00f1adir" + }, + "init": { + "menu_options": { + "add_sensor": "A\u00f1adir sensor", + "remove_sensor": "Eliminar sensor" + } + }, + "remove_sensor": { + "data": { + "sensor_device_id": "Nombre del sensor" + }, + "data_description": { + "sensor_device_id": "El sensor para eliminar" + }, + "title": "Eliminar Sensor" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/purpleair/translations/et.json b/homeassistant/components/purpleair/translations/et.json new file mode 100644 index 00000000000..ee6c954e4d5 --- /dev/null +++ b/homeassistant/components/purpleair/translations/et.json @@ -0,0 +1,104 @@ +{ + "config": { + "abort": { + "already_configured": "Seade on juba h\u00e4\u00e4lestatud", + "reauth_successful": "Taastuvastamine \u00f5nnestus" + }, + "error": { + "invalid_api_key": "Kehtetu API v\u00f5ti", + "no_sensors_near_coordinates": "Koordinaatide l\u00e4heduses (kauguses) andureid ei leitud", + "unknown": "Ootamatu t\u00f5rge" + }, + "step": { + "by_coordinates": { + "data": { + "distance": "Otsingu raadius", + "latitude": "Laiuskraad", + "longitude": "Pikkuskraad" + }, + "data_description": { + "distance": "Otsinguringi raadius (kilomeetrites).", + "latitude": "Laiuskraad, mille \u00fcmber andureid otsida", + "longitude": "Pikkuskraad, mille \u00fcmber andureid otsida" + }, + "description": "Otsi PurpleAir-andurit teatud laiuskraadi/pikkuse piires." + }, + "choose_sensor": { + "data": { + "sensor_index": "Andur" + }, + "data_description": { + "sensor_index": "Andur mida j\u00e4lgida" + }, + "description": "Milliseid l\u00e4heduses olevaid andureid soovid j\u00e4lgida?" + }, + "reauth_confirm": { + "data": { + "api_key": "API v\u00f5ti" + }, + "data_description": { + "api_key": "PurpleAir API v\u00f5ti (kui on nii lugemis- kui ka kirjutamisv\u00f5ti, kasuta lugemisv\u00f5ti)." + } + }, + "user": { + "data": { + "api_key": "API v\u00f5ti" + }, + "data_description": { + "api_key": "PurpleAir API v\u00f5ti (kui on nii lugemis- kui ka kirjutamisv\u00f5ti, kasuta lugemisv\u00f5ti)." + } + } + } + }, + "options": { + "abort": { + "already_configured": "Seade on juba h\u00e4\u00e4lestatud" + }, + "error": { + "invalid_api_key": "Vale API v\u00f5ti", + "no_sensors_near_coordinates": "Koordinaatide l\u00e4heduses (kauguses) andureid ei leitud", + "unknown": "Ootamatu t\u00f5rge" + }, + "step": { + "add_sensor": { + "data": { + "distance": "Otsingu raadius", + "latitude": "Laiuskraad", + "longitude": "Pikkuskraad" + }, + "data_description": { + "distance": "Otsinguringi raadius (kilomeetrites).", + "latitude": "Laiuskraad, mille \u00fcmber andureid otsida", + "longitude": "Pikkuskraad, mille \u00fcmber andureid otsida" + }, + "description": "Otsi PurpleAir-andurit teatud laiuskraadi/pikkuse piires.", + "title": "Lisa andur" + }, + "choose_sensor": { + "data": { + "sensor_index": "Andur" + }, + "data_description": { + "sensor_index": "Andur mida j\u00e4lgida" + }, + "description": "Milliseid l\u00e4heduses olevaid andureid soovid j\u00e4lgida?", + "title": "Vali lisatav andur" + }, + "init": { + "menu_options": { + "add_sensor": "Lisa andur", + "remove_sensor": "Eemalda andur" + } + }, + "remove_sensor": { + "data": { + "sensor_device_id": "Anduri nimi" + }, + "data_description": { + "sensor_device_id": "Eemaldatav andur" + }, + "title": "Eemalda andur" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/purpleair/translations/he.json b/homeassistant/components/purpleair/translations/he.json new file mode 100644 index 00000000000..2dcdd574c2b --- /dev/null +++ b/homeassistant/components/purpleair/translations/he.json @@ -0,0 +1,36 @@ +{ + "config": { + "abort": { + "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", + "reauth_successful": "\u05d4\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05d4\u05e6\u05dc\u05d9\u05d7" + }, + "error": { + "invalid_api_key": "\u05de\u05e4\u05ea\u05d7 API \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9", + "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" + }, + "step": { + "by_coordinates": { + "data": { + "latitude": "\u05e7\u05d5 \u05e8\u05d5\u05d7\u05d1", + "longitude": "\u05e7\u05d5 \u05d0\u05d5\u05e8\u05da" + } + }, + "reauth_confirm": { + "data": { + "api_key": "\u05de\u05e4\u05ea\u05d7 API" + }, + "data_description": { + "api_key": "\u05de\u05e4\u05ea\u05d7 \u05d4-API \u05e9\u05dc PurpleAir (\u05d0\u05dd \u05d9\u05e9 \u05dc\u05da \u05de\u05e7\u05e9\u05d9 \u05e7\u05e8\u05d9\u05d0\u05d4 \u05d5\u05db\u05ea\u05d9\u05d1\u05d4, \u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05de\u05e7\u05e9 \u05d4\u05e7\u05e8\u05d9\u05d0\u05d4)" + } + }, + "user": { + "data": { + "api_key": "\u05de\u05e4\u05ea\u05d7 API" + }, + "data_description": { + "api_key": "\u05de\u05e4\u05ea\u05d7 \u05d4-API \u05e9\u05dc PurpleAir (\u05d0\u05dd \u05d9\u05e9 \u05dc\u05da \u05de\u05e7\u05e9\u05d9 \u05e7\u05e8\u05d9\u05d0\u05d4 \u05d5\u05db\u05ea\u05d9\u05d1\u05d4, \u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05de\u05e7\u05e9 \u05d4\u05e7\u05e8\u05d9\u05d0\u05d4)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/purpleair/translations/hu.json b/homeassistant/components/purpleair/translations/hu.json new file mode 100644 index 00000000000..9625181051c --- /dev/null +++ b/homeassistant/components/purpleair/translations/hu.json @@ -0,0 +1,104 @@ +{ + "config": { + "abort": { + "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", + "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt." + }, + "error": { + "invalid_api_key": "\u00c9rv\u00e9nytelen API kulcs", + "no_sensors_near_coordinates": "Nem tal\u00e1lhat\u00f3 \u00e9rz\u00e9kel\u0151 a koordin\u00e1t\u00e1k k\u00f6zel\u00e9ben (t\u00e1vols\u00e1gon bel\u00fcl)", + "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" + }, + "step": { + "by_coordinates": { + "data": { + "distance": "Keres\u00e9s hat\u00f3sugara", + "latitude": "Sz\u00e9less\u00e9g", + "longitude": "Hossz\u00fas\u00e1g" + }, + "data_description": { + "distance": "A keresend\u0151 k\u00f6r sugara (kilom\u00e9terben)", + "latitude": "A sz\u00e9less\u00e9g, amely k\u00f6r\u00fcl \u00e9rz\u00e9kel\u0151ket kell keresni", + "longitude": "A hossz\u00fas\u00e1g, amely k\u00f6r\u00fcl \u00e9rz\u00e9kel\u0151ket kell keresni" + }, + "description": "PurpleAir-\u00e9rz\u00e9kel\u0151k keres\u00e9se egy koordin\u00e1ta meghat\u00e1rozott t\u00e1vols\u00e1g\u00e1n bel\u00fcl." + }, + "choose_sensor": { + "data": { + "sensor_index": "\u00c9rz\u00e9kel\u0151" + }, + "data_description": { + "sensor_index": "A nyomon k\u00f6vetni k\u00edv\u00e1nt \u00e9rz\u00e9kel\u0151" + }, + "description": "Melyik k\u00f6zeli \u00e9rz\u00e9kel\u0151t szeretn\u00e9 nyomon k\u00f6vetni?" + }, + "reauth_confirm": { + "data": { + "api_key": "API kulcs" + }, + "data_description": { + "api_key": "A PurpleAir API-kulcs (ha olvas\u00e1si \u00e9s \u00edr\u00e1si kulcsokkal is rendelkezik, haszn\u00e1lja az olvas\u00e1si kulcsot)" + } + }, + "user": { + "data": { + "api_key": "API kulcs" + }, + "data_description": { + "api_key": "A PurpleAir API-kulcs (ha olvas\u00e1si \u00e9s \u00edr\u00e1si kulcsokkal is rendelkezik, haszn\u00e1lja az olvas\u00e1si kulcsot)" + } + } + } + }, + "options": { + "abort": { + "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van" + }, + "error": { + "invalid_api_key": "\u00c9rv\u00e9nytelen API kulcs", + "no_sensors_near_coordinates": "Nem tal\u00e1lhat\u00f3 \u00e9rz\u00e9kel\u0151 a koordin\u00e1t\u00e1k k\u00f6zel\u00e9ben (t\u00e1vols\u00e1gon bel\u00fcl)", + "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" + }, + "step": { + "add_sensor": { + "data": { + "distance": "Keres\u00e9s hat\u00f3sugara", + "latitude": "Sz\u00e9less\u00e9g", + "longitude": "Hossz\u00fas\u00e1g" + }, + "data_description": { + "distance": "A keresend\u0151 k\u00f6r sugara (kilom\u00e9terben)", + "latitude": "A sz\u00e9less\u00e9g, amely k\u00f6r\u00fcl \u00e9rz\u00e9kel\u0151ket kell keresni", + "longitude": "A hossz\u00fas\u00e1g, amely k\u00f6r\u00fcl \u00e9rz\u00e9kel\u0151ket kell keresni" + }, + "description": "PurpleAir-\u00e9rz\u00e9kel\u0151k keres\u00e9se egy koordin\u00e1ta meghat\u00e1rozott t\u00e1vols\u00e1g\u00e1n bel\u00fcl.", + "title": "\u00c9rz\u00e9kel\u0151 hozz\u00e1ad\u00e1sa" + }, + "choose_sensor": { + "data": { + "sensor_index": "\u00c9rz\u00e9kel\u0151" + }, + "data_description": { + "sensor_index": "A nyomon k\u00f6vetni k\u00edv\u00e1nt \u00e9rz\u00e9kel\u0151" + }, + "description": "Melyik k\u00f6zeli \u00e9rz\u00e9kel\u0151t szeretn\u00e9 nyomon k\u00f6vetni?", + "title": "V\u00e1lassza ki a hozz\u00e1adand\u00f3 \u00e9rz\u00e9kel\u0151t" + }, + "init": { + "menu_options": { + "add_sensor": "\u00c9rz\u00e9kel\u0151 hozz\u00e1ad\u00e1sa", + "remove_sensor": "\u00c9rz\u00e9kel\u0151 elt\u00e1vol\u00edt\u00e1sa" + } + }, + "remove_sensor": { + "data": { + "sensor_device_id": "\u00c9rz\u00e9kel\u0151 elnevez\u00e9se" + }, + "data_description": { + "sensor_device_id": "Az elt\u00e1vol\u00edtand\u00f3 \u00e9rz\u00e9kel\u0151" + }, + "title": "\u00c9rz\u00e9kel\u0151 elt\u00e1vol\u00edt\u00e1sa" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/purpleair/translations/id.json b/homeassistant/components/purpleair/translations/id.json new file mode 100644 index 00000000000..c12ca7e649b --- /dev/null +++ b/homeassistant/components/purpleair/translations/id.json @@ -0,0 +1,104 @@ +{ + "config": { + "abort": { + "already_configured": "Perangkat sudah dikonfigurasi", + "reauth_successful": "Autentikasi ulang berhasil" + }, + "error": { + "invalid_api_key": "Kunci API tidak valid", + "no_sensors_near_coordinates": "Tidak ada sensor yang ditemukan di dekat koordinat (dalam jarak yang ditentukan)", + "unknown": "Kesalahan yang tidak diharapkan" + }, + "step": { + "by_coordinates": { + "data": { + "distance": "Radius Pencarian", + "latitude": "Lintang", + "longitude": "Bujur" + }, + "data_description": { + "distance": "Jari-jari (dalam kilometer) lingkaran untuk mencari di dalam", + "latitude": "Garis lintang untuk mencari sensor", + "longitude": "Garis bujur untuk mencari sensor" + }, + "description": "Cari sensor PurpleAir dalam jarak tertentu dari garis lintang/bujur." + }, + "choose_sensor": { + "data": { + "sensor_index": "Sensor" + }, + "data_description": { + "sensor_index": "Sensor yang dilacak" + }, + "description": "Manakah dari sensor terdekat yang ingin dilacak?" + }, + "reauth_confirm": { + "data": { + "api_key": "Kunci API" + }, + "data_description": { + "api_key": "Kunci API PurpleAir Anda (jika tersedia kunci baca dan tulis, gunakan kunci baca)" + } + }, + "user": { + "data": { + "api_key": "Kunci API" + }, + "data_description": { + "api_key": "Kunci API PurpleAir Anda (jika tersedia kunci baca dan tulis, gunakan kunci baca)" + } + } + } + }, + "options": { + "abort": { + "already_configured": "Perangkat sudah dikonfigurasi" + }, + "error": { + "invalid_api_key": "Kunci API tidak valid", + "no_sensors_near_coordinates": "Tidak ada sensor yang ditemukan di dekat koordinat (dalam jarak yang ditentukan)", + "unknown": "Kesalahan yang tidak diharapkan" + }, + "step": { + "add_sensor": { + "data": { + "distance": "Radius Pencarian", + "latitude": "Lintang", + "longitude": "Bujur" + }, + "data_description": { + "distance": "Jari-jari (dalam kilometer) lingkaran untuk mencari di dalam", + "latitude": "Garis lintang untuk mencari sensor", + "longitude": "Garis bujur untuk mencari sensor" + }, + "description": "Cari sensor PurpleAir dalam jarak tertentu dari garis lintang/bujur.", + "title": "Tambahkan Sensor" + }, + "choose_sensor": { + "data": { + "sensor_index": "Sensor" + }, + "data_description": { + "sensor_index": "Sensor yang dilacak" + }, + "description": "Manakah dari sensor terdekat yang ingin dilacak?", + "title": "Pilih Sensor untuk Ditambahkan" + }, + "init": { + "menu_options": { + "add_sensor": "Tambahkan sensor", + "remove_sensor": "Hapus sensor" + } + }, + "remove_sensor": { + "data": { + "sensor_device_id": "Nama Sensor" + }, + "data_description": { + "sensor_device_id": "Sensor untuk dihapus" + }, + "title": "Hapus Sensor" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/purpleair/translations/it.json b/homeassistant/components/purpleair/translations/it.json new file mode 100644 index 00000000000..2156d4d839b --- /dev/null +++ b/homeassistant/components/purpleair/translations/it.json @@ -0,0 +1,104 @@ +{ + "config": { + "abort": { + "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato", + "reauth_successful": "La nuova autenticazione \u00e8 stata eseguita correttamente" + }, + "error": { + "invalid_api_key": "Chiave API non valida", + "no_sensors_near_coordinates": "Nessun sensore trovato vicino alle coordinate (entro la distanza)", + "unknown": "Errore imprevisto" + }, + "step": { + "by_coordinates": { + "data": { + "distance": "Raggio di ricerca", + "latitude": "Latitudine", + "longitude": "Logitudine" + }, + "data_description": { + "distance": "Il raggio (in chilometri) del cerchio in cui eseguire la ricerca", + "latitude": "La latitudine attorno alla quale cercare i sensori", + "longitude": "La longitudine attorno alla quale cercare i sensori" + }, + "description": "Cerca un sensore PurpleAir entro una certa distanza da una latitudine/longitudine." + }, + "choose_sensor": { + "data": { + "sensor_index": "Sensore" + }, + "data_description": { + "sensor_index": "Il sensore da monitorare" + }, + "description": "Quale dei sensori nelle vicinanze vorresti monitorare?" + }, + "reauth_confirm": { + "data": { + "api_key": "Chiave API" + }, + "data_description": { + "api_key": "La tua chiave API PurpleAir (se hai sia la chiave di lettura che quella di scrittura, usa la chiave di lettura)" + } + }, + "user": { + "data": { + "api_key": "Chiave API" + }, + "data_description": { + "api_key": "La tua chiave API PurpleAir (se hai sia la chiave di lettura che quella di scrittura, usa la chiave di lettura)" + } + } + } + }, + "options": { + "abort": { + "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato" + }, + "error": { + "invalid_api_key": "Chiave API non valida", + "no_sensors_near_coordinates": "Nessun sensore trovato vicino alle coordinate (entro la distanza)", + "unknown": "Errore imprevisto" + }, + "step": { + "add_sensor": { + "data": { + "distance": "Raggio di ricerca", + "latitude": "Latitudine", + "longitude": "Longitudine" + }, + "data_description": { + "distance": "Il raggio (in chilometri) del cerchio in cui eseguire la ricerca", + "latitude": "La latitudine attorno alla quale cercare i sensori", + "longitude": "La longitudine attorno alla quale cercare i sensori" + }, + "description": "Cerca un sensore PurpleAir entro una certa distanza da una latitudine/longitudine.", + "title": "Aggiungi sensore" + }, + "choose_sensor": { + "data": { + "sensor_index": "Sensore" + }, + "data_description": { + "sensor_index": "Il sensore da monitorare" + }, + "description": "Quale dei sensori nelle vicinanze vorresti monitorare?", + "title": "Scegli il sensore da aggiungere" + }, + "init": { + "menu_options": { + "add_sensor": "Aggiungi sensore", + "remove_sensor": "Rimuovere il sensore" + } + }, + "remove_sensor": { + "data": { + "sensor_device_id": "Nome sensore" + }, + "data_description": { + "sensor_device_id": "Il sensore da rimuovere" + }, + "title": "Rimuovi sensore" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/purpleair/translations/no.json b/homeassistant/components/purpleair/translations/no.json new file mode 100644 index 00000000000..d8727722e45 --- /dev/null +++ b/homeassistant/components/purpleair/translations/no.json @@ -0,0 +1,104 @@ +{ + "config": { + "abort": { + "already_configured": "Enheten er allerede konfigurert", + "reauth_successful": "Re-autentisering var vellykket" + }, + "error": { + "invalid_api_key": "Ugyldig API-n\u00f8kkel", + "no_sensors_near_coordinates": "Ingen sensorer funnet i n\u00e6rheten av koordinater (innenfor avstand)", + "unknown": "Uventet feil" + }, + "step": { + "by_coordinates": { + "data": { + "distance": "S\u00f8k radius", + "latitude": "Breddegrad", + "longitude": "Lengdegrad" + }, + "data_description": { + "distance": "Radiusen (i kilometer) av sirkelen som skal s\u00f8kes innenfor", + "latitude": "Breddegraden for \u00e5 s\u00f8ke etter sensorer", + "longitude": "Lengdegraden for \u00e5 s\u00f8ke etter sensorer" + }, + "description": "S\u00f8k etter en PurpleAir-sensor innenfor en viss avstand fra en breddegrad/lengdegrad." + }, + "choose_sensor": { + "data": { + "sensor_index": "Sensor" + }, + "data_description": { + "sensor_index": "Sensoren \u00e5 spore" + }, + "description": "Hvilken av sensorene i n\u00e6rheten vil du spore?" + }, + "reauth_confirm": { + "data": { + "api_key": "API-n\u00f8kkel" + }, + "data_description": { + "api_key": "PurpleAir API-n\u00f8kkelen din (hvis du har b\u00e5de lese- og skriven\u00f8kler, bruk lesen\u00f8kkelen)" + } + }, + "user": { + "data": { + "api_key": "API-n\u00f8kkel" + }, + "data_description": { + "api_key": "PurpleAir API-n\u00f8kkelen din (hvis du har b\u00e5de lese- og skriven\u00f8kler, bruk lesen\u00f8kkelen)" + } + } + } + }, + "options": { + "abort": { + "already_configured": "Enheten er allerede konfigurert" + }, + "error": { + "invalid_api_key": "Ugyldig API-n\u00f8kkel", + "no_sensors_near_coordinates": "Ingen sensorer funnet i n\u00e6rheten av koordinater (innenfor avstand)", + "unknown": "Uventet feil" + }, + "step": { + "add_sensor": { + "data": { + "distance": "S\u00f8k radius", + "latitude": "Breddegrad", + "longitude": "Lengdegrad" + }, + "data_description": { + "distance": "Radiusen (i kilometer) av sirkelen som skal s\u00f8kes innenfor", + "latitude": "Breddegraden for \u00e5 s\u00f8ke etter sensorer", + "longitude": "Lengdegraden for \u00e5 s\u00f8ke etter sensorer" + }, + "description": "S\u00f8k etter en PurpleAir-sensor innenfor en viss avstand fra en breddegrad/lengdegrad.", + "title": "Legg til sensor" + }, + "choose_sensor": { + "data": { + "sensor_index": "Sensor" + }, + "data_description": { + "sensor_index": "Sensoren \u00e5 spore" + }, + "description": "Hvilken av sensorene i n\u00e6rheten vil du spore?", + "title": "Velg Sensor \u00e5 legge til" + }, + "init": { + "menu_options": { + "add_sensor": "Legg til sensor", + "remove_sensor": "Fjern sensoren" + } + }, + "remove_sensor": { + "data": { + "sensor_device_id": "Sensornavn" + }, + "data_description": { + "sensor_device_id": "Sensoren som skal fjernes" + }, + "title": "Fjern sensoren" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/purpleair/translations/pl.json b/homeassistant/components/purpleair/translations/pl.json new file mode 100644 index 00000000000..715b3e0bebd --- /dev/null +++ b/homeassistant/components/purpleair/translations/pl.json @@ -0,0 +1,104 @@ +{ + "config": { + "abort": { + "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane", + "reauth_successful": "Ponowne uwierzytelnienie powiod\u0142o si\u0119" + }, + "error": { + "invalid_api_key": "Nieprawid\u0142owy klucz API", + "no_sensors_near_coordinates": "Nie znaleziono sensor\u00f3w w pobli\u017cu wsp\u00f3\u0142rz\u0119dnych (w okre\u015blonej odleg\u0142o\u015bci)", + "unknown": "Nieoczekiwany b\u0142\u0105d" + }, + "step": { + "by_coordinates": { + "data": { + "distance": "Promie\u0144 wyszukiwania", + "latitude": "Szeroko\u015b\u0107 geograficzna", + "longitude": "D\u0142ugo\u015b\u0107 geograficzna" + }, + "data_description": { + "distance": "Promie\u0144 okr\u0119gu (w kilometrach) do przeszukania", + "latitude": "Szeroko\u015b\u0107 geograficzna, wok\u00f3\u0142 kt\u00f3rej nale\u017cy szuka\u0107 sensor\u00f3w", + "longitude": "D\u0142ugo\u015b\u0107 geograficzna, wok\u00f3\u0142 kt\u00f3rej nale\u017cy szuka\u0107 sensor\u00f3w" + }, + "description": "Wyszukaj sensory PurpleAir w okre\u015blonej odleg\u0142o\u015bci od szeroko\u015bci/d\u0142ugo\u015bci geograficznej." + }, + "choose_sensor": { + "data": { + "sensor_index": "Sensor" + }, + "data_description": { + "sensor_index": "Sensory do \u015bledzenia" + }, + "description": "Kt\u00f3re z pobliskich sensor\u00f3w chcesz \u015bledzi\u0107?" + }, + "reauth_confirm": { + "data": { + "api_key": "Klucz API" + }, + "data_description": { + "api_key": "Tw\u00f3j klucz API PurpleAir (je\u015bli masz zar\u00f3wno klucze do odczytu, jak i zapisu, u\u017cyj klucza do odczytu)" + } + }, + "user": { + "data": { + "api_key": "Klucz API" + }, + "data_description": { + "api_key": "Tw\u00f3j klucz API PurpleAir (je\u015bli masz zar\u00f3wno klucze do odczytu, jak i zapisu, u\u017cyj klucza do odczytu)" + } + } + } + }, + "options": { + "abort": { + "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane" + }, + "error": { + "invalid_api_key": "Nieprawid\u0142owy klucz API", + "no_sensors_near_coordinates": "Nie znaleziono sensor\u00f3w w pobli\u017cu wsp\u00f3\u0142rz\u0119dnych (w okre\u015blonej odleg\u0142o\u015bci)", + "unknown": "Nieoczekiwany b\u0142\u0105d" + }, + "step": { + "add_sensor": { + "data": { + "distance": "Promie\u0144 wyszukiwania", + "latitude": "Szeroko\u015b\u0107 geograficzna", + "longitude": "D\u0142ugo\u015b\u0107 geograficzna" + }, + "data_description": { + "distance": "Promie\u0144 okr\u0119gu (w kilometrach) do przeszukania", + "latitude": "Szeroko\u015b\u0107 geograficzna, wok\u00f3\u0142 kt\u00f3rej nale\u017cy szuka\u0107 sensor\u00f3w", + "longitude": "D\u0142ugo\u015b\u0107 geograficzna, wok\u00f3\u0142 kt\u00f3rej nale\u017cy szuka\u0107 sensor\u00f3w" + }, + "description": "Wyszukaj sensory PurpleAir w okre\u015blonej odleg\u0142o\u015bci od szeroko\u015bci/d\u0142ugo\u015bci geograficznej.", + "title": "Dodaj sensor" + }, + "choose_sensor": { + "data": { + "sensor_index": "Sensor" + }, + "data_description": { + "sensor_index": "Sensory do \u015bledzenia" + }, + "description": "Kt\u00f3re z pobliskich sensor\u00f3w chcesz \u015bledzi\u0107?", + "title": "Wybierz sensor do dodania" + }, + "init": { + "menu_options": { + "add_sensor": "Dodaj sensor", + "remove_sensor": "Usu\u0144 sensor" + } + }, + "remove_sensor": { + "data": { + "sensor_device_id": "Nazwa sensora" + }, + "data_description": { + "sensor_device_id": "Sensor do usuni\u0119cia" + }, + "title": "Usu\u0144 sensor" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/purpleair/translations/pt-BR.json b/homeassistant/components/purpleair/translations/pt-BR.json new file mode 100644 index 00000000000..ab636a6192b --- /dev/null +++ b/homeassistant/components/purpleair/translations/pt-BR.json @@ -0,0 +1,104 @@ +{ + "config": { + "abort": { + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", + "reauth_successful": "A reautentica\u00e7\u00e3o foi bem-sucedida" + }, + "error": { + "invalid_api_key": "Chave de API inv\u00e1lida", + "no_sensors_near_coordinates": "Nenhum sensor encontrado perto das coordenadas (dentro da dist\u00e2ncia)", + "unknown": "Erro inesperado" + }, + "step": { + "by_coordinates": { + "data": { + "distance": "Raio de pesquisa", + "latitude": "Latitude", + "longitude": "Longitude" + }, + "data_description": { + "distance": "O raio (em quil\u00f4metros) do c\u00edrculo a ser pesquisado", + "latitude": "A latitude em torno da qual procurar por sensores", + "longitude": "A longitude em torno da qual procurar por sensores" + }, + "description": "Procure um sensor PurpleAir dentro de uma certa dist\u00e2ncia de uma latitude/longitude." + }, + "choose_sensor": { + "data": { + "sensor_index": "Sensor" + }, + "data_description": { + "sensor_index": "O sensor para rastrear" + }, + "description": "Qual dos sensores pr\u00f3ximos voc\u00ea gostaria de rastrear?" + }, + "reauth_confirm": { + "data": { + "api_key": "Chave de API" + }, + "data_description": { + "api_key": "Sua chave de API PurpleAir (se voc\u00ea tiver chaves de leitura e grava\u00e7\u00e3o, use a chave de leitura)" + } + }, + "user": { + "data": { + "api_key": "Chave de API" + }, + "data_description": { + "api_key": "Sua chave de API PurpleAir (se voc\u00ea tiver chaves de leitura e grava\u00e7\u00e3o, use a chave de leitura)" + } + } + } + }, + "options": { + "abort": { + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" + }, + "error": { + "invalid_api_key": "Chave de API inv\u00e1lida", + "no_sensors_near_coordinates": "Nenhum sensor encontrado perto das coordenadas (dentro da dist\u00e2ncia)", + "unknown": "Erro inesperado" + }, + "step": { + "add_sensor": { + "data": { + "distance": "Raio de pesquisa", + "latitude": "Latitude", + "longitude": "Longitude" + }, + "data_description": { + "distance": "O raio (em quil\u00f4metros) do c\u00edrculo a ser pesquisado", + "latitude": "A latitude em torno da qual procurar por sensores", + "longitude": "A longitude em torno da qual procurar por sensores" + }, + "description": "Procure um sensor PurpleAir dentro de uma certa dist\u00e2ncia de uma latitude/longitude.", + "title": "Adicionar sensor" + }, + "choose_sensor": { + "data": { + "sensor_index": "Sensor" + }, + "data_description": { + "sensor_index": "O sensor para rastrear" + }, + "description": "Qual dos sensores pr\u00f3ximos voc\u00ea gostaria de rastrear?", + "title": "Escolha o sensor para adicionar" + }, + "init": { + "menu_options": { + "add_sensor": "Adicionar sensor", + "remove_sensor": "Remover sensor" + } + }, + "remove_sensor": { + "data": { + "sensor_device_id": "Nome do sensor" + }, + "data_description": { + "sensor_device_id": "O sensor a ser removido" + }, + "title": "Remover sensor" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/purpleair/translations/ru.json b/homeassistant/components/purpleair/translations/ru.json new file mode 100644 index 00000000000..cf410fd47c2 --- /dev/null +++ b/homeassistant/components/purpleair/translations/ru.json @@ -0,0 +1,104 @@ +{ + "config": { + "abort": { + "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant.", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e." + }, + "error": { + "invalid_api_key": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 API.", + "no_sensors_near_coordinates": "\u0420\u044f\u0434\u043e\u043c \u0441 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u043c\u0438 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0430\u043c\u0438 \u0434\u0430\u0442\u0447\u0438\u043a\u0438 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b.", + "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." + }, + "step": { + "by_coordinates": { + "data": { + "distance": "\u0420\u0430\u0434\u0438\u0443\u0441 \u043f\u043e\u0438\u0441\u043a\u0430", + "latitude": "\u0428\u0438\u0440\u043e\u0442\u0430", + "longitude": "\u0414\u043e\u043b\u0433\u043e\u0442\u0430" + }, + "data_description": { + "distance": "\u0420\u0430\u0434\u0438\u0443\u0441 \u043a\u0440\u0443\u0433\u0430 \u0434\u043b\u044f \u043f\u043e\u0438\u0441\u043a\u0430 (\u0432 \u043a\u0438\u043b\u043e\u043c\u0435\u0442\u0440\u0430\u0445)", + "latitude": "\u0428\u0438\u0440\u043e\u0442\u0430, \u0432\u043e\u043a\u0440\u0443\u0433 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0438\u0441\u043a\u0430\u0442\u044c \u0434\u0430\u0442\u0447\u0438\u043a\u0438", + "longitude": "\u0414\u043e\u043b\u0433\u043e\u0442\u0430, \u0432\u043e\u043a\u0440\u0443\u0433 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0438\u0441\u043a\u0430\u0442\u044c \u0434\u0430\u0442\u0447\u0438\u043a\u0438" + }, + "description": "\u041f\u043e\u0438\u0441\u043a \u0434\u0430\u0442\u0447\u0438\u043a\u0430 PurpleAir \u0432 \u043f\u0440\u0435\u0434\u0435\u043b\u0430\u0445 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u0433\u043e \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u043e\u0442 \u0448\u0438\u0440\u043e\u0442\u044b/\u0434\u043e\u043b\u0433\u043e\u0442\u044b." + }, + "choose_sensor": { + "data": { + "sensor_index": "\u0414\u0430\u0442\u0447\u0438\u043a" + }, + "data_description": { + "sensor_index": "\u0414\u0430\u0442\u0447\u0438\u043a \u0434\u043b\u044f \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u044f" + }, + "description": "\u041a\u0430\u043a\u0438\u0435 \u0438\u0437 \u0431\u043b\u0438\u0436\u0430\u0439\u0448\u0438\u0445 \u0434\u0430\u0442\u0447\u0438\u043a\u043e\u0432 \u0412\u044b \u0445\u043e\u0442\u0435\u043b\u0438 \u0431\u044b \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0442\u044c?" + }, + "reauth_confirm": { + "data": { + "api_key": "\u041a\u043b\u044e\u0447 API" + }, + "data_description": { + "api_key": "\u0412\u0430\u0448 \u043a\u043b\u044e\u0447 API PurpleAir (\u0435\u0441\u043b\u0438 \u0443 \u0412\u0430\u0441 \u0435\u0441\u0442\u044c \u043a\u043b\u044e\u0447\u0438 \u0447\u0442\u0435\u043d\u0438\u044f \u0438 \u0437\u0430\u043f\u0438\u0441\u0438, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u043a\u043b\u044e\u0447 \u0447\u0442\u0435\u043d\u0438\u044f)" + } + }, + "user": { + "data": { + "api_key": "\u041a\u043b\u044e\u0447 API" + }, + "data_description": { + "api_key": "\u0412\u0430\u0448 \u043a\u043b\u044e\u0447 API PurpleAir (\u0435\u0441\u043b\u0438 \u0443 \u0412\u0430\u0441 \u0435\u0441\u0442\u044c \u043a\u043b\u044e\u0447\u0438 \u0447\u0442\u0435\u043d\u0438\u044f \u0438 \u0437\u0430\u043f\u0438\u0441\u0438, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u043a\u043b\u044e\u0447 \u0447\u0442\u0435\u043d\u0438\u044f)" + } + } + } + }, + "options": { + "abort": { + "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant." + }, + "error": { + "invalid_api_key": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 API.", + "no_sensors_near_coordinates": "\u0420\u044f\u0434\u043e\u043c \u0441 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u043c\u0438 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0430\u043c\u0438 \u0434\u0430\u0442\u0447\u0438\u043a\u0438 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b.", + "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." + }, + "step": { + "add_sensor": { + "data": { + "distance": "\u0420\u0430\u0434\u0438\u0443\u0441 \u043f\u043e\u0438\u0441\u043a\u0430", + "latitude": "\u0428\u0438\u0440\u043e\u0442\u0430", + "longitude": "\u0414\u043e\u043b\u0433\u043e\u0442\u0430" + }, + "data_description": { + "distance": "\u0420\u0430\u0434\u0438\u0443\u0441 \u043a\u0440\u0443\u0433\u0430 \u0434\u043b\u044f \u043f\u043e\u0438\u0441\u043a\u0430 (\u0432 \u043a\u0438\u043b\u043e\u043c\u0435\u0442\u0440\u0430\u0445)", + "latitude": "\u0428\u0438\u0440\u043e\u0442\u0430, \u0432\u043e\u043a\u0440\u0443\u0433 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0438\u0441\u043a\u0430\u0442\u044c \u0434\u0430\u0442\u0447\u0438\u043a\u0438", + "longitude": "\u0414\u043e\u043b\u0433\u043e\u0442\u0430, \u0432\u043e\u043a\u0440\u0443\u0433 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0438\u0441\u043a\u0430\u0442\u044c \u0434\u0430\u0442\u0447\u0438\u043a\u0438" + }, + "description": "\u041f\u043e\u0438\u0441\u043a \u0434\u0430\u0442\u0447\u0438\u043a\u0430 PurpleAir \u0432 \u043f\u0440\u0435\u0434\u0435\u043b\u0430\u0445 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u0433\u043e \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u043e\u0442 \u0448\u0438\u0440\u043e\u0442\u044b/\u0434\u043e\u043b\u0433\u043e\u0442\u044b.", + "title": "\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0434\u0430\u0442\u0447\u0438\u043a" + }, + "choose_sensor": { + "data": { + "sensor_index": "\u0414\u0430\u0442\u0447\u0438\u043a" + }, + "data_description": { + "sensor_index": "\u0414\u0430\u0442\u0447\u0438\u043a \u0434\u043b\u044f \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u044f" + }, + "description": "\u041a\u0430\u043a\u0438\u0435 \u0438\u0437 \u0431\u043b\u0438\u0436\u0430\u0439\u0448\u0438\u0445 \u0434\u0430\u0442\u0447\u0438\u043a\u043e\u0432 \u0412\u044b \u0445\u043e\u0442\u0435\u043b\u0438 \u0431\u044b \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0442\u044c?", + "title": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0434\u0430\u0442\u0447\u0438\u043a" + }, + "init": { + "menu_options": { + "add_sensor": "\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0434\u0430\u0442\u0447\u0438\u043a", + "remove_sensor": "\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0434\u0430\u0442\u0447\u0438\u043a" + } + }, + "remove_sensor": { + "data": { + "sensor_device_id": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0434\u0430\u0442\u0447\u0438\u043a\u0430" + }, + "data_description": { + "sensor_device_id": "\u0414\u0430\u0442\u0447\u0438\u043a, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0443\u0436\u043d\u043e \u0443\u0434\u0430\u043b\u0438\u0442\u044c" + }, + "title": "\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0434\u0430\u0442\u0447\u0438\u043a" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/purpleair/translations/sk.json b/homeassistant/components/purpleair/translations/sk.json new file mode 100644 index 00000000000..71ea14fb3ad --- /dev/null +++ b/homeassistant/components/purpleair/translations/sk.json @@ -0,0 +1,102 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" + }, + "error": { + "invalid_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d", + "no_sensors_near_coordinates": "V bl\u00edzkosti s\u00faradn\u00edc (v r\u00e1mci vzdialenosti) sa nena\u0161li \u017eiadne sn\u00edma\u010de", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "step": { + "by_coordinates": { + "data": { + "distance": "Polomer vyh\u013ead\u00e1vania", + "latitude": "Zemepisn\u00e1 \u0161\u00edrka", + "longitude": "Zemepisn\u00e1 d\u013a\u017eka" + }, + "data_description": { + "distance": "Polomer (v kilometroch) kruhu, v ktorom sa m\u00e1 vyh\u013ead\u00e1va\u0165", + "latitude": "Zemepisn\u00e1 \u0161\u00edrka, v ktorej sa maj\u00fa vyh\u013ead\u00e1va\u0165 sn\u00edma\u010de", + "longitude": "Zemepisn\u00e1 d\u013a\u017eka, okolo ktorej sa maj\u00fa vyh\u013ead\u00e1va\u0165 sn\u00edma\u010de" + }, + "description": "Vyh\u013eadajte sn\u00edma\u010d PurpleAir v ur\u010ditej vzdialenosti zemepisnej \u0161\u00edrky/d\u013a\u017eky." + }, + "choose_sensor": { + "data": { + "sensor_index": "Sn\u00edma\u010d" + }, + "data_description": { + "sensor_index": "Sn\u00edma\u010d na sledovanie" + }, + "description": "Ktor\u00fd zo sn\u00edma\u010dov v okol\u00ed chcete sledova\u0165?" + }, + "reauth_confirm": { + "data": { + "api_key": "API k\u013e\u00fa\u010d" + }, + "data_description": { + "api_key": "V\u00e1\u0161 k\u013e\u00fa\u010d API PurpleAir (ak m\u00e1te k\u013e\u00fa\u010de na \u010d\u00edtanie aj z\u00e1pis, pou\u017eite k\u013e\u00fa\u010d na \u010d\u00edtanie)" + } + }, + "user": { + "data": { + "api_key": "API k\u013e\u00fa\u010d" + }, + "data_description": { + "api_key": "V\u00e1\u0161 k\u013e\u00fa\u010d API PurpleAir (ak m\u00e1te k\u013e\u00fa\u010de na \u010d\u00edtanie aj z\u00e1pis, pou\u017eite k\u013e\u00fa\u010d na \u010d\u00edtanie)" + } + } + } + }, + "options": { + "abort": { + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + }, + "error": { + "invalid_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d", + "no_sensors_near_coordinates": "V bl\u00edzkosti s\u00faradn\u00edc (v r\u00e1mci vzdialenosti) sa nena\u0161li \u017eiadne sn\u00edma\u010de", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "step": { + "add_sensor": { + "data": { + "distance": "Polomer vyh\u013ead\u00e1vania" + }, + "data_description": { + "distance": "Polomer (v kilometroch) kruhu, v ktorom sa m\u00e1 vyh\u013ead\u00e1va\u0165", + "latitude": "Zemepisn\u00e1 \u0161\u00edrka, v ktorej sa maj\u00fa vyh\u013ead\u00e1va\u0165 sn\u00edma\u010de", + "longitude": "Zemepisn\u00e1 d\u013a\u017eka, okolo ktorej sa maj\u00fa vyh\u013ead\u00e1va\u0165 sn\u00edma\u010de" + }, + "description": "Vyh\u013eadajte sn\u00edma\u010d PurpleAir v ur\u010ditej vzdialenosti zemepisnej \u0161\u00edrky/d\u013a\u017eky.", + "title": "Prida\u0165 sn\u00edma\u010d" + }, + "choose_sensor": { + "data": { + "sensor_index": "Sn\u00edma\u010d" + }, + "data_description": { + "sensor_index": "Sn\u00edma\u010d na sledovanie" + }, + "description": "Ktor\u00fd zo sn\u00edma\u010dov v okol\u00ed chcete sledova\u0165?", + "title": "Vyberte sn\u00edma\u010d, ktor\u00fd chcete prida\u0165" + }, + "init": { + "menu_options": { + "add_sensor": "Pridanie sn\u00edma\u010da", + "remove_sensor": "Odstr\u00e1nenie sn\u00edma\u010da" + } + }, + "remove_sensor": { + "data": { + "sensor_device_id": "N\u00e1zov sn\u00edma\u010da" + }, + "data_description": { + "sensor_device_id": "Sn\u00edma\u010d na odstr\u00e1nenie" + }, + "title": "Odstr\u00e1nenie sn\u00edma\u010da" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/purpleair/translations/sv.json b/homeassistant/components/purpleair/translations/sv.json new file mode 100644 index 00000000000..61d53cf5e6d --- /dev/null +++ b/homeassistant/components/purpleair/translations/sv.json @@ -0,0 +1,39 @@ +{ + "config": { + "step": { + "choose_sensor": { + "data": { + "sensor_index": "Sensor" + } + } + } + }, + "options": { + "step": { + "add_sensor": { + "title": "L\u00e4gg till sensor" + }, + "choose_sensor": { + "data": { + "sensor_index": "Sensor" + }, + "title": "V\u00e4lj sensor att l\u00e4gga till" + }, + "init": { + "menu_options": { + "add_sensor": "L\u00e4gg till sensor", + "remove_sensor": "Ta bort sensor" + } + }, + "remove_sensor": { + "data": { + "sensor_device_id": "Sensor namn" + }, + "data_description": { + "sensor_device_id": "V\u00e4lj sensor att ta bort" + }, + "title": "Ta bort sensor" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/purpleair/translations/zh-Hant.json b/homeassistant/components/purpleair/translations/zh-Hant.json new file mode 100644 index 00000000000..68a0d2211d5 --- /dev/null +++ b/homeassistant/components/purpleair/translations/zh-Hant.json @@ -0,0 +1,104 @@ +{ + "config": { + "abort": { + "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", + "reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f" + }, + "error": { + "invalid_api_key": "API \u91d1\u9470\u7121\u6548", + "no_sensors_near_coordinates": "\u5ea7\u6a19\u9644\u8fd1\uff08\u8ddd\u96e2\u5167\uff09\u627e\u4e0d\u5230\u611f\u6e2c\u5668", + "unknown": "\u672a\u9810\u671f\u932f\u8aa4" + }, + "step": { + "by_coordinates": { + "data": { + "distance": "\u641c\u5c0b\u534a\u5f91", + "latitude": "\u7def\u5ea6", + "longitude": "\u7d93\u5ea6" + }, + "data_description": { + "distance": "\u641c\u5c0b\u7684\u5713\u5708\u534a\u5f91\uff08\u516c\u91cc\uff09", + "latitude": "\u641c\u5c0b\u611f\u6e2c\u5668\u7684\u7def\u5ea6\u503c", + "longitude": "\u641c\u5c0b\u611f\u6e2c\u5668\u7684\u7d93\u5ea6\u503c" + }, + "description": "\u4f7f\u7528\u7d93\u7def\u5ea6\u6307\u5b9a\u8ddd\u96e2\u4ee5\u641c\u5c0b PurpleAir \u611f\u6e2c\u5668\u3002" + }, + "choose_sensor": { + "data": { + "sensor_index": "\u611f\u6e2c\u5668" + }, + "data_description": { + "sensor_index": "\u8ffd\u8e64\u7684\u611f\u6e2c\u5668" + }, + "description": "\u6240\u8981\u8ffd\u8e64\u7684\u5468\u570d\u611f\u6e2c\u5668\uff1f" + }, + "reauth_confirm": { + "data": { + "api_key": "API \u91d1\u9470" + }, + "data_description": { + "api_key": "PurpleAir API \u91d1\u9470\uff08\u5047\u5982\u540c\u6642\u53d6\u5f97\u8b80\u53d6\u8ddf\u5beb\u5165\u91d1\u9470\u3001\u8acb\u4f7f\u7528\u8b80\u53d6\u91d1\u9470\uff09" + } + }, + "user": { + "data": { + "api_key": "API \u91d1\u9470" + }, + "data_description": { + "api_key": "PurpleAir API \u91d1\u9470\uff08\u5047\u5982\u540c\u6642\u53d6\u5f97\u8b80\u53d6\u8ddf\u5beb\u5165\u91d1\u9470\u3001\u8acb\u4f7f\u7528\u8b80\u53d6\u91d1\u9470\uff09" + } + } + } + }, + "options": { + "abort": { + "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210" + }, + "error": { + "invalid_api_key": "API \u91d1\u9470\u7121\u6548", + "no_sensors_near_coordinates": "\u5ea7\u6a19\u9644\u8fd1\uff08\u8ddd\u96e2\u5167\uff09\u627e\u4e0d\u5230\u611f\u6e2c\u5668", + "unknown": "\u672a\u9810\u671f\u932f\u8aa4" + }, + "step": { + "add_sensor": { + "data": { + "distance": "\u641c\u5c0b\u534a\u5f91", + "latitude": "\u7def\u5ea6", + "longitude": "\u7d93\u5ea6" + }, + "data_description": { + "distance": "\u641c\u5c0b\u7684\u5713\u5708\u534a\u5f91\uff08\u516c\u91cc\uff09", + "latitude": "\u641c\u5c0b\u611f\u6e2c\u5668\u7684\u7def\u5ea6\u503c", + "longitude": "\u641c\u5c0b\u611f\u6e2c\u5668\u7684\u7d93\u5ea6\u503c" + }, + "description": "\u4f7f\u7528\u7d93\u7def\u5ea6\u6307\u5b9a\u8ddd\u96e2\u4ee5\u641c\u5c0b PurpleAir \u611f\u6e2c\u5668\u3002", + "title": "\u65b0\u589e\u611f\u6e2c\u5668" + }, + "choose_sensor": { + "data": { + "sensor_index": "\u611f\u6e2c\u5668" + }, + "data_description": { + "sensor_index": "\u8ffd\u8e64\u7684\u611f\u6e2c\u5668" + }, + "description": "\u6240\u8981\u8ffd\u8e64\u7684\u5468\u570d\u611f\u6e2c\u5668\uff1f", + "title": "\u9078\u64c7\u8981\u65b0\u589e\u7684\u611f\u6e2c\u5668" + }, + "init": { + "menu_options": { + "add_sensor": "\u65b0\u589e\u611f\u6e2c\u5668", + "remove_sensor": "\u79fb\u9664\u611f\u6e2c\u5668" + } + }, + "remove_sensor": { + "data": { + "sensor_device_id": "\u611f\u6e2c\u5668\u540d\u7a31" + }, + "data_description": { + "sensor_device_id": "\u8981\u79fb\u9664\u7684\u611f\u6e2c\u5668" + }, + "title": "\u79fb\u9664\u611f\u6e2c\u5668" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/pushbullet/translations/ko.json b/homeassistant/components/pushbullet/translations/ko.json new file mode 100644 index 00000000000..8ed385e00f9 --- /dev/null +++ b/homeassistant/components/pushbullet/translations/ko.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "\uc11c\ube44\uc2a4\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_api_key": "API \ud0a4\uac00 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "api_key": "API \ud0a4", + "name": "\uc774\ub984" + } + } + } + }, + "issues": { + "deprecated_yaml": { + "description": "YAML\uc744 \uc0ac\uc6a9\ud55c Pushbullet \uad6c\uc131\uc774 \uc81c\uac70\ub429\ub2c8\ub2e4. \n\n\uae30\uc874 YAML \uad6c\uc131\uc744 UI\ub85c \uc790\ub3d9\uc73c\ub85c \uac00\uc838\uc654\uc2b5\ub2c8\ub2e4. \n\n\uc774 \ubb38\uc81c\ub97c \ud574\uacb0\ud558\ub824\uba74 configuration.yaml \ud30c\uc77c\uc5d0\uc11c Pushbullet YAML \uad6c\uc131\uc744 \uc81c\uac70\ud558\uace0 \ud648\uc5b4\uc2dc\uc2a4\ud134\ud2b8\ub97c \ub2e4\uc2dc \uc2dc\uc791\ud558\uc2ed\uc2dc\uc624.", + "title": "Pushbullet YAML \uad6c\uc131\uc774 \uc81c\uac70\ub429\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/pushbullet/translations/pt.json b/homeassistant/components/pushbullet/translations/pt.json new file mode 100644 index 00000000000..17cb9dfd1cf --- /dev/null +++ b/homeassistant/components/pushbullet/translations/pt.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "O servi\u00e7o j\u00e1 est\u00e1 configurado" + }, + "error": { + "cannot_connect": "A liga\u00e7\u00e3o falhou", + "invalid_api_key": "Chave de API inv\u00e1lida" + }, + "step": { + "user": { + "data": { + "api_key": "Chave da API", + "name": "Nome" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/pushbullet/translations/sk.json b/homeassistant/components/pushbullet/translations/sk.json index 65dd8989032..23415556eb2 100644 --- a/homeassistant/components/pushbullet/translations/sk.json +++ b/homeassistant/components/pushbullet/translations/sk.json @@ -15,5 +15,11 @@ } } } + }, + "issues": { + "deprecated_yaml": { + "description": "Konfigur\u00e1cia Pushbullet pomocou YAML sa odstra\u0148uje. \n\n Va\u0161a existuj\u00faca konfigur\u00e1cia YAML bola importovan\u00e1 do pou\u017e\u00edvate\u013esk\u00e9ho rozhrania automaticky. \n\n Odstr\u00e1\u0148te konfigur\u00e1ciu Pushbullet YAML zo s\u00faboru configuration.yaml a re\u0161tartujte Home Assistant, aby ste tento probl\u00e9m vyrie\u0161ili.", + "title": "Konfigur\u00e1cia Pushbullet YAML sa odstra\u0148uje" + } } } \ No newline at end of file diff --git a/homeassistant/components/pushover/translations/ko.json b/homeassistant/components/pushover/translations/ko.json new file mode 100644 index 00000000000..682b8f2825d --- /dev/null +++ b/homeassistant/components/pushover/translations/ko.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "already_configured": "\uc11c\ube44\uc2a4\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_api_key": "API \ud0a4\uac00 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "API \ud0a4" + }, + "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" + }, + "user": { + "data": { + "api_key": "API \ud0a4", + "name": "\uc774\ub984" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/pushover/translations/pt.json b/homeassistant/components/pushover/translations/pt.json index 9b06668589f..45f47e26309 100644 --- a/homeassistant/components/pushover/translations/pt.json +++ b/homeassistant/components/pushover/translations/pt.json @@ -1,8 +1,24 @@ { "config": { + "abort": { + "already_configured": "O servi\u00e7o j\u00e1 est\u00e1 configurado", + "reauth_successful": "Reautentica\u00e7\u00e3o bem sucedida" + }, + "error": { + "cannot_connect": "A liga\u00e7\u00e3o falhou", + "invalid_api_key": "Chave de API inv\u00e1lida" + }, "step": { + "reauth_confirm": { + "data": { + "api_key": "Chave da API" + }, + "title": "Reautenticar integra\u00e7\u00e3o" + }, "user": { "data": { + "api_key": "Chave da API", + "name": "Nome", "user_key": "Chave do usu\u00e1rio" } } diff --git a/homeassistant/components/pushover/translations/sk.json b/homeassistant/components/pushover/translations/sk.json index cf835f57ac7..315d9adf7cd 100644 --- a/homeassistant/components/pushover/translations/sk.json +++ b/homeassistant/components/pushover/translations/sk.json @@ -24,5 +24,11 @@ } } } + }, + "issues": { + "removed_yaml": { + "description": "Konfigur\u00e1cia Pushover pomocou YAML bola odstr\u00e1nen\u00e1. \n\n Asistent dom\u00e1cnosti nepou\u017e\u00edva va\u0161u existuj\u00facu konfigur\u00e1ciu YAML. \n\n Odstr\u00e1\u0148te konfigur\u00e1ciu Pushover YAML zo s\u00faboru configuration.yaml a re\u0161tartujte Home Assistant, aby ste tento probl\u00e9m vyrie\u0161ili.", + "title": "Konfigur\u00e1cia Pushover YAML bola odstr\u00e1nen\u00e1" + } } } \ No newline at end of file diff --git a/homeassistant/components/pvoutput/sensor.py b/homeassistant/components/pvoutput/sensor.py index a0afee0f3eb..700757c6d58 100644 --- a/homeassistant/components/pvoutput/sensor.py +++ b/homeassistant/components/pvoutput/sensor.py @@ -14,12 +14,10 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - ELECTRIC_POTENTIAL_VOLT, - ENERGY_KILO_WATT_HOUR, - ENERGY_WATT_HOUR, - POWER_KILO_WATT, - POWER_WATT, - TEMP_CELSIUS, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfPower, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo @@ -48,7 +46,7 @@ SENSORS: tuple[PVOutputSensorEntityDescription, ...] = ( PVOutputSensorEntityDescription( key="energy_consumption", name="Energy consumed", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, value_fn=lambda status: status.energy_consumption, @@ -56,7 +54,7 @@ SENSORS: tuple[PVOutputSensorEntityDescription, ...] = ( PVOutputSensorEntityDescription( key="energy_generation", name="Energy generated", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, value_fn=lambda status: status.energy_generation, @@ -64,14 +62,16 @@ SENSORS: tuple[PVOutputSensorEntityDescription, ...] = ( PVOutputSensorEntityDescription( key="normalized_output", name="Efficiency", - native_unit_of_measurement=f"{ENERGY_KILO_WATT_HOUR}/{POWER_KILO_WATT}", + native_unit_of_measurement=( + f"{UnitOfEnergy.KILO_WATT_HOUR}/{UnitOfPower.KILO_WATT}" + ), state_class=SensorStateClass.MEASUREMENT, value_fn=lambda status: status.normalized_output, ), PVOutputSensorEntityDescription( key="power_consumption", name="Power consumed", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, value_fn=lambda status: status.power_consumption, @@ -79,7 +79,7 @@ SENSORS: tuple[PVOutputSensorEntityDescription, ...] = ( PVOutputSensorEntityDescription( key="power_generation", name="Power generated", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, value_fn=lambda status: status.power_generation, @@ -87,7 +87,7 @@ SENSORS: tuple[PVOutputSensorEntityDescription, ...] = ( PVOutputSensorEntityDescription( key="temperature", name="Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, value_fn=lambda status: status.temperature, @@ -95,7 +95,7 @@ SENSORS: tuple[PVOutputSensorEntityDescription, ...] = ( PVOutputSensorEntityDescription( key="voltage", name="Voltage", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, value_fn=lambda status: status.voltage, diff --git a/homeassistant/components/pvoutput/translations/ko.json b/homeassistant/components/pvoutput/translations/ko.json new file mode 100644 index 00000000000..bca89f5b152 --- /dev/null +++ b/homeassistant/components/pvoutput/translations/ko.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "API \ud0a4" + } + }, + "user": { + "data": { + "api_key": "API \ud0a4" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/pvoutput/translations/pt.json b/homeassistant/components/pvoutput/translations/pt.json index 98fe3611480..caca52685b1 100644 --- a/homeassistant/components/pvoutput/translations/pt.json +++ b/homeassistant/components/pvoutput/translations/pt.json @@ -1,7 +1,7 @@ { "config": { "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida" } } diff --git a/homeassistant/components/pvoutput/translations/sk.json b/homeassistant/components/pvoutput/translations/sk.json index 027281e1819..7aedb02505d 100644 --- a/homeassistant/components/pvoutput/translations/sk.json +++ b/homeassistant/components/pvoutput/translations/sk.json @@ -11,13 +11,15 @@ "reauth_confirm": { "data": { "api_key": "API k\u013e\u00fa\u010d" - } + }, + "description": "Na op\u00e4tovn\u00e9 overenie pomocou PVOutput budete musie\u0165 z\u00edska\u0165 k\u013e\u00fa\u010d API na adrese {account_url}." }, "user": { "data": { "api_key": "API k\u013e\u00fa\u010d", "system_id": "ID syst\u00e9mu" - } + }, + "description": "Na overenie pomocou PVOutput budete musie\u0165 z\u00edska\u0165 k\u013e\u00fa\u010d API na adrese {account_url}. \n\n Syst\u00e9mov\u00e9 ID registrovan\u00fdch syst\u00e9mov s\u00fa uveden\u00e9 na tej istej str\u00e1nke." } } } diff --git a/homeassistant/components/pvpc_hourly_pricing/__init__.py b/homeassistant/components/pvpc_hourly_pricing/__init__.py index f10d3b995a8..27a006833ea 100644 --- a/homeassistant/components/pvpc_hourly_pricing/__init__.py +++ b/homeassistant/components/pvpc_hourly_pricing/__init__.py @@ -80,10 +80,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: try: await async_migrate_entries(hass, entry.entry_id, update_unique_id) _LOGGER.warning( - "Migrating PVPC sensor from old tariff '%s' to new '%s'. " - "Configure the integration to set your contracted power, " - "and select prices for Ceuta/Melilla, " - "if that is your case", + ( + "Migrating PVPC sensor from old tariff '%s' to new '%s'. " + "Configure the integration to set your contracted power, " + "and select prices for Ceuta/Melilla, " + "if that is your case" + ), entry.data[ATTR_TARIFF], _DEFAULT_TARIFF, ) @@ -95,8 +97,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: if reg_entry.config_entry_id == entry.entry_id: ent_reg.async_remove(entity_id) _LOGGER.warning( - "Old PVPC Sensor %s is removed " - "(another one already exists, using the same tariff)", + ( + "Old PVPC Sensor %s is removed " + "(another one already exists, using the same tariff)" + ), entity_id, ) break diff --git a/homeassistant/components/pvpc_hourly_pricing/sensor.py b/homeassistant/components/pvpc_hourly_pricing/sensor.py index 8cfae034bff..7419b8eef46 100644 --- a/homeassistant/components/pvpc_hourly_pricing/sensor.py +++ b/homeassistant/components/pvpc_hourly_pricing/sensor.py @@ -12,7 +12,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_NAME, CURRENCY_EURO, ENERGY_KILO_WATT_HOUR +from homeassistant.const import CONF_NAME, CURRENCY_EURO, UnitOfEnergy from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.device_registry import DeviceEntryType from homeassistant.helpers.entity import DeviceInfo @@ -30,7 +30,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="PVPC", icon="mdi:currency-eur", - native_unit_of_measurement=f"{CURRENCY_EURO}/{ENERGY_KILO_WATT_HOUR}", + native_unit_of_measurement=f"{CURRENCY_EURO}/{UnitOfEnergy.KILO_WATT_HOUR}", state_class=SensorStateClass.MEASUREMENT, ), ) diff --git a/homeassistant/components/pyload/sensor.py b/homeassistant/components/pyload/sensor.py index be014a2a405..53762c5df35 100644 --- a/homeassistant/components/pyload/sensor.py +++ b/homeassistant/components/pyload/sensor.py @@ -7,7 +7,12 @@ import logging import requests import voluptuous as vol -from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity +from homeassistant.components.sensor import ( + PLATFORM_SCHEMA, + SensorDeviceClass, + SensorEntity, + SensorEntityDescription, +) from homeassistant.const import ( CONF_HOST, CONF_MONITORED_VARIABLES, @@ -17,7 +22,7 @@ from homeassistant.const import ( CONF_SSL, CONF_USERNAME, CONTENT_TYPE_JSON, - DATA_RATE_MEGABYTES_PER_SECOND, + UnitOfDataRate, ) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv @@ -33,7 +38,14 @@ DEFAULT_PORT = 8000 MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=15) -SENSOR_TYPES = {"speed": ["speed", "Speed", DATA_RATE_MEGABYTES_PER_SECOND]} +SENSOR_TYPES = { + "speed": SensorEntityDescription( + key="speed", + name="Speed", + native_unit_of_measurement=UnitOfDataRate.MEGABYTES_PER_SECOND, + device_class=SensorDeviceClass.DATA_RATE, + ) +} PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( { @@ -78,7 +90,7 @@ def setup_platform( devices = [] for ng_type in monitored_types: new_sensor = PyLoadSensor( - api=pyloadapi, sensor_type=SENSOR_TYPES.get(ng_type), client_name=name + api=pyloadapi, sensor_type=SENSOR_TYPES[ng_type], client_name=name ) devices.append(new_sensor) @@ -88,28 +100,14 @@ def setup_platform( class PyLoadSensor(SensorEntity): """Representation of a pyLoad sensor.""" - def __init__(self, api, sensor_type, client_name): + def __init__( + self, api: PyLoadAPI, sensor_type: SensorEntityDescription, client_name + ): """Initialize a new pyLoad sensor.""" - self._name = f"{client_name} {sensor_type[1]}" - self.type = sensor_type[0] + self._attr_name = f"{client_name} {sensor_type.name}" + self.type = sensor_type.key self.api = api - self._state = None - self._unit_of_measurement = sensor_type[2] - - @property - def name(self): - """Return the name of the sensor.""" - return self._name - - @property - def native_value(self): - """Return the state of the sensor.""" - return self._state - - @property - def native_unit_of_measurement(self): - """Return the unit of measurement of this entity, if any.""" - return self._unit_of_measurement + self.entity_description = sensor_type def update(self) -> None: """Update state of sensor.""" @@ -121,7 +119,7 @@ class PyLoadSensor(SensorEntity): if self.api.status is None: _LOGGER.debug( - "Update of %s requested, but no status is available", self._name + "Update of %s requested, but no status is available", self.name ) return @@ -131,9 +129,9 @@ class PyLoadSensor(SensorEntity): if "speed" in self.type and value > 0: # Convert download rate from Bytes/s to MBytes/s - self._state = round(value / 2**20, 2) + self._attr_native_value = round(value / 2**20, 2) else: - self._state = value + self._attr_native_value = value class PyLoadAPI: diff --git a/homeassistant/components/qbittorrent/sensor.py b/homeassistant/components/qbittorrent/sensor.py index 151055a1688..ff37d4f59dd 100644 --- a/homeassistant/components/qbittorrent/sensor.py +++ b/homeassistant/components/qbittorrent/sensor.py @@ -9,6 +9,7 @@ import voluptuous as vol from homeassistant.components.sensor import ( PLATFORM_SCHEMA, + SensorDeviceClass, SensorEntity, SensorEntityDescription, ) @@ -17,8 +18,8 @@ from homeassistant.const import ( CONF_PASSWORD, CONF_URL, CONF_USERNAME, - DATA_RATE_KIBIBYTES_PER_SECOND, STATE_IDLE, + UnitOfDataRate, ) from homeassistant.core import HomeAssistant from homeassistant.exceptions import PlatformNotReady @@ -42,12 +43,14 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key=SENSOR_TYPE_DOWNLOAD_SPEED, name="Down Speed", - native_unit_of_measurement=DATA_RATE_KIBIBYTES_PER_SECOND, + device_class=SensorDeviceClass.DATA_RATE, + native_unit_of_measurement=UnitOfDataRate.KIBIBYTES_PER_SECOND, ), SensorEntityDescription( key=SENSOR_TYPE_UPLOAD_SPEED, name="Up Speed", - native_unit_of_measurement=DATA_RATE_KIBIBYTES_PER_SECOND, + device_class=SensorDeviceClass.DATA_RATE, + native_unit_of_measurement=UnitOfDataRate.KIBIBYTES_PER_SECOND, ), ) diff --git a/homeassistant/components/qingping/sensor.py b/homeassistant/components/qingping/sensor.py index 6f1ad8118ab..84276c11292 100644 --- a/homeassistant/components/qingping/sensor.py +++ b/homeassistant/components/qingping/sensor.py @@ -27,9 +27,9 @@ from homeassistant.const import ( CONCENTRATION_PARTS_PER_MILLION, LIGHT_LUX, PERCENTAGE, - PRESSURE_MBAR, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, - TEMP_CELSIUS, + UnitOfPressure, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory @@ -89,7 +89,7 @@ SENSOR_DESCRIPTIONS = { (QingpingSensorDeviceClass.PRESSURE, Units.PRESSURE_MBAR): SensorEntityDescription( key=f"{QingpingSensorDeviceClass.PRESSURE}_{Units.PRESSURE_MBAR}", device_class=SensorDeviceClass.PRESSURE, - native_unit_of_measurement=PRESSURE_MBAR, + native_unit_of_measurement=UnitOfPressure.MBAR, state_class=SensorStateClass.MEASUREMENT, ), ( @@ -109,7 +109,7 @@ SENSOR_DESCRIPTIONS = { ): SensorEntityDescription( key=f"{QingpingSensorDeviceClass.TEMPERATURE}_{Units.TEMP_CELSIUS}", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, ), } diff --git a/homeassistant/components/qingping/translations/en.json b/homeassistant/components/qingping/translations/en.json index ebd9760c161..afe859ca766 100644 --- a/homeassistant/components/qingping/translations/en.json +++ b/homeassistant/components/qingping/translations/en.json @@ -9,13 +9,13 @@ "flow_title": "{name}", "step": { "bluetooth_confirm": { - "description": "Do you want to setup {name}?" + "description": "Do you want to set up {name}?" }, "user": { "data": { "address": "Device" }, - "description": "Choose a device to setup" + "description": "Choose a device to set up" } } } diff --git a/homeassistant/components/qingping/translations/it.json b/homeassistant/components/qingping/translations/it.json index 7784ed3a240..97113c57103 100644 --- a/homeassistant/components/qingping/translations/it.json +++ b/homeassistant/components/qingping/translations/it.json @@ -15,7 +15,7 @@ "data": { "address": "Dispositivo" }, - "description": "Seleziona un dispositivo da configurare" + "description": "Scegli un dispositivo da configurare" } } } diff --git a/homeassistant/components/qingping/translations/ko.json b/homeassistant/components/qingping/translations/ko.json new file mode 100644 index 00000000000..1a59c05b30c --- /dev/null +++ b/homeassistant/components/qingping/translations/ko.json @@ -0,0 +1,9 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", + "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/qingping/translations/no.json b/homeassistant/components/qingping/translations/no.json index 0bf8b1695ec..38ab3d096f2 100644 --- a/homeassistant/components/qingping/translations/no.json +++ b/homeassistant/components/qingping/translations/no.json @@ -9,7 +9,7 @@ "flow_title": "{name}", "step": { "bluetooth_confirm": { - "description": "Vil du konfigurere {name}?" + "description": "Vil du sette opp {name} ?" }, "user": { "data": { diff --git a/homeassistant/components/qingping/translations/pt.json b/homeassistant/components/qingping/translations/pt.json new file mode 100644 index 00000000000..c6a15010072 --- /dev/null +++ b/homeassistant/components/qingping/translations/pt.json @@ -0,0 +1,9 @@ +{ + "config": { + "abort": { + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", + "already_in_progress": "O processo de configura\u00e7\u00e3o j\u00e1 est\u00e1 a decorrer", + "no_devices_found": "Nenhum dispositivo encontrado na rede" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/qld_bushfire/geo_location.py b/homeassistant/components/qld_bushfire/geo_location.py index e754de466d1..fc9fd727615 100644 --- a/homeassistant/components/qld_bushfire/geo_location.py +++ b/homeassistant/components/qld_bushfire/geo_location.py @@ -19,7 +19,7 @@ from homeassistant.const import ( CONF_RADIUS, CONF_SCAN_INTERVAL, EVENT_HOMEASSISTANT_START, - LENGTH_KILOMETERS, + UnitOfLength, ) from homeassistant.core import Event, HomeAssistant, callback import homeassistant.helpers.config_validation as cv @@ -154,7 +154,7 @@ class QldBushfireLocationEvent(GeolocationEvent): _attr_icon = "mdi:fire" _attr_should_poll = False _attr_source = SOURCE - _attr_unit_of_measurement = LENGTH_KILOMETERS + _attr_unit_of_measurement = UnitOfLength.KILOMETERS def __init__( self, feed_manager: QldBushfireFeedEntityManager, external_id: str diff --git a/homeassistant/components/qnap/sensor.py b/homeassistant/components/qnap/sensor.py index 8c093eb9232..378ed165f83 100644 --- a/homeassistant/components/qnap/sensor.py +++ b/homeassistant/components/qnap/sensor.py @@ -23,10 +23,10 @@ from homeassistant.const import ( CONF_TIMEOUT, CONF_USERNAME, CONF_VERIFY_SSL, - DATA_GIBIBYTES, - DATA_RATE_MEBIBYTES_PER_SECOND, PERCENTAGE, - TEMP_CELSIUS, + UnitOfDataRate, + UnitOfInformation, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.exceptions import PlatformNotReady @@ -74,7 +74,7 @@ _SYSTEM_MON_COND: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="system_temp", name="System Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, ), ) @@ -82,7 +82,7 @@ _CPU_MON_COND: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="cpu_temp", name="CPU Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, ), SensorEntityDescription( @@ -96,13 +96,15 @@ _MEMORY_MON_COND: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="memory_free", name="Memory Available", - native_unit_of_measurement=DATA_GIBIBYTES, + native_unit_of_measurement=UnitOfInformation.GIBIBYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:memory", ), SensorEntityDescription( key="memory_used", name="Memory Used", - native_unit_of_measurement=DATA_GIBIBYTES, + native_unit_of_measurement=UnitOfInformation.GIBIBYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:memory", ), SensorEntityDescription( @@ -121,13 +123,15 @@ _NETWORK_MON_COND: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="network_tx", name="Network Up", - native_unit_of_measurement=DATA_RATE_MEBIBYTES_PER_SECOND, + native_unit_of_measurement=UnitOfDataRate.MEBIBYTES_PER_SECOND, + device_class=SensorDeviceClass.DATA_RATE, icon="mdi:upload", ), SensorEntityDescription( key="network_rx", name="Network Down", - native_unit_of_measurement=DATA_RATE_MEBIBYTES_PER_SECOND, + native_unit_of_measurement=UnitOfDataRate.MEBIBYTES_PER_SECOND, + device_class=SensorDeviceClass.DATA_RATE, icon="mdi:download", ), ) @@ -140,7 +144,7 @@ _DRIVE_MON_COND: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="drive_temp", name="Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, ), ) @@ -148,13 +152,15 @@ _VOLUME_MON_COND: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="volume_size_used", name="Used Space", - native_unit_of_measurement=DATA_GIBIBYTES, + native_unit_of_measurement=UnitOfInformation.GIBIBYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:chart-pie", ), SensorEntityDescription( key="volume_size_free", name="Free Space", - native_unit_of_measurement=DATA_GIBIBYTES, + native_unit_of_measurement=UnitOfInformation.GIBIBYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:chart-pie", ), SensorEntityDescription( @@ -372,7 +378,7 @@ class QNAPMemorySensor(QNAPSensor): if self._api.data: data = self._api.data["system_stats"]["memory"] size = round_nicely(float(data["total"]) / 1024) - return {ATTR_MEMORY_SIZE: f"{size} {DATA_GIBIBYTES}"} + return {ATTR_MEMORY_SIZE: f"{size} {UnitOfInformation.GIBIBYTES}"} class QNAPNetworkSensor(QNAPSensor): @@ -456,7 +462,10 @@ class QNAPDriveSensor(QNAPSensor): """Return the name of the sensor, if any.""" server_name = self._api.data["system_stats"]["system"]["name"] - return f"{server_name} {self.entity_description.name} (Drive {self.monitor_device})" + return ( + f"{server_name} {self.entity_description.name} (Drive" + f" {self.monitor_device})" + ) @property def extra_state_attributes(self): @@ -499,4 +508,8 @@ class QNAPVolumeSensor(QNAPSensor): data = self._api.data["volumes"][self.monitor_device] total_gb = int(data["total_size"]) / 1024 / 1024 / 1024 - return {ATTR_VOLUME_SIZE: f"{round_nicely(total_gb)} {DATA_GIBIBYTES}"} + return { + ATTR_VOLUME_SIZE: ( + f"{round_nicely(total_gb)} {UnitOfInformation.GIBIBYTES}" + ) + } diff --git a/homeassistant/components/qnap_qsw/manifest.json b/homeassistant/components/qnap_qsw/manifest.json index 91d77a7362f..370dd96d4f8 100644 --- a/homeassistant/components/qnap_qsw/manifest.json +++ b/homeassistant/components/qnap_qsw/manifest.json @@ -3,7 +3,7 @@ "name": "QNAP QSW", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/qnap_qsw", - "requirements": ["aioqsw==0.2.2"], + "requirements": ["aioqsw==0.3.1"], "codeowners": ["@Noltari"], "iot_class": "local_polling", "loggers": ["aioqsw"], diff --git a/homeassistant/components/qnap_qsw/sensor.py b/homeassistant/components/qnap_qsw/sensor.py index 5fecf28c9f2..a00103a0f32 100644 --- a/homeassistant/components/qnap_qsw/sensor.py +++ b/homeassistant/components/qnap_qsw/sensor.py @@ -32,10 +32,10 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - DATA_BYTES, - DATA_RATE_BYTES_PER_SECOND, - TEMP_CELSIUS, - TIME_SECONDS, + UnitOfDataRate, + UnitOfInformation, + UnitOfTemperature, + UnitOfTime, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import EntityCategory @@ -83,10 +83,11 @@ SENSOR_TYPES: Final[tuple[QswSensorEntityDescription, ...]] = ( ), QswSensorEntityDescription( entity_registry_enabled_default=False, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:download-network", key=QSD_PORTS_STATISTICS, name="RX", - native_unit_of_measurement=DATA_BYTES, + native_unit_of_measurement=UnitOfInformation.BYTES, state_class=SensorStateClass.TOTAL_INCREASING, subkey=QSD_RX_OCTETS, ), @@ -101,10 +102,11 @@ SENSOR_TYPES: Final[tuple[QswSensorEntityDescription, ...]] = ( ), QswSensorEntityDescription( entity_registry_enabled_default=False, + device_class=SensorDeviceClass.DATA_RATE, icon="mdi:download-network", key=QSD_PORTS_STATISTICS, name="RX Speed", - native_unit_of_measurement=DATA_RATE_BYTES_PER_SECOND, + native_unit_of_measurement=UnitOfDataRate.BYTES_PER_SECOND, state_class=SensorStateClass.MEASUREMENT, subkey=QSD_RX_SPEED, ), @@ -115,25 +117,27 @@ SENSOR_TYPES: Final[tuple[QswSensorEntityDescription, ...]] = ( device_class=SensorDeviceClass.TEMPERATURE, key=QSD_SYSTEM_SENSOR, name="Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, subkey=QSD_TEMP, ), QswSensorEntityDescription( entity_registry_enabled_default=False, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:upload-network", key=QSD_PORTS_STATISTICS, name="TX", - native_unit_of_measurement=DATA_BYTES, + native_unit_of_measurement=UnitOfInformation.BYTES, state_class=SensorStateClass.TOTAL_INCREASING, subkey=QSD_TX_OCTETS, ), QswSensorEntityDescription( entity_registry_enabled_default=False, + device_class=SensorDeviceClass.DATA_RATE, icon="mdi:upload-network", key=QSD_PORTS_STATISTICS, name="TX Speed", - native_unit_of_measurement=DATA_RATE_BYTES_PER_SECOND, + native_unit_of_measurement=UnitOfDataRate.BYTES_PER_SECOND, state_class=SensorStateClass.MEASUREMENT, subkey=QSD_TX_SPEED, ), @@ -142,7 +146,7 @@ SENSOR_TYPES: Final[tuple[QswSensorEntityDescription, ...]] = ( key=QSD_SYSTEM_TIME, entity_category=EntityCategory.DIAGNOSTIC, name="Uptime", - native_unit_of_measurement=TIME_SECONDS, + native_unit_of_measurement=UnitOfTime.SECONDS, state_class=SensorStateClass.TOTAL_INCREASING, subkey=QSD_UPTIME, ), diff --git a/homeassistant/components/qnap_qsw/translations/ko.json b/homeassistant/components/qnap_qsw/translations/ko.json index d31f3ea7fcd..421da1a3c59 100644 --- a/homeassistant/components/qnap_qsw/translations/ko.json +++ b/homeassistant/components/qnap_qsw/translations/ko.json @@ -1,11 +1,25 @@ { "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, "step": { "discovered_connection": { "data": { "password": "\ube44\ubc00\ubc88\ud638", "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" } + }, + "user": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638", + "url": "URL \uc8fc\uc18c", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + } } } } diff --git a/homeassistant/components/qnap_qsw/translations/pt.json b/homeassistant/components/qnap_qsw/translations/pt.json index 9f02a9e2519..5b871085b9f 100644 --- a/homeassistant/components/qnap_qsw/translations/pt.json +++ b/homeassistant/components/qnap_qsw/translations/pt.json @@ -1,7 +1,7 @@ { "config": { "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "discovered_connection": { diff --git a/homeassistant/components/rachio/__init__.py b/homeassistant/components/rachio/__init__.py index 6723678012f..8f9c9395ade 100644 --- a/homeassistant/components/rachio/__init__.py +++ b/homeassistant/components/rachio/__init__.py @@ -86,7 +86,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: _LOGGER.error("No Rachio devices found in account %s", person.username) return False _LOGGER.info( - "%d Rachio device(s) found; The url %s must be accessible from the internet in order to receive updates", + ( + "%d Rachio device(s) found; The url %s must be accessible from the internet" + " in order to receive updates" + ), len(person.controllers), webhook_url, ) diff --git a/homeassistant/components/rachio/device.py b/homeassistant/components/rachio/device.py index 5053fa01495..485a726acd0 100644 --- a/homeassistant/components/rachio/device.py +++ b/homeassistant/components/rachio/device.py @@ -154,7 +154,10 @@ class RachioPerson: if isinstance(webhooks, dict): if webhooks.get("code") == PERMISSION_ERROR: _LOGGER.info( - "Not adding controller '%s', only controllers owned by '%s' may be added", + ( + "Not adding controller '%s', only controllers owned by '%s'" + " may be added" + ), controller[KEY_NAME], self.username, ) diff --git a/homeassistant/components/rachio/translations/pt.json b/homeassistant/components/rachio/translations/pt.json index d6320473ac0..2ef8363d72e 100644 --- a/homeassistant/components/rachio/translations/pt.json +++ b/homeassistant/components/rachio/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, diff --git a/homeassistant/components/rachio/translations/sk.json b/homeassistant/components/rachio/translations/sk.json index 89ef7514ac8..c427de2f747 100644 --- a/homeassistant/components/rachio/translations/sk.json +++ b/homeassistant/components/rachio/translations/sk.json @@ -5,13 +5,16 @@ }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { "data": { "api_key": "API k\u013e\u00fa\u010d" - } + }, + "description": "Budete potrebova\u0165 k\u013e\u00fa\u010d API zo str\u00e1nky https://app.rach.io/. Prejdite na Nastavenia a potom kliknite na polo\u017eku \"GET API KEY\".", + "title": "Pripojte sa k zariadeniu Rachio" } } }, diff --git a/homeassistant/components/radarr/coordinator.py b/homeassistant/components/radarr/coordinator.py index dfcd1e3a269..c2f8d7ce6ba 100644 --- a/homeassistant/components/radarr/coordinator.py +++ b/homeassistant/components/radarr/coordinator.py @@ -1,7 +1,7 @@ """Data update coordinator for the Radarr integration.""" from __future__ import annotations -from abc import abstractmethod +from abc import ABC, abstractmethod from datetime import timedelta from typing import Generic, TypeVar, Union, cast @@ -19,7 +19,7 @@ from .const import DOMAIN, LOGGER T = TypeVar("T", bound=Union[SystemStatus, list[RootFolder], list[Health], int]) -class RadarrDataUpdateCoordinator(DataUpdateCoordinator[T], Generic[T]): +class RadarrDataUpdateCoordinator(DataUpdateCoordinator[T], Generic[T], ABC): """Data update coordinator for the Radarr integration.""" config_entry: ConfigEntry diff --git a/homeassistant/components/radarr/sensor.py b/homeassistant/components/radarr/sensor.py index 0f244b92a16..bd1e8e139c9 100644 --- a/homeassistant/components/radarr/sensor.py +++ b/homeassistant/components/radarr/sensor.py @@ -15,12 +15,7 @@ from homeassistant.components.sensor import ( SensorEntityDescription, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - DATA_BYTES, - DATA_GIGABYTES, - DATA_KILOBYTES, - DATA_MEGABYTES, -) +from homeassistant.const import UnitOfInformation from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -35,7 +30,7 @@ from .coordinator import RadarrDataUpdateCoordinator, T def get_space(data: list[Diskspace], name: str) -> str: """Get space.""" space = [ - mount.freeSpace / 1024 ** BYTE_SIZES.index(DATA_GIGABYTES) + mount.freeSpace / 1024 ** BYTE_SIZES.index(UnitOfInformation.GIGABYTES) for mount in data if name in mount.path ] @@ -76,7 +71,8 @@ SENSOR_TYPES: dict[str, RadarrSensorEntityDescription[Any]] = { "disk_space": RadarrSensorEntityDescription( key="disk_space", name="Disk space", - native_unit_of_measurement=DATA_GIGABYTES, + native_unit_of_measurement=UnitOfInformation.GIGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:harddisk", value_fn=get_space, description_fn=get_modified_description, @@ -100,10 +96,10 @@ SENSOR_TYPES: dict[str, RadarrSensorEntityDescription[Any]] = { } BYTE_SIZES = [ - DATA_BYTES, - DATA_KILOBYTES, - DATA_MEGABYTES, - DATA_GIGABYTES, + UnitOfInformation.BYTES, + UnitOfInformation.KILOBYTES, + UnitOfInformation.MEGABYTES, + UnitOfInformation.GIGABYTES, ] PARALLEL_UPDATES = 1 diff --git a/homeassistant/components/radarr/translations/bg.json b/homeassistant/components/radarr/translations/bg.json index 070befea137..0ded8c7c583 100644 --- a/homeassistant/components/radarr/translations/bg.json +++ b/homeassistant/components/radarr/translations/bg.json @@ -23,10 +23,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0435\u0442\u043e \u043d\u0430 Radarr \u0441 \u043f\u043e\u043c\u043e\u0449\u0442\u0430 \u043d\u0430 YAML \u0441\u0435 \u043f\u0440\u0435\u043c\u0430\u0445\u0432\u0430.\n\n\u0412\u0430\u0448\u0430\u0442\u0430 \u0441\u044a\u0449\u0435\u0441\u0442\u0432\u0443\u0432\u0430\u0449\u0430 YAML \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u0435 \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0430\u043d\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e \u0432 \u043f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u0438\u044f \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441.\n\n\u041f\u0440\u0435\u043c\u0430\u0445\u043d\u0435\u0442\u0435 YAML \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u043d\u0430 Radarr \u043e\u0442 \u0432\u0430\u0448\u0438\u044f \u0444\u0430\u0439\u043b configuration.yaml \u0438 \u0440\u0435\u0441\u0442\u0430\u0440\u0442\u0438\u0440\u0430\u0439\u0442\u0435 Home Assistant, \u0437\u0430 \u0434\u0430 \u043a\u043e\u0440\u0438\u0433\u0438\u0440\u0430\u0442\u0435 \u0442\u043e\u0437\u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c.", - "title": "YAML \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u043d\u0430 Radarr \u0441\u0435 \u043f\u0440\u0435\u043c\u0430\u0445\u0432\u0430" - }, "removed_yaml": { "description": "\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0435\u0442\u043e \u043d\u0430 Radarr \u0441 \u043f\u043e\u043c\u043e\u0449\u0442\u0430 \u043d\u0430 YAML \u0435 \u043f\u0440\u0435\u043c\u0430\u0445\u043d\u0430\u0442\u043e.\n\n\u0412\u0430\u0448\u0430\u0442\u0430 \u0441\u044a\u0449\u0435\u0441\u0442\u0432\u0443\u0432\u0430\u0449\u0430 YAML \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043d\u0435 \u0441\u0435 \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430 \u043e\u0442 Home Assistant.\n\n\u041f\u0440\u0435\u043c\u0430\u0445\u043d\u0435\u0442\u0435 YAML \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u043e\u0442 \u0432\u0430\u0448\u0438\u044f \u0444\u0430\u0439\u043b configuration.yaml \u0438 \u0440\u0435\u0441\u0442\u0430\u0440\u0442\u0438\u0440\u0430\u0439\u0442\u0435 Home Assistant, \u0437\u0430 \u0434\u0430 \u043a\u043e\u0440\u0438\u0433\u0438\u0440\u0430\u0442\u0435 \u0442\u043e\u0437\u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c.", "title": "YAML \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u043d\u0430 Radarr \u0435 \u043f\u0440\u0435\u043c\u0430\u0445\u043d\u0430\u0442\u0430" diff --git a/homeassistant/components/radarr/translations/ca.json b/homeassistant/components/radarr/translations/ca.json index de8c3861de9..a07748a2787 100644 --- a/homeassistant/components/radarr/translations/ca.json +++ b/homeassistant/components/radarr/translations/ca.json @@ -27,10 +27,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "La configuraci\u00f3 de Radarr mitjan\u00e7ant YAML s'eliminar\u00e0 de Home Assistant.\n\nLa configuraci\u00f3 YAML existent s'ha importat autom\u00e0ticament a la interf\u00edcie d'usuari.\n\nElimina la configuraci\u00f3 YAML de Radarr del fitxer configuration.yaml i reinicia Home Assistant per solucionar aquest problema.", - "title": "La configuraci\u00f3 YAML de Radarr est\u00e0 sent eliminada" - }, "removed_yaml": { "description": "La configuraci\u00f3 de Radarr mitjan\u00e7ant YAML s'ha eliminat de Home Assistant.\n\nHome Assistant ja no utilitza la configuraci\u00f3 YAML existent.\n\nElimina la configuraci\u00f3 YAML corresponent del fitxer configuration.yaml i reinicia Home Assistant per solucionar aquest problema.", "title": "La configuraci\u00f3 YAML de Radarr s'ha eliminat" diff --git a/homeassistant/components/radarr/translations/cs.json b/homeassistant/components/radarr/translations/cs.json index 08b98b728ff..54f2dd44550 100644 --- a/homeassistant/components/radarr/translations/cs.json +++ b/homeassistant/components/radarr/translations/cs.json @@ -24,9 +24,6 @@ } }, "issues": { - "deprecated_yaml": { - "title": "Konfigurace Radarr YAML se odstra\u0148uje" - }, "removed_yaml": { "description": "Konfigurace Radaru pomoc\u00ed YAML byla odstran\u011bna. \n\nVa\u0161i st\u00e1vaj\u00edc\u00ed konfigurace YAML nen\u00ed vyu\u017eita Home Assistantem. \n\nOdstra\u0148te konfiguraci YAML ze souboru configuration.yaml a restartujte Home Assistant, abyste se tento probl\u00e9m vy\u0159e\u0161il.", "title": "Konfigurace Radarr YAML byla odstran\u011bna" diff --git a/homeassistant/components/radarr/translations/de.json b/homeassistant/components/radarr/translations/de.json index 9e6a805ad94..a103f8406d4 100644 --- a/homeassistant/components/radarr/translations/de.json +++ b/homeassistant/components/radarr/translations/de.json @@ -27,10 +27,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "Die Konfiguration von Radarr mit YAML wird entfernt. \n\nDeine vorhandene YAML-Konfiguration wurde automatisch in die Benutzeroberfl\u00e4che importiert. \n\nEntferne die Radarr-YAML-Konfiguration aus deiner configuration.yaml-Datei und starte Home Assistant neu, um dieses Problem zu beheben.", - "title": "Die Radarr YAML-Konfiguration wird entfernt" - }, "removed_yaml": { "description": "Die Konfiguration von Radarr mittels YAML wurde entfernt.\n\nDeine bestehende YAML-Konfiguration wird vom Home Assistant nicht verwendet.\n\nEntferne die YAML-Konfiguration aus deiner configuration.yaml Datei und starte Home Assistant neu, um dieses Problem zu beheben.", "title": "Die Radarr YAML-Konfiguration wurde entfernt" diff --git a/homeassistant/components/radarr/translations/el.json b/homeassistant/components/radarr/translations/el.json index e10f213a156..1ed81e3cd54 100644 --- a/homeassistant/components/radarr/translations/el.json +++ b/homeassistant/components/radarr/translations/el.json @@ -27,10 +27,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 Radarr \u03bc\u03b5 \u03c7\u03c1\u03ae\u03c3\u03b7 YAML \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b5\u03af\u03c4\u03b1\u03b9. \n\n \u0397 \u03c5\u03c0\u03ac\u03c1\u03c7\u03bf\u03c5\u03c3\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03ae \u03c3\u03b1\u03c2 YAML \u03ad\u03c7\u03b5\u03b9 \u03b5\u03b9\u03c3\u03b1\u03c7\u03b8\u03b5\u03af \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b1 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03c0\u03b1\u03c6\u03ae \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7. \n\n \u039a\u03b1\u03c4\u03b1\u03c1\u03b3\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 Radarr YAML \u03b1\u03c0\u03cc \u03c4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf configuration.yaml \u03ba\u03b1\u03b9 \u03b5\u03c0\u03b1\u03bd\u03b5\u03ba\u03ba\u03b9\u03bd\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf Home Assistant \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c0\u03c1\u03cc\u03b2\u03bb\u03b7\u03bc\u03b1.", - "title": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 Radarr YAML \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b5\u03af\u03c4\u03b1\u03b9" - }, "removed_yaml": { "description": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 Radarr \u03bc\u03b5 \u03c7\u03c1\u03ae\u03c3\u03b7 YAML \u03ad\u03c7\u03b5\u03b9 \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b7\u03b8\u03b5\u03af. \n\n \u0397 \u03c5\u03c0\u03ac\u03c1\u03c7\u03bf\u03c5\u03c3\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 YAML \u03b4\u03b5\u03bd \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03bf Home Assistant. \n\n \u039a\u03b1\u03c4\u03b1\u03c1\u03b3\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 YAML \u03b1\u03c0\u03cc \u03c4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf configuration.yaml \u03ba\u03b1\u03b9 \u03b5\u03c0\u03b1\u03bd\u03b5\u03ba\u03ba\u03b9\u03bd\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf Home Assistant \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c0\u03c1\u03cc\u03b2\u03bb\u03b7\u03bc\u03b1.", "title": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 Radarr YAML \u03ad\u03c7\u03b5\u03b9 \u03b1\u03c6\u03b1\u03b9\u03c1\u03b5\u03b8\u03b5\u03af" diff --git a/homeassistant/components/radarr/translations/en.json b/homeassistant/components/radarr/translations/en.json index 928dc3a99f9..eb8f8e03fda 100644 --- a/homeassistant/components/radarr/translations/en.json +++ b/homeassistant/components/radarr/translations/en.json @@ -27,10 +27,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "Configuring Radarr using YAML is being removed.\n\nYour existing YAML configuration has been imported into the UI automatically.\n\nRemove the Radarr YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue.", - "title": "The Radarr YAML configuration is being removed" - }, "removed_yaml": { "description": "Configuring Radarr using YAML has been removed.\n\nYour existing YAML configuration is not used by Home Assistant.\n\nRemove the YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue.", "title": "The Radarr YAML configuration has been removed" diff --git a/homeassistant/components/radarr/translations/es.json b/homeassistant/components/radarr/translations/es.json index c3d087fa3b0..ee5952fbf31 100644 --- a/homeassistant/components/radarr/translations/es.json +++ b/homeassistant/components/radarr/translations/es.json @@ -27,10 +27,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "Se va a eliminar la configuraci\u00f3n de Radarr mediante YAML. \n\nTu configuraci\u00f3n YAML existente se ha importado a la IU autom\u00e1ticamente. \n\nElimina la configuraci\u00f3n YAML de Radarr de tu archivo configuration.yaml y reinicia Home Assistant para solucionar este problema.", - "title": "Se va a eliminar la configuraci\u00f3n YAML de Radarr" - }, "removed_yaml": { "description": "Se ha eliminado la configuraci\u00f3n de Radarr usando YAML. \n\nHome Assistant no utiliza tu configuraci\u00f3n YAML existente. \n\nElimina la configuraci\u00f3n YAML de tu archivo configuration.yaml y reinicia Home Assistant para solucionar este problema.", "title": "Se ha eliminado la configuraci\u00f3n YAML de Radarr" diff --git a/homeassistant/components/radarr/translations/et.json b/homeassistant/components/radarr/translations/et.json index 64c48f67220..f1bfc193f18 100644 --- a/homeassistant/components/radarr/translations/et.json +++ b/homeassistant/components/radarr/translations/et.json @@ -27,10 +27,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "Radarri seadistamine YAML-i abil eemaldatakse.\n\nTeie olemasolev YAML-i konfiguratsioon imporditakse kasutajaliidesesse automaatselt.\n\nEemaldage failist configuration.yaml radarr YAML konfiguratsioon ja taask\u00e4ivitage selle probleemi lahendamiseks Home Assistant.", - "title": "Radarr YAML-i konfiguratsiooni eemaldatakse" - }, "removed_yaml": { "description": "Radarri konfigureerimine YAML-i abil on eemaldatud.\n\nTeie olemasolevat YAML-konfiguratsiooni ei kasuta Home Assistant.\n\nProbleemi lahendamiseks eemaldage YAML-konfiguratsioon failist configuration.yaml ja k\u00e4ivitage Home Assistant uuesti.", "title": "Radarr YAML-i konfiguratsioon on eemaldatud" diff --git a/homeassistant/components/radarr/translations/fr.json b/homeassistant/components/radarr/translations/fr.json index 9dbf28c5129..2078a1a778d 100644 --- a/homeassistant/components/radarr/translations/fr.json +++ b/homeassistant/components/radarr/translations/fr.json @@ -25,11 +25,6 @@ } } }, - "issues": { - "deprecated_yaml": { - "title": "La configuration YAML pour Radarr sera bient\u00f4t supprim\u00e9e" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/radarr/translations/hu.json b/homeassistant/components/radarr/translations/hu.json index cf5a08d3fec..56fa961d607 100644 --- a/homeassistant/components/radarr/translations/hu.json +++ b/homeassistant/components/radarr/translations/hu.json @@ -27,9 +27,9 @@ } }, "issues": { - "deprecated_yaml": { - "description": "A Radarr YAML haszn\u00e1lat\u00e1val t\u00f6rt\u00e9n\u0151 konfigur\u00e1l\u00e1sa elt\u00e1vol\u00edt\u00e1sra ker\u00fcl.\n\nA megl\u00e9v\u0151 YAML konfigur\u00e1ci\u00f3 automatikusan import\u00e1l\u00e1sra ker\u00fclt a felhaszn\u00e1l\u00f3i fel\u00fcletre.\n\nA hiba kijav\u00edt\u00e1s\u00e1hoz t\u00e1vol\u00edtsa el a Radarr YAML konfigur\u00e1ci\u00f3t a configuration.yaml f\u00e1jlb\u00f3l, \u00e9s ind\u00edtsa \u00fajra a Home Assistantot.", - "title": "A Radarr YAML konfigur\u00e1ci\u00f3 elt\u00e1vol\u00edt\u00e1sra ker\u00fcl" + "removed_yaml": { + "description": "A Radarr YAML haszn\u00e1lat\u00e1val t\u00f6rt\u00e9n\u0151 konfigur\u00e1l\u00e1sa elt\u00e1vol\u00edt\u00e1sra ker\u00fclt.\n\nA megl\u00e9v\u0151 YAML konfigur\u00e1ci\u00f3t a Home Assistant nem haszn\u00e1lja.\n\nA probl\u00e9ma megold\u00e1s\u00e1hoz t\u00e1vol\u00edtsa el a YAML konfigur\u00e1ci\u00f3t a configuration.yaml f\u00e1jlb\u00f3l, \u00e9s ind\u00edtsa \u00fajra a Home Assistantot.", + "title": "A Radarr YAML konfigur\u00e1ci\u00f3 elt\u00e1vol\u00edt\u00e1sra ker\u00fclt" } }, "options": { diff --git a/homeassistant/components/radarr/translations/id.json b/homeassistant/components/radarr/translations/id.json index 97311c8f48b..b20a68afa77 100644 --- a/homeassistant/components/radarr/translations/id.json +++ b/homeassistant/components/radarr/translations/id.json @@ -27,10 +27,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "Proses konfigurasi Integrasi Radarr lewat YAML dalam proses penghapusan.\n\nKonfigurasi YAML yang ada telah diimpor ke antarmuka secara otomatis.\n\nHapus konfigurasi YAML Integrasi Radarr dari file configuration.yaml dan mulai ulang Home Assistant untuk memperbaiki masalah ini.", - "title": "Konfigurasi YAML Integrasi Radarr dalam proses penghapusan" - }, "removed_yaml": { "description": "Proses konfigurasi Integrasi Radarr lewat YAML telah dihapus.\n\nKonfigurasi YAML yang ada tidak digunakan oleh Home Assistant.\n\nHapus konfigurasi YAML dari file configuration.yaml dan mulai ulang Home Assistant untuk memperbaiki masalah ini.", "title": "Konfigurasi YAML Integrasi Radarr telah dihapus" diff --git a/homeassistant/components/radarr/translations/it.json b/homeassistant/components/radarr/translations/it.json index d6a1445493d..8aec8d276a9 100644 --- a/homeassistant/components/radarr/translations/it.json +++ b/homeassistant/components/radarr/translations/it.json @@ -27,8 +27,8 @@ } }, "issues": { - "deprecated_yaml": { - "description": "La configurazione di Radarr tramite YAML \u00e8 stata rimossa.\n\nLa configurazione YAML esistente \u00e8 stata importata automaticamente nell'interfaccia utente.\n\nRimuovere la configurazione YAML di Radarr dal file configuration.yaml e riavviare Home Assistant per risolvere il problema.", + "removed_yaml": { + "description": "La configurazione di Radarr tramite YAML \u00e8 stata rimossa. \n\nLa tua configurazione YAML esistente non \u00e8 utilizzata da Home Assistant. \n\nRimuovi la configurazione YAML dal tuo file configuration.yaml e riavvia Home Assistant per risolvere questo problema.", "title": "La configurazione YAML di Radarr \u00e8 stata rimossa" } }, diff --git a/homeassistant/components/radarr/translations/ko.json b/homeassistant/components/radarr/translations/ko.json new file mode 100644 index 00000000000..414fbdb627d --- /dev/null +++ b/homeassistant/components/radarr/translations/ko.json @@ -0,0 +1,31 @@ +{ + "config": { + "abort": { + "already_configured": "\uc11c\ube44\uc2a4\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "reauth_confirm": { + "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" + }, + "user": { + "data": { + "api_key": "API \ud0a4", + "url": "URL \uc8fc\uc18c", + "verify_ssl": "SSL \uc778\uc99d\uc11c \ud655\uc778" + } + } + } + }, + "issues": { + "removed_yaml": { + "description": "YAML\uc744 \uc0ac\uc6a9\ud55c Radarr \uad6c\uc131\uc774 \uc81c\uac70\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \n\n \uae30\uc874 YAML \uad6c\uc131\uc740 Home Assistant \uc5d0\uc11c \uc0ac\uc6a9\ub418\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. \n\n configuration.yaml \ud30c\uc77c\uc5d0\uc11c YAML \uad6c\uc131\uc744 \uc81c\uac70\ud558\uace0 Home Assistant\ub97c \ub2e4\uc2dc \uc2dc\uc791\ud558\uc5ec \uc774 \ubb38\uc81c\ub97c \ud574\uacb0\ud558\uc2ed\uc2dc\uc624.", + "title": "Radarr YAML \uad6c\uc131\uc774 \uc81c\uac70\ub418\uc5c8\uc2b5\ub2c8\ub2e4." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/radarr/translations/nl.json b/homeassistant/components/radarr/translations/nl.json index 0047e41fe62..a5d69757ab6 100644 --- a/homeassistant/components/radarr/translations/nl.json +++ b/homeassistant/components/radarr/translations/nl.json @@ -24,9 +24,6 @@ } }, "issues": { - "deprecated_yaml": { - "title": "De Radarr YAML-configuratie wordt verwijderd" - }, "removed_yaml": { "title": "De Radarr YAML configuratie is verwijderd" } diff --git a/homeassistant/components/radarr/translations/no.json b/homeassistant/components/radarr/translations/no.json index e74efdaf846..38772f5afcd 100644 --- a/homeassistant/components/radarr/translations/no.json +++ b/homeassistant/components/radarr/translations/no.json @@ -27,10 +27,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "Konfigurering av Radarr med YAML blir fjernet. \n\n Din eksisterende YAML-konfigurasjon har blitt importert til brukergrensesnittet automatisk. \n\n Fjern Radarr YAML-konfigurasjonen fra configuration.yaml-filen og start Home Assistant p\u00e5 nytt for \u00e5 fikse dette problemet.", - "title": "Radarr YAML-konfigurasjonen blir fjernet" - }, "removed_yaml": { "description": "Konfigurering av Radarr med YAML er fjernet. \n\n Din eksisterende YAML-konfigurasjon brukes ikke av Home Assistant. \n\n Fjern YAML-konfigurasjonen fra configuration.yaml-filen og start Home Assistant p\u00e5 nytt for \u00e5 fikse dette problemet.", "title": "Radarr YAML-konfigurasjonen er fjernet" diff --git a/homeassistant/components/radarr/translations/pl.json b/homeassistant/components/radarr/translations/pl.json index 49d773258b9..5cc98eefa84 100644 --- a/homeassistant/components/radarr/translations/pl.json +++ b/homeassistant/components/radarr/translations/pl.json @@ -27,9 +27,9 @@ } }, "issues": { - "deprecated_yaml": { - "description": "Konfiguracja Radarr przy u\u017cyciu YAML zostanie usuni\u0119ta. \n\nTwoja istniej\u0105ca konfiguracja YAML zosta\u0142a automatycznie zaimportowana do interfejsu u\u017cytkownika. \n\nUsu\u0144 konfiguracj\u0119 YAML z pliku configuration.yaml i uruchom ponownie Home Assistanta, aby rozwi\u0105za\u0107 ten problem.", - "title": "Konfiguracja YAML dla Radarr zostanie usuni\u0119ta" + "removed_yaml": { + "description": "Konfiguracja Radarr za pomoc\u0105 YAML zosta\u0142a usuni\u0119ta. \n\nTwoja istniej\u0105ca konfiguracja YAML nie jest u\u017cywana przez Home Assistant. \n\nUsu\u0144 konfiguracj\u0119 YAML z pliku configuration.yaml i uruchom ponownie Home Assistanta, aby rozwi\u0105za\u0107 ten problem.", + "title": "Konfiguracja YAML dla Radarr zosta\u0142a usuni\u0119ta" } }, "options": { diff --git a/homeassistant/components/radarr/translations/pt-BR.json b/homeassistant/components/radarr/translations/pt-BR.json index a9c955561f9..add3ab666ab 100644 --- a/homeassistant/components/radarr/translations/pt-BR.json +++ b/homeassistant/components/radarr/translations/pt-BR.json @@ -27,10 +27,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "A configura\u00e7\u00e3o do Radarr usando YAML est\u00e1 sendo removida. \n\n Sua configura\u00e7\u00e3o YAML existente foi importada para a interface do usu\u00e1rio automaticamente. \n\n Remova a configura\u00e7\u00e3o YAML do Radarr do arquivo configuration.yaml e reinicie o Home Assistant para corrigir esse problema.", - "title": "A configura\u00e7\u00e3o YAML do Radarr est\u00e1 sendo removida" - }, "removed_yaml": { "description": "A configura\u00e7\u00e3o do Radarr usando YAML foi removida. \n\n Sua configura\u00e7\u00e3o YAML existente n\u00e3o \u00e9 usada pelo Home Assistant. \n\n Remova a configura\u00e7\u00e3o YAML do arquivo configuration.yaml e reinicie o Home Assistant para corrigir esse problema.", "title": "A configura\u00e7\u00e3o YAML de Radarr foi removida" diff --git a/homeassistant/components/radarr/translations/pt.json b/homeassistant/components/radarr/translations/pt.json new file mode 100644 index 00000000000..9ea9cbc0362 --- /dev/null +++ b/homeassistant/components/radarr/translations/pt.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "O servi\u00e7o j\u00e1 est\u00e1 configurado", + "reauth_successful": "Reautentica\u00e7\u00e3o bem sucedida" + }, + "error": { + "cannot_connect": "A liga\u00e7\u00e3o falhou", + "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", + "unknown": "Erro inesperado" + }, + "step": { + "reauth_confirm": { + "title": "Reautenticar integra\u00e7\u00e3o" + }, + "user": { + "data": { + "api_key": "Chave da API", + "url": "", + "verify_ssl": "Verificar o certificado SSL" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/radarr/translations/ru.json b/homeassistant/components/radarr/translations/ru.json index 7013a6e3a04..be44db34b22 100644 --- a/homeassistant/components/radarr/translations/ru.json +++ b/homeassistant/components/radarr/translations/ru.json @@ -27,10 +27,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 Radarr \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e YAML \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043b\u0435\u043d\u0430.\n\n\u0412\u0430\u0448\u0430 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0430\u044f YAML-\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0430. \u0423\u0434\u0430\u043b\u0438\u0442\u0435 \u0435\u0451 \u0438\u0437 \u0444\u0430\u0439\u043b\u0430 configuration.yaml \u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 Home Assistant, \u0447\u0442\u043e\u0431\u044b \u0443\u0441\u0442\u0440\u0430\u043d\u0438\u0442\u044c \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443.", - "title": "\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 Radarr \u0447\u0435\u0440\u0435\u0437 YAML \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043b\u0435\u043d\u0430" - }, "removed_yaml": { "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \"Radarr\" \u0442\u0435\u043f\u0435\u0440\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u0430 \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0435\u0440\u0435\u0437 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441.\n\n\u0412\u0430\u0448\u0430 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0430\u044f YAML-\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f Home Assistant. \u0423\u0434\u0430\u043b\u0438\u0442\u0435 \u0435\u0451 \u0438\u0437 \u0444\u0430\u0439\u043b\u0430 configuration.yaml \u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 Home Assistant, \u0447\u0442\u043e\u0431\u044b \u0443\u0441\u0442\u0440\u0430\u043d\u0438\u0442\u044c \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443.", "title": "\u0423\u0434\u0430\u043b\u0435\u043d\u0430 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 Radarr \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e YAML" diff --git a/homeassistant/components/radarr/translations/sk.json b/homeassistant/components/radarr/translations/sk.json index 76e74a69f4a..8476f325e94 100644 --- a/homeassistant/components/radarr/translations/sk.json +++ b/homeassistant/components/radarr/translations/sk.json @@ -1,15 +1,19 @@ { "config": { "abort": { - "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba", + "wrong_app": "Dosiahnut\u00e1 nespr\u00e1vna aplik\u00e1cia. Sk\u00faste to pros\u00edm znova", "zeroconf_failed": "K\u013e\u00fa\u010d API sa nena\u0161iel. Zadajte ho ru\u010dne" }, "step": { "reauth_confirm": { + "description": "Integr\u00e1cia radaru sa mus\u00ed manu\u00e1lne znova overi\u0165 pomocou rozhrania Radarr API", "title": "Znova overi\u0165 integr\u00e1ciu" }, "user": { @@ -17,6 +21,22 @@ "api_key": "API k\u013e\u00fa\u010d", "url": "URL", "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" + }, + "description": "API k\u013e\u00fa\u010d je mo\u017en\u00e9 z\u00edska\u0165 automaticky, ak v aplik\u00e1cii neboli nastaven\u00e9 prihlasovacie \u00fadaje.\nV\u00e1\u0161 k\u013e\u00fa\u010d API n\u00e1jdete v \u010dasti Nastavenia > V\u0161eobecn\u00e9 vo webovom pou\u017e\u00edvate\u013eskom rozhran\u00ed Radarr." + } + } + }, + "issues": { + "removed_yaml": { + "description": "Konfigur\u00e1cia radaru pomocou YAML bola odstr\u00e1nen\u00e1. \n\n Asistent dom\u00e1cnosti nepou\u017e\u00edva va\u0161u existuj\u00facu konfigur\u00e1ciu YAML. \n\n Ak chcete tento probl\u00e9m vyrie\u0161i\u0165, odstr\u00e1\u0148te konfigur\u00e1ciu YAML zo s\u00faboru configuration.yaml a re\u0161tartujte aplik\u00e1ciu Home Assistant.", + "title": "Konfigur\u00e1cia Radarr YAML bola odstr\u00e1nen\u00e1" + } + }, + "options": { + "step": { + "init": { + "data": { + "upcoming_days": "Po\u010det nasleduj\u00facich dn\u00ed, ktor\u00e9 sa maj\u00fa zobrazi\u0165" } } } diff --git a/homeassistant/components/radarr/translations/sv.json b/homeassistant/components/radarr/translations/sv.json index 132fc4c4335..498092328dd 100644 --- a/homeassistant/components/radarr/translations/sv.json +++ b/homeassistant/components/radarr/translations/sv.json @@ -26,12 +26,6 @@ } } }, - "issues": { - "deprecated_yaml": { - "description": "Konfigurering av Radarr med YAML tas bort. \n\n Din befintliga YAML-konfiguration har automatiskt importerats till anv\u00e4ndargr\u00e4nssnittet. \n\n Ta bort Radarr YAML-konfigurationen fr\u00e5n filen configuration.yaml och starta om Home Assistant f\u00f6r att \u00e5tg\u00e4rda problemet.", - "title": "Radarr YAML-konfigurationen tas bort" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/radarr/translations/tr.json b/homeassistant/components/radarr/translations/tr.json index ef2eb8c5575..10452a355ce 100644 --- a/homeassistant/components/radarr/translations/tr.json +++ b/homeassistant/components/radarr/translations/tr.json @@ -26,12 +26,6 @@ } } }, - "issues": { - "deprecated_yaml": { - "description": "Radarr'\u0131n YAML kullan\u0131larak yap\u0131land\u0131r\u0131lmas\u0131 kald\u0131r\u0131l\u0131yor. \n\n Mevcut YAML yap\u0131land\u0131rman\u0131z otomatik olarak kullan\u0131c\u0131 aray\u00fcz\u00fcne aktar\u0131ld\u0131. \n\n Radarr YAML yap\u0131land\u0131rmas\u0131n\u0131 configuration.yaml dosyan\u0131zdan kald\u0131r\u0131n ve bu sorunu gidermek i\u00e7in Home Assistant'\u0131 yeniden ba\u015flat\u0131n.", - "title": "Radarr YAML yap\u0131land\u0131rmas\u0131 kald\u0131r\u0131l\u0131yor" - } - }, "options": { "step": { "init": { diff --git a/homeassistant/components/radarr/translations/zh-Hant.json b/homeassistant/components/radarr/translations/zh-Hant.json index 1c0275c23bf..ca1ae32b77d 100644 --- a/homeassistant/components/radarr/translations/zh-Hant.json +++ b/homeassistant/components/radarr/translations/zh-Hant.json @@ -27,10 +27,6 @@ } }, "issues": { - "deprecated_yaml": { - "description": "\u4f7f\u7528 YAML \u8a2d\u5b9a\u7684 Radarr \u5373\u5c07\u9032\u884c\u79fb\u9664\u3002\n\n\u65e2\u6709\u7684 YAML \u8a2d\u5b9a\u5c07\u81ea\u52d5\u532f\u5165\u81f3 UI \u5167\u3002\n\n\u8acb\u65bc configuration.yaml \u6a94\u6848\u4e2d\u79fb\u9664 Radarr YAML \u8a2d\u5b9a\u4e26\u91cd\u65b0\u555f\u52d5 Home Assistant \u4ee5\u4fee\u6b63\u6b64\u554f\u984c\u3002", - "title": "Radarr YAML \u8a2d\u5b9a\u5373\u5c07\u79fb\u9664" - }, "removed_yaml": { "description": "\u4f7f\u7528 YAML \u8a2d\u5b9a Radarr \u7684\u529f\u80fd\u5373\u5c07\u79fb\u9664\u3002\n\nHome Assistant \u5c07\u4e0d\u518d\u4f7f\u7528\u73fe\u6709\u7684 YAML \u8a2d\u5b9a\u3002\n\n\u7531 configuration.yaml \u6a94\u6848\u4e2d\u79fb\u9664 YAML \u8a2d\u5b9a\u4e26\u91cd\u555f Home Assistant \u4ee5\u4fee\u6b63\u6b64\u554f\u984c\u3002", "title": "Radarr YAML \u8a2d\u5b9a\u5df2\u7d93\u79fb\u9664" diff --git a/homeassistant/components/radio_browser/translations/ko.json b/homeassistant/components/radio_browser/translations/ko.json new file mode 100644 index 00000000000..416fb8d164e --- /dev/null +++ b/homeassistant/components/radio_browser/translations/ko.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/radio_browser/translations/sk.json b/homeassistant/components/radio_browser/translations/sk.json index c294bc45d7c..46832de8aae 100644 --- a/homeassistant/components/radio_browser/translations/sk.json +++ b/homeassistant/components/radio_browser/translations/sk.json @@ -2,6 +2,11 @@ "config": { "abort": { "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, + "step": { + "user": { + "description": "Chcete do aplik\u00e1cie Home Assistant prida\u0165 preh\u013ead\u00e1va\u010d r\u00e1di\u00ed?" + } } } } \ No newline at end of file diff --git a/homeassistant/components/radiotherm/climate.py b/homeassistant/components/radiotherm/climate.py index f8ccf068f69..a800061b583 100644 --- a/homeassistant/components/radiotherm/climate.py +++ b/homeassistant/components/radiotherm/climate.py @@ -24,7 +24,7 @@ from homeassistant.const import ( ATTR_TEMPERATURE, CONF_HOST, PRECISION_HALVES, - TEMP_FAHRENHEIT, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant, callback import homeassistant.helpers.config_validation as cv @@ -137,10 +137,10 @@ async def async_setup_platform( translation_key="deprecated_yaml", ) _LOGGER.warning( - "Configuration of the Radio Thermostat climate platform in YAML is deprecated and " - "will be removed in Home Assistant 2022.9; Your existing configuration " - "has been imported into the UI automatically and can be safely removed " - "from your configuration.yaml file" + "Configuration of the Radio Thermostat climate platform in YAML is deprecated" + " and will be removed in Home Assistant 2022.9; Your existing configuration has" + " been imported into the UI automatically and can be safely removed from your" + " configuration.yaml file" ) hosts: list[str] = [] @@ -169,7 +169,7 @@ class RadioThermostat(RadioThermostatEntity, ClimateEntity): """Representation of a Radio Thermostat.""" _attr_hvac_modes = OPERATION_LIST - _attr_temperature_unit = TEMP_FAHRENHEIT + _attr_temperature_unit = UnitOfTemperature.FAHRENHEIT _attr_precision = PRECISION_HALVES def __init__(self, coordinator: RadioThermUpdateCoordinator) -> None: diff --git a/homeassistant/components/radiotherm/strings.json b/homeassistant/components/radiotherm/strings.json index 51505d4d727..f0b31cdb4d6 100644 --- a/homeassistant/components/radiotherm/strings.json +++ b/homeassistant/components/radiotherm/strings.json @@ -8,7 +8,7 @@ } }, "confirm": { - "description": "Do you want to setup {name} {model} ({host})?" + "description": "Do you want to set up {name} {model} ({host})?" } }, "error": { diff --git a/homeassistant/components/radiotherm/translations/en.json b/homeassistant/components/radiotherm/translations/en.json index 224c8ffeb3c..d8a963190d0 100644 --- a/homeassistant/components/radiotherm/translations/en.json +++ b/homeassistant/components/radiotherm/translations/en.json @@ -10,7 +10,7 @@ "flow_title": "{name} {model} ({host})", "step": { "confirm": { - "description": "Do you want to setup {name} {model} ({host})?" + "description": "Do you want to set up {name} {model} ({host})?" }, "user": { "data": { diff --git a/homeassistant/components/radiotherm/translations/he.json b/homeassistant/components/radiotherm/translations/he.json index bdf76e7c217..b43d675b5a9 100644 --- a/homeassistant/components/radiotherm/translations/he.json +++ b/homeassistant/components/radiotherm/translations/he.json @@ -15,5 +15,10 @@ } } } + }, + "issues": { + "deprecated_yaml": { + "description": "\u05d4\u05d2\u05d3\u05e8\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05e9\u05dc \u05e4\u05dc\u05d8\u05e4\u05d5\u05e8\u05de\u05ea \u05d4\u05d0\u05e7\u05dc\u05d9\u05dd \u05e8\u05d3\u05d9\u05d5 \u05ea\u05e8\u05de\u05d5\u05e1\u05d8\u05d8 \u05d1\u05d0\u05de\u05e6\u05e2\u05d5\u05ea YAML \u05de\u05d5\u05e1\u05e8\u05ea \u05d1-Home Assistant 2022.9.\n\n\u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05d4\u05e7\u05d9\u05d9\u05de\u05ea \u05e9\u05dc\u05da \u05d9\u05d5\u05d1\u05d0\u05d4 \u05dc\u05de\u05de\u05e9\u05e7 \u05d4\u05de\u05e9\u05ea\u05de\u05e9 \u05d1\u05d0\u05d5\u05e4\u05df \u05d0\u05d5\u05d8\u05d5\u05de\u05d8\u05d9. \u05d9\u05e9 \u05dc\u05d4\u05e1\u05d9\u05e8 \u05d0\u05ea \u05ea\u05e6\u05d5\u05e8\u05ea YAML \u05de\u05e7\u05d5\u05d1\u05e5 configuration.yaml \u05d5\u05dc\u05d4\u05e4\u05e2\u05d9\u05dc \u05de\u05d7\u05d3\u05e9 \u05d0\u05ea Home Assistant \u05db\u05d3\u05d9 \u05dc\u05e4\u05ea\u05d5\u05e8 \u05d1\u05e2\u05d9\u05d4 \u05d6\u05d5." + } } } \ No newline at end of file diff --git a/homeassistant/components/radiotherm/translations/ko.json b/homeassistant/components/radiotherm/translations/ko.json new file mode 100644 index 00000000000..85281856809 --- /dev/null +++ b/homeassistant/components/radiotherm/translations/ko.json @@ -0,0 +1,18 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/radiotherm/translations/no.json b/homeassistant/components/radiotherm/translations/no.json index 4ba3f323973..6f6a993526f 100644 --- a/homeassistant/components/radiotherm/translations/no.json +++ b/homeassistant/components/radiotherm/translations/no.json @@ -10,7 +10,7 @@ "flow_title": "{name} {model} ( {host} )", "step": { "confirm": { - "description": "Vil du konfigurere {name} {model} ( {host} )?" + "description": "Vil du sette opp {name} {model} ( {host} )?" }, "user": { "data": { diff --git a/homeassistant/components/radiotherm/translations/pt.json b/homeassistant/components/radiotherm/translations/pt.json index ae100e45845..632cb64e485 100644 --- a/homeassistant/components/radiotherm/translations/pt.json +++ b/homeassistant/components/radiotherm/translations/pt.json @@ -4,7 +4,15 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { + "cannot_connect": "A liga\u00e7\u00e3o falhou", "unknown": "Erro inesperado" + }, + "step": { + "user": { + "data": { + "host": "Endere\u00e7o" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/radiotherm/translations/sk.json b/homeassistant/components/radiotherm/translations/sk.json index 4595dcba837..87751adff37 100644 --- a/homeassistant/components/radiotherm/translations/sk.json +++ b/homeassistant/components/radiotherm/translations/sk.json @@ -18,5 +18,20 @@ } } } + }, + "issues": { + "deprecated_yaml": { + "description": "Konfigur\u00e1cia klimatiza\u010dnej platformy r\u00e1diov\u00e9ho termostatu pomocou YAML sa odstra\u0148uje z Home Assistant 2022.9. \n\n Va\u0161a existuj\u00faca konfigur\u00e1cia bola importovan\u00e1 do pou\u017e\u00edvate\u013esk\u00e9ho rozhrania automaticky. Ak chcete tento probl\u00e9m vyrie\u0161i\u0165, odstr\u00e1\u0148te konfigur\u00e1ciu YAML zo s\u00faboru configuration.yaml a re\u0161tartujte aplik\u00e1ciu Home Assistant.", + "title": "Konfigur\u00e1cia YAML r\u00e1diov\u00e9ho termostatu sa odstr\u00e1ni" + } + }, + "options": { + "step": { + "init": { + "data": { + "hold_temp": "Pri nastavovan\u00ed teploty nastavte trval\u00e9 podr\u017eanie." + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/rainbird/__init__.py b/homeassistant/components/rainbird/__init__.py index d36cf7786ac..1e80cfb1cbc 100644 --- a/homeassistant/components/rainbird/__init__.py +++ b/homeassistant/components/rainbird/__init__.py @@ -1,13 +1,16 @@ """Support for Rain Bird Irrigation system LNK WiFi Module.""" from __future__ import annotations +import asyncio import logging -from pyrainbird import RainbirdController +from pyrainbird.async_client import ( + AsyncRainbirdClient, + AsyncRainbirdController, + RainbirdApiException, +) import voluptuous as vol -from homeassistant.components.binary_sensor import BinarySensorEntityDescription -from homeassistant.components.sensor import SensorEntityDescription from homeassistant.const import ( CONF_FRIENDLY_NAME, CONF_HOST, @@ -17,49 +20,24 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant from homeassistant.helpers import discovery +from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv from homeassistant.helpers.typing import ConfigType -CONF_ZONES = "zones" +from .const import ( + CONF_ZONES, + RAINBIRD_CONTROLLER, + SENSOR_TYPE_RAINDELAY, + SENSOR_TYPE_RAINSENSOR, +) +from .coordinator import RainbirdUpdateCoordinator PLATFORMS = [Platform.SWITCH, Platform.SENSOR, Platform.BINARY_SENSOR] _LOGGER = logging.getLogger(__name__) -RAINBIRD_CONTROLLER = "controller" -DATA_RAINBIRD = "rainbird" DOMAIN = "rainbird" -SENSOR_TYPE_RAINDELAY = "raindelay" -SENSOR_TYPE_RAINSENSOR = "rainsensor" - - -SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( - SensorEntityDescription( - key=SENSOR_TYPE_RAINSENSOR, - name="Rainsensor", - icon="mdi:water", - ), - SensorEntityDescription( - key=SENSOR_TYPE_RAINDELAY, - name="Raindelay", - icon="mdi:water-off", - ), -) - -BINARY_SENSOR_TYPES: tuple[BinarySensorEntityDescription, ...] = ( - BinarySensorEntityDescription( - key=SENSOR_TYPE_RAINSENSOR, - name="Rainsensor", - icon="mdi:water", - ), - BinarySensorEntityDescription( - key=SENSOR_TYPE_RAINDELAY, - name="Raindelay", - icon="mdi:water-off", - ), -) - TRIGGER_TIME_SCHEMA = vol.All( cv.time_period, cv.positive_timedelta, lambda td: (td.total_seconds() // 60) ) @@ -84,36 +62,46 @@ CONFIG_SCHEMA = vol.Schema( ) -def setup(hass: HomeAssistant, config: ConfigType) -> bool: +async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up the Rain Bird component.""" - - hass.data[DATA_RAINBIRD] = [] - success = False - for controller_config in config[DOMAIN]: - success = success or _setup_controller(hass, controller_config, config) - - return success + return all( + await asyncio.gather( + *[ + _setup_controller(hass, controller_config, config) + for controller_config in config[DOMAIN] + ] + ) + ) -def _setup_controller(hass, controller_config, config): +async def _setup_controller(hass, controller_config, config): """Set up a controller.""" server = controller_config[CONF_HOST] password = controller_config[CONF_PASSWORD] - controller = RainbirdController(server, password) - position = len(hass.data[DATA_RAINBIRD]) + client = AsyncRainbirdClient(async_get_clientsession(hass), server, password) + controller = AsyncRainbirdController(client) try: - controller.get_serial_number() - except Exception as exc: # pylint: disable=broad-except + await controller.get_serial_number() + except RainbirdApiException as exc: _LOGGER.error("Unable to setup controller: %s", exc) return False - hass.data[DATA_RAINBIRD].append(controller) - _LOGGER.debug("Rain Bird Controller %d set to: %s", position, server) + + rain_coordinator = RainbirdUpdateCoordinator(hass, controller.get_rain_sensor_state) + delay_coordinator = RainbirdUpdateCoordinator(hass, controller.get_rain_delay) + for platform in PLATFORMS: - discovery.load_platform( - hass, - platform, - DOMAIN, - {RAINBIRD_CONTROLLER: position, **controller_config}, - config, + hass.async_create_task( + discovery.async_load_platform( + hass, + platform, + DOMAIN, + { + RAINBIRD_CONTROLLER: controller, + SENSOR_TYPE_RAINSENSOR: rain_coordinator, + SENSOR_TYPE_RAINDELAY: delay_coordinator, + **controller_config, + }, + config, + ) ) return True diff --git a/homeassistant/components/rainbird/binary_sensor.py b/homeassistant/components/rainbird/binary_sensor.py index 9b7a3f89da5..02ea8b21bb1 100644 --- a/homeassistant/components/rainbird/binary_sensor.py +++ b/homeassistant/components/rainbird/binary_sensor.py @@ -2,8 +2,7 @@ from __future__ import annotations import logging - -from pyrainbird import RainbirdController +from typing import Union from homeassistant.components.binary_sensor import ( BinarySensorEntity, @@ -12,56 +11,62 @@ from homeassistant.components.binary_sensor import ( from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType +from homeassistant.helpers.update_coordinator import CoordinatorEntity -from . import ( - BINARY_SENSOR_TYPES, - DATA_RAINBIRD, - RAINBIRD_CONTROLLER, - SENSOR_TYPE_RAINDELAY, - SENSOR_TYPE_RAINSENSOR, -) +from .const import SENSOR_TYPE_RAINDELAY, SENSOR_TYPE_RAINSENSOR +from .coordinator import RainbirdUpdateCoordinator _LOGGER = logging.getLogger(__name__) -def setup_platform( +BINARY_SENSOR_TYPES: tuple[BinarySensorEntityDescription, ...] = ( + BinarySensorEntityDescription( + key=SENSOR_TYPE_RAINSENSOR, + name="Rainsensor", + icon="mdi:water", + ), + BinarySensorEntityDescription( + key=SENSOR_TYPE_RAINDELAY, + name="Raindelay", + icon="mdi:water-off", + ), +) + + +async def async_setup_platform( hass: HomeAssistant, config: ConfigType, - add_entities: AddEntitiesCallback, + async_add_entities: AddEntitiesCallback, discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up a Rain Bird sensor.""" if discovery_info is None: return - controller = hass.data[DATA_RAINBIRD][discovery_info[RAINBIRD_CONTROLLER]] - add_entities( + async_add_entities( [ - RainBirdSensor(controller, description) + RainBirdSensor(discovery_info[description.key], description) for description in BINARY_SENSOR_TYPES ], True, ) -class RainBirdSensor(BinarySensorEntity): +class RainBirdSensor( + CoordinatorEntity[RainbirdUpdateCoordinator[Union[int, bool]]], BinarySensorEntity +): """A sensor implementation for Rain Bird device.""" def __init__( self, - controller: RainbirdController, + coordinator: RainbirdUpdateCoordinator[int | bool], description: BinarySensorEntityDescription, ) -> None: """Initialize the Rain Bird sensor.""" + super().__init__(coordinator) self.entity_description = description - self._controller = controller - def update(self) -> None: - """Get the latest data and updates the states.""" - _LOGGER.debug("Updating sensor: %s", self.name) - state = None - if self.entity_description.key == SENSOR_TYPE_RAINSENSOR: - state = self._controller.get_rain_sensor_state() - elif self.entity_description.key == SENSOR_TYPE_RAINDELAY: - state = self._controller.get_rain_delay() - self._attr_is_on = None if state is None else bool(state) + @property + def is_on(self) -> bool | None: + """Return True if entity is on.""" + return None if self.coordinator.data is None else bool(self.coordinator.data) diff --git a/homeassistant/components/rainbird/const.py b/homeassistant/components/rainbird/const.py new file mode 100644 index 00000000000..be06fdb8224 --- /dev/null +++ b/homeassistant/components/rainbird/const.py @@ -0,0 +1,10 @@ +"""Constants for rainbird.""" + +DOMAIN = "rainbird" + +SENSOR_TYPE_RAINDELAY = "raindelay" +SENSOR_TYPE_RAINSENSOR = "rainsensor" + +RAINBIRD_CONTROLLER = "controller" + +CONF_ZONES = "zones" diff --git a/homeassistant/components/rainbird/coordinator.py b/homeassistant/components/rainbird/coordinator.py new file mode 100644 index 00000000000..ee6857fe93c --- /dev/null +++ b/homeassistant/components/rainbird/coordinator.py @@ -0,0 +1,47 @@ +"""Update coordinators for rainbird.""" + +from __future__ import annotations + +from collections.abc import Awaitable, Callable +import datetime +import logging +from typing import TypeVar + +import async_timeout +from pyrainbird.async_client import RainbirdApiException + +from homeassistant.core import HomeAssistant +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed + +TIMEOUT_SECONDS = 20 +UPDATE_INTERVAL = datetime.timedelta(minutes=1) + +_LOGGER = logging.getLogger(__name__) + +_T = TypeVar("_T") + + +class RainbirdUpdateCoordinator(DataUpdateCoordinator[_T]): + """Coordinator for rainbird API calls.""" + + def __init__( + self, + hass: HomeAssistant, + update_method: Callable[[], Awaitable[_T]], + ) -> None: + """Initialize ZoneStateUpdateCoordinator.""" + super().__init__( + hass, + _LOGGER, + name="Rainbird Zones", + update_method=update_method, + update_interval=UPDATE_INTERVAL, + ) + + async def _async_update_data(self) -> _T: + """Fetch data from API endpoint.""" + try: + async with async_timeout.timeout(TIMEOUT_SECONDS): + return await self.update_method() # type: ignore[misc] + except RainbirdApiException as err: + raise UpdateFailed(f"Error communicating with API: {err}") from err diff --git a/homeassistant/components/rainbird/manifest.json b/homeassistant/components/rainbird/manifest.json index 23924bbad18..a7366fac4b5 100644 --- a/homeassistant/components/rainbird/manifest.json +++ b/homeassistant/components/rainbird/manifest.json @@ -2,8 +2,8 @@ "domain": "rainbird", "name": "Rain Bird", "documentation": "https://www.home-assistant.io/integrations/rainbird", - "requirements": ["pyrainbird==0.6.3"], - "codeowners": ["@konikvranik"], + "requirements": ["pyrainbird==0.7.1"], + "codeowners": ["@konikvranik", "@allenporter"], "iot_class": "local_polling", "loggers": ["pyrainbird"] } diff --git a/homeassistant/components/rainbird/sensor.py b/homeassistant/components/rainbird/sensor.py index a3e431418f8..e1dd56d1fb3 100644 --- a/homeassistant/components/rainbird/sensor.py +++ b/homeassistant/components/rainbird/sensor.py @@ -2,29 +2,38 @@ from __future__ import annotations import logging - -from pyrainbird import RainbirdController +from typing import Union from homeassistant.components.sensor import SensorEntity, SensorEntityDescription from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType +from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType, StateType +from homeassistant.helpers.update_coordinator import CoordinatorEntity -from . import ( - DATA_RAINBIRD, - RAINBIRD_CONTROLLER, - SENSOR_TYPE_RAINDELAY, - SENSOR_TYPE_RAINSENSOR, - SENSOR_TYPES, -) +from .const import SENSOR_TYPE_RAINDELAY, SENSOR_TYPE_RAINSENSOR +from .coordinator import RainbirdUpdateCoordinator _LOGGER = logging.getLogger(__name__) -def setup_platform( +SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( + SensorEntityDescription( + key=SENSOR_TYPE_RAINSENSOR, + name="Rainsensor", + icon="mdi:water", + ), + SensorEntityDescription( + key=SENSOR_TYPE_RAINDELAY, + name="Raindelay", + icon="mdi:water-off", + ), +) + + +async def async_setup_platform( hass: HomeAssistant, config: ConfigType, - add_entities: AddEntitiesCallback, + async_add_entities: AddEntitiesCallback, discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up a Rain Bird sensor.""" @@ -32,29 +41,30 @@ def setup_platform( if discovery_info is None: return - controller = hass.data[DATA_RAINBIRD][discovery_info[RAINBIRD_CONTROLLER]] - add_entities( - [RainBirdSensor(controller, description) for description in SENSOR_TYPES], + async_add_entities( + [ + RainBirdSensor(discovery_info[description.key], description) + for description in SENSOR_TYPES + ], True, ) -class RainBirdSensor(SensorEntity): +class RainBirdSensor( + CoordinatorEntity[RainbirdUpdateCoordinator[Union[int, bool]]], SensorEntity +): """A sensor implementation for Rain Bird device.""" def __init__( self, - controller: RainbirdController, + coordinator: RainbirdUpdateCoordinator[int | bool], description: SensorEntityDescription, ) -> None: """Initialize the Rain Bird sensor.""" + super().__init__(coordinator) self.entity_description = description - self._controller = controller - def update(self) -> None: - """Get the latest data and updates the states.""" - _LOGGER.debug("Updating sensor: %s", self.name) - if self.entity_description.key == SENSOR_TYPE_RAINSENSOR: - self._attr_native_value = self._controller.get_rain_sensor_state() - elif self.entity_description.key == SENSOR_TYPE_RAINDELAY: - self._attr_native_value = self._controller.get_rain_delay() + @property + def native_value(self) -> StateType: + """Return the value reported by the sensor.""" + return self.coordinator.data diff --git a/homeassistant/components/rainbird/switch.py b/homeassistant/components/rainbird/switch.py index f18e42b058f..5a9edee2753 100644 --- a/homeassistant/components/rainbird/switch.py +++ b/homeassistant/components/rainbird/switch.py @@ -1,17 +1,26 @@ """Support for Rain Bird Irrigation system LNK WiFi Module.""" from __future__ import annotations -from pyrainbird import AvailableStations, RainbirdController +import logging + +from pyrainbird import AvailableStations +from pyrainbird.async_client import AsyncRainbirdController, RainbirdApiException +from pyrainbird.data import States import voluptuous as vol from homeassistant.components.switch import SwitchEntity from homeassistant.const import ATTR_ENTITY_ID, CONF_FRIENDLY_NAME, CONF_TRIGGER_TIME from homeassistant.core import HomeAssistant, ServiceCall +from homeassistant.exceptions import ConfigEntryNotReady, PlatformNotReady from homeassistant.helpers import config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType +from homeassistant.helpers.update_coordinator import CoordinatorEntity -from . import CONF_ZONES, DATA_RAINBIRD, DOMAIN, RAINBIRD_CONTROLLER +from .const import CONF_ZONES, DOMAIN, RAINBIRD_CONTROLLER +from .coordinator import RainbirdUpdateCoordinator + +_LOGGER = logging.getLogger(__name__) ATTR_DURATION = "duration" @@ -32,10 +41,10 @@ SERVICE_SCHEMA_RAIN_DELAY = vol.Schema( ) -def setup_platform( +async def async_setup_platform( hass: HomeAssistant, config: ConfigType, - add_entities: AddEntitiesCallback, + async_add_entities: AddEntitiesCallback, discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up Rain Bird switches over a Rain Bird controller.""" @@ -43,12 +52,16 @@ def setup_platform( if discovery_info is None: return - controller: RainbirdController = hass.data[DATA_RAINBIRD][ - discovery_info[RAINBIRD_CONTROLLER] - ] - available_stations: AvailableStations = controller.get_available_stations() + controller: AsyncRainbirdController = discovery_info[RAINBIRD_CONTROLLER] + try: + available_stations: AvailableStations = ( + await controller.get_available_stations() + ) + except RainbirdApiException as err: + raise PlatformNotReady(f"Failed to get stations: {str(err)}") from err if not (available_stations and available_stations.stations): return + coordinator = RainbirdUpdateCoordinator(hass, controller.get_zone_states) devices = [] for zone in range(1, available_stations.stations.count + 1): if available_stations.stations.active(zone): @@ -57,6 +70,7 @@ def setup_platform( name = zone_config.get(CONF_FRIENDLY_NAME) devices.append( RainBirdSwitch( + coordinator, controller, zone, time, @@ -64,29 +78,34 @@ def setup_platform( ) ) - add_entities(devices, True) + try: + await coordinator.async_config_entry_first_refresh() + except ConfigEntryNotReady as err: + raise PlatformNotReady(f"Failed to load zone state: {str(err)}") from err - def start_irrigation(service: ServiceCall) -> None: + async_add_entities(devices) + + async def start_irrigation(service: ServiceCall) -> None: entity_id = service.data[ATTR_ENTITY_ID] duration = service.data[ATTR_DURATION] for device in devices: if device.entity_id == entity_id: - device.turn_on(duration=duration) + await device.async_turn_on(duration=duration) - hass.services.register( + hass.services.async_register( DOMAIN, SERVICE_START_IRRIGATION, start_irrigation, schema=SERVICE_SCHEMA_IRRIGATION, ) - def set_rain_delay(service: ServiceCall) -> None: + async def set_rain_delay(service: ServiceCall) -> None: duration = service.data[ATTR_DURATION] - controller.set_rain_delay(duration) + await controller.set_rain_delay(duration) - hass.services.register( + hass.services.async_register( DOMAIN, SERVICE_SET_RAIN_DELAY, set_rain_delay, @@ -94,12 +113,22 @@ def setup_platform( ) -class RainBirdSwitch(SwitchEntity): +class RainBirdSwitch( + CoordinatorEntity[RainbirdUpdateCoordinator[States]], SwitchEntity +): """Representation of a Rain Bird switch.""" - def __init__(self, controller: RainbirdController, zone, time, name): + def __init__( + self, + coordinator: RainbirdUpdateCoordinator[States], + rainbird: AsyncRainbirdController, + zone: int, + time: int, + name: str, + ) -> None: """Initialize a Rain Bird Switch Device.""" - self._rainbird = controller + super().__init__(coordinator) + self._rainbird = rainbird self._zone = zone self._name = name self._state = None @@ -116,24 +145,20 @@ class RainBirdSwitch(SwitchEntity): """Get the name of the switch.""" return self._name - def turn_on(self, **kwargs): + async def async_turn_on(self, **kwargs): """Turn the switch on.""" - if self._rainbird.irrigate_zone( + await self._rainbird.irrigate_zone( int(self._zone), int(kwargs[ATTR_DURATION] if ATTR_DURATION in kwargs else self._duration), - ): - self._state = True + ) + await self.coordinator.async_request_refresh() - def turn_off(self, **kwargs): + async def async_turn_off(self, **kwargs): """Turn the switch off.""" - if self._rainbird.stop_irrigation(): - self._state = False - - def update(self): - """Update switch status.""" - self._state = self._rainbird.get_zone_state(self._zone) + await self._rainbird.stop_irrigation() + await self.coordinator.async_request_refresh() @property def is_on(self): """Return true if switch is on.""" - return self._state + return self.coordinator.data.active(self._zone) diff --git a/homeassistant/components/raincloud/__init__.py b/homeassistant/components/raincloud/__init__.py index 40a9514e1d7..1214fd9416f 100644 --- a/homeassistant/components/raincloud/__init__.py +++ b/homeassistant/components/raincloud/__init__.py @@ -12,8 +12,7 @@ from homeassistant.const import ( CONF_SCAN_INTERVAL, CONF_USERNAME, PERCENTAGE, - TIME_DAYS, - TIME_MINUTES, + UnitOfTime, ) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv @@ -63,9 +62,9 @@ UNIT_OF_MEASUREMENT_MAP = { "is_watering": "", "manual_watering": "", "next_cycle": "", - "rain_delay": TIME_DAYS, + "rain_delay": UnitOfTime.DAYS, "status": "", - "watering_time": TIME_MINUTES, + "watering_time": UnitOfTime.MINUTES, } BINARY_SENSORS = ["is_watering", "status"] @@ -108,7 +107,7 @@ def setup(hass: HomeAssistant, config: ConfigType) -> bool: _LOGGER.error("Unable to connect to Rain Cloud service: %s", str(ex)) persistent_notification.create( hass, - f"Error: {ex}
" "You will need to restart hass after fixing.", + f"Error: {ex}
You will need to restart hass after fixing.", title=NOTIFICATION_TITLE, notification_id=NOTIFICATION_ID, ) diff --git a/homeassistant/components/rainforest_eagle/sensor.py b/homeassistant/components/rainforest_eagle/sensor.py index a2c336bc5d0..0680aa7455d 100644 --- a/homeassistant/components/rainforest_eagle/sensor.py +++ b/homeassistant/components/rainforest_eagle/sensor.py @@ -8,7 +8,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ENERGY_KILO_WATT_HOUR, POWER_KILO_WATT +from homeassistant.const import UnitOfEnergy, UnitOfPower from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -23,21 +23,21 @@ SENSORS = ( key="zigbee:InstantaneousDemand", # We can drop the "Eagle-200" part of the name in HA 2021.12 name="Eagle-200 Meter Power Demand", - native_unit_of_measurement=POWER_KILO_WATT, + native_unit_of_measurement=UnitOfPower.KILO_WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="zigbee:CurrentSummationDelivered", name="Eagle-200 Total Meter Energy Delivered", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), SensorEntityDescription( key="zigbee:CurrentSummationReceived", name="Eagle-200 Total Meter Energy Received", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), @@ -58,7 +58,7 @@ async def async_setup_entry( SensorEntityDescription( key="zigbee:Price", name="Meter Price", - native_unit_of_measurement=f"{coordinator.data['zigbee:PriceCurrency']}/{ENERGY_KILO_WATT_HOUR}", + native_unit_of_measurement=f"{coordinator.data['zigbee:PriceCurrency']}/{UnitOfEnergy.KILO_WATT_HOUR}", state_class=SensorStateClass.MEASUREMENT, ), ) diff --git a/homeassistant/components/rainforest_eagle/translations/ko.json b/homeassistant/components/rainforest_eagle/translations/ko.json index 2da0f2c8ee1..d97c465f184 100644 --- a/homeassistant/components/rainforest_eagle/translations/ko.json +++ b/homeassistant/components/rainforest_eagle/translations/ko.json @@ -4,7 +4,16 @@ "already_configured": "\ub514\ubc14\uc774\uc2a4\uac00 \uc774\ubbf8 \uc124\uc815\ub418\uc5b4 \uc788\uc74c" }, "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc5d0\ub7ec" + }, + "step": { + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/rainforest_eagle/translations/pt.json b/homeassistant/components/rainforest_eagle/translations/pt.json index 91786f4b324..6d81843965d 100644 --- a/homeassistant/components/rainforest_eagle/translations/pt.json +++ b/homeassistant/components/rainforest_eagle/translations/pt.json @@ -4,12 +4,12 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "user": { "data": { - "host": "Servidor" + "host": "Endere\u00e7o" } } } diff --git a/homeassistant/components/rainmachine/__init__.py b/homeassistant/components/rainmachine/__init__.py index 321a3a057af..2b3f642dfe4 100644 --- a/homeassistant/components/rainmachine/__init__.py +++ b/homeassistant/components/rainmachine/__init__.py @@ -507,7 +507,7 @@ async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry) -> None: await hass.config_entries.async_reload(entry.entry_id) -class RainMachineEntity(CoordinatorEntity): +class RainMachineEntity(CoordinatorEntity[RainMachineDataUpdateCoordinator]): """Define a generic RainMachine entity.""" _attr_has_entity_name = True diff --git a/homeassistant/components/rainmachine/sensor.py b/homeassistant/components/rainmachine/sensor.py index 0830fe6bc6a..ba1ce24d472 100644 --- a/homeassistant/components/rainmachine/sensor.py +++ b/homeassistant/components/rainmachine/sensor.py @@ -14,7 +14,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import VOLUME_CUBIC_METERS, VOLUME_LITERS +from homeassistant.const import UnitOfVolume from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -72,7 +72,7 @@ SENSOR_DESCRIPTIONS = ( key=TYPE_FLOW_SENSOR_CLICK_M3, name="Flow sensor clicks per cubic meter", icon="mdi:water-pump", - native_unit_of_measurement=f"clicks/{VOLUME_CUBIC_METERS}", + native_unit_of_measurement=f"clicks/{UnitOfVolume.CUBIC_METERS}", entity_category=EntityCategory.DIAGNOSTIC, entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, @@ -83,8 +83,9 @@ SENSOR_DESCRIPTIONS = ( key=TYPE_FLOW_SENSOR_CONSUMED_LITERS, name="Flow sensor consumed liters", icon="mdi:water-pump", + device_class=SensorDeviceClass.WATER, entity_category=EntityCategory.DIAGNOSTIC, - native_unit_of_measurement=VOLUME_LITERS, + native_unit_of_measurement=UnitOfVolume.LITERS, entity_registry_enabled_default=False, state_class=SensorStateClass.TOTAL_INCREASING, api_category=DATA_PROVISION_SETTINGS, @@ -105,8 +106,9 @@ SENSOR_DESCRIPTIONS = ( key=TYPE_FLOW_SENSOR_LEAK_VOLUME, name="Flow sensor leak volume", icon="mdi:pipe-leak", + device_class=SensorDeviceClass.WATER, entity_category=EntityCategory.DIAGNOSTIC, - native_unit_of_measurement=VOLUME_LITERS, + native_unit_of_measurement=UnitOfVolume.LITERS, entity_registry_enabled_default=False, state_class=SensorStateClass.TOTAL_INCREASING, api_category=DATA_PROVISION_SETTINGS, diff --git a/homeassistant/components/rainmachine/translations/bs.json b/homeassistant/components/rainmachine/translations/bs.json new file mode 100644 index 00000000000..f43ecf4b0b4 --- /dev/null +++ b/homeassistant/components/rainmachine/translations/bs.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "ip_address": "N\u00e1zev hostitele nebo IP adresa" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/rainmachine/translations/sk.json b/homeassistant/components/rainmachine/translations/sk.json index 351e0cf97be..570aa494060 100644 --- a/homeassistant/components/rainmachine/translations/sk.json +++ b/homeassistant/components/rainmachine/translations/sk.json @@ -17,5 +17,16 @@ "title": "Vypl\u0148te svoje \u00fadaje" } } + }, + "options": { + "step": { + "init": { + "data": { + "use_app_run_times": "Pou\u017eite \u010dasy chodu z\u00f3ny z aplik\u00e1cie RainMachine", + "zone_run_time": "Predvolen\u00fd \u010das chodu z\u00f3ny (v sekund\u00e1ch)" + }, + "title": "Konfigur\u00e1cia RainMachine" + } + } } } \ No newline at end of file diff --git a/homeassistant/components/raspberry_pi/__init__.py b/homeassistant/components/raspberry_pi/__init__.py index ab1114722c6..3750a1c7068 100644 --- a/homeassistant/components/raspberry_pi/__init__.py +++ b/homeassistant/components/raspberry_pi/__init__.py @@ -13,7 +13,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: # The hassio integration has not yet fetched data from the supervisor raise ConfigEntryNotReady - board: str + board: str | None if (board := os_info.get("board")) is None or not board.startswith("rpi"): # Not running on a Raspberry Pi, Home Assistant may have been migrated hass.async_create_task(hass.config_entries.async_remove(entry.entry_id)) diff --git a/homeassistant/components/raspberry_pi/hardware.py b/homeassistant/components/raspberry_pi/hardware.py index 61417f751ac..e90316ccb3c 100644 --- a/homeassistant/components/raspberry_pi/hardware.py +++ b/homeassistant/components/raspberry_pi/hardware.py @@ -36,7 +36,7 @@ def async_info(hass: HomeAssistant) -> list[HardwareInfo]: """Return board info.""" if (os_info := get_os_info(hass)) is None: raise HomeAssistantError - board: str + board: str | None if (board := os_info.get("board")) is None: raise HomeAssistantError if not board.startswith("rpi"): diff --git a/homeassistant/components/rdw/binary_sensor.py b/homeassistant/components/rdw/binary_sensor.py index 25ef6b27f1d..13a04515143 100644 --- a/homeassistant/components/rdw/binary_sensor.py +++ b/homeassistant/components/rdw/binary_sensor.py @@ -71,7 +71,9 @@ async def async_setup_entry( ) -class RDWBinarySensorEntity(CoordinatorEntity, BinarySensorEntity): +class RDWBinarySensorEntity( + CoordinatorEntity[DataUpdateCoordinator[Vehicle]], BinarySensorEntity +): """Defines an RDW binary sensor.""" entity_description: RDWBinarySensorEntityDescription @@ -80,7 +82,7 @@ class RDWBinarySensorEntity(CoordinatorEntity, BinarySensorEntity): def __init__( self, *, - coordinator: DataUpdateCoordinator, + coordinator: DataUpdateCoordinator[Vehicle], description: RDWBinarySensorEntityDescription, ) -> None: """Initialize RDW binary sensor.""" diff --git a/homeassistant/components/rdw/sensor.py b/homeassistant/components/rdw/sensor.py index d4cb97005a8..e262665dd63 100644 --- a/homeassistant/components/rdw/sensor.py +++ b/homeassistant/components/rdw/sensor.py @@ -72,7 +72,7 @@ async def async_setup_entry( ) -class RDWSensorEntity(CoordinatorEntity, SensorEntity): +class RDWSensorEntity(CoordinatorEntity[DataUpdateCoordinator[Vehicle]], SensorEntity): """Defines an RDW sensor.""" entity_description: RDWSensorEntityDescription @@ -81,7 +81,7 @@ class RDWSensorEntity(CoordinatorEntity, SensorEntity): def __init__( self, *, - coordinator: DataUpdateCoordinator, + coordinator: DataUpdateCoordinator[Vehicle], license_plate: str, description: RDWSensorEntityDescription, ) -> None: diff --git a/homeassistant/components/rdw/translations/ko.json b/homeassistant/components/rdw/translations/ko.json new file mode 100644 index 00000000000..85b43491dae --- /dev/null +++ b/homeassistant/components/rdw/translations/ko.json @@ -0,0 +1,7 @@ +{ + "config": { + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/rdw/translations/pt.json b/homeassistant/components/rdw/translations/pt.json index 3b5850222d9..439e6f27fc1 100644 --- a/homeassistant/components/rdw/translations/pt.json +++ b/homeassistant/components/rdw/translations/pt.json @@ -1,7 +1,7 @@ { "config": { "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" } } } \ No newline at end of file diff --git a/homeassistant/components/rdw/translations/sk.json b/homeassistant/components/rdw/translations/sk.json index d6fa5e791b4..20a688b1699 100644 --- a/homeassistant/components/rdw/translations/sk.json +++ b/homeassistant/components/rdw/translations/sk.json @@ -1,7 +1,15 @@ { "config": { "error": { - "cannot_connect": "Nepodarilo sa pripoji\u0165" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unknown_license_plate": "Nezn\u00e1ma \u0160PZ" + }, + "step": { + "user": { + "data": { + "license_plate": "\u0160pz" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/recollect_waste/__init__.py b/homeassistant/components/recollect_waste/__init__.py index c4c3f9a1e35..0d7f35b6e62 100644 --- a/homeassistant/components/recollect_waste/__init__.py +++ b/homeassistant/components/recollect_waste/__init__.py @@ -42,7 +42,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: coordinator = DataUpdateCoordinator( hass, LOGGER, - name=f"Place {entry.data[CONF_PLACE_ID]}, Service {entry.data[CONF_SERVICE_ID]}", + name=( + f"Place {entry.data[CONF_PLACE_ID]}, Service {entry.data[CONF_SERVICE_ID]}" + ), update_interval=DEFAULT_UPDATE_INTERVAL, update_method=async_get_pickup_events, ) diff --git a/homeassistant/components/recollect_waste/translations/sk.json b/homeassistant/components/recollect_waste/translations/sk.json index 09913d47d95..ceacb6d9625 100644 --- a/homeassistant/components/recollect_waste/translations/sk.json +++ b/homeassistant/components/recollect_waste/translations/sk.json @@ -9,9 +9,20 @@ "step": { "user": { "data": { + "place_id": "ID miesta", "service_id": "ID slu\u017eby" } } } + }, + "options": { + "step": { + "init": { + "data": { + "friendly_name": "Pou\u017e\u00edvanie priate\u013esk\u00fdch n\u00e1zvov pre typy vyzdvihnutia (ak je to mo\u017en\u00e9)" + }, + "title": "Nakonfigurujte Recollect Waste" + } + } } } \ No newline at end of file diff --git a/homeassistant/components/recorder/core.py b/homeassistant/components/recorder/core.py index a79724f765a..66e85eac2b3 100644 --- a/homeassistant/components/recorder/core.py +++ b/homeassistant/components/recorder/core.py @@ -319,10 +319,12 @@ class Recorder(threading.Thread): if size <= MAX_QUEUE_BACKLOG: return _LOGGER.error( - "The recorder backlog queue reached the maximum size of %s events; " - "usually, the system is CPU bound, I/O bound, or the database " - "is corrupt due to a disk problem; The recorder will stop " - "recording events to avoid running out of memory", + ( + "The recorder backlog queue reached the maximum size of %s events; " + "usually, the system is CPU bound, I/O bound, or the database " + "is corrupt due to a disk problem; The recorder will stop " + "recording events to avoid running out of memory" + ), MAX_QUEUE_BACKLOG, ) self._async_stop_queue_watcher_and_event_listener() @@ -635,8 +637,10 @@ class Recorder(threading.Thread): else: persistent_notification.create( self.hass, - "The database migration failed, check [the logs](/config/logs)." - "Database Migration Failed", + ( + "The database migration failed, check [the logs](/config/logs)." + "Database Migration Failed" + ), "recorder_database_migration", ) self.hass.add_job(self.async_set_db_ready) @@ -722,7 +726,12 @@ class Recorder(threading.Thread): """Migrate schema to the latest version.""" persistent_notification.create( self.hass, - "System performance will temporarily degrade during the database upgrade. Do not power down or restart the system until the upgrade completes. Integrations that read the database, such as logbook and history, may return inconsistent results until the upgrade completes.", + ( + "System performance will temporarily degrade during the database" + " upgrade. Do not power down or restart the system until the upgrade" + " completes. Integrations that read the database, such as logbook and" + " history, may return inconsistent results until the upgrade completes." + ), "Database upgrade in progress", "recorder_database_migration", ) diff --git a/homeassistant/components/recorder/db_schema.py b/homeassistant/components/recorder/db_schema.py index d2373d96aeb..2c8c1ad2fff 100644 --- a/homeassistant/components/recorder/db_schema.py +++ b/homeassistant/components/recorder/db_schema.py @@ -165,7 +165,7 @@ class Events(Base): # type: ignore[misc,valid-type] def __repr__(self) -> str: """Return string representation of instance for debugging.""" return ( - f"" @@ -222,9 +222,9 @@ class EventData(Base): # type: ignore[misc,valid-type] def __repr__(self) -> str: """Return string representation of instance for debugging.""" return ( - f"" + ")>" ) @staticmethod @@ -290,12 +290,10 @@ class States(Base): # type: ignore[misc,valid-type] def __repr__(self) -> str: """Return string representation of instance for debugging.""" return ( - f"" + f"" ) @staticmethod @@ -374,9 +372,8 @@ class StateAttributes(Base): # type: ignore[misc,valid-type] def __repr__(self) -> str: """Return string representation of instance for debugging.""" return ( - f"" + f"" ) @staticmethod @@ -522,11 +519,10 @@ class RecorderRuns(Base): # type: ignore[misc,valid-type] f"'{self.end.isoformat(sep=' ', timespec='seconds')}'" if self.end else None ) return ( - f"" + f"" ) def entity_ids(self, point_in_time: datetime | None = None) -> list[str]: @@ -566,10 +562,10 @@ class SchemaChanges(Base): # type: ignore[misc,valid-type] def __repr__(self) -> str: """Return string representation of instance for debugging.""" return ( - f"" + ")>" ) @@ -583,9 +579,8 @@ class StatisticsRuns(Base): # type: ignore[misc,valid-type] def __repr__(self) -> str: """Return string representation of instance for debugging.""" return ( - f"" + f"" ) diff --git a/homeassistant/components/recorder/filters.py b/homeassistant/components/recorder/filters.py index 72e9916b6a7..72cfa784074 100644 --- a/homeassistant/components/recorder/filters.py +++ b/homeassistant/components/recorder/filters.py @@ -92,7 +92,8 @@ class Filters: def __repr__(self) -> str: """Return human readable excludes/includes.""" return ( - f"" diff --git a/homeassistant/components/recorder/migration.py b/homeassistant/components/recorder/migration.py index 28952f127e2..5b4b3afb3d9 100644 --- a/homeassistant/components/recorder/migration.py +++ b/homeassistant/components/recorder/migration.py @@ -182,9 +182,11 @@ def _create_index( index = index_list[0] _LOGGER.debug("Creating %s index", index_name) _LOGGER.warning( - "Adding index `%s` to database. Note: this can take several " - "minutes on large databases and slow computers. Please " - "be patient!", + ( + "Adding index `%s` to database. Note: this can take several " + "minutes on large databases and slow computers. Please " + "be patient!" + ), index_name, ) with session_scope(session=session_maker()) as session: @@ -271,9 +273,11 @@ def _drop_index( return _LOGGER.warning( - "Failed to drop index %s from table %s. Schema " - "Migration will continue; this is not a " - "critical operation", + ( + "Failed to drop index %s from table %s. Schema " + "Migration will continue; this is not a " + "critical operation" + ), index_name, table_name, ) @@ -284,9 +288,11 @@ def _add_columns( ) -> None: """Add columns to a table.""" _LOGGER.warning( - "Adding columns %s to table %s. Note: this can take several " - "minutes on large databases and slow computers. Please " - "be patient!", + ( + "Adding columns %s to table %s. Note: this can take several " + "minutes on large databases and slow computers. Please " + "be patient!" + ), ", ".join(column.split(" ")[0] for column in columns_def), table_name, ) @@ -338,18 +344,22 @@ def _modify_columns( """Modify columns in a table.""" if engine.dialect.name == SupportedDialect.SQLITE: _LOGGER.debug( - "Skipping to modify columns %s in table %s; " - "Modifying column length in SQLite is unnecessary, " - "it does not impose any length restrictions", + ( + "Skipping to modify columns %s in table %s; " + "Modifying column length in SQLite is unnecessary, " + "it does not impose any length restrictions" + ), ", ".join(column.split(" ")[0] for column in columns_def), table_name, ) return _LOGGER.warning( - "Modifying columns %s in table %s. Note: this can take several " - "minutes on large databases and slow computers. Please " - "be patient!", + ( + "Modifying columns %s in table %s. Note: this can take several " + "minutes on large databases and slow computers. Please " + "be patient!" + ), ", ".join(column.split(" ")[0] for column in columns_def), table_name, ) @@ -636,9 +646,11 @@ def _apply_update( # noqa: C901 if engine.dialect.name == SupportedDialect.MYSQL: for table in ("events", "states", "statistics_meta"): _LOGGER.warning( - "Updating character set and collation of table %s to utf8mb4. " - "Note: this can take several minutes on large databases and slow " - "computers. Please be patient!", + ( + "Updating character set and collation of table %s to utf8mb4." + " Note: this can take several minutes on large databases and" + " slow computers. Please be patient!" + ), table, ) with contextlib.suppress(SQLAlchemyError): @@ -648,8 +660,8 @@ def _apply_update( # noqa: C901 # Using LOCK=EXCLUSIVE to prevent the database from corrupting # https://github.com/home-assistant/core/issues/56104 text( - f"ALTER TABLE {table} CONVERT TO " - "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, LOCK=EXCLUSIVE" + f"ALTER TABLE {table} CONVERT TO CHARACTER SET utf8mb4" + " COLLATE utf8mb4_unicode_ci, LOCK=EXCLUSIVE" ) ) elif new_version == 22: diff --git a/homeassistant/components/recorder/pool.py b/homeassistant/components/recorder/pool.py index 35a8623a1c1..4a0fbddc10b 100644 --- a/homeassistant/components/recorder/pool.py +++ b/homeassistant/components/recorder/pool.py @@ -81,9 +81,11 @@ class RecorderPool(SingletonThreadPool, NullPool): # type: ignore[misc] def _do_get_db_connection_protected(self) -> Any: report( - "accesses the database without the database executor; " - f"{ADVISE_MSG} " - "for faster database operations", + ( + "accesses the database without the database executor; " + f"{ADVISE_MSG} " + "for faster database operations" + ), exclude_integrations={"recorder"}, error_if_core=False, ) diff --git a/homeassistant/components/recorder/purge.py b/homeassistant/components/recorder/purge.py index c470575c5f1..30f4d34e331 100644 --- a/homeassistant/components/recorder/purge.py +++ b/homeassistant/components/recorder/purge.py @@ -89,14 +89,16 @@ def purge_old_data( has_more_to_purge = False if _purging_legacy_format(session): _LOGGER.debug( - "Purge running in legacy format as there are states with event_id remaining" + "Purge running in legacy format as there are states with event_id" + " remaining" ) has_more_to_purge |= _purge_legacy_format( instance, session, purge_before, using_sqlite ) else: _LOGGER.debug( - "Purge running in new format as there are NO states with event_id remaining" + "Purge running in new format as there are NO states with event_id" + " remaining" ) # Once we are done purging legacy rows, we use the new method has_more_to_purge |= _purge_states_and_attributes_ids( diff --git a/homeassistant/components/recorder/statistics.py b/homeassistant/components/recorder/statistics.py index dea454a5a33..fab4a67a749 100644 --- a/homeassistant/components/recorder/statistics.py +++ b/homeassistant/components/recorder/statistics.py @@ -508,8 +508,10 @@ def delete_statistics_duplicates(hass: HomeAssistant, session: Session) -> None: cls=JSONEncoder, ) _LOGGER.warning( - "Deleted %s non identical duplicated %s rows, a backup of the deleted rows " - "has been saved to %s", + ( + "Deleted %s non identical duplicated %s rows, a backup of the deleted" + " rows has been saved to %s" + ), len(non_identical_duplicates), Statistics.__tablename__, backup_path, @@ -2070,7 +2072,10 @@ def _filter_unique_constraint_integrity_error( if ignore: _LOGGER.warning( - "Blocked attempt to insert duplicated statistic rows, please report at %s", + ( + "Blocked attempt to insert duplicated statistic rows, please report" + " at %s" + ), "https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue+label%3A%22integration%3A+recorder%22", exc_info=err, ) @@ -2415,9 +2420,11 @@ def correct_db_schema( if "statistics_meta.4-byte UTF-8" in schema_errors: # Attempt to convert the table to utf8mb4 _LOGGER.warning( - "Updating character set and collation of table %s to utf8mb4. " - "Note: this can take several minutes on large databases and slow " - "computers. Please be patient!", + ( + "Updating character set and collation of table %s to utf8mb4. " + "Note: this can take several minutes on large databases and slow " + "computers. Please be patient!" + ), "statistics_meta", ) with contextlib.suppress(SQLAlchemyError): @@ -2427,8 +2434,8 @@ def correct_db_schema( # Using LOCK=EXCLUSIVE to prevent the database from corrupting # https://github.com/home-assistant/core/issues/56104 text( - "ALTER TABLE statistics_meta CONVERT TO " - "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, LOCK=EXCLUSIVE" + "ALTER TABLE statistics_meta CONVERT TO CHARACTER SET utf8mb4" + " COLLATE utf8mb4_unicode_ci, LOCK=EXCLUSIVE" ) ) diff --git a/homeassistant/components/recorder/translations/sk.json b/homeassistant/components/recorder/translations/sk.json index 69acec2ee46..3f019a4cb17 100644 --- a/homeassistant/components/recorder/translations/sk.json +++ b/homeassistant/components/recorder/translations/sk.json @@ -2,7 +2,10 @@ "system_health": { "info": { "current_recorder_run": "Aktu\u00e1lny \u010das spustenia", - "database_version": "Verzia datab\u00e1zy" + "database_engine": "Datab\u00e1zov\u00fd engine", + "database_version": "Verzia datab\u00e1zy", + "estimated_db_size": "Odhadovan\u00e1 ve\u013ekos\u0165 datab\u00e1zy (MiB)", + "oldest_recorder_run": "Najstar\u0161\u00ed \u010das za\u010diatku vykon\u00e1vania" } } } \ No newline at end of file diff --git a/homeassistant/components/recorder/util.py b/homeassistant/components/recorder/util.py index a52a067b975..ffef4d64773 100644 --- a/homeassistant/components/recorder/util.py +++ b/homeassistant/components/recorder/util.py @@ -295,7 +295,10 @@ def run_checks_on_open_db(dbpath: str, cursor: CursorFetchStrategy) -> None: if not last_run_was_clean: _LOGGER.warning( - "The system could not validate that the sqlite3 database at %s was shutdown cleanly", + ( + "The system could not validate that the sqlite3 database at %s was" + " shutdown cleanly" + ), dbpath, ) @@ -307,7 +310,10 @@ def move_away_broken_database(dbfile: str) -> None: corrupt_postfix = f".corrupt.{isotime}" _LOGGER.error( - "The system will rename the corrupt database file %s to %s in order to allow startup to proceed", + ( + "The system will rename the corrupt database file %s to %s in order to" + " allow startup to proceed" + ), dbfile, f"{dbfile}{corrupt_postfix}", ) @@ -338,9 +344,11 @@ def query_on_connection(dbapi_connection: Any, statement: str) -> Any: def _fail_unsupported_dialect(dialect_name: str) -> None: """Warn about unsupported database version.""" _LOGGER.error( - "Database %s is not supported; Home Assistant supports %s. " - "Starting with Home Assistant 2022.6 this prevents the recorder from " - "starting. Please migrate your database to a supported software", + ( + "Database %s is not supported; Home Assistant supports %s. " + "Starting with Home Assistant 2022.6 this prevents the recorder from " + "starting. Please migrate your database to a supported software" + ), dialect_name, "MariaDB ≥ 10.3, MySQL ≥ 8.0, PostgreSQL ≥ 12, SQLite ≥ 3.31.0", ) @@ -352,9 +360,11 @@ def _fail_unsupported_version( ) -> None: """Warn about unsupported database version.""" _LOGGER.error( - "Version %s of %s is not supported; minimum supported version is %s. " - "Starting with Home Assistant 2022.6 this prevents the recorder from " - "starting. Please upgrade your database software", + ( + "Version %s of %s is not supported; minimum supported version is %s. " + "Starting with Home Assistant 2022.6 this prevents the recorder from " + "starting. Please upgrade your database software" + ), server_version, dialect_name, minimum_version, diff --git a/homeassistant/components/rejseplanen/sensor.py b/homeassistant/components/rejseplanen/sensor.py index fa1f9cc0b24..9ef8b5dc370 100644 --- a/homeassistant/components/rejseplanen/sensor.py +++ b/homeassistant/components/rejseplanen/sensor.py @@ -15,7 +15,7 @@ import rjpl import voluptuous as vol from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity -from homeassistant.const import CONF_NAME, TIME_MINUTES +from homeassistant.const import CONF_NAME, UnitOfTime from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -142,7 +142,7 @@ class RejseplanenTransportSensor(SensorEntity): @property def native_unit_of_measurement(self): """Return the unit this state is expressed in.""" - return TIME_MINUTES + return UnitOfTime.MINUTES @property def icon(self): diff --git a/homeassistant/components/remember_the_milk/__init__.py b/homeassistant/components/remember_the_milk/__init__.py index 3331f9c61d4..21a386c6598 100644 --- a/homeassistant/components/remember_the_milk/__init__.py +++ b/homeassistant/components/remember_the_milk/__init__.py @@ -328,8 +328,10 @@ class RememberTheMilk(Entity): rtm_id = self._rtm_config.get_rtm_id(self._name, hass_id) if rtm_id is None: _LOGGER.error( - "Could not find task with ID %s in account %s. " - "So task could not be closed", + ( + "Could not find task with ID %s in account %s. " + "So task could not be closed" + ), hass_id, self._name, ) diff --git a/homeassistant/components/remote/translations/sk.json b/homeassistant/components/remote/translations/sk.json index 4c8b090aea1..d98318376c6 100644 --- a/homeassistant/components/remote/translations/sk.json +++ b/homeassistant/components/remote/translations/sk.json @@ -1,7 +1,18 @@ { "device_automation": { + "action_type": { + "toggle": "Prepn\u00fa\u0165 {entity_name}", + "turn_off": "Vypn\u00fa\u0165 {entity_name}", + "turn_on": "Zapn\u00fa\u0165 {entity_name}" + }, + "condition_type": { + "is_off": "{entity_name} je vypnut\u00e9", + "is_on": "{entity_name} je zapnut\u00e9" + }, "trigger_type": { - "changed_states": "{entity_name} zapnut\u00e9 alebo vypnut\u00e9" + "changed_states": "{entity_name} zapnut\u00e9 alebo vypnut\u00e9", + "turned_off": "{entity_name} vypnut\u00e9", + "turned_on": "{entity_name} zapnut\u00e9" } }, "state": { diff --git a/homeassistant/components/renault/const.py b/homeassistant/components/renault/const.py index b29f4ad0701..8e68eac01eb 100644 --- a/homeassistant/components/renault/const.py +++ b/homeassistant/components/renault/const.py @@ -15,7 +15,3 @@ PLATFORMS = [ Platform.SELECT, Platform.SENSOR, ] - -DEVICE_CLASS_PLUG_STATE = "renault__plug_state" -DEVICE_CLASS_CHARGE_STATE = "renault__charge_state" -DEVICE_CLASS_CHARGE_MODE = "renault__charge_mode" diff --git a/homeassistant/components/renault/select.py b/homeassistant/components/renault/select.py index 2a1e207695c..1d34c9fdf2b 100644 --- a/homeassistant/components/renault/select.py +++ b/homeassistant/components/renault/select.py @@ -13,7 +13,7 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import StateType -from .const import DEVICE_CLASS_CHARGE_MODE, DOMAIN +from .const import DOMAIN from .renault_entities import RenaultDataEntity, RenaultDataEntityDescription from .renault_hub import RenaultHub @@ -90,7 +90,7 @@ SENSOR_TYPES: tuple[RenaultSelectEntityDescription, ...] = ( key="charge_mode", coordinator="charge_mode", data_key="chargeMode", - device_class=DEVICE_CLASS_CHARGE_MODE, + translation_key="charge_mode", icon_lambda=_get_charge_mode_icon, name="Charge mode", options=["always", "always_charging", "schedule_mode"], diff --git a/homeassistant/components/renault/sensor.py b/homeassistant/components/renault/sensor.py index 436b9209e89..679fbbd370f 100644 --- a/homeassistant/components/renault/sensor.py +++ b/homeassistant/components/renault/sensor.py @@ -23,20 +23,20 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - ENERGY_KILO_WATT_HOUR, - LENGTH_KILOMETERS, PERCENTAGE, - POWER_KILO_WATT, - TEMP_CELSIUS, - TIME_MINUTES, - VOLUME_LITERS, + UnitOfEnergy, + UnitOfLength, + UnitOfPower, + UnitOfTemperature, + UnitOfTime, + UnitOfVolume, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import StateType from homeassistant.util.dt import as_utc, parse_datetime -from .const import DEVICE_CLASS_CHARGE_STATE, DEVICE_CLASS_PLUG_STATE, DOMAIN +from .const import DOMAIN from .renault_coordinator import T from .renault_entities import RenaultDataEntity, RenaultDataEntityDescription from .renault_hub import RenaultHub @@ -171,10 +171,21 @@ SENSOR_TYPES: tuple[RenaultSensorEntityDescription[Any], ...] = ( key="charge_state", coordinator="battery", data_key="chargingStatus", - device_class=DEVICE_CLASS_CHARGE_STATE, + translation_key="charge_state", + device_class=SensorDeviceClass.ENUM, entity_class=RenaultSensor[KamereonVehicleBatteryStatusData], icon_lambda=_get_charge_state_icon, name="Charge state", + options=[ + "not_in_charge", + "waiting_for_a_planned_charge", + "charge_ended", + "waiting_for_current_charge", + "energy_flap_opened", + "charge_in_progress", + "charge_error", + "unavailable", + ], value_lambda=_get_charge_state_formatted, ), RenaultSensorEntityDescription( @@ -184,7 +195,7 @@ SENSOR_TYPES: tuple[RenaultSensorEntityDescription[Any], ...] = ( entity_class=RenaultSensor[KamereonVehicleBatteryStatusData], icon="mdi:timer", name="Charging remaining time", - native_unit_of_measurement=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, state_class=SensorStateClass.MEASUREMENT, ), RenaultSensorEntityDescription( @@ -198,7 +209,7 @@ SENSOR_TYPES: tuple[RenaultSensorEntityDescription[Any], ...] = ( device_class=SensorDeviceClass.POWER, entity_class=RenaultSensor[KamereonVehicleBatteryStatusData], name="Admissible charging power", - native_unit_of_measurement=POWER_KILO_WATT, + native_unit_of_measurement=UnitOfPower.KILO_WATT, state_class=SensorStateClass.MEASUREMENT, ), RenaultSensorEntityDescription( @@ -211,7 +222,7 @@ SENSOR_TYPES: tuple[RenaultSensorEntityDescription[Any], ...] = ( device_class=SensorDeviceClass.POWER, entity_class=RenaultSensor[KamereonVehicleBatteryStatusData], name="Charging power", - native_unit_of_measurement=POWER_KILO_WATT, + native_unit_of_measurement=UnitOfPower.KILO_WATT, state_class=SensorStateClass.MEASUREMENT, value_lambda=_get_charging_power, ), @@ -219,10 +230,12 @@ SENSOR_TYPES: tuple[RenaultSensorEntityDescription[Any], ...] = ( key="plug_state", coordinator="battery", data_key="plugStatus", - device_class=DEVICE_CLASS_PLUG_STATE, + translation_key="plug_state", + device_class=SensorDeviceClass.ENUM, entity_class=RenaultSensor[KamereonVehicleBatteryStatusData], icon_lambda=_get_plug_state_icon, name="Plug state", + options=["unplugged", "plugged", "plug_error", "plug_unknown"], value_lambda=_get_plug_state_formatted, ), RenaultSensorEntityDescription( @@ -233,7 +246,7 @@ SENSOR_TYPES: tuple[RenaultSensorEntityDescription[Any], ...] = ( entity_class=RenaultSensor[KamereonVehicleBatteryStatusData], icon="mdi:ev-station", name="Battery autonomy", - native_unit_of_measurement=LENGTH_KILOMETERS, + native_unit_of_measurement=UnitOfLength.KILOMETERS, state_class=SensorStateClass.MEASUREMENT, ), RenaultSensorEntityDescription( @@ -243,7 +256,7 @@ SENSOR_TYPES: tuple[RenaultSensorEntityDescription[Any], ...] = ( entity_class=RenaultSensor[KamereonVehicleBatteryStatusData], device_class=SensorDeviceClass.ENERGY, name="Battery available energy", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, state_class=SensorStateClass.MEASUREMENT, ), RenaultSensorEntityDescription( @@ -253,7 +266,7 @@ SENSOR_TYPES: tuple[RenaultSensorEntityDescription[Any], ...] = ( device_class=SensorDeviceClass.TEMPERATURE, entity_class=RenaultSensor[KamereonVehicleBatteryStatusData], name="Battery temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, ), RenaultSensorEntityDescription( @@ -274,7 +287,7 @@ SENSOR_TYPES: tuple[RenaultSensorEntityDescription[Any], ...] = ( entity_class=RenaultSensor[KamereonVehicleCockpitData], icon="mdi:sign-direction", name="Mileage", - native_unit_of_measurement=LENGTH_KILOMETERS, + native_unit_of_measurement=UnitOfLength.KILOMETERS, state_class=SensorStateClass.TOTAL_INCREASING, value_lambda=_get_rounded_value, ), @@ -286,7 +299,7 @@ SENSOR_TYPES: tuple[RenaultSensorEntityDescription[Any], ...] = ( entity_class=RenaultSensor[KamereonVehicleCockpitData], icon="mdi:gas-station", name="Fuel autonomy", - native_unit_of_measurement=LENGTH_KILOMETERS, + native_unit_of_measurement=UnitOfLength.KILOMETERS, state_class=SensorStateClass.MEASUREMENT, requires_fuel=True, value_lambda=_get_rounded_value, @@ -299,7 +312,7 @@ SENSOR_TYPES: tuple[RenaultSensorEntityDescription[Any], ...] = ( entity_class=RenaultSensor[KamereonVehicleCockpitData], icon="mdi:fuel", name="Fuel quantity", - native_unit_of_measurement=VOLUME_LITERS, + native_unit_of_measurement=UnitOfVolume.LITERS, state_class=SensorStateClass.MEASUREMENT, requires_fuel=True, value_lambda=_get_rounded_value, @@ -311,7 +324,7 @@ SENSOR_TYPES: tuple[RenaultSensorEntityDescription[Any], ...] = ( data_key="externalTemperature", entity_class=RenaultSensor[KamereonVehicleHvacStatusData], name="Outside temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, ), RenaultSensorEntityDescription( diff --git a/homeassistant/components/renault/strings.json b/homeassistant/components/renault/strings.json index 30a356b7c42..b28f1727c2f 100644 --- a/homeassistant/components/renault/strings.json +++ b/homeassistant/components/renault/strings.json @@ -31,5 +31,38 @@ "title": "Set Renault credentials" } } + }, + "entity": { + "select": { + "charge_mode": { + "state": { + "always": "Instant", + "always_charging": "Instant", + "schedule_mode": "Planner" + } + } + }, + "sensor": { + "plug_state": { + "state": { + "unplugged": "Unplugged", + "plugged": "Plugged in", + "plug_error": "Plug error", + "plug_unknown": "Plug unknown" + } + }, + "charge_state": { + "state": { + "not_in_charge": "Not charging", + "waiting_for_a_planned_charge": "Waiting for planned charge", + "charge_ended": "Charge ended", + "waiting_for_current_charge": "Waiting for current charge", + "energy_flap_opened": "Energy flap opened", + "charge_in_progress": "Charging", + "charge_error": "Not charging or plugged in", + "unavailable": "Unavailable" + } + } + } } } diff --git a/homeassistant/components/renault/translations/ca.json b/homeassistant/components/renault/translations/ca.json index 694b84e7c1b..9d5c0bf95f8 100644 --- a/homeassistant/components/renault/translations/ca.json +++ b/homeassistant/components/renault/translations/ca.json @@ -31,5 +31,38 @@ "title": "Defineix les credencials de Renault" } } + }, + "entity": { + "select": { + "charge_mode": { + "state": { + "always": "Instantani", + "always_charging": "Instantani", + "schedule_mode": "Programat" + } + } + }, + "sensor": { + "charge_state": { + "state": { + "charge_ended": "C\u00e0rrega finalitzada", + "charge_error": "No carregant ni endollat", + "charge_in_progress": "Carregant", + "energy_flap_opened": "Tapa d'entrada d'energia oberta", + "not_in_charge": "No carregant", + "unavailable": "No disponible", + "waiting_for_a_planned_charge": "Esperant c\u00e0rrega programada", + "waiting_for_current_charge": "Esperant c\u00e0rrega actual" + } + }, + "plug_state": { + "state": { + "plug_error": "Error d'endoll", + "plug_unknown": "Endoll desconegut", + "plugged": "Endollat", + "unplugged": "Desendollat" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/renault/translations/de.json b/homeassistant/components/renault/translations/de.json index 808d8b174f8..3577aab2617 100644 --- a/homeassistant/components/renault/translations/de.json +++ b/homeassistant/components/renault/translations/de.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Konto wurde bereits konfiguriert", - "kamereon_no_account": "Kamereon-Konto kann nicht gefunden werden.", + "kamereon_no_account": "Kamereon Konto kann nicht gefunden werden.", "reauth_successful": "Die erneute Authentifizierung war erfolgreich" }, "error": { @@ -11,9 +11,9 @@ "step": { "kamereon": { "data": { - "kamereon_account_id": "Kamereon-Kontonummer" + "kamereon_account_id": "Kamereon Kontonummer" }, - "title": "Kamereon-Kontonummer ausw\u00e4hlen" + "title": "Kamereon Kontonummer ausw\u00e4hlen" }, "reauth_confirm": { "data": { @@ -31,5 +31,38 @@ "title": "Renault-Anmeldeinformationen festlegen" } } + }, + "entity": { + "select": { + "charge_mode": { + "state": { + "always": "Sofort", + "always_charging": "Sofort", + "schedule_mode": "Planer" + } + } + }, + "sensor": { + "charge_state": { + "state": { + "charge_ended": "Ladung beendet", + "charge_error": "Nicht aufgeladen oder eingesteckt", + "charge_in_progress": "L\u00e4dt", + "energy_flap_opened": "Energieklappe ge\u00f6ffnet", + "not_in_charge": "L\u00e4dt nicht", + "unavailable": "Nicht verf\u00fcgbar", + "waiting_for_a_planned_charge": "Warten auf geplante Ladung", + "waiting_for_current_charge": "Warten auf aktuelle Ladung" + } + }, + "plug_state": { + "state": { + "plug_error": "Steckerfehler", + "plug_unknown": "Stecker unbekannt", + "plugged": "Eingesteckt", + "unplugged": "Ausgesteckt" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/renault/translations/el.json b/homeassistant/components/renault/translations/el.json index 1d8c0e543fe..38cca63c9b9 100644 --- a/homeassistant/components/renault/translations/el.json +++ b/homeassistant/components/renault/translations/el.json @@ -31,5 +31,38 @@ "title": "\u039f\u03c1\u03b9\u03c3\u03bc\u03cc\u03c2 \u03b4\u03b9\u03b1\u03c0\u03b9\u03c3\u03c4\u03b5\u03c5\u03c4\u03b7\u03c1\u03af\u03c9\u03bd Renault" } } + }, + "entity": { + "select": { + "charge_mode": { + "state": { + "always": "\u0386\u03bc\u03b5\u03c3\u03b7", + "always_charging": "\u0386\u03bc\u03b5\u03c3\u03b7", + "schedule_mode": "\u03a3\u03c7\u03b5\u03b4\u03b9\u03b1\u03c3\u03c4\u03ae\u03c2" + } + } + }, + "sensor": { + "charge_state": { + "state": { + "charge_ended": "\u0397 \u03c6\u03cc\u03c1\u03c4\u03b9\u03c3\u03b7 \u03ad\u03bb\u03b7\u03be\u03b5", + "charge_error": "\u0394\u03b5\u03bd \u03c6\u03bf\u03c1\u03c4\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03bf\u03cd\u03c4\u03b5 \u03b5\u03af\u03bd\u03b1\u03b9 \u03c3\u03c5\u03bd\u03b4\u03b5\u03b4\u03b5\u03bc\u03ad\u03bd\u03bf", + "charge_in_progress": "\u03a6\u03bf\u03c1\u03c4\u03af\u03b6\u03b5\u03b9", + "energy_flap_opened": "\u03a4\u03bf \u03b5\u03bd\u03b5\u03c1\u03b3\u03b5\u03b9\u03b1\u03ba\u03cc \u03c0\u03c4\u03b5\u03c1\u03cd\u03b3\u03b9\u03bf \u03ac\u03bd\u03bf\u03b9\u03be\u03b5", + "not_in_charge": "\u0394\u03b5\u03bd \u03c6\u03bf\u03c1\u03c4\u03af\u03b6\u03b5\u03b9", + "unavailable": "\u039c\u03b7 \u03b4\u03b9\u03b1\u03b8\u03ad\u03c3\u03b9\u03bc\u03bf", + "waiting_for_a_planned_charge": "\u0391\u03bd\u03b1\u03bc\u03bf\u03bd\u03ae \u03b3\u03b9\u03b1 \u03c0\u03c1\u03bf\u03b3\u03c1\u03b1\u03bc\u03bc\u03b1\u03c4\u03b9\u03c3\u03bc\u03ad\u03bd\u03b7 \u03c6\u03cc\u03c1\u03c4\u03b9\u03c3\u03b7", + "waiting_for_current_charge": "\u0391\u03bd\u03b1\u03bc\u03bf\u03bd\u03ae \u03b3\u03b9\u03b1 \u03c4\u03c1\u03ad\u03c7\u03bf\u03c5\u03c3\u03b1 \u03c6\u03cc\u03c1\u03c4\u03b9\u03c3\u03b7" + } + }, + "plug_state": { + "state": { + "plug_error": "\u03a3\u03c6\u03ac\u03bb\u03bc\u03b1 \u03b2\u03cd\u03c3\u03bc\u03b1\u03c4\u03bf\u03c2", + "plug_unknown": "\u0392\u03cd\u03c3\u03bc\u03b1 \u03ac\u03b3\u03bd\u03c9\u03c3\u03c4\u03bf", + "plugged": "\u03a3\u03c4\u03b7\u03bd \u03c0\u03c1\u03af\u03b6\u03b1", + "unplugged": "\u0391\u03c0\u03bf\u03c3\u03c5\u03bd\u03b4\u03b5\u03b4\u03b5\u03bc\u03ad\u03bd\u03bf" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/renault/translations/en.json b/homeassistant/components/renault/translations/en.json index 104a3f0ba64..f8b57951cc4 100644 --- a/homeassistant/components/renault/translations/en.json +++ b/homeassistant/components/renault/translations/en.json @@ -31,5 +31,38 @@ "title": "Set Renault credentials" } } + }, + "entity": { + "select": { + "charge_mode": { + "state": { + "always": "Instant", + "always_charging": "Instant", + "schedule_mode": "Planner" + } + } + }, + "sensor": { + "charge_state": { + "state": { + "charge_ended": "Charge ended", + "charge_error": "Not charging or plugged in", + "charge_in_progress": "Charging", + "energy_flap_opened": "Energy flap opened", + "not_in_charge": "Not charging", + "unavailable": "Unavailable", + "waiting_for_a_planned_charge": "Waiting for planned charge", + "waiting_for_current_charge": "Waiting for current charge" + } + }, + "plug_state": { + "state": { + "plug_error": "Plug error", + "plug_unknown": "Plug unknown", + "plugged": "Plugged in", + "unplugged": "Unplugged" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/renault/translations/es.json b/homeassistant/components/renault/translations/es.json index fc110deb15b..a239e4510e6 100644 --- a/homeassistant/components/renault/translations/es.json +++ b/homeassistant/components/renault/translations/es.json @@ -31,5 +31,38 @@ "title": "Establecer las credenciales de Renault" } } + }, + "entity": { + "select": { + "charge_mode": { + "state": { + "always": "Instant\u00e1nea", + "always_charging": "Instant\u00e1nea", + "schedule_mode": "Planificador" + } + } + }, + "sensor": { + "charge_state": { + "state": { + "charge_ended": "Carga finalizada", + "charge_error": "No est\u00e1 cargando ni est\u00e1 enchufado", + "charge_in_progress": "Cargando", + "energy_flap_opened": "Tapa de energ\u00eda abierta", + "not_in_charge": "No est\u00e1 cargando", + "unavailable": "No disponible", + "waiting_for_a_planned_charge": "Esperando la carga planificada", + "waiting_for_current_charge": "Esperando la carga actual" + } + }, + "plug_state": { + "state": { + "plug_error": "Error de enchufe", + "plug_unknown": "Enchufe desconocido", + "plugged": "Enchufado", + "unplugged": "Desenchufado" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/renault/translations/et.json b/homeassistant/components/renault/translations/et.json index 464c27d2ecc..af1e655de52 100644 --- a/homeassistant/components/renault/translations/et.json +++ b/homeassistant/components/renault/translations/et.json @@ -31,5 +31,38 @@ "title": "M\u00e4\u00e4ra Renault sidumise parameetrid" } } + }, + "entity": { + "select": { + "charge_mode": { + "state": { + "always": "Kohe", + "always_charging": "Kohe", + "schedule_mode": "Planeerija" + } + } + }, + "sensor": { + "charge_state": { + "state": { + "charge_ended": "Laadimine l\u00f5ppes", + "charge_error": "Ei lae ega ole \u00fchendatud", + "charge_in_progress": "Laeb", + "energy_flap_opened": "Laadimisklapp avatud", + "not_in_charge": "Ei lae", + "unavailable": "Kadunud", + "waiting_for_a_planned_charge": "Oodates planeeritud laadimist", + "waiting_for_current_charge": "Ootab praegust laadimist" + } + }, + "plug_state": { + "state": { + "plug_error": "Pistiku viga", + "plug_unknown": "\u00dchendus teadmata", + "plugged": "\u00dchendatud", + "unplugged": "Lahti \u00fchendatud" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/renault/translations/hu.json b/homeassistant/components/renault/translations/hu.json index 9d2367af80b..0585f8182f2 100644 --- a/homeassistant/components/renault/translations/hu.json +++ b/homeassistant/components/renault/translations/hu.json @@ -31,5 +31,38 @@ "title": "\u00c1ll\u00edtsa be a Renault hiteles\u00edt\u0151 adatait" } } + }, + "entity": { + "select": { + "charge_mode": { + "state": { + "always": "Azonnali", + "always_charging": "Azonnali", + "schedule_mode": "Tervez\u0151" + } + } + }, + "sensor": { + "charge_state": { + "state": { + "charge_ended": "A t\u00f6lt\u00e9s v\u00e9get \u00e9rt", + "charge_error": "Nincs t\u00f6lt\u00e9s vagy nincs csatlakoztatva", + "charge_in_progress": "T\u00f6lt\u0151dik", + "energy_flap_opened": "Nyitott energiafed\u00e9l", + "not_in_charge": "Nem t\u00f6lt\u0151dik", + "unavailable": "Nem el\u00e9rhet\u0151", + "waiting_for_a_planned_charge": "V\u00e1rakoz\u00e1s a tervezett t\u00f6lt\u00e9sre", + "waiting_for_current_charge": "V\u00e1rakoz\u00e1s az aktu\u00e1lis t\u00f6lt\u00e9sre" + } + }, + "plug_state": { + "state": { + "plug_error": "Csatlakoz\u00f3 hiba", + "plug_unknown": "Ismeretlen csatlakoz\u00f3", + "plugged": "Csatlakoztatva", + "unplugged": "Kih\u00fazva" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/renault/translations/id.json b/homeassistant/components/renault/translations/id.json index eacdca285d2..cbea8e6e1a2 100644 --- a/homeassistant/components/renault/translations/id.json +++ b/homeassistant/components/renault/translations/id.json @@ -31,5 +31,38 @@ "title": "Setel kredensial Renault" } } + }, + "entity": { + "select": { + "charge_mode": { + "state": { + "always": "Instan", + "always_charging": "Instan", + "schedule_mode": "Perencana" + } + } + }, + "sensor": { + "charge_state": { + "state": { + "charge_ended": "Pengisian daya berakhir", + "charge_error": "Tidak mengisi daya atau terhubung", + "charge_in_progress": "Mengisi daya", + "energy_flap_opened": "Tutup energi terbuka", + "not_in_charge": "Tidak mengisi daya", + "unavailable": "Tidak tersedia", + "waiting_for_a_planned_charge": "Menunggu pengisian daya yang direncanakan", + "waiting_for_current_charge": "Menunggu pengisian daya saat ini" + } + }, + "plug_state": { + "state": { + "plug_error": "Kesalahan steker", + "plug_unknown": "Steker tidak diketahui", + "plugged": "Dicolokkan", + "unplugged": "Dicabut" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/renault/translations/it.json b/homeassistant/components/renault/translations/it.json index 8086620dbc1..2c0a59242e1 100644 --- a/homeassistant/components/renault/translations/it.json +++ b/homeassistant/components/renault/translations/it.json @@ -31,5 +31,38 @@ "title": "Imposta le credenziali Renault" } } + }, + "entity": { + "select": { + "charge_mode": { + "state": { + "always": "Immediata", + "always_charging": "Immediata", + "schedule_mode": "Pianificatore" + } + } + }, + "sensor": { + "charge_state": { + "state": { + "charge_ended": "Carica terminata", + "charge_error": "Non \u00e8 in carica o non \u00e8 collegata", + "charge_in_progress": "Ricaricando", + "energy_flap_opened": "Sportello energetico aperto", + "not_in_charge": "Non in carica", + "unavailable": "Non disponibile", + "waiting_for_a_planned_charge": "In attesa di addebito pianificato", + "waiting_for_current_charge": "In attesa della carica corrente" + } + }, + "plug_state": { + "state": { + "plug_error": "Errore di spina", + "plug_unknown": "Spina sconosciuta", + "plugged": "Collegato", + "unplugged": "Scollegato" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/renault/translations/ja.json b/homeassistant/components/renault/translations/ja.json index 42d819589b7..34ebc5ec5fc 100644 --- a/homeassistant/components/renault/translations/ja.json +++ b/homeassistant/components/renault/translations/ja.json @@ -31,5 +31,16 @@ "title": "\u30eb\u30ce\u30fc\u306e\u8a8d\u8a3c\u60c5\u5831\u3092\u8a2d\u5b9a" } } + }, + "entity": { + "sensor": { + "plug_state": { + "state": { + "plug_unknown": "\u30d7\u30e9\u30b0\u4e0d\u660e", + "plugged": "\u30d7\u30e9\u30b0\u30a4\u30f3", + "unplugged": "\u30a2\u30f3\u30d7\u30e9\u30b0\u30c9" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/renault/translations/ko.json b/homeassistant/components/renault/translations/ko.json new file mode 100644 index 00000000000..19f53fa0408 --- /dev/null +++ b/homeassistant/components/renault/translations/ko.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "invalid_credentials": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638" + }, + "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" + }, + "user": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc774\uba54\uc77c" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/renault/translations/nl.json b/homeassistant/components/renault/translations/nl.json index 1d2066dbdfc..197ed32f6de 100644 --- a/homeassistant/components/renault/translations/nl.json +++ b/homeassistant/components/renault/translations/nl.json @@ -31,5 +31,14 @@ "title": "Renault-inloggegevens instellen" } } + }, + "entity": { + "sensor": { + "charge_state": { + "state": { + "charge_in_progress": "Opladen" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/renault/translations/no.json b/homeassistant/components/renault/translations/no.json index 3891d56b928..0dbf7697d35 100644 --- a/homeassistant/components/renault/translations/no.json +++ b/homeassistant/components/renault/translations/no.json @@ -31,5 +31,38 @@ "title": "Angi Renault-legitimasjon" } } + }, + "entity": { + "select": { + "charge_mode": { + "state": { + "always": "Umiddelbar", + "always_charging": "Umiddelbar", + "schedule_mode": "Planner" + } + } + }, + "sensor": { + "charge_state": { + "state": { + "charge_ended": "Ladningen ble avsluttet", + "charge_error": "Lader ikke eller kobles til", + "charge_in_progress": "Lader", + "energy_flap_opened": "Energiluken \u00e5pnet", + "not_in_charge": "Lader ikke", + "unavailable": "Utilgjengelig", + "waiting_for_a_planned_charge": "Venter p\u00e5 planlagt lading", + "waiting_for_current_charge": "Venter p\u00e5 gjeldende lading" + } + }, + "plug_state": { + "state": { + "plug_error": "Pluggfeil", + "plug_unknown": "Plugg ukjent", + "plugged": "Plugget inn", + "unplugged": "Koblet fra" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/renault/translations/pl.json b/homeassistant/components/renault/translations/pl.json index 087dd45e378..a56789facad 100644 --- a/homeassistant/components/renault/translations/pl.json +++ b/homeassistant/components/renault/translations/pl.json @@ -31,5 +31,38 @@ "title": "Dane logowania Renault" } } + }, + "entity": { + "select": { + "charge_mode": { + "state": { + "always": "natychmiast", + "always_charging": "natychmiast", + "schedule_mode": "harmonogram" + } + } + }, + "sensor": { + "charge_state": { + "state": { + "charge_ended": "\u0142adowanie zako\u0144czone", + "charge_error": "nie \u0142aduje lub nie pod\u0142\u0105czony", + "charge_in_progress": "\u0142adowanie", + "energy_flap_opened": "pokrywa \u0142adowania otwarta", + "not_in_charge": "roz\u0142adowywanie", + "unavailable": "niedost\u0119pny", + "waiting_for_a_planned_charge": "oczekiwanie na zaplanowane \u0142adowanie", + "waiting_for_current_charge": "oczekiwanie na bie\u017c\u0105ce \u0142adowanie" + } + }, + "plug_state": { + "state": { + "plug_error": "b\u0142\u0105d wtyczki", + "plug_unknown": "nieznana wtyczka", + "plugged": "pod\u0142\u0105czony", + "unplugged": "od\u0142\u0105czony" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/renault/translations/pt-BR.json b/homeassistant/components/renault/translations/pt-BR.json index 28054eac1c5..e51690fcd13 100644 --- a/homeassistant/components/renault/translations/pt-BR.json +++ b/homeassistant/components/renault/translations/pt-BR.json @@ -31,5 +31,38 @@ "title": "Definir credenciais Renault" } } + }, + "entity": { + "select": { + "charge_mode": { + "state": { + "always": "Instant\u00e2neo", + "always_charging": "Instant\u00e2neo", + "schedule_mode": "Planejador" + } + } + }, + "sensor": { + "charge_state": { + "state": { + "charge_ended": "Carga encerrada", + "charge_error": "N\u00e3o est\u00e1 carregando ou conectado", + "charge_in_progress": "Carregando", + "energy_flap_opened": "Aba de energia aberta", + "not_in_charge": "N\u00e3o est\u00e1 carregando", + "unavailable": "Indispon\u00edvel", + "waiting_for_a_planned_charge": "Aguardando carga planejada", + "waiting_for_current_charge": "Aguardando carga atual" + } + }, + "plug_state": { + "state": { + "plug_error": "Erro de plugue", + "plug_unknown": "Plug desconhecido", + "plugged": "Conectado", + "unplugged": "Desconectado" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/renault/translations/ru.json b/homeassistant/components/renault/translations/ru.json index 65f3a4ffbae..21e6e2932ef 100644 --- a/homeassistant/components/renault/translations/ru.json +++ b/homeassistant/components/renault/translations/ru.json @@ -31,5 +31,38 @@ "title": "\u0423\u0447\u0435\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 Renault" } } + }, + "entity": { + "select": { + "charge_mode": { + "state": { + "always": "\u041c\u0433\u043d\u043e\u0432\u0435\u043d\u043d\u043e", + "always_charging": "\u041c\u0433\u043d\u043e\u0432\u0435\u043d\u043d\u043e", + "schedule_mode": "\u041f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a" + } + } + }, + "sensor": { + "charge_state": { + "state": { + "charge_ended": "\u0417\u0430\u0440\u044f\u0436\u0435\u043d\u043e", + "charge_error": "\u041d\u0435 \u0437\u0430\u0440\u044f\u0436\u0430\u0435\u0442\u0441\u044f \u0438\u043b\u0438 \u043d\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043e", + "charge_in_progress": "\u0417\u0430\u0440\u044f\u0436\u0430\u0435\u0442\u0441\u044f", + "energy_flap_opened": "\u041e\u0442\u043a\u0440\u044b\u0442\u0430 \u043a\u0440\u044b\u0448\u043a\u0430 \u0440\u0430\u0437\u044a\u0435\u043c\u0430 \u0434\u043b\u044f \u0437\u0430\u0440\u044f\u0434\u043a\u0438", + "not_in_charge": "\u041d\u0435 \u0437\u0430\u0440\u044f\u0436\u0430\u0435\u0442\u0441\u044f", + "unavailable": "\u041d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e", + "waiting_for_a_planned_charge": "\u041e\u0436\u0438\u0434\u0430\u043d\u0438\u0435 \u0437\u0430\u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0439 \u0437\u0430\u0440\u044f\u0434\u043a\u0438", + "waiting_for_current_charge": "\u041e\u0436\u0438\u0434\u0430\u043d\u0438\u0435 \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u0437\u0430\u0440\u044f\u0434\u043a\u0438" + } + }, + "plug_state": { + "state": { + "plug_error": "\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f", + "plug_unknown": "\u0412\u0438\u043b\u043a\u0430 \u043d\u0435 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0430", + "plugged": "\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043e", + "unplugged": "\u041e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u043e" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/renault/translations/sk.json b/homeassistant/components/renault/translations/sk.json index 79e529d998a..fdf677e4c09 100644 --- a/homeassistant/components/renault/translations/sk.json +++ b/homeassistant/components/renault/translations/sk.json @@ -2,12 +2,19 @@ "config": { "abort": { "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", + "kamereon_no_account": "Ned\u00e1 sa n\u00e1js\u0165 \u00fa\u010det Kamereon", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { "invalid_credentials": "Neplatn\u00e9 overenie" }, "step": { + "kamereon": { + "data": { + "kamereon_account_id": "ID \u00fa\u010dtu Kamereon" + }, + "title": "Vyberte ID \u00fa\u010dtu Kamereon" + }, "reauth_confirm": { "data": { "password": "Heslo" @@ -17,8 +24,43 @@ }, "user": { "data": { + "locale": "Miestne nastavenie", "password": "Heslo", "username": "Email" + }, + "title": "Nastavte prihlasovacie \u00fadaje Renault" + } + } + }, + "entity": { + "select": { + "charge_mode": { + "state": { + "always": "Okam\u017eit\u00e9", + "always_charging": "Okam\u017eit\u00e9", + "schedule_mode": "Pl\u00e1nova\u010d" + } + } + }, + "sensor": { + "charge_state": { + "state": { + "charge_ended": "Nab\u00edjanie skon\u010dilo", + "charge_error": "Nenab\u00edja sa ani nie je zapojen\u00fd", + "charge_in_progress": "Nab\u00edja sa", + "energy_flap_opened": "Otvoren\u00e1 energetick\u00e1 klapka", + "not_in_charge": "Nenab\u00edja sa", + "unavailable": "Nedostupn\u00e9", + "waiting_for_a_planned_charge": "\u010cakanie na pl\u00e1novan\u00e9 nab\u00edjanie", + "waiting_for_current_charge": "\u010cakanie na aktu\u00e1lne nabitie" + } + }, + "plug_state": { + "state": { + "plug_error": "Chyba pripojenia", + "plug_unknown": "Nezn\u00e1my zdroj", + "plugged": "Zapojen\u00e9", + "unplugged": "Odpojen\u00e9" } } } diff --git a/homeassistant/components/renault/translations/zh-Hant.json b/homeassistant/components/renault/translations/zh-Hant.json index a423d9d2359..ce45cdb421c 100644 --- a/homeassistant/components/renault/translations/zh-Hant.json +++ b/homeassistant/components/renault/translations/zh-Hant.json @@ -31,5 +31,38 @@ "title": "\u8a2d\u5b9a Renault \u6191\u8b49" } } + }, + "entity": { + "select": { + "charge_mode": { + "state": { + "always": "\u7acb\u5373\u5145\u96fb", + "always_charging": "\u7acb\u5373\u5145\u96fb", + "schedule_mode": "\u898f\u5283\u6a21\u5f0f" + } + } + }, + "sensor": { + "charge_state": { + "state": { + "charge_ended": "\u5145\u96fb\u5b8c\u6210", + "charge_error": "\u672a\u5728\u5145\u96fb\u6216\u63d2\u4e0a", + "charge_in_progress": "\u5145\u96fb\u4e2d", + "energy_flap_opened": "\u5145\u96fb\u84cb\u958b\u555f", + "not_in_charge": "\u672a\u5728\u5145\u96fb", + "unavailable": "\u4e0d\u53ef\u7528", + "waiting_for_a_planned_charge": "\u7b49\u5019\u898f\u5283\u5145\u96fb", + "waiting_for_current_charge": "\u7b49\u5019\u96fb\u6d41\u5145\u96fb" + } + }, + "plug_state": { + "state": { + "plug_error": "\u5145\u96fb\u5668\u932f\u8aa4", + "plug_unknown": "\u5145\u96fb\u5668\u672a\u77e5", + "plugged": "\u5145\u96fb\u5668\u5df2\u63d2\u4e0a", + "unplugged": "\u5145\u96fb\u5668\u672a\u63d2\u4e0a" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/reolink/__init__.py b/homeassistant/components/reolink/__init__.py new file mode 100644 index 00000000000..a4daba45ba7 --- /dev/null +++ b/homeassistant/components/reolink/__init__.py @@ -0,0 +1,107 @@ +"""Reolink integration for HomeAssistant.""" + +from __future__ import annotations + +import asyncio +from dataclasses import dataclass +from datetime import timedelta +import logging + +from aiohttp import ClientConnectorError +import async_timeout +from reolink_aio.exceptions import ApiError, InvalidContentTypeError + +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import EVENT_HOMEASSISTANT_STOP, Platform +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import ConfigEntryNotReady +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator + +from .const import DOMAIN +from .host import ReolinkHost + +_LOGGER = logging.getLogger(__name__) + +PLATFORMS = [Platform.CAMERA] +DEVICE_UPDATE_INTERVAL = 60 + + +@dataclass +class ReolinkData: + """Data for the Reolink integration.""" + + host: ReolinkHost + device_coordinator: DataUpdateCoordinator + + +async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: + """Set up Reolink from a config entry.""" + host = ReolinkHost(hass, config_entry.data, config_entry.options) + + try: + if not await host.async_init(): + raise ConfigEntryNotReady( + f"Error while trying to setup {host.api.host}:{host.api.port}: " + "failed to obtain data from device." + ) + except ( + ClientConnectorError, + asyncio.TimeoutError, + ApiError, + InvalidContentTypeError, + ) as err: + raise ConfigEntryNotReady( + f'Error while trying to setup {host.api.host}:{host.api.port}: "{str(err)}".' + ) from err + + config_entry.async_on_unload( + hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, host.stop) + ) + + async def async_device_config_update(): + """Update the host state cache and renew the ONVIF-subscription.""" + async with async_timeout.timeout(host.api.timeout): + # Login session is implicitly updated here + await host.update_states() + + coordinator_device_config_update = DataUpdateCoordinator( + hass, + _LOGGER, + name=f"reolink.{host.api.nvr_name}", + update_method=async_device_config_update, + update_interval=timedelta(seconds=DEVICE_UPDATE_INTERVAL), + ) + # Fetch initial data so we have data when entities subscribe + await coordinator_device_config_update.async_config_entry_first_refresh() + + hass.data.setdefault(DOMAIN, {})[config_entry.entry_id] = ReolinkData( + host=host, + device_coordinator=coordinator_device_config_update, + ) + + await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS) + + config_entry.async_on_unload( + config_entry.add_update_listener(entry_update_listener) + ) + + return True + + +async def entry_update_listener(hass: HomeAssistant, config_entry: ConfigEntry): + """Update the configuration of the host entity.""" + await hass.config_entries.async_reload(config_entry.entry_id) + + +async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: + """Unload a config entry.""" + host: ReolinkHost = hass.data[DOMAIN][config_entry.entry_id].host + + await host.stop() + + if unload_ok := await hass.config_entries.async_unload_platforms( + config_entry, PLATFORMS + ): + hass.data[DOMAIN].pop(config_entry.entry_id) + + return unload_ok diff --git a/homeassistant/components/reolink/camera.py b/homeassistant/components/reolink/camera.py new file mode 100644 index 00000000000..aafc9686eea --- /dev/null +++ b/homeassistant/components/reolink/camera.py @@ -0,0 +1,70 @@ +"""This component provides support for Reolink IP cameras.""" +from __future__ import annotations + +import logging + +from homeassistant.components.camera import Camera, CameraEntityFeature +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from . import ReolinkData +from .const import DOMAIN +from .entity import ReolinkCoordinatorEntity + +_LOGGER = logging.getLogger(__name__) + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up a Reolink IP Camera.""" + reolink_data: ReolinkData = hass.data[DOMAIN][config_entry.entry_id] + host = reolink_data.host + + cameras = [] + for channel in host.api.channels: + streams = ["sub", "main", "snapshots"] + if host.api.protocol == "rtmp": + streams.append("ext") + + for stream in streams: + cameras.append(ReolinkCamera(reolink_data, config_entry, channel, stream)) + + async_add_entities(cameras, update_before_add=True) + + +class ReolinkCamera(ReolinkCoordinatorEntity, Camera): + """An implementation of a Reolink IP camera.""" + + _attr_supported_features: CameraEntityFeature = CameraEntityFeature.STREAM + _attr_has_entity_name = True + + def __init__( + self, + reolink_data: ReolinkData, + config_entry: ConfigEntry, + channel: int, + stream: str, + ) -> None: + """Initialize Reolink camera stream.""" + ReolinkCoordinatorEntity.__init__(self, reolink_data, config_entry, channel) + Camera.__init__(self) + + self._stream = stream + + self._attr_name = self._stream + self._attr_unique_id = f"{self._host.unique_id}_{self._channel}_{self._stream}" + self._attr_entity_registry_enabled_default = stream == "sub" + + async def stream_source(self) -> str | None: + """Return the source of the stream.""" + return await self._host.api.get_stream_source(self._channel, self._stream) + + async def async_camera_image( + self, width: int | None = None, height: int | None = None + ) -> bytes | None: + """Return a still image response from the camera.""" + return await self._host.api.get_snapshot(self._channel) diff --git a/homeassistant/components/reolink/config_flow.py b/homeassistant/components/reolink/config_flow.py new file mode 100644 index 00000000000..31f1a10dc1e --- /dev/null +++ b/homeassistant/components/reolink/config_flow.py @@ -0,0 +1,138 @@ +"""Config flow for the Reolink camera component.""" +from __future__ import annotations + +import logging +from typing import Any + +from reolink_aio.exceptions import ApiError, CredentialsInvalidError +import voluptuous as vol + +from homeassistant import config_entries, core, exceptions +from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT, CONF_USERNAME +from homeassistant.core import callback +from homeassistant.data_entry_flow import FlowResult +from homeassistant.helpers import config_validation as cv + +from .const import CONF_PROTOCOL, CONF_USE_HTTPS, DEFAULT_PROTOCOL, DOMAIN +from .host import ReolinkHost + +_LOGGER = logging.getLogger(__name__) + +DEFAULT_OPTIONS = {CONF_PROTOCOL: DEFAULT_PROTOCOL} + + +class ReolinkOptionsFlowHandler(config_entries.OptionsFlow): + """Handle Reolink options.""" + + def __init__(self, config_entry): + """Initialize ReolinkOptionsFlowHandler.""" + self.config_entry = config_entry + + async def async_step_init( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Manage the Reolink options.""" + if user_input is not None: + return self.async_create_entry(data=user_input) + + return self.async_show_form( + step_id="init", + data_schema=vol.Schema( + { + vol.Required( + CONF_PROTOCOL, + default=self.config_entry.options[CONF_PROTOCOL], + ): vol.In(["rtsp", "rtmp"]), + } + ), + ) + + +class ReolinkFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): + """Handle a config flow for Reolink device.""" + + VERSION = 1 + + @staticmethod + @callback + def async_get_options_flow( + config_entry: config_entries.ConfigEntry, + ) -> ReolinkOptionsFlowHandler: + """Options callback for Reolink.""" + return ReolinkOptionsFlowHandler(config_entry) + + async def async_step_user( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle the initial step.""" + errors = {} + placeholders = {} + + if user_input is not None: + try: + host = await async_obtain_host_settings(self.hass, user_input) + except CannotConnect: + errors[CONF_HOST] = "cannot_connect" + except CredentialsInvalidError: + errors[CONF_HOST] = "invalid_auth" + except ApiError as err: + placeholders["error"] = str(err) + errors[CONF_HOST] = "api_error" + except Exception as err: # pylint: disable=broad-except + _LOGGER.exception("Unexpected exception") + placeholders["error"] = str(err) + errors[CONF_HOST] = "unknown" + + if not errors: + user_input[CONF_PORT] = host.api.port + user_input[CONF_USE_HTTPS] = host.api.use_https + + await self.async_set_unique_id(host.unique_id, raise_on_progress=False) + self._abort_if_unique_id_configured(updates=user_input) + + return self.async_create_entry( + title=str(host.api.nvr_name), + data=user_input, + options=DEFAULT_OPTIONS, + ) + + data_schema = vol.Schema( + { + vol.Required(CONF_USERNAME, default="admin"): str, + vol.Required(CONF_PASSWORD): str, + vol.Required(CONF_HOST): str, + } + ) + if errors: + data_schema = data_schema.extend( + { + vol.Optional(CONF_PORT): cv.positive_int, + vol.Optional(CONF_USE_HTTPS): bool, + } + ) + + return self.async_show_form( + step_id="user", + data_schema=data_schema, + errors=errors, + description_placeholders=placeholders, + ) + + +async def async_obtain_host_settings( + hass: core.HomeAssistant, user_input: dict +) -> ReolinkHost: + """Initialize the Reolink host and get the host information.""" + host = ReolinkHost(hass, user_input, DEFAULT_OPTIONS) + + try: + if not await host.async_init(): + raise CannotConnect + finally: + await host.stop() + + return host + + +class CannotConnect(exceptions.HomeAssistantError): + """Error to indicate we cannot connect.""" diff --git a/homeassistant/components/reolink/const.py b/homeassistant/components/reolink/const.py new file mode 100644 index 00000000000..180c3ccae11 --- /dev/null +++ b/homeassistant/components/reolink/const.py @@ -0,0 +1,9 @@ +"""Constants for the Reolink Camera integration.""" + +DOMAIN = "reolink" + +CONF_USE_HTTPS = "use_https" +CONF_PROTOCOL = "protocol" + +DEFAULT_PROTOCOL = "rtsp" +DEFAULT_TIMEOUT = 60 diff --git a/homeassistant/components/reolink/entity.py b/homeassistant/components/reolink/entity.py new file mode 100644 index 00000000000..403ea278889 --- /dev/null +++ b/homeassistant/components/reolink/entity.py @@ -0,0 +1,52 @@ +"""Reolink parent entity class.""" +from __future__ import annotations + +from homeassistant.config_entries import ConfigEntry +from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC +from homeassistant.helpers.entity import DeviceInfo +from homeassistant.helpers.update_coordinator import CoordinatorEntity + +from . import ReolinkData +from .const import DOMAIN + + +class ReolinkCoordinatorEntity(CoordinatorEntity): + """Parent class for Reolink Entities.""" + + def __init__( + self, reolink_data: ReolinkData, config_entry: ConfigEntry, channel: int | None + ) -> None: + """Initialize ReolinkCoordinatorEntity.""" + coordinator = reolink_data.device_coordinator + super().__init__(coordinator) + + self._host = reolink_data.host + self._channel = channel + + http_s = "https" if self._host.api.use_https else "http" + conf_url = f"{http_s}://{self._host.api.host}:{self._host.api.port}" + if self._host.api.is_nvr and self._channel is not None: + self._attr_device_info = DeviceInfo( + identifiers={(DOMAIN, f"{self._host.unique_id}_ch{self._channel}")}, + via_device=(DOMAIN, self._host.unique_id), + name=self._host.api.camera_name(self._channel), + model=self._host.api.camera_model(self._channel), + manufacturer=self._host.api.manufacturer, + configuration_url=conf_url, + ) + else: + self._attr_device_info = DeviceInfo( + identifiers={(DOMAIN, self._host.unique_id)}, + connections={(CONNECTION_NETWORK_MAC, self._host.api.mac_address)}, + name=self._host.api.nvr_name, + model=self._host.api.model, + manufacturer=self._host.api.manufacturer, + hw_version=self._host.api.hardware_version, + sw_version=self._host.api.sw_version, + configuration_url=conf_url, + ) + + @property + def available(self) -> bool: + """Return True if entity is available.""" + return self._host.api.session_active and super().available diff --git a/homeassistant/components/reolink/host.py b/homeassistant/components/reolink/host.py new file mode 100644 index 00000000000..fc5e4947afa --- /dev/null +++ b/homeassistant/components/reolink/host.py @@ -0,0 +1,165 @@ +"""This component encapsulates the NVR/camera API and subscription.""" +from __future__ import annotations + +import asyncio +from collections.abc import Mapping +import logging +from typing import Any + +import aiohttp +from reolink_aio.api import Host +from reolink_aio.exceptions import ( + ApiError, + CredentialsInvalidError, + InvalidContentTypeError, +) + +from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT, CONF_USERNAME +from homeassistant.core import HomeAssistant +from homeassistant.helpers.device_registry import format_mac + +from .const import CONF_PROTOCOL, CONF_USE_HTTPS, DEFAULT_TIMEOUT + +_LOGGER = logging.getLogger(__name__) + + +class ReolinkHost: + """The implementation of the Reolink Host class.""" + + def __init__( + self, + hass: HomeAssistant, + config: Mapping[str, Any], + options: Mapping[str, Any], + ) -> None: + """Initialize Reolink Host. Could be either NVR, or Camera.""" + self._hass: HomeAssistant = hass + + self._clientsession: aiohttp.ClientSession | None = None + self._unique_id: str = "" + + self._api = Host( + config[CONF_HOST], + config[CONF_USERNAME], + config[CONF_PASSWORD], + port=config.get(CONF_PORT), + use_https=config.get(CONF_USE_HTTPS), + protocol=options[CONF_PROTOCOL], + timeout=DEFAULT_TIMEOUT, + ) + + @property + def unique_id(self) -> str: + """Create the unique ID, base for all entities.""" + return self._unique_id + + @property + def api(self): + """Return the API object.""" + return self._api + + async def async_init(self) -> bool: + """Connect to Reolink host.""" + self._api.expire_session() + + if not await self._api.get_host_data(): + return False + + if self._api.mac_address is None: + return False + + enable_onvif = None + enable_rtmp = None + enable_rtsp = None + + if not self._api.onvif_enabled: + _LOGGER.debug( + "ONVIF is disabled on %s, trying to enable it", self._api.nvr_name + ) + enable_onvif = True + + if not self._api.rtmp_enabled and self._api.protocol == "rtmp": + _LOGGER.debug( + "RTMP is disabled on %s, trying to enable it", self._api.nvr_name + ) + enable_rtmp = True + elif not self._api.rtsp_enabled and self._api.protocol == "rtsp": + _LOGGER.debug( + "RTSP is disabled on %s, trying to enable it", self._api.nvr_name + ) + enable_rtsp = True + + if enable_onvif or enable_rtmp or enable_rtsp: + if not await self._api.set_net_port( + enable_onvif=enable_onvif, + enable_rtmp=enable_rtmp, + enable_rtsp=enable_rtsp, + ): + if enable_onvif: + _LOGGER.error( + "Failed to enable ONVIF on %s. Set it to ON to receive notifications", + self._api.nvr_name, + ) + + if enable_rtmp: + _LOGGER.error( + "Failed to enable RTMP on %s. Set it to ON", + self._api.nvr_name, + ) + elif enable_rtsp: + _LOGGER.error( + "Failed to enable RTSP on %s. Set it to ON", + self._api.nvr_name, + ) + + self._unique_id = format_mac(self._api.mac_address) + + return True + + async def update_states(self) -> bool: + """Call the API of the camera device to update the states.""" + return await self._api.get_states() + + async def disconnect(self): + """Disconnect from the API, so the connection will be released.""" + await self._api.unsubscribe_all() + + try: + await self._api.logout() + except aiohttp.ClientConnectorError as err: + _LOGGER.error( + "Reolink connection error while logging out for host %s:%s: %s", + self._api.host, + self._api.port, + str(err), + ) + except asyncio.TimeoutError: + _LOGGER.error( + "Reolink connection timeout while logging out for host %s:%s", + self._api.host, + self._api.port, + ) + except ApiError as err: + _LOGGER.error( + "Reolink API error while logging out for host %s:%s: %s", + self._api.host, + self._api.port, + str(err), + ) + except CredentialsInvalidError: + _LOGGER.error( + "Reolink credentials error while logging out for host %s:%s", + self._api.host, + self._api.port, + ) + except InvalidContentTypeError as err: + _LOGGER.error( + "Reolink content type error while logging out for host %s:%s: %s", + self._api.host, + self._api.port, + str(err), + ) + + async def stop(self, event=None): + """Disconnect the API.""" + await self.disconnect() diff --git a/homeassistant/components/reolink/manifest.json b/homeassistant/components/reolink/manifest.json new file mode 100644 index 00000000000..2c0deafca45 --- /dev/null +++ b/homeassistant/components/reolink/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "reolink", + "name": "Reolink IP NVR/camera", + "config_flow": true, + "documentation": "https://www.home-assistant.io/integrations/reolink", + "requirements": ["reolink-aio==0.1.1"], + "codeowners": ["@starkillerOG"], + "iot_class": "local_polling", + "loggers": ["reolink-aio"] +} diff --git a/homeassistant/components/reolink/strings.json b/homeassistant/components/reolink/strings.json new file mode 100644 index 00000000000..88211774240 --- /dev/null +++ b/homeassistant/components/reolink/strings.json @@ -0,0 +1,33 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "[%key:common::config_flow::data::host%]", + "port": "[%key:common::config_flow::data::port%]", + "use_https": "Enable HTTPS", + "username": "[%key:common::config_flow::data::username%]", + "password": "[%key:common::config_flow::data::password%]" + } + } + }, + "error": { + "api_error": "API error occurred: {error}", + "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", + "invalid_auth": "[%key:common::config_flow::error::invalid_auth%]", + "unknown": "[%key:common::config_flow::error::unknown%]: {error}" + }, + "abort": { + "already_configured": "[%key:common::config_flow::abort::already_configured_device%]" + } + }, + "options": { + "step": { + "init": { + "data": { + "protocol": "Protocol" + } + } + } + } +} diff --git a/homeassistant/components/reolink/translations/ca.json b/homeassistant/components/reolink/translations/ca.json new file mode 100644 index 00000000000..c5bcbecd22a --- /dev/null +++ b/homeassistant/components/reolink/translations/ca.json @@ -0,0 +1,33 @@ +{ + "config": { + "abort": { + "already_configured": "El dispositiu ja est\u00e0 configurat" + }, + "error": { + "api_error": "S'ha produ\u00eft un error a l'API: {error}", + "cannot_connect": "Ha fallat la connexi\u00f3", + "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida", + "unknown": "Error inesperat: {error}" + }, + "step": { + "user": { + "data": { + "host": "Amfitri\u00f3", + "password": "Contrasenya", + "port": "Port", + "use_https": "Activa HTTPS", + "username": "Nom d'usuari" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "protocol": "Protocol" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/reolink/translations/en.json b/homeassistant/components/reolink/translations/en.json new file mode 100644 index 00000000000..028f61ed8c7 --- /dev/null +++ b/homeassistant/components/reolink/translations/en.json @@ -0,0 +1,33 @@ +{ + "config": { + "abort": { + "already_configured": "Device is already configured" + }, + "error": { + "api_error": "API error occurred: {error}", + "cannot_connect": "Failed to connect", + "invalid_auth": "Invalid authentication", + "unknown": "Unexpected error: {error}" + }, + "step": { + "user": { + "data": { + "host": "Host", + "password": "Password", + "port": "Port", + "use_https": "Enable HTTPS", + "username": "Username" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "protocol": "Protocol" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/reolink/translations/es.json b/homeassistant/components/reolink/translations/es.json new file mode 100644 index 00000000000..44a9939d4d7 --- /dev/null +++ b/homeassistant/components/reolink/translations/es.json @@ -0,0 +1,33 @@ +{ + "config": { + "abort": { + "already_configured": "El dispositivo ya est\u00e1 configurado" + }, + "error": { + "api_error": "Ocurri\u00f3 un error de API: {error}", + "cannot_connect": "No se pudo conectar", + "invalid_auth": "Autenticaci\u00f3n no v\u00e1lida", + "unknown": "Error inesperado: {error}" + }, + "step": { + "user": { + "data": { + "host": "Host", + "password": "Contrase\u00f1a", + "port": "Puerto", + "use_https": "Habilitar HTTPS", + "username": "Nombre de usuario" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "protocol": "Protocolo" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/repairs/issue_handler.py b/homeassistant/components/repairs/issue_handler.py index 4914be8b520..4bf7f466fd2 100644 --- a/homeassistant/components/repairs/issue_handler.py +++ b/homeassistant/components/repairs/issue_handler.py @@ -53,7 +53,7 @@ class RepairsFlowManager(data_entry_flow.FlowManager): async def async_create_flow( self, - handler_key: Any, + handler_key: str, *, context: dict[str, Any] | None = None, data: dict[str, Any] | None = None, diff --git a/homeassistant/components/repetier/__init__.py b/homeassistant/components/repetier/__init__.py index c657d5d0502..6a0df99ce1d 100644 --- a/homeassistant/components/repetier/__init__.py +++ b/homeassistant/components/repetier/__init__.py @@ -17,7 +17,7 @@ from homeassistant.const import ( CONF_PORT, CONF_SENSORS, PERCENTAGE, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv @@ -142,21 +142,21 @@ SENSOR_TYPES: dict[str, RepetierSensorEntityDescription] = { "bed_temperature": RepetierSensorEntityDescription( key="bed_temperature", type="temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, name="_bed_", device_class=SensorDeviceClass.TEMPERATURE, ), "extruder_temperature": RepetierSensorEntityDescription( key="extruder_temperature", type="temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, name="_extruder_", device_class=SensorDeviceClass.TEMPERATURE, ), "chamber_temperature": RepetierSensorEntityDescription( key="chamber_temperature", type="temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, name="_chamber_", device_class=SensorDeviceClass.TEMPERATURE, ), diff --git a/homeassistant/components/rflink/__init__.py b/homeassistant/components/rflink/__init__.py index cfa76a57e67..e703f0d09de 100644 --- a/homeassistant/components/rflink/__init__.py +++ b/homeassistant/components/rflink/__init__.py @@ -225,9 +225,11 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: keepalive_idle_timer = config[DOMAIN][CONF_KEEPALIVE_IDLE] if keepalive_idle_timer < 0: _LOGGER.error( - "A bogus TCP Keepalive IDLE timer was provided (%d secs), " - "it will be disabled. " - "Recommended values: 60-3600 (seconds)", + ( + "A bogus TCP Keepalive IDLE timer was provided (%d secs), " + "it will be disabled. " + "Recommended values: 60-3600 (seconds)" + ), keepalive_idle_timer, ) keepalive_idle_timer = None @@ -235,9 +237,11 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: keepalive_idle_timer = None elif keepalive_idle_timer <= 30: _LOGGER.warning( - "A very short TCP Keepalive IDLE timer was provided (%d secs) " - "and may produce unexpected disconnections from RFlink device." - " Recommended values: 60-3600 (seconds)", + ( + "A very short TCP Keepalive IDLE timer was provided (%d secs) " + "and may produce unexpected disconnections from RFlink device." + " Recommended values: 60-3600 (seconds)" + ), keepalive_idle_timer, ) diff --git a/homeassistant/components/rflink/light.py b/homeassistant/components/rflink/light.py index 94576155502..347d2f1074f 100644 --- a/homeassistant/components/rflink/light.py +++ b/homeassistant/components/rflink/light.py @@ -135,9 +135,11 @@ def devices_from_config(domain_config): repetitions_enabled = device_config[CONF_SIGNAL_REPETITIONS] != 1 if is_hybrid and repetitions_enabled: _LOGGER.warning( - "Hybrid type for %s not compatible with signal " - "repetitions. Please set 'dimmable' or 'switchable' " - "type explicitly in configuration", + ( + "Hybrid type for %s not compatible with signal " + "repetitions. Please set 'dimmable' or 'switchable' " + "type explicitly in configuration" + ), device_id, ) diff --git a/homeassistant/components/rflink/sensor.py b/homeassistant/components/rflink/sensor.py index baccd47ebb3..b96e03e7eb4 100644 --- a/homeassistant/components/rflink/sensor.py +++ b/homeassistant/components/rflink/sensor.py @@ -20,14 +20,14 @@ from homeassistant.const import ( CONF_SENSOR_TYPE, CONF_UNIT_OF_MEASUREMENT, DEGREE, - ELECTRIC_CURRENT_AMPERE, - ELECTRIC_POTENTIAL_VOLT, LIGHT_LUX, PERCENTAGE, - PRECIPITATION_MILLIMETERS, UV_INDEX, + UnitOfElectricCurrent, + UnitOfElectricPotential, UnitOfLength, UnitOfPower, + UnitOfPrecipitationDepth, UnitOfPressure, UnitOfSpeed, UnitOfTemperature, @@ -91,21 +91,21 @@ SENSOR_TYPES = ( name="Current phase 1", device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, ), SensorEntityDescription( key="current_phase_2", name="Current phase 2", device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, ), SensorEntityDescription( key="current_phase_3", name="Current phase 3", device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, ), SensorEntityDescription( key="distance", @@ -189,7 +189,7 @@ SENSOR_TYPES = ( name="Total rain", device_class=SensorDeviceClass.PRECIPITATION, state_class=SensorStateClass.TOTAL_INCREASING, - native_unit_of_measurement=PRECIPITATION_MILLIMETERS, + native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, ), SensorEntityDescription( key="uv_intensity", @@ -208,7 +208,7 @@ SENSOR_TYPES = ( name="Voltage", device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, ), SensorEntityDescription( key="watt", diff --git a/homeassistant/components/rfxtrx/__init__.py b/homeassistant/components/rfxtrx/__init__.py index d0d3793fd82..c32852fa448 100644 --- a/homeassistant/components/rfxtrx/__init__.py +++ b/homeassistant/components/rfxtrx/__init__.py @@ -405,11 +405,13 @@ def find_possible_pt2262_device(device_ids: list[str], device_id: str) -> str | if size is not None: size = len(dev_id) - size - 1 _LOGGER.info( - "Found possible device %s for %s " - "with the following configuration:\n" - "data_bits=%d\n" - "command_on=0x%s\n" - "command_off=0x%s\n", + ( + "Found possible device %s for %s " + "with the following configuration:\n" + "data_bits=%d\n" + "command_on=0x%s\n" + "command_off=0x%s\n" + ), device_id, dev_id, size * 4, diff --git a/homeassistant/components/rfxtrx/sensor.py b/homeassistant/components/rfxtrx/sensor.py index 5f85f344e9b..32c24d74fc0 100644 --- a/homeassistant/components/rfxtrx/sensor.py +++ b/homeassistant/components/rfxtrx/sensor.py @@ -19,17 +19,17 @@ from homeassistant.components.sensor import ( from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( DEGREE, - ELECTRIC_CURRENT_AMPERE, - ELECTRIC_POTENTIAL_VOLT, - ENERGY_KILO_WATT_HOUR, - LENGTH_MILLIMETERS, PERCENTAGE, - POWER_WATT, - PRESSURE_HPA, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, - SPEED_METERS_PER_SECOND, - TEMP_CELSIUS, UV_INDEX, + UnitOfElectricCurrent, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfPower, + UnitOfPrecipitationDepth, + UnitOfPressure, + UnitOfSpeed, + UnitOfTemperature, UnitOfVolumetricFlux, ) from homeassistant.core import HomeAssistant, callback @@ -72,7 +72,7 @@ SENSOR_TYPES = ( name="Barometer", device_class=SensorDeviceClass.PRESSURE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=PRESSURE_HPA, + native_unit_of_measurement=UnitOfPressure.HPA, ), RfxtrxSensorEntityDescription( key="Battery numeric", @@ -88,35 +88,35 @@ SENSOR_TYPES = ( name="Current", device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, ), RfxtrxSensorEntityDescription( key="Current Ch. 1", name="Current Ch. 1", device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, ), RfxtrxSensorEntityDescription( key="Current Ch. 2", name="Current Ch. 2", device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, ), RfxtrxSensorEntityDescription( key="Current Ch. 3", name="Current Ch. 3", device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, ), RfxtrxSensorEntityDescription( key="Energy usage", name="Instantaneous power", device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, ), RfxtrxSensorEntityDescription( key="Humidity", @@ -139,28 +139,28 @@ SENSOR_TYPES = ( name="Temperature", device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, ), RfxtrxSensorEntityDescription( key="Temperature2", name="Temperature 2", device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, ), RfxtrxSensorEntityDescription( key="Total usage", name="Total energy usage", device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, ), RfxtrxSensorEntityDescription( key="Voltage", name="Voltage", device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, ), RfxtrxSensorEntityDescription( key="Wind direction", @@ -200,27 +200,28 @@ SENSOR_TYPES = ( name="Chill", device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, ), RfxtrxSensorEntityDescription( key="Wind average speed", name="Wind average speed", state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=SPEED_METERS_PER_SECOND, - device_class=SensorDeviceClass.SPEED, + native_unit_of_measurement=UnitOfSpeed.METERS_PER_SECOND, + device_class=SensorDeviceClass.WIND_SPEED, ), RfxtrxSensorEntityDescription( key="Wind gust", name="Wind gust", state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=SPEED_METERS_PER_SECOND, - device_class=SensorDeviceClass.SPEED, + native_unit_of_measurement=UnitOfSpeed.METERS_PER_SECOND, + device_class=SensorDeviceClass.WIND_SPEED, ), RfxtrxSensorEntityDescription( key="Rain total", name="Rain total", state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=LENGTH_MILLIMETERS, + native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + device_class=SensorDeviceClass.PRECIPITATION, ), RfxtrxSensorEntityDescription( key="Forecast", diff --git a/homeassistant/components/rfxtrx/translations/de.json b/homeassistant/components/rfxtrx/translations/de.json index 2661fc41211..16683c526ea 100644 --- a/homeassistant/components/rfxtrx/translations/de.json +++ b/homeassistant/components/rfxtrx/translations/de.json @@ -58,7 +58,7 @@ "prompt_options": { "data": { "automatic_add": "Automatisches Hinzuf\u00fcgen aktivieren", - "debug": "Debugging aktivieren", + "debug": "Debuggen aktivieren", "device": "Zu konfigurierendes Ger\u00e4t ausw\u00e4hlen", "event_code": "Ereigniscode zum Hinzuf\u00fcgen eingeben", "protocols": "Protokolle" diff --git a/homeassistant/components/rfxtrx/translations/pt.json b/homeassistant/components/rfxtrx/translations/pt.json index c962097e6ad..f07e1f1ea06 100644 --- a/homeassistant/components/rfxtrx/translations/pt.json +++ b/homeassistant/components/rfxtrx/translations/pt.json @@ -2,15 +2,15 @@ "config": { "abort": { "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "setup_network": { "data": { - "host": "Servidor", + "host": "Endere\u00e7o", "port": "Porta" } }, diff --git a/homeassistant/components/rfxtrx/translations/sk.json b/homeassistant/components/rfxtrx/translations/sk.json index 83867537e65..6dc87e3d309 100644 --- a/homeassistant/components/rfxtrx/translations/sk.json +++ b/homeassistant/components/rfxtrx/translations/sk.json @@ -5,9 +5,17 @@ "cannot_connect": "Nepodarilo sa pripoji\u0165" }, "error": { - "cannot_connect": "Nepodarilo sa pripoji\u0165" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "few": "Pr\u00e1zdnych", + "many": "Pr\u00e1zdnych", + "one": "Pr\u00e1zdny", + "other": "Pr\u00e1zdny" }, "step": { + "few": "Pr\u00e1zdnych", + "many": "Pr\u00e1zdnych", + "one": "Pr\u00e1zdny", + "other": "Pr\u00e1zdny", "setup_network": { "data": { "host": "Hostite\u013e", @@ -45,18 +53,42 @@ "status": "Prijat\u00fd stav: {subtype}" } }, + "few": "Pr\u00e1zdnych", + "many": "Pr\u00e1zdnych", + "one": "Pr\u00e1zdny", "options": { "error": { "already_configured_device": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "invalid_event_code": "Neplatn\u00fd k\u00f3d udalosti", + "invalid_input_2262_off": "Neplatn\u00fd vstup pre vyp\u00ednac\u00ed pr\u00edkaz", + "invalid_input_2262_on": "Neplatn\u00fd vstup pre zap\u00ednac\u00ed pr\u00edkaz", + "invalid_input_off_delay": "Neplatn\u00fd vstup pre oneskoren\u00e9 vypnutie", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "prompt_options": { "data": { + "automatic_add": "Povoli\u0165 automatick\u00e9 pridanie", + "debug": "Povoli\u0165 ladenie", "device": "Vyberte zariadenie, ktor\u00e9 chcete nakonfigurova\u0165", + "event_code": "Zadajte k\u00f3d udalosti, ktor\u00fa chcete prida\u0165", "protocols": "Protokoly" - } + }, + "title": "Mo\u017enosti Rfxtrx" + }, + "set_device_options": { + "data": { + "command_off": "Hodnota d\u00e1tov\u00fdch bitov pre pr\u00edkaz off", + "command_on": "Hodnota d\u00e1tov\u00fdch bitov pre pr\u00edkaz on", + "data_bit": "Po\u010det d\u00e1tov\u00fdch bitov", + "off_delay": "Oneskorenie vypnutia", + "off_delay_enabled": "Povoli\u0165 oneskorenie vypnutia", + "replace_device": "Vyberte zariadenie, ktor\u00e9 chcete nahradi\u0165", + "venetian_blind_mode": "Re\u017eim \u017eal\u00fazie" + }, + "title": "Konfigur\u00e1cia mo\u017enost\u00ed zariadenia" } } - } + }, + "other": "Pr\u00e1zdny" } \ No newline at end of file diff --git a/homeassistant/components/rhasspy/translations/ko.json b/homeassistant/components/rhasspy/translations/ko.json new file mode 100644 index 00000000000..416fb8d164e --- /dev/null +++ b/homeassistant/components/rhasspy/translations/ko.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/rhasspy/translations/sk.json b/homeassistant/components/rhasspy/translations/sk.json index c294bc45d7c..378ef54a900 100644 --- a/homeassistant/components/rhasspy/translations/sk.json +++ b/homeassistant/components/rhasspy/translations/sk.json @@ -2,6 +2,11 @@ "config": { "abort": { "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, + "step": { + "user": { + "description": "Chcete povoli\u0165 podporu Rhasspy?" + } } } } \ No newline at end of file diff --git a/homeassistant/components/ridwell/translations/ko.json b/homeassistant/components/ridwell/translations/ko.json new file mode 100644 index 00000000000..4acc13c7385 --- /dev/null +++ b/homeassistant/components/ridwell/translations/ko.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638" + }, + "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" + }, + "user": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ridwell/translations/lb.json b/homeassistant/components/ridwell/translations/lb.json new file mode 100644 index 00000000000..0e3a8987acd --- /dev/null +++ b/homeassistant/components/ridwell/translations/lb.json @@ -0,0 +1,9 @@ +{ + "config": { + "step": { + "user": { + "description": "G\u00ebff d\u00e4i Benotzernumm an d\u00e4i Passwuert an:" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ring/translations/sk.json b/homeassistant/components/ring/translations/sk.json index 805e6449829..2595214aa12 100644 --- a/homeassistant/components/ring/translations/sk.json +++ b/homeassistant/components/ring/translations/sk.json @@ -11,13 +11,15 @@ "2fa": { "data": { "2fa": "Dvojfaktorov\u00fd k\u00f3d" - } + }, + "title": "Dvojfaktorov\u00e1 autentifik\u00e1cia" }, "user": { "data": { "password": "Heslo", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" - } + }, + "title": "Prihl\u00e1senie pomocou konta Ring" } } } diff --git a/homeassistant/components/risco/__init__.py b/homeassistant/components/risco/__init__.py index f143244d31d..88f8ba9bdfa 100644 --- a/homeassistant/components/risco/__init__.py +++ b/homeassistant/components/risco/__init__.py @@ -12,6 +12,8 @@ from pyrisco import ( RiscoLocal, UnauthorizedError, ) +from pyrisco.cloud.alarm import Alarm +from pyrisco.cloud.event import Event from pyrisco.common import Partition, Zone from homeassistant.config_entries import ConfigEntry @@ -175,10 +177,12 @@ async def _update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None: await hass.config_entries.async_reload(entry.entry_id) -class RiscoDataUpdateCoordinator(DataUpdateCoordinator): +class RiscoDataUpdateCoordinator(DataUpdateCoordinator[Alarm]): """Class to manage fetching risco data.""" - def __init__(self, hass, risco, scan_interval): + def __init__( + self, hass: HomeAssistant, risco: RiscoCloud, scan_interval: int + ) -> None: """Initialize global risco data updater.""" self.risco = risco interval = timedelta(seconds=scan_interval) @@ -189,7 +193,7 @@ class RiscoDataUpdateCoordinator(DataUpdateCoordinator): update_interval=interval, ) - async def _async_update_data(self): + async def _async_update_data(self) -> Alarm: """Fetch data from risco.""" try: return await self.risco.get_state() @@ -197,13 +201,15 @@ class RiscoDataUpdateCoordinator(DataUpdateCoordinator): raise UpdateFailed(error) from error -class RiscoEventsDataUpdateCoordinator(DataUpdateCoordinator): +class RiscoEventsDataUpdateCoordinator(DataUpdateCoordinator[list[Event]]): """Class to manage fetching risco data.""" - def __init__(self, hass, risco, eid, scan_interval): + def __init__( + self, hass: HomeAssistant, risco: RiscoCloud, eid: str, scan_interval: int + ) -> None: """Initialize global risco data updater.""" self.risco = risco - self._store = Store( + self._store = Store[dict[str, Any]]( hass, LAST_EVENT_STORAGE_VERSION, f"risco_{eid}_last_event_timestamp" ) interval = timedelta(seconds=scan_interval) @@ -214,7 +220,7 @@ class RiscoEventsDataUpdateCoordinator(DataUpdateCoordinator): update_interval=interval, ) - async def _async_update_data(self): + async def _async_update_data(self) -> list[Event]: """Fetch data from risco.""" last_store = await self._store.async_load() or {} last_timestamp = last_store.get( diff --git a/homeassistant/components/risco/sensor.py b/homeassistant/components/risco/sensor.py index f2cb9821166..bb416b8c550 100644 --- a/homeassistant/components/risco/sensor.py +++ b/homeassistant/components/risco/sensor.py @@ -1,7 +1,7 @@ """Sensor for Risco Events.""" from __future__ import annotations -from collections.abc import Mapping +from collections.abc import Collection, Mapping from typing import Any from homeassistant.components.binary_sensor import DOMAIN as BS_DOMAIN @@ -63,10 +63,17 @@ async def async_setup_entry( async_add_entities(sensors) -class RiscoSensor(CoordinatorEntity, SensorEntity): +class RiscoSensor(CoordinatorEntity[RiscoEventsDataUpdateCoordinator], SensorEntity): """Sensor for Risco events.""" - def __init__(self, coordinator, category_id, excludes, name, entry_id) -> None: + def __init__( + self, + coordinator: RiscoEventsDataUpdateCoordinator, + category_id: int | None, + excludes: Collection[int] | None, + name: str, + entry_id: str, + ) -> None: """Initialize sensor.""" super().__init__(coordinator) self._event = None diff --git a/homeassistant/components/risco/translations/it.json b/homeassistant/components/risco/translations/it.json index df714cbd9fc..3cb7833921d 100644 --- a/homeassistant/components/risco/translations/it.json +++ b/homeassistant/components/risco/translations/it.json @@ -36,7 +36,7 @@ "ha_to_risco": { "data": { "armed_away": "Fuori casa attivato", - "armed_custom_bypass": "Attivo con bypass personalizzato", + "armed_custom_bypass": "Attivo con esclusione personalizzata", "armed_home": "Attivo In casa", "armed_night": "Attivo Notte" }, diff --git a/homeassistant/components/risco/translations/ko.json b/homeassistant/components/risco/translations/ko.json index f9cd9a28cc4..dc243e70539 100644 --- a/homeassistant/components/risco/translations/ko.json +++ b/homeassistant/components/risco/translations/ko.json @@ -7,6 +7,22 @@ "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "cloud": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638", + "pin": "PIN \ucf54\ub4dc", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + } + }, + "local": { + "data": { + "host": "\ud638\uc2a4\ud2b8", + "pin": "PIN \ucf54\ub4dc", + "port": "\ud3ec\ud2b8" + } + } } }, "options": { diff --git a/homeassistant/components/risco/translations/lb.json b/homeassistant/components/risco/translations/lb.json index 1cab6cbfa32..fb889d36d15 100644 --- a/homeassistant/components/risco/translations/lb.json +++ b/homeassistant/components/risco/translations/lb.json @@ -20,8 +20,8 @@ }, "init": { "data": { - "code_arm_required": "Pin Code n\u00e9ideg fir unzeschalten", - "code_disarm_required": "Pin Code n\u00e9ideg fir auszeschalten" + "code_arm_required": "PIN-Code fir ze Entsp\u00e4re virginn", + "code_disarm_required": "PIN-Code fir ze Entsp\u00e4re virginn" }, "title": "Optioune konfigur\u00e9ieren" }, diff --git a/homeassistant/components/risco/translations/pt.json b/homeassistant/components/risco/translations/pt.json index bdbab687bc0..e3c3fe5d90f 100644 --- a/homeassistant/components/risco/translations/pt.json +++ b/homeassistant/components/risco/translations/pt.json @@ -4,11 +4,25 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, "step": { + "cloud": { + "data": { + "password": "Palavra-passe", + "pin": "C\u00f3digo PIN", + "username": "Nome de Utilizador" + } + }, + "local": { + "data": { + "host": "Endere\u00e7o", + "pin": "C\u00f3digo PIN", + "port": "Porta" + } + }, "user": { "menu_options": { "cloud": "Risco Cloud (recomendado)", diff --git a/homeassistant/components/risco/translations/sk.json b/homeassistant/components/risco/translations/sk.json index b7c83c9709e..5475a6fcd46 100644 --- a/homeassistant/components/risco/translations/sk.json +++ b/homeassistant/components/risco/translations/sk.json @@ -5,7 +5,8 @@ }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "cloud": { @@ -21,12 +22,33 @@ "pin": "PIN k\u00f3d", "port": "Port" } + }, + "user": { + "menu_options": { + "cloud": "Risco Cloud (odpor\u00fa\u010dan\u00e9)", + "local": "Miestny panel Risco (pokro\u010dil\u00fd)" + } } } }, "options": { "step": { + "ha_to_risco": { + "data": { + "armed_away": "Nepr\u00edtomnos\u0165", + "armed_custom_bypass": "Akt\u00edvny, definovan\u00fd u\u017e\u00edvate\u013eom", + "armed_home": "Re\u017eim domov", + "armed_night": "No\u010dn\u00fd re\u017eim" + }, + "description": "Vyberte stav, do ktor\u00e9ho chcete nastavi\u0165 alarm Risco pri aktiv\u00e1cii alarmu Home Assistant", + "title": "Mapova\u0165 stavy Home Assistant na stavy Risco" + }, "init": { + "data": { + "code_arm_required": "Vy\u017eadova\u0165 PIN k\u00f3d na zabezpe\u010denie", + "code_disarm_required": "Po\u017eadovan\u00e9 PIN k\u00f3d pre odomknutie", + "scan_interval": "Ako \u010dasto dotazova\u0165 Risco (v sekund\u00e1ch)" + }, "title": "Konfigur\u00e1cia mo\u017enost\u00ed" }, "risco_to_ha": { @@ -34,8 +56,12 @@ "A": "Skupina A", "B": "Skupina B", "C": "Skupina C", - "D": "Skupina D" - } + "D": "Skupina D", + "arm": "Akt\u00edvny (nepr\u00edtomn\u00fd)", + "partial_arm": "\u010ciasto\u010dne akt\u00edvny (STAY)" + }, + "description": "Vyberte, ak\u00fd \u0161t\u00e1t bude v\u00e1\u0161 alarm Home Assistant hl\u00e1si\u0165 pre ka\u017ed\u00fd \u0161t\u00e1t hl\u00e1sen\u00fd spolo\u010dnos\u0165ou Risco", + "title": "Mapujte \u0161t\u00e1ty Risco na \u0161t\u00e1ty Home Assistant" } } } diff --git a/homeassistant/components/rituals_perfume_genie/__init__.py b/homeassistant/components/rituals_perfume_genie/__init__.py index 441eb04cbe8..a165fc0b2f0 100644 --- a/homeassistant/components/rituals_perfume_genie/__init__.py +++ b/homeassistant/components/rituals_perfume_genie/__init__.py @@ -65,7 +65,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return unload_ok -class RitualsDataUpdateCoordinator(DataUpdateCoordinator): +class RitualsDataUpdateCoordinator(DataUpdateCoordinator[None]): """Class to manage fetching Rituals Perfume Genie device data from single endpoint.""" def __init__(self, hass: HomeAssistant, device: Diffuser) -> None: diff --git a/homeassistant/components/rituals_perfume_genie/number.py b/homeassistant/components/rituals_perfume_genie/number.py index 778b70753cb..8fe0a663e37 100644 --- a/homeassistant/components/rituals_perfume_genie/number.py +++ b/homeassistant/components/rituals_perfume_genie/number.py @@ -56,6 +56,7 @@ class DiffuserPerfumeAmount(DiffuserEntity, NumberEntity): """Set the perfume amount.""" if not value.is_integer(): raise ValueError( - f"Can't set the perfume amount to {value}. Perfume amount must be an integer." + f"Can't set the perfume amount to {value}. Perfume amount must be an" + " integer." ) await self._diffuser.set_perfume_amount(int(value)) diff --git a/homeassistant/components/rituals_perfume_genie/sensor.py b/homeassistant/components/rituals_perfume_genie/sensor.py index 0172df4b1c1..d1fbb3f1cb1 100644 --- a/homeassistant/components/rituals_perfume_genie/sensor.py +++ b/homeassistant/components/rituals_perfume_genie/sensor.py @@ -106,7 +106,6 @@ class DiffuserBatterySensor(DiffuserEntity, SensorEntity): class DiffuserWifiSensor(DiffuserEntity, SensorEntity): """Representation of a diffuser wifi sensor.""" - _attr_device_class = SensorDeviceClass.SIGNAL_STRENGTH _attr_native_unit_of_measurement = PERCENTAGE _attr_entity_category = EntityCategory.DIAGNOSTIC diff --git a/homeassistant/components/rituals_perfume_genie/translations/pt.json b/homeassistant/components/rituals_perfume_genie/translations/pt.json index e3b78cd8e42..4accc1dfca7 100644 --- a/homeassistant/components/rituals_perfume_genie/translations/pt.json +++ b/homeassistant/components/rituals_perfume_genie/translations/pt.json @@ -4,14 +4,14 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, "step": { "user": { "data": { - "email": "Email", + "email": "", "password": "Palavra-passe" } } diff --git a/homeassistant/components/rituals_perfume_genie/translations/sk.json b/homeassistant/components/rituals_perfume_genie/translations/sk.json index ab95d8060d4..7116eaf6226 100644 --- a/homeassistant/components/rituals_perfume_genie/translations/sk.json +++ b/homeassistant/components/rituals_perfume_genie/translations/sk.json @@ -13,7 +13,8 @@ "data": { "email": "Email", "password": "Heslo" - } + }, + "title": "Pripojte sa k svojmu \u00fa\u010dtu Rituals" } } } diff --git a/homeassistant/components/rmvtransport/sensor.py b/homeassistant/components/rmvtransport/sensor.py index 25f14ae6df3..d3ee722af94 100644 --- a/homeassistant/components/rmvtransport/sensor.py +++ b/homeassistant/components/rmvtransport/sensor.py @@ -13,7 +13,7 @@ from RMVtransport.rmvtransport import ( import voluptuous as vol from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity -from homeassistant.const import CONF_NAME, CONF_TIMEOUT, TIME_MINUTES +from homeassistant.const import CONF_NAME, CONF_TIMEOUT, UnitOfTime from homeassistant.core import HomeAssistant from homeassistant.exceptions import PlatformNotReady import homeassistant.helpers.config_validation as cv @@ -184,7 +184,7 @@ class RMVDepartureSensor(SensorEntity): @property def native_unit_of_measurement(self): """Return the unit this state is expressed in.""" - return TIME_MINUTES + return UnitOfTime.MINUTES async def async_update(self) -> None: """Get the latest data and update the state.""" diff --git a/homeassistant/components/roku/media_player.py b/homeassistant/components/roku/media_player.py index 7b495247c4d..b09ddb7ef7d 100644 --- a/homeassistant/components/roku/media_player.py +++ b/homeassistant/components/roku/media_player.py @@ -130,7 +130,7 @@ class RokuMediaPlayer(RokuEntity, MediaPlayerEntity): return self.coordinator.data.media.duration > 0 @property - def device_class(self) -> str | None: + def device_class(self) -> MediaPlayerDeviceClass: """Return the class of this device.""" if self.coordinator.data.info.device_type == "tv": return MediaPlayerDeviceClass.TV diff --git a/homeassistant/components/roku/translations/pt.json b/homeassistant/components/roku/translations/pt.json index 1d4aea3279b..e73b5a1b516 100644 --- a/homeassistant/components/roku/translations/pt.json +++ b/homeassistant/components/roku/translations/pt.json @@ -5,7 +5,7 @@ "unknown": "Erro inesperado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "flow_title": "Roku: {name}", "step": { diff --git a/homeassistant/components/roku/translations/sk.json b/homeassistant/components/roku/translations/sk.json index 80f5e0b1cc7..e70a5c0f060 100644 --- a/homeassistant/components/roku/translations/sk.json +++ b/homeassistant/components/roku/translations/sk.json @@ -11,8 +11,22 @@ "flow_title": "{name}", "step": { "discovery_confirm": { + "data": { + "few": "Pr\u00e1zdnych", + "many": "Pr\u00e1zdnych", + "one": "Pr\u00e1zdny", + "other": "Pr\u00e1zdny" + }, "description": "Chcete nastavi\u0165 {name}?" }, + "ssdp_confirm": { + "data": { + "few": "Pr\u00e1zdnych", + "many": "Pr\u00e1zdnych", + "one": "Pr\u00e1zdny", + "other": "Pr\u00e1zdny" + } + }, "user": { "data": { "host": "Hostite\u013e" diff --git a/homeassistant/components/roomba/translations/pt.json b/homeassistant/components/roomba/translations/pt.json index fc6cc956c6d..10463d49442 100644 --- a/homeassistant/components/roomba/translations/pt.json +++ b/homeassistant/components/roomba/translations/pt.json @@ -1,10 +1,13 @@ { "config": { "abort": { - "not_irobot_device": "O dispositivo descoberto n\u00e3o \u00e9 um iRobot" + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", + "cannot_connect": "A liga\u00e7\u00e3o falhou", + "not_irobot_device": "O dispositivo descoberto n\u00e3o \u00e9 um iRobot", + "short_blid": "O BLID est\u00e1 incompleto" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "flow_title": "{name} ({host})", "step": { @@ -16,18 +19,22 @@ "data": { "password": "Palavra-passe" }, - "description": "A palavra-passe n\u00e3o p\u00f4de ser obtida automaticamente a partir do dispositivo. Por favor, garanta que a app iRobot Home n\u00e3o est\u00e1 aberta em nenhum dispositivo enquanto tenta obter a palavra-passe. Siga os passos descritos na documenta\u00e7\u00e3o em: {auth_help_url}." + "description": "A palavra-passe n\u00e3o p\u00f4de ser obtida automaticamente a partir do dispositivo. Por favor, garanta que a app iRobot Home n\u00e3o est\u00e1 aberta em nenhum dispositivo enquanto tenta obter a palavra-passe. Siga os passos descritos na documenta\u00e7\u00e3o em: {auth_help_url}.", + "title": "Introduza a Palavra-Passe" }, "manual": { "data": { - "host": "Anfitri\u00e3o" - } + "host": "Endere\u00e7o" + }, + "description": "Nenhum dispositivo Roomba ou Braava foi encontrado na rede.", + "title": "Ligar manualmente ao dispositivo" }, "user": { "data": { - "host": "Servidor" + "host": "Endere\u00e7o" }, - "title": "Conectar ao dispositivo" + "description": "Selecione um Roomba ou Braava.", + "title": "Ligar automaticamente ao dispositivo" } } }, diff --git a/homeassistant/components/roomba/translations/sk.json b/homeassistant/components/roomba/translations/sk.json index 946fbadf22a..8e776a3bb4a 100644 --- a/homeassistant/components/roomba/translations/sk.json +++ b/homeassistant/components/roomba/translations/sk.json @@ -3,7 +3,8 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "cannot_connect": "Nepodarilo sa pripoji\u0165", - "not_irobot_device": "Zisten\u00e9 zariadenie nie je zariadenie iRobot" + "not_irobot_device": "Zisten\u00e9 zariadenie nie je zariadenie iRobot", + "short_blid": "BLID bol skr\u00e1ten\u00fd" }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165" @@ -11,26 +12,40 @@ "flow_title": "{name} ({host})", "step": { "link": { + "description": "Uistite sa, \u017ee aplik\u00e1cia iRobot nie je spusten\u00e1 na \u017eiadnom zariaden\u00ed. Stla\u010dte a podr\u017ete tla\u010didlo Domov na {name}, k\u00fdm zariadenie nevygeneruje zvuk (asi dve sekundy), potom odo\u0161lite do 30 sek\u00fand.", "title": "Op\u00e4tovne z\u00edska\u0165 heslo" }, "link_manual": { "data": { "password": "Heslo" }, + "description": "Heslo nebolo mo\u017en\u00e9 zo zariadenia z\u00edska\u0165 automaticky. Pri pokuse o z\u00edskanie hesla sa uistite, \u017ee aplik\u00e1cia iRobot nie je otvoren\u00e1 na \u017eiadnom zariaden\u00ed. Postupujte pod\u013ea krokov uveden\u00fdch v dokument\u00e1cii na adrese: {auth_help_url}", "title": "Zadajte heslo" }, "manual": { "data": { "host": "Hostite\u013e" }, + "description": "Vo va\u0161ej sieti nebola objaven\u00e1 \u017eiadna Roomba ani Braava.", "title": "Manu\u00e1lne pripojenie k zariadeniu" }, "user": { "data": { "host": "Hostite\u013e" }, + "description": "Vyberte Roomba alebo Braava.", "title": "Automaticky sa pripoji\u0165 k zariadeniu" } } + }, + "options": { + "step": { + "init": { + "data": { + "continuous": "Nepretr\u017eit\u00fd", + "delay": "Oneskorenie" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/roon/manifest.json b/homeassistant/components/roon/manifest.json index 297bf5e9d7a..272c353601e 100644 --- a/homeassistant/components/roon/manifest.json +++ b/homeassistant/components/roon/manifest.json @@ -3,7 +3,7 @@ "name": "RoonLabs music player", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/roon", - "requirements": ["roonapi==0.1.1"], + "requirements": ["roonapi==0.1.2"], "codeowners": ["@pavoni"], "iot_class": "local_push", "loggers": ["roonapi"] diff --git a/homeassistant/components/roon/media_player.py b/homeassistant/components/roon/media_player.py index 227a7e5c6a1..ab811e0853b 100644 --- a/homeassistant/components/roon/media_player.py +++ b/homeassistant/components/roon/media_player.py @@ -415,7 +415,10 @@ class RoonDevice(MediaPlayerEntity): return if name not in sync_available: _LOGGER.error( - "Can't join player %s with %s because it's not in the join available list %s", + ( + "Can't join player %s with %s because it's not in the join" + " available list %s" + ), name, self.name, list(sync_available), diff --git a/homeassistant/components/roon/translations/en_GB.json b/homeassistant/components/roon/translations/en-GB.json similarity index 100% rename from homeassistant/components/roon/translations/en_GB.json rename to homeassistant/components/roon/translations/en-GB.json diff --git a/homeassistant/components/roon/translations/pt.json b/homeassistant/components/roon/translations/pt.json index 105f0b64676..adaab5c65be 100644 --- a/homeassistant/components/roon/translations/pt.json +++ b/homeassistant/components/roon/translations/pt.json @@ -10,7 +10,7 @@ "step": { "fallback": { "data": { - "host": "Servidor" + "host": "Endere\u00e7o" } } } diff --git a/homeassistant/components/roon/translations/sk.json b/homeassistant/components/roon/translations/sk.json index 2d0aa74c391..3cc7597775d 100644 --- a/homeassistant/components/roon/translations/sk.json +++ b/homeassistant/components/roon/translations/sk.json @@ -14,6 +14,16 @@ "port": "Port" }, "description": "Server Roon sa nepodarilo objavi\u0165, zadajte n\u00e1zov hostite\u013ea a port." + }, + "link": { + "description": "V Roon mus\u00edte autorizova\u0165 Home Assistant. Po kliknut\u00ed na tla\u010didlo Odosla\u0165 prejdite do aplik\u00e1cie Roon Core, otvorte Nastavenia a povo\u013ete HomeAssistant na karte Roz\u0161\u00edrenia.", + "title": "Autorizujte HomeAssistant v Roone" + }, + "user": { + "few": "Pr\u00e1zdnych", + "many": "Pr\u00e1zdnych", + "one": "Pr\u00e1zdny", + "other": "Pr\u00e1zdny" } } } diff --git a/homeassistant/components/rpi_power/binary_sensor.py b/homeassistant/components/rpi_power/binary_sensor.py index 08535daf970..c0c65e814ea 100644 --- a/homeassistant/components/rpi_power/binary_sensor.py +++ b/homeassistant/components/rpi_power/binary_sensor.py @@ -19,7 +19,10 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback _LOGGER = logging.getLogger(__name__) DESCRIPTION_NORMALIZED = "Voltage normalized. Everything is working as intended." -DESCRIPTION_UNDER_VOLTAGE = "Under-voltage was detected. Consider getting a uninterruptible power supply for your Raspberry Pi." +DESCRIPTION_UNDER_VOLTAGE = ( + "Under-voltage was detected. Consider getting a uninterruptible power supply for" + " your Raspberry Pi." +) async def async_setup_entry( diff --git a/homeassistant/components/rpi_power/translations/en.json b/homeassistant/components/rpi_power/translations/en.json index 6995190979a..374dfd8f798 100644 --- a/homeassistant/components/rpi_power/translations/en.json +++ b/homeassistant/components/rpi_power/translations/en.json @@ -6,7 +6,7 @@ }, "step": { "confirm": { - "description": "Do you want to start set up?" + "description": "Do you want to start setup?" } } }, diff --git a/homeassistant/components/rpi_power/translations/it.json b/homeassistant/components/rpi_power/translations/it.json index 4e7a14d05e8..157e166925f 100644 --- a/homeassistant/components/rpi_power/translations/it.json +++ b/homeassistant/components/rpi_power/translations/it.json @@ -6,7 +6,7 @@ }, "step": { "confirm": { - "description": "Vuoi iniziare la configurazione?" + "description": "Vuoi avviare la configurazione?" } } }, diff --git a/homeassistant/components/rpi_power/translations/pt.json b/homeassistant/components/rpi_power/translations/pt.json index 9890048c368..63a12f5e8d1 100644 --- a/homeassistant/components/rpi_power/translations/pt.json +++ b/homeassistant/components/rpi_power/translations/pt.json @@ -5,7 +5,7 @@ }, "step": { "confirm": { - "description": "Quer dar inicio \u00e0 configura\u00e7\u00e3o?" + "description": "Quer dar in\u00edcio \u00e0 configura\u00e7\u00e3o?" } } } diff --git a/homeassistant/components/rtorrent/sensor.py b/homeassistant/components/rtorrent/sensor.py index 278741f20db..e9c7aa89ffe 100644 --- a/homeassistant/components/rtorrent/sensor.py +++ b/homeassistant/components/rtorrent/sensor.py @@ -8,6 +8,7 @@ import voluptuous as vol from homeassistant.components.sensor import ( PLATFORM_SCHEMA, + SensorDeviceClass, SensorEntity, SensorEntityDescription, ) @@ -15,8 +16,8 @@ from homeassistant.const import ( CONF_MONITORED_VARIABLES, CONF_NAME, CONF_URL, - DATA_RATE_KILOBYTES_PER_SECOND, STATE_IDLE, + UnitOfDataRate, ) from homeassistant.core import HomeAssistant from homeassistant.exceptions import PlatformNotReady @@ -45,12 +46,14 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key=SENSOR_TYPE_DOWNLOAD_SPEED, name="Down Speed", - native_unit_of_measurement=DATA_RATE_KILOBYTES_PER_SECOND, + device_class=SensorDeviceClass.DATA_RATE, + native_unit_of_measurement=UnitOfDataRate.KILOBYTES_PER_SECOND, ), SensorEntityDescription( key=SENSOR_TYPE_UPLOAD_SPEED, name="Up Speed", - native_unit_of_measurement=DATA_RATE_KILOBYTES_PER_SECOND, + device_class=SensorDeviceClass.DATA_RATE, + native_unit_of_measurement=UnitOfDataRate.KILOBYTES_PER_SECOND, ), SensorEntityDescription( key=SENSOR_TYPE_ALL_TORRENTS, diff --git a/homeassistant/components/rtsp_to_webrtc/translations/ko.json b/homeassistant/components/rtsp_to_webrtc/translations/ko.json new file mode 100644 index 00000000000..416fb8d164e --- /dev/null +++ b/homeassistant/components/rtsp_to_webrtc/translations/ko.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/rtsp_to_webrtc/translations/sk.json b/homeassistant/components/rtsp_to_webrtc/translations/sk.json index acecadcff42..4a387e61366 100644 --- a/homeassistant/components/rtsp_to_webrtc/translations/sk.json +++ b/homeassistant/components/rtsp_to_webrtc/translations/sk.json @@ -1,7 +1,36 @@ { "config": { + "abort": { + "server_failure": "Server RTSPtoWebRTC vr\u00e1til chybu. \u010eal\u0161ie inform\u00e1cie n\u00e1jdete v logoch.", + "server_unreachable": "Ned\u00e1 sa komunikova\u0165 so serverom RTSPtoWebRTC. \u010eal\u0161ie inform\u00e1cie n\u00e1jdete v logoch.", + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, "error": { - "invalid_url": "Mus\u00ed to by\u0165 platn\u00e1 adresa URL servera RTSPtoWebRTC, napr. https://example.com" + "invalid_url": "Mus\u00ed to by\u0165 platn\u00e1 adresa URL servera RTSPtoWebRTC, napr. https://example.com", + "server_failure": "Server RTSPtoWebRTC vr\u00e1til chybu. \u010eal\u0161ie inform\u00e1cie n\u00e1jdete v logoch.", + "server_unreachable": "Ned\u00e1 sa komunikova\u0165 so serverom RTSPtoWebRTC. \u010eal\u0161ie inform\u00e1cie n\u00e1jdete v logoch." + }, + "step": { + "hassio_confirm": { + "description": "Chcete nakonfigurova\u0165 Home Assistant na pripojenie k serveru RTSPtoWebRTC poskytovan\u00e9mu doplnkom: {addon}?", + "title": "RTSPtoWebRTC cez doplnok Home Assistant" + }, + "user": { + "data": { + "server_url": "URL servera RTSPtoWebRTC, napr. https://example.com" + }, + "description": "Integr\u00e1cia RTSPtoWebRTC vy\u017eaduje server na preklad streamov RTSP do WebRTC. Zadajte adresu URL na server RTSPtoWebRTC.", + "title": "Nakonfigurujte RTSPtoWebRTC" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "stun_server": "Adresa servera Stun (host:port)" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/ruckus_unleashed/translations/pt.json b/homeassistant/components/ruckus_unleashed/translations/pt.json index 561c8d77287..7e727215891 100644 --- a/homeassistant/components/ruckus_unleashed/translations/pt.json +++ b/homeassistant/components/ruckus_unleashed/translations/pt.json @@ -4,14 +4,14 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, "step": { "user": { "data": { - "host": "Servidor", + "host": "Endere\u00e7o", "password": "Palavra-passe", "username": "Nome de Utilizador" } diff --git a/homeassistant/components/ruuvitag_ble/manifest.json b/homeassistant/components/ruuvitag_ble/manifest.json index a3500fca7c6..f7207f685c9 100644 --- a/homeassistant/components/ruuvitag_ble/manifest.json +++ b/homeassistant/components/ruuvitag_ble/manifest.json @@ -5,10 +5,12 @@ "documentation": "https://www.home-assistant.io/integrations/ruuvitag_ble", "bluetooth": [ { - "manufacturer_id": 1177 + "manufacturer_id": 1177, + "connectable": false }, { - "local_name": "Ruuvi *" + "local_name": "Ruuvi *", + "connectable": false } ], "requirements": ["ruuvitag-ble==0.1.1"], diff --git a/homeassistant/components/ruuvitag_ble/sensor.py b/homeassistant/components/ruuvitag_ble/sensor.py index f99e78f65a6..bd21e479abf 100644 --- a/homeassistant/components/ruuvitag_ble/sensor.py +++ b/homeassistant/components/ruuvitag_ble/sensor.py @@ -11,7 +11,7 @@ from sensor_state_data import ( Units, ) -from homeassistant import config_entries, const +from homeassistant import config_entries from homeassistant.components.bluetooth.passive_update_processor import ( PassiveBluetoothDataProcessor, PassiveBluetoothDataUpdate, @@ -25,6 +25,13 @@ from homeassistant.components.sensor import ( SensorEntityDescription, SensorStateClass, ) +from homeassistant.const import ( + PERCENTAGE, + SIGNAL_STRENGTH_DECIBELS_MILLIWATT, + UnitOfElectricPotential, + UnitOfPressure, + UnitOfTemperature, +) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.sensor import sensor_device_info_to_hass_device_info @@ -35,19 +42,19 @@ SENSOR_DESCRIPTIONS = { (SSDSensorDeviceClass.TEMPERATURE, Units.TEMP_CELSIUS): SensorEntityDescription( key=f"{SSDSensorDeviceClass.TEMPERATURE}_{Units.TEMP_CELSIUS}", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=const.TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, ), (SSDSensorDeviceClass.HUMIDITY, Units.PERCENTAGE): SensorEntityDescription( key=f"{SSDSensorDeviceClass.HUMIDITY}_{Units.PERCENTAGE}", device_class=SensorDeviceClass.HUMIDITY, - native_unit_of_measurement=const.PERCENTAGE, + native_unit_of_measurement=PERCENTAGE, state_class=SensorStateClass.MEASUREMENT, ), (SSDSensorDeviceClass.PRESSURE, Units.PRESSURE_HPA): SensorEntityDescription( key=f"{SSDSensorDeviceClass.PRESSURE}_{Units.PRESSURE_HPA}", device_class=SensorDeviceClass.PRESSURE, - native_unit_of_measurement=const.PRESSURE_HPA, + native_unit_of_measurement=UnitOfPressure.HPA, state_class=SensorStateClass.MEASUREMENT, ), ( @@ -55,8 +62,7 @@ SENSOR_DESCRIPTIONS = { Units.ELECTRIC_POTENTIAL_MILLIVOLT, ): SensorEntityDescription( key=f"{SSDSensorDeviceClass.VOLTAGE}_{Units.ELECTRIC_POTENTIAL_MILLIVOLT}", - device_class=SensorDeviceClass.VOLTAGE, - native_unit_of_measurement=const.ELECTRIC_POTENTIAL_MILLIVOLT, + native_unit_of_measurement=UnitOfElectricPotential.MILLIVOLT, state_class=SensorStateClass.MEASUREMENT, ), ( @@ -65,7 +71,7 @@ SENSOR_DESCRIPTIONS = { ): SensorEntityDescription( key=f"{SSDSensorDeviceClass.SIGNAL_STRENGTH}_{Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT}", device_class=SensorDeviceClass.SIGNAL_STRENGTH, - native_unit_of_measurement=const.SIGNAL_STRENGTH_DECIBELS_MILLIWATT, + native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS_MILLIWATT, state_class=SensorStateClass.MEASUREMENT, entity_registry_enabled_default=False, ), diff --git a/homeassistant/components/sabnzbd/sensor.py b/homeassistant/components/sabnzbd/sensor.py index b4231a469ec..a29e1e46abd 100644 --- a/homeassistant/components/sabnzbd/sensor.py +++ b/homeassistant/components/sabnzbd/sensor.py @@ -4,16 +4,13 @@ from __future__ import annotations from dataclasses import dataclass from homeassistant.components.sensor import ( + SensorDeviceClass, SensorEntity, SensorEntityDescription, SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - DATA_GIGABYTES, - DATA_MEGABYTES, - DATA_RATE_MEGABYTES_PER_SECOND, -) +from homeassistant.const import UnitOfDataRate, UnitOfInformation from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceEntryType from homeassistant.helpers.dispatcher import async_dispatcher_connect @@ -46,31 +43,36 @@ SENSOR_TYPES: tuple[SabnzbdSensorEntityDescription, ...] = ( SabnzbdSensorEntityDescription( key=SPEED_KEY, name="Speed", - native_unit_of_measurement=DATA_RATE_MEGABYTES_PER_SECOND, + device_class=SensorDeviceClass.DATA_RATE, + native_unit_of_measurement=UnitOfDataRate.MEGABYTES_PER_SECOND, state_class=SensorStateClass.MEASUREMENT, ), SabnzbdSensorEntityDescription( key="mb", name="Queue", - native_unit_of_measurement=DATA_MEGABYTES, + native_unit_of_measurement=UnitOfInformation.MEGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, state_class=SensorStateClass.MEASUREMENT, ), SabnzbdSensorEntityDescription( key="mbleft", name="Left", - native_unit_of_measurement=DATA_MEGABYTES, + native_unit_of_measurement=UnitOfInformation.MEGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, state_class=SensorStateClass.MEASUREMENT, ), SabnzbdSensorEntityDescription( key="diskspacetotal1", name="Disk", - native_unit_of_measurement=DATA_GIGABYTES, + native_unit_of_measurement=UnitOfInformation.GIGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, state_class=SensorStateClass.MEASUREMENT, ), SabnzbdSensorEntityDescription( key="diskspace1", name="Disk Free", - native_unit_of_measurement=DATA_GIGABYTES, + native_unit_of_measurement=UnitOfInformation.GIGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, state_class=SensorStateClass.MEASUREMENT, ), SabnzbdSensorEntityDescription( @@ -81,28 +83,32 @@ SENSOR_TYPES: tuple[SabnzbdSensorEntityDescription, ...] = ( SabnzbdSensorEntityDescription( key="day_size", name="Daily Total", - native_unit_of_measurement=DATA_GIGABYTES, + native_unit_of_measurement=UnitOfInformation.GIGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, entity_registry_enabled_default=False, state_class=SensorStateClass.TOTAL_INCREASING, ), SabnzbdSensorEntityDescription( key="week_size", name="Weekly Total", - native_unit_of_measurement=DATA_GIGABYTES, + native_unit_of_measurement=UnitOfInformation.GIGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, entity_registry_enabled_default=False, state_class=SensorStateClass.TOTAL_INCREASING, ), SabnzbdSensorEntityDescription( key="month_size", name="Monthly Total", - native_unit_of_measurement=DATA_GIGABYTES, + native_unit_of_measurement=UnitOfInformation.GIGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, entity_registry_enabled_default=False, state_class=SensorStateClass.TOTAL_INCREASING, ), SabnzbdSensorEntityDescription( key="total_size", name="Total", - native_unit_of_measurement=DATA_GIGABYTES, + native_unit_of_measurement=UnitOfInformation.GIGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, state_class=SensorStateClass.TOTAL_INCREASING, ), ) diff --git a/homeassistant/components/sabnzbd/translations/ko.json b/homeassistant/components/sabnzbd/translations/ko.json index 86887876ff5..e20f9375f25 100644 --- a/homeassistant/components/sabnzbd/translations/ko.json +++ b/homeassistant/components/sabnzbd/translations/ko.json @@ -7,7 +7,10 @@ "step": { "user": { "data": { - "api_key": "API \ud0a4" + "api_key": "API \ud0a4", + "name": "\uc774\ub984", + "path": "\uacbd\ub85c", + "url": "URL \uc8fc\uc18c" } } } diff --git a/homeassistant/components/safe_mode/__init__.py b/homeassistant/components/safe_mode/__init__.py index 162dd204c54..2f010fe79c9 100644 --- a/homeassistant/components/safe_mode/__init__.py +++ b/homeassistant/components/safe_mode/__init__.py @@ -10,7 +10,10 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up the Safe Mode component.""" persistent_notification.async_create( hass, - "Home Assistant is running in safe mode. Check [the error log](/config/logs) to see what went wrong.", + ( + "Home Assistant is running in safe mode. Check [the error" + " log](/config/logs) to see what went wrong." + ), "Safe Mode", ) return True diff --git a/homeassistant/components/saj/sensor.py b/homeassistant/components/saj/sensor.py index eec6755e015..6476fb10328 100644 --- a/homeassistant/components/saj/sensor.py +++ b/homeassistant/components/saj/sensor.py @@ -19,13 +19,12 @@ from homeassistant.const import ( CONF_PASSWORD, CONF_TYPE, CONF_USERNAME, - ENERGY_KILO_WATT_HOUR, EVENT_HOMEASSISTANT_STOP, - MASS_KILOGRAMS, - POWER_WATT, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, - TIME_HOURS, + UnitOfEnergy, + UnitOfMass, + UnitOfPower, + UnitOfTemperature, + UnitOfTime, ) from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback from homeassistant.exceptions import PlatformNotReady @@ -44,11 +43,11 @@ INVERTER_TYPES = ["ethernet", "wifi"] SAJ_UNIT_MAPPINGS = { "": None, - "h": TIME_HOURS, - "kg": MASS_KILOGRAMS, - "kWh": ENERGY_KILO_WATT_HOUR, - "W": POWER_WATT, - "°C": TEMP_CELSIUS, + "h": UnitOfTime.HOURS, + "kg": UnitOfMass.KILOGRAMS, + "kWh": UnitOfEnergy.KILO_WATT_HOUR, + "W": UnitOfPower.WATT, + "°C": UnitOfTemperature.CELSIUS, } PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( @@ -191,7 +190,7 @@ class SAJsensor(SensorEntity): self._attr_state_class = SensorStateClass.TOTAL_INCREASING @property - def name(self): + def name(self) -> str: """Return the name of the sensor.""" if self._inverter_name: return f"saj_{self._inverter_name}_{self._sensor.name}" @@ -204,19 +203,23 @@ class SAJsensor(SensorEntity): return self._state @property - def native_unit_of_measurement(self): + def native_unit_of_measurement(self) -> str | None: """Return the unit the value is expressed in.""" return SAJ_UNIT_MAPPINGS[self._sensor.unit] @property - def device_class(self): + def device_class(self) -> SensorDeviceClass | None: """Return the device class the sensor belongs to.""" - if self.native_unit_of_measurement == POWER_WATT: + if self.native_unit_of_measurement == UnitOfPower.WATT: return SensorDeviceClass.POWER - if self.native_unit_of_measurement == ENERGY_KILO_WATT_HOUR: + if self.native_unit_of_measurement == UnitOfEnergy.KILO_WATT_HOUR: return SensorDeviceClass.ENERGY - if self.native_unit_of_measurement in (TEMP_CELSIUS, TEMP_FAHRENHEIT): + if self.native_unit_of_measurement in ( + UnitOfTemperature.CELSIUS, + UnitOfTemperature.FAHRENHEIT, + ): return SensorDeviceClass.TEMPERATURE + return None @property def per_day_basis(self) -> bool: @@ -250,6 +253,6 @@ class SAJsensor(SensorEntity): self.async_write_ha_state() @property - def unique_id(self): + def unique_id(self) -> str: """Return a unique identifier for this sensor.""" return f"{self._serialnumber}_{self._sensor.name}" diff --git a/homeassistant/components/samsungtv/__init__.py b/homeassistant/components/samsungtv/__init__.py index 0a7fd4b3378..5edae371517 100644 --- a/homeassistant/components/samsungtv/__init__.py +++ b/homeassistant/components/samsungtv/__init__.py @@ -282,8 +282,10 @@ async def _async_create_bridge_with_updated_data( if model_requires_encryption(model) and method != METHOD_ENCRYPTED_WEBSOCKET: LOGGER.info( - "Detected model %s for %s. Some televisions from H and J series use " - "an encrypted protocol but you are using %s which may not be supported", + ( + "Detected model %s for %s. Some televisions from H and J series use " + "an encrypted protocol but you are using %s which may not be supported" + ), model, host, method, diff --git a/homeassistant/components/samsungtv/bridge.py b/homeassistant/components/samsungtv/bridge.py index 502c7f2bbb6..20a4765957a 100644 --- a/homeassistant/components/samsungtv/bridge.py +++ b/homeassistant/components/samsungtv/bridge.py @@ -503,9 +503,11 @@ class SamsungTVWSBridge( return RESULT_SUCCESS except ConnectionClosedError as err: LOGGER.info( - "Working but unsupported config: %s, error: '%s'; this may " - "be an indication that access to the TV has been denied. Please " - "check the Device Connection Manager on your TV", + ( + "Working but unsupported config: %s, error: '%s'; this may be" + " an indication that access to the TV has been denied. Please" + " check the Device Connection Manager on your TV" + ), config, err, ) @@ -590,8 +592,10 @@ class SamsungTVWSBridge( self._remote = None except ConnectionFailure as err: LOGGER.warning( - "Unexpected ConnectionFailure trying to get remote for %s, " - "please report this issue: %s", + ( + "Unexpected ConnectionFailure trying to get remote for %s, " + "please report this issue: %s" + ), self.host, repr(err), ) @@ -633,8 +637,10 @@ class SamsungTVWSBridge( message := data.get("message") ) == "unrecognized method value : ms.remote.control": LOGGER.error( - "Your TV seems to be unsupported by SamsungTVWSBridge" - " and needs a PIN: '%s'. Updating config entry", + ( + "Your TV seems to be unsupported by SamsungTVWSBridge" + " and needs a PIN: '%s'. Updating config entry" + ), message, ) self._notify_update_config_entry( @@ -780,7 +786,10 @@ class SamsungTVEncryptedBridge( else: if self._model and not self._power_off_warning_logged: LOGGER.warning( - "Unknown power_off command for %s (%s): sending KEY_POWEROFF and KEY_POWER", + ( + "Unknown power_off command for %s (%s): sending KEY_POWEROFF" + " and KEY_POWER" + ), self._model, self.host, ) diff --git a/homeassistant/components/samsungtv/config_flow.py b/homeassistant/components/samsungtv/config_flow.py index d7ad62bdf7a..bc8cc895849 100644 --- a/homeassistant/components/samsungtv/config_flow.py +++ b/homeassistant/components/samsungtv/config_flow.py @@ -467,7 +467,7 @@ class SamsungTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): await self._async_get_and_check_device_info() # The UDN provided by the ssdp discovery doesn't always match the UDN - # from the device_info, used by the the other methods so we need to + # from the device_info, used by the other methods so we need to # ensure the device_info is loaded before setting the unique_id await self._async_set_unique_id_from_udn() self._async_update_and_abort_for_matching_unique_id() diff --git a/homeassistant/components/samsungtv/manifest.json b/homeassistant/components/samsungtv/manifest.json index 8a922fbbaf0..e1c326ba364 100644 --- a/homeassistant/components/samsungtv/manifest.json +++ b/homeassistant/components/samsungtv/manifest.json @@ -8,7 +8,7 @@ "samsungctl[websocket]==0.7.1", "samsungtvws[async,encrypted]==2.5.0", "wakeonlan==2.1.0", - "async-upnp-client==0.32.3" + "async-upnp-client==0.33.0" ], "ssdp": [ { diff --git a/homeassistant/components/samsungtv/strings.json b/homeassistant/components/samsungtv/strings.json index d0f4526335a..e67b50fae78 100644 --- a/homeassistant/components/samsungtv/strings.json +++ b/homeassistant/components/samsungtv/strings.json @@ -16,7 +16,7 @@ "description": "[%key:component::samsungtv::config::step::confirm::description%]" }, "reauth_confirm": { - "description": "After submitting, accept the the popup on {device} requesting authorization within 30 seconds or input PIN." + "description": "After submitting, accept the popup on {device} requesting authorization within 30 seconds or input PIN." }, "encrypted_pairing": { "description": "Please enter the PIN displayed on {device}." diff --git a/homeassistant/components/samsungtv/translations/en.json b/homeassistant/components/samsungtv/translations/en.json index c4e0e181090..4e7af766199 100644 --- a/homeassistant/components/samsungtv/translations/en.json +++ b/homeassistant/components/samsungtv/translations/en.json @@ -26,7 +26,7 @@ "description": "Do you want to set up {device}? If you never connected Home Assistant before you should see a popup on your TV asking for authorization." }, "reauth_confirm": { - "description": "After submitting, accept the the popup on {device} requesting authorization within 30 seconds or input PIN." + "description": "After submitting, accept the popup on {device} requesting authorization within 30 seconds or input PIN." }, "reauth_confirm_encrypted": { "description": "Please enter the PIN displayed on {device}." diff --git a/homeassistant/components/samsungtv/translations/et.json b/homeassistant/components/samsungtv/translations/et.json index e2dcf966ae9..ee66b539b8a 100644 --- a/homeassistant/components/samsungtv/translations/et.json +++ b/homeassistant/components/samsungtv/translations/et.json @@ -26,7 +26,7 @@ "description": "Kas h\u00e4\u00e4lestada seade {device}? Kui varem pole Home Assistantiga \u00fchendutud peaks teler kuvama h\u00fcpikakna tuvastusteabe sisestamiseks." }, "reauth_confirm": { - "description": "P\u00e4rast esitamist n\u00f5ustu {device} h\u00fcpikaknaga, mis taotleb autoriseerimist 30 sekundi jooksul v\u00f5i sisesta PIN." + "description": "P\u00e4rast esitamist n\u00f5ustu {device} h\u00fcpikaknaga mis taotleb autoriseerimist 30 sekundi jooksul v\u00f5i sisesta PIN." }, "reauth_confirm_encrypted": { "description": "Sisesta seadmes {device} kuvatav PIN-kood." diff --git a/homeassistant/components/samsungtv/translations/hu.json b/homeassistant/components/samsungtv/translations/hu.json index d4f37d72922..15237c12cc2 100644 --- a/homeassistant/components/samsungtv/translations/hu.json +++ b/homeassistant/components/samsungtv/translations/hu.json @@ -26,7 +26,7 @@ "description": "Szeretn\u00e9 be\u00e1ll\u00edtani {device} k\u00e9sz\u00fcl\u00e9k\u00e9t? Ha kor\u00e1bban m\u00e9g sosem csatlakoztatta Home Assistanthoz, akkor meg kell jelennie egy felugr\u00f3 ablaknak a TV k\u00e9perny\u0151j\u00e9n, ami j\u00f3v\u00e1hagy\u00e1sra v\u00e1r." }, "reauth_confirm": { - "description": "A bek\u00fcld\u00e9s ut\u00e1n, fogadja el a {device} felugr\u00f3 ablak\u00e1ban l\u00e1that\u00f3 \u00fczenetet, mely 30 m\u00e1sodpercig \u00e1ll rendelkez\u00e9sre, vagy adja meg a PIN k\u00f3dot." + "description": "A bek\u00fcld\u00e9s ut\u00e1n, fogadja el a {device} k\u00e9perny\u0151j\u00e9n felugr\u00f3 ablakban l\u00e1that\u00f3 \u00fczenetet, vagy adja meg a PIN k\u00f3dot 30 m\u00e1sodpercen bel\u00fcl." }, "reauth_confirm_encrypted": { "description": "K\u00e9rem, adja meg az eszk\u00f6z\u00f6n megjelen\u0151 PIN-k\u00f3dot: {device}" diff --git a/homeassistant/components/samsungtv/translations/it.json b/homeassistant/components/samsungtv/translations/it.json index 2f96d7566a6..f190831e7de 100644 --- a/homeassistant/components/samsungtv/translations/it.json +++ b/homeassistant/components/samsungtv/translations/it.json @@ -26,7 +26,7 @@ "description": "Vuoi configurare {device}? Se non hai mai collegato Home Assistant, dovresti vedere un popup sulla tua TV che chiede l'autorizzazione." }, "reauth_confirm": { - "description": "Dopo l'invio, accetta la notifica su {device} richiedendo l'autorizzazione entro 30 secondi o digita il PIN." + "description": "Dopo l'invio, accetta la notifica su {device} richiedendo l'autorizzazione entro 30 secondi oppure inserisci il PIN." }, "reauth_confirm_encrypted": { "description": "Digita il PIN visualizzato su {device}." diff --git a/homeassistant/components/samsungtv/translations/no.json b/homeassistant/components/samsungtv/translations/no.json index 049b3c27198..7bde8d4dc5d 100644 --- a/homeassistant/components/samsungtv/translations/no.json +++ b/homeassistant/components/samsungtv/translations/no.json @@ -26,7 +26,7 @@ "description": "Vil du konfigurere {device} ? Hvis du aldri har koblet til Home Assistant f\u00f8r, b\u00f8r du se en popup p\u00e5 TV-en din som ber om autorisasjon." }, "reauth_confirm": { - "description": "Etter innsending, godta popup-vinduet p\u00e5 {device} ber om autorisasjon innen 30 sekunder eller angi PIN-kode." + "description": "Etter innsending godtar du popup-vinduet p\u00e5 {device} ber om autorisasjon innen 30 sekunder eller skriv inn PIN-kode." }, "reauth_confirm_encrypted": { "description": "Vennligst skriv inn PIN-koden som vises p\u00e5 {device} ." diff --git a/homeassistant/components/samsungtv/translations/pt-BR.json b/homeassistant/components/samsungtv/translations/pt-BR.json index c1480bb93c9..7c146dac1c6 100644 --- a/homeassistant/components/samsungtv/translations/pt-BR.json +++ b/homeassistant/components/samsungtv/translations/pt-BR.json @@ -26,7 +26,7 @@ "description": "Deseja configurar {device}? Se voc\u00ea nunca conectou o Home Assistant antes, aparecer\u00e1 um pop-up na sua TV pedindo autoriza\u00e7\u00e3o." }, "reauth_confirm": { - "description": "Ap\u00f3s o envio, aceite o pop-up em {device} solicitando autoriza\u00e7\u00e3o em 30 segundos ou insira o PIN." + "description": "Depois de enviar, aceite o pop-up em {device} solicitando autoriza\u00e7\u00e3o em 30 segundos ou digite o PIN." }, "reauth_confirm_encrypted": { "description": "Insira o PIN exibido em {device}." diff --git a/homeassistant/components/samsungtv/translations/pt.json b/homeassistant/components/samsungtv/translations/pt.json index 3bfc36fd84d..3a197cfab41 100644 --- a/homeassistant/components/samsungtv/translations/pt.json +++ b/homeassistant/components/samsungtv/translations/pt.json @@ -3,13 +3,13 @@ "abort": { "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", "already_in_progress": "O processo de configura\u00e7\u00e3o j\u00e1 est\u00e1 a decorrer", - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "flow_title": "{device}", "step": { "user": { "data": { - "host": "Servidor", + "host": "Endere\u00e7o", "name": "Nome" } } diff --git a/homeassistant/components/samsungtv/translations/sk.json b/homeassistant/components/samsungtv/translations/sk.json index 4be02aa40f4..838f4a7ed51 100644 --- a/homeassistant/components/samsungtv/translations/sk.json +++ b/homeassistant/components/samsungtv/translations/sk.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "auth_missing": "Home Assistant nem\u00e1 opr\u00e1vnenie na pripojenie k tomuto telev\u00edzoru Samsung. Skontrolujte nastavenia Spr\u00e1vcu extern\u00fdch zariaden\u00ed v\u00e1\u0161ho telev\u00edzora a povo\u013ete Home Assistant.", "cannot_connect": "Nepodarilo sa pripoji\u0165", "id_missing": "Toto zariadenie Samsung nem\u00e1 s\u00e9riov\u00e9 \u010d\u00edslo.", "not_supported": "Toto zariadenie Samsung moment\u00e1lne nie je podporovan\u00e9.", @@ -10,13 +11,23 @@ "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "error": { + "auth_missing": "Home Assistant nem\u00e1 opr\u00e1vnenie na pripojenie k tomuto telev\u00edzoru Samsung. Skontrolujte nastavenia Spr\u00e1vcu extern\u00fdch zariaden\u00ed v\u00e1\u0161ho telev\u00edzora a povo\u013ete Home Assistant.", "invalid_pin": "PIN je neplatn\u00fd, sk\u00faste to znova." }, "flow_title": "{device}", "step": { + "confirm": { + "description": "Chcete nastavi\u0165 {device}? Ak ste Home Assistant predt\u00fdm nikdy nepripojili, na va\u0161om telev\u00edzore by sa malo zobrazi\u0165 vyskakovacie okno so \u017eiados\u0165ou o autoriz\u00e1ciu." + }, "encrypted_pairing": { "description": "Zadajte k\u00f3d PIN zobrazen\u00fd na {device}." }, + "pairing": { + "description": "Chcete nastavi\u0165 {device}? Ak ste Home Assistant predt\u00fdm nikdy nepripojili, na va\u0161om telev\u00edzore by sa malo zobrazi\u0165 vyskakovacie okno so \u017eiados\u0165ou o autoriz\u00e1ciu." + }, + "reauth_confirm": { + "description": "Po odoslan\u00ed prijmite na {device} do 30 sek\u00fand kontextov\u00e9 okno so \u017eiados\u0165ou o autoriz\u00e1ciu alebo zadajte PIN." + }, "reauth_confirm_encrypted": { "description": "Zadajte k\u00f3d PIN zobrazen\u00fd na {device}." }, @@ -24,7 +35,8 @@ "data": { "host": "Hostite\u013e", "name": "N\u00e1zov" - } + }, + "description": "Zadajte inform\u00e1cie o svojom telev\u00edzore Samsung. Ak ste Home Assistant predt\u00fdm nikdy nepripojili, na va\u0161om telev\u00edzore by sa malo zobrazi\u0165 vyskakovacie okno so \u017eiados\u0165ou o autoriz\u00e1ciu." } } } diff --git a/homeassistant/components/schedule/__init__.py b/homeassistant/components/schedule/__init__.py index fefb5189e3c..d7c61798587 100644 --- a/homeassistant/components/schedule/__init__.py +++ b/homeassistant/components/schedule/__init__.py @@ -69,7 +69,8 @@ def valid_schedule(schedule: list[dict[str, str]]) -> list[dict[str, str]]: for time_range in schedule: if time_range[CONF_FROM] >= time_range[CONF_TO]: raise vol.Invalid( - f"Invalid time range, from {time_range[CONF_FROM]} is after {time_range[CONF_TO]}" + f"Invalid time range, from {time_range[CONF_FROM]} is after" + f" {time_range[CONF_TO]}" ) # Check if the from time of the event is after the to time of the previous event diff --git a/homeassistant/components/schedule/translations/ko.json b/homeassistant/components/schedule/translations/ko.json new file mode 100644 index 00000000000..511a01b0141 --- /dev/null +++ b/homeassistant/components/schedule/translations/ko.json @@ -0,0 +1,8 @@ +{ + "state": { + "_": { + "off": "\uaebc\uc9d0", + "on": "\ucf1c\uc9d0" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/schedule/translations/sk.json b/homeassistant/components/schedule/translations/sk.json index 06788442e27..62c5e37c094 100644 --- a/homeassistant/components/schedule/translations/sk.json +++ b/homeassistant/components/schedule/translations/sk.json @@ -4,5 +4,6 @@ "off": "Neakt\u00edvny", "on": "Akt\u00edvny" } - } + }, + "title": "Pl\u00e1novanie" } \ No newline at end of file diff --git a/homeassistant/components/schluter/climate.py b/homeassistant/components/schluter/climate.py index 24a09437d4b..c8c0d76690d 100644 --- a/homeassistant/components/schluter/climate.py +++ b/homeassistant/components/schluter/climate.py @@ -10,13 +10,12 @@ import voluptuous as vol from homeassistant.components.climate import ( PLATFORM_SCHEMA, SCAN_INTERVAL, - TEMP_CELSIUS, ClimateEntity, ClimateEntityFeature, HVACAction, HVACMode, ) -from homeassistant.const import ATTR_TEMPERATURE, CONF_SCAN_INTERVAL +from homeassistant.const import ATTR_TEMPERATURE, CONF_SCAN_INTERVAL, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType @@ -81,7 +80,7 @@ class SchluterThermostat(CoordinatorEntity, ClimateEntity): _attr_hvac_mode = HVACMode.HEAT _attr_hvac_modes = [HVACMode.HEAT] _attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS def __init__(self, coordinator, serial_number, api, session_id): """Initialize the thermostat.""" diff --git a/homeassistant/components/scrape/sensor.py b/homeassistant/components/scrape/sensor.py index e16f6c20f8d..22184a17b80 100644 --- a/homeassistant/components/scrape/sensor.py +++ b/homeassistant/components/scrape/sensor.py @@ -13,7 +13,9 @@ from homeassistant.components.sensor import ( DEVICE_CLASSES_SCHEMA, PLATFORM_SCHEMA as PARENT_PLATFORM_SCHEMA, STATE_CLASSES_SCHEMA, + SensorDeviceClass, ) +from homeassistant.components.sensor.helpers import async_parse_date_datetime from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONF_ATTRIBUTE, @@ -246,12 +248,19 @@ class ScrapeSensor(CoordinatorEntity[ScrapeCoordinator], TemplateSensor): """Update state from the rest data.""" value = self._extract_value() - if self._value_template is not None: - self._attr_native_value = ( - self._value_template.async_render_with_possible_json_value(value, None) - ) - else: + if (template := self._value_template) is not None: + value = template.async_render_with_possible_json_value(value, None) + + if self.device_class not in { + SensorDeviceClass.DATE, + SensorDeviceClass.TIMESTAMP, + }: self._attr_native_value = value + return + + self._attr_native_value = async_parse_date_datetime( + value, self.entity_id, self.device_class + ) @callback def _handle_coordinator_update(self) -> None: diff --git a/homeassistant/components/scrape/translations/bg.json b/homeassistant/components/scrape/translations/bg.json index 059fe59152c..221da5ae23b 100644 --- a/homeassistant/components/scrape/translations/bg.json +++ b/homeassistant/components/scrape/translations/bg.json @@ -18,7 +18,7 @@ }, "user": { "data": { - "authentication": "\u0410\u0432\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f", + "authentication": "\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u043c\u0435\u0442\u043e\u0434 \u0437\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f", "method": "\u041c\u0435\u0442\u043e\u0434", "password": "\u041f\u0430\u0440\u043e\u043b\u0430", "username": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435" @@ -33,12 +33,35 @@ }, "options": { "step": { - "init": { + "add_sensor": { "data": { - "authentication": "\u0410\u0432\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f", + "attribute": "\u0410\u0442\u0440\u0438\u0431\u0443\u0442", + "index": "\u0418\u043d\u0434\u0435\u043a\u0441", + "select": "\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435", + "unit_of_measurement": "\u041c\u0435\u0440\u043d\u0430 \u0435\u0434\u0438\u043d\u0438\u0446\u0430" + } + }, + "edit_sensor": { + "data": { + "attribute": "\u0410\u0442\u0440\u0438\u0431\u0443\u0442", + "index": "\u0418\u043d\u0434\u0435\u043a\u0441", + "select": "\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435", + "unit_of_measurement": "\u041c\u0435\u0440\u043d\u0430 \u0435\u0434\u0438\u043d\u0438\u0446\u0430" + } + }, + "init": { + "menu_options": { + "add_sensor": "\u0414\u043e\u0431\u0430\u0432\u044f\u043d\u0435 \u043d\u0430 \u0441\u0435\u043d\u0437\u043e\u0440", + "remove_sensor": "\u041f\u0440\u0435\u043c\u0430\u0445\u0432\u0430\u043d\u0435 \u043d\u0430 \u0441\u0435\u043d\u0437\u043e\u0440\u0430", + "resource": "\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u0440\u0435\u0441\u0443\u0440\u0441", + "select_edit_sensor": "\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u0441\u0435\u043d\u0437\u043e\u0440\u0430" + } + }, + "resource": { + "data": { + "authentication": "\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u043c\u0435\u0442\u043e\u0434 \u0437\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f", "method": "\u041c\u0435\u0442\u043e\u0434", - "password": "\u041f\u0430\u0440\u043e\u043b\u0430", - "username": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435" + "resource": "\u0420\u0435\u0441\u0443\u0440\u0441" } } } diff --git a/homeassistant/components/scrape/translations/ca.json b/homeassistant/components/scrape/translations/ca.json index 5b3688f2e2d..26e67a7d43a 100644 --- a/homeassistant/components/scrape/translations/ca.json +++ b/homeassistant/components/scrape/translations/ca.json @@ -30,7 +30,7 @@ }, "user": { "data": { - "authentication": "Autenticaci\u00f3", + "authentication": "Selecciona el m\u00e8tode d'autenticaci\u00f3", "headers": "Cap\u00e7aleres", "method": "M\u00e8tode", "password": "Contrasenya", @@ -62,6 +62,28 @@ "attribute": "Atribut", "device_class": "Classe de dispositiu", "index": "\u00cdndex", + "name": "Nom", + "select": "Selecciona", + "state_class": "Classe d'estat", + "unit_of_measurement": "Unitat de mesura", + "value_template": "Plantilla de valor" + }, + "data_description": { + "attribute": "Obt\u00e9 el valor d'un atribut de l'etiqueta seleccionada", + "device_class": "Tipus/classe del sensor per configurar-ne la icona a la interf\u00edcie d'usuari", + "index": "Defineix quins dels elements retornats pel selector CSS utilitzar", + "select": "Defineix quina etiqueta s'ha de buscar. Consulta els selectors CSS de Beautifulsoup per m\u00e9s informaci\u00f3", + "state_class": "'state_class' del sensor", + "unit_of_measurement": "Tria la unitat de mesura de temperatura o crea la teva", + "value_template": "Plantilla per obtenir l'estat del sensor" + } + }, + "edit_sensor": { + "data": { + "attribute": "Atribut", + "device_class": "Classe de dispositiu", + "index": "\u00cdndex", + "name": "Nom", "select": "Selecciona", "state_class": "Classe d'estat", "unit_of_measurement": "Unitat de mesura", @@ -78,8 +100,16 @@ } }, "init": { + "menu_options": { + "add_sensor": "Afegeix sensor", + "remove_sensor": "Elimina sensor", + "resource": "Configura recurs", + "select_edit_sensor": "Configura sensor" + } + }, + "resource": { "data": { - "authentication": "Autenticaci\u00f3", + "authentication": "Selecciona el m\u00e8tode d'autenticaci\u00f3", "headers": "Cap\u00e7aleres", "method": "M\u00e8tode", "password": "Contrasenya", @@ -88,27 +118,6 @@ "username": "Nom d'usuari", "verify_ssl": "Verifica el certificat SSL" }, - "data_description": { - "authentication": "Tipus d'autenticaci\u00f3 HTTP. O b\u00e0sica o 'digest'", - "headers": "Cap\u00e7aleres a utilitzar per a la sol\u00b7licitud web", - "resource": "URL del lloc web que cont\u00e9 el valor", - "timeout": "Temps d'espera de connexi\u00f3 al lloc web", - "verify_ssl": "Activa/desactiva la verificaci\u00f3 del certificat SSL/TLS, per exemple, si est\u00e0 autosignat" - }, - "menu_options": { - "add_sensor": "Afegeix sensor", - "remove_sensor": "Elimina sensor", - "resource": "Configura recurs" - } - }, - "resource": { - "data": { - "authentication": "Autenticaci\u00f3", - "headers": "Cap\u00e7aleres", - "method": "M\u00e8tode", - "resource": "Recurs", - "timeout": "Temps d'espera" - }, "data_description": { "authentication": "Tipus d'autenticaci\u00f3 HTTP. O b\u00e0sica o 'digest'", "headers": "Cap\u00e7aleres a utilitzar per a la sol\u00b7licitud web", diff --git a/homeassistant/components/scrape/translations/cs.json b/homeassistant/components/scrape/translations/cs.json index 68cbf40a518..1c1cca64b5a 100644 --- a/homeassistant/components/scrape/translations/cs.json +++ b/homeassistant/components/scrape/translations/cs.json @@ -14,6 +14,7 @@ "index": "Index", "name": "Jm\u00e9no", "select": "Vybrat", + "state_class": "Trieda stavu", "unit_of_measurement": "Jednotka m\u011b\u0159en\u00ed", "value_template": "\u0160ablona hodnoty" }, @@ -43,13 +44,21 @@ }, "options": { "step": { - "init": { + "edit_sensor": { "data": { - "method": "Metoda", - "timeout": "\u010casov\u00fd limit" + "attribute": "Atribut", + "device_class": "T\u0159\u00edda za\u0159\u00edzen\u00ed", + "index": "Index", + "select": "Vybrat", + "state_class": "Trieda stavu", + "unit_of_measurement": "Jednotka m\u011b\u0159en\u00ed", + "value_template": "\u0160ablona hodnoty" }, "data_description": { - "timeout": "\u010casov\u00fd limit pro p\u0159ipojen\u00ed k webu" + "attribute": "Z\u00edskat hodnotu atributu na vybran\u00e9m tagu", + "device_class": "Typ/t\u0159\u00edda senzoru pro nastaven\u00ed ikony v rozhran\u00ed", + "index": "Definuje, kter\u00fd z prvk\u016f vr\u00e1cen\u00fdch selektorem CSS se m\u00e1 pou\u017e\u00edt", + "select": "Definuje, jak\u00fd tag hledat. Podrobnosti najdete v selektorech CSS Beautifulsoup" } } } diff --git a/homeassistant/components/scrape/translations/de.json b/homeassistant/components/scrape/translations/de.json index 9deb92dec32..c7fa0e71df7 100644 --- a/homeassistant/components/scrape/translations/de.json +++ b/homeassistant/components/scrape/translations/de.json @@ -40,7 +40,7 @@ "verify_ssl": "SSL-Zertifikat \u00fcberpr\u00fcfen" }, "data_description": { - "authentication": "Typ der HTTP-Authentifizierung. Entweder basic oder digest", + "authentication": "Typ der HTTP-Authentifizierung. Entweder Basic oder Digest", "headers": "F\u00fcr die Webanforderung zu verwendende Header", "resource": "Die URL der Website, die den Wert enth\u00e4lt", "timeout": "Zeit\u00fcberschreitung f\u00fcr die Verbindung zur Website", @@ -78,28 +78,33 @@ "value_template": "Definiert eine Vorlage, um den Zustand des Sensors zu ermitteln" } }, - "init": { + "edit_sensor": { "data": { - "authentication": "Authentifizierungsmethode ausw\u00e4hlen", - "headers": "Header", - "method": "Methode", - "password": "Passwort", - "resource": "Ressource", - "timeout": "Zeit\u00fcberschreitung", - "username": "Benutzername", - "verify_ssl": "SSL-Zertifikat \u00fcberpr\u00fcfen" + "attribute": "Attribut", + "device_class": "Ger\u00e4teklasse", + "index": "Index", + "name": "Name", + "select": "Ausw\u00e4hlen", + "state_class": "Zustandsklasse", + "unit_of_measurement": "Ma\u00dfeinheit", + "value_template": "Wertvorlage" }, "data_description": { - "authentication": "Typ der HTTP-Authentifizierung. Entweder basic oder digest", - "headers": "F\u00fcr die Webanforderung zu verwendende Header", - "resource": "Die URL der Website, die den Wert enth\u00e4lt", - "timeout": "Zeit\u00fcberschreitung f\u00fcr die Verbindung zur Website", - "verify_ssl": "Aktiviert/deaktiviert die \u00dcberpr\u00fcfung des SSL/TLS-Zertifikats, z.B. wenn es selbst signiert ist" - }, + "attribute": "Wert eines Attributs auf dem ausgew\u00e4hlten Tag abrufen", + "device_class": "Der Typ/die Klasse des Sensors, um das Symbol im Frontend festzulegen", + "index": "Definiert, welche der vom CSS-Selektor zur\u00fcckgegebenen Elemente verwendet werden sollen", + "select": "Legt fest, nach welchem Tag gesucht werden soll. Siehe Beautifulsoup CSS-Selektoren f\u00fcr Details", + "state_class": "Die state_class des Sensors", + "unit_of_measurement": "W\u00e4hle die Temperaturmessung oder erstelle deine eigene", + "value_template": "Definiert eine Vorlage, um den Zustand des Sensors zu ermitteln" + } + }, + "init": { "menu_options": { "add_sensor": "Sensor hinzuf\u00fcgen", "remove_sensor": "Sensor entfernen", - "resource": "Ressource konfigurieren" + "resource": "Ressource konfigurieren", + "select_edit_sensor": "Sensor konfigurieren" } }, "resource": { @@ -114,7 +119,7 @@ "verify_ssl": "SSL-Zertifikat \u00fcberpr\u00fcfen" }, "data_description": { - "authentication": "Typ der HTTP-Authentifizierung. Entweder basic oder digest", + "authentication": "Typ der HTTP-Authentifizierung. Entweder Basic oder Digest", "headers": "F\u00fcr die Webanforderung zu verwendende Header", "resource": "Die URL der Website, die den Wert enth\u00e4lt", "timeout": "Zeit\u00fcberschreitung f\u00fcr die Verbindung zur Website", diff --git a/homeassistant/components/scrape/translations/el.json b/homeassistant/components/scrape/translations/el.json index b516f9b37d7..6bb9e6b0345 100644 --- a/homeassistant/components/scrape/translations/el.json +++ b/homeassistant/components/scrape/translations/el.json @@ -16,7 +16,7 @@ "select": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae", "state_class": "\u039a\u03bb\u03ac\u03c3\u03b7 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2", "unit_of_measurement": "\u039c\u03bf\u03bd\u03ac\u03b4\u03b1 \u03bc\u03ad\u03c4\u03c1\u03b7\u03c3\u03b7\u03c2", - "value_template": "\u03a0\u03c1\u03cc\u03c4\u03c5\u03c0\u03bf \u03c4\u03b9\u03bc\u03ae\u03c2 " + "value_template": "\u03a0\u03c1\u03cc\u03c4\u03c5\u03c0\u03bf \u03c4\u03b9\u03bc\u03ae\u03c2" }, "data_description": { "attribute": "\u039b\u03ae\u03c8\u03b7 \u03c4\u03b7\u03c2 \u03c4\u03b9\u03bc\u03ae\u03c2 \u03b5\u03bd\u03cc\u03c2 \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03b7\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03bf\u03cd \u03c3\u03c4\u03b7\u03bd \u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03b7 \u03b5\u03c4\u03b9\u03ba\u03ad\u03c4\u03b1", @@ -30,7 +30,7 @@ }, "user": { "data": { - "authentication": "\u0395\u03bb\u03ad\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", + "authentication": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03bc\u03ad\u03b8\u03bf\u03b4\u03bf \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", "headers": "\u039a\u03b5\u03c6\u03b1\u03bb\u03af\u03b4\u03b5\u03c2", "method": "\u039c\u03ad\u03b8\u03bf\u03b4\u03bf\u03c2", "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", @@ -57,16 +57,62 @@ }, "options": { "step": { - "init": { + "add_sensor": { "data": { - "authentication": "\u0395\u03bb\u03ad\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", + "attribute": "\u03a7\u03b1\u03c1\u03b1\u03ba\u03c4\u03b7\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc", + "device_class": "\u039a\u03bb\u03ac\u03c3\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2", + "index": "\u0394\u03b5\u03af\u03ba\u03c4\u03b7\u03c2", + "select": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae", + "state_class": "\u039a\u03bb\u03ac\u03c3\u03b7 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2", + "unit_of_measurement": "\u039c\u03bf\u03bd\u03ac\u03b4\u03b1 \u03bc\u03ad\u03c4\u03c1\u03b7\u03c3\u03b7\u03c2", + "value_template": "\u03a0\u03c1\u03cc\u03c4\u03c5\u03c0\u03bf \u03c4\u03b9\u03bc\u03ae\u03c2" + }, + "data_description": { + "attribute": "\u039b\u03ae\u03c8\u03b7 \u03c4\u03b7\u03c2 \u03c4\u03b9\u03bc\u03ae\u03c2 \u03b5\u03bd\u03cc\u03c2 \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03b7\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03bf\u03cd \u03c3\u03c4\u03b7\u03bd \u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03b7 \u03b5\u03c4\u03b9\u03ba\u03ad\u03c4\u03b1", + "device_class": "\u039f \u03c4\u03cd\u03c0\u03bf\u03c2/\u03ba\u03bb\u03ac\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 \u03b3\u03b9\u03b1 \u03c4\u03bf\u03bd \u03bf\u03c1\u03b9\u03c3\u03bc\u03cc \u03c4\u03bf\u03c5 \u03b5\u03b9\u03ba\u03bf\u03bd\u03b9\u03b4\u03af\u03bf\u03c5 \u03c3\u03c4\u03bf frontend", + "index": "\u039a\u03b1\u03b8\u03bf\u03c1\u03af\u03b6\u03b5\u03b9 \u03c0\u03bf\u03b9\u03b1 \u03b1\u03c0\u03cc \u03c4\u03b1 \u03c3\u03c4\u03bf\u03b9\u03c7\u03b5\u03af\u03b1 \u03c0\u03bf\u03c5 \u03b5\u03c0\u03b9\u03c3\u03c4\u03c1\u03ad\u03c6\u03bf\u03bd\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03bf\u03bd \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03b1 CSS \u03b3\u03b9\u03b1 \u03c7\u03c1\u03ae\u03c3\u03b7", + "select": "\u039a\u03b1\u03b8\u03bf\u03c1\u03af\u03b6\u03b5\u03b9 \u03c0\u03bf\u03b9\u03b1 \u03b5\u03c4\u03b9\u03ba\u03ad\u03c4\u03b1 \u03b8\u03b1 \u03b1\u03bd\u03b1\u03b6\u03b7\u03c4\u03b7\u03b8\u03b5\u03af. \u0395\u03bb\u03ad\u03b3\u03be\u03c4\u03b5 \u03c4\u03bf\u03c5\u03c2 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03b5\u03af\u03c2 CSS Beautifulsoup \u03b3\u03b9\u03b1 \u03bb\u03b5\u03c0\u03c4\u03bf\u03bc\u03ad\u03c1\u03b5\u03b9\u03b5\u03c2", + "state_class": "\u0397 state_class \u03c4\u03bf\u03c5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1", + "unit_of_measurement": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03bc\u03ad\u03c4\u03c1\u03b7\u03c3\u03b7 \u03b8\u03b5\u03c1\u03bc\u03bf\u03ba\u03c1\u03b1\u03c3\u03af\u03b1\u03c2 \u03ae \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03ba\u03ae \u03c3\u03b1\u03c2", + "value_template": "\u039a\u03b1\u03b8\u03bf\u03c1\u03af\u03b6\u03b5\u03b9 \u03ad\u03bd\u03b1 \u03c0\u03c1\u03cc\u03c4\u03c5\u03c0\u03bf \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03bb\u03ae\u03c8\u03b7 \u03c4\u03b7\u03c2 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1" + } + }, + "edit_sensor": { + "data": { + "attribute": "\u03a7\u03b1\u03c1\u03b1\u03ba\u03c4\u03b7\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc", + "device_class": "\u039a\u03bb\u03ac\u03c3\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2", + "index": "\u0394\u03b5\u03af\u03ba\u03c4\u03b7\u03c2", + "select": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae", + "state_class": "\u039a\u03bb\u03ac\u03c3\u03b7 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2", + "unit_of_measurement": "\u039c\u03bf\u03bd\u03ac\u03b4\u03b1 \u03bc\u03ad\u03c4\u03c1\u03b7\u03c3\u03b7\u03c2", + "value_template": "\u03a0\u03c1\u03cc\u03c4\u03c5\u03c0\u03bf \u03c4\u03b9\u03bc\u03ae\u03c2" + }, + "data_description": { + "attribute": "\u039b\u03ae\u03c8\u03b7 \u03c4\u03b7\u03c2 \u03c4\u03b9\u03bc\u03ae\u03c2 \u03b5\u03bd\u03cc\u03c2 \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03b7\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03bf\u03cd \u03c3\u03c4\u03b7\u03bd \u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03b7 \u03b5\u03c4\u03b9\u03ba\u03ad\u03c4\u03b1", + "device_class": "\u039f \u03c4\u03cd\u03c0\u03bf\u03c2/\u03ba\u03bb\u03ac\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 \u03b3\u03b9\u03b1 \u03c4\u03bf\u03bd \u03bf\u03c1\u03b9\u03c3\u03bc\u03cc \u03c4\u03bf\u03c5 \u03b5\u03b9\u03ba\u03bf\u03bd\u03b9\u03b4\u03af\u03bf\u03c5 \u03c3\u03c4\u03bf frontend", + "index": "\u039a\u03b1\u03b8\u03bf\u03c1\u03af\u03b6\u03b5\u03b9 \u03c0\u03bf\u03b9\u03b1 \u03b1\u03c0\u03cc \u03c4\u03b1 \u03c3\u03c4\u03bf\u03b9\u03c7\u03b5\u03af\u03b1 \u03c0\u03bf\u03c5 \u03b5\u03c0\u03b9\u03c3\u03c4\u03c1\u03ad\u03c6\u03bf\u03bd\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03bf\u03bd \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03b1 CSS \u03b3\u03b9\u03b1 \u03c7\u03c1\u03ae\u03c3\u03b7", + "select": "\u039a\u03b1\u03b8\u03bf\u03c1\u03af\u03b6\u03b5\u03b9 \u03c0\u03bf\u03b9\u03b1 \u03b5\u03c4\u03b9\u03ba\u03ad\u03c4\u03b1 \u03b8\u03b1 \u03b1\u03bd\u03b1\u03b6\u03b7\u03c4\u03b7\u03b8\u03b5\u03af. \u0395\u03bb\u03ad\u03b3\u03be\u03c4\u03b5 \u03c4\u03bf\u03c5\u03c2 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03b5\u03af\u03c2 CSS Beautifulsoup \u03b3\u03b9\u03b1 \u03bb\u03b5\u03c0\u03c4\u03bf\u03bc\u03ad\u03c1\u03b5\u03b9\u03b5\u03c2", + "state_class": "\u0397 state_class \u03c4\u03bf\u03c5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1", + "unit_of_measurement": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03bc\u03ad\u03c4\u03c1\u03b7\u03c3\u03b7 \u03b8\u03b5\u03c1\u03bc\u03bf\u03ba\u03c1\u03b1\u03c3\u03af\u03b1\u03c2 \u03ae \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03ba\u03ae \u03c3\u03b1\u03c2", + "value_template": "\u039a\u03b1\u03b8\u03bf\u03c1\u03af\u03b6\u03b5\u03b9 \u03ad\u03bd\u03b1 \u03c0\u03c1\u03cc\u03c4\u03c5\u03c0\u03bf \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03bb\u03ae\u03c8\u03b7 \u03c4\u03b7\u03c2 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1" + } + }, + "init": { + "menu_options": { + "add_sensor": "\u03a0\u03c1\u03bf\u03c3\u03b8\u03ae\u03ba\u03b7 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1", + "remove_sensor": "\u039a\u03b1\u03c4\u03ac\u03c1\u03b3\u03b7\u03c3\u03b7 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1", + "resource": "\u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c0\u03cc\u03c1\u03bf\u03c5", + "select_edit_sensor": "\u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1" + } + }, + "resource": { + "data": { + "authentication": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03bc\u03ad\u03b8\u03bf\u03b4\u03bf \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", "headers": "\u039a\u03b5\u03c6\u03b1\u03bb\u03af\u03b4\u03b5\u03c2", "method": "\u039c\u03ad\u03b8\u03bf\u03b4\u03bf\u03c2", - "password": "\u039a\u03b5\u03bd\u03cc", + "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", "resource": "\u03a0\u03cc\u03c1\u03bf\u03c2", - "timeout": "\u03a7\u03c1\u03bf\u03bd\u03b9\u03ba\u03cc \u03cc\u03c1\u03b9\u03bf", - "username": "\u039a\u03b5\u03bd\u03cc", - "verify_ssl": "\u0395\u03c0\u03b1\u03bb\u03b7\u03b8\u03b5\u03cd\u03c3\u03c4\u03b5 \u03c4\u03bf \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03cc SSL" + "timeout": "\u03a7\u03c1\u03bf\u03bd\u03b9\u03ba\u03cc \u03cc\u03c1\u03b9\u03bf" }, "data_description": { "authentication": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03c4\u03bf\u03c5 \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 HTTP. \u0395\u03af\u03c4\u03b5 \u03b2\u03b1\u03c3\u03b9\u03ba\u03cc\u03c2 \u03b5\u03af\u03c4\u03b5 \u03ba\u03c9\u03b4\u03b9\u03ba\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03bf\u03c2", diff --git a/homeassistant/components/scrape/translations/en.json b/homeassistant/components/scrape/translations/en.json index 4b0e96da680..2aafac8e4fc 100644 --- a/homeassistant/components/scrape/translations/en.json +++ b/homeassistant/components/scrape/translations/en.json @@ -102,9 +102,9 @@ "init": { "menu_options": { "add_sensor": "Add sensor", - "select_edit_sensor": "Configure sensor", "remove_sensor": "Remove sensor", - "resource": "Configure resource" + "resource": "Configure resource", + "select_edit_sensor": "Configure sensor" } }, "resource": { diff --git a/homeassistant/components/scrape/translations/es.json b/homeassistant/components/scrape/translations/es.json index a53e6545a54..5616e126330 100644 --- a/homeassistant/components/scrape/translations/es.json +++ b/homeassistant/components/scrape/translations/es.json @@ -78,28 +78,33 @@ "value_template": "Define una plantilla para obtener el estado del sensor" } }, - "init": { + "edit_sensor": { "data": { - "authentication": "Selecciona el m\u00e9todo de autenticaci\u00f3n", - "headers": "Cabeceras", - "method": "M\u00e9todo", - "password": "Contrase\u00f1a", - "resource": "Recurso", - "timeout": "Tiempo de espera", - "username": "Nombre de usuario", - "verify_ssl": "Verificar el certificado SSL" + "attribute": "Atributo", + "device_class": "Clase de dispositivo", + "index": "\u00cdndice", + "name": "Nombre", + "select": "Seleccionar", + "state_class": "Clase de estado", + "unit_of_measurement": "Unidad de medida", + "value_template": "Plantilla de valor" }, "data_description": { - "authentication": "Tipo de autenticaci\u00f3n HTTP. Puede ser basic o digest", - "headers": "Cabeceras a usar para la petici\u00f3n web", - "resource": "La URL del sitio web que contiene el valor.", - "timeout": "Tiempo de espera para la conexi\u00f3n al sitio web", - "verify_ssl": "Habilita/deshabilita la verificaci\u00f3n del certificado SSL/TLS, por ejemplo, si est\u00e1 autofirmado" - }, + "attribute": "Obtener el valor de un atributo en la etiqueta seleccionada", + "device_class": "El tipo/clase del sensor para establecer el icono en la interfaz", + "index": "Define cu\u00e1l de los elementos devueltos por el selector CSS usar", + "select": "Define qu\u00e9 etiqueta buscar. Revisa los selectores CSS de Beautifulsoup para obtener m\u00e1s informaci\u00f3n.", + "state_class": "El state_class del sensor", + "unit_of_measurement": "Elige la medici\u00f3n de temperatura o crea la suya propia", + "value_template": "Define una plantilla para obtener el estado del sensor" + } + }, + "init": { "menu_options": { "add_sensor": "A\u00f1adir sensor", "remove_sensor": "Eliminar sensor", - "resource": "Configurar recurso" + "resource": "Configurar recurso", + "select_edit_sensor": "Configurar sensor" } }, "resource": { diff --git a/homeassistant/components/scrape/translations/et.json b/homeassistant/components/scrape/translations/et.json index bd60ffcbcc0..780023e2627 100644 --- a/homeassistant/components/scrape/translations/et.json +++ b/homeassistant/components/scrape/translations/et.json @@ -78,28 +78,33 @@ "value_template": "M\u00e4\u00e4rab malli anduri v\u00e4\u00e4rtuse jaoks" } }, - "init": { + "edit_sensor": { "data": { - "authentication": "Tuvastamine", - "headers": "P\u00e4ised", - "method": "Meetod", - "password": "Salas\u00f5na", - "resource": "Resurss", - "timeout": "Ajal\u00f5pp", - "username": "", - "verify_ssl": "" + "attribute": "Attribuut", + "device_class": "Seadme klass", + "index": "Indeks", + "name": "", + "select": "Vali", + "state_class": "Oleku klass", + "unit_of_measurement": "M\u00f5\u00f5t\u00fchik", + "value_template": "V\u00e4\u00e4rtuse mall" }, "data_description": { - "authentication": "HTTP kasutaja tuvastamise meetod; algeline v\u00f5i muu", - "headers": "Veebip\u00e4ringus kasutatav p\u00e4is", - "resource": "Veebilehe URL ei sisalda soovitud v\u00e4\u00e4rtusi", - "timeout": "Veebilehe ajal\u00f5pp", - "verify_ssl": "Lubab v\u00f5i keelab SSL/TLS serdi tuvastamise n\u00e4iteks juhul kui sert on ise allkirjastatud" - }, + "attribute": "Hangi valitud kirje v\u00e4\u00e4rtus", + "device_class": "Kuvatav ikoon anduri t\u00fc\u00fcbi/klassi alusel", + "index": "M\u00e4\u00e4rab milliseid elemente CSS valikutest kasutada", + "select": "M\u00e4\u00e4rab mida otsida. Lisateave Beatifulsoap CSS valikutest.", + "state_class": "Anduri oleku klass", + "unit_of_measurement": "Vali temperatuuri olem v\u00f5i loo uus", + "value_template": "M\u00e4\u00e4rab anduri oleku malli" + } + }, + "init": { "menu_options": { "add_sensor": "Lisa andur", "remove_sensor": "Eemalda andur", - "resource": "Seadista resurss" + "resource": "Seadista resurss", + "select_edit_sensor": "Seadista andur" } }, "resource": { diff --git a/homeassistant/components/scrape/translations/fr.json b/homeassistant/components/scrape/translations/fr.json index 6e4c0f45b67..a3479001f83 100644 --- a/homeassistant/components/scrape/translations/fr.json +++ b/homeassistant/components/scrape/translations/fr.json @@ -36,28 +36,5 @@ } } } - }, - "options": { - "step": { - "init": { - "data": { - "authentication": "S\u00e9lectionner la m\u00e9thode d\u2019authentification", - "headers": "En-t\u00eates", - "method": "M\u00e9thode", - "password": "Mot de passe", - "resource": "Ressource", - "timeout": "D\u00e9lai d\u2019expiration", - "username": "Nom d'utilisateur", - "verify_ssl": "V\u00e9rifier le certificat SSL" - }, - "data_description": { - "authentication": "M\u00e9thode d'authentification HTTP. \u00ab\u00a0basic\u00a0\u00bb ou \u00ab\u00a0digest\u00a0\u00bb", - "headers": "Les en-t\u00eates \u00e0 utiliser pour la requ\u00eate Web", - "resource": "L'URL du site web qui contient la valeur", - "timeout": "D\u00e9lai d\u2019expiration pour la connexion au site Web", - "verify_ssl": "Active ou d\u00e9sactive la v\u00e9rification du certificat SSL/TLS, par exemple s'il est auto-sign\u00e9" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/scrape/translations/he.json b/homeassistant/components/scrape/translations/he.json index 144a9567d17..b6caa6ec9d8 100644 --- a/homeassistant/components/scrape/translations/he.json +++ b/homeassistant/components/scrape/translations/he.json @@ -12,16 +12,5 @@ } } } - }, - "options": { - "step": { - "init": { - "data": { - "password": "\u05e1\u05d9\u05e1\u05de\u05d4", - "username": "\u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9", - "verify_ssl": "\u05d0\u05d9\u05de\u05d5\u05ea \u05d0\u05d9\u05e9\u05d5\u05e8 SSL" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/scrape/translations/hu.json b/homeassistant/components/scrape/translations/hu.json index 1bf1f159154..8c05fe36a2b 100644 --- a/homeassistant/components/scrape/translations/hu.json +++ b/homeassistant/components/scrape/translations/hu.json @@ -3,13 +3,39 @@ "abort": { "already_configured": "A fi\u00f3k m\u00e1r konfigur\u00e1lva van" }, + "error": { + "resource_error": "Nem siker\u00fclt friss\u00edteni a 'rest' adatokat. K\u00e9rem, ellen\u0151rizze a konfigur\u00e1ci\u00f3t." + }, "step": { + "sensor": { + "data": { + "attribute": "Attrib\u00fatum", + "device_class": "Eszk\u00f6zoszt\u00e1ly", + "index": "Index", + "name": "Elnevez\u00e9s", + "select": "V\u00e1laszt\u00f3", + "state_class": "\u00c1llapotoszt\u00e1ly", + "unit_of_measurement": "M\u00e9rt\u00e9kegys\u00e9g", + "value_template": "\u00c9rt\u00e9ksablon" + }, + "data_description": { + "attribute": "Egy attrib\u00fatum \u00e9rt\u00e9k\u00e9nek lek\u00e9r\u00e9se a kiv\u00e1lasztott tag-en", + "device_class": "Az \u00e9rz\u00e9kel\u0151 t\u00edpusa/oszt\u00e1lya az ikonnak a kezl\u0151fel\u00fcleten val\u00f3 be\u00e1ll\u00edt\u00e1s\u00e1hoz", + "index": "Meghat\u00e1rozza, hogy a CSS-v\u00e1laszt\u00f3 \u00e1ltal visszaadott elemek k\u00f6z\u00fcl melyiket haszn\u00e1lja.", + "select": "Meghat\u00e1rozza, hogy milyen c\u00edmk\u00e9t keressen. N\u00e9zze meg a Beautifulsoup CSS szelektorokat a r\u00e9szletek\u00e9rt", + "state_class": "Az \u00e9rz\u00e9kel\u0151 state_class -ja", + "unit_of_measurement": "V\u00e1lasszon h\u0151m\u00e9rs\u00e9kletm\u00e9r\u00e9st vagy hozzon l\u00e9tre saj\u00e1tot", + "value_template": "Meghat\u00e1roz egy sablont az \u00e9rz\u00e9kel\u0151 \u00e1llapot\u00e1nak lek\u00e9rdez\u00e9s\u00e9re." + } + }, "user": { "data": { - "authentication": "Hiteles\u00edt\u00e9s", + "authentication": "V\u00e1lassza ki a hiteles\u00edt\u00e9si m\u00f3dot", "headers": "Fejl\u00e9cek", + "method": "M\u00f3dszer", "password": "Jelsz\u00f3", "resource": "Er\u0151forr\u00e1s", + "timeout": "Id\u0151t\u00fall\u00e9p\u00e9s", "username": "Felhaszn\u00e1l\u00f3n\u00e9v", "verify_ssl": "SSL-tan\u00fas\u00edtv\u00e1ny ellen\u0151rz\u00e9se" }, @@ -17,6 +43,7 @@ "authentication": "A HTTP-hiteles\u00edt\u00e9s t\u00edpusa. Basic vagy digest", "headers": "A webes k\u00e9r\u00e9shez haszn\u00e1land\u00f3 fejl\u00e9cek", "resource": "Az \u00e9rt\u00e9ket tartalmaz\u00f3 weboldal URL c\u00edme", + "timeout": "A weboldalhoz val\u00f3 csatlakoz\u00e1s id\u0151korl\u00e1tja", "verify_ssl": "Az SSL/TLS tan\u00fas\u00edtv\u00e1ny ellen\u0151rz\u00e9s\u00e9nek enged\u00e9lyez\u00e9se/letilt\u00e1sa, p\u00e9ld\u00e1ul ha saj\u00e1t al\u00e1\u00edr\u00e1s\u00fa." } } @@ -30,12 +57,64 @@ }, "options": { "step": { - "init": { + "add_sensor": { "data": { - "authentication": "Hiteles\u00edt\u00e9s", + "attribute": "Attrib\u00fatum", + "device_class": "Eszk\u00f6zoszt\u00e1ly", + "index": "Index", + "name": "Elnevez\u00e9s", + "select": "V\u00e1laszt\u00f3", + "state_class": "\u00c1llapotoszt\u00e1ly", + "unit_of_measurement": "M\u00e9rt\u00e9kegys\u00e9g", + "value_template": "\u00c9rt\u00e9ksablon" + }, + "data_description": { + "attribute": "Egy attrib\u00fatum \u00e9rt\u00e9k\u00e9nek lek\u00e9r\u00e9se a kiv\u00e1lasztott tag-en", + "device_class": "Az \u00e9rz\u00e9kel\u0151 t\u00edpusa/oszt\u00e1lya az ikonnak a kezl\u0151fel\u00fcleten val\u00f3 be\u00e1ll\u00edt\u00e1s\u00e1hoz", + "index": "Meghat\u00e1rozza, hogy a CSS-v\u00e1laszt\u00f3 \u00e1ltal visszaadott elemek k\u00f6z\u00fcl melyiket haszn\u00e1lja.", + "select": "Meghat\u00e1rozza, hogy milyen c\u00edmk\u00e9t keressen. N\u00e9zze meg a Beautifulsoup CSS szelektorokat a r\u00e9szletek\u00e9rt", + "state_class": "Az \u00e9rz\u00e9kel\u0151 state_class -ja", + "unit_of_measurement": "V\u00e1lasszon h\u0151m\u00e9rs\u00e9kletm\u00e9r\u00e9st vagy hozzon l\u00e9tre saj\u00e1tot", + "value_template": "Meghat\u00e1roz egy sablont az \u00e9rz\u00e9kel\u0151 \u00e1llapot\u00e1nak lek\u00e9rdez\u00e9s\u00e9re." + } + }, + "edit_sensor": { + "data": { + "attribute": "Attrib\u00fatum", + "device_class": "Eszk\u00f6zoszt\u00e1ly", + "index": "Index", + "name": "Elnevez\u00e9s", + "select": "V\u00e1laszt\u00f3", + "state_class": "\u00c1llapotoszt\u00e1ly", + "unit_of_measurement": "M\u00e9rt\u00e9kegys\u00e9g", + "value_template": "\u00c9rt\u00e9ksablon" + }, + "data_description": { + "attribute": "Egy attrib\u00fatum \u00e9rt\u00e9k\u00e9nek lek\u00e9r\u00e9se a kiv\u00e1lasztott tag-en", + "device_class": "Az \u00e9rz\u00e9kel\u0151 t\u00edpusa/oszt\u00e1lya az ikonnak a kezl\u0151fel\u00fcleten val\u00f3 be\u00e1ll\u00edt\u00e1s\u00e1hoz", + "index": "Meghat\u00e1rozza, hogy a CSS-v\u00e1laszt\u00f3 \u00e1ltal visszaadott elemek k\u00f6z\u00fcl melyiket haszn\u00e1lja.", + "select": "Meghat\u00e1rozza, hogy milyen c\u00edmk\u00e9t keressen. N\u00e9zze meg a Beautifulsoup CSS szelektorokat a r\u00e9szletek\u00e9rt", + "state_class": "Az \u00e9rz\u00e9kel\u0151 state_class -ja", + "unit_of_measurement": "V\u00e1lasszon h\u0151m\u00e9rs\u00e9kletm\u00e9r\u00e9st vagy hozzon l\u00e9tre saj\u00e1tot", + "value_template": "Meghat\u00e1roz egy sablont az \u00e9rz\u00e9kel\u0151 \u00e1llapot\u00e1nak lek\u00e9rdez\u00e9s\u00e9re." + } + }, + "init": { + "menu_options": { + "add_sensor": "\u00c9rz\u00e9kel\u0151 hozz\u00e1ad\u00e1sa", + "remove_sensor": "\u00c9rz\u00e9kel\u0151 elt\u00e1vol\u00edt\u00e1sa", + "resource": "Er\u0151forr\u00e1s konfigur\u00e1l\u00e1sa", + "select_edit_sensor": "\u00c9rz\u00e9kel\u0151 konfigur\u00e1l\u00e1sa" + } + }, + "resource": { + "data": { + "authentication": "V\u00e1lassza ki a hiteles\u00edt\u00e9si m\u00f3dot", "headers": "Fejl\u00e9cek", + "method": "M\u00f3dszer", "password": "Jelsz\u00f3", "resource": "Er\u0151forr\u00e1s", + "timeout": "Id\u0151t\u00fall\u00e9p\u00e9s", "username": "Felhaszn\u00e1l\u00f3n\u00e9v", "verify_ssl": "SSL-tan\u00fas\u00edtv\u00e1ny ellen\u0151rz\u00e9se" }, @@ -43,6 +122,7 @@ "authentication": "A HTTP-hiteles\u00edt\u00e9s t\u00edpusa. Basic vagy digest", "headers": "A webes k\u00e9r\u00e9shez haszn\u00e1land\u00f3 fejl\u00e9cek", "resource": "Az \u00e9rt\u00e9ket tartalmaz\u00f3 weboldal URL c\u00edme", + "timeout": "A weboldalhoz val\u00f3 csatlakoz\u00e1s id\u0151korl\u00e1tja", "verify_ssl": "Az SSL/TLS tan\u00fas\u00edtv\u00e1ny ellen\u0151rz\u00e9s\u00e9nek enged\u00e9lyez\u00e9se/letilt\u00e1sa, p\u00e9ld\u00e1ul ha saj\u00e1t al\u00e1\u00edr\u00e1s\u00fa." } } diff --git a/homeassistant/components/scrape/translations/id.json b/homeassistant/components/scrape/translations/id.json index f0b017d651e..6e3a3d7032d 100644 --- a/homeassistant/components/scrape/translations/id.json +++ b/homeassistant/components/scrape/translations/id.json @@ -75,28 +75,33 @@ "value_template": "Mendefinisikan templat untuk mendapatkan status sensor" } }, - "init": { + "edit_sensor": { "data": { - "authentication": "Pilih metode autentikasi", - "headers": "Header", - "method": "Metode", - "password": "Kata Sandi", - "resource": "Sumber Daya", - "timeout": "Tenggang waktu", - "username": "Nama Pengguna", - "verify_ssl": "Verifikasi sertifikat SSL" + "attribute": "Atribut", + "device_class": "Kelas Perangkat", + "index": "Indeks", + "name": "Nama", + "select": "Pilih", + "state_class": "Kelas Status", + "unit_of_measurement": "Satuan Pengukuran", + "value_template": "Nilai Templat" }, "data_description": { - "authentication": "Jenis autentikasi HTTP. Salah satu dari basic atau digest", - "headers": "Header yang digunakan untuk permintaan web", - "resource": "URL ke situs web yang mengandung nilai", - "timeout": "Tenggang waktu untuk koneksi ke situs web", - "verify_ssl": "Mengaktifkan/menonaktifkan verifikasi sertifikat SSL/TLS, misalnya jika sertifikat ditandatangani sendiri" - }, + "attribute": "Dapatkan nilai atribut pada tag yang dipilih", + "device_class": "Jenis/kelas sensor untuk mengatur ikon di antarmuka", + "index": "Menentukan elemen mana yang dikembalikan oleh selektor CSS untuk digunakan", + "select": "Menentukan tag yang harus dicari. Periksa selektor CSS Beautifulsoup untuk melihat detailnya", + "state_class": "Nilai state_class dari sensor", + "unit_of_measurement": "Pilih pengukuran suhu atau buat sendiri", + "value_template": "Mendefinisikan templat untuk mendapatkan status sensor" + } + }, + "init": { "menu_options": { "add_sensor": "Tambahkan sensor", "remove_sensor": "Hapus sensor", - "resource": "Konfigurasikan sumber daya" + "resource": "Konfigurasikan sumber daya", + "select_edit_sensor": "Konfigurasikan Sensor" } }, "resource": { diff --git a/homeassistant/components/scrape/translations/it.json b/homeassistant/components/scrape/translations/it.json index 6367fd70613..39dc9e09d2f 100644 --- a/homeassistant/components/scrape/translations/it.json +++ b/homeassistant/components/scrape/translations/it.json @@ -19,9 +19,9 @@ "value_template": "Modello di valore" }, "data_description": { - "attribute": "Ottenere il valore di un attributo dell'etichetta selezionata", + "attribute": "Ottieni il valore di un attributo sull'etichetta selezionata", "device_class": "Il tipo/classe del sensore per impostare l'icona nel frontend", - "index": "Definisce quale degli elementi restituiti dal selettore CSS utilizzare", + "index": "Definisce quale utilizzare degli elementi restituiti dal selettore CSS ", "select": "Definisce quale etichetta cercare. Controlla i selettori CSS di Beautifulsoup per i dettagli", "state_class": "La state_class del sensore", "unit_of_measurement": "Scegli l'unit\u00e0 di misura della temperatura o crearne una tua", @@ -30,7 +30,7 @@ }, "user": { "data": { - "authentication": "Autenticazione", + "authentication": "Seleziona il metodo di autenticazione", "headers": "Intestazioni", "method": "Metodo", "password": "Password", @@ -57,9 +57,59 @@ }, "options": { "step": { - "init": { + "add_sensor": { "data": { - "authentication": "Autenticazione", + "attribute": "Attributo", + "device_class": "Classe del dispositivo", + "index": "Indice", + "name": "Nome", + "select": "Seleziona", + "state_class": "Classe di stato", + "unit_of_measurement": "Unit\u00e0 di misura", + "value_template": "Modello di valore" + }, + "data_description": { + "attribute": "Ottieni il valore di un attributo sull'etichetta selezionata", + "device_class": "Il tipo/classe del sensore per impostare l'icona nel frontend", + "index": "Definisce quale utilizzare degli elementi restituiti dal selettore CSS ", + "select": "Definisce quale etichetta cercare. Controlla i selettori CSS di Beautifulsoup per i dettagli", + "state_class": "La state_class del sensore", + "unit_of_measurement": "Scegli l'unit\u00e0 di misura della temperatura o crearne una tua", + "value_template": "Definisce un modello per ottenere lo stato del sensore" + } + }, + "edit_sensor": { + "data": { + "attribute": "Attributo", + "device_class": "Classe del dispositivo", + "index": "Indice", + "name": "Nome", + "select": "Seleziona", + "state_class": "Classe di stato", + "unit_of_measurement": "Unit\u00e0 di misura", + "value_template": "Modello di valore" + }, + "data_description": { + "attribute": "Ottieni il valore di un attributo sull'etichetta selezionata", + "device_class": "Il tipo/classe del sensore per impostare l'icona nel frontend", + "index": "Definisce quale utilizzare degli elementi restituiti dal selettore CSS ", + "select": "Definisce quale etichetta cercare. Controlla i selettori CSS di Beautifulsoup per i dettagli", + "state_class": "La state_class del sensore", + "unit_of_measurement": "Scegli l'unit\u00e0 di misura della temperatura o crearne una tua", + "value_template": "Definisce un modello per ottenere lo stato del sensore" + } + }, + "init": { + "menu_options": { + "add_sensor": "Aggiungi sensore", + "remove_sensor": "Rimuovi il sensore", + "resource": "Configura risorsa", + "select_edit_sensor": "Configura sensore" + } + }, + "resource": { + "data": { + "authentication": "Seleziona il metodo di autenticazione", "headers": "Intestazioni", "method": "Metodo", "password": "Password", diff --git a/homeassistant/components/scrape/translations/ja.json b/homeassistant/components/scrape/translations/ja.json index 1caac0ac2ec..1002130c743 100644 --- a/homeassistant/components/scrape/translations/ja.json +++ b/homeassistant/components/scrape/translations/ja.json @@ -21,25 +21,5 @@ } } } - }, - "options": { - "step": { - "init": { - "data": { - "authentication": "\u8a8d\u8a3c", - "headers": "\u30d8\u30c3\u30c0\u30fc", - "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", - "resource": "\u30ea\u30bd\u30fc\u30b9", - "username": "\u30e6\u30fc\u30b6\u30fc\u540d", - "verify_ssl": "SSL\u8a3c\u660e\u66f8\u3092\u78ba\u8a8d\u3059\u308b" - }, - "data_description": { - "authentication": "HTTP\u8a8d\u8a3c\u306e\u7a2e\u985e\u3002\u30d9\u30fc\u30b7\u30c3\u30af\u307e\u305f\u306f\u30c0\u30a4\u30b8\u30a7\u30b9\u30c8\u306e\u3069\u3061\u3089\u304b", - "headers": "Web\u30ea\u30af\u30a8\u30b9\u30c8\u306b\u4f7f\u7528\u3059\u308b\u30d8\u30c3\u30c0\u30fc", - "resource": "\u5024\u3092\u542b\u3080\u30a6\u30a7\u30d6\u30b5\u30a4\u30c8\u306eURL", - "verify_ssl": "SSL/TLS\u8a3c\u660e\u66f8\u306e\u691c\u8a3c\u3092\u6709\u52b9/\u7121\u52b9\u306b\u3057\u307e\u3059\u3002(\u81ea\u5df1\u7f72\u540d\u306e\u5834\u5408\u306a\u3069)" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/scrape/translations/ko.json b/homeassistant/components/scrape/translations/ko.json new file mode 100644 index 00000000000..07db9b763a4 --- /dev/null +++ b/homeassistant/components/scrape/translations/ko.json @@ -0,0 +1,108 @@ +{ + "config": { + "abort": { + "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "resource_error": "rest \ub370\uc774\ud130\ub97c \uc5c5\ub370\uc774\ud2b8\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4. \uad6c\uc131\ub0b4\uc6a9\uc744 \ud655\uc778\ud558\uc138\uc694" + }, + "step": { + "sensor": { + "data": { + "attribute": "\uc18d\uc131", + "device_class": "\uae30\uae30 \ud074\ub798\uc2a4", + "index": "\uc0c9\uc778", + "name": "\uc774\ub984", + "select": "\uc120\ud0dd", + "state_class": "\uc0c1\ud0dc \ud074\ub798\uc2a4", + "unit_of_measurement": "\uce21\uc815 \ub2e8\uc704", + "value_template": "\uac12 \ud15c\ud50c\ub9bf" + }, + "data_description": { + "attribute": "\uc120\ud0dd\ud55c \ud0dc\uadf8\uc758 \uc18d\uc131 \uac12 \uac00\uc838\uc624\uae30", + "device_class": "\ud504\ub860\ud2b8\uc5d4\ub4dc\uc5d0\uc11c \ubcf4\uc5ec\uc9c8 \uc544\uc774\ucf58\uc744 \uc704\ud55c \uc13c\uc11c\uc758 \uc720\ud615/\ud074\ub798\uc2a4", + "index": "CSS \uc120\ud0dd\uae30\uc5d0\uc11c \ubc18\ud658\ub41c \uc694\uc18c \uc911 \uc0ac\uc6a9\ud560 \uc694\uc18c\ub97c \uc815\uc758\ud569\ub2c8\ub2e4.", + "select": "\uac80\uc0c9\ud560 \ud0dc\uadf8\ub97c \uc815\uc758\ud569\ub2c8\ub2e4. \uc790\uc138\ud55c \ub0b4\uc6a9\uc740 Beautifulsoup CSS \uc120\ud0dd\uae30\ub97c \ud655\uc778\ud558\uc2ed\uc2dc\uc624.", + "state_class": "\uc13c\uc11c\uc758 state_class", + "unit_of_measurement": "\uc628\ub3c4 \uce21\uc815\uac12\uc744 \uc120\ud0dd\ud558\uac70\ub098 \uc9c1\uc811 \uc0dd\uc131" + } + }, + "user": { + "data": { + "method": "\uba54\uc11c\ub4dc", + "password": "\ube44\ubc00\ubc88\ud638", + "timeout": "\ud0c0\uc784\uc544\uc6c3", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984", + "verify_ssl": "SSL \uc778\uc99d\uc11c \ud655\uc778" + }, + "data_description": { + "timeout": "\uc6f9 \uc0ac\uc774\ud2b8 \uc5f0\uacb0 \uc2dc\uac04 \ucd08\uacfc" + } + } + } + }, + "options": { + "step": { + "add_sensor": { + "data": { + "attribute": "\uc18d\uc131", + "device_class": "\uae30\uae30 \ud074\ub798\uc2a4", + "index": "\uc0c9\uc778", + "name": "\uc774\ub984", + "select": "\uc120\ud0dd", + "state_class": "\uc0c1\ud0dc \ud074\ub798\uc2a4", + "unit_of_measurement": "\uce21\uc815 \ub2e8\uc704", + "value_template": "\uac12 \ud15c\ud50c\ub9bf" + }, + "data_description": { + "attribute": "\uc120\ud0dd\ud55c \ud0dc\uadf8\uc758 \uc18d\uc131 \uac12 \uac00\uc838\uc624\uae30", + "device_class": "\ud504\ub860\ud2b8\uc5d4\ub4dc\uc5d0\uc11c \ubcf4\uc5ec\uc9c8 \uc544\uc774\ucf58\uc744 \uc704\ud55c \uc13c\uc11c\uc758 \uc720\ud615/\ud074\ub798\uc2a4", + "index": "CSS \uc120\ud0dd\uae30\uc5d0\uc11c \ubc18\ud658\ub41c \uc694\uc18c \uc911 \uc0ac\uc6a9\ud560 \uc694\uc18c\ub97c \uc815\uc758\ud569\ub2c8\ub2e4.", + "select": "\uac80\uc0c9\ud560 \ud0dc\uadf8\ub97c \uc815\uc758\ud569\ub2c8\ub2e4. \uc790\uc138\ud55c \ub0b4\uc6a9\uc740 Beautifulsoup CSS \uc120\ud0dd\uae30\ub97c \ud655\uc778\ud558\uc2ed\uc2dc\uc624.", + "state_class": "\uc13c\uc11c\uc758 state_class", + "unit_of_measurement": "\uc628\ub3c4 \uce21\uc815\uac12\uc744 \uc120\ud0dd\ud558\uac70\ub098 \uc9c1\uc811 \uc0dd\uc131" + } + }, + "edit_sensor": { + "data": { + "attribute": "\uc18d\uc131", + "device_class": "\uae30\uae30 \ud074\ub798\uc2a4", + "index": "\uc0c9\uc778", + "name": "\uc774\ub984", + "select": "\uc120\ud0dd", + "state_class": "\uc0c1\ud0dc \ud074\ub798\uc2a4", + "unit_of_measurement": "\uce21\uc815 \ub2e8\uc704", + "value_template": "\uac12 \ud15c\ud50c\ub9bf" + }, + "data_description": { + "attribute": "\uc120\ud0dd\ud55c \ud0dc\uadf8\uc758 \uc18d\uc131 \uac12 \uac00\uc838\uc624\uae30", + "device_class": "\ud504\ub860\ud2b8\uc5d4\ub4dc\uc5d0\uc11c \ubcf4\uc5ec\uc9c8 \uc544\uc774\ucf58\uc744 \uc704\ud55c \uc13c\uc11c\uc758 \uc720\ud615/\ud074\ub798\uc2a4", + "index": "CSS \uc120\ud0dd\uae30\uc5d0\uc11c \ubc18\ud658\ub41c \uc694\uc18c \uc911 \uc0ac\uc6a9\ud560 \uc694\uc18c\ub97c \uc815\uc758\ud569\ub2c8\ub2e4.", + "select": "\uac80\uc0c9\ud560 \ud0dc\uadf8\ub97c \uc815\uc758\ud569\ub2c8\ub2e4. \uc790\uc138\ud55c \ub0b4\uc6a9\uc740 Beautifulsoup CSS \uc120\ud0dd\uae30\ub97c \ud655\uc778\ud558\uc2ed\uc2dc\uc624.", + "state_class": "\uc13c\uc11c\uc758 state_class", + "unit_of_measurement": "\uc628\ub3c4 \uce21\uc815\uac12\uc744 \uc120\ud0dd\ud558\uac70\ub098 \uc9c1\uc811 \uc0dd\uc131" + } + }, + "init": { + "menu_options": { + "add_sensor": "\uc13c\uc11c \ucd94\uac00", + "remove_sensor": "\uc13c\uc11c \uc81c\uac70", + "resource": "\ub9ac\uc18c\uc2a4 \uad6c\uc131", + "select_edit_sensor": "\uc13c\uc11c \uad6c\uc131\ud558\uae30" + } + }, + "resource": { + "data": { + "method": "\uba54\uc11c\ub4dc", + "password": "\ube44\ubc00\ubc88\ud638", + "timeout": "\ud0c0\uc784\uc544\uc6c3", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984", + "verify_ssl": "SSL \uc778\uc99d\uc11c \ud655\uc778" + }, + "data_description": { + "timeout": "\uc6f9 \uc0ac\uc774\ud2b8 \uc5f0\uacb0 \uc2dc\uac04 \ucd08\uacfc" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/scrape/translations/ml.json b/homeassistant/components/scrape/translations/ml.json new file mode 100644 index 00000000000..23b5cd46a57 --- /dev/null +++ b/homeassistant/components/scrape/translations/ml.json @@ -0,0 +1,11 @@ +{ + "options": { + "step": { + "init": { + "menu_options": { + "select_edit_sensor": "Configure sensor" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/scrape/translations/nl.json b/homeassistant/components/scrape/translations/nl.json index dba74863b52..78c43519015 100644 --- a/homeassistant/components/scrape/translations/nl.json +++ b/homeassistant/components/scrape/translations/nl.json @@ -51,16 +51,47 @@ }, "options": { "step": { + "add_sensor": { + "data": { + "attribute": "Attribuut", + "device_class": "Apparaatklasse (device_class)", + "index": "Index" + } + }, + "edit_sensor": { + "data": { + "attribute": "Attribuut", + "device_class": "Apparaatklasse (device_class)", + "index": "Index", + "select": "Selecteer", + "state_class": "Statusklasse (state_class)", + "unit_of_measurement": "Eenheid", + "value_template": "Waardesjabloon (template)" + }, + "data_description": { + "attribute": "Bepaal de waarde van een attribuut op van de geselecteerde tag", + "device_class": "Type/klasse van de sensor voor het icoon in de frontend", + "index": "Bepaalt welke van de elementen worden teruggegeven door de CSS selector om te gebruiken", + "select": "Bepaalt de tag om naar te zoeken. Zie Beautifulsoup CSS selectors voor meer details", + "state_class": "De state_class van de sensor", + "unit_of_measurement": "Kies een temperatuurmeting of cre\u00eber er zelf een", + "value_template": "Definieert een template om de status van de sensor te bepalen" + } + }, "init": { + "menu_options": { + "add_sensor": "Sensor toevoegen", + "remove_sensor": "Sensor verwijderen", + "select_edit_sensor": "Sensor configureren" + } + }, + "resource": { "data": { "authentication": "Authenticatie", "headers": "Headers", "method": "Methode", - "password": "Wachtwoord", "resource": "Bron", - "timeout": "Wachttijd verstreken", - "username": "Gebruikersnaam", - "verify_ssl": "SSL-certificaat verifi\u00ebren" + "timeout": "Wachttijd verstreken" }, "data_description": { "authentication": "Type van de HTTP-authenticatie. Ofwel basic of digest", diff --git a/homeassistant/components/scrape/translations/no.json b/homeassistant/components/scrape/translations/no.json index 0050ead5208..230c91ab9d4 100644 --- a/homeassistant/components/scrape/translations/no.json +++ b/homeassistant/components/scrape/translations/no.json @@ -78,28 +78,33 @@ "value_template": "Definerer en mal for \u00e5 f\u00e5 tilstanden til sensoren" } }, - "init": { + "edit_sensor": { "data": { - "authentication": "Velg autentiseringsmetode", - "headers": "Overskrifter", - "method": "Metode", - "password": "Passord", - "resource": "Ressurs", - "timeout": "Tidsavbrudd", - "username": "Brukernavn", - "verify_ssl": "Verifisere SSL-sertifikat" + "attribute": "Attributt", + "device_class": "Enhetsklasse", + "index": "Indeks", + "name": "Navn", + "select": "Velg", + "state_class": "Statsklasse", + "unit_of_measurement": "M\u00e5leenhet", + "value_template": "Verdimal" }, "data_description": { - "authentication": "Type HTTP-godkjenning. Enten grunnleggende eller ufullstendig", - "headers": "Overskrifter som skal brukes for nettforesp\u00f8rselen", - "resource": "URL-en til nettstedet som inneholder verdien", - "timeout": "Tidsavbrudd for tilkobling til nettside", - "verify_ssl": "Aktiverer/deaktiverer verifisering av SSL/TLS-sertifikat, for eksempel hvis det er selvsignert" - }, + "attribute": "F\u00e5 verdien av et attributt p\u00e5 den valgte taggen", + "device_class": "Type/klasse av sensoren for \u00e5 angi ikonet i frontend", + "index": "Definerer hvilke av elementene som returneres av CSS-velgeren som skal brukes", + "select": "Definerer hvilken tag som skal s\u00f8kes etter. Sjekk Beautifulsoup CSS-velgere for detaljer", + "state_class": "Sensorens state_class", + "unit_of_measurement": "Velg temperaturm\u00e5ling eller lag din egen", + "value_template": "Definerer en mal for \u00e5 f\u00e5 tilstanden til sensoren" + } + }, + "init": { "menu_options": { "add_sensor": "Legg til sensor", "remove_sensor": "Fjern sensoren", - "resource": "Konfigurer ressurs" + "resource": "Konfigurer ressurs", + "select_edit_sensor": "Konfigurer sensor" } }, "resource": { diff --git a/homeassistant/components/scrape/translations/pl.json b/homeassistant/components/scrape/translations/pl.json index 8e05d2e9474..46de7003777 100644 --- a/homeassistant/components/scrape/translations/pl.json +++ b/homeassistant/components/scrape/translations/pl.json @@ -57,16 +57,64 @@ }, "options": { "step": { + "add_sensor": { + "data": { + "attribute": "Atrybut", + "device_class": "Klasa urz\u0105dzenia", + "index": "Indeks", + "name": "Nazwa", + "select": "Wybierz", + "state_class": "Klasa stanu", + "unit_of_measurement": "Jednostka miary", + "value_template": "Szablon warto\u015bci" + }, + "data_description": { + "attribute": "Pobierz warto\u015b\u0107 atrybutu w wybranym tagu", + "device_class": "Typ/klasa sensora do ustawienia ikony w interfejsie u\u017cytkownika", + "index": "Okre\u015bla, kt\u00f3rego z element\u00f3w zwracanych przez selektor CSS nale\u017cy u\u017cy\u0107", + "select": "Okre\u015bla jakiego taga szuka\u0107. Sprawd\u017a selektory CSS Beautifulsoup, aby uzyska\u0107 szczeg\u00f3\u0142owe informacje.", + "state_class": "state_class sensora", + "unit_of_measurement": "Wybierz pomiar temperatury lub stw\u00f3rz w\u0142asny", + "value_template": "Szablon, kt\u00f3ry pozwala uzyska\u0107 stan czujnika" + } + }, + "edit_sensor": { + "data": { + "attribute": "Atrybut", + "device_class": "Klasa urz\u0105dzenia", + "index": "Indeks", + "name": "Nazwa", + "select": "Wybierz", + "state_class": "Klasa stanu", + "unit_of_measurement": "Jednostka miary", + "value_template": "Szablon warto\u015bci" + }, + "data_description": { + "attribute": "Pobierz warto\u015b\u0107 atrybutu w wybranym tagu", + "device_class": "Typ/klasa sensora do ustawienia ikony w interfejsie u\u017cytkownika", + "index": "Okre\u015bla, kt\u00f3rego z element\u00f3w zwracanych przez selektor CSS nale\u017cy u\u017cy\u0107", + "select": "Okre\u015bla jakiego taga szuka\u0107. Sprawd\u017a selektory CSS Beautifulsoup, aby uzyska\u0107 szczeg\u00f3\u0142owe informacje.", + "state_class": "state_class sensora", + "unit_of_measurement": "Wybierz pomiar temperatury lub stw\u00f3rz w\u0142asny", + "value_template": "Szablon, kt\u00f3ry pozwala uzyska\u0107 stan czujnika" + } + }, "init": { + "menu_options": { + "add_sensor": "Dodaj sensor", + "remove_sensor": "Usu\u0144 sensor", + "resource": "Konfiguracja zasobu", + "select_edit_sensor": "Konfiguracja sensora" + } + }, + "resource": { "data": { "authentication": "Wybierz metod\u0119 uwierzytelniania", "headers": "Nag\u0142\u00f3wki", "method": "Metoda", "password": "Has\u0142o", "resource": "Zas\u00f3b", - "timeout": "Limit czasu", - "username": "Nazwa u\u017cytkownika", - "verify_ssl": "Weryfikacja certyfikatu SSL" + "timeout": "Limit czasu" }, "data_description": { "authentication": "Typ uwierzytelniania HTTP. Podstawowy lub digest.", diff --git a/homeassistant/components/scrape/translations/pt-BR.json b/homeassistant/components/scrape/translations/pt-BR.json index 729aa589102..65d33aef529 100644 --- a/homeassistant/components/scrape/translations/pt-BR.json +++ b/homeassistant/components/scrape/translations/pt-BR.json @@ -60,10 +60,32 @@ "add_sensor": { "data": { "attribute": "Atributo", - "device_class": "Classe de dispositivo", + "device_class": "Classe do dispositivo", "index": "\u00cdndice", + "name": "Nome", "select": "Selecione", - "state_class": "Classe do estado", + "state_class": "Classe do Estado", + "unit_of_measurement": "Unidade de medida", + "value_template": "Modelo de valor" + }, + "data_description": { + "attribute": "Obtenha o valor de um atributo na tag selecionada", + "device_class": "O tipo/classe do sensor para definir o \u00edcone no frontend", + "index": "Define qual dos elementos retornados pelo seletor CSS usar", + "select": "Define qual tag procurar. Verifique os seletores CSS do Beautifulsoup para obter detalhes", + "state_class": "O estado_classe do sensor", + "unit_of_measurement": "Escolha a medi\u00e7\u00e3o de temperatura ou crie a sua pr\u00f3pria", + "value_template": "Define um modelo para obter o estado do sensor" + } + }, + "edit_sensor": { + "data": { + "attribute": "Atributo", + "device_class": "Classe do dispositivo", + "index": "\u00cdndice", + "name": "Nome", + "select": "Selecione", + "state_class": "Classe do Estado", "unit_of_measurement": "Unidade de medida", "value_template": "Modelo de valor" }, @@ -78,6 +100,14 @@ } }, "init": { + "menu_options": { + "add_sensor": "Adicionar sensor", + "remove_sensor": "Remover sensor", + "resource": "Configurar recurso", + "select_edit_sensor": "Configurar sensor" + } + }, + "resource": { "data": { "authentication": "Selecione o m\u00e9todo de autentica\u00e7\u00e3o", "headers": "Cabe\u00e7alhos", @@ -85,7 +115,7 @@ "password": "Senha", "resource": "Recurso", "timeout": "Tempo limite", - "username": "Usu\u00e1rio", + "username": "Nome de usu\u00e1rio", "verify_ssl": "Verificar SSL" }, "data_description": { @@ -94,27 +124,6 @@ "resource": "A URL para o site que cont\u00e9m o valor", "timeout": "Tempo limite para conex\u00e3o com o site", "verify_ssl": "Ativa/desativa a verifica\u00e7\u00e3o do certificado SSL/TLS, por exemplo, se for autoassinado" - }, - "menu_options": { - "add_sensor": "Adicionar sensor", - "remove_sensor": "Remover sensor", - "resource": "Configurar recurso" - } - }, - "resource": { - "data": { - "authentication": "Selecione o m\u00e9todo de autentica\u00e7\u00e3o", - "headers": "Cabe\u00e7alhos", - "method": "M\u00e9todo", - "resource": "Recurso", - "timeout": "Tempo esgotado" - }, - "data_description": { - "authentication": "Tipo da autentica\u00e7\u00e3o HTTP. Seja b\u00e1sico ou resumido", - "headers": "Cabe\u00e7alhos a serem usados para a solicita\u00e7\u00e3o da web", - "resource": "A URL para o site que cont\u00e9m o valor", - "timeout": "Tempo esgotado para conex\u00e3o com o site", - "verify_ssl": "Ativa/desativa a verifica\u00e7\u00e3o do certificado SSL/TLS, por exemplo, se for autoassinado" } } } diff --git a/homeassistant/components/scrape/translations/pt.json b/homeassistant/components/scrape/translations/pt.json index 3ad22ead218..076c2ffb2a8 100644 --- a/homeassistant/components/scrape/translations/pt.json +++ b/homeassistant/components/scrape/translations/pt.json @@ -2,6 +2,13 @@ "config": { "abort": { "already_configured": "Conta j\u00e1 configurada" + }, + "step": { + "sensor": { + "data": { + "name": "Nome" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/scrape/translations/ru.json b/homeassistant/components/scrape/translations/ru.json index e212e487f98..0b9860e1810 100644 --- a/homeassistant/components/scrape/translations/ru.json +++ b/homeassistant/components/scrape/translations/ru.json @@ -72,28 +72,33 @@ "value_template": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u0448\u0430\u0431\u043b\u043e\u043d \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0434\u0430\u0442\u0447\u0438\u043a\u0430." } }, - "init": { + "edit_sensor": { "data": { - "authentication": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u043f\u043e\u0441\u043e\u0431 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438", - "headers": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438", - "method": "\u041c\u0435\u0442\u043e\u0434", - "password": "\u041f\u0430\u0440\u043e\u043b\u044c", - "resource": "\u0420\u0435\u0441\u0443\u0440\u0441", - "timeout": "\u0422\u0430\u0439\u043c-\u0430\u0443\u0442", - "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f", - "verify_ssl": "\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 SSL" + "attribute": "\u0410\u0442\u0440\u0438\u0431\u0443\u0442", + "device_class": "\u041a\u043b\u0430\u0441\u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430", + "index": "\u0418\u043d\u0434\u0435\u043a\u0441", + "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", + "select": "\u0412\u044b\u0431\u0440\u0430\u0442\u044c", + "state_class": "\u041a\u043b\u0430\u0441\u0441 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f", + "unit_of_measurement": "\u0415\u0434\u0438\u043d\u0438\u0446\u0430 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f", + "value_template": "\u0428\u0430\u0431\u043b\u043e\u043d \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f" }, "data_description": { - "authentication": "\u0422\u0438\u043f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 HTTP: basic \u0438\u043b\u0438 digest.", - "headers": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0435 \u0434\u043b\u044f \u0432\u0435\u0431-\u0437\u0430\u043f\u0440\u043e\u0441\u0430.", - "resource": "URL-\u0430\u0434\u0440\u0435\u0441 \u0432\u0435\u0431-\u0441\u0430\u0439\u0442\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435.", - "timeout": "\u0422\u0430\u0439\u043c-\u0430\u0443\u0442 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u0441\u0430\u0439\u0442\u0443", - "verify_ssl": "\u0412\u043a\u043b\u044e\u0447\u0430\u0435\u0442/\u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u0430 SSL/TLS. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u044d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u0438\u0433\u043e\u0434\u0438\u0442\u044c\u0441\u044f \u0435\u0441\u043b\u0438 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 \u0441\u0430\u043c\u043e\u043f\u043e\u0434\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0439." - }, + "attribute": "\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u0430 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0433\u043e \u0442\u0435\u0433\u0430.", + "device_class": "\u0422\u0438\u043f/\u043a\u043b\u0430\u0441\u0441 \u0441\u0435\u043d\u0441\u043e\u0440\u0430 \u0434\u043b\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0432 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435.", + "index": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442, \u043a\u0430\u043a\u043e\u0439 \u0438\u0437 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u044b\u0445 \u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u043e\u043c CSS \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c.", + "select": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442, \u043a\u0430\u043a\u043e\u0439 \u0442\u0435\u0433 \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0438\u0441\u043a\u0430\u0442\u044c. \u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u0432 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0438 \u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u043e\u0432 CSS Beautifulsoup.", + "state_class": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 state_class \u0434\u043b\u044f \u0441\u0435\u043d\u0441\u043e\u0440\u0430.", + "unit_of_measurement": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u0435 \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u044b \u0438\u043b\u0438 \u0441\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u0441\u0432\u043e\u0435 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0435", + "value_template": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u0448\u0430\u0431\u043b\u043e\u043d \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0434\u0430\u0442\u0447\u0438\u043a\u0430." + } + }, + "init": { "menu_options": { "add_sensor": "\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u0435\u043d\u0441\u043e\u0440", "remove_sensor": "\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0441\u0435\u043d\u0441\u043e\u0440", - "resource": "\u041d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0440\u0435\u0441\u0443\u0440\u0441" + "resource": "\u041d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0440\u0435\u0441\u0443\u0440\u0441", + "select_edit_sensor": "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0441\u0435\u043d\u0441\u043e\u0440\u0430" } }, "resource": { diff --git a/homeassistant/components/scrape/translations/sk.json b/homeassistant/components/scrape/translations/sk.json index f29805634e1..9c55e4a357a 100644 --- a/homeassistant/components/scrape/translations/sk.json +++ b/homeassistant/components/scrape/translations/sk.json @@ -10,37 +10,120 @@ "sensor": { "data": { "attribute": "Atrib\u00fat", + "device_class": "Trieda zariadenia", + "index": "Index", "name": "N\u00e1zov", "select": "Vyberte", + "state_class": "Triada stavu", "unit_of_measurement": "Jednotka merania", "value_template": "\u0160abl\u00f3na hodnoty" }, "data_description": { - "unit_of_measurement": "Vyberte si meranie teploty alebo si vytvorte vlastn\u00e9" + "attribute": "Z\u00edskajte hodnotu atrib\u00fatu na vybranom tagu", + "device_class": "Typ/trieda sn\u00edma\u010da na nastavenie ikony na frontende", + "index": "Definuje, ktor\u00e9 z prvkov vr\u00e1ten\u00fdch selektorom CSS sa maj\u00fa pou\u017ei\u0165", + "select": "Definuje, ktor\u00e1 zna\u010dka sa m\u00e1 h\u013eada\u0165. Podrobnosti n\u00e1jdete v selektoroch CSS Beautifulsoup", + "state_class": "State_class sn\u00edma\u010da", + "unit_of_measurement": "Vyberte si meranie teploty alebo si vytvorte vlastn\u00e9", + "value_template": "Definuje \u0161abl\u00f3nu na z\u00edskanie stavu senzora" } }, "user": { "data": { + "authentication": "Vyberte met\u00f3du overenia", + "headers": "Hlavi\u010dky", + "method": "Met\u00f3da", "password": "Heslo", + "resource": "Zdroj", + "timeout": "\u010casov\u00fd limit", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno", "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" }, "data_description": { - "timeout": "\u010casov\u00fd limit pre pripojenie k webovej str\u00e1nke" + "authentication": "Typ overovania HTTP. Bu\u010f z\u00e1kladn\u00e9, alebo digest", + "headers": "Hlavi\u010dky, ktor\u00e9 sa maj\u00fa pou\u017ei\u0165 pre webov\u00fa po\u017eiadavku", + "resource": "Adresa URL webovej lokality, ktor\u00e1 obsahuje hodnotu", + "timeout": "\u010casov\u00fd limit pre pripojenie k webovej str\u00e1nke", + "verify_ssl": "Povol\u00ed/zak\u00e1\u017ee overenie SSL/TLS certifik\u00e1tu, napr\u00edklad ak je podp\u00edsan\u00fd s\u00e1m sebou" } } } }, + "issues": { + "moved_yaml": { + "description": "Konfigur\u00e1cia Scrape pomocou YAML bola presunut\u00e1 do integra\u010dn\u00e9ho k\u013e\u00fa\u010da. \n\n Va\u0161a existuj\u00faca konfigur\u00e1cia YAML bude fungova\u0165 pre \u010fal\u0161ie 2 verzie. \n\n Migrujte svoju konfigur\u00e1ciu YAML na integra\u010dn\u00fd k\u013e\u00fa\u010d pod\u013ea dokument\u00e1cie.", + "title": "Konfigur\u00e1cia Scrape YAML bola presunut\u00e1" + } + }, "options": { "step": { - "init": { + "add_sensor": { "data": { + "attribute": "Atrib\u00fat", + "device_class": "Trieda zariadenia", + "index": "Index", + "name": "N\u00e1zov", + "select": "Vyberte", + "state_class": "Triada stavu", + "unit_of_measurement": "Jednotka merania", + "value_template": "\u0160abl\u00f3na hodnoty" + }, + "data_description": { + "attribute": "Z\u00edskajte hodnotu atrib\u00fatu na vybranom tagu", + "device_class": "Typ/trieda sn\u00edma\u010da na nastavenie ikony na frontende", + "index": "Definuje, ktor\u00e9 z prvkov vr\u00e1ten\u00fdch selektorom CSS sa maj\u00fa pou\u017ei\u0165", + "select": "Definuje, ktor\u00e1 zna\u010dka sa m\u00e1 h\u013eada\u0165. Podrobnosti n\u00e1jdete v selektoroch CSS Beautifulsoup", + "state_class": "State_class sn\u00edma\u010da", + "unit_of_measurement": "Vyberte si meranie teploty alebo si vytvorte vlastn\u00e9", + "value_template": "Definuje \u0161abl\u00f3nu na z\u00edskanie stavu senzora" + } + }, + "edit_sensor": { + "data": { + "attribute": "Atrib\u00fat", + "device_class": "Trieda zariadenia", + "index": "Index", + "name": "N\u00e1zov", + "select": "Vyberte", + "state_class": "Triada stavu", + "unit_of_measurement": "Jednotka merania", + "value_template": "\u0160abl\u00f3na hodnoty" + }, + "data_description": { + "attribute": "Z\u00edskajte hodnotu atrib\u00fatu na vybranom tagu", + "device_class": "Typ/trieda sn\u00edma\u010da na nastavenie ikony na frontende", + "index": "Definuje, ktor\u00e9 z prvkov vr\u00e1ten\u00fdch selektorom CSS sa maj\u00fa pou\u017ei\u0165", + "select": "Definuje, ktor\u00e1 zna\u010dka sa m\u00e1 h\u013eada\u0165. Podrobnosti n\u00e1jdete v selektoroch CSS Beautifulsoup", + "state_class": "State_class sn\u00edma\u010da", + "unit_of_measurement": "Vyberte si meranie teploty alebo si vytvorte vlastn\u00e9", + "value_template": "Definuje \u0161abl\u00f3nu na z\u00edskanie stavu senzora" + } + }, + "init": { + "menu_options": { + "add_sensor": "Pridanie sn\u00edma\u010da", + "remove_sensor": "Odstr\u00e1nenie sn\u00edma\u010da", + "resource": "Nakonfigurujte zdroj", + "select_edit_sensor": "Konfigur\u00e1cia sn\u00edma\u010da" + } + }, + "resource": { + "data": { + "authentication": "Vyberte met\u00f3du overenia", + "headers": "Hlavi\u010dky", + "method": "Met\u00f3da", "password": "Heslo", + "resource": "Zdroj", + "timeout": "\u010casov\u00fd limit", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno", "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" }, "data_description": { - "timeout": "\u010casov\u00fd limit pre pripojenie k webovej str\u00e1nke" + "authentication": "Typ overovania HTTP. Bu\u010f z\u00e1kladn\u00e9, alebo digest", + "headers": "Hlavi\u010dky, ktor\u00e9 sa maj\u00fa pou\u017ei\u0165 pre webov\u00fa po\u017eiadavku", + "resource": "Adresa URL webovej lokality, ktor\u00e1 obsahuje hodnotu", + "timeout": "\u010casov\u00fd limit pre pripojenie k webovej str\u00e1nke", + "verify_ssl": "Povol\u00ed/zak\u00e1\u017ee overenie SSL/TLS certifik\u00e1tu, napr\u00edklad ak je podp\u00edsan\u00fd s\u00e1m sebou" } } } diff --git a/homeassistant/components/scrape/translations/sv.json b/homeassistant/components/scrape/translations/sv.json index 96ecf2a934e..631dff9b4c3 100644 --- a/homeassistant/components/scrape/translations/sv.json +++ b/homeassistant/components/scrape/translations/sv.json @@ -21,25 +21,5 @@ } } } - }, - "options": { - "step": { - "init": { - "data": { - "authentication": "Autentisering", - "headers": "Headers", - "password": "L\u00f6senord", - "resource": "Resurs", - "username": "Anv\u00e4ndarnamn", - "verify_ssl": "Verifiera SSL-certifikat" - }, - "data_description": { - "authentication": "Typ av HTTP-autentisering. Antingen basic eller digest", - "headers": "Rubriker att anv\u00e4nda f\u00f6r webbf\u00f6rfr\u00e5gan", - "resource": "Webbadressen till webbplatsen som inneh\u00e5ller v\u00e4rdet", - "verify_ssl": "Aktiverar/inaktiverar verifiering av SSL/TLS-certifikat, till exempel om det \u00e4r sj\u00e4lvsignerat" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/scrape/translations/tr.json b/homeassistant/components/scrape/translations/tr.json index 1bc92367580..dec08746179 100644 --- a/homeassistant/components/scrape/translations/tr.json +++ b/homeassistant/components/scrape/translations/tr.json @@ -21,25 +21,5 @@ } } } - }, - "options": { - "step": { - "init": { - "data": { - "authentication": "Kimlik do\u011frulama", - "headers": "Ba\u015fl\u0131klar", - "password": "Parola", - "resource": "Kaynak", - "username": "Kullan\u0131c\u0131 Ad\u0131", - "verify_ssl": "SSL sertifikas\u0131n\u0131 do\u011frulay\u0131n" - }, - "data_description": { - "authentication": "HTTP kimlik do\u011frulamas\u0131n\u0131n t\u00fcr\u00fc. Temel veya basit", - "headers": "Web iste\u011fi i\u00e7in kullan\u0131lacak ba\u015fl\u0131klar", - "resource": "De\u011feri i\u00e7eren web sitesinin URL'si", - "verify_ssl": "\u00d6rne\u011fin, kendinden imzal\u0131ysa, SSL/TLS sertifikas\u0131n\u0131n do\u011frulanmas\u0131n\u0131 etkinle\u015ftirir/devre d\u0131\u015f\u0131 b\u0131rak\u0131r" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/scrape/translations/zh-Hans.json b/homeassistant/components/scrape/translations/zh-Hans.json index c178e103c2e..87ebecd81cc 100644 --- a/homeassistant/components/scrape/translations/zh-Hans.json +++ b/homeassistant/components/scrape/translations/zh-Hans.json @@ -22,18 +22,5 @@ } } } - }, - "options": { - "step": { - "init": { - "data": { - "method": "\u8bf7\u6c42\u65b9\u5f0f", - "timeout": "\u8d85\u65f6" - }, - "data_description": { - "timeout": "\u8fde\u63a5\u5230\u7f51\u7ad9\u8d85\u65f6" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/scrape/translations/zh-Hant.json b/homeassistant/components/scrape/translations/zh-Hant.json index 9c101ee5df2..9b13c79cb78 100644 --- a/homeassistant/components/scrape/translations/zh-Hant.json +++ b/homeassistant/components/scrape/translations/zh-Hant.json @@ -20,7 +20,7 @@ }, "data_description": { "attribute": "\u7372\u53d6\u6240\u9078\u6a19\u7c64\u5c6c\u6027\u6578\u503c", - "device_class": "\u65bc Frontend \u4e2d\u8a2d\u5b9a\u4e4b\u50b3\u611f\u5668\u985e\u578b/\u985e\u5225\u5716\u793a", + "device_class": "\u65bc Frontend \u4e2d\u8a2d\u5b9a\u4e4b\u611f\u6e2c\u5668\u985e\u578b/\u985e\u5225\u5716\u793a", "index": "\u5b9a\u7fa9\u4f7f\u7528 CSS selector \u56de\u8986\u5143\u7d20", "select": "\u5b9a\u7fa9\u8981\u7d22\u7684\u6a19\u7c64\u3002\u53c3\u95b1 Beautifulsoup CSS selector \u4ee5\u7372\u5f97\u8a73\u7d30\u8cc7\u8a0a", "state_class": "\u611f\u6e2c\u5668 state_class", @@ -70,7 +70,28 @@ }, "data_description": { "attribute": "\u7372\u53d6\u6240\u9078\u6a19\u7c64\u5c6c\u6027\u6578\u503c", - "device_class": "\u65bc Frontend \u4e2d\u8a2d\u5b9a\u4e4b\u50b3\u611f\u5668\u985e\u578b/\u985e\u5225\u5716\u793a", + "device_class": "\u65bc Frontend \u4e2d\u8a2d\u5b9a\u4e4b\u611f\u6e2c\u5668\u985e\u578b/\u985e\u5225\u5716\u793a", + "index": "\u5b9a\u7fa9\u4f7f\u7528 CSS selector \u56de\u8986\u5143\u7d20", + "select": "\u5b9a\u7fa9\u8981\u7d22\u7684\u6a19\u7c64\u3002\u53c3\u95b1 Beautifulsoup CSS selector \u4ee5\u7372\u5f97\u8a73\u7d30\u8cc7\u8a0a", + "state_class": "\u611f\u6e2c\u5668 state_class", + "unit_of_measurement": "\u9078\u64c7\u6eab\u5ea6\u6e2c\u91cf\u6216\u8005\u5275\u5efa\u81ea\u5b9a\u7fa9", + "value_template": "\u5b9a\u7fa9\u6a21\u677f\u4ee5\u53d6\u5f97\u611f\u6e2c\u5668\u72c0\u614b" + } + }, + "edit_sensor": { + "data": { + "attribute": "\u5c6c\u6027", + "device_class": "\u88dd\u7f6e\u985e\u5225", + "index": "\u6307\u6578", + "name": "\u540d\u7a31", + "select": "\u9078\u64c7", + "state_class": "\u72c0\u614b\u985e\u5225", + "unit_of_measurement": "\u6e2c\u91cf\u55ae\u4f4d", + "value_template": "\u6578\u503c\u6a21\u677f" + }, + "data_description": { + "attribute": "\u7372\u53d6\u6240\u9078\u6a19\u7c64\u5c6c\u6027\u6578\u503c", + "device_class": "\u65bc Frontend \u4e2d\u8a2d\u5b9a\u4e4b\u611f\u6e2c\u5668\u985e\u578b/\u985e\u5225\u5716\u793a", "index": "\u5b9a\u7fa9\u4f7f\u7528 CSS selector \u56de\u8986\u5143\u7d20", "select": "\u5b9a\u7fa9\u8981\u7d22\u7684\u6a19\u7c64\u3002\u53c3\u95b1 Beautifulsoup CSS selector \u4ee5\u7372\u5f97\u8a73\u7d30\u8cc7\u8a0a", "state_class": "\u611f\u6e2c\u5668 state_class", @@ -79,27 +100,11 @@ } }, "init": { - "data": { - "authentication": "\u9078\u64c7\u9a57\u8b49\u6a21\u5f0f", - "headers": "Headers", - "method": "\u65b9\u5f0f", - "password": "\u5bc6\u78bc", - "resource": "\u4f86\u6e90", - "timeout": "\u903e\u6642", - "username": "\u4f7f\u7528\u8005\u540d\u7a31", - "verify_ssl": "\u78ba\u8a8d SSL \u8a8d\u8b49" - }, - "data_description": { - "authentication": "HTTP \u9a57\u8b49\u985e\u578b\u3002\u57fa\u672c\u6216\u6458\u8981", - "headers": "\u7528\u65bc Web \u8acb\u6c42\u4e4b Headers", - "resource": "\u5305\u542b\u6578\u503c\u7684\u7db2\u7ad9 URL", - "timeout": "\u7db2\u7ad9\u9023\u7dda\u903e\u6642", - "verify_ssl": "\u958b\u555f/\u95dc\u9589 SSL/TLS \u9a57\u8b49\u8a8d\u8b49\uff0c\u4f8b\u5982\u81ea\u7c3d\u7ae0\u6191\u8b49" - }, "menu_options": { "add_sensor": "\u65b0\u589e\u611f\u6e2c\u5668", "remove_sensor": "\u79fb\u9664\u611f\u6e2c\u5668", - "resource": "\u8a2d\u5b9a\u4f86\u6e90" + "resource": "\u8a2d\u5b9a\u4f86\u6e90", + "select_edit_sensor": "\u8a2d\u5b9a\u611f\u6e2c\u5668" } }, "resource": { diff --git a/homeassistant/components/screenlogic/climate.py b/homeassistant/components/screenlogic/climate.py index 4b28eea7208..588d6d9a581 100644 --- a/homeassistant/components/screenlogic/climate.py +++ b/homeassistant/components/screenlogic/climate.py @@ -12,7 +12,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS, TEMP_FAHRENHEIT +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -98,8 +98,8 @@ class ScreenLogicClimate(ScreenlogicEntity, ClimateEntity, RestoreEntity): def temperature_unit(self) -> str: """Return the unit of measurement.""" if self.config_data["is_celsius"]["value"] == 1: - return TEMP_CELSIUS - return TEMP_FAHRENHEIT + return UnitOfTemperature.CELSIUS + return UnitOfTemperature.FAHRENHEIT @property def hvac_mode(self) -> HVACMode: @@ -142,7 +142,8 @@ class ScreenLogicClimate(ScreenlogicEntity, ClimateEntity, RestoreEntity): await self._async_refresh() else: raise HomeAssistantError( - f"Failed to set_temperature {temperature} on body {self.body['body_type']['value']}" + f"Failed to set_temperature {temperature} on body" + f" {self.body['body_type']['value']}" ) async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None: @@ -156,7 +157,8 @@ class ScreenLogicClimate(ScreenlogicEntity, ClimateEntity, RestoreEntity): await self._async_refresh() else: raise HomeAssistantError( - f"Failed to set_hvac_mode {mode} on body {self.body['body_type']['value']}" + f"Failed to set_hvac_mode {mode} on body" + f" {self.body['body_type']['value']}" ) async def async_set_preset_mode(self, preset_mode: str) -> None: @@ -170,7 +172,8 @@ class ScreenLogicClimate(ScreenlogicEntity, ClimateEntity, RestoreEntity): await self._async_refresh() else: raise HomeAssistantError( - f"Failed to set_preset_mode {mode} on body {self.body['body_type']['value']}" + f"Failed to set_preset_mode {mode} on body" + f" {self.body['body_type']['value']}" ) async def async_added_to_hass(self) -> None: diff --git a/homeassistant/components/screenlogic/config_flow.py b/homeassistant/components/screenlogic/config_flow.py index 2b845d453df..77040bdb216 100644 --- a/homeassistant/components/screenlogic/config_flow.py +++ b/homeassistant/components/screenlogic/config_flow.py @@ -129,7 +129,9 @@ class ScreenlogicConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): vol.Required(GATEWAY_SELECT_KEY): vol.In( { **unconfigured_gateways, - GATEWAY_MANUAL_ENTRY: "Manually configure a ScreenLogic gateway", + GATEWAY_MANUAL_ENTRY: ( + "Manually configure a ScreenLogic gateway" + ), } ) } diff --git a/homeassistant/components/screenlogic/services.py b/homeassistant/components/screenlogic/services.py index 09f76c0e09e..17c52932e09 100644 --- a/homeassistant/components/screenlogic/services.py +++ b/homeassistant/components/screenlogic/services.py @@ -48,7 +48,8 @@ def async_load_screenlogic_services(hass: HomeAssistant): ) ): raise HomeAssistantError( - f"Failed to call service '{SERVICE_SET_COLOR_MODE}'. Config entry for target not found" + f"Failed to call service '{SERVICE_SET_COLOR_MODE}'. Config entry for" + " target not found" ) color_num = SUPPORTED_COLOR_MODES[service_call.data[ATTR_COLOR_MODE]] for entry_id in screenlogic_entry_ids: diff --git a/homeassistant/components/screenlogic/translations/sk.json b/homeassistant/components/screenlogic/translations/sk.json index 88a6f2a1c76..c674b181b0d 100644 --- a/homeassistant/components/screenlogic/translations/sk.json +++ b/homeassistant/components/screenlogic/translations/sk.json @@ -12,12 +12,27 @@ "data": { "ip_address": "IP adresa", "port": "Port" - } + }, + "description": "Zadajte inform\u00e1cie o va\u0161ej br\u00e1ne ScreenLogic.", + "title": "ScreenLogic" }, "gateway_select": { "data": { "selected_gateway": "Gateway" - } + }, + "description": "Boli objaven\u00e9 nasleduj\u00face br\u00e1ny ScreenLogic. Vyberte jednu, ktor\u00fa chcete nakonfigurova\u0165, alebo sa rozhodnite manu\u00e1lne nakonfigurova\u0165 br\u00e1nu ScreenLogic.", + "title": "ScreenLogic" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "scan_interval": "Sekundy medzi skenovaniami" + }, + "description": "Zadajte nastavenia pre {gateway_name}", + "title": "ScreenLogic" } } } diff --git a/homeassistant/components/script/__init__.py b/homeassistant/components/script/__init__.py index 359486d2687..45786ae984a 100644 --- a/homeassistant/components/script/__init__.py +++ b/homeassistant/components/script/__init__.py @@ -7,10 +7,9 @@ import logging from typing import Any, cast import voluptuous as vol -from voluptuous.humanize import humanize_error from homeassistant.components import websocket_api -from homeassistant.components.blueprint import CONF_USE_BLUEPRINT, BlueprintInputs +from homeassistant.components.blueprint import CONF_USE_BLUEPRINT from homeassistant.const import ( ATTR_ENTITY_ID, ATTR_MODE, @@ -53,7 +52,7 @@ from homeassistant.helpers.typing import ConfigType from homeassistant.loader import bind_hass from homeassistant.util.dt import parse_datetime -from .config import ScriptConfig, async_validate_config_item +from .config import ScriptConfig from .const import ( ATTR_LAST_ACTION, ATTR_LAST_TRIGGERED, @@ -249,32 +248,11 @@ async def _prepare_script_config( """Parse configuration and prepare script entity configuration.""" script_configs: list[ScriptEntityConfig] = [] - conf: dict[str, dict[str, Any] | BlueprintInputs] = config[DOMAIN] + conf: dict[str, ConfigType] = config[DOMAIN] for key, config_block in conf.items(): - raw_blueprint_inputs = None - raw_config = None - - if isinstance(config_block, BlueprintInputs): - blueprint_inputs = config_block - raw_blueprint_inputs = blueprint_inputs.config_with_inputs - - try: - raw_config = blueprint_inputs.async_substitute() - config_block = cast( - dict[str, Any], - await async_validate_config_item(hass, raw_config), - ) - except vol.Invalid as err: - LOGGER.error( - "Blueprint %s generated invalid script with input %s: %s", - blueprint_inputs.blueprint.name, - blueprint_inputs.inputs, - humanize_error(config_block, err), - ) - continue - else: - raw_config = cast(ScriptConfig, config_block).raw_config + raw_config = cast(ScriptConfig, config_block).raw_config + raw_blueprint_inputs = cast(ScriptConfig, config_block).raw_blueprint_inputs script_configs.append( ScriptEntityConfig(config_block, key, raw_blueprint_inputs, raw_config) diff --git a/homeassistant/components/script/config.py b/homeassistant/components/script/config.py index bd7ca1fc791..193a010671e 100644 --- a/homeassistant/components/script/config.py +++ b/homeassistant/components/script/config.py @@ -1,14 +1,19 @@ """Config validation helper for the script integration.""" +from __future__ import annotations + +from collections.abc import Mapping from contextlib import suppress +from typing import Any import voluptuous as vol +from voluptuous.humanize import humanize_error from homeassistant.components.blueprint import ( - BlueprintInputs, + BlueprintException, is_blueprint_instance_config, ) from homeassistant.components.trace import TRACE_CONFIG_SCHEMA -from homeassistant.config import async_log_exception, config_without_domain +from homeassistant.config import config_without_domain from homeassistant.const import ( CONF_ALIAS, CONF_DEFAULT, @@ -19,6 +24,7 @@ from homeassistant.const import ( CONF_SEQUENCE, CONF_VARIABLES, ) +from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import config_per_platform, config_validation as cv from homeassistant.helpers.script import ( @@ -27,6 +33,8 @@ from homeassistant.helpers.script import ( make_script_schema, ) from homeassistant.helpers.selector import validate_selector +from homeassistant.helpers.typing import ConfigType +from homeassistant.util.yaml.input import UndefinedSubstitution from .const import ( CONF_ADVANCED, @@ -65,45 +73,132 @@ SCRIPT_ENTITY_SCHEMA = make_script_schema( ) -async def async_validate_config_item(hass, config, full_config=None): +async def _async_validate_config_item( + hass: HomeAssistant, object_id: str, config: ConfigType, warn_on_errors: bool +) -> ScriptConfig: """Validate config item.""" + raw_config = None + raw_blueprint_inputs = None + uses_blueprint = False + with suppress(ValueError): # Invalid config + raw_config = dict(config) + + def _log_invalid_script( + err: Exception, + script_name: str, + problem: str, + data: Any, + ) -> None: + """Log an error about invalid script.""" + if not warn_on_errors: + return + + if uses_blueprint: + LOGGER.error( + "Blueprint '%s' generated invalid script with inputs %s: %s", + blueprint_inputs.blueprint.name, + blueprint_inputs.inputs, + humanize_error(data, err) if isinstance(err, vol.Invalid) else err, + ) + return + + LOGGER.error( + "%s %s and has been disabled: %s", + script_name, + problem, + humanize_error(data, err) if isinstance(err, vol.Invalid) else err, + ) + return + if is_blueprint_instance_config(config): + uses_blueprint = True blueprints = async_get_blueprints(hass) - return await blueprints.async_inputs_from_config(config) + try: + blueprint_inputs = await blueprints.async_inputs_from_config(config) + except BlueprintException as err: + if warn_on_errors: + LOGGER.error( + "Failed to generate script from blueprint: %s", + err, + ) + raise - config = SCRIPT_ENTITY_SCHEMA(config) - config[CONF_SEQUENCE] = await async_validate_actions_config( - hass, config[CONF_SEQUENCE] - ) + raw_blueprint_inputs = blueprint_inputs.config_with_inputs - return config + try: + config = blueprint_inputs.async_substitute() + raw_config = dict(config) + except UndefinedSubstitution as err: + if warn_on_errors: + LOGGER.error( + "Blueprint '%s' failed to generate script with inputs %s: %s", + blueprint_inputs.blueprint.name, + blueprint_inputs.inputs, + err, + ) + raise HomeAssistantError from err + + script_name = f"Script with object id '{object_id}'" + if isinstance(config, Mapping): + if CONF_ALIAS in config: + script_name = f"Script with alias '{config[CONF_ALIAS]}'" + + try: + cv.slug(object_id) + except vol.Invalid as err: + _log_invalid_script(err, script_name, "has invalid object id", object_id) + raise + try: + validated_config = SCRIPT_ENTITY_SCHEMA(config) + except vol.Invalid as err: + _log_invalid_script(err, script_name, "could not be validated", config) + raise + + try: + validated_config[CONF_SEQUENCE] = await async_validate_actions_config( + hass, validated_config[CONF_SEQUENCE] + ) + except ( + vol.Invalid, + HomeAssistantError, + ) as err: + _log_invalid_script( + err, script_name, "failed to setup actions", validated_config + ) + raise + + script_config = ScriptConfig(validated_config) + script_config.raw_blueprint_inputs = raw_blueprint_inputs + script_config.raw_config = raw_config + return script_config class ScriptConfig(dict): """Dummy class to allow adding attributes.""" - raw_config = None + raw_config: ConfigType | None = None + raw_blueprint_inputs: ConfigType | None = None -async def _try_async_validate_config_item(hass, object_id, config, full_config=None): +async def _try_async_validate_config_item( + hass: HomeAssistant, + object_id: str, + config: ConfigType, +) -> ScriptConfig | None: """Validate config item.""" - raw_config = None - with suppress(ValueError): # Invalid config - raw_config = dict(config) - try: - cv.slug(object_id) - config = await async_validate_config_item(hass, config, full_config) - except (vol.Invalid, HomeAssistantError) as ex: - async_log_exception(ex, DOMAIN, full_config or config, hass) + return await _async_validate_config_item(hass, object_id, config, True) + except (vol.Invalid, HomeAssistantError): return None - if isinstance(config, BlueprintInputs): - return config - config = ScriptConfig(config) - config.raw_config = raw_config - return config +async def async_validate_config_item( + hass: HomeAssistant, + object_id: str, + config: dict[str, Any], +) -> ScriptConfig | None: + """Validate config item, called by EditScriptConfigView.""" + return await _async_validate_config_item(hass, object_id, config, False) async def async_validate_config(hass, config): @@ -114,7 +209,7 @@ async def async_validate_config(hass, config): if object_id in scripts: LOGGER.warning("Duplicate script detected with name: '%s'", object_id) continue - cfg = await _try_async_validate_config_item(hass, object_id, cfg, config) + cfg = await _try_async_validate_config_item(hass, object_id, cfg) if cfg is not None: scripts[object_id] = cfg diff --git a/homeassistant/components/season/sensor.py b/homeassistant/components/season/sensor.py index 8bb8773860d..a568e51ed9d 100644 --- a/homeassistant/components/season/sensor.py +++ b/homeassistant/components/season/sensor.py @@ -8,6 +8,7 @@ import voluptuous as vol from homeassistant.components.sensor import ( PLATFORM_SCHEMA as PARENT_PLATFORM_SCHEMA, + SensorDeviceClass, SensorEntity, ) from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry @@ -132,8 +133,10 @@ def get_season( class SeasonSensorEntity(SensorEntity): """Representation of the current season.""" - _attr_device_class = "season__season" + _attr_device_class = SensorDeviceClass.ENUM _attr_has_entity_name = True + _attr_options = ["spring", "summer", "autumn", "winter"] + _attr_translation_key = "season" def __init__(self, entry: ConfigEntry, hemisphere: str) -> None: """Initialize the season.""" diff --git a/homeassistant/components/season/strings.json b/homeassistant/components/season/strings.json index 25d121d16e6..bff02df5c6c 100644 --- a/homeassistant/components/season/strings.json +++ b/homeassistant/components/season/strings.json @@ -16,5 +16,17 @@ "title": "The Season YAML configuration has been removed", "description": "Configuring Season using YAML has been removed.\n\nYour existing YAML configuration is not used by Home Assistant.\n\nRemove the YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue." } + }, + "entity": { + "sensor": { + "season": { + "state": { + "spring": "Spring", + "summer": "Summer", + "autumn": "Autumn", + "winter": "Winter" + } + } + } } } diff --git a/homeassistant/components/season/strings.sensor.json b/homeassistant/components/season/strings.sensor.json deleted file mode 100644 index 0407d99dee4..00000000000 --- a/homeassistant/components/season/strings.sensor.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "state": { - "season__season": { - "spring": "Spring", - "summer": "Summer", - "autumn": "Autumn", - "winter": "Winter" - } - } -} diff --git a/homeassistant/components/season/translations/bg.json b/homeassistant/components/season/translations/bg.json index 80a7cc489a9..b5f6aecfa47 100644 --- a/homeassistant/components/season/translations/bg.json +++ b/homeassistant/components/season/translations/bg.json @@ -3,5 +3,17 @@ "abort": { "already_configured": "\u0423\u0441\u043b\u0443\u0433\u0430\u0442\u0430 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0430" } + }, + "entity": { + "sensor": { + "season": { + "state": { + "autumn": "\u0415\u0441\u0435\u043d", + "spring": "\u041f\u0440\u043e\u043b\u0435\u0442", + "summer": "\u041b\u044f\u0442\u043e", + "winter": "\u0417\u0438\u043c\u0430" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/season/translations/ca.json b/homeassistant/components/season/translations/ca.json index a2c47ff2fc3..669b5e158dc 100644 --- a/homeassistant/components/season/translations/ca.json +++ b/homeassistant/components/season/translations/ca.json @@ -11,6 +11,18 @@ } } }, + "entity": { + "sensor": { + "season": { + "state": { + "autumn": "Tardor", + "spring": "Primavera", + "summer": "Estiu", + "winter": "Hivern" + } + } + } + }, "issues": { "removed_yaml": { "description": "La configuraci\u00f3 d'Estaci\u00f3 de l'any mitjan\u00e7ant YAML s'ha eliminat.\n\nHome Assistant ja no utilitza la configuraci\u00f3 YAML existent.\n\nElimina la configuraci\u00f3 YAML corresponent del fitxer configuration.yaml i reinicia Home Assistant per solucionar aquest problema.", diff --git a/homeassistant/components/season/translations/de.json b/homeassistant/components/season/translations/de.json index c01c6f1b963..728ec204077 100644 --- a/homeassistant/components/season/translations/de.json +++ b/homeassistant/components/season/translations/de.json @@ -11,6 +11,18 @@ } } }, + "entity": { + "sensor": { + "season": { + "state": { + "autumn": "Herbst", + "spring": "Fr\u00fchling", + "summer": "Sommer", + "winter": "Winter" + } + } + } + }, "issues": { "removed_yaml": { "description": "Das Konfigurieren von Saison mit YAML wurde entfernt. \n\nDeine vorhandene YAML-Konfiguration wird von Home Assistant nicht verwendet. \n\nEntferne die YAML-Konfiguration aus deiner configuration.yaml-Datei und starte Home Assistant neu, um dieses Problem zu beheben.", diff --git a/homeassistant/components/season/translations/el.json b/homeassistant/components/season/translations/el.json index 2f3e0ba48bc..b1748c4097a 100644 --- a/homeassistant/components/season/translations/el.json +++ b/homeassistant/components/season/translations/el.json @@ -11,6 +11,18 @@ } } }, + "entity": { + "sensor": { + "season": { + "state": { + "autumn": "\u03a6\u03b8\u03b9\u03bd\u03cc\u03c0\u03c9\u03c1\u03bf", + "spring": "\u0386\u03bd\u03bf\u03b9\u03be\u03b7", + "summer": "\u039a\u03b1\u03bb\u03bf\u03ba\u03b1\u03af\u03c1\u03b9", + "winter": "\u03a7\u03b5\u03b9\u03bc\u03ce\u03bd\u03b1\u03c2" + } + } + } + }, "issues": { "removed_yaml": { "description": "\u0397 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c4\u03b7\u03c2 Season \u03bc\u03b5 \u03c7\u03c1\u03ae\u03c3\u03b7 YAML \u03ad\u03c7\u03b5\u03b9 \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b7\u03b8\u03b5\u03af.\n\n\u0397 \u03c5\u03c0\u03ac\u03c1\u03c7\u03bf\u03c5\u03c3\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 YAML \u03b4\u03b5\u03bd \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03bf Home Assistant.\n\n\u0391\u03c6\u03b1\u03b9\u03c1\u03ad\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 YAML \u03b1\u03c0\u03cc \u03c4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf configuration.yaml \u03ba\u03b1\u03b9 \u03b5\u03c0\u03b1\u03bd\u03b5\u03ba\u03ba\u03b9\u03bd\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf Home Assistant \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c0\u03c1\u03cc\u03b2\u03bb\u03b7\u03bc\u03b1.", diff --git a/homeassistant/components/season/translations/en.json b/homeassistant/components/season/translations/en.json index 5508c305fea..79d23fabf40 100644 --- a/homeassistant/components/season/translations/en.json +++ b/homeassistant/components/season/translations/en.json @@ -11,6 +11,18 @@ } } }, + "entity": { + "sensor": { + "season": { + "state": { + "autumn": "Autumn", + "spring": "Spring", + "summer": "Summer", + "winter": "Winter" + } + } + } + }, "issues": { "removed_yaml": { "description": "Configuring Season using YAML has been removed.\n\nYour existing YAML configuration is not used by Home Assistant.\n\nRemove the YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue.", diff --git a/homeassistant/components/season/translations/es.json b/homeassistant/components/season/translations/es.json index a92d65637da..e2207862315 100644 --- a/homeassistant/components/season/translations/es.json +++ b/homeassistant/components/season/translations/es.json @@ -11,6 +11,18 @@ } } }, + "entity": { + "sensor": { + "season": { + "state": { + "autumn": "Oto\u00f1o", + "spring": "Primavera", + "summer": "Verano", + "winter": "Invierno" + } + } + } + }, "issues": { "removed_yaml": { "description": "Se ha eliminado la configuraci\u00f3n de Season mediante YAML. \n\nHome Assistant no utiliza tu configuraci\u00f3n YAML existente. \n\nElimina la configuraci\u00f3n YAML de tu archivo configuration.yaml y reinicia Home Assistant para solucionar este problema.", diff --git a/homeassistant/components/season/translations/et.json b/homeassistant/components/season/translations/et.json index 60abe6adf65..2b433496256 100644 --- a/homeassistant/components/season/translations/et.json +++ b/homeassistant/components/season/translations/et.json @@ -11,6 +11,18 @@ } } }, + "entity": { + "sensor": { + "season": { + "state": { + "autumn": "S\u00fcgis", + "spring": "Kevad", + "summer": "Suvi", + "winter": "Talv" + } + } + } + }, "issues": { "removed_yaml": { "description": "Season seadistamine YAML-i abil on eemaldatud. \n\n Koduassistent ei kasuta teie olemasolevat YAML-i konfiguratsiooni. \n\n Selle probleemi lahendamiseks eemaldage YAML-i konfiguratsioon failist configuration.yaml ja taask\u00e4ivitage Home Assistant.", diff --git a/homeassistant/components/season/translations/he.json b/homeassistant/components/season/translations/he.json index 48a6eeeea33..baf907cf85b 100644 --- a/homeassistant/components/season/translations/he.json +++ b/homeassistant/components/season/translations/he.json @@ -3,5 +3,17 @@ "abort": { "already_configured": "\u05e9\u05d9\u05e8\u05d5\u05ea \u05d6\u05d4 \u05db\u05d1\u05e8 \u05de\u05d5\u05d2\u05d3\u05e8" } + }, + "entity": { + "sensor": { + "season": { + "state": { + "autumn": "\u05e1\u05ea\u05d9\u05d5", + "spring": "\u05d0\u05d1\u05d9\u05d1", + "summer": "\u05e7\u05d9\u05e5", + "winter": "\u05d7\u05d5\u05e8\u05e3" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/season/translations/hu.json b/homeassistant/components/season/translations/hu.json index 32a3e7c77b6..bf092fed3b1 100644 --- a/homeassistant/components/season/translations/hu.json +++ b/homeassistant/components/season/translations/hu.json @@ -11,6 +11,18 @@ } } }, + "entity": { + "sensor": { + "season": { + "state": { + "autumn": "\u0150sz", + "spring": "Tavasz", + "summer": "Ny\u00e1r", + "winter": "T\u00e9l" + } + } + } + }, "issues": { "removed_yaml": { "description": "A Season konfigur\u00e1l\u00e1sa YAML haszn\u00e1lat\u00e1val elt\u00e1vol\u00edt\u00e1sra ker\u00fclt.\n\nA megl\u00e9v\u0151 YAML konfigur\u00e1ci\u00f3t a Home Assistant nem haszn\u00e1lja.\n\nA probl\u00e9ma megold\u00e1s\u00e1hoz t\u00e1vol\u00edtsa el a YAML konfigur\u00e1ci\u00f3t a configuration.yaml f\u00e1jlb\u00f3l, \u00e9s ind\u00edtsa \u00fajra a Home Assistantot.", diff --git a/homeassistant/components/season/translations/id.json b/homeassistant/components/season/translations/id.json index 0b557ccaabb..1679e71aad6 100644 --- a/homeassistant/components/season/translations/id.json +++ b/homeassistant/components/season/translations/id.json @@ -11,6 +11,18 @@ } } }, + "entity": { + "sensor": { + "season": { + "state": { + "autumn": "Musim gugur", + "spring": "Musim semi", + "summer": "Musim panas", + "winter": "Musim dingin" + } + } + } + }, "issues": { "removed_yaml": { "description": "Proses konfigurasi Integrasi Musim lewat YAML telah dihapus.\n\nKonfigurasi YAML yang ada tidak digunakan oleh Home Assistant.\n\nHapus konfigurasi YAML dari file configuration.yaml dan mulai ulang Home Assistant untuk memperbaiki masalah ini.", diff --git a/homeassistant/components/season/translations/it.json b/homeassistant/components/season/translations/it.json index 8771365d3c5..c00b0388a98 100644 --- a/homeassistant/components/season/translations/it.json +++ b/homeassistant/components/season/translations/it.json @@ -11,6 +11,18 @@ } } }, + "entity": { + "sensor": { + "season": { + "state": { + "autumn": "Autunno", + "spring": "Primavera", + "summer": "Estate", + "winter": "Inverno" + } + } + } + }, "issues": { "removed_yaml": { "description": "La configurazione di Season tramite YAML \u00e8 stata rimossa. \n\nLa tua configurazione YAML esistente non viene utilizzata da Home Assistant. \n\nRimuovere la configurazione YAML dal file configuration.yaml e riavviare Home Assistant per risolvere questo problema.", diff --git a/homeassistant/components/season/translations/ko.json b/homeassistant/components/season/translations/ko.json new file mode 100644 index 00000000000..e1300423811 --- /dev/null +++ b/homeassistant/components/season/translations/ko.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "\uc11c\ube44\uc2a4\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/season/translations/no.json b/homeassistant/components/season/translations/no.json index 1b1c0f332e5..6bc5e930871 100644 --- a/homeassistant/components/season/translations/no.json +++ b/homeassistant/components/season/translations/no.json @@ -11,6 +11,18 @@ } } }, + "entity": { + "sensor": { + "season": { + "state": { + "autumn": "H\u00f8st", + "spring": "V\u00e5r", + "summer": "Sommer", + "winter": "Vinter" + } + } + } + }, "issues": { "removed_yaml": { "description": "Konfigurering av sesong med YAML er fjernet. \n\n Din eksisterende YAML-konfigurasjon brukes ikke av Home Assistant. \n\n Fjern YAML-konfigurasjonen fra configuration.yaml-filen og start Home Assistant p\u00e5 nytt for \u00e5 fikse dette problemet.", diff --git a/homeassistant/components/season/translations/pl.json b/homeassistant/components/season/translations/pl.json index 21342aeb8b8..31e177ad20a 100644 --- a/homeassistant/components/season/translations/pl.json +++ b/homeassistant/components/season/translations/pl.json @@ -11,6 +11,18 @@ } } }, + "entity": { + "sensor": { + "season": { + "state": { + "autumn": "jesie\u0144", + "spring": "wiosna", + "summer": "lato", + "winter": "zima" + } + } + } + }, "issues": { "removed_yaml": { "description": "Konfiguracja Sezon\u00f3w za pomoc\u0105 YAML zosta\u0142a usuni\u0119ta. \n\nTwoja istniej\u0105ca konfiguracja YAML nie jest u\u017cywana przez Home Assistant. \n\nUsu\u0144 konfiguracj\u0119 YAML z pliku configuration.yaml i uruchom ponownie Home Assistant, aby rozwi\u0105za\u0107 ten problem.", diff --git a/homeassistant/components/season/translations/pt-BR.json b/homeassistant/components/season/translations/pt-BR.json index bc61719f1b1..442fdeca860 100644 --- a/homeassistant/components/season/translations/pt-BR.json +++ b/homeassistant/components/season/translations/pt-BR.json @@ -11,6 +11,18 @@ } } }, + "entity": { + "sensor": { + "season": { + "state": { + "autumn": "Outono", + "spring": "Primavera", + "summer": "Ver\u00e3o", + "winter": "Inverno" + } + } + } + }, "issues": { "removed_yaml": { "description": "A configura\u00e7\u00e3o da Season usando YAML foi removida. \n\n Sua configura\u00e7\u00e3o YAML existente n\u00e3o \u00e9 usada pelo Home Assistant. \n\n Remova a configura\u00e7\u00e3o YAML do arquivo configuration.yaml e reinicie o Home Assistant para corrigir esse problema.", diff --git a/homeassistant/components/season/translations/ru.json b/homeassistant/components/season/translations/ru.json index fdde0dd6319..0f579c98248 100644 --- a/homeassistant/components/season/translations/ru.json +++ b/homeassistant/components/season/translations/ru.json @@ -11,6 +11,18 @@ } } }, + "entity": { + "sensor": { + "season": { + "state": { + "autumn": "\u041e\u0441\u0435\u043d\u044c", + "spring": "\u0412\u0435\u0441\u043d\u0430", + "summer": "\u041b\u0435\u0442\u043e", + "winter": "\u0417\u0438\u043c\u0430" + } + } + } + }, "issues": { "removed_yaml": { "description": "\u0418\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0441\u0435\u043d\u0441\u043e\u0440\u0430 \u0441\u0435\u0437\u043e\u043d\u0430 \u0442\u0435\u043f\u0435\u0440\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u0430 \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0435\u0440\u0435\u0437 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441.\n\n\u0412\u0430\u0448\u0430 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0430\u044f YAML-\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f Home Assistant. \u0423\u0434\u0430\u043b\u0438\u0442\u0435 \u0435\u0451 \u0438\u0437 \u0444\u0430\u0439\u043b\u0430 configuration.yaml \u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 Home Assistant, \u0447\u0442\u043e\u0431\u044b \u0443\u0441\u0442\u0440\u0430\u043d\u0438\u0442\u044c \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443.", diff --git a/homeassistant/components/season/translations/sk.json b/homeassistant/components/season/translations/sk.json index 2e87a326f7c..f03f083e2e8 100644 --- a/homeassistant/components/season/translations/sk.json +++ b/homeassistant/components/season/translations/sk.json @@ -10,5 +10,23 @@ } } } + }, + "entity": { + "sensor": { + "season": { + "state": { + "autumn": "Jese\u0148", + "spring": "Jar", + "summer": "Leto", + "winter": "Zima" + } + } + } + }, + "issues": { + "removed_yaml": { + "description": "Konfigur\u00e1cia sez\u00f3ny pomocou YAML bola odstr\u00e1nen\u00e1. \n\n Asistent dom\u00e1cnosti nepou\u017e\u00edva va\u0161u existuj\u00facu konfigur\u00e1ciu YAML. \n\n Ak chcete tento probl\u00e9m vyrie\u0161i\u0165, odstr\u00e1\u0148te konfigur\u00e1ciu YAML zo s\u00faboru configuration.yaml a re\u0161tartujte aplik\u00e1ciu Home Assistant.", + "title": "Konfigur\u00e1cia Season YAML bola odstr\u00e1nen\u00e1" + } } } \ No newline at end of file diff --git a/homeassistant/components/season/translations/zh-Hant.json b/homeassistant/components/season/translations/zh-Hant.json index 9f9363edf18..7e1335a5a99 100644 --- a/homeassistant/components/season/translations/zh-Hant.json +++ b/homeassistant/components/season/translations/zh-Hant.json @@ -11,6 +11,18 @@ } } }, + "entity": { + "sensor": { + "season": { + "state": { + "autumn": "\u79cb\u5b63", + "spring": "\u6625\u5b63", + "summer": "\u590f\u5b63", + "winter": "\u51ac\u5b63" + } + } + } + }, "issues": { "removed_yaml": { "description": "\u4f7f\u7528 YAML \u8a2d\u5b9a Season \u7684\u529f\u80fd\u5373\u5c07\u79fb\u9664\u3002\n\nHome Assistant \u5c07\u4e0d\u518d\u4f7f\u7528\u73fe\u6709\u7684 YAML \u8a2d\u5b9a\u3002\n\n\u7531 configuration.yaml \u6a94\u6848\u4e2d\u79fb\u9664 YAML \u8a2d\u5b9a\u4e26\u91cd\u555f Home Assistant \u4ee5\u4fee\u6b63\u6b64\u554f\u984c\u3002", diff --git a/homeassistant/components/select/translations/sk.json b/homeassistant/components/select/translations/sk.json index 220711deee0..849417e972b 100644 --- a/homeassistant/components/select/translations/sk.json +++ b/homeassistant/components/select/translations/sk.json @@ -1,5 +1,8 @@ { "device_automation": { + "action_type": { + "select_option": "Zme\u0148te mo\u017enos\u0165 {entity_name}" + }, "condition_type": { "selected_option": "Aktu\u00e1lna vybrat\u00e1 mo\u017enos\u0165 {entity_name}" }, diff --git a/homeassistant/components/sense/const.py b/homeassistant/components/sense/const.py index 622e9897a66..1b7fdd8fabc 100644 --- a/homeassistant/components/sense/const.py +++ b/homeassistant/components/sense/const.py @@ -39,8 +39,6 @@ FROM_GRID_ID = "from_grid" SOLAR_POWERED_NAME = "Solar Powered Percentage" SOLAR_POWERED_ID = "solar_powered" -ICON = "mdi:flash" - SENSE_TIMEOUT_EXCEPTIONS = (asyncio.TimeoutError, SenseAPITimeoutException) SENSE_EXCEPTIONS = (socket.gaierror, SenseWebsocketException) SENSE_CONNECT_EXCEPTIONS = ( diff --git a/homeassistant/components/sense/sensor.py b/homeassistant/components/sense/sensor.py index 54f05e2dbb5..9a3bb8bc3f0 100644 --- a/homeassistant/components/sense/sensor.py +++ b/homeassistant/components/sense/sensor.py @@ -7,10 +7,10 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - ELECTRIC_POTENTIAL_VOLT, - ENERGY_KILO_WATT_HOUR, PERCENTAGE, - POWER_WATT, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfPower, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect @@ -27,7 +27,6 @@ from .const import ( DOMAIN, FROM_GRID_ID, FROM_GRID_NAME, - ICON, MDI_ICONS, NET_PRODUCTION_ID, NET_PRODUCTION_NAME, @@ -156,8 +155,8 @@ async def async_setup_entry( class SenseActiveSensor(SensorEntity): """Implementation of a Sense energy sensor.""" - _attr_icon = ICON - _attr_native_unit_of_measurement = POWER_WATT + _attr_device_class = SensorDeviceClass.POWER + _attr_native_unit_of_measurement = UnitOfPower.WATT _attr_attribution = ATTRIBUTION _attr_should_poll = False _attr_available = False @@ -210,9 +209,9 @@ class SenseActiveSensor(SensorEntity): class SenseVoltageSensor(SensorEntity): """Implementation of a Sense energy voltage sensor.""" - _attr_native_unit_of_measurement = ELECTRIC_POTENTIAL_VOLT + _attr_device_class = SensorDeviceClass.VOLTAGE + _attr_native_unit_of_measurement = UnitOfElectricPotential.VOLT _attr_attribution = ATTRIBUTION - _attr_icon = ICON _attr_should_poll = False _attr_available = False @@ -256,9 +255,8 @@ class SenseTrendsSensor(CoordinatorEntity, SensorEntity): _attr_device_class = SensorDeviceClass.ENERGY _attr_state_class = SensorStateClass.TOTAL - _attr_native_unit_of_measurement = ENERGY_KILO_WATT_HOUR + _attr_native_unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR _attr_attribution = ATTRIBUTION - _attr_icon = ICON _attr_should_poll = False def __init__( @@ -309,7 +307,7 @@ class SenseEnergyDevice(SensorEntity): _attr_available = False _attr_state_class = SensorStateClass.MEASUREMENT - _attr_native_unit_of_measurement = POWER_WATT + _attr_native_unit_of_measurement = UnitOfPower.WATT _attr_attribution = ATTRIBUTION _attr_device_class = SensorDeviceClass.POWER _attr_should_poll = False diff --git a/homeassistant/components/sense/translations/ko.json b/homeassistant/components/sense/translations/ko.json index b28f41f0d64..0d5931eb437 100644 --- a/homeassistant/components/sense/translations/ko.json +++ b/homeassistant/components/sense/translations/ko.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" }, "error": { "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", @@ -9,6 +10,12 @@ "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" }, "step": { + "reauth_validate": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638" + }, + "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" + }, "user": { "data": { "email": "\uc774\uba54\uc77c", diff --git a/homeassistant/components/sense/translations/pt.json b/homeassistant/components/sense/translations/pt.json index 1b8b4e2dc18..4f963d34f7a 100644 --- a/homeassistant/components/sense/translations/pt.json +++ b/homeassistant/components/sense/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, @@ -17,7 +17,7 @@ }, "user": { "data": { - "email": "Email", + "email": "", "password": "Palavra-passe" } } diff --git a/homeassistant/components/sense/translations/sk.json b/homeassistant/components/sense/translations/sk.json index 1a1a1e64869..0b344294c05 100644 --- a/homeassistant/components/sense/translations/sk.json +++ b/homeassistant/components/sense/translations/sk.json @@ -20,13 +20,16 @@ "user": { "data": { "email": "Email", - "password": "Heslo" - } + "password": "Heslo", + "timeout": "\u010casov\u00fd limit" + }, + "title": "Pripojenie k monitoru Sense Energy" }, "validation": { "data": { "code": "Overovac\u00ed k\u00f3d" - } + }, + "title": "Viacfaktorov\u00e9 overovanie Sense" } } } diff --git a/homeassistant/components/senseme/strings.json b/homeassistant/components/senseme/strings.json index f5129ee001e..2de35bc7fed 100644 --- a/homeassistant/components/senseme/strings.json +++ b/homeassistant/components/senseme/strings.json @@ -9,7 +9,7 @@ } }, "discovery_confirm": { - "description": "Do you want to setup {name} - {model} ({host})?" + "description": "Do you want to set up {name} - {model} ({host})?" }, "manual": { "description": "Enter an IP Address.", diff --git a/homeassistant/components/senseme/translations/en.json b/homeassistant/components/senseme/translations/en.json index cb21f8ff193..d497f1bbbcf 100644 --- a/homeassistant/components/senseme/translations/en.json +++ b/homeassistant/components/senseme/translations/en.json @@ -11,7 +11,7 @@ "flow_title": "{name} - {model} ({host})", "step": { "discovery_confirm": { - "description": "Do you want to setup {name} - {model} ({host})?" + "description": "Do you want to set up {name} - {model} ({host})?" }, "manual": { "data": { diff --git a/homeassistant/components/senseme/translations/ko.json b/homeassistant/components/senseme/translations/ko.json new file mode 100644 index 00000000000..bab50eb7334 --- /dev/null +++ b/homeassistant/components/senseme/translations/ko.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_host": "\ud638\uc2a4\ud2b8 \uc774\ub984 \ub610\ub294 IP \uc8fc\uc18c\uac00 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "step": { + "manual": { + "data": { + "host": "\ud638\uc2a4\ud2b8" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/senseme/translations/no.json b/homeassistant/components/senseme/translations/no.json index 47e330100c9..334dda56e03 100644 --- a/homeassistant/components/senseme/translations/no.json +++ b/homeassistant/components/senseme/translations/no.json @@ -11,7 +11,7 @@ "flow_title": "{name} - {model} ( {host} )", "step": { "discovery_confirm": { - "description": "Vil du konfigurere {name} - {model} ( {host} )?" + "description": "Vil du sette opp {name} - {model} ( {host} )?" }, "manual": { "data": { diff --git a/homeassistant/components/senseme/translations/pt-BR.json b/homeassistant/components/senseme/translations/pt-BR.json index 210b33376d5..7d12b989d34 100644 --- a/homeassistant/components/senseme/translations/pt-BR.json +++ b/homeassistant/components/senseme/translations/pt-BR.json @@ -11,7 +11,7 @@ "flow_title": "{name} - {model} ({host})", "step": { "discovery_confirm": { - "description": "Deseja configurar {name} - {model} ( {host} )?" + "description": "Deseja configurar {name} - {model} ({host})?" }, "manual": { "data": { diff --git a/homeassistant/components/senseme/translations/pt.json b/homeassistant/components/senseme/translations/pt.json index 4c3266a6022..3d4aa8fb4b0 100644 --- a/homeassistant/components/senseme/translations/pt.json +++ b/homeassistant/components/senseme/translations/pt.json @@ -1,12 +1,12 @@ { "config": { "error": { - "invalid_host": "Nome de servidor ou endere\u00e7o IP inv\u00e1lido." + "invalid_host": "Endere\u00e7o IP ou hostname inv\u00e1lido." }, "step": { "manual": { "data": { - "host": "Servidor" + "host": "Endere\u00e7o" } } } diff --git a/homeassistant/components/sensibo/climate.py b/homeassistant/components/sensibo/climate.py index b1980b5e595..583d25112b4 100644 --- a/homeassistant/components/sensibo/climate.py +++ b/homeassistant/components/sensibo/climate.py @@ -19,8 +19,7 @@ from homeassistant.const import ( ATTR_STATE, ATTR_TEMPERATURE, PRECISION_TENTHS, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError @@ -171,7 +170,9 @@ class SensiboClimate(SensiboDeviceBaseEntity, ClimateEntity): super().__init__(coordinator, device_id) self._attr_unique_id = device_id self._attr_temperature_unit = ( - TEMP_CELSIUS if self.device_data.temp_unit == "C" else TEMP_FAHRENHEIT + UnitOfTemperature.CELSIUS + if self.device_data.temp_unit == "C" + else UnitOfTemperature.FAHRENHEIT ) self._attr_supported_features = self.get_features() self._attr_precision = PRECISION_TENTHS @@ -212,7 +213,7 @@ class SensiboClimate(SensiboDeviceBaseEntity, ClimateEntity): if self.device_data.temp: return TemperatureConverter.convert( self.device_data.temp, - TEMP_CELSIUS, + UnitOfTemperature.CELSIUS, self.temperature_unit, ) return None @@ -220,7 +221,11 @@ class SensiboClimate(SensiboDeviceBaseEntity, ClimateEntity): @property def temperature_unit(self) -> str: """Return temperature unit.""" - return TEMP_CELSIUS if self.device_data.temp_unit == "C" else TEMP_FAHRENHEIT + return ( + UnitOfTemperature.CELSIUS + if self.device_data.temp_unit == "C" + else UnitOfTemperature.FAHRENHEIT + ) @property def target_temperature(self) -> float | None: @@ -464,10 +469,14 @@ class SensiboClimate(SensiboDeviceBaseEntity, ClimateEntity): if high_temperature_state.get("temperatureUnit") == "F": high_temp = TemperatureConverter.convert( - high_temperature_threshold, TEMP_FAHRENHEIT, TEMP_CELSIUS + high_temperature_threshold, + UnitOfTemperature.FAHRENHEIT, + UnitOfTemperature.CELSIUS, ) low_temp = TemperatureConverter.convert( - low_temperature_threshold, TEMP_FAHRENHEIT, TEMP_CELSIUS + low_temperature_threshold, + UnitOfTemperature.FAHRENHEIT, + UnitOfTemperature.CELSIUS, ) params: dict[str, str | bool | float | dict] = { diff --git a/homeassistant/components/sensibo/coordinator.py b/homeassistant/components/sensibo/coordinator.py index 6c5a993e34a..1cdcbd79932 100644 --- a/homeassistant/components/sensibo/coordinator.py +++ b/homeassistant/components/sensibo/coordinator.py @@ -20,11 +20,9 @@ from .const import DEFAULT_SCAN_INTERVAL, DOMAIN, LOGGER, TIMEOUT REQUEST_REFRESH_DELAY = 0.35 -class SensiboDataUpdateCoordinator(DataUpdateCoordinator): +class SensiboDataUpdateCoordinator(DataUpdateCoordinator[SensiboData]): """A Sensibo Data Update Coordinator.""" - data: SensiboData - def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None: """Initialize the Sensibo coordinator.""" self.client = SensiboClient( diff --git a/homeassistant/components/sensibo/select.py b/homeassistant/components/sensibo/select.py index afb961b429a..b69de8d0763 100644 --- a/homeassistant/components/sensibo/select.py +++ b/homeassistant/components/sensibo/select.py @@ -104,7 +104,8 @@ class SensiboSelect(SensiboDeviceBaseEntity, SelectEntity): """Set state to the selected option.""" if self.entity_description.key not in self.device_data.active_features: raise HomeAssistantError( - f"Current mode {self.device_data.hvac_mode} doesn't support setting {self.entity_description.name}" + f"Current mode {self.device_data.hvac_mode} doesn't support setting" + f" {self.entity_description.name}" ) await self.async_send_api_call( diff --git a/homeassistant/components/sensibo/sensor.py b/homeassistant/components/sensibo/sensor.py index ad5286a02c2..348c5986bd2 100644 --- a/homeassistant/components/sensibo/sensor.py +++ b/homeassistant/components/sensibo/sensor.py @@ -19,10 +19,10 @@ from homeassistant.const import ( CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, CONCENTRATION_PARTS_PER_BILLION, CONCENTRATION_PARTS_PER_MILLION, - ELECTRIC_POTENTIAL_VOLT, PERCENTAGE, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, - TEMP_CELSIUS, + UnitOfElectricPotential, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory @@ -90,7 +90,7 @@ MOTION_SENSOR_TYPES: tuple[SensiboMotionSensorEntityDescription, ...] = ( key="battery_voltage", device_class=SensorDeviceClass.VOLTAGE, entity_category=EntityCategory.DIAGNOSTIC, - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, state_class=SensorStateClass.MEASUREMENT, name="Battery voltage", icon="mdi:battery", @@ -131,7 +131,7 @@ PURE_SENSOR_TYPES: tuple[SensiboDeviceSensorEntityDescription, ...] = ( icon="mdi:air-filter", value_fn=lambda data: data.pure_sensitivity, extra_fn=None, - device_class="sensibo__sensitivity", + translation_key="sensitivity", ), FILTER_LAST_RESET_DESCRIPTION, ) @@ -174,7 +174,7 @@ DEVICE_SENSOR_TYPES: tuple[SensiboDeviceSensorEntityDescription, ...] = ( ), SensiboDeviceSensorEntityDescription( key="climate_react_type", - device_class="sensibo__smart_type", + translation_key="smart_type", name="Climate React type", value_fn=lambda data: data.smart_type, extra_fn=None, @@ -311,7 +311,7 @@ class SensiboMotionSensor(SensiboMotionBaseEntity, SensorEntity): def native_unit_of_measurement(self) -> str | None: """Add native unit of measurement.""" if self.entity_description.device_class == SensorDeviceClass.TEMPERATURE: - return TEMP_CELSIUS + return UnitOfTemperature.CELSIUS return self.entity_description.native_unit_of_measurement @property @@ -345,7 +345,7 @@ class SensiboDeviceSensor(SensiboDeviceBaseEntity, SensorEntity): def native_unit_of_measurement(self) -> str | None: """Add native unit of measurement.""" if self.entity_description.device_class == SensorDeviceClass.TEMPERATURE: - return TEMP_CELSIUS + return UnitOfTemperature.CELSIUS return self.entity_description.native_unit_of_measurement @property diff --git a/homeassistant/components/sensibo/strings.json b/homeassistant/components/sensibo/strings.json index 2af4e6043cb..21830ff913c 100644 --- a/homeassistant/components/sensibo/strings.json +++ b/homeassistant/components/sensibo/strings.json @@ -29,5 +29,22 @@ } } } + }, + "entity": { + "sensor": { + "sensitivity": { + "state": { + "n": "Normal", + "s": "Sensitive" + } + }, + "smart_type": { + "state": { + "temperature": "Temperature", + "feelslike": "Feels like", + "humidity": "Humidity" + } + } + } } } diff --git a/homeassistant/components/sensibo/strings.sensor.json b/homeassistant/components/sensibo/strings.sensor.json deleted file mode 100644 index 6226fd26a0f..00000000000 --- a/homeassistant/components/sensibo/strings.sensor.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "state": { - "sensibo__sensitivity": { - "n": "Normal", - "s": "Sensitive" - }, - "sensibo__smart_type": { - "temperature": "Temperature", - "feelslike": "Feels like", - "humidity": "Humidity" - } - } -} diff --git a/homeassistant/components/sensibo/switch.py b/homeassistant/components/sensibo/switch.py index f57d72e5fb3..8d0dc5fba2f 100644 --- a/homeassistant/components/sensibo/switch.py +++ b/homeassistant/components/sensibo/switch.py @@ -184,7 +184,8 @@ class SensiboDeviceSwitch(SensiboDeviceBaseEntity, SwitchEntity): """Make service call to api for setting Climate React.""" if self.device_data.smart_type is None: raise HomeAssistantError( - "Use Sensibo Enable Climate React Service once to enable switch or the Sensibo app" + "Use Sensibo Enable Climate React Service once to enable switch or the" + " Sensibo app" ) new_state = bool(self.device_data.smart_on is False) data: dict[str, Any] = {"enabled": new_state} diff --git a/homeassistant/components/sensibo/translations/bg.json b/homeassistant/components/sensibo/translations/bg.json index 3e64be8cd23..50b9fabe5c2 100644 --- a/homeassistant/components/sensibo/translations/bg.json +++ b/homeassistant/components/sensibo/translations/bg.json @@ -26,5 +26,15 @@ } } } + }, + "entity": { + "sensor": { + "smart_type": { + "state": { + "humidity": "\u0412\u043b\u0430\u0436\u043d\u043e\u0441\u0442", + "temperature": "\u0422\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/sensibo/translations/ca.json b/homeassistant/components/sensibo/translations/ca.json index 44257e5e3f7..d1a30f07ffe 100644 --- a/homeassistant/components/sensibo/translations/ca.json +++ b/homeassistant/components/sensibo/translations/ca.json @@ -29,5 +29,22 @@ } } } + }, + "entity": { + "sensor": { + "sensitivity": { + "state": { + "n": "Normal", + "s": "Sensible" + } + }, + "smart_type": { + "state": { + "feelslike": "Sensaci\u00f3 de", + "humidity": "Humitat", + "temperature": "Temperatura" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/sensibo/translations/de.json b/homeassistant/components/sensibo/translations/de.json index 13c258d4016..35fb78b7949 100644 --- a/homeassistant/components/sensibo/translations/de.json +++ b/homeassistant/components/sensibo/translations/de.json @@ -29,5 +29,22 @@ } } } + }, + "entity": { + "sensor": { + "sensitivity": { + "state": { + "n": "Normal", + "s": "Empfindlich" + } + }, + "smart_type": { + "state": { + "feelslike": "F\u00fchlt sich an wie", + "humidity": "Luftfeuchtigkeit", + "temperature": "Temperatur" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/sensibo/translations/el.json b/homeassistant/components/sensibo/translations/el.json index 4b5e30f94be..022cf65ea6d 100644 --- a/homeassistant/components/sensibo/translations/el.json +++ b/homeassistant/components/sensibo/translations/el.json @@ -29,5 +29,22 @@ } } } + }, + "entity": { + "sensor": { + "sensitivity": { + "state": { + "n": "\u039a\u03b1\u03bd\u03bf\u03bd\u03b9\u03ba\u03cc", + "s": "\u0395\u03c5\u03b1\u03af\u03c3\u03b8\u03b7\u03c4\u03bf" + } + }, + "smart_type": { + "state": { + "feelslike": "\u0391\u03af\u03c3\u03b8\u03b7\u03c3\u03b7 \u03c3\u03b1\u03bd", + "humidity": "\u03a5\u03b3\u03c1\u03b1\u03c3\u03af\u03b1", + "temperature": "\u0398\u03b5\u03c1\u03bc\u03bf\u03ba\u03c1\u03b1\u03c3\u03af\u03b1" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/sensibo/translations/en.json b/homeassistant/components/sensibo/translations/en.json index 3b73823a2f7..14a399f63a5 100644 --- a/homeassistant/components/sensibo/translations/en.json +++ b/homeassistant/components/sensibo/translations/en.json @@ -29,5 +29,22 @@ } } } + }, + "entity": { + "sensor": { + "sensitivity": { + "state": { + "n": "Normal", + "s": "Sensitive" + } + }, + "smart_type": { + "state": { + "feelslike": "Feels like", + "humidity": "Humidity", + "temperature": "Temperature" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/sensibo/translations/es.json b/homeassistant/components/sensibo/translations/es.json index 3bc6910024d..b295bc24418 100644 --- a/homeassistant/components/sensibo/translations/es.json +++ b/homeassistant/components/sensibo/translations/es.json @@ -29,5 +29,22 @@ } } } + }, + "entity": { + "sensor": { + "sensitivity": { + "state": { + "n": "Normal", + "s": "Sensible" + } + }, + "smart_type": { + "state": { + "feelslike": "Se siente como", + "humidity": "Humedad", + "temperature": "Temperatura" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/sensibo/translations/et.json b/homeassistant/components/sensibo/translations/et.json index 803dcc01600..01feb3b925c 100644 --- a/homeassistant/components/sensibo/translations/et.json +++ b/homeassistant/components/sensibo/translations/et.json @@ -29,5 +29,22 @@ } } } + }, + "entity": { + "sensor": { + "sensitivity": { + "state": { + "n": "Tavaline", + "s": "Tundlik" + } + }, + "smart_type": { + "state": { + "feelslike": "Tundub nagu", + "humidity": "Niiskus", + "temperature": "Temperatuur" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/sensibo/translations/hu.json b/homeassistant/components/sensibo/translations/hu.json index 575fc6b7128..27ab4c103bd 100644 --- a/homeassistant/components/sensibo/translations/hu.json +++ b/homeassistant/components/sensibo/translations/hu.json @@ -29,5 +29,22 @@ } } } + }, + "entity": { + "sensor": { + "sensitivity": { + "state": { + "n": "Norm\u00e1l", + "s": "\u00c9rz\u00e9keny" + } + }, + "smart_type": { + "state": { + "feelslike": "\u00c9rz\u00e9sre", + "humidity": "P\u00e1ratartalom", + "temperature": "H\u0151m\u00e9rs\u00e9klet" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/sensibo/translations/id.json b/homeassistant/components/sensibo/translations/id.json index 64558ebb852..5368bc42d31 100644 --- a/homeassistant/components/sensibo/translations/id.json +++ b/homeassistant/components/sensibo/translations/id.json @@ -29,5 +29,22 @@ } } } + }, + "entity": { + "sensor": { + "sensitivity": { + "state": { + "n": "Normal", + "s": "Sensitif" + } + }, + "smart_type": { + "state": { + "feelslike": "Terasa seperti", + "humidity": "Kelembaban", + "temperature": "Suhu" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/sensibo/translations/it.json b/homeassistant/components/sensibo/translations/it.json index 8b34b5eaf50..7676d85b5bd 100644 --- a/homeassistant/components/sensibo/translations/it.json +++ b/homeassistant/components/sensibo/translations/it.json @@ -29,5 +29,22 @@ } } } + }, + "entity": { + "sensor": { + "sensitivity": { + "state": { + "n": "Normale", + "s": "Sensibile" + } + }, + "smart_type": { + "state": { + "feelslike": "Percepita", + "humidity": "Umidit\u00e0", + "temperature": "Temperatura" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/sensibo/translations/ja.json b/homeassistant/components/sensibo/translations/ja.json index d961099628a..a537c9a5bf1 100644 --- a/homeassistant/components/sensibo/translations/ja.json +++ b/homeassistant/components/sensibo/translations/ja.json @@ -29,5 +29,15 @@ } } } + }, + "entity": { + "sensor": { + "smart_type": { + "state": { + "humidity": "\u6e7f\u5ea6", + "temperature": "\u6e29\u5ea6" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/sensibo/translations/ko.json b/homeassistant/components/sensibo/translations/ko.json index 7f03247bb3d..12bb7e41180 100644 --- a/homeassistant/components/sensibo/translations/ko.json +++ b/homeassistant/components/sensibo/translations/ko.json @@ -1,7 +1,25 @@ { "config": { + "abort": { + "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" + }, "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "no_devices": "\uac80\uc0c9\ub41c \uae30\uae30 \uc5c6\uc74c" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "API \ud0a4" + } + }, + "user": { + "data": { + "api_key": "API \ud0a4" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/sensibo/translations/lb.json b/homeassistant/components/sensibo/translations/lb.json new file mode 100644 index 00000000000..4c830ecb94b --- /dev/null +++ b/homeassistant/components/sensibo/translations/lb.json @@ -0,0 +1,7 @@ +{ + "config": { + "error": { + "no_username": "Benotzernumm konnt net erm\u00ebttelt ginn" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sensibo/translations/nl.json b/homeassistant/components/sensibo/translations/nl.json index f853edc6ac6..9d8d0cd9871 100644 --- a/homeassistant/components/sensibo/translations/nl.json +++ b/homeassistant/components/sensibo/translations/nl.json @@ -29,5 +29,20 @@ } } } + }, + "entity": { + "sensor": { + "sensitivity": { + "state": { + "n": "Normaal" + } + }, + "smart_type": { + "state": { + "humidity": "Vochtigheid", + "temperature": "Temperatuur" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/sensibo/translations/no.json b/homeassistant/components/sensibo/translations/no.json index ebd4cb77b48..1adea0f007d 100644 --- a/homeassistant/components/sensibo/translations/no.json +++ b/homeassistant/components/sensibo/translations/no.json @@ -29,5 +29,22 @@ } } } + }, + "entity": { + "sensor": { + "sensitivity": { + "state": { + "n": "Normal", + "s": "F\u00f8lsom" + } + }, + "smart_type": { + "state": { + "feelslike": "F\u00f8les som", + "humidity": "Fuktighet", + "temperature": "Temperatur" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/sensibo/translations/pl.json b/homeassistant/components/sensibo/translations/pl.json index bd42df1e2fe..c52fb671aad 100644 --- a/homeassistant/components/sensibo/translations/pl.json +++ b/homeassistant/components/sensibo/translations/pl.json @@ -29,5 +29,22 @@ } } } + }, + "entity": { + "sensor": { + "sensitivity": { + "state": { + "n": "normalna", + "s": "wysoka" + } + }, + "smart_type": { + "state": { + "feelslike": "odczuwalna", + "humidity": "wilgotno\u015b\u0107", + "temperature": "temperatura" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/sensibo/translations/pt-BR.json b/homeassistant/components/sensibo/translations/pt-BR.json index 0429945137c..506417c1da6 100644 --- a/homeassistant/components/sensibo/translations/pt-BR.json +++ b/homeassistant/components/sensibo/translations/pt-BR.json @@ -29,5 +29,22 @@ } } } + }, + "entity": { + "sensor": { + "sensitivity": { + "state": { + "n": "Normal", + "s": "Sens\u00edvel" + } + }, + "smart_type": { + "state": { + "feelslike": "Parece que", + "humidity": "Umidade", + "temperature": "Temperatura" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/sensibo/translations/pt.json b/homeassistant/components/sensibo/translations/pt.json index 56abfd08b05..5f6648292cb 100644 --- a/homeassistant/components/sensibo/translations/pt.json +++ b/homeassistant/components/sensibo/translations/pt.json @@ -9,7 +9,7 @@ "api_key": "Chave da API" }, "data_description": { - "api_key": "Siga a documenta\u00e7\u00e3o para obter uma nova chave de API." + "api_key": "Siga a documenta\u00e7\u00e3o para obter sua chave de API." } }, "user": { diff --git a/homeassistant/components/sensibo/translations/ru.json b/homeassistant/components/sensibo/translations/ru.json index b478b6095f8..90da8687e85 100644 --- a/homeassistant/components/sensibo/translations/ru.json +++ b/homeassistant/components/sensibo/translations/ru.json @@ -29,5 +29,22 @@ } } } + }, + "entity": { + "sensor": { + "sensitivity": { + "state": { + "n": "\u041d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u044b\u0439", + "s": "\u0427\u0443\u0432\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439" + } + }, + "smart_type": { + "state": { + "feelslike": "\u041e\u0449\u0443\u0449\u0430\u0435\u0442\u0441\u044f \u043a\u0430\u043a", + "humidity": "\u0412\u043b\u0430\u0436\u043d\u043e\u0441\u0442\u044c", + "temperature": "\u0422\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/sensibo/translations/sk.json b/homeassistant/components/sensibo/translations/sk.json index cc38d3ef6f5..e70216e0784 100644 --- a/homeassistant/components/sensibo/translations/sk.json +++ b/homeassistant/components/sensibo/translations/sk.json @@ -29,5 +29,22 @@ } } } + }, + "entity": { + "sensor": { + "sensitivity": { + "state": { + "n": "Norm\u00e1lne", + "s": "Citliv\u00e9" + } + }, + "smart_type": { + "state": { + "feelslike": "Pocitovo", + "humidity": "Vlhkos\u0165", + "temperature": "Teplota" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/sensibo/translations/zh-Hant.json b/homeassistant/components/sensibo/translations/zh-Hant.json index b40fad85fad..3c144345f3d 100644 --- a/homeassistant/components/sensibo/translations/zh-Hant.json +++ b/homeassistant/components/sensibo/translations/zh-Hant.json @@ -29,5 +29,22 @@ } } } + }, + "entity": { + "sensor": { + "sensitivity": { + "state": { + "n": "\u6b63\u5e38", + "s": "\u654f\u611f" + } + }, + "smart_type": { + "state": { + "feelslike": "\u9ad4\u611f", + "humidity": "\u6fd5\u5ea6", + "temperature": "\u6eab\u5ea6" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/sensirion_ble/sensor.py b/homeassistant/components/sensirion_ble/sensor.py index 3612b25f341..2af5808fa56 100644 --- a/homeassistant/components/sensirion_ble/sensor.py +++ b/homeassistant/components/sensirion_ble/sensor.py @@ -11,7 +11,7 @@ from sensor_state_data import ( Units, ) -from homeassistant import config_entries, const +from homeassistant import config_entries from homeassistant.components.bluetooth.passive_update_processor import ( PassiveBluetoothDataProcessor, PassiveBluetoothDataUpdate, @@ -25,6 +25,11 @@ from homeassistant.components.sensor import ( SensorEntityDescription, SensorStateClass, ) +from homeassistant.const import ( + CONCENTRATION_PARTS_PER_MILLION, + PERCENTAGE, + UnitOfTemperature, +) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.sensor import sensor_device_info_to_hass_device_info @@ -40,19 +45,19 @@ SENSOR_DESCRIPTIONS: dict[ ): SensorEntityDescription( key=f"{SSDSensorDeviceClass.CO2}_{Units.CONCENTRATION_PARTS_PER_MILLION}", device_class=SensorDeviceClass.CO2, - native_unit_of_measurement=const.CONCENTRATION_PARTS_PER_MILLION, + native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION, state_class=SensorStateClass.MEASUREMENT, ), (SSDSensorDeviceClass.HUMIDITY, Units.PERCENTAGE): SensorEntityDescription( key=f"{SSDSensorDeviceClass.HUMIDITY}_{Units.PERCENTAGE}", device_class=SensorDeviceClass.HUMIDITY, - native_unit_of_measurement=const.PERCENTAGE, + native_unit_of_measurement=PERCENTAGE, state_class=SensorStateClass.MEASUREMENT, ), (SSDSensorDeviceClass.TEMPERATURE, Units.TEMP_CELSIUS): SensorEntityDescription( key=f"{SSDSensorDeviceClass.TEMPERATURE}_{Units.TEMP_CELSIUS}", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=const.TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, ), } diff --git a/homeassistant/components/sensor/__init__.py b/homeassistant/components/sensor/__init__.py index a1a7e9ce1a7..7beac83f059 100644 --- a/homeassistant/components/sensor/__init__.py +++ b/homeassistant/components/sensor/__init__.py @@ -16,6 +16,8 @@ import voluptuous as vol from homeassistant.backports.enum import StrEnum from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( # noqa: F401, pylint: disable=[hass-deprecated-import] + CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + CONCENTRATION_PARTS_PER_MILLION, CONF_UNIT_OF_MEASUREMENT, DEVICE_CLASS_AQI, DEVICE_CLASS_BATTERY, @@ -45,9 +47,30 @@ from homeassistant.const import ( # noqa: F401, pylint: disable=[hass-deprecate DEVICE_CLASS_TIMESTAMP, DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS, DEVICE_CLASS_VOLTAGE, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, - TEMP_KELVIN, + LIGHT_LUX, + PERCENTAGE, + POWER_VOLT_AMPERE_REACTIVE, + SIGNAL_STRENGTH_DECIBELS, + SIGNAL_STRENGTH_DECIBELS_MILLIWATT, + UnitOfApparentPower, + UnitOfDataRate, + UnitOfElectricCurrent, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfFrequency, + UnitOfInformation, + UnitOfIrradiance, + UnitOfLength, + UnitOfMass, + UnitOfPower, + UnitOfPrecipitationDepth, + UnitOfPressure, + UnitOfSoundPressure, + UnitOfSpeed, + UnitOfTemperature, + UnitOfTime, + UnitOfVolume, + UnitOfVolumetricFlux, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import entity_registry as er @@ -63,7 +86,11 @@ from homeassistant.helpers.typing import ConfigType, StateType from homeassistant.util import dt as dt_util from homeassistant.util.unit_conversion import ( BaseUnitConverter, + DataRateConverter, DistanceConverter, + ElectricCurrentConverter, + ElectricPotentialConverter, + InformationConverter, MassConverter, PressureConverter, SpeedConverter, @@ -77,6 +104,7 @@ _LOGGER: Final = logging.getLogger(__name__) ATTR_LAST_RESET: Final = "last_reset" ATTR_STATE_CLASS: Final = "state_class" +ATTR_OPTIONS: Final = "options" DOMAIN: Final = "sensor" @@ -103,6 +131,14 @@ class SensorDeviceClass(StrEnum): Unit of measurement: `d`, `h`, `min`, `s` """ + ENUM = "enum" + """Enumeration. + + Provides a fixed list of options the state of the sensor can be in. + + Unit of measurement: `None` + """ + TIMESTAMP = "timestamp" """Timestamp. @@ -124,6 +160,12 @@ class SensorDeviceClass(StrEnum): Unit of measurement: `None` """ + ATMOSPHERIC_PRESSURE = "atmospheric_pressure" + """Atmospheric pressure. + + Unit of measurement: `UnitOfPressure` units + """ + BATTERY = "battery" """Percentage of battery that is left. @@ -145,7 +187,19 @@ class SensorDeviceClass(StrEnum): CURRENT = "current" """Current. - Unit of measurement: `A` + Unit of measurement: `A`, `mA` + """ + + DATA_RATE = "data_rate" + """Data rate. + + Unit of measurement: UnitOfDataRate + """ + + DATA_SIZE = "data_size" + """Data size. + + Unit of measurement: UnitOfInformation """ DISTANCE = "distance" @@ -171,7 +225,9 @@ class SensorDeviceClass(StrEnum): GAS = "gas" """Gas. - Unit of measurement: `m³`, `ft³` + Unit of measurement: + - SI / metric: `m³` + - USCS / imperial: `ft³`, `CCF` """ HUMIDITY = "humidity" @@ -183,7 +239,15 @@ class SensorDeviceClass(StrEnum): ILLUMINANCE = "illuminance" """Illuminance. - Unit of measurement: `lx`, `lm` + Unit of measurement: `lx` + """ + + IRRADIANCE = "irradiance" + """Irradiance. + + Unit of measurement: + - SI / metric: `W/m²` + - USCS / imperial: `BTU/(h⋅ft²)` """ MOISTURE = "moisture" @@ -257,8 +321,8 @@ class SensorDeviceClass(StrEnum): PRECIPITATION = "precipitation" """Precipitation. - Unit of measurement: - - SI / metric: `mm` + Unit of measurement: UnitOfPrecipitationDepth + - SI / metric: `cm`, `mm` - USCS / imperial: `in` """ @@ -292,6 +356,12 @@ class SensorDeviceClass(StrEnum): Unit of measurement: `dB`, `dBm` """ + SOUND_PRESSURE = "sound_pressure" + """Sound pressure. + + Unit of measurement: `dB`, `dBA` + """ + SPEED = "speed" """Generic speed. @@ -322,7 +392,7 @@ class SensorDeviceClass(StrEnum): VOLTAGE = "voltage" """Voltage. - Unit of measurement: `V` + Unit of measurement: `V`, `mV` """ VOLUME = "volume" @@ -330,7 +400,7 @@ class SensorDeviceClass(StrEnum): Unit of measurement: `VOLUME_*` units - SI / metric: `mL`, `L`, `m³` - - USCS / imperial: `fl. oz.`, `ft³`, `gal` (warning: volumes expressed in + - USCS / imperial: `ft³`, `CCF`, `fl. oz.`, `gal` (warning: volumes expressed in USCS/imperial units are currently assumed to be US volumes) """ @@ -339,7 +409,7 @@ class SensorDeviceClass(StrEnum): Unit of measurement: - SI / metric: `m³`, `L` - - USCS / imperial: `ft³`, `gal` (warning: volumes expressed in + - USCS / imperial: `ft³`, `CCF`, `gal` (warning: volumes expressed in USCS/imperial units are currently assumed to be US volumes) """ @@ -400,18 +470,90 @@ STATE_CLASSES: Final[list[str]] = [cls.value for cls in SensorStateClass] # Note: this needs to be aligned with frontend: OVERRIDE_SENSOR_UNITS in # `entity-registry-settings.ts` UNIT_CONVERTERS: dict[SensorDeviceClass | str | None, type[BaseUnitConverter]] = { + SensorDeviceClass.DATA_RATE: DataRateConverter, + SensorDeviceClass.DATA_SIZE: InformationConverter, SensorDeviceClass.DISTANCE: DistanceConverter, + SensorDeviceClass.CURRENT: ElectricCurrentConverter, SensorDeviceClass.GAS: VolumeConverter, SensorDeviceClass.PRECIPITATION: DistanceConverter, SensorDeviceClass.PRESSURE: PressureConverter, SensorDeviceClass.SPEED: SpeedConverter, SensorDeviceClass.TEMPERATURE: TemperatureConverter, + SensorDeviceClass.VOLTAGE: ElectricPotentialConverter, SensorDeviceClass.VOLUME: VolumeConverter, SensorDeviceClass.WATER: VolumeConverter, SensorDeviceClass.WEIGHT: MassConverter, SensorDeviceClass.WIND_SPEED: SpeedConverter, } +DEVICE_CLASS_UNITS: dict[SensorDeviceClass, set[type[StrEnum] | str | None]] = { + SensorDeviceClass.APPARENT_POWER: set(UnitOfApparentPower), + SensorDeviceClass.AQI: {None}, + SensorDeviceClass.ATMOSPHERIC_PRESSURE: set(UnitOfPressure), + SensorDeviceClass.BATTERY: {PERCENTAGE}, + SensorDeviceClass.CO: {CONCENTRATION_PARTS_PER_MILLION}, + SensorDeviceClass.CO2: {CONCENTRATION_PARTS_PER_MILLION}, + SensorDeviceClass.CURRENT: set(UnitOfElectricCurrent), + SensorDeviceClass.DATA_RATE: set(UnitOfDataRate), + SensorDeviceClass.DATA_SIZE: set(UnitOfInformation), + SensorDeviceClass.DISTANCE: set(UnitOfLength), + SensorDeviceClass.DURATION: { + UnitOfTime.DAYS, + UnitOfTime.HOURS, + UnitOfTime.MINUTES, + UnitOfTime.SECONDS, + }, + SensorDeviceClass.ENERGY: set(UnitOfEnergy), + SensorDeviceClass.FREQUENCY: set(UnitOfFrequency), + SensorDeviceClass.GAS: { + UnitOfVolume.CENTUM_CUBIC_FEET, + UnitOfVolume.CUBIC_FEET, + UnitOfVolume.CUBIC_METERS, + }, + SensorDeviceClass.HUMIDITY: {PERCENTAGE}, + SensorDeviceClass.ILLUMINANCE: {LIGHT_LUX}, + SensorDeviceClass.IRRADIANCE: set(UnitOfIrradiance), + SensorDeviceClass.MOISTURE: {PERCENTAGE}, + SensorDeviceClass.NITROGEN_DIOXIDE: {CONCENTRATION_MICROGRAMS_PER_CUBIC_METER}, + SensorDeviceClass.NITROGEN_MONOXIDE: {CONCENTRATION_MICROGRAMS_PER_CUBIC_METER}, + SensorDeviceClass.NITROUS_OXIDE: {CONCENTRATION_MICROGRAMS_PER_CUBIC_METER}, + SensorDeviceClass.OZONE: {CONCENTRATION_MICROGRAMS_PER_CUBIC_METER}, + SensorDeviceClass.PM1: {CONCENTRATION_MICROGRAMS_PER_CUBIC_METER}, + SensorDeviceClass.PM10: {CONCENTRATION_MICROGRAMS_PER_CUBIC_METER}, + SensorDeviceClass.PM25: {CONCENTRATION_MICROGRAMS_PER_CUBIC_METER}, + SensorDeviceClass.POWER_FACTOR: {PERCENTAGE}, + SensorDeviceClass.POWER: {UnitOfPower.WATT, UnitOfPower.KILO_WATT}, + SensorDeviceClass.PRECIPITATION: set(UnitOfPrecipitationDepth), + SensorDeviceClass.PRECIPITATION_INTENSITY: set(UnitOfVolumetricFlux), + SensorDeviceClass.PRESSURE: set(UnitOfPressure), + SensorDeviceClass.REACTIVE_POWER: {POWER_VOLT_AMPERE_REACTIVE}, + SensorDeviceClass.SIGNAL_STRENGTH: { + SIGNAL_STRENGTH_DECIBELS, + SIGNAL_STRENGTH_DECIBELS_MILLIWATT, + }, + SensorDeviceClass.SOUND_PRESSURE: set(UnitOfSoundPressure), + SensorDeviceClass.SPEED: set(UnitOfSpeed).union(set(UnitOfVolumetricFlux)), + SensorDeviceClass.SULPHUR_DIOXIDE: {CONCENTRATION_MICROGRAMS_PER_CUBIC_METER}, + SensorDeviceClass.TEMPERATURE: { + UnitOfTemperature.CELSIUS, + UnitOfTemperature.FAHRENHEIT, + }, + SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS: { + CONCENTRATION_MICROGRAMS_PER_CUBIC_METER + }, + SensorDeviceClass.VOLTAGE: set(UnitOfElectricPotential), + SensorDeviceClass.VOLUME: set(UnitOfVolume), + SensorDeviceClass.WATER: { + UnitOfVolume.CENTUM_CUBIC_FEET, + UnitOfVolume.CUBIC_FEET, + UnitOfVolume.CUBIC_METERS, + UnitOfVolume.GALLONS, + UnitOfVolume.LITERS, + }, + SensorDeviceClass.WEIGHT: set(UnitOfMass), + SensorDeviceClass.WIND_SPEED: set(UnitOfSpeed), +} + # mypy: disallow-any-generics @@ -441,11 +583,12 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: class SensorEntityDescription(EntityDescription): """A class that describes sensor entities.""" - device_class: SensorDeviceClass | str | None = None + device_class: SensorDeviceClass | None = None suggested_unit_of_measurement: str | None = None last_reset: datetime | None = None native_unit_of_measurement: str | None = None state_class: SensorStateClass | str | None = None + options: list[str] | None = None unit_of_measurement: None = None # Type override, use native_unit_of_measurement @@ -453,16 +596,18 @@ class SensorEntity(Entity): """Base class for sensor entities.""" entity_description: SensorEntityDescription - _attr_device_class: SensorDeviceClass | str | None + _attr_device_class: SensorDeviceClass | None _attr_last_reset: datetime | None _attr_native_unit_of_measurement: str | None _attr_native_value: StateType | date | datetime | Decimal = None + _attr_options: list[str] | None _attr_state_class: SensorStateClass | str | None _attr_state: None = None # Subclasses of SensorEntity should not set this _attr_suggested_unit_of_measurement: str | None _attr_unit_of_measurement: None = ( None # Subclasses of SensorEntity should not set this ) + _invalid_unit_of_measurement_reported = False _last_reset_reported = False _sensor_option_unit_of_measurement: str | None = None @@ -473,10 +618,19 @@ class SensorEntity(Entity): platform: EntityPlatform, parallel_updates: asyncio.Semaphore | None, ) -> None: - """Start adding an entity to a platform.""" + """Start adding an entity to a platform. + + Allows integrations to remove legacy custom unit conversion which is no longer + needed without breaking existing sensors. Only works for sensors which are in + the entity registry. + + This can be removed once core integrations have dropped unneeded custom unit + conversion. + """ super().add_to_platform_start(hass, platform, parallel_updates) - if self.unique_id is None: + # Bail out if the sensor doesn't have a unique_id or a device class + if self.unique_id is None or self.device_class is None: return registry = er.async_get(self.hass) if not ( @@ -488,24 +642,36 @@ class SensorEntity(Entity): registry_entry = registry.async_get(entity_id) assert registry_entry - # Store unit override according to automatic unit conversion rules if: - # - no unit override is stored in the entity registry - # - units have changed - # - the unit stored in the registry matches automatic unit conversion rules - # This allows integrations to drop custom unit conversion and rely on automatic - # conversion. + # If the sensor has 'unit_of_measurement' in its sensor options, the user has + # overridden the unit. + # If the sensor has 'sensor.private' in its entity options, it was added after + # automatic unit conversion was implemented. registry_unit = registry_entry.unit_of_measurement if ( - DOMAIN not in registry_entry.options - and f"{DOMAIN}.private" not in registry_entry.options - and self.unit_of_measurement != registry_unit - and (suggested_unit := self._get_initial_suggested_unit()) == registry_unit - ): - registry.async_update_entity_options( - entity_id, - f"{DOMAIN}.private", - {"suggested_unit_of_measurement": suggested_unit}, + ( + (sensor_options := registry_entry.options.get(DOMAIN)) + and CONF_UNIT_OF_MEASUREMENT in sensor_options ) + or f"{DOMAIN}.private" in registry_entry.options + or self.unit_of_measurement == registry_unit + ): + return + + # Make sure we can convert the units + if ( + (unit_converter := UNIT_CONVERTERS.get(self.device_class)) is None + or registry_unit not in unit_converter.VALID_UNITS + or self.unit_of_measurement not in unit_converter.VALID_UNITS + ): + return + + # Set suggested_unit_of_measurement to the old unit to enable automatic + # conversion + registry.async_update_entity_options( + entity_id, + f"{DOMAIN}.private", + {"suggested_unit_of_measurement": registry_unit}, + ) async def async_internal_added_to_hass(self) -> None: """Call when the sensor entity is added to hass.""" @@ -515,7 +681,7 @@ class SensorEntity(Entity): self.async_registry_entry_updated() @property - def device_class(self) -> SensorDeviceClass | str | None: + def device_class(self) -> SensorDeviceClass | None: """Return the class of this entity.""" if hasattr(self, "_attr_device_class"): return self._attr_device_class @@ -523,6 +689,15 @@ class SensorEntity(Entity): return self.entity_description.device_class return None + @property + def options(self) -> list[str] | None: + """Return a set of possible options.""" + if hasattr(self, "_attr_options"): + return self._attr_options + if hasattr(self, "entity_description"): + return self.entity_description.options + return None + @property def state_class(self) -> SensorStateClass | str | None: """Return the state class of this entity, if any.""" @@ -547,10 +722,17 @@ class SensorEntity(Entity): if state_class := self.state_class: return {ATTR_STATE_CLASS: state_class} + if options := self.options: + return {ATTR_OPTIONS: options} + return None - def _get_initial_suggested_unit(self) -> str | None: - """Return initial suggested unit of measurement.""" + def get_initial_entity_options(self) -> er.EntityOptionsType | None: + """Return initial entity options. + + These will be stored in the entity registry the first time the entity is seen, + and then never updated. + """ # Unit suggested by the integration suggested_unit_of_measurement = self.suggested_unit_of_measurement @@ -560,15 +742,6 @@ class SensorEntity(Entity): self.device_class, self.native_unit_of_measurement ) - return suggested_unit_of_measurement - - def get_initial_entity_options(self) -> er.EntityOptionsType | None: - """Return initial entity options. - - These will be stored in the entity registry the first time the entity is seen, - and then never updated. - """ - suggested_unit_of_measurement = self._get_initial_suggested_unit() if suggested_unit_of_measurement is None: return None @@ -591,11 +764,12 @@ class SensorEntity(Entity): report_issue = self._suggest_report_issue() # This should raise in Home Assistant Core 2022.5 _LOGGER.warning( - "Entity %s (%s) with state_class %s has set last_reset. Setting " - "last_reset for entities with state_class other than 'total' is " - "not supported. " - "Please update your configuration if state_class is manually " - "configured, otherwise %s", + ( + "Entity %s (%s) with state_class %s has set last_reset. Setting" + " last_reset for entities with state_class other than 'total'" + " is not supported. Please update your configuration if" + " state_class is manually configured, otherwise %s" + ), self.entity_id, type(self), self.state_class, @@ -664,7 +838,8 @@ class SensorEntity(Entity): if ( self.device_class == DEVICE_CLASS_TEMPERATURE - and native_unit_of_measurement in (TEMP_CELSIUS, TEMP_FAHRENHEIT) + and native_unit_of_measurement + in {UnitOfTemperature.CELSIUS, UnitOfTemperature.FAHRENHEIT} ): return self.hass.config.units.temperature_unit @@ -715,6 +890,45 @@ class SensorEntity(Entity): f"but provides state {value}:{type(value)} resulting in '{err}'" ) from err + # Sensors with device classes indicating a non-numeric value + # should not have a state class or unit of measurement + if device_class in { + SensorDeviceClass.DATE, + SensorDeviceClass.ENUM, + SensorDeviceClass.TIMESTAMP, + }: + if self.state_class: + raise ValueError( + f"Sensor {self.entity_id} has a state class and thus indicating " + "it has a numeric value; however, it has the non-numeric " + f"device class: {device_class}" + ) + + if unit_of_measurement: + raise ValueError( + f"Sensor {self.entity_id} has a unit of measurement and thus " + "indicating it has a numeric value; however, it has the " + f"non-numeric device class: {device_class}" + ) + + # Enum checks + if value is not None and ( + device_class == SensorDeviceClass.ENUM or self.options is not None + ): + if device_class != SensorDeviceClass.ENUM: + reason = "is missing the enum device class" + if device_class is not None: + reason = f"has device class '{device_class}' instead of 'enum'" + raise ValueError( + f"Sensor {self.entity_id} is providing enum options, but {reason}" + ) + + if (options := self.options) and value not in options: + raise ValueError( + f"Sensor {self.entity_id} provides state value '{value}', " + "which is not in the list of options provided" + ) + if ( value is not None and native_unit_of_measurement != unit_of_measurement @@ -751,6 +965,32 @@ class SensorEntity(Entity): # Round to the wanted precision value = round(value_f_new) if prec == 0 else round(value_f_new, prec) + # Validate unit of measurement used for sensors with a device class + if ( + not self._invalid_unit_of_measurement_reported + and value is not None + and device_class + and (units := DEVICE_CLASS_UNITS.get(device_class)) is not None + and native_unit_of_measurement not in units + ): + self._invalid_unit_of_measurement_reported = True + report_issue = self._suggest_report_issue() + + # This should raise in Home Assistant Core 2023.6 + _LOGGER.warning( + ( + "Entity %s (%s) is using native unit of measurement '%s' which " + "is not a valid unit for the device class ('%s') it is using; " + "Please update your configuration if your entity is manually " + "configured, otherwise %s" + ), + self.entity_id, + type(self), + native_unit_of_measurement, + device_class, + report_issue, + ) + return value def __repr__(self) -> str: @@ -840,7 +1080,7 @@ class SensorExtraStoredData(ExtraStoredData): # native_value is a dict, but does not have all values return None except DecimalInvalidOperation: - # native_value coulnd't be returned from decimal_str + # native_value couldn't be returned from decimal_str return None return cls(native_value, native_unit_of_measurement) diff --git a/homeassistant/components/sensor/device_condition.py b/homeassistant/components/sensor/device_condition.py index 0e6a629a648..dc276772340 100644 --- a/homeassistant/components/sensor/device_condition.py +++ b/homeassistant/components/sensor/device_condition.py @@ -32,16 +32,20 @@ from . import ATTR_STATE_CLASS, DOMAIN, SensorDeviceClass DEVICE_CLASS_NONE = "none" CONF_IS_APPARENT_POWER = "is_apparent_power" +CONF_IS_ATMOSPHERIC_PRESSURE = "atmospheric_pressure" CONF_IS_BATTERY_LEVEL = "is_battery_level" CONF_IS_CO = "is_carbon_monoxide" CONF_IS_CO2 = "is_carbon_dioxide" CONF_IS_CURRENT = "is_current" +CONF_IS_DATA_RATE = "is_data_rate" +CONF_IS_DATA_SIZE = "is_data_size" CONF_IS_DISTANCE = "is_distance" CONF_IS_ENERGY = "is_energy" CONF_IS_FREQUENCY = "is_frequency" CONF_IS_HUMIDITY = "is_humidity" CONF_IS_GAS = "is_gas" CONF_IS_ILLUMINANCE = "is_illuminance" +CONF_IS_IRRADIANCE = "is_irradiance" CONF_IS_MOISTURE = "is_moisture" CONF_IS_NITROGEN_DIOXIDE = "is_nitrogen_dioxide" CONF_IS_NITROGEN_MONOXIDE = "is_nitrogen_monoxide" @@ -58,6 +62,7 @@ CONF_IS_PRESSURE = "is_pressure" CONF_IS_SPEED = "is_speed" CONF_IS_REACTIVE_POWER = "is_reactive_power" CONF_IS_SIGNAL_STRENGTH = "is_signal_strength" +CONF_IS_SOUND_PRESSURE = "is_sound_pressure" CONF_IS_SULPHUR_DIOXIDE = "is_sulphur_dioxide" CONF_IS_TEMPERATURE = "is_temperature" CONF_IS_VALUE = "is_value" @@ -70,16 +75,20 @@ CONF_IS_WIND_SPEED = "is_wind_speed" ENTITY_CONDITIONS = { SensorDeviceClass.APPARENT_POWER: [{CONF_TYPE: CONF_IS_APPARENT_POWER}], + SensorDeviceClass.ATMOSPHERIC_PRESSURE: [{CONF_TYPE: CONF_IS_ATMOSPHERIC_PRESSURE}], SensorDeviceClass.BATTERY: [{CONF_TYPE: CONF_IS_BATTERY_LEVEL}], SensorDeviceClass.CO: [{CONF_TYPE: CONF_IS_CO}], SensorDeviceClass.CO2: [{CONF_TYPE: CONF_IS_CO2}], SensorDeviceClass.CURRENT: [{CONF_TYPE: CONF_IS_CURRENT}], + SensorDeviceClass.DATA_RATE: [{CONF_TYPE: CONF_IS_DATA_RATE}], + SensorDeviceClass.DATA_SIZE: [{CONF_TYPE: CONF_IS_DATA_SIZE}], SensorDeviceClass.DISTANCE: [{CONF_TYPE: CONF_IS_DISTANCE}], SensorDeviceClass.ENERGY: [{CONF_TYPE: CONF_IS_ENERGY}], SensorDeviceClass.FREQUENCY: [{CONF_TYPE: CONF_IS_FREQUENCY}], SensorDeviceClass.GAS: [{CONF_TYPE: CONF_IS_GAS}], SensorDeviceClass.HUMIDITY: [{CONF_TYPE: CONF_IS_HUMIDITY}], SensorDeviceClass.ILLUMINANCE: [{CONF_TYPE: CONF_IS_ILLUMINANCE}], + SensorDeviceClass.IRRADIANCE: [{CONF_TYPE: CONF_IS_IRRADIANCE}], SensorDeviceClass.MOISTURE: [{CONF_TYPE: CONF_IS_MOISTURE}], SensorDeviceClass.NITROGEN_DIOXIDE: [{CONF_TYPE: CONF_IS_NITROGEN_DIOXIDE}], SensorDeviceClass.NITROGEN_MONOXIDE: [{CONF_TYPE: CONF_IS_NITROGEN_MONOXIDE}], @@ -97,6 +106,7 @@ ENTITY_CONDITIONS = { SensorDeviceClass.PRESSURE: [{CONF_TYPE: CONF_IS_PRESSURE}], SensorDeviceClass.REACTIVE_POWER: [{CONF_TYPE: CONF_IS_REACTIVE_POWER}], SensorDeviceClass.SIGNAL_STRENGTH: [{CONF_TYPE: CONF_IS_SIGNAL_STRENGTH}], + SensorDeviceClass.SOUND_PRESSURE: [{CONF_TYPE: CONF_IS_SOUND_PRESSURE}], SensorDeviceClass.SPEED: [{CONF_TYPE: CONF_IS_SPEED}], SensorDeviceClass.SULPHUR_DIOXIDE: [{CONF_TYPE: CONF_IS_SULPHUR_DIOXIDE}], SensorDeviceClass.TEMPERATURE: [{CONF_TYPE: CONF_IS_TEMPERATURE}], @@ -118,16 +128,20 @@ CONDITION_SCHEMA = vol.All( vol.Required(CONF_TYPE): vol.In( [ CONF_IS_APPARENT_POWER, + CONF_IS_ATMOSPHERIC_PRESSURE, CONF_IS_BATTERY_LEVEL, CONF_IS_CO, CONF_IS_CO2, CONF_IS_CURRENT, + CONF_IS_DATA_RATE, + CONF_IS_DATA_SIZE, CONF_IS_DISTANCE, CONF_IS_ENERGY, CONF_IS_FREQUENCY, CONF_IS_GAS, CONF_IS_HUMIDITY, CONF_IS_ILLUMINANCE, + CONF_IS_IRRADIANCE, CONF_IS_MOISTURE, CONF_IS_NITROGEN_DIOXIDE, CONF_IS_NITROGEN_MONOXIDE, @@ -143,6 +157,7 @@ CONDITION_SCHEMA = vol.All( CONF_IS_PRESSURE, CONF_IS_REACTIVE_POWER, CONF_IS_SIGNAL_STRENGTH, + CONF_IS_SOUND_PRESSURE, CONF_IS_SPEED, CONF_IS_SULPHUR_DIOXIDE, CONF_IS_TEMPERATURE, diff --git a/homeassistant/components/sensor/device_trigger.py b/homeassistant/components/sensor/device_trigger.py index 2550afad2fc..721df69cc50 100644 --- a/homeassistant/components/sensor/device_trigger.py +++ b/homeassistant/components/sensor/device_trigger.py @@ -31,16 +31,20 @@ from . import ATTR_STATE_CLASS, DOMAIN, SensorDeviceClass DEVICE_CLASS_NONE = "none" CONF_APPARENT_POWER = "apparent_power" +CONF_ATMOSPHERIC_PRESSURE = "atmospheric_pressure" CONF_BATTERY_LEVEL = "battery_level" CONF_CO = "carbon_monoxide" CONF_CO2 = "carbon_dioxide" CONF_CURRENT = "current" +CONF_DATA_RATE = "data_rate" +CONF_DATA_SIZE = "data_size" CONF_DISTANCE = "distance" CONF_ENERGY = "energy" CONF_FREQUENCY = "frequency" CONF_GAS = "gas" CONF_HUMIDITY = "humidity" CONF_ILLUMINANCE = "illuminance" +CONF_IRRADIANCE = "irradiance" CONF_MOISTURE = "moisture" CONF_NITROGEN_DIOXIDE = "nitrogen_dioxide" CONF_NITROGEN_MONOXIDE = "nitrogen_monoxide" @@ -56,6 +60,7 @@ CONF_PRECIPITATION_INTENSITY = "precipitation_intensity" CONF_PRESSURE = "pressure" CONF_REACTIVE_POWER = "reactive_power" CONF_SIGNAL_STRENGTH = "signal_strength" +CONF_SOUND_PRESSURE = "sound_pressure" CONF_SPEED = "speed" CONF_SULPHUR_DIOXIDE = "sulphur_dioxide" CONF_TEMPERATURE = "temperature" @@ -69,16 +74,20 @@ CONF_WIND_SPEED = "wind_speed" ENTITY_TRIGGERS = { SensorDeviceClass.APPARENT_POWER: [{CONF_TYPE: CONF_APPARENT_POWER}], + SensorDeviceClass.ATMOSPHERIC_PRESSURE: [{CONF_TYPE: CONF_ATMOSPHERIC_PRESSURE}], SensorDeviceClass.BATTERY: [{CONF_TYPE: CONF_BATTERY_LEVEL}], SensorDeviceClass.CO: [{CONF_TYPE: CONF_CO}], SensorDeviceClass.CO2: [{CONF_TYPE: CONF_CO2}], SensorDeviceClass.CURRENT: [{CONF_TYPE: CONF_CURRENT}], + SensorDeviceClass.DATA_RATE: [{CONF_TYPE: CONF_DATA_RATE}], + SensorDeviceClass.DATA_SIZE: [{CONF_TYPE: CONF_DATA_SIZE}], SensorDeviceClass.DISTANCE: [{CONF_TYPE: CONF_DISTANCE}], SensorDeviceClass.ENERGY: [{CONF_TYPE: CONF_ENERGY}], SensorDeviceClass.FREQUENCY: [{CONF_TYPE: CONF_FREQUENCY}], SensorDeviceClass.GAS: [{CONF_TYPE: CONF_GAS}], SensorDeviceClass.HUMIDITY: [{CONF_TYPE: CONF_HUMIDITY}], SensorDeviceClass.ILLUMINANCE: [{CONF_TYPE: CONF_ILLUMINANCE}], + SensorDeviceClass.IRRADIANCE: [{CONF_TYPE: CONF_IRRADIANCE}], SensorDeviceClass.MOISTURE: [{CONF_TYPE: CONF_MOISTURE}], SensorDeviceClass.NITROGEN_DIOXIDE: [{CONF_TYPE: CONF_NITROGEN_DIOXIDE}], SensorDeviceClass.NITROGEN_MONOXIDE: [{CONF_TYPE: CONF_NITROGEN_MONOXIDE}], @@ -96,6 +105,7 @@ ENTITY_TRIGGERS = { SensorDeviceClass.PRESSURE: [{CONF_TYPE: CONF_PRESSURE}], SensorDeviceClass.REACTIVE_POWER: [{CONF_TYPE: CONF_REACTIVE_POWER}], SensorDeviceClass.SIGNAL_STRENGTH: [{CONF_TYPE: CONF_SIGNAL_STRENGTH}], + SensorDeviceClass.SOUND_PRESSURE: [{CONF_TYPE: CONF_SOUND_PRESSURE}], SensorDeviceClass.SPEED: [{CONF_TYPE: CONF_SPEED}], SensorDeviceClass.SULPHUR_DIOXIDE: [{CONF_TYPE: CONF_SULPHUR_DIOXIDE}], SensorDeviceClass.TEMPERATURE: [{CONF_TYPE: CONF_TEMPERATURE}], @@ -118,16 +128,20 @@ TRIGGER_SCHEMA = vol.All( vol.Required(CONF_TYPE): vol.In( [ CONF_APPARENT_POWER, + CONF_ATMOSPHERIC_PRESSURE, CONF_BATTERY_LEVEL, CONF_CO, CONF_CO2, CONF_CURRENT, + CONF_DATA_RATE, + CONF_DATA_SIZE, CONF_DISTANCE, CONF_ENERGY, CONF_FREQUENCY, CONF_GAS, CONF_HUMIDITY, CONF_ILLUMINANCE, + CONF_IRRADIANCE, CONF_MOISTURE, CONF_NITROGEN_DIOXIDE, CONF_NITROGEN_MONOXIDE, @@ -143,6 +157,7 @@ TRIGGER_SCHEMA = vol.All( CONF_PRESSURE, CONF_REACTIVE_POWER, CONF_SIGNAL_STRENGTH, + CONF_SOUND_PRESSURE, CONF_SPEED, CONF_SULPHUR_DIOXIDE, CONF_TEMPERATURE, diff --git a/homeassistant/components/sensor/recorder.py b/homeassistant/components/sensor/recorder.py index f4b3897492d..412ea3d4d5d 100644 --- a/homeassistant/components/sensor/recorder.py +++ b/homeassistant/components/sensor/recorder.py @@ -26,16 +26,18 @@ from homeassistant.components.recorder.models import ( from homeassistant.const import ( ATTR_UNIT_OF_MEASUREMENT, REVOLUTIONS_PER_MINUTE, - VOLUME_CUBIC_FEET, - VOLUME_CUBIC_METERS, + UnitOfIrradiance, + UnitOfSoundPressure, + UnitOfVolume, ) -from homeassistant.core import HomeAssistant, State, split_entity_id +from homeassistant.core import HomeAssistant, State, callback, split_entity_id from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.entity import entity_sources from homeassistant.util import dt as dt_util from . import ( ATTR_LAST_RESET, + ATTR_OPTIONS, ATTR_STATE_CLASS, DOMAIN, STATE_CLASS_MEASUREMENT, @@ -53,9 +55,11 @@ DEFAULT_STATISTICS = { } EQUIVALENT_UNITS = { + "BTU/(h×ft²)": UnitOfIrradiance.BTUS_PER_HOUR_SQUARE_FOOT, + "dBa": UnitOfSoundPressure.WEIGHTED_DECIBEL_A, "RPM": REVOLUTIONS_PER_MINUTE, - "ft3": VOLUME_CUBIC_FEET, - "m3": VOLUME_CUBIC_METERS, + "ft3": UnitOfVolume.CUBIC_FEET, + "m3": UnitOfVolume.CUBIC_METERS, } # Keep track of entities for which a warning about decreasing value has been logged @@ -198,9 +202,11 @@ def _normalize_states( f"({old_metadata['unit_of_measurement']})" ) _LOGGER.warning( - "The unit of %s is changing, got multiple %s, generation of long term " - "statistics will be suppressed unless the unit is stable%s. " - "Go to %s to fix this", + ( + "The unit of %s is changing, got multiple %s, generation of" + " long term statistics will be suppressed unless the unit is" + " stable%s. Go to %s to fix this" + ), entity_id, all_units, extra, @@ -222,11 +228,12 @@ def _normalize_states( if entity_id not in hass.data[WARN_UNSUPPORTED_UNIT]: hass.data[WARN_UNSUPPORTED_UNIT].add(entity_id) _LOGGER.warning( - "The unit of %s (%s) can not be converted to the unit of previously " - "compiled statistics (%s). Generation of long term statistics " - "will be suppressed unless the unit changes back to %s or a " - "compatible unit. " - "Go to %s to fix this", + ( + "The unit of %s (%s) can not be converted to the unit of" + " previously compiled statistics (%s). Generation of long term" + " statistics will be suppressed unless the unit changes back to" + " %s or a compatible unit. Go to %s to fix this" + ), entity_id, state_unit, statistics_unit, @@ -287,9 +294,11 @@ def warn_dip( if domain in ["energy", "growatt_server", "solaredge"]: return _LOGGER.warning( - "Entity %s %shas state class total_increasing, but its state is " - "not strictly increasing. Triggered by state %s (%s) with last_updated set to %s. " - "Please %s", + ( + "Entity %s %shas state class total_increasing, but its state is not" + " strictly increasing. Triggered by state %s (%s) with last_updated set" + " to %s. Please %s" + ), entity_id, f"from integration {domain} " if domain else "", state.state, @@ -307,8 +316,10 @@ def warn_negative(hass: HomeAssistant, entity_id: str, state: State) -> None: hass.data[WARN_NEGATIVE].add(entity_id) domain = entity_sources(hass).get(entity_id, {}).get("domain") _LOGGER.warning( - "Entity %s %shas state class total_increasing, but its state is " - "negative. Triggered by state %s with last_updated set to %s. Please %s", + ( + "Entity %s %shas state class total_increasing, but its state is " + "negative. Triggered by state %s with last_updated set to %s. Please %s" + ), entity_id, f"from integration {domain} " if domain else "", state.state, @@ -468,11 +479,13 @@ def _compile_statistics( # noqa: C901 if entity_id not in hass.data[WARN_UNSTABLE_UNIT]: hass.data[WARN_UNSTABLE_UNIT].add(entity_id) _LOGGER.warning( - "The unit of %s (%s) can not be converted to the unit of previously " - "compiled statistics (%s). Generation of long term statistics " - "will be suppressed unless the unit changes back to %s or a " - "compatible unit. " - "Go to %s to fix this", + ( + "The unit of %s (%s) can not be converted to the unit of" + " previously compiled statistics (%s). Generation of long" + " term statistics will be suppressed unless the unit" + " changes back to %s or a compatible unit. Go to %s to fix" + " this" + ), entity_id, statistics_unit, old_metadata[1]["unit_of_measurement"], @@ -527,13 +540,19 @@ def _compile_statistics( # noqa: C901 ): if old_state is None: _LOGGER.info( - "Compiling initial sum statistics for %s, zero point set to %s", + ( + "Compiling initial sum statistics for %s, zero point" + " set to %s" + ), entity_id, fstate, ) else: _LOGGER.info( - "Detected new cycle for %s, last_reset set to %s (old last_reset %s)", + ( + "Detected new cycle for %s, last_reset set to %s (old" + " last_reset %s)" + ), entity_id, last_reset, old_last_reset, @@ -553,8 +572,11 @@ def _compile_statistics( # noqa: C901 ): reset = True _LOGGER.info( - "Detected new cycle for %s, value dropped from %s to %s, " - "triggered by state with last_updated set to %s", + ( + "Detected new cycle for %s, value dropped from %s" + " to %s, triggered by state with last_updated set" + " to %s" + ), entity_id, new_state, state.last_updated.isoformat(), @@ -724,3 +746,9 @@ def validate_statistics( ) return validation_result + + +@callback +def exclude_attributes(hass: HomeAssistant) -> set[str]: + """Exclude attributes from being recorded in the database.""" + return {ATTR_OPTIONS} diff --git a/homeassistant/components/sensor/significant_change.py b/homeassistant/components/sensor/significant_change.py index 6ff23b43508..1a4dc65f010 100644 --- a/homeassistant/components/sensor/significant_change.py +++ b/homeassistant/components/sensor/significant_change.py @@ -6,7 +6,7 @@ from typing import Any from homeassistant.const import ( ATTR_DEVICE_CLASS, ATTR_UNIT_OF_MEASUREMENT, - TEMP_FAHRENHEIT, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.significant_change import ( @@ -44,7 +44,7 @@ def async_check_significant_change( absolute_change: float | None = None percentage_change: float | None = None if device_class == SensorDeviceClass.TEMPERATURE: - if new_attrs.get(ATTR_UNIT_OF_MEASUREMENT) == TEMP_FAHRENHEIT: + if new_attrs.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfTemperature.FAHRENHEIT: absolute_change = 1.0 else: absolute_change = 0.5 diff --git a/homeassistant/components/sensor/strings.json b/homeassistant/components/sensor/strings.json index 12c902816b1..2d536126980 100644 --- a/homeassistant/components/sensor/strings.json +++ b/homeassistant/components/sensor/strings.json @@ -3,16 +3,20 @@ "device_automation": { "condition_type": { "is_apparent_power": "Current {entity_name} apparent power", + "is_atmospheric_pressure": "Current {entity_name} atmospheric pressure", "is_battery_level": "Current {entity_name} battery level", "is_carbon_monoxide": "Current {entity_name} carbon monoxide concentration level", "is_carbon_dioxide": "Current {entity_name} carbon dioxide concentration level", "is_current": "Current {entity_name} current", + "is_data_rate": "Current {entity_name} data rate", + "is_data_size": "Current {entity_name} data size", "is_distance": "Current {entity_name} distance", "is_energy": "Current {entity_name} energy", "is_frequency": "Current {entity_name} frequency", "is_gas": "Current {entity_name} gas", "is_humidity": "Current {entity_name} humidity", "is_illuminance": "Current {entity_name} illuminance", + "is_irradiance": "Current {entity_name} irradiance", "is_moisture": "Current {entity_name} moisture", "is_nitrogen_dioxide": "Current {entity_name} nitrogen dioxide concentration level", "is_nitrogen_monoxide": "Current {entity_name} nitrogen monoxide concentration level", @@ -26,6 +30,7 @@ "is_pressure": "Current {entity_name} pressure", "is_reactive_power": "Current {entity_name} reactive power", "is_signal_strength": "Current {entity_name} signal strength", + "is_sound_pressure": "Current {entity_name} sound pressure", "is_speed": "Current {entity_name} speed", "is_sulphur_dioxide": "Current {entity_name} sulphur dioxide concentration level", "is_temperature": "Current {entity_name} temperature", @@ -38,16 +43,19 @@ }, "trigger_type": { "apparent_power": "{entity_name} apparent power changes", + "atmospheric_pressure": "{entity_name} atmospheric pressure changes", "battery_level": "{entity_name} battery level changes", "carbon_monoxide": "{entity_name} carbon monoxide concentration changes", "carbon_dioxide": "{entity_name} carbon dioxide concentration changes", "current": "{entity_name} current changes", + "data_rate": "{entity_name} data rate changes", "distance": "{entity_name} distance changes", "energy": "{entity_name} energy changes", "frequency": "{entity_name} frequency changes", "gas": "{entity_name} gas changes", "humidity": "{entity_name} humidity changes", "illuminance": "{entity_name} illuminance changes", + "irradiance": "{entity_name} irradiance changes", "moisture": "{entity_name} moisture changes", "nitrogen_dioxide": "{entity_name} nitrogen dioxide concentration changes", "nitrogen_monoxide": "{entity_name} nitrogen monoxide concentration changes", @@ -61,6 +69,7 @@ "pressure": "{entity_name} pressure changes", "reactive_power": "{entity_name} reactive power changes", "signal_strength": "{entity_name} signal strength changes", + "sound_pressure": "{entity_name} sound pressure changes", "speed": "{entity_name} speed changes", "sulphur_dioxide": "{entity_name} sulphur dioxide concentration changes", "temperature": "{entity_name} temperature changes", diff --git a/homeassistant/components/sensor/translations/ca.json b/homeassistant/components/sensor/translations/ca.json index 18517b48066..8240871d6b3 100644 --- a/homeassistant/components/sensor/translations/ca.json +++ b/homeassistant/components/sensor/translations/ca.json @@ -2,16 +2,20 @@ "device_automation": { "condition_type": { "is_apparent_power": "Pot\u00e8ncia aparent actual de {entity_name}", + "is_atmospheric_pressure": "Pressi\u00f3 atmosf\u00e8rica actual de {entity_name}", "is_battery_level": "Nivell de bateria actual de {entity_name}", "is_carbon_dioxide": "Concentraci\u00f3 actual de di\u00f2xid de carboni de {entity_name}", "is_carbon_monoxide": "Concentraci\u00f3 actual de mon\u00f2xid de carboni de {entity_name}", "is_current": "Intensitat actual de {entity_name}", + "is_data_rate": "Taxa de dades actual de {entity_name}", + "is_data_size": "Mida de dades actual de {entity_name}", "is_distance": "Dist\u00e0ncia actual de {entity_name}", "is_energy": "Energia actual de {entity_name}", "is_frequency": "Freq\u00fc\u00e8ncia actual de {entity_name}", "is_gas": "Gas actual de {entity_name}", "is_humidity": "Humitat actual de {entity_name}", "is_illuminance": "Il\u00b7luminaci\u00f3 actual de {entity_name}", + "is_irradiance": "Irradi\u00e0ncia actual de {entity_name}", "is_moisture": "Humitat actual de {entity_name}", "is_nitrogen_dioxide": "Concentraci\u00f3 actual de di\u00f2xid de nitrogen de {entity_name}", "is_nitrogen_monoxide": "Concentraci\u00f3 actual de mon\u00f2xid de nitrogen de {entity_name}", @@ -25,6 +29,7 @@ "is_pressure": "Pressi\u00f3 actual de {entity_name}", "is_reactive_power": "Pot\u00e8ncia reactiva actual de {entity_name}", "is_signal_strength": "Pot\u00e8ncia de senyal actual de {entity_name}", + "is_sound_pressure": "Pressi\u00f3 del so actual de {entity_name}", "is_speed": "Velocitat actual de {entity_name}", "is_sulphur_dioxide": "Concentraci\u00f3 actual de di\u00f2xid de sofre de {entity_name}", "is_temperature": "Temperatura actual de {entity_name}", @@ -37,16 +42,20 @@ }, "trigger_type": { "apparent_power": "Canvia la pot\u00e8ncia aparent de {entity_name}", + "atmospheric_pressure": "Canvia la pressi\u00f3 atmosf\u00e8rica de {entity_name}", "battery_level": "Canvia el nivell de bateria de {entity_name}", "carbon_dioxide": "Canvia la concentraci\u00f3 de di\u00f2xid de carboni de {entity_name}", "carbon_monoxide": "Canvia la concentraci\u00f3 de mon\u00f2xid de carboni de {entity_name}", "current": "Canvia la intensitat de {entity_name}", + "data_rate": "Canvia la taxa de dades de {entity_name}", + "data_size": "Canvia la mida de dades de {entity_name}", "distance": "Canvia la dist\u00e0ncia de {entity_name}", "energy": "Canvia l'energia de {entity_name}", "frequency": "Canvia la freq\u00fc\u00e8ncia de {entity_name}", "gas": "Canvia el gas de {entity_name}", "humidity": "Canvia la humitat de {entity_name}", "illuminance": "Canvia la il\u00b7luminaci\u00f3 de {entity_name}", + "irradiance": "Canvia la irradi\u00e0ncia de {entity_name}", "moisture": "Canvia la humitat de {entity_name}", "nitrogen_dioxide": "Canvia la concentraci\u00f3 de di\u00f2xid de nitrogen de {entity_name}", "nitrogen_monoxide": "Canvia la concentraci\u00f3 de mon\u00f2xid de nitrogen de {entity_name}", @@ -60,6 +69,7 @@ "pressure": "Canvia la pressi\u00f3 de {entity_name}", "reactive_power": "Canvia la pot\u00e8ncia reactiva de {entity_name}", "signal_strength": "Canvia la pot\u00e8ncia de senyal de {entity_name}", + "sound_pressure": "Canvia la pressi\u00f3 del so de {entity_name}", "speed": "Canvia la velocitat de {entity_name}", "sulphur_dioxide": "Canvia la concentraci\u00f3 de di\u00f2xid de sofre de {entity_name}", "temperature": "Canvia la temperatura de {entity_name}", diff --git a/homeassistant/components/sensor/translations/de.json b/homeassistant/components/sensor/translations/de.json index 3920f829bf4..2e2065570c1 100644 --- a/homeassistant/components/sensor/translations/de.json +++ b/homeassistant/components/sensor/translations/de.json @@ -2,19 +2,23 @@ "device_automation": { "condition_type": { "is_apparent_power": "Aktuelle Scheinleistung von {entity_name}", + "is_atmospheric_pressure": "Aktueller atmosph\u00e4rischer Druck von {entity_name}", "is_battery_level": "{entity_name} Batteriestand", "is_carbon_dioxide": "Aktuelle {entity_name} Kohlenstoffdioxid-Konzentration", "is_carbon_monoxide": "Aktuelle {entity_name} Kohlenstoffmonoxid-Konzentration", "is_current": "Aktueller Strom von {entity_name}", + "is_data_rate": "Aktuelle Datenrate von {entity_name}", + "is_data_size": "Aktuelle Datengr\u00f6\u00dfe von {entity_name}", "is_distance": "Aktuelle Entfernung zu {entity_name}", "is_energy": "Aktuelle Energie von {entity_name}", "is_frequency": "Aktuelle {entity_name} Frequenz", "is_gas": "Aktuelles {entity_name} Gas", "is_humidity": "{entity_name} Feuchtigkeit", "is_illuminance": "Aktuelle {entity_name} Helligkeit", + "is_irradiance": "Aktuelle Einstrahlung von {entity_name}", "is_moisture": "Aktuelle Feuchtigkeit von {entity_name}", "is_nitrogen_dioxide": "Aktuelle Stickstoffdioxid-Konzentration von {entity_name}", - "is_nitrogen_monoxide": "Aktuelle Stickstoffmonoxidkonzentration von {entity_name}", + "is_nitrogen_monoxide": "Aktuelle Stickstoffmonoxid Konzentration von {entity_name}", "is_nitrous_oxide": "Aktuelle Lachgaskonzentration von {entity_name}", "is_ozone": "Aktuelle Ozonkonzentration von {entity_name}", "is_pm1": "Aktuelle PM1-Konzentration von {entity_name}", @@ -25,6 +29,7 @@ "is_pressure": "{entity_name} Druck", "is_reactive_power": "Aktuelle Blindleistung von {entity_name}", "is_signal_strength": "Aktuelle {entity_name} Signalst\u00e4rke", + "is_sound_pressure": "Aktueller Schalldruck von {entity_name}", "is_speed": "Aktuelle Geschwindigkeit von {entity_name}", "is_sulphur_dioxide": "Aktuelle Schwefeldioxid-Konzentration von {entity_name}", "is_temperature": "Aktuelle {entity_name} Temperatur", @@ -37,16 +42,20 @@ }, "trigger_type": { "apparent_power": "{entity_name} \u00c4nderungen der Scheinleistung", + "atmospheric_pressure": "Der atmosph\u00e4rische Druck von {entity_name} \u00e4ndert sich", "battery_level": "{entity_name} Batteriestatus\u00e4nderungen", "carbon_dioxide": "{entity_name} Kohlenstoffdioxid-Konzentrations\u00e4nderung", "carbon_monoxide": "{entity_name} Kohlenstoffmonoxid-Konzentrations\u00e4nderung", "current": "{entity_name} Stromver\u00e4nderung", + "data_rate": "\u00c4nderung der Datenrate von {entity_name}", + "data_size": "\u00c4nderung der Datengr\u00f6\u00dfe von {entity_name}", "distance": "Abstand zu {entity_name} \u00e4ndert sich", "energy": "{entity_name} Energie\u00e4nderungen", "frequency": "{entity_name} Frequenz\u00e4nderungen", "gas": "{entity_name} Gas\u00e4nderungen", "humidity": "{entity_name} Feuchtigkeits\u00e4nderungen", "illuminance": "{entity_name} Helligkeits\u00e4nderungen", + "irradiance": "\u00c4nderung der Einstrahlung von {entity_name}", "moisture": "{entity_name} Feuchtigkeits\u00e4nderungen", "nitrogen_dioxide": "\u00c4nderung der Stickstoffdioxidkonzentration bei {entity_name}", "nitrogen_monoxide": "\u00c4nderung der Stickstoffmonoxid-Konzentration bei {entity_name}", @@ -60,6 +69,7 @@ "pressure": "{entity_name} Druck\u00e4nderungen", "reactive_power": "{entity_name} Blindleistung \u00e4ndert sich", "signal_strength": "{entity_name} Signalst\u00e4rke\u00e4nderungen", + "sound_pressure": "Der Schalldruck von {entity_name} \u00e4ndert sich", "speed": "Geschwindigkeit von {entity_name} \u00e4ndert sich", "sulphur_dioxide": "\u00c4nderung der Schwefeldioxidkonzentration bei {entity_name}", "temperature": "{entity_name} Temperatur\u00e4nderungen", diff --git a/homeassistant/components/sensor/translations/el.json b/homeassistant/components/sensor/translations/el.json index 6e31e95bf6b..cf4e854f3fb 100644 --- a/homeassistant/components/sensor/translations/el.json +++ b/homeassistant/components/sensor/translations/el.json @@ -2,16 +2,20 @@ "device_automation": { "condition_type": { "is_apparent_power": "\u03a4\u03c1\u03ad\u03c7\u03bf\u03c5\u03c3\u03b1 \u03c6\u03b1\u03b9\u03bd\u03bf\u03bc\u03b5\u03bd\u03b9\u03ba\u03ae \u03b9\u03c3\u03c7\u03cd\u03c2 {entity_name}", + "is_atmospheric_pressure": "\u03a4\u03c1\u03ad\u03c7\u03bf\u03c5\u03c3\u03b1 \u03b1\u03c4\u03bc\u03bf\u03c3\u03c6\u03b1\u03b9\u03c1\u03b9\u03ba\u03ae \u03c0\u03af\u03b5\u03c3\u03b7 {entity_name}", "is_battery_level": "\u03a4\u03c1\u03ad\u03c7\u03bf\u03bd \u03b5\u03c0\u03af\u03c0\u03b5\u03b4\u03bf \u03bc\u03c0\u03b1\u03c4\u03b1\u03c1\u03af\u03b1\u03c2 {entity_name}", "is_carbon_dioxide": "\u03a4\u03c1\u03ad\u03c7\u03bf\u03bd \u03b5\u03c0\u03af\u03c0\u03b5\u03b4\u03bf \u03c3\u03c5\u03b3\u03ba\u03ad\u03bd\u03c4\u03c1\u03c9\u03c3\u03b7\u03c2 \u03b4\u03b9\u03bf\u03be\u03b5\u03b9\u03b4\u03af\u03bf\u03c5 \u03c4\u03bf\u03c5 \u03ac\u03bd\u03b8\u03c1\u03b1\u03ba\u03b1 \u03c4\u03bf\u03c5 {entity_name}", "is_carbon_monoxide": "\u03a4\u03c1\u03ad\u03c7\u03bf\u03bd \u03b5\u03c0\u03af\u03c0\u03b5\u03b4\u03bf \u03c3\u03c5\u03b3\u03ba\u03ad\u03bd\u03c4\u03c1\u03c9\u03c3\u03b7\u03c2 \u03bc\u03bf\u03bd\u03bf\u03be\u03b5\u03b9\u03b4\u03af\u03bf\u03c5 \u03c4\u03bf\u03c5 \u03ac\u03bd\u03b8\u03c1\u03b1\u03ba\u03b1 \u03c4\u03bf\u03c5 {entity_name}", "is_current": "\u03a4\u03c1\u03ad\u03c7\u03bf\u03bd \u03c1\u03b5\u03cd\u03bc\u03b1 \u03b3\u03b9\u03b1 {entity_name}", + "is_data_rate": "\u03a4\u03c1\u03ad\u03c7\u03bf\u03c5\u03c3\u03b1 \u03c4\u03b1\u03c7\u03cd\u03c4\u03b7\u03c4\u03b1 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03c9\u03bd {entity_name}", + "is_data_size": "\u03a4\u03c1\u03ad\u03c7\u03bf\u03bd \u03bc\u03ad\u03b3\u03b5\u03b8\u03bf\u03c2 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03c9\u03bd {entity_name}", "is_distance": "\u03a4\u03c1\u03ad\u03c7\u03bf\u03c5\u03c3\u03b1 \u03b1\u03c0\u03cc\u03c3\u03c4\u03b1\u03c3\u03b7 {entity_name}", "is_energy": "\u03a4\u03c1\u03ad\u03c7\u03bf\u03c5\u03c3\u03b1 \u03b5\u03bd\u03ad\u03c1\u03b3\u03b5\u03b9\u03b1 {entity_name}", "is_frequency": "\u03a4\u03c1\u03ad\u03c7\u03bf\u03c5\u03c3\u03b1 \u03c3\u03c5\u03c7\u03bd\u03cc\u03c4\u03b7\u03c4\u03b1 {entity_name}", "is_gas": "\u03a4\u03c1\u03ad\u03c7\u03bf\u03bd \u03b1\u03ad\u03c1\u03b9\u03bf {entity_name}", "is_humidity": "\u03a4\u03c1\u03ad\u03c7\u03bf\u03c5\u03c3\u03b1 \u03c5\u03b3\u03c1\u03b1\u03c3\u03af\u03b1 {entity_name}", "is_illuminance": "\u03a4\u03c1\u03ad\u03c7\u03bf\u03c5\u03c3\u03b1 \u03c6\u03c9\u03c4\u03b5\u03b9\u03bd\u03cc\u03c4\u03b7\u03c4\u03b1 {entity_name}", + "is_irradiance": "\u03a4\u03c1\u03ad\u03c7\u03bf\u03c5\u03c3\u03b1 \u03b1\u03ba\u03c4\u03b9\u03bd\u03bf\u03b2\u03bf\u03bb\u03af\u03b1 {entity_name}", "is_moisture": "\u03a4\u03c1\u03ad\u03c7\u03bf\u03c5\u03c3\u03b1 \u03c5\u03b3\u03c1\u03b1\u03c3\u03af\u03b1 {entity_name}", "is_nitrogen_dioxide": "\u03a4\u03c1\u03ad\u03c7\u03bf\u03bd \u03b5\u03c0\u03af\u03c0\u03b5\u03b4\u03bf \u03c3\u03c5\u03b3\u03ba\u03ad\u03bd\u03c4\u03c1\u03c9\u03c3\u03b7\u03c2 \u03b4\u03b9\u03bf\u03be\u03b5\u03b9\u03b4\u03af\u03bf\u03c5 \u03c4\u03bf\u03c5 \u03b1\u03b6\u03ce\u03c4\u03bf\u03c5 {entity_name}", "is_nitrogen_monoxide": "\u03a4\u03c1\u03ad\u03c7\u03bf\u03bd \u03b5\u03c0\u03af\u03c0\u03b5\u03b4\u03bf \u03c3\u03c5\u03b3\u03ba\u03ad\u03bd\u03c4\u03c1\u03c9\u03c3\u03b7\u03c2 \u03bc\u03bf\u03bd\u03bf\u03be\u03b5\u03b9\u03b4\u03af\u03bf\u03c5 \u03c4\u03bf\u03c5 \u03b1\u03b6\u03ce\u03c4\u03bf\u03c5 {entity_name}", @@ -25,6 +29,7 @@ "is_pressure": "\u03a4\u03c1\u03ad\u03c7\u03bf\u03c5\u03c3\u03b1 \u03c0\u03af\u03b5\u03c3\u03b7 {entity_name}", "is_reactive_power": "\u03a4\u03c1\u03ad\u03c7\u03bf\u03c5\u03c3\u03b1 \u03ac\u03b5\u03c1\u03b3\u03b7 \u03b9\u03c3\u03c7\u03cd\u03c2 {entity_name}", "is_signal_strength": "\u03a4\u03c1\u03ad\u03c7\u03bf\u03c5\u03c3\u03b1 \u03b9\u03c3\u03c7\u03cd\u03c2 \u03c3\u03ae\u03bc\u03b1\u03c4\u03bf\u03c2 {entity_name}", + "is_sound_pressure": "\u03a4\u03c1\u03ad\u03c7\u03bf\u03c5\u03c3\u03b1 \u03b7\u03c7\u03b7\u03c4\u03b9\u03ba\u03ae \u03c0\u03af\u03b5\u03c3\u03b7 {entity_name}", "is_speed": "\u03a4\u03c1\u03ad\u03c7\u03bf\u03c5\u03c3\u03b1 \u03c4\u03b1\u03c7\u03cd\u03c4\u03b7\u03c4\u03b1 {entity_name}", "is_sulphur_dioxide": "\u03a4\u03c1\u03ad\u03c7\u03bf\u03bd \u03b5\u03c0\u03af\u03c0\u03b5\u03b4\u03bf \u03c3\u03c5\u03b3\u03ba\u03ad\u03bd\u03c4\u03c1\u03c9\u03c3\u03b7\u03c2 \u03b4\u03b9\u03bf\u03be\u03b5\u03b9\u03b4\u03af\u03bf\u03c5 \u03c4\u03bf\u03c5 \u03b8\u03b5\u03af\u03bf\u03c5 {entity_name}", "is_temperature": "\u03a4\u03c1\u03ad\u03c7\u03bf\u03c5\u03c3\u03b1 \u03b8\u03b5\u03c1\u03bc\u03bf\u03ba\u03c1\u03b1\u03c3\u03af\u03b1 {entity_name}", @@ -37,16 +42,20 @@ }, "trigger_type": { "apparent_power": "\u0395\u03bc\u03c6\u03b1\u03bd\u03b5\u03af\u03c2 \u03b1\u03bb\u03bb\u03b1\u03b3\u03ad\u03c2 \u03b9\u03c3\u03c7\u03cd\u03bf\u03c2 {entity_name}", + "atmospheric_pressure": "\u039c\u03b5\u03c4\u03b1\u03b2\u03bf\u03bb\u03ae \u03b1\u03c4\u03bc\u03bf\u03c3\u03c6\u03b1\u03b9\u03c1\u03b9\u03ba\u03ae\u03c2 \u03c0\u03af\u03b5\u03c3\u03b7\u03c2 {entity_name}", "battery_level": "\u0391\u03bb\u03bb\u03b1\u03b3\u03ae \u03b5\u03c0\u03b9\u03c0\u03ad\u03b4\u03bf\u03c5 \u03bc\u03c0\u03b1\u03c4\u03b1\u03c1\u03af\u03b1\u03c2 \u03b3\u03b9\u03b1 {entity_name}", "carbon_dioxide": "\u0397 \u03c3\u03c5\u03b3\u03ba\u03ad\u03bd\u03c4\u03c1\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03b4\u03b9\u03bf\u03be\u03b5\u03b9\u03b4\u03af\u03bf\u03c5 \u03c4\u03bf\u03c5 \u03ac\u03bd\u03b8\u03c1\u03b1\u03ba\u03b1 \u03c4\u03bf\u03c5 {entity_name} \u03b1\u03bb\u03bb\u03ac\u03b6\u03b5\u03b9", "carbon_monoxide": "\u0397 \u03c3\u03c5\u03b3\u03ba\u03ad\u03bd\u03c4\u03c1\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03bc\u03bf\u03bd\u03bf\u03be\u03b5\u03b9\u03b4\u03af\u03bf\u03c5 \u03c4\u03bf\u03c5 \u03ac\u03bd\u03b8\u03c1\u03b1\u03ba\u03b1 \u03c4\u03bf\u03c5 {entity_name} \u03b1\u03bb\u03bb\u03ac\u03b6\u03b5\u03b9", "current": "{entity_name} \u03c4\u03c1\u03ad\u03c7\u03bf\u03c5\u03c3\u03b5\u03c2 \u03b1\u03bb\u03bb\u03b1\u03b3\u03ad\u03c2", + "data_rate": "\u0391\u03bb\u03bb\u03b1\u03b3\u03ad\u03c2 \u03c1\u03c5\u03b8\u03bc\u03bf\u03cd \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03c9\u03bd {entity_name}", + "data_size": "\u0391\u03bb\u03bb\u03ac\u03b6\u03b5\u03b9 \u03c4\u03bf \u03bc\u03ad\u03b3\u03b5\u03b8\u03bf\u03c2 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03c9\u03bd {entity_name}", "distance": "\u0391\u03bb\u03bb\u03b1\u03b3\u03ae \u03b1\u03c0\u03cc\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2 {entity_name}", "energy": "\u0397 \u03b5\u03bd\u03ad\u03c1\u03b3\u03b5\u03b9\u03b1 \u03c4\u03bf\u03c5 {entity_name} \u03b1\u03bb\u03bb\u03ac\u03b6\u03b5\u03b9", "frequency": "\u0391\u03bb\u03bb\u03b1\u03b3\u03ad\u03c2 \u03c3\u03c5\u03c7\u03bd\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 {entity_name}", "gas": "{entity_name} \u03bc\u03b5\u03c4\u03b1\u03b2\u03bf\u03bb\u03ad\u03c2 \u03b1\u03b5\u03c1\u03af\u03bf\u03c5", "humidity": "\u0397 \u03c5\u03b3\u03c1\u03b1\u03c3\u03af\u03b1 \u03c4\u03bf\u03c5 {entity_name} \u03b1\u03bb\u03bb\u03ac\u03b6\u03b5\u03b9", "illuminance": "\u039f \u03c6\u03c9\u03c4\u03b9\u03c3\u03bc\u03cc\u03c2 \u03c4\u03bf\u03c5 {entity_name} \u03b1\u03bb\u03bb\u03ac\u03b6\u03b5\u03b9", + "irradiance": "\u0391\u03bb\u03bb\u03b1\u03b3\u03ad\u03c2 \u03b1\u03ba\u03c4\u03b9\u03bd\u03bf\u03b2\u03bf\u03bb\u03af\u03b1\u03c2 {entity_name}", "moisture": "{entity_name} \u03b1\u03bb\u03bb\u03ac\u03b6\u03b5\u03b9 \u03c5\u03b3\u03c1\u03b1\u03c3\u03af\u03b1", "nitrogen_dioxide": "{entity_name} \u03bc\u03b5\u03c4\u03b1\u03b2\u03bf\u03bb\u03ad\u03c2 \u03c4\u03b7\u03c2 \u03c3\u03c5\u03b3\u03ba\u03ad\u03bd\u03c4\u03c1\u03c9\u03c3\u03b7\u03c2 \u03b4\u03b9\u03bf\u03be\u03b5\u03b9\u03b4\u03af\u03bf\u03c5 \u03c4\u03bf\u03c5 \u03b1\u03b6\u03ce\u03c4\u03bf\u03c5", "nitrogen_monoxide": "{entity_name} \u03bc\u03b5\u03c4\u03b1\u03b2\u03bf\u03bb\u03ad\u03c2 \u03c4\u03b7\u03c2 \u03c3\u03c5\u03b3\u03ba\u03ad\u03bd\u03c4\u03c1\u03c9\u03c3\u03b7\u03c2 \u03bc\u03bf\u03bd\u03bf\u03be\u03b5\u03b9\u03b4\u03af\u03bf\u03c5 \u03c4\u03bf\u03c5 \u03b1\u03b6\u03ce\u03c4\u03bf\u03c5", @@ -60,6 +69,7 @@ "pressure": "\u0397 \u03c0\u03af\u03b5\u03c3\u03b7 \u03c4\u03bf\u03c5 {entity_name} \u03b1\u03bb\u03bb\u03ac\u03b6\u03b5\u03b9", "reactive_power": "\u0391\u03bb\u03bb\u03b1\u03b3\u03ad\u03c2 \u03b1\u03ad\u03c1\u03b3\u03bf\u03c5 \u03b9\u03c3\u03c7\u03cd\u03bf\u03c2 {entity_name}", "signal_strength": "\u0397 \u03b9\u03c3\u03c7\u03cd\u03c2 \u03c4\u03bf\u03c5 \u03c3\u03ae\u03bc\u03b1\u03c4\u03bf\u03c2 \u03c4\u03bf\u03c5 {entity_name} \u03b1\u03bb\u03bb\u03ac\u03b6\u03b5\u03b9", + "sound_pressure": "\u0397 \u03b7\u03c7\u03b7\u03c4\u03b9\u03ba\u03ae \u03c0\u03af\u03b5\u03c3\u03b7 {entity_name} \u03b1\u03bb\u03bb\u03ac\u03b6\u03b5\u03b9", "speed": "\u0397 \u03c4\u03b1\u03c7\u03cd\u03c4\u03b7\u03c4\u03b1 {entity_name} \u03b1\u03bb\u03bb\u03ac\u03b6\u03b5\u03b9", "sulphur_dioxide": "{entity_name} \u03bc\u03b5\u03c4\u03b1\u03b2\u03bf\u03bb\u03ad\u03c2 \u03c3\u03c4\u03b7 \u03c3\u03c5\u03b3\u03ba\u03ad\u03bd\u03c4\u03c1\u03c9\u03c3\u03b7\u03c2 \u03b4\u03b9\u03bf\u03be\u03b5\u03b9\u03b4\u03af\u03bf\u03c5 \u03c4\u03bf\u03c5 \u03b8\u03b5\u03af\u03bf\u03c5", "temperature": "\u0397 \u03b8\u03b5\u03c1\u03bc\u03bf\u03ba\u03c1\u03b1\u03c3\u03af\u03b1 \u03c4\u03bf\u03c5 {entity_name} \u03b1\u03bb\u03bb\u03ac\u03b6\u03b5\u03b9", diff --git a/homeassistant/components/sensor/translations/en.json b/homeassistant/components/sensor/translations/en.json index 7fbb6e4a336..18ca324a557 100644 --- a/homeassistant/components/sensor/translations/en.json +++ b/homeassistant/components/sensor/translations/en.json @@ -2,16 +2,20 @@ "device_automation": { "condition_type": { "is_apparent_power": "Current {entity_name} apparent power", + "is_atmospheric_pressure": "Current {entity_name} atmospheric pressure", "is_battery_level": "Current {entity_name} battery level", "is_carbon_dioxide": "Current {entity_name} carbon dioxide concentration level", "is_carbon_monoxide": "Current {entity_name} carbon monoxide concentration level", "is_current": "Current {entity_name} current", + "is_data_rate": "Current {entity_name} data rate", + "is_data_size": "Current {entity_name} data size", "is_distance": "Current {entity_name} distance", "is_energy": "Current {entity_name} energy", "is_frequency": "Current {entity_name} frequency", "is_gas": "Current {entity_name} gas", "is_humidity": "Current {entity_name} humidity", "is_illuminance": "Current {entity_name} illuminance", + "is_irradiance": "Current {entity_name} irradiance", "is_moisture": "Current {entity_name} moisture", "is_nitrogen_dioxide": "Current {entity_name} nitrogen dioxide concentration level", "is_nitrogen_monoxide": "Current {entity_name} nitrogen monoxide concentration level", @@ -25,6 +29,7 @@ "is_pressure": "Current {entity_name} pressure", "is_reactive_power": "Current {entity_name} reactive power", "is_signal_strength": "Current {entity_name} signal strength", + "is_sound_pressure": "Current {entity_name} sound pressure", "is_speed": "Current {entity_name} speed", "is_sulphur_dioxide": "Current {entity_name} sulphur dioxide concentration level", "is_temperature": "Current {entity_name} temperature", @@ -37,16 +42,20 @@ }, "trigger_type": { "apparent_power": "{entity_name} apparent power changes", + "atmospheric_pressure": "{entity_name} atmospheric pressure changes", "battery_level": "{entity_name} battery level changes", "carbon_dioxide": "{entity_name} carbon dioxide concentration changes", "carbon_monoxide": "{entity_name} carbon monoxide concentration changes", "current": "{entity_name} current changes", + "data_rate": "{entity_name} data rate changes", + "data_size": "{entity_name} data size changes", "distance": "{entity_name} distance changes", "energy": "{entity_name} energy changes", "frequency": "{entity_name} frequency changes", "gas": "{entity_name} gas changes", "humidity": "{entity_name} humidity changes", "illuminance": "{entity_name} illuminance changes", + "irradiance": "{entity_name} irradiance changes", "moisture": "{entity_name} moisture changes", "nitrogen_dioxide": "{entity_name} nitrogen dioxide concentration changes", "nitrogen_monoxide": "{entity_name} nitrogen monoxide concentration changes", @@ -60,6 +69,7 @@ "pressure": "{entity_name} pressure changes", "reactive_power": "{entity_name} reactive power changes", "signal_strength": "{entity_name} signal strength changes", + "sound_pressure": "{entity_name} sound pressure changes", "speed": "{entity_name} speed changes", "sulphur_dioxide": "{entity_name} sulphur dioxide concentration changes", "temperature": "{entity_name} temperature changes", diff --git a/homeassistant/components/sensor/translations/es.json b/homeassistant/components/sensor/translations/es.json index 7914f0ff0d3..a7cfb9bdfa9 100644 --- a/homeassistant/components/sensor/translations/es.json +++ b/homeassistant/components/sensor/translations/es.json @@ -2,16 +2,20 @@ "device_automation": { "condition_type": { "is_apparent_power": "La potencia aparente actual de {entity_name}", + "is_atmospheric_pressure": "Presi\u00f3n atmosf\u00e9rica actual de {entity_name}", "is_battery_level": "El nivel de bater\u00eda actual de {entity_name}", "is_carbon_dioxide": "El nivel de la concentraci\u00f3n de di\u00f3xido de carbono actual de {entity_name}", "is_carbon_monoxide": "El nivel de la concentraci\u00f3n de mon\u00f3xido de carbono actual de {entity_name}", "is_current": "La intensidad de corriente actual de {entity_name}", + "is_data_rate": "Velocidad de datos actual de {entity_name}", + "is_data_size": "Tama\u00f1o actual de los datos de {entity_name}", "is_distance": "Distancia actual de {entity_name}", "is_energy": "La energ\u00eda actual de {entity_name}", "is_frequency": "La frecuencia actual de {entity_name}", "is_gas": "El gas actual de {entity_name}", "is_humidity": "La humedad actual de {entity_name}", "is_illuminance": "La luminosidad actual de {entity_name}", + "is_irradiance": "La irradiaci\u00f3n actual de {entity_name}", "is_moisture": "La humedad actual de {entity_name}", "is_nitrogen_dioxide": "El nivel de la concentraci\u00f3n de di\u00f3xido de nitr\u00f3geno actual de {entity_name}", "is_nitrogen_monoxide": "El nivel de la concentraci\u00f3n de mon\u00f3xido de nitr\u00f3geno actual de {entity_name}", @@ -25,6 +29,7 @@ "is_pressure": "La presi\u00f3n actual de {entity_name}", "is_reactive_power": "La potencia reactiva actual de {entity_name}", "is_signal_strength": "La intensidad de la se\u00f1al actual de {entity_name}", + "is_sound_pressure": "Presi\u00f3n sonora actual de {entity_name}", "is_speed": "Velocidad actual de {entity_name}", "is_sulphur_dioxide": "El nivel de la concentraci\u00f3n de di\u00f3xido de azufre actual de {entity_name}", "is_temperature": "La temperatura actual de {entity_name}", @@ -37,16 +42,20 @@ }, "trigger_type": { "apparent_power": "La potencia aparente de {entity_name} cambia", + "atmospheric_pressure": "La presi\u00f3n atmosf\u00e9rica de {entity_name} cambia", "battery_level": "El nivel de bater\u00eda de {entity_name} cambia", "carbon_dioxide": "La concentraci\u00f3n de di\u00f3xido de carbono de {entity_name} cambia", "carbon_monoxide": "La concentraci\u00f3n de mon\u00f3xido de carbono de {entity_name} cambia", "current": "La intensidad de corriente de {entity_name} cambia", + "data_rate": "La velocidad de datos de {entity_name} cambia", + "data_size": "El tama\u00f1o de los datos de {entity_name} cambia", "distance": "La distancia de {entity_name} cambia", "energy": "La energ\u00eda de {entity_name} cambia", "frequency": "La frecuencia de {entity_name} cambia", "gas": "El gas de {entity_name} cambia", "humidity": "La humedad de {entity_name} cambia", "illuminance": "La luminosidad de {entity_name} cambia", + "irradiance": "La irradiaci\u00f3n de {entity_name} cambia", "moisture": "La humedad de {entity_name} cambia", "nitrogen_dioxide": "La concentraci\u00f3n de di\u00f3xido de nitr\u00f3geno de {entity_name} cambia", "nitrogen_monoxide": "La concentraci\u00f3n de mon\u00f3xido de nitr\u00f3geno de {entity_name} cambia", @@ -60,6 +69,7 @@ "pressure": "La presi\u00f3n de {entity_name} cambia", "reactive_power": "La potencia reactiva de {entity_name} cambia", "signal_strength": "La intensidad de se\u00f1al de {entity_name} cambia", + "sound_pressure": "La presi\u00f3n sonora actual de {entity_name} cambia", "speed": "La velocidad de {entity_name} cambia", "sulphur_dioxide": "La concentraci\u00f3n de di\u00f3xido de azufre de {entity_name} cambia", "temperature": "La temperatura de {entity_name} cambia", diff --git a/homeassistant/components/sensor/translations/et.json b/homeassistant/components/sensor/translations/et.json index cbc760ea929..497fb3ee613 100644 --- a/homeassistant/components/sensor/translations/et.json +++ b/homeassistant/components/sensor/translations/et.json @@ -2,16 +2,20 @@ "device_automation": { "condition_type": { "is_apparent_power": "Praegune {entity_name} n\u00e4iv v\u00f5imsus", + "is_atmospheric_pressure": "Praegune {entity_name} atmosf\u00e4\u00e4rir\u00f5hk", "is_battery_level": "Praegune {entity_name} aku tase", "is_carbon_dioxide": "{entity_name} praegune s\u00fcsihappegaasi tase", "is_carbon_monoxide": "{entity_name} praegune vingugaasi tase", "is_current": "Praegune {entity_name} voolutugevus", + "is_data_rate": "Praegune {entity_name} andmeedastuskiirus", + "is_data_size": "Praegune {entity_name} andmemaht", "is_distance": "Praegune {entity_name} kaugus", "is_energy": "Praegune {entity_name} v\u00f5imsus", "is_frequency": "Praegune {entity_name} sagedus", "is_gas": "Praegune {entity_name} gaas", "is_humidity": "Praegune {entity_name} niiskus", "is_illuminance": "Praegune {entity_name} valgustatus", + "is_irradiance": "Praegune {entity_name} kiirgustihedus", "is_moisture": "Praegune {entity_name} niiskus", "is_nitrogen_dioxide": "Praegune {entity_name} l\u00e4mmastikdioksiidi kontsentratsioonitase", "is_nitrogen_monoxide": "Praegune {entity_name} l\u00e4mmastikmonooksiidi kontsentratsioonitase", @@ -25,6 +29,7 @@ "is_pressure": "Praegune {entity_name} r\u00f5hk", "is_reactive_power": "Praegune {entity_name} reaktiivv\u00f5imsus", "is_signal_strength": "Praegune {entity_name} signaali tugevus", + "is_sound_pressure": "Praegune {entity_name} helir\u00f5hk", "is_speed": "Praegune {entity_name} kiirus", "is_sulphur_dioxide": "Praegune v\u00e4\u00e4veldioksiidi kontsentratsioonitase {entity_name}", "is_temperature": "Praegune {entity_name} temperatuur", @@ -37,16 +42,20 @@ }, "trigger_type": { "apparent_power": "{entity_name} n\u00e4iv v\u00f5imsus muutub", + "atmospheric_pressure": "{entity_name} atmosf\u00e4\u00e4rir\u00f5hk muutub", "battery_level": "{entity_name} aku tase muutub", "carbon_dioxide": "{entity_name} s\u00fcsihappegaasi tase muutus", "carbon_monoxide": "{entity_name} vingugaasi tase muutus", "current": "{entity_name} voolutugevus muutub", + "data_rate": "{entity_name} andmeedastuskiirus muutub", + "data_size": "{entity_name} andmemahu muutused", "distance": "{entity_name} kaugus muutub", "energy": "{entity_name} v\u00f5imsus muutub", "frequency": "{entity_name} sagedus muutub", "gas": "{entity_name} gaasivahetus", "humidity": "{entity_name} niiskus muutub", "illuminance": "{entity_name} valgustustugevus muutub", + "irradiance": "{entity_name} kiirgustihedus muutub", "moisture": "{entity_name} niiskus muutus", "nitrogen_dioxide": "{entity_name} l\u00e4mmastikdioksiidi kontsentratsiooni muutused", "nitrogen_monoxide": "{entity_name} l\u00e4mmastikmonooksiidi kontsentratsiooni muutused", @@ -60,6 +69,7 @@ "pressure": "{entity_name} r\u00f5hk muutub", "reactive_power": "{entity_name} reaktiivv\u00f5imsus muutub", "signal_strength": "{entity_name} signaalitugevus muutub", + "sound_pressure": "{entity_name} helir\u00f5hk muutub", "speed": "{entity_name} kiirus muutub", "sulphur_dioxide": "{entity_name} v\u00e4\u00e4veldioksiidi kontsentratsiooni muutused", "temperature": "{entity_name} temperatuur muutub", diff --git a/homeassistant/components/sensor/translations/he.json b/homeassistant/components/sensor/translations/he.json index a3dda234d0c..a5e6f6b1d9a 100644 --- a/homeassistant/components/sensor/translations/he.json +++ b/homeassistant/components/sensor/translations/he.json @@ -2,14 +2,20 @@ "device_automation": { "condition_type": { "is_apparent_power": "\u05d4\u05e2\u05d5\u05e6\u05de\u05d4 \u05d4\u05e0\u05d5\u05db\u05d7\u05d9\u05ea {entity_name} \u05de\u05e1\u05ea\u05de\u05e0\u05ea", + "is_atmospheric_pressure": "\u05dc\u05d7\u05e5 \u05d0\u05d8\u05de\u05d5\u05e1\u05e4\u05e8\u05d9 \u05e0\u05d5\u05db\u05d7\u05d9 \u05e9\u05dc {entity_name}", "is_battery_level": "\u05e8\u05de\u05ea \u05d4\u05e1\u05d5\u05dc\u05dc\u05d4 \u05d4\u05e0\u05d5\u05db\u05d7\u05d9\u05ea \u05e9\u05dc {entity_name}", + "is_carbon_dioxide": "\u05e8\u05de\u05ea \u05e8\u05d9\u05db\u05d5\u05d6 \u05d4\u05e4\u05d7\u05de\u05df \u05d4\u05d3\u05d5 \u05d7\u05de\u05e6\u05e0\u05d9 \u05d4\u05e0\u05d5\u05db\u05d7\u05d9\u05ea \u05e9\u05dc {entity_name}", + "is_carbon_monoxide": "\u05e8\u05de\u05ea \u05e8\u05d9\u05db\u05d5\u05d6 \u05d4\u05e4\u05d7\u05de\u05df \u05d4\u05d7\u05d3 \u05d7\u05de\u05e6\u05e0\u05d9 \u05d4\u05e0\u05d5\u05db\u05d7\u05d9\u05ea \u05e9\u05dc {entity_name}", "is_current": "\u05db\u05e2\u05ea {entity_name}", + "is_data_rate": "\u05e7\u05e6\u05d1 \u05d4\u05e0\u05ea\u05d5\u05e0\u05d9\u05dd \u05d4\u05e0\u05d5\u05db\u05d7\u05d9 \u05e9\u05dc {entity_name}", + "is_data_size": "\u05d2\u05d5\u05d3\u05dc \u05d4\u05e0\u05ea\u05d5\u05e0\u05d9\u05dd \u05d4\u05e0\u05d5\u05db\u05d7\u05d9 \u05e9\u05dc {entity_name}", "is_distance": "\u05de\u05e8\u05d7\u05e7 \u05e0\u05d5\u05db\u05d7\u05d9 {entity_name}", "is_energy": "\u05d0\u05e0\u05e8\u05d2\u05d9\u05d4 \u05e0\u05d5\u05db\u05d7\u05d9\u05ea {entity_name}", "is_frequency": "\u05ea\u05d3\u05e8 \u05e0\u05d5\u05db\u05d7\u05d9 {entity_name}", "is_gas": "\u05db\u05e2\u05ea {entity_name} \u05d2\u05d6", "is_humidity": "\u05dc\u05d7\u05d5\u05ea \u05e0\u05d5\u05db\u05d7\u05d9\u05ea {entity_name}", "is_illuminance": "\u05e2\u05d5\u05e6\u05de\u05ea \u05d4\u05d0\u05e8\u05d4 {entity_name} \u05e0\u05d5\u05db\u05d7\u05d9\u05ea", + "is_irradiance": "\u05e7\u05e8\u05d9\u05e0\u05d4 \u05e0\u05d5\u05db\u05d7\u05d9\u05ea \u05e9\u05dc {entity_name}", "is_moisture": "\u05d4\u05dc\u05d7\u05d5\u05ea \u05d4\u05e0\u05d5\u05db\u05d7\u05d9\u05ea {entity_name}", "is_nitrogen_dioxide": "\u05e8\u05de\u05ea \u05e8\u05d9\u05db\u05d5\u05d6 \u05d4\u05d7\u05e0\u05e7\u05df \u05d4\u05d3\u05d5-\u05d7\u05de\u05e6\u05e0\u05d9 \u05d4\u05e0\u05d5\u05db\u05d7\u05d9\u05ea {entity_name}", "is_nitrogen_monoxide": "\u05e8\u05de\u05ea \u05e8\u05d9\u05db\u05d5\u05d6 \u05d4\u05d7\u05e0\u05e7\u05df \u05d7\u05d3 \u05d7\u05de\u05e6\u05e0\u05d9 {entity_name} \u05d4\u05e0\u05d5\u05db\u05d7\u05d9\u05ea", @@ -19,28 +25,37 @@ "is_pm10": "\u05e8\u05de\u05ea \u05e8\u05d9\u05db\u05d5\u05d6 \u05d6\u05e8\u05dd {entity_name} PM10", "is_pm25": "\u05e8\u05de\u05ea \u05e8\u05d9\u05db\u05d5\u05d6 \u05d6\u05e8\u05dd {entity_name} PM2.5", "is_power": "\u05db\u05d5\u05d7 \u05e0\u05d5\u05db\u05d7\u05d9 {entity_name}", + "is_power_factor": "\u05d2\u05d5\u05e8\u05dd \u05d4\u05e1\u05e4\u05e7 \u05d6\u05e8\u05dd {entity_name}", "is_pressure": "\u05dc\u05d7\u05e5 \u05e0\u05d5\u05db\u05d7\u05d9 {entity_name}", "is_reactive_power": "\u05d4\u05e1\u05e4\u05e7 \u05ea\u05d2\u05d5\u05d1\u05ea\u05d9 \u05e0\u05d5\u05db\u05d7\u05d9 {entity_name}", "is_signal_strength": "\u05e2\u05d5\u05e6\u05de\u05ea \u05d4\u05d0\u05d5\u05ea \u05d4\u05e0\u05d5\u05db\u05d7\u05d9\u05ea {entity_name}", + "is_sound_pressure": "\u05dc\u05d7\u05e5 \u05d4\u05e7\u05d5\u05dc \u05d4\u05e0\u05d5\u05db\u05d7\u05d9 \u05e9\u05dc {entity_name}", "is_speed": "\u05de\u05d4\u05d9\u05e8\u05d5\u05ea \u05e0\u05d5\u05db\u05d7\u05d9\u05ea {entity_name}", "is_sulphur_dioxide": "\u05e8\u05de\u05ea \u05e8\u05d9\u05db\u05d5\u05d6 \u05d4\u05d2\u05d5\u05e4\u05e8\u05d9\u05ea \u05d4\u05d3\u05d5-\u05d7\u05de\u05e6\u05e0\u05d9\u05ea \u05e9\u05dc \u05d6\u05e8\u05dd {entity_name}", "is_temperature": "\u05db\u05e2\u05ea {entity_name} \u05d8\u05de\u05e4\u05e8\u05d8\u05d5\u05e8\u05d4", "is_value": "\u05e2\u05e8\u05da \u05e0\u05d5\u05db\u05d7\u05d9 {entity_name}", "is_volatile_organic_compounds": "\u05e8\u05de\u05ea \u05e8\u05d9\u05db\u05d5\u05d6 \u05d4\u05ea\u05e8\u05db\u05d5\u05d1\u05d5\u05ea \u05d4\u05d0\u05d5\u05e8\u05d2\u05e0\u05d9\u05d5\u05ea \u05d4\u05e0\u05d3\u05d9\u05e4\u05d5\u05ea \u05d4\u05e0\u05d5\u05db\u05d7\u05d9\u05d5\u05ea {entity_name}", + "is_voltage": "\u05de\u05ea\u05d7 \u05e0\u05d5\u05db\u05d7\u05d9 {entity_name}", "is_volume": "\u05e0\u05e4\u05d7 \u05e0\u05d5\u05db\u05d7\u05d9 \u05e9\u05dc {entity_name}", "is_water": "\u05de\u05d9\u05dd \u05e0\u05d5\u05db\u05d7\u05d9 {entity_name}", "is_weight": "\u05de\u05e9\u05e7\u05dc \u05e0\u05d5\u05db\u05d7\u05d9 {entity_name}" }, "trigger_type": { "apparent_power": "{entity_name} \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9 \u05d4\u05e1\u05e4\u05e7 \u05dc\u05db\u05d0\u05d5\u05e8\u05d4", + "atmospheric_pressure": "{entity_name} \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9\u05dd \u05d1\u05dc\u05d7\u05e5 \u05d4\u05d0\u05d8\u05de\u05d5\u05e1\u05e4\u05e8\u05d9", "battery_level": "{entity_name} \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9\u05dd \u05d1\u05e8\u05de\u05ea \u05d4\u05e1\u05d5\u05dc\u05dc\u05d4", + "carbon_dioxide": "{entity_name} \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9\u05dd \u05d1\u05e8\u05d9\u05db\u05d5\u05d6 \u05d4\u05e4\u05d7\u05de\u05df \u05d4\u05d3\u05d5-\u05d7\u05de\u05e6\u05e0\u05d9", + "carbon_monoxide": "{entity_name} \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9\u05dd \u05d1\u05e8\u05d9\u05db\u05d5\u05d6 \u05d7\u05d3 \u05ea\u05d7\u05de\u05d5\u05e6\u05ea \u05d4\u05e4\u05d7\u05de\u05df", "current": "{entity_name} \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9\u05dd \u05e0\u05d5\u05db\u05d7\u05d9\u05d9\u05dd", + "data_rate": "\u05e9\u05d9\u05e0\u05d5\u05d9\u05d9\u05dd \u05d1\u05e7\u05e6\u05d1 \u05d4\u05e0\u05ea\u05d5\u05e0\u05d9\u05dd {entity_name}", + "data_size": "\u05e9\u05d9\u05e0\u05d5\u05d9\u05d9\u05dd \u05d1\u05d2\u05d5\u05d3\u05dc \u05d4\u05e0\u05ea\u05d5\u05e0\u05d9\u05dd {entity_name}", "distance": "{entity_name} \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9 \u05de\u05e8\u05d7\u05e7", "energy": "{entity_name} \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9 \u05d0\u05e0\u05e8\u05d2\u05d9\u05d4", "frequency": "{entity_name} \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9 \u05ea\u05d3\u05e8\u05d9\u05dd", "gas": "{entity_name} \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9 \u05d2\u05d6", "humidity": "{entity_name} \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9 \u05dc\u05d7\u05d5\u05ea", "illuminance": "{entity_name} \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9 \u05e2\u05d5\u05e6\u05de\u05ea \u05d4\u05d0\u05e8\u05d4", + "irradiance": "\u05e9\u05d9\u05e0\u05d5\u05d9\u05d9\u05dd \u05d1\u05e7\u05e8\u05d9\u05e0\u05d4 \u05e9\u05dc {entity_name}", "moisture": "{entity_name} \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9 \u05dc\u05d7\u05d5\u05ea", "nitrogen_dioxide": "{entity_name} \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9\u05dd \u05d1\u05e8\u05d9\u05db\u05d5\u05d6 \u05d4\u05d7\u05e0\u05e7\u05df \u05d4\u05d3\u05d5-\u05d7\u05de\u05e6\u05e0\u05d9", "nitrogen_monoxide": "{entity_name} \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9\u05dd \u05d1\u05e8\u05d9\u05db\u05d5\u05d6 \u05d7\u05d3 \u05ea\u05d7\u05de\u05d5\u05e6\u05ea \u05d4\u05d7\u05e0\u05e7\u05df", @@ -54,10 +69,12 @@ "pressure": "{entity_name} \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9 \u05dc\u05d7\u05e5", "reactive_power": "{entity_name} \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9 \u05d4\u05e1\u05e4\u05e7 \u05ea\u05d2\u05d5\u05d1\u05ea\u05d9", "signal_strength": "{entity_name} \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9\u05dd \u05d1\u05e2\u05d5\u05e6\u05de\u05ea \u05d4\u05d0\u05d5\u05ea", + "sound_pressure": "{entity_name} \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9\u05dd \u05d1\u05dc\u05d7\u05e5 \u05d4\u05e7\u05d5\u05dc", "speed": "{entity_name} \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9 \u05de\u05d4\u05d9\u05e8\u05d5\u05ea", "sulphur_dioxide": "{entity_name} \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9\u05dd \u05d1\u05e8\u05d9\u05db\u05d5\u05d6 \u05d3\u05d5 \u05ea\u05d7\u05de\u05d5\u05e6\u05ea \u05d4\u05d2\u05d5\u05e4\u05e8\u05d9\u05ea", "temperature": "{entity_name} \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9 \u05d8\u05de\u05e4\u05e8\u05d8\u05d5\u05e8\u05d4", "value": "{entity_name} \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9 \u05e2\u05e8\u05da", + "volatile_organic_compounds": "{entity_name} \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9\u05dd \u05d1\u05e8\u05d9\u05db\u05d5\u05d6 \u05d4\u05ea\u05e8\u05db\u05d5\u05d1\u05d5\u05ea \u05d4\u05d0\u05d5\u05e8\u05d2\u05e0\u05d9\u05d5\u05ea \u05d4\u05e0\u05d3\u05d9\u05e4\u05d5\u05ea", "voltage": "{entity_name} \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9 \u05de\u05ea\u05d7", "volume": "{entity_name} \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9 \u05e0\u05e4\u05d7", "water": "{entity_name} \u05e9\u05d9\u05e0\u05d5\u05d9\u05d9 \u05de\u05d9\u05dd", diff --git a/homeassistant/components/sensor/translations/hu.json b/homeassistant/components/sensor/translations/hu.json index 4a124c4402b..c39e3708d35 100644 --- a/homeassistant/components/sensor/translations/hu.json +++ b/homeassistant/components/sensor/translations/hu.json @@ -2,16 +2,20 @@ "device_automation": { "condition_type": { "is_apparent_power": "Aktu\u00e1lis {entity_name} l\u00e1tsz\u00f3lagos teljes\u00edtm\u00e9ny", + "is_atmospheric_pressure": "Jelenlegi {entity_name} l\u00e9gk\u00f6ri nyom\u00e1s", "is_battery_level": "{entity_name} aktu\u00e1lis akku szintje", "is_carbon_dioxide": "Jelenlegi {entity_name} sz\u00e9n-dioxid koncentr\u00e1ci\u00f3 szint", "is_carbon_monoxide": "Jelenlegi {entity_name} sz\u00e9n-monoxid koncentr\u00e1ci\u00f3 szint", "is_current": "Jelenlegi {entity_name} \u00e1ram", + "is_data_rate": "Aktu\u00e1lis {entity_name} adat\u00e1tviteli sebess\u00e9g", + "is_data_size": "Aktu\u00e1lis {entity_name} adatmennyis\u00e9g", "is_distance": "{entity_name} aktu\u00e1lis t\u00e1vols\u00e1ga", "is_energy": "A jelenlegi {entity_name} energia", "is_frequency": "Aktu\u00e1lis {entity_name} gyakoris\u00e1g", "is_gas": "Jelenlegi {entity_name} g\u00e1z", "is_humidity": "{entity_name} aktu\u00e1lis p\u00e1ratartalma", "is_illuminance": "{entity_name} aktu\u00e1lis megvil\u00e1g\u00edt\u00e1sa", + "is_irradiance": "Jelenlegi {entity_name} besug\u00e1rz\u00e1s", "is_moisture": "{entity_name} aktu\u00e1lis nedvess\u00e9gtartalom", "is_nitrogen_dioxide": "Jelenlegi {entity_name} nitrog\u00e9n-dioxid-koncentr\u00e1ci\u00f3 szint", "is_nitrogen_monoxide": "Jelenlegi {entity_name} nitrog\u00e9n-monoxid-koncentr\u00e1ci\u00f3 szint", @@ -25,6 +29,7 @@ "is_pressure": "{entity_name} aktu\u00e1lis nyom\u00e1sa", "is_reactive_power": "Aktu\u00e1lis {entity_name} reakt\u00edv teljes\u00edtm\u00e9ny", "is_signal_strength": "{entity_name} aktu\u00e1lis jeler\u0151ss\u00e9ge", + "is_sound_pressure": "Jelenlegi {entity_name} hangnyom\u00e1s", "is_speed": "{entity_name} aktu\u00e1lis sebess\u00e9ge", "is_sulphur_dioxide": "A {entity_name} k\u00e9n-dioxid koncentr\u00e1ci\u00f3 jelenlegi szintje", "is_temperature": "{entity_name} aktu\u00e1lis h\u0151m\u00e9rs\u00e9klete", @@ -37,16 +42,20 @@ }, "trigger_type": { "apparent_power": "{entity_name} l\u00e1tsz\u00f3lagos teljes\u00edtm\u00e9ny v\u00e1ltoz\u00e1sok", + "atmospheric_pressure": "{entity_name} l\u00e9gk\u00f6ri nyom\u00e1sv\u00e1ltoz\u00e1sok", "battery_level": "{entity_name} akku szintje v\u00e1ltozik", "carbon_dioxide": "{entity_name} sz\u00e9n-dioxid koncentr\u00e1ci\u00f3ja megv\u00e1ltozik", "carbon_monoxide": "{entity_name} sz\u00e9n-monoxid koncentr\u00e1ci\u00f3ja megv\u00e1ltozik", "current": "{entity_name} aktu\u00e1lis v\u00e1ltoz\u00e1sai", + "data_rate": "{entity_name} adat\u00e1tviteli sebess\u00e9g v\u00e1ltoz\u00e1sa", + "data_size": "{entity_name} adatmennyis\u00e9g v\u00e1ltoz\u00e1s", "distance": "{entity_name} t\u00e1vols\u00e1g v\u00e1ltoz\u00e1s", "energy": "{entity_name} energiav\u00e1ltoz\u00e1sa", "frequency": "{entity_name} gyakoris\u00e1gi v\u00e1ltoz\u00e1sok", "gas": "{entity_name} g\u00e1z v\u00e1ltoz\u00e1sok", "humidity": "{entity_name} p\u00e1ratartalma v\u00e1ltozik", "illuminance": "{entity_name} megvil\u00e1g\u00edt\u00e1sa v\u00e1ltozik", + "irradiance": "{entity_name} besug\u00e1rz\u00e1si v\u00e1ltoz\u00e1sok", "moisture": "{entity_name} nedvess\u00e9gv\u00e1ltoz\u00e1s", "nitrogen_dioxide": "{entity_name} nitrog\u00e9n-dioxid koncentr\u00e1ci\u00f3 v\u00e1ltozik", "nitrogen_monoxide": "{entity_name} nitrog\u00e9n-monoxid koncentr\u00e1ci\u00f3 v\u00e1ltozik", @@ -60,6 +69,7 @@ "pressure": "{entity_name} nyom\u00e1sa v\u00e1ltozik", "reactive_power": "{entity_name} reakt\u00edv teljes\u00edtm\u00e9ny v\u00e1ltoz\u00e1sok", "signal_strength": "{entity_name} jeler\u0151ss\u00e9ge v\u00e1ltozik", + "sound_pressure": "{entity_name} hangnyom\u00e1sv\u00e1ltoz\u00e1s", "speed": "{entity_name} sebess\u00e9gv\u00e1ltoz\u00e1s", "sulphur_dioxide": "{entity_name} k\u00e9n-dioxid koncentr\u00e1ci\u00f3v\u00e1ltoz\u00e1s", "temperature": "{entity_name} h\u0151m\u00e9rs\u00e9klete v\u00e1ltozik", diff --git a/homeassistant/components/sensor/translations/id.json b/homeassistant/components/sensor/translations/id.json index 7a782767547..c231ce3b331 100644 --- a/homeassistant/components/sensor/translations/id.json +++ b/homeassistant/components/sensor/translations/id.json @@ -2,16 +2,20 @@ "device_automation": { "condition_type": { "is_apparent_power": "Daya nyata {entity_name}", + "is_atmospheric_pressure": "Tekanan atmosfer {entity_name} saat ini", "is_battery_level": "Level baterai {entity_name} saat ini", "is_carbon_dioxide": "Level konsentasi karbondioksida {entity_name} saat ini", "is_carbon_monoxide": "Level konsentasi karbonmonoksida {entity_name} saat ini", "is_current": "Arus {entity_name} saat ini", + "is_data_rate": "Laju data {entity_name} saat ini", + "is_data_size": "Ukuran data {entity_name} saat ini", "is_distance": "Jarak {entity_name} saat ini", "is_energy": "Energi {entity_name} saat ini", "is_frequency": "Frekuensi {entity_name} saat ini", "is_gas": "Gas {entity_name} saat ini", "is_humidity": "Kelembaban {entity_name} saat ini", "is_illuminance": "Pencahayaan {entity_name} saat ini", + "is_irradiance": "Radiasi {entity_name} saat ini", "is_moisture": "Pengembunan {entity_name} saat ini", "is_nitrogen_dioxide": "Tingkat konsentrasi nitrogen dioksida {entity_name} saat ini", "is_nitrogen_monoxide": "Tingkat konsentrasi nitrogen monoksida {entity_name} saat ini", @@ -25,6 +29,7 @@ "is_pressure": "Tekanan {entity_name} saat ini", "is_reactive_power": "Daya reaktif {entity_name}", "is_signal_strength": "Kekuatan sinyal {entity_name} saat ini", + "is_sound_pressure": "Tekanan suara {entity_name} saat ini", "is_speed": "Kecepatan {entity_name} saat ini", "is_sulphur_dioxide": "Tingkat konsentrasi sulfur dioksida {entity_name} saat ini", "is_temperature": "Suhu {entity_name} saat ini", @@ -37,16 +42,20 @@ }, "trigger_type": { "apparent_power": "Perubahan daya nyata {entity_name}", + "atmospheric_pressure": "Perubahan tekanan atmosfer {entity_name}", "battery_level": "Perubahan level baterai {entity_name}", "carbon_dioxide": "Perubahan konsentrasi karbondioksida {entity_name}", "carbon_monoxide": "Perubahan konsentrasi karbonmonoksida {entity_name}", "current": "Perubahan arus {entity_name}", + "data_rate": "Perubahan laju data {entity_name}", + "data_size": "Perubahan ukuran data {entity_name}", "distance": "Perubahan jarak {entity_name}", "energy": "Perubahan energi {entity_name}", "frequency": "Perubahan frekuensi {entity_name}", "gas": "Perubahan gas {entity_name}", "humidity": "Perubahan kelembaban {entity_name}", "illuminance": "Perubahan pencahayaan {entity_name}", + "irradiance": "Perubahan radiasi {entity_name}", "moisture": "Perubahan pengembunan {entity_name}", "nitrogen_dioxide": "Perubahan konsentrasi nitrogen dioksida {entity_name}", "nitrogen_monoxide": "Perubahan konsentrasi nitrogen monoksida {entity_name}", @@ -60,6 +69,7 @@ "pressure": "Perubahan tekanan {entity_name}", "reactive_power": "Perubahan daya reaktif {entity_name}", "signal_strength": "Perubahan kekuatan sinyal {entity_name}", + "sound_pressure": "Perubahan tekanan suara {entity_name}", "speed": "Perubahan kecepatan {entity_name}", "sulphur_dioxide": "Perubahan konsentrasi sulfur dioksida {entity_name}", "temperature": "Perubahan suhu {entity_name}", diff --git a/homeassistant/components/sensor/translations/it.json b/homeassistant/components/sensor/translations/it.json index edd6b85c4ff..39ac47650b5 100644 --- a/homeassistant/components/sensor/translations/it.json +++ b/homeassistant/components/sensor/translations/it.json @@ -2,16 +2,20 @@ "device_automation": { "condition_type": { "is_apparent_power": "Potenza apparente attuale di {entity_name}", + "is_atmospheric_pressure": "Pressione atmosferica attuale di {entity_name}", "is_battery_level": "Livello della batteria attuale di {entity_name}", "is_carbon_dioxide": "Livello di concentrazione di anidride carbonica attuale in {entity_name}", "is_carbon_monoxide": "Livello attuale di concentrazione di monossido di carbonio in {entity_name}", "is_current": "Corrente attuale di {entity_name}", + "is_data_rate": "Velocit\u00e0 di trasmissione dati corrente di {entity_name}", + "is_data_size": "Dimensione dati corrente di {entity_name}", "is_distance": "Distanza attuale di {entity_name}", "is_energy": "Energia attuale di {entity_name}", "is_frequency": "Frequenza attuale di {entity_name}", "is_gas": "Attuale gas di {entity_name}", "is_humidity": "Umidit\u00e0 attuale di {entity_name}", "is_illuminance": "Illuminazione attuale di {entity_name}", + "is_irradiance": "Irraggiamento attuale di {entity_name}", "is_moisture": "Umidit\u00e0 attuale di {entity_name}", "is_nitrogen_dioxide": "Attuale livello di concentrazione di biossido di azoto di {entity_name}", "is_nitrogen_monoxide": "Attuale livello di concentrazione di monossido di azoto di {entity_name}", @@ -25,6 +29,7 @@ "is_pressure": "Pressione attuale di {entity_name}", "is_reactive_power": "Potenza reattiva attuale di {entity_name}", "is_signal_strength": "Potenza del segnale attuale di {entity_name}", + "is_sound_pressure": "Pressione sonora attuale di {entity_name}", "is_speed": "Velocit\u00e0 corrente di {entity_name}", "is_sulphur_dioxide": "Attuale livello di concentrazione di anidride solforosa di {entity_name}", "is_temperature": "Temperatura attuale di {entity_name}", @@ -37,16 +42,20 @@ }, "trigger_type": { "apparent_power": "Variazioni di potenza apparente di {entity_name}", + "atmospheric_pressure": "{entity_name} variazioni della pressione atmosferica", "battery_level": "variazioni del livello di batteria di {entity_name} ", "carbon_dioxide": "Variazioni della concentrazione di anidride carbonica di {entity_name}", "carbon_monoxide": "Variazioni nella concentrazione di monossido di carbonio di {entity_name}", "current": "Variazioni di corrente di {entity_name}", + "data_rate": "{entity_name} modifiche alla velocit\u00e0 dei dati", + "data_size": "{entity_name} cambia la dimensione dei dati", "distance": "Variazioni di distanza di {entity_name}", "energy": "Variazioni di energia di {entity_name}", "frequency": "{entity_name} cambiamenti di frequenza", "gas": "Variazioni di gas di {entity_name}", "humidity": "Variazioni di umidit\u00e0 di {entity_name} ", "illuminance": "Variazioni dell'illuminazione di {entity_name}", + "irradiance": "Cambiamenti di irraggiamento di {entity_name}", "moisture": "{entity_name} cambiamenti di umidit\u00e0", "nitrogen_dioxide": "Variazioni della concentrazione di biossido di azoto di {entity_name}", "nitrogen_monoxide": "Variazioni della concentrazione di monossido di azoto di {entity_name}", @@ -60,6 +69,7 @@ "pressure": "Variazioni della pressione di {entity_name}", "reactive_power": "Variazioni di potenza reattiva di {entity_name}", "signal_strength": "Variazioni della potenza del segnale di {entity_name}", + "sound_pressure": "{entity_name} variazioni di pressione sonora", "speed": "Variazioni di velocit\u00e0 di {entity_name}", "sulphur_dioxide": "Variazioni della concentrazione di anidride solforosa di {entity_name}", "temperature": "Variazioni di temperatura di {entity_name}", diff --git a/homeassistant/components/sensor/translations/lt.json b/homeassistant/components/sensor/translations/lt.json index 3cf0e9b442d..f9197c923ba 100644 --- a/homeassistant/components/sensor/translations/lt.json +++ b/homeassistant/components/sensor/translations/lt.json @@ -1,4 +1,9 @@ { + "device_automation": { + "condition_type": { + "is_illuminance": "Dabartinis {entity_name} ap\u0161vietimas" + } + }, "state": { "_": { "off": "I\u0161jungta", diff --git a/homeassistant/components/sensor/translations/no.json b/homeassistant/components/sensor/translations/no.json index 53ef0c00942..ca1cc72e86a 100644 --- a/homeassistant/components/sensor/translations/no.json +++ b/homeassistant/components/sensor/translations/no.json @@ -2,16 +2,20 @@ "device_automation": { "condition_type": { "is_apparent_power": "N\u00e5v\u00e6rende tilsynelatende kraft for {entity_name}", + "is_atmospheric_pressure": "Gjeldende {entity_name} atmosf\u00e6risk trykk", "is_battery_level": "Gjeldende {entity_name} batteriniv\u00e5", "is_carbon_dioxide": "Gjeldende {entity_name} karbondioksidkonsentrasjonsniv\u00e5", "is_carbon_monoxide": "Gjeldende {entity_name} karbonmonoksid konsentrasjonsniv\u00e5", "is_current": "Gjeldende {entity_name} str\u00f8m", + "is_data_rate": "Gjeldende datahastighet {entity_name}", + "is_data_size": "Gjeldende datast\u00f8rrelse {entity_name}", "is_distance": "Gjeldende avstand til {entity_name}", "is_energy": "Gjeldende {entity_name} effekt", "is_frequency": "Gjeldende {entity_name} -frekvens", "is_gas": "Gjeldende {entity_name} gass", "is_humidity": "Gjeldende {entity_name} fuktighet", "is_illuminance": "Gjeldende {entity_name} belysningsstyrke", + "is_irradiance": "Gjeldende {entity_name} -bestr\u00e5ling", "is_moisture": "Gjeldende {entity_name}", "is_nitrogen_dioxide": "Gjeldende konsentrasjonsniv\u00e5 for {entity_name}", "is_nitrogen_monoxide": "Gjeldende {entity_name} nitrogenmonoksidkonsentrasjonsniv\u00e5", @@ -25,6 +29,7 @@ "is_pressure": "Gjeldende {entity_name} trykk", "is_reactive_power": "N\u00e5v\u00e6rende reaktiv effekt for {entity_name}", "is_signal_strength": "Gjeldende {entity_name} signalstyrke", + "is_sound_pressure": "Gjeldende lydtrykk for {entity_name}", "is_speed": "Gjeldende hastighet {entity_name}", "is_sulphur_dioxide": "Gjeldende konsentrasjonsniv\u00e5 for svoveldioksid for {entity_name}", "is_temperature": "Gjeldende {entity_name} temperatur", @@ -37,16 +42,20 @@ }, "trigger_type": { "apparent_power": "{entity_name} tilsynelatende kraftendringer", + "atmospheric_pressure": "{entity_name} endringer i atmosf\u00e6risk trykk", "battery_level": "{entity_name} batteriniv\u00e5 endres", "carbon_dioxide": "{entity_name} endringer i konsentrasjonen av karbondioksid", "carbon_monoxide": "{entity_name} endringer i konsentrasjonen av karbonmonoksid", "current": "{entity_name} gjeldende endringer", + "data_rate": "Datahastighetendringer {entity_name}", + "data_size": "Datast\u00f8rrelsen {entity_name} endres", "distance": "{entity_name} avstandsendringer", "energy": "{entity_name} effektendringer", "frequency": "{entity_name} frekvensendringer", "gas": "{entity_name} gass endres", "humidity": "{entity_name} fuktighets endringer", "illuminance": "{entity_name} belysningsstyrke endringer", + "irradiance": "{entity_name} bestr\u00e5lingsendringer", "moisture": "{entity_name} fuktighetsendringer", "nitrogen_dioxide": "{entity_name} nitrogendioksidkonsentrasjonsendringer", "nitrogen_monoxide": "{entity_name} nitrogenmonoksidkonsentrasjonsendringer", @@ -60,6 +69,7 @@ "pressure": "{entity_name} trykk endringer", "reactive_power": "{entity_name} endringer i reaktiv effekt", "signal_strength": "{entity_name} signalstyrkeendringer", + "sound_pressure": "{entity_name} lydtrykk endres", "speed": "{entity_name} hastighetsendringer", "sulphur_dioxide": "{entity_name} svoveldioksidkonsentrasjon endres", "temperature": "{entity_name} temperaturendringer", diff --git a/homeassistant/components/sensor/translations/pl.json b/homeassistant/components/sensor/translations/pl.json index 2accf6eaf52..d0ccf56410d 100644 --- a/homeassistant/components/sensor/translations/pl.json +++ b/homeassistant/components/sensor/translations/pl.json @@ -2,16 +2,20 @@ "device_automation": { "condition_type": { "is_apparent_power": "aktualna moc pozorna {entity_name}", + "is_atmospheric_pressure": "obecne ci\u015bnienie atmosferyczne {entity_name}", "is_battery_level": "obecny poziom na\u0142adowania baterii {entity_name}", "is_carbon_dioxide": "obecny poziom st\u0119\u017cenia dwutlenku w\u0119gla w {entity_name}", "is_carbon_monoxide": "obecny poziom st\u0119\u017cenia tlenku w\u0119gla w {entity_name}", "is_current": "obecne nat\u0119\u017cenie pr\u0105du {entity_name}", + "is_data_rate": "Obecna pr\u0119dko\u015b\u0107 transmisji danych {entity_name}", + "is_data_size": "obecny rozmiar danych {entity_name}", "is_distance": "obecna odleg\u0142o\u015b\u0107 {entity_name}", "is_energy": "obecna energia {entity_name}", "is_frequency": "obecna cz\u0119stotliwo\u015b\u0107 {entity_name}", "is_gas": "obecny poziom gazu {entity_name}", "is_humidity": "obecna wilgotno\u015b\u0107 {entity_name}", "is_illuminance": "obecne nat\u0119\u017cenie o\u015bwietlenia {entity_name}", + "is_irradiance": "obecne nat\u0119\u017cenie promieniowania {entity_name}", "is_moisture": "obecna wilgotno\u015b\u0107 {entity_name}", "is_nitrogen_dioxide": "obecny poziom st\u0119\u017cenia dwutlenku azotu {entity_name}", "is_nitrogen_monoxide": "obecny poziom st\u0119\u017cenia tlenku azotu {entity_name}", @@ -25,6 +29,7 @@ "is_pressure": "obecne ci\u015bnienie {entity_name}", "is_reactive_power": "aktualna moc bierna {entity_name}", "is_signal_strength": "obecna si\u0142a sygna\u0142u {entity_name}", + "is_sound_pressure": "obecne ci\u015bnienie akustyczne {entity_name}", "is_speed": "obecna pr\u0119dko\u015b\u0107 {entity_name}", "is_sulphur_dioxide": "obecny poziom st\u0119\u017cenia dwutlenku siarki {entity_name}", "is_temperature": "obecna temperatura {entity_name}", @@ -37,16 +42,20 @@ }, "trigger_type": { "apparent_power": "zmieni si\u0119 moc pozorna {entity_name}", + "atmospheric_pressure": "zmieni si\u0119 ci\u015bnienie atmosferyczne {entity_name}", "battery_level": "zmieni si\u0119 poziom baterii {entity_name}", "carbon_dioxide": "{entity_name} wykryje zmian\u0119 st\u0119\u017cenia dwutlenku w\u0119gla", "carbon_monoxide": "{entity_name} wykryje zmian\u0119 st\u0119\u017cenia tlenku w\u0119gla", "current": "zmieni si\u0119 nat\u0119\u017cenie pr\u0105du w {entity_name}", + "data_rate": "zmieni si\u0119 rozmiar danych {entity_name}", + "data_size": "zmieni si\u0119 rozmiar danych {entity_name}", "distance": "zmieni si\u0119 odleg\u0142o\u015b\u0107 {entity_name}", "energy": "zmieni si\u0119 energia {entity_name}", "frequency": "zmieni si\u0119 cz\u0119stotliwo\u015b\u0107 w {entity_name}", "gas": "{entity_name} wykryje zmian\u0119 poziomu gazu", "humidity": "zmieni si\u0119 wilgotno\u015b\u0107 {entity_name}", "illuminance": "zmieni si\u0119 nat\u0119\u017cenie o\u015bwietlenia {entity_name}", + "irradiance": "zmieni si\u0119 nat\u0119\u017cenie promieniowania {entity_name}", "moisture": "zmieni si\u0119 wilgotno\u015b\u0107 {entity_name}", "nitrogen_dioxide": "zmieni si\u0119 st\u0119\u017cenie dwutlenku azotu w {entity_name}", "nitrogen_monoxide": "{entity_name} wykryje zmian\u0119 st\u0119\u017cenia tlenku azotu", @@ -60,6 +69,7 @@ "pressure": "zmieni si\u0119 ci\u015bnienie {entity_name}", "reactive_power": "zmieni si\u0119 moc bierna {entity_name}", "signal_strength": "zmieni si\u0119 si\u0142a sygna\u0142u {entity_name}", + "sound_pressure": "zmieni si\u0119 ci\u015bnienie akustyczne {entity_name}", "speed": "zmieni si\u0119 pr\u0119dko\u015b\u0107 {entity_name}", "sulphur_dioxide": "{entity_name} wykryje zmian\u0119 st\u0119\u017cenia dwutlenku siarki", "temperature": "zmieni si\u0119 temperatura {entity_name}", diff --git a/homeassistant/components/sensor/translations/pt-BR.json b/homeassistant/components/sensor/translations/pt-BR.json index 6284a2dd170..50fa4194d64 100644 --- a/homeassistant/components/sensor/translations/pt-BR.json +++ b/homeassistant/components/sensor/translations/pt-BR.json @@ -2,16 +2,20 @@ "device_automation": { "condition_type": { "is_apparent_power": "Pot\u00eancia aparente atual de {entity_name}", + "is_atmospheric_pressure": "Press\u00e3o atmosf\u00e9rica atual {entity_name}", "is_battery_level": "N\u00edvel atual da bateria {entity_name}", "is_carbon_dioxide": "N\u00edvel atual de concentra\u00e7\u00e3o de di\u00f3xido de carbono de {entity_name}", "is_carbon_monoxide": "N\u00edvel de concentra\u00e7\u00e3o de mon\u00f3xido de carbono atual de {entity_name}", "is_current": "Corrente atual de {entity_name}", + "is_data_rate": "Taxa de dados atual {entity_name}", + "is_data_size": "Tamanho atual dos dados {entity_name}", "is_distance": "Dist\u00e2ncia atual de {entity_name}", "is_energy": "Energia atual de {entity_name}", "is_frequency": "Frequ\u00eancia atual de {entity_name}", "is_gas": "G\u00e1s atual de {entity_name}", "is_humidity": "Humidade atual do(a) {entity_name}", "is_illuminance": "Luminosidade atual {entity_name}", + "is_irradiance": "Irradi\u00e2ncia atual {entity_name}", "is_moisture": "Umidade atual {entity_name}", "is_nitrogen_dioxide": "N\u00edvel atual de concentra\u00e7\u00e3o de di\u00f3xido de nitrog\u00eanio de {entity_name}", "is_nitrogen_monoxide": "N\u00edvel atual de concentra\u00e7\u00e3o de mon\u00f3xido de nitrog\u00eanio de {entity_name}", @@ -25,6 +29,7 @@ "is_pressure": "Press\u00e3o atual do(a) {entity_name}", "is_reactive_power": "Pot\u00eancia reativa atual de {entity_name}", "is_signal_strength": "For\u00e7a do sinal atual do(a) {entity_name}", + "is_sound_pressure": "Press\u00e3o sonora atual {entity_name}", "is_speed": "Velocidade atual de {entity_name}", "is_sulphur_dioxide": "N\u00edvel atual de concentra\u00e7\u00e3o de di\u00f3xido de enxofre de {entity_name}", "is_temperature": "Temperatura atual do(a) {entity_name}", @@ -37,16 +42,20 @@ }, "trigger_type": { "apparent_power": "Mudan\u00e7as de poder aparentes de {entity_name}", + "atmospheric_pressure": "{entity_name} mudan\u00e7as na press\u00e3o atmosf\u00e9rica", "battery_level": "{entity_name} mudan\u00e7as no n\u00edvel da bateria", "carbon_dioxide": "Mudan\u00e7as na concentra\u00e7\u00e3o de di\u00f3xido de carbono de {entity_name}", "carbon_monoxide": "Altera\u00e7\u00f5es na concentra\u00e7\u00e3o de mon\u00f3xido de carbono de {entity_name}", "current": "Mudan\u00e7a na corrente de {entity_name}", + "data_rate": "Altera\u00e7\u00f5es na taxa de dados de {entity_name}", + "data_size": "{entity_name} altera\u00e7\u00f5es no tamanho dos dados", "distance": "Mudan\u00e7as da dist\u00e2ncia de {entity_name}", "energy": "Mudan\u00e7as na energia de {entity_name}", "frequency": "Altera\u00e7\u00f5es de frequ\u00eancia de {entity_name}", "gas": "Mudan\u00e7as de g\u00e1s de {entity_name}", "humidity": "{entity_name} mudan\u00e7as de umidade", "illuminance": "{entity_name} mudan\u00e7as de luminosidade", + "irradiance": "{entity_name} mudan\u00e7as de irradi\u00e2ncia", "moisture": "Mudan\u00e7as de umidade {entity_name}", "nitrogen_dioxide": "Mudan\u00e7as na concentra\u00e7\u00e3o de di\u00f3xido de nitrog\u00eanio de {entity_name}", "nitrogen_monoxide": "Mudan\u00e7as na concentra\u00e7\u00e3o de mon\u00f3xido de nitrog\u00eanio de {entity_name}", @@ -60,6 +69,7 @@ "pressure": "{entity_name} mudan\u00e7as de press\u00e3o", "reactive_power": "Altera\u00e7\u00f5es de pot\u00eancia reativa de {entity_name}", "signal_strength": "{entity_name} muda a for\u00e7a do sinal", + "sound_pressure": "{entity_name} mudan\u00e7as de press\u00e3o sonora", "speed": "Mudan\u00e7as de velocidade de {entity_name}", "sulphur_dioxide": "Altera\u00e7\u00f5es na concentra\u00e7\u00e3o de di\u00f3xido de enxofre de {entity_name}", "temperature": "{entity_name} mudan\u00e7as de temperatura", diff --git a/homeassistant/components/sensor/translations/ru.json b/homeassistant/components/sensor/translations/ru.json index 1961ef86ca0..e7833d31f4a 100644 --- a/homeassistant/components/sensor/translations/ru.json +++ b/homeassistant/components/sensor/translations/ru.json @@ -2,16 +2,20 @@ "device_automation": { "condition_type": { "is_apparent_power": "{entity_name} \u0438\u043c\u0435\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435", + "is_atmospheric_pressure": "{entity_name} \u0438\u043c\u0435\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435", "is_battery_level": "{entity_name} \u0438\u043c\u0435\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435", "is_carbon_dioxide": "{entity_name} \u0438\u043c\u0435\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0443\u0440\u043e\u0432\u043d\u044f \u043a\u043e\u043d\u0446\u0435\u043d\u0442\u0440\u0430\u0446\u0438\u0438 \u0443\u0433\u043b\u0435\u043a\u0438\u0441\u043b\u043e\u0433\u043e \u0433\u0430\u0437\u0430", "is_carbon_monoxide": "{entity_name} \u0438\u043c\u0435\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0443\u0440\u043e\u0432\u043d\u044f \u043a\u043e\u043d\u0446\u0435\u043d\u0442\u0440\u0430\u0446\u0438\u0438 \u0443\u0433\u0430\u0440\u043d\u043e\u0433\u043e \u0433\u0430\u0437\u0430", "is_current": "{entity_name} \u0438\u043c\u0435\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0441\u0438\u043b\u044b \u0442\u043e\u043a\u0430", + "is_data_rate": "{entity_name} \u0438\u043c\u0435\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435", + "is_data_size": "{entity_name} \u0438\u043c\u0435\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435", "is_distance": "{entity_name} \u0438\u043c\u0435\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435", "is_energy": "{entity_name} \u0438\u043c\u0435\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043c\u043e\u0449\u043d\u043e\u0441\u0442\u0438", "is_frequency": "{entity_name} \u0438\u043c\u0435\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435", "is_gas": "{entity_name} \u0438\u043c\u0435\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435", "is_humidity": "{entity_name} \u0438\u043c\u0435\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435", "is_illuminance": "{entity_name} \u0438\u043c\u0435\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435", + "is_irradiance": "{entity_name} \u0438\u043c\u0435\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435", "is_moisture": "{entity_name} \u0438\u043c\u0435\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435", "is_nitrogen_dioxide": "{entity_name} \u0438\u043c\u0435\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0443\u0440\u043e\u0432\u043d\u044f \u043a\u043e\u043d\u0446\u0435\u043d\u0442\u0440\u0430\u0446\u0438\u0438 \u0434\u0438\u043e\u043a\u0441\u0438\u0434\u0430 \u0430\u0437\u043e\u0442\u0430", "is_nitrogen_monoxide": "{entity_name} \u0438\u043c\u0435\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0443\u0440\u043e\u0432\u043d\u044f \u043a\u043e\u043d\u0446\u0435\u043d\u0442\u0440\u0430\u0446\u0438\u0438 \u043c\u043e\u043d\u043e\u043e\u043a\u0441\u0438\u0434\u0430 \u0430\u0437\u043e\u0442\u0430", @@ -25,6 +29,7 @@ "is_pressure": "{entity_name} \u0438\u043c\u0435\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435", "is_reactive_power": "{entity_name} \u0438\u043c\u0435\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435", "is_signal_strength": "{entity_name} \u0438\u043c\u0435\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435", + "is_sound_pressure": "{entity_name} \u0438\u043c\u0435\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435", "is_speed": "{entity_name} \u0438\u043c\u0435\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435", "is_sulphur_dioxide": "{entity_name} \u0438\u043c\u0435\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0443\u0440\u043e\u0432\u043d\u044f \u043a\u043e\u043d\u0446\u0435\u043d\u0442\u0440\u0430\u0446\u0438\u0438 \u0434\u0438\u043e\u043a\u0441\u0438\u0434\u0430 \u0441\u0435\u0440\u044b", "is_temperature": "{entity_name} \u0438\u043c\u0435\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435", @@ -37,16 +42,20 @@ }, "trigger_type": { "apparent_power": "{entity_name} \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u043d\u043e\u0439 \u043c\u043e\u0449\u043d\u043e\u0441\u0442\u0438", + "atmospheric_pressure": "{entity_name} \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435", "battery_level": "{entity_name} \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435", "carbon_dioxide": "{entity_name} \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435", "carbon_monoxide": "{entity_name} \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435", "current": "{entity_name} \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0441\u0438\u043b\u044b \u0442\u043e\u043a\u0430", + "data_rate": "{entity_name} \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435", + "data_size": "{entity_name} \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435", "distance": "{entity_name} \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u0442 \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0435", "energy": "{entity_name} \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043c\u043e\u0449\u043d\u043e\u0441\u0442\u0438", "frequency": "{entity_name} \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435", "gas": "{entity_name} \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435 \u0433\u0430\u0437\u0430", "humidity": "{entity_name} \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435", "illuminance": "{entity_name} \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435", + "irradiance": "{entity_name} \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435", "moisture": "{entity_name} \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435 \u0432\u043b\u0430\u0433\u0438", "nitrogen_dioxide": "{entity_name} \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043a\u043e\u043d\u0446\u0435\u043d\u0442\u0440\u0430\u0446\u0438\u0438 \u0434\u0438\u043e\u043a\u0441\u0438\u0434\u0430 \u0430\u0437\u043e\u0442\u0430", "nitrogen_monoxide": "{entity_name} \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043a\u043e\u043d\u0446\u0435\u043d\u0442\u0440\u0430\u0446\u0438\u0438 \u043c\u043e\u043d\u043e\u043e\u043a\u0441\u0438\u0434\u0430 \u0430\u0437\u043e\u0442\u0430", @@ -60,6 +69,7 @@ "pressure": "{entity_name} \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435", "reactive_power": "{entity_name} \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0440\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0439 \u043c\u043e\u0449\u043d\u043e\u0441\u0442\u0438", "signal_strength": "{entity_name} \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435", + "sound_pressure": "{entity_name} \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435", "speed": "{entity_name} \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u0442 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c", "sulphur_dioxide": "{entity_name} \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043a\u043e\u043d\u0446\u0435\u043d\u0442\u0440\u0430\u0446\u0438\u0438 \u0434\u0438\u043e\u043a\u0441\u0438\u0434\u0430 \u0441\u0435\u0440\u044b", "temperature": "{entity_name} \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435", diff --git a/homeassistant/components/sensor/translations/sk.json b/homeassistant/components/sensor/translations/sk.json index d6e664f221d..6eb25658a88 100644 --- a/homeassistant/components/sensor/translations/sk.json +++ b/homeassistant/components/sensor/translations/sk.json @@ -2,16 +2,20 @@ "device_automation": { "condition_type": { "is_apparent_power": "Aktu\u00e1lny zdanliv\u00fd v\u00fdkon {entity_name}", + "is_atmospheric_pressure": "Aktu\u00e1lny atmosf\u00e9rick\u00fd tlak {entity_name}", "is_battery_level": "Aktu\u00e1lna \u00farove\u0148 nabitia bat\u00e9rie {entity_name}", "is_carbon_dioxide": "Aktu\u00e1lna \u00farove\u0148 koncentr\u00e1cie oxidu uhli\u010dit\u00e9ho {entity_name}", "is_carbon_monoxide": "Aktu\u00e1lna \u00farove\u0148 koncentr\u00e1cie oxidu uho\u013enat\u00e9ho {entity_name}", "is_current": "Aktu\u00e1lny {entity_name} pr\u00fad", + "is_data_rate": "Aktu\u00e1lna r\u00fdchlos\u0165 prenosu d\u00e1t {entity_name}", + "is_data_size": "Aktu\u00e1lna ve\u013ekos\u0165 \u00fadajov {entity_name}", "is_distance": "Aktu\u00e1lna vzdialenos\u0165 {entity_name}", "is_energy": "Aktu\u00e1lna energia {entity_name}", "is_frequency": "Aktu\u00e1lna frekvencia {entity_name}", "is_gas": "Aktu\u00e1lny plyn {entity_name}", "is_humidity": "Aktu\u00e1lna vlhkos\u0165 {entity_name}", "is_illuminance": "Aktu\u00e1lne osvetlenie {entity_name}", + "is_irradiance": "Aktu\u00e1lna o\u017eiarenos\u0165 {entity_name}", "is_moisture": "Aktu\u00e1lna vlhkos\u0165 {entity_name}", "is_nitrogen_dioxide": "Aktu\u00e1lna \u00farove\u0148 koncentr\u00e1cie oxidu dusi\u010dit\u00e9ho {entity_name}", "is_nitrogen_monoxide": "Aktu\u00e1lna \u00farove\u0148 koncentr\u00e1cie oxidu uho\u013enat\u00e9ho {entity_name}", @@ -25,6 +29,7 @@ "is_pressure": "Aktu\u00e1lny tlak {entity_name}", "is_reactive_power": "Aktu\u00e1lny jalov\u00fd v\u00fdkon {entity_name}", "is_signal_strength": "Aktu\u00e1lna sila sign\u00e1lu {entity_name}", + "is_sound_pressure": "Aktu\u00e1lny akustick\u00fd tlak {entity_name}", "is_speed": "Aktu\u00e1lna r\u00fdchlos\u0165 {entity_name}", "is_sulphur_dioxide": "Aktu\u00e1lna \u00farove\u0148 koncentr\u00e1cie oxidu siri\u010dit\u00e9ho {entity_name}", "is_temperature": "Aktu\u00e1lna teplota {entity_name}", @@ -36,17 +41,44 @@ "is_weight": "Aktu\u00e1lna hmotnos\u0165 {entity_name}" }, "trigger_type": { + "apparent_power": "Pri zmene zdanliv\u00e9ho v\u00fdkonu {entity_name}", + "atmospheric_pressure": "Pri zmene atmosf\u00e9rick\u00e9ho tlaku {entity_name}", + "battery_level": "Pri zmene \u00farovne bat\u00e9rie {entity_name}", "carbon_dioxide": "{entity_name} sa men\u00ed koncentr\u00e1cia oxidu uhli\u010dit\u00e9ho", "carbon_monoxide": "{entity_name} sa men\u00ed koncentr\u00e1cia oxidu uho\u013enat\u00e9ho", "current": "{entity_name} pr\u00fad sa zmen\u00ed", + "data_rate": "Pri zmene r\u00fdchlosti prenosu d\u00e1t {entity_name}", + "data_size": "Pri zmene ve\u013ekosti \u00fadajov {entity_name}", + "distance": "Pri zmene vzdialenosti {entity_name}", + "energy": "Pri zmene energie {entity_name}", + "frequency": "Pri zmene frekvencie {entity_name}", + "gas": "Pri zmene mno\u017estva plynu {entity_name}", "humidity": "{n\u00e1zov_objektu} zmeny vlhkosti", + "illuminance": "Pri zmene osvetlenia {entity_name}", + "irradiance": "Pri zmene o\u017eiarenia {entity_name}", + "moisture": "Pri zmene vlhkosti {entity_name}", "nitrogen_dioxide": "{entity_name} zmeny koncentr\u00e1cie oxidu dusi\u010dit\u00e9ho", "nitrogen_monoxide": "{entity_name} zmeny koncentr\u00e1cie oxidu dusnat\u00e9ho", "nitrous_oxide": "{entity_name} zmeny koncentr\u00e1cie oxidu dusn\u00e9ho", + "ozone": "Pri zmene koncentr\u00e1cie oz\u00f3nu {entity_name}", + "pm1": "Pri zmene koncentr\u00e1cie PM1 {entity_name}", + "pm10": "Pri zmene koncentr\u00e1cie PM10 {entity_name}", + "pm25": "Pri zmene koncentr\u00e1cie PM2.5 {entity_name}", + "power": "Pri zmene v\u00fdkonu {n\u00e1zov subjektu}", + "power_factor": "Pri zmene \u00fa\u010dinn\u00edka {n\u00e1zov subjektu}", + "pressure": "Pri zmene tlaku {entity_name}", + "reactive_power": "Pri zmene reakt\u00edvneho v\u00fdkonu {entity_name}", + "signal_strength": "Pri sa zmene sily sign\u00e1lu {entity_name}", + "sound_pressure": "Pri zmene akustick\u00e9ho tlaku {entity_name}", + "speed": "Pri zmene r\u00fdchlosti {entity_name}", "sulphur_dioxide": "{entity_name} zmeny koncentr\u00e1cie oxidu siri\u010dit\u00e9ho", "temperature": "Zmena teploty {entity_name}", "value": "Zmena hodnoty {entity_name}", - "voltage": "{entity_name} zmen\u00ed nap\u00e4tie" + "volatile_organic_compounds": "Pri zmene koncentr\u00e1cie prchav\u00fdch organick\u00fdch zl\u00fa\u010den\u00edn {entity_name}", + "voltage": "{entity_name} zmen\u00ed nap\u00e4tie", + "volume": "Pri zmene objemu {entity_name}", + "water": "Pri zmene mno\u0161tva vody {entity_name}", + "weight": "Pri zmene hmotnosti {entity_name}" } }, "state": { diff --git a/homeassistant/components/sensor/translations/zh-Hant.json b/homeassistant/components/sensor/translations/zh-Hant.json index 1adb71c652b..ecd82e9f2b6 100644 --- a/homeassistant/components/sensor/translations/zh-Hant.json +++ b/homeassistant/components/sensor/translations/zh-Hant.json @@ -2,16 +2,20 @@ "device_automation": { "condition_type": { "is_apparent_power": "\u76ee\u524d{entity_name}\u8996\u5728\u529f\u7387", + "is_atmospheric_pressure": "\u76ee\u524d{entity_name}\u6c23\u58d3", "is_battery_level": "\u76ee\u524d{entity_name}\u96fb\u91cf", "is_carbon_dioxide": "\u76ee\u524d {entity_name} \u4e8c\u6c27\u5316\u78b3\u6fc3\u5ea6\u72c0\u614b", "is_carbon_monoxide": "\u76ee\u524d {entity_name} \u4e00\u6c27\u5316\u78b3\u6fc3\u5ea6\u72c0\u614b", "is_current": "\u76ee\u524d{entity_name}\u96fb\u6d41", + "is_data_rate": "\u76ee\u524d {entity_name}\u8cc7\u6599\u50b3\u8f38\u7387", + "is_data_size": "\u76ee\u524d {entity_name} \u8cc7\u6599\u5927\u5c0f", "is_distance": "\u76ee\u524d{entity_name}\u8ddd\u96e2", "is_energy": "\u76ee\u524d{entity_name}\u96fb\u529b", "is_frequency": "\u76ee\u524d{entity_name}\u983b\u7387", "is_gas": "\u76ee\u524d{entity_name}\u6c23\u9ad4", "is_humidity": "\u76ee\u524d{entity_name}\u6fd5\u5ea6", "is_illuminance": "\u76ee\u524d{entity_name}\u7167\u5ea6", + "is_irradiance": "\u76ee\u524d{entity_name}\u8f3b\u7167\u5ea6", "is_moisture": "\u76ee\u524d{entity_name}\u6fd5\u5ea6", "is_nitrogen_dioxide": "\u76ee\u524d {entity_name} \u4e8c\u6c27\u5316\u6c2e\u6fc3\u5ea6\u72c0\u614b", "is_nitrogen_monoxide": "\u76ee\u524d {entity_name} \u4e00\u6c27\u5316\u6c2e\u6fc3\u5ea6\u72c0\u614b", @@ -25,6 +29,7 @@ "is_pressure": "\u76ee\u524d{entity_name}\u58d3\u529b", "is_reactive_power": "\u76ee\u524d{entity_name}\u7121\u6548\u529f\u7387", "is_signal_strength": "\u76ee\u524d{entity_name}\u8a0a\u865f\u5f37\u5ea6", + "is_sound_pressure": "\u76ee\u524d{entity_name}\u8072\u58d3", "is_speed": "\u76ee\u524d{entity_name}\u901f\u5ea6", "is_sulphur_dioxide": "\u76ee\u524d {entity_name} \u4e8c\u6c27\u5316\u786b\u6fc3\u5ea6\u72c0\u614b", "is_temperature": "\u76ee\u524d{entity_name}\u6eab\u5ea6", @@ -37,16 +42,20 @@ }, "trigger_type": { "apparent_power": "{entity_name}\u8996\u5728\u529f\u7387\u8b8a\u66f4", + "atmospheric_pressure": "{entity_name}\u6c23\u58d3\u8b8a\u66f4", "battery_level": "{entity_name}\u96fb\u91cf\u8b8a\u66f4", "carbon_dioxide": "{entity_name} \u4e8c\u6c27\u5316\u78b3\u6fc3\u5ea6\u8b8a\u5316", "carbon_monoxide": "{entity_name} \u4e00\u6c27\u5316\u78b3\u6fc3\u5ea6\u8b8a\u5316", "current": "\u76ee\u524d{entity_name}\u96fb\u6d41\u8b8a\u66f4", + "data_rate": "{entity_name}\u8cc7\u6599\u50b3\u8f38\u7387\u8b8a\u66f4", + "data_size": "{entity_name} \u8cc7\u6599\u5927\u5c0f\u8b8a\u66f4", "distance": "{entity_name}\u8ddd\u96e2\u8b8a\u66f4", "energy": "\u76ee\u524d{entity_name}\u96fb\u529b\u8b8a\u66f4", "frequency": "{entity_name}\u983b\u7387\u8b8a\u66f4", "gas": "{entity_name}\u6c23\u9ad4\u8b8a\u66f4", "humidity": "{entity_name}\u6fd5\u5ea6\u8b8a\u66f4", "illuminance": "{entity_name}\u7167\u5ea6\u8b8a\u66f4", + "irradiance": "{entity_name}\u8f3b\u7167\u5ea6\u8b8a\u66f4", "moisture": "{entity_name}\u6fd5\u5ea6\u8b8a\u66f4", "nitrogen_dioxide": "{entity_name} \u4e8c\u6c27\u5316\u6c2e\u6fc3\u5ea6\u8b8a\u5316", "nitrogen_monoxide": "{entity_name} \u4e00\u6c27\u5316\u6c2e\u6fc3\u5ea6\u8b8a\u5316", @@ -60,6 +69,7 @@ "pressure": "{entity_name}\u58d3\u529b\u8b8a\u66f4", "reactive_power": "{entity_name}\u7121\u6548\u529f\u7387\u8b8a\u66f4", "signal_strength": "{entity_name}\u8a0a\u865f\u5f37\u5ea6\u8b8a\u66f4", + "sound_pressure": "{entity_name}\u8072\u58d3\u8b8a\u66f4", "speed": "{entity_name}\u901f\u5ea6\u8b8a\u66f4", "sulphur_dioxide": "{entity_name} \u4e8c\u6c27\u5316\u786b\u6fc3\u5ea6\u8b8a\u5316", "temperature": "{entity_name}\u6eab\u5ea6\u8b8a\u66f4", diff --git a/homeassistant/components/sensorpro/manifest.json b/homeassistant/components/sensorpro/manifest.json index 6f2f8806262..ba9b8bcf164 100644 --- a/homeassistant/components/sensorpro/manifest.json +++ b/homeassistant/components/sensorpro/manifest.json @@ -15,7 +15,7 @@ "connectable": false } ], - "requirements": ["sensorpro-ble==0.5.0"], + "requirements": ["sensorpro-ble==0.5.1"], "dependencies": ["bluetooth"], "codeowners": ["@bdraco"], "iot_class": "local_push" diff --git a/homeassistant/components/sensorpro/sensor.py b/homeassistant/components/sensorpro/sensor.py index deb79e62bdc..edfe2fb21c5 100644 --- a/homeassistant/components/sensorpro/sensor.py +++ b/homeassistant/components/sensorpro/sensor.py @@ -23,10 +23,10 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.const import ( - ELECTRIC_POTENTIAL_VOLT, PERCENTAGE, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, - TEMP_CELSIUS, + UnitOfElectricPotential, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory @@ -67,7 +67,7 @@ SENSOR_DESCRIPTIONS = { ): SensorEntityDescription( key=f"{SensorProSensorDeviceClass.TEMPERATURE}_{Units.TEMP_CELSIUS}", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, ), ( @@ -76,7 +76,7 @@ SENSOR_DESCRIPTIONS = { ): SensorEntityDescription( key=f"{SensorProSensorDeviceClass.VOLTAGE}_{Units.ELECTRIC_POTENTIAL_VOLT}", device_class=SensorDeviceClass.VOLTAGE, - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, state_class=SensorStateClass.MEASUREMENT, ), } diff --git a/homeassistant/components/sensorpro/translations/en.json b/homeassistant/components/sensorpro/translations/en.json index ebd9760c161..afe859ca766 100644 --- a/homeassistant/components/sensorpro/translations/en.json +++ b/homeassistant/components/sensorpro/translations/en.json @@ -9,13 +9,13 @@ "flow_title": "{name}", "step": { "bluetooth_confirm": { - "description": "Do you want to setup {name}?" + "description": "Do you want to set up {name}?" }, "user": { "data": { "address": "Device" }, - "description": "Choose a device to setup" + "description": "Choose a device to set up" } } } diff --git a/homeassistant/components/sensorpro/translations/it.json b/homeassistant/components/sensorpro/translations/it.json index 7784ed3a240..97113c57103 100644 --- a/homeassistant/components/sensorpro/translations/it.json +++ b/homeassistant/components/sensorpro/translations/it.json @@ -15,7 +15,7 @@ "data": { "address": "Dispositivo" }, - "description": "Seleziona un dispositivo da configurare" + "description": "Scegli un dispositivo da configurare" } } } diff --git a/homeassistant/components/sensorpro/translations/ko.json b/homeassistant/components/sensorpro/translations/ko.json new file mode 100644 index 00000000000..1a59c05b30c --- /dev/null +++ b/homeassistant/components/sensorpro/translations/ko.json @@ -0,0 +1,9 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", + "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sensorpro/translations/no.json b/homeassistant/components/sensorpro/translations/no.json index 0bf8b1695ec..38ab3d096f2 100644 --- a/homeassistant/components/sensorpro/translations/no.json +++ b/homeassistant/components/sensorpro/translations/no.json @@ -9,7 +9,7 @@ "flow_title": "{name}", "step": { "bluetooth_confirm": { - "description": "Vil du konfigurere {name}?" + "description": "Vil du sette opp {name} ?" }, "user": { "data": { diff --git a/homeassistant/components/sensorpro/translations/pt.json b/homeassistant/components/sensorpro/translations/pt.json index 5a10362e52b..a91d8b08e67 100644 --- a/homeassistant/components/sensorpro/translations/pt.json +++ b/homeassistant/components/sensorpro/translations/pt.json @@ -1,6 +1,9 @@ { "config": { "abort": { + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", + "already_in_progress": "O processo de configura\u00e7\u00e3o j\u00e1 est\u00e1 a decorrer", + "no_devices_found": "Nenhum dispositivo encontrado na rede", "not_supported": "Dispositivo n\u00e3o suportado" } } diff --git a/homeassistant/components/sensorpush/sensor.py b/homeassistant/components/sensorpush/sensor.py index 58b569d5227..7742642e92c 100644 --- a/homeassistant/components/sensorpush/sensor.py +++ b/homeassistant/components/sensorpush/sensor.py @@ -21,9 +21,9 @@ from homeassistant.components.sensor import ( ) from homeassistant.const import ( PERCENTAGE, - PRESSURE_MBAR, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, - TEMP_CELSIUS, + UnitOfPressure, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -35,7 +35,7 @@ SENSOR_DESCRIPTIONS = { (DeviceClass.TEMPERATURE, Units.TEMP_CELSIUS): SensorEntityDescription( key=f"{DeviceClass.TEMPERATURE}_{Units.TEMP_CELSIUS}", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, ), (DeviceClass.HUMIDITY, Units.PERCENTAGE): SensorEntityDescription( @@ -47,7 +47,7 @@ SENSOR_DESCRIPTIONS = { (DeviceClass.PRESSURE, Units.PRESSURE_MBAR): SensorEntityDescription( key=f"{DeviceClass.PRESSURE}_{Units.PRESSURE_MBAR}", device_class=SensorDeviceClass.PRESSURE, - native_unit_of_measurement=PRESSURE_MBAR, + native_unit_of_measurement=UnitOfPressure.MBAR, state_class=SensorStateClass.MEASUREMENT, ), ( diff --git a/homeassistant/components/sensorpush/translations/en.json b/homeassistant/components/sensorpush/translations/en.json index d24df64f135..f99ec0bbe63 100644 --- a/homeassistant/components/sensorpush/translations/en.json +++ b/homeassistant/components/sensorpush/translations/en.json @@ -8,13 +8,13 @@ "flow_title": "{name}", "step": { "bluetooth_confirm": { - "description": "Do you want to setup {name}?" + "description": "Do you want to set up {name}?" }, "user": { "data": { "address": "Device" }, - "description": "Choose a device to setup" + "description": "Choose a device to set up" } } } diff --git a/homeassistant/components/sensorpush/translations/it.json b/homeassistant/components/sensorpush/translations/it.json index 501b5095826..3ede7709c00 100644 --- a/homeassistant/components/sensorpush/translations/it.json +++ b/homeassistant/components/sensorpush/translations/it.json @@ -14,7 +14,7 @@ "data": { "address": "Dispositivo" }, - "description": "Seleziona un dispositivo da configurare" + "description": "Scegli un dispositivo da configurare" } } } diff --git a/homeassistant/components/sensorpush/translations/ko.json b/homeassistant/components/sensorpush/translations/ko.json new file mode 100644 index 00000000000..1a59c05b30c --- /dev/null +++ b/homeassistant/components/sensorpush/translations/ko.json @@ -0,0 +1,9 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", + "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sensorpush/translations/no.json b/homeassistant/components/sensorpush/translations/no.json index 28ec4582177..0a44ef08d80 100644 --- a/homeassistant/components/sensorpush/translations/no.json +++ b/homeassistant/components/sensorpush/translations/no.json @@ -8,7 +8,7 @@ "flow_title": "{name}", "step": { "bluetooth_confirm": { - "description": "Vil du konfigurere {name}?" + "description": "Vil du sette opp {name} ?" }, "user": { "data": { diff --git a/homeassistant/components/sensorpush/translations/pt.json b/homeassistant/components/sensorpush/translations/pt.json new file mode 100644 index 00000000000..c6a15010072 --- /dev/null +++ b/homeassistant/components/sensorpush/translations/pt.json @@ -0,0 +1,9 @@ +{ + "config": { + "abort": { + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", + "already_in_progress": "O processo de configura\u00e7\u00e3o j\u00e1 est\u00e1 a decorrer", + "no_devices_found": "Nenhum dispositivo encontrado na rede" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sentry/manifest.json b/homeassistant/components/sentry/manifest.json index e86ff697896..29490403316 100644 --- a/homeassistant/components/sentry/manifest.json +++ b/homeassistant/components/sentry/manifest.json @@ -3,7 +3,7 @@ "name": "Sentry", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/sentry", - "requirements": ["sentry-sdk==1.11.0"], + "requirements": ["sentry-sdk==1.12.1"], "codeowners": ["@dcramer", "@frenck"], "integration_type": "service", "iot_class": "cloud_polling" diff --git a/homeassistant/components/sentry/translations/de.json b/homeassistant/components/sentry/translations/de.json index 9af3d633924..831b6e77b9c 100644 --- a/homeassistant/components/sentry/translations/de.json +++ b/homeassistant/components/sentry/translations/de.json @@ -25,8 +25,8 @@ "event_third_party_packages": "Senden von Ereignissen aus Drittanbieterpaketen", "logging_event_level": "Der Log-Level, f\u00fcr den Sentry ein Ereignis registrieren wird", "logging_level": "Der Log-Level, f\u00fcr den Sentry Protokolle als Breadcrums aufzeichnen wird", - "tracing": "Aktivieren des Leistungs-Tracings", - "tracing_sample_rate": "Tracing Abtastrate; zwischen 0,0 und 1,0 (1,0 = 100 %)" + "tracing": "Aktivieren der Leistungsverfolgung", + "tracing_sample_rate": "R\u00fcckverfolgung Abtastrate; zwischen 0,0 und 1,0 (1,0 = 100 %)" } } } diff --git a/homeassistant/components/sentry/translations/sk.json b/homeassistant/components/sentry/translations/sk.json index 6be09774065..30fb9524988 100644 --- a/homeassistant/components/sentry/translations/sk.json +++ b/homeassistant/components/sentry/translations/sk.json @@ -6,6 +6,29 @@ "error": { "bad_dsn": "Neplatn\u00e9 DSN", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "step": { + "user": { + "data": { + "dsn": "Sentry DSN" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "environment": "Volite\u013en\u00fd n\u00e1zov prostredia.", + "event_custom_components": "Odosielajte udalosti z vlastn\u00fdch komponentov", + "event_handled": "Odosla\u0165 spracovan\u00e9 udalosti", + "event_third_party_packages": "Odosielanie udalost\u00ed z bal\u00edkov tret\u00edch str\u00e1n", + "logging_event_level": "Sentry na \u00farovni denn\u00edka zaregistruje udalos\u0165", + "logging_level": "Sentry na \u00farovni denn\u00edka bude zaznamen\u00e1va\u0165 denn\u00edky ako navig\u00e1ciu", + "tracing": "Povoli\u0165 sledovanie v\u00fdkonu", + "tracing_sample_rate": "Vzorkovacia frekvencia; medzi 0,0 a 1,0 (1,0 = 100 %)" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/senz/climate.py b/homeassistant/components/senz/climate.py index 2e023e13491..f52a94d2c9a 100644 --- a/homeassistant/components/senz/climate.py +++ b/homeassistant/components/senz/climate.py @@ -12,7 +12,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, PRECISION_TENTHS, TEMP_CELSIUS +from homeassistant.const import ATTR_TEMPERATURE, PRECISION_TENTHS, UnitOfTemperature from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -37,7 +37,7 @@ async def async_setup_entry( class SENZClimate(CoordinatorEntity, ClimateEntity): """Representation of a SENZ climate entity.""" - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS _attr_precision = PRECISION_TENTHS _attr_hvac_modes = [HVACMode.HEAT, HVACMode.AUTO] _attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE diff --git a/homeassistant/components/senz/translations/ko.json b/homeassistant/components/senz/translations/ko.json index c3f709296a5..e48a6305442 100644 --- a/homeassistant/components/senz/translations/ko.json +++ b/homeassistant/components/senz/translations/ko.json @@ -1,7 +1,11 @@ { "config": { "abort": { + "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", + "authorize_url_timeout": "\uc778\uc99d URL \uc0dd\uc131 \uc2dc\uac04\uc774 \ucd08\uacfc\ub418\uc5c8\uc2b5\ub2c8\ub2e4.", "missing_configuration": "\uad6c\uc131\uc694\uc18c\uac00 \uad6c\uc131\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4. \uc124\uba85\uc11c\ub97c \ucc38\uace0\ud574\uc8fc\uc138\uc694.", + "no_url_available": "\uc0ac\uc6a9 \uac00\ub2a5\ud55c URL\uc774 \uc5c6\uc2b5\ub2c8\ub2e4. \uc774 \uc624\ub958\uc5d0 \ub300\ud55c \uc790\uc138\ud55c \ub0b4\uc6a9\uc740 [\ub3c4\uc6c0\ub9d0 \uc139\uc158]({docs_url}) \uc744(\ub97c) \ucc38\uc870\ud574\uc8fc\uc138\uc694.", "oauth_error": "\uc798\ubabb\ub41c \ud1a0\ud070 \ub370\uc774\ud130\ub97c \ubc1b\uc558\uc2b5\ub2c8\ub2e4." }, "create_entry": { diff --git a/homeassistant/components/senz/translations/sk.json b/homeassistant/components/senz/translations/sk.json index 63bb5647a49..720fa75c832 100644 --- a/homeassistant/components/senz/translations/sk.json +++ b/homeassistant/components/senz/translations/sk.json @@ -16,5 +16,11 @@ "title": "Vyberte met\u00f3du overenia" } } + }, + "issues": { + "removed_yaml": { + "description": "Konfigur\u00e1cia nVent RAYCHEM SENZ pomocou YAML bola odstr\u00e1nen\u00e1. \n\nAsistent dom\u00e1cnosti nepou\u017e\u00edva va\u0161u existuj\u00facu konfigur\u00e1ciu YAML. \n\nAk chcete tento probl\u00e9m vyrie\u0161i\u0165, odstr\u00e1\u0148te konfigur\u00e1ciu YAML zo s\u00faboru configuration.yaml a re\u0161tartujte aplik\u00e1ciu Home Assistant.", + "title": "Konfigur\u00e1cia nVent RAYCHEM SENZ YAML bola odstr\u00e1nen\u00e1" + } } } \ No newline at end of file diff --git a/homeassistant/components/sharkiq/translations/pt.json b/homeassistant/components/sharkiq/translations/pt.json index dfae15e9686..a25b9172c95 100644 --- a/homeassistant/components/sharkiq/translations/pt.json +++ b/homeassistant/components/sharkiq/translations/pt.json @@ -2,12 +2,12 @@ "config": { "abort": { "already_configured": "Conta j\u00e1 configurada", - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "reauth_successful": "Reautentica\u00e7\u00e3o bem sucedida", "unknown": "Erro inesperado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, diff --git a/homeassistant/components/sharkiq/translations/sk.json b/homeassistant/components/sharkiq/translations/sk.json index 76f7b6b566c..71a9b1e34da 100644 --- a/homeassistant/components/sharkiq/translations/sk.json +++ b/homeassistant/components/sharkiq/translations/sk.json @@ -14,12 +14,14 @@ "step": { "reauth": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } }, "user": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } diff --git a/homeassistant/components/sharkiq/update_coordinator.py b/homeassistant/components/sharkiq/update_coordinator.py index 41853ad4e41..2afeb574f92 100644 --- a/homeassistant/components/sharkiq/update_coordinator.py +++ b/homeassistant/components/sharkiq/update_coordinator.py @@ -20,7 +20,7 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda from .const import API_TIMEOUT, DOMAIN, LOGGER, UPDATE_INTERVAL -class SharkIqUpdateCoordinator(DataUpdateCoordinator): +class SharkIqUpdateCoordinator(DataUpdateCoordinator[bool]): """Define a wrapper class to update Shark IQ data.""" def __init__( diff --git a/homeassistant/components/shelly/__init__.py b/homeassistant/components/shelly/__init__.py index 1e77fae439e..df054598f5c 100644 --- a/homeassistant/components/shelly/__init__.py +++ b/homeassistant/components/shelly/__init__.py @@ -1,6 +1,7 @@ """The Shelly integration.""" from __future__ import annotations +import contextlib from typing import Any, Final import aioshelly @@ -97,7 +98,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: # value, so if host isn't present, config entry will not be configured. if not entry.data.get(CONF_HOST): LOGGER.warning( - "The config entry %s probably comes from a custom integration, please remove it if you want to use core Shelly integration", + ( + "The config entry %s probably comes from a custom integration, please" + " remove it if you want to use core Shelly integration" + ), entry.title, ) return False @@ -311,7 +315,13 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: entry, platforms ): if shelly_entry_data.rpc: - await shelly_entry_data.rpc.shutdown() + with contextlib.suppress(DeviceConnectionError): + # If the device is restarting or has gone offline before + # the ping/pong timeout happens, the shutdown command + # will fail, but we don't care since we are unloading + # and if we setup again, we will fix anything that is + # in an inconsistent state at that time. + await shelly_entry_data.rpc.shutdown() get_entry_data(hass).pop(entry.entry_id) return unload_ok diff --git a/homeassistant/components/shelly/climate.py b/homeassistant/components/shelly/climate.py index a624ba341af..b9435a56ebd 100644 --- a/homeassistant/components/shelly/climate.py +++ b/homeassistant/components/shelly/climate.py @@ -265,7 +265,8 @@ class BlockSleepingClimate( except DeviceConnectionError as err: self.coordinator.last_update_success = False raise HomeAssistantError( - f"Setting state for entity {self.name} failed, state: {kwargs}, error: {repr(err)}" + f"Setting state for entity {self.name} failed, state: {kwargs}, error:" + f" {repr(err)}" ) from err except InvalidAuthError: self.coordinator.entry.async_start_reauth(self.hass) diff --git a/homeassistant/components/shelly/config_flow.py b/homeassistant/components/shelly/config_flow.py index 9bf4a6126b0..8679edf5382 100644 --- a/homeassistant/components/shelly/config_flow.py +++ b/homeassistant/components/shelly/config_flow.py @@ -30,7 +30,7 @@ from .const import ( LOGGER, BLEScannerMode, ) -from .coordinator import get_entry_data +from .coordinator import async_reconnect_soon, get_entry_data from .utils import ( get_block_device_name, get_block_device_sleep_period, @@ -41,6 +41,7 @@ from .utils import ( get_rpc_device_name, get_rpc_device_sleep_period, get_ws_context, + mac_address_from_name, ) HOST_SCHEMA: Final = vol.Schema({vol.Required(CONF_HOST): str}) @@ -210,11 +211,25 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): step_id="credentials", data_schema=vol.Schema(schema), errors=errors ) + async def _async_discovered_mac(self, mac: str, host: str) -> None: + """Abort and reconnect soon if the device with the mac address is already configured.""" + if ( + current_entry := await self.async_set_unique_id(mac) + ) and current_entry.data[CONF_HOST] == host: + await async_reconnect_soon(self.hass, current_entry) + self._abort_if_unique_id_configured({CONF_HOST: host}) + async def async_step_zeroconf( self, discovery_info: zeroconf.ZeroconfServiceInfo ) -> FlowResult: """Handle zeroconf discovery.""" host = discovery_info.host + # First try to get the mac address from the name + # so we can avoid making another connection to the + # device if we already have it configured + if mac := mac_address_from_name(discovery_info.name): + await self._async_discovered_mac(mac, host) + try: self.info = await self._async_get_info(host) except DeviceConnectionError: @@ -222,10 +237,12 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): except FirmwareUnsupported: return self.async_abort(reason="unsupported_firmware") - await self.async_set_unique_id(self.info["mac"]) - self._abort_if_unique_id_configured({CONF_HOST: host}) - self.host = host + if not mac: + # We could not get the mac address from the name + # so need to check here since we just got the info + await self._async_discovered_mac(self.info["mac"], host) + self.host = host self.context.update( { "title_placeholders": {"name": discovery_info.name.split(".")[0]}, diff --git a/homeassistant/components/shelly/coordinator.py b/homeassistant/components/shelly/coordinator.py index 867cacb3647..aeb3dcf7ccf 100644 --- a/homeassistant/components/shelly/coordinator.py +++ b/homeassistant/components/shelly/coordinator.py @@ -14,6 +14,7 @@ from aioshelly.exceptions import DeviceConnectionError, InvalidAuthError, RpcCal from aioshelly.rpc_device import RpcDevice, UpdateType from awesomeversion import AwesomeVersion +from homeassistant import config_entries from homeassistant.config_entries import ConfigEntry from homeassistant.const import ATTR_DEVICE_ID, CONF_HOST, EVENT_HOMEASSISTANT_STOP from homeassistant.core import CALLBACK_TYPE, Event, HomeAssistant, callback @@ -72,7 +73,7 @@ def get_entry_data(hass: HomeAssistant) -> dict[str, ShellyEntryData]: return cast(dict[str, ShellyEntryData], hass.data[DOMAIN][DATA_CONFIG_ENTRY]) -class ShellyBlockCoordinator(DataUpdateCoordinator): +class ShellyBlockCoordinator(DataUpdateCoordinator[None]): """Coordinator for a Shelly block based device.""" def __init__( @@ -317,7 +318,7 @@ class ShellyRestCoordinator(DataUpdateCoordinator): return cast(str, self.device.settings["device"]["mac"]) -class ShellyRpcCoordinator(DataUpdateCoordinator): +class ShellyRpcCoordinator(DataUpdateCoordinator[None]): """Coordinator for a Shelly RPC based device.""" def __init__( @@ -479,6 +480,9 @@ class ShellyRpcCoordinator(DataUpdateCoordinator): return self.connected = False self._async_run_disconnected_events() + # Try to reconnect right away if hass is not stopping + if not self.hass.is_stopping: + await self.async_request_refresh() @callback def _async_run_disconnected_events(self) -> None: @@ -539,7 +543,7 @@ class ShellyRpcCoordinator(DataUpdateCoordinator): elif update_type is UpdateType.DISCONNECTED: self.hass.async_create_task(self._async_disconnected()) elif update_type is UpdateType.STATUS: - self.async_set_updated_data(self.device) + self.async_set_updated_data(None) elif update_type is UpdateType.EVENT and (event := self.device.event): self._async_device_event_handler(event) @@ -643,3 +647,16 @@ def get_rpc_coordinator_by_device_id( return coordinator return None + + +async def async_reconnect_soon( + hass: HomeAssistant, entry: config_entries.ConfigEntry +) -> None: + """Try to reconnect soon.""" + if ( + not hass.is_stopping + and entry.state == config_entries.ConfigEntryState.LOADED + and (entry_data := get_entry_data(hass).get(entry.entry_id)) + and (coordinator := entry_data.rpc) + ): + hass.async_create_task(coordinator.async_request_refresh()) diff --git a/homeassistant/components/shelly/diagnostics.py b/homeassistant/components/shelly/diagnostics.py index 6e5f8d139a2..f73de5fb32d 100644 --- a/homeassistant/components/shelly/diagnostics.py +++ b/homeassistant/components/shelly/diagnostics.py @@ -1,10 +1,12 @@ """Diagnostics support for Shelly.""" from __future__ import annotations +from homeassistant.components.bluetooth import async_scanner_by_source from homeassistant.components.diagnostics import async_redact_data from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_PASSWORD, CONF_USERNAME from homeassistant.core import HomeAssistant +from homeassistant.helpers.device_registry import format_mac from .coordinator import get_entry_data @@ -19,6 +21,7 @@ async def async_get_config_entry_diagnostics( device_settings: str | dict = "not initialized" device_status: str | dict = "not initialized" + bluetooth: str | dict = "not initialized" if shelly_entry_data.block: block_coordinator = shelly_entry_data.block assert block_coordinator @@ -68,6 +71,12 @@ async def async_get_config_entry_diagnostics( if k in ["sys", "wifi"] } + source = format_mac(rpc_coordinator.mac).upper() + if scanner := async_scanner_by_source(hass, source): + bluetooth = { + "scanner": await scanner.async_diagnostics(), + } + if isinstance(device_status, dict): device_status = async_redact_data(device_status, ["ssid"]) @@ -76,4 +85,5 @@ async def async_get_config_entry_diagnostics( "device_info": device_info, "device_settings": device_settings, "device_status": device_status, + "bluetooth": bluetooth, } diff --git a/homeassistant/components/shelly/entity.py b/homeassistant/components/shelly/entity.py index 3c57bee8f02..2ab0af8f18e 100644 --- a/homeassistant/components/shelly/entity.py +++ b/homeassistant/components/shelly/entity.py @@ -347,7 +347,8 @@ class ShellyBlockEntity(CoordinatorEntity[ShellyBlockCoordinator]): except DeviceConnectionError as err: self.coordinator.last_update_success = False raise HomeAssistantError( - f"Setting state for entity {self.name} failed, state: {kwargs}, error: {repr(err)}" + f"Setting state for entity {self.name} failed, state: {kwargs}, error:" + f" {repr(err)}" ) from err except InvalidAuthError: self.coordinator.entry.async_start_reauth(self.hass) @@ -399,11 +400,13 @@ class ShellyRpcEntity(CoordinatorEntity[ShellyRpcCoordinator]): except DeviceConnectionError as err: self.coordinator.last_update_success = False raise HomeAssistantError( - f"Call RPC for {self.name} connection error, method: {method}, params: {params}, error: {repr(err)}" + f"Call RPC for {self.name} connection error, method: {method}, params:" + f" {params}, error: {repr(err)}" ) from err except RpcCallError as err: raise HomeAssistantError( - f"Call RPC for {self.name} request error, method: {method}, params: {params}, error: {repr(err)}" + f"Call RPC for {self.name} request error, method: {method}, params:" + f" {params}, error: {repr(err)}" ) from err except InvalidAuthError: self.coordinator.entry.async_start_reauth(self.hass) @@ -519,20 +522,21 @@ class ShellyRpcAttributeEntity(ShellyRpcEntity, entity.Entity): self._attr_name = get_rpc_entity_name(coordinator.device, key, description.name) self._last_value = None + @property + def sub_status(self) -> Any: + """Device status by entity key.""" + return self.status[self.entity_description.sub_key] + @property def attribute_value(self) -> StateType: """Value of sensor.""" if callable(self.entity_description.value): + # using "get" here since subkey might not exist (e.g. "errors" sub_key) self._last_value = self.entity_description.value( - self.coordinator.device.status[self.key].get( - self.entity_description.sub_key - ), - self._last_value, + self.status.get(self.entity_description.sub_key), self._last_value ) else: - self._last_value = self.coordinator.device.status[self.key][ - self.entity_description.sub_key - ] + self._last_value = self.sub_status return self._last_value @@ -544,9 +548,7 @@ class ShellyRpcAttributeEntity(ShellyRpcEntity, entity.Entity): if not available or not self.entity_description.available: return available - return self.entity_description.available( - self.coordinator.device.status[self.key][self.entity_description.sub_key] - ) + return self.entity_description.available(self.sub_status) class ShellySleepingBlockAttributeEntity(ShellyBlockAttributeEntity, RestoreEntity): diff --git a/homeassistant/components/shelly/light.py b/homeassistant/components/shelly/light.py index 805b8147ba5..18c1da8a795 100644 --- a/homeassistant/components/shelly/light.py +++ b/homeassistant/components/shelly/light.py @@ -39,10 +39,12 @@ from .coordinator import ShellyBlockCoordinator, ShellyRpcCoordinator, get_entry from .entity import ShellyBlockEntity, ShellyRpcEntity from .utils import ( async_remove_shelly_entity, + brightness_to_percentage, get_device_entry_gen, get_rpc_key_ids, is_block_channel_type_light, is_rpc_channel_type_light, + percentage_to_brightness, ) @@ -109,10 +111,15 @@ def async_setup_rpc_entry( unique_id = f"{coordinator.mac}-switch:{id_}" async_remove_shelly_entity(hass, "switch", unique_id) - if not switch_ids: + if switch_ids: + async_add_entities( + RpcShellySwitchAsLight(coordinator, id_) for id_ in switch_ids + ) return - async_add_entities(RpcShellyLight(coordinator, id_) for id_ in switch_ids) + light_key_ids = get_rpc_key_ids(coordinator.device.status, "light") + if light_key_ids: + async_add_entities(RpcShellyLight(coordinator, id_) for id_ in light_key_ids) class BlockShellyLight(ShellyBlockEntity, LightEntity): @@ -184,19 +191,15 @@ class BlockShellyLight(ShellyBlockEntity, LightEntity): @property def brightness(self) -> int: """Return the brightness of this light between 0..255.""" - brightness_pct: int if self.mode == "color": if self.control_result: - brightness_pct = self.control_result["gain"] - else: - brightness_pct = cast(int, self.block.gain) - else: - if self.control_result: - brightness_pct = self.control_result["brightness"] - else: - brightness_pct = cast(int, self.block.brightness) + return percentage_to_brightness(self.control_result["gain"]) + return percentage_to_brightness(cast(int, self.block.gain)) - return round(255 * brightness_pct / 100) + # white mode + if self.control_result: + return percentage_to_brightness(self.control_result["brightness"]) + return percentage_to_brightness(cast(int, self.block.brightness)) @property def color_mode(self) -> ColorMode: @@ -287,11 +290,10 @@ class BlockShellyLight(ShellyBlockEntity, LightEntity): ) if ATTR_BRIGHTNESS in kwargs and brightness_supported(supported_color_modes): - brightness_pct = int(100 * (kwargs[ATTR_BRIGHTNESS] + 1) / 255) if hasattr(self.block, "gain"): - params["gain"] = brightness_pct + params["gain"] = brightness_to_percentage(kwargs[ATTR_BRIGHTNESS]) if hasattr(self.block, "brightness"): - params["brightness"] = brightness_pct + params["brightness"] = brightness_to_percentage(kwargs[ATTR_BRIGHTNESS]) if ( ATTR_COLOR_TEMP_KELVIN in kwargs @@ -367,8 +369,8 @@ class BlockShellyLight(ShellyBlockEntity, LightEntity): super()._update_callback() -class RpcShellyLight(ShellyRpcEntity, LightEntity): - """Entity that controls a light on RPC based Shelly devices.""" +class RpcShellySwitchAsLight(ShellyRpcEntity, LightEntity): + """Entity that controls a relay as light on RPC based Shelly devices.""" _attr_color_mode = ColorMode.ONOFF _attr_supported_color_modes = {ColorMode.ONOFF} @@ -381,7 +383,7 @@ class RpcShellyLight(ShellyRpcEntity, LightEntity): @property def is_on(self) -> bool: """If light is on.""" - return bool(self.coordinator.device.status[self.key]["output"]) + return bool(self.status["output"]) async def async_turn_on(self, **kwargs: Any) -> None: """Turn on light.""" @@ -390,3 +392,38 @@ class RpcShellyLight(ShellyRpcEntity, LightEntity): async def async_turn_off(self, **kwargs: Any) -> None: """Turn off light.""" await self.call_rpc("Switch.Set", {"id": self._id, "on": False}) + + +class RpcShellyLight(ShellyRpcEntity, LightEntity): + """Entity that controls a light on RPC based Shelly devices.""" + + _attr_color_mode = ColorMode.BRIGHTNESS + _attr_supported_color_modes = {ColorMode.BRIGHTNESS} + + def __init__(self, coordinator: ShellyRpcCoordinator, id_: int) -> None: + """Initialize light.""" + super().__init__(coordinator, f"light:{id_}") + self._id = id_ + + @property + def is_on(self) -> bool: + """If light is on.""" + return bool(self.status["output"]) + + @property + def brightness(self) -> int: + """Return the brightness of this light between 0..255.""" + return percentage_to_brightness(self.status["brightness"]) + + async def async_turn_on(self, **kwargs: Any) -> None: + """Turn on light.""" + params: dict[str, Any] = {"id": self._id, "on": True} + + if ATTR_BRIGHTNESS in kwargs: + params["brightness"] = brightness_to_percentage(kwargs[ATTR_BRIGHTNESS]) + + await self.call_rpc("Light.Set", params) + + async def async_turn_off(self, **kwargs: Any) -> None: + """Turn off light.""" + await self.call_rpc("Light.Set", {"id": self._id, "on": False}) diff --git a/homeassistant/components/shelly/logbook.py b/homeassistant/components/shelly/logbook.py index 38465112345..6099bffd596 100644 --- a/homeassistant/components/shelly/logbook.py +++ b/homeassistant/components/shelly/logbook.py @@ -53,7 +53,9 @@ def async_describe_events( return { LOGBOOK_ENTRY_NAME: "Shelly", - LOGBOOK_ENTRY_MESSAGE: f"'{click_type}' click event for {input_name} Input was fired", + LOGBOOK_ENTRY_MESSAGE: ( + f"'{click_type}' click event for {input_name} Input was fired" + ), } async_describe_event(DOMAIN, EVENT_SHELLY_CLICK, async_describe_shelly_click_event) diff --git a/homeassistant/components/shelly/manifest.json b/homeassistant/components/shelly/manifest.json index 39723bf4b0f..911b0cf8c7c 100644 --- a/homeassistant/components/shelly/manifest.json +++ b/homeassistant/components/shelly/manifest.json @@ -3,7 +3,7 @@ "name": "Shelly", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/shelly", - "requirements": ["aioshelly==5.1.2"], + "requirements": ["aioshelly==5.2.0"], "dependencies": ["bluetooth", "http"], "zeroconf": [ { diff --git a/homeassistant/components/shelly/number.py b/homeassistant/components/shelly/number.py index 7066f386355..d13c891e13a 100644 --- a/homeassistant/components/shelly/number.py +++ b/homeassistant/components/shelly/number.py @@ -117,7 +117,8 @@ class BlockSleepingNumber(ShellySleepingBlockAttributeEntity, NumberEntity): except DeviceConnectionError as err: self.coordinator.last_update_success = False raise HomeAssistantError( - f"Setting state for entity {self.name} failed, state: {params}, error: {repr(err)}" + f"Setting state for entity {self.name} failed, state: {params}, error:" + f" {repr(err)}" ) from err except InvalidAuthError: self.coordinator.entry.async_start_reauth(self.hass) diff --git a/homeassistant/components/shelly/sensor.py b/homeassistant/components/shelly/sensor.py index 6bc3b52b65f..70a29857708 100644 --- a/homeassistant/components/shelly/sensor.py +++ b/homeassistant/components/shelly/sensor.py @@ -17,14 +17,14 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONCENTRATION_PARTS_PER_MILLION, DEGREE, - ELECTRIC_CURRENT_AMPERE, - ELECTRIC_POTENTIAL_VOLT, - ENERGY_KILO_WATT_HOUR, LIGHT_LUX, PERCENTAGE, - POWER_WATT, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, - TEMP_CELSIUS, + UnitOfElectricCurrent, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfPower, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory @@ -79,7 +79,7 @@ SENSORS: Final = { ("device", "deviceTemp"): BlockSensorDescription( key="device|deviceTemp", name="Device Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, value=lambda value: round(value, 1), device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, @@ -89,7 +89,7 @@ SENSORS: Final = { ("emeter", "current"): BlockSensorDescription( key="emeter|current", name="Current", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, value=lambda value: value, device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, @@ -97,7 +97,7 @@ SENSORS: Final = { ("light", "power"): BlockSensorDescription( key="light|power", name="Power", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, value=lambda value: round(value, 1), device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, @@ -106,7 +106,7 @@ SENSORS: Final = { ("device", "power"): BlockSensorDescription( key="device|power", name="Power", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, value=lambda value: round(value, 1), device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, @@ -114,7 +114,7 @@ SENSORS: Final = { ("emeter", "power"): BlockSensorDescription( key="emeter|power", name="Power", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, value=lambda value: round(value, 1), device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, @@ -122,7 +122,7 @@ SENSORS: Final = { ("device", "voltage"): BlockSensorDescription( key="device|voltage", name="Voltage", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, value=lambda value: round(value, 1), device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, @@ -131,7 +131,7 @@ SENSORS: Final = { ("emeter", "voltage"): BlockSensorDescription( key="emeter|voltage", name="Voltage", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, value=lambda value: round(value, 1), device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, @@ -147,7 +147,7 @@ SENSORS: Final = { ("relay", "power"): BlockSensorDescription( key="relay|power", name="Power", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, value=lambda value: round(value, 1), device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, @@ -155,7 +155,7 @@ SENSORS: Final = { ("roller", "rollerPower"): BlockSensorDescription( key="roller|rollerPower", name="Power", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, value=lambda value: round(value, 1), device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, @@ -163,7 +163,7 @@ SENSORS: Final = { ("device", "energy"): BlockSensorDescription( key="device|energy", name="Energy", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, value=lambda value: round(value / 60 / 1000, 2), device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, @@ -171,7 +171,7 @@ SENSORS: Final = { ("emeter", "energy"): BlockSensorDescription( key="emeter|energy", name="Energy", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, value=lambda value: round(value / 1000, 2), device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, @@ -180,7 +180,7 @@ SENSORS: Final = { ("emeter", "energyReturned"): BlockSensorDescription( key="emeter|energyReturned", name="Energy Returned", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, value=lambda value: round(value / 1000, 2), device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, @@ -189,7 +189,7 @@ SENSORS: Final = { ("light", "energy"): BlockSensorDescription( key="light|energy", name="Energy", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, value=lambda value: round(value / 60 / 1000, 2), device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, @@ -198,7 +198,7 @@ SENSORS: Final = { ("relay", "energy"): BlockSensorDescription( key="relay|energy", name="Energy", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, value=lambda value: round(value / 60 / 1000, 2), device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, @@ -206,7 +206,7 @@ SENSORS: Final = { ("roller", "rollerEnergy"): BlockSensorDescription( key="roller|rollerEnergy", name="Energy", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, value=lambda value: round(value / 60 / 1000, 2), device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, @@ -221,7 +221,7 @@ SENSORS: Final = { ("sensor", "temp"): BlockSensorDescription( key="sensor|temp", name="Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, value=lambda value: round(value, 1), device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, @@ -230,7 +230,7 @@ SENSORS: Final = { ("sensor", "extTemp"): BlockSensorDescription( key="sensor|extTemp", name="Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, value=lambda value: round(value, 1), device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, @@ -276,7 +276,7 @@ SENSORS: Final = { ("adc", "adc"): BlockSensorDescription( key="adc|adc", name="ADC", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, value=lambda value: round(value, 2), device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, @@ -317,7 +317,7 @@ RPC_SENSORS: Final = { key="switch", sub_key="apower", name="Power", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, value=lambda status, _: round(float(status), 1), device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, @@ -326,7 +326,7 @@ RPC_SENSORS: Final = { key="switch", sub_key="voltage", name="Voltage", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, value=lambda status, _: round(float(status), 1), device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, @@ -336,7 +336,7 @@ RPC_SENSORS: Final = { key="switch", sub_key="aenergy", name="Energy", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, value=lambda status, _: round(status["total"] / 1000, 2), device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, @@ -345,7 +345,7 @@ RPC_SENSORS: Final = { key="switch", sub_key="temperature", name="Device Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, value=lambda status, _: round(status["tC"], 1), device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, @@ -357,7 +357,7 @@ RPC_SENSORS: Final = { key="temperature", sub_key="tC", name="Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, value=lambda status, _: round(status, 1), device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, @@ -409,7 +409,7 @@ RPC_SENSORS: Final = { key="voltmeter", sub_key="voltage", name="Voltmeter", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, value=lambda status, _: round(float(status), 2), device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, diff --git a/homeassistant/components/shelly/strings.json b/homeassistant/components/shelly/strings.json index 15f3be4d1e5..9f67ed0181d 100644 --- a/homeassistant/components/shelly/strings.json +++ b/homeassistant/components/shelly/strings.json @@ -3,7 +3,7 @@ "flow_title": "{name}", "step": { "user": { - "description": "Before set up, battery-powered devices must be woken up, you can now wake the device up using a button on it.", + "description": "Before setup, battery-powered devices must be woken up, you can now wake the device up using a button on it.", "data": { "host": "[%key:common::config_flow::data::host%]" } diff --git a/homeassistant/components/shelly/switch.py b/homeassistant/components/shelly/switch.py index 39e754eaf86..3f5186a2017 100644 --- a/homeassistant/components/shelly/switch.py +++ b/homeassistant/components/shelly/switch.py @@ -138,7 +138,7 @@ class RpcRelaySwitch(ShellyRpcEntity, SwitchEntity): @property def is_on(self) -> bool: """If switch is on.""" - return bool(self.coordinator.device.status[self.key]["output"]) + return bool(self.status["output"]) async def async_turn_on(self, **kwargs: Any) -> None: """Turn on relay.""" diff --git a/homeassistant/components/shelly/translations/ca.json b/homeassistant/components/shelly/translations/ca.json index 73736725969..2fa312a93af 100644 --- a/homeassistant/components/shelly/translations/ca.json +++ b/homeassistant/components/shelly/translations/ca.json @@ -33,7 +33,7 @@ "data": { "host": "Amfitri\u00f3" }, - "description": "Abans de configurar-lo, els dispositius amb bateria s'han de desperar, ja pots clicar el bot\u00f3 del dispositiu per a despertar-lo." + "description": "Abans de configurar-ho, els dispositius amb bateria s'han de despertar, pots clicar el bot\u00f3 del dispositiu per a despertar-lo, ara." } } }, @@ -67,7 +67,8 @@ "init": { "data": { "ble_scanner_mode": "Mode d'escaneig Bluetooth" - } + }, + "description": "L'escaneig Bluetooth pot ser actiu o passiu. Si \u00e9s actiu, el Shelly sol\u00b7licita dades a dispositius propers; en passiu, el Shelly rep dades no sol\u00b7licitades de dispositius propers." } } } diff --git a/homeassistant/components/shelly/translations/en.json b/homeassistant/components/shelly/translations/en.json index 4ca783b6fe4..d6e41c0d118 100644 --- a/homeassistant/components/shelly/translations/en.json +++ b/homeassistant/components/shelly/translations/en.json @@ -33,7 +33,7 @@ "data": { "host": "Host" }, - "description": "Before set up, battery-powered devices must be woken up, you can now wake the device up using a button on it." + "description": "Before setup, battery-powered devices must be woken up, you can now wake the device up using a button on it." } } }, diff --git a/homeassistant/components/shelly/translations/it.json b/homeassistant/components/shelly/translations/it.json index 04bf99179b4..e93ef824acc 100644 --- a/homeassistant/components/shelly/translations/it.json +++ b/homeassistant/components/shelly/translations/it.json @@ -33,7 +33,7 @@ "data": { "host": "Host" }, - "description": "Prima della configurazione, i dispositivi alimentati a batteria devono essere riattivati, ora puoi riattivare il dispositivo utilizzando un pulsante su di esso." + "description": "Prima della configurazione, i dispositivi alimentati a batteria devono essere riattivati, ora \u00e8 possibile riattivare il dispositivo utilizzando un pulsante su di esso." } } }, diff --git a/homeassistant/components/shelly/translations/ko.json b/homeassistant/components/shelly/translations/ko.json index d4af4248578..169a7c028e3 100644 --- a/homeassistant/components/shelly/translations/ko.json +++ b/homeassistant/components/shelly/translations/ko.json @@ -2,6 +2,7 @@ "config": { "abort": { "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4", "unsupported_firmware": "\uae30\uae30\uac00 \uc9c0\uc6d0\ub418\uc9c0 \uc54a\ub294 \ud38c\uc6e8\uc5b4 \ubc84\uc804\uc744 \uc0ac\uc6a9\ud558\uace0 \uc788\uc2b5\ub2c8\ub2e4." }, "error": { @@ -20,6 +21,12 @@ "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" } }, + "reauth_confirm": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + } + }, "user": { "data": { "host": "\ud638\uc2a4\ud2b8" @@ -43,5 +50,18 @@ "single_long": "\"{subtype}\"\uc774(\uac00) \uc9e7\uac8c \ub20c\ub838\ub2e4\uac00 \uae38\uac8c \ub20c\ub838\uc744 \ub54c", "triple": "\"{subtype}\"\uc774(\uac00) \uc138 \ubc88 \ub20c\ub838\uc744 \ub54c" } + }, + "options": { + "abort": { + "ble_unsupported": "\ube14\ub8e8\ud22c\uc2a4\ub294 \ud38c\uc6e8\uc5b4 \ubc84\uc804 {ble_min_version} \uc774\uc0c1\uc774 \ud544\uc694\ud569\ub2c8\ub2e4." + }, + "step": { + "init": { + "data": { + "ble_scanner_mode": "\ube14\ub8e8\ud22c\uc2a4 \uac80\uc0c9 \ubaa8\ub4dc" + }, + "description": "\ube14\ub8e8\ud22c\uc2a4 \uac80\uc0c9\uc740 \uc561\ud2f0\ube0c\ub098 \ud328\uc2dc\ube0c\ub85c \uac00\ub2a5\ud569\ub2c8\ub2e4. \ud65c\uc131\ud654\ub418\uba74 Shelly\ub294 \uc8fc\ubcc0 \uc7a5\uce58\uc5d0 \ub370\uc774\ud130\ub97c \uc694\uccad\ud569\ub2c8\ub2e4. \ud328\uc2dc\ube0c\ub97c \uc0ac\uc6a9\ud558\uba74 Shelly\ub294 \uc8fc\ubcc0 \uc7a5\uce58\uc5d0\uc11c \ud56d\uc0c1 \ub370\uc774\ud130\ub97c \uc218\uc2e0\ud569\ub2c8\ub2e4." + } + } } } \ No newline at end of file diff --git a/homeassistant/components/shelly/translations/no.json b/homeassistant/components/shelly/translations/no.json index 4b1390b4dfe..4d08095058a 100644 --- a/homeassistant/components/shelly/translations/no.json +++ b/homeassistant/components/shelly/translations/no.json @@ -33,7 +33,7 @@ "data": { "host": "Vert" }, - "description": "F\u00f8r du setter opp, m\u00e5 batteridrevne enheter vekkes, du kan n\u00e5 vekke enheten med en knapp p\u00e5 den." + "description": "F\u00f8r oppsett m\u00e5 batteridrevne enheter vekkes, du kan n\u00e5 vekke enheten ved \u00e5 bruke en knapp p\u00e5 den." } } }, diff --git a/homeassistant/components/shelly/translations/pl.json b/homeassistant/components/shelly/translations/pl.json index dd7d9d10486..e2aefb4ac7a 100644 --- a/homeassistant/components/shelly/translations/pl.json +++ b/homeassistant/components/shelly/translations/pl.json @@ -58,5 +58,18 @@ "single_push": "{subtype} zostanie pojedynczo naci\u015bni\u0119ty", "triple": "{subtype} zostanie trzykrotnie naci\u015bni\u0119ty" } + }, + "options": { + "abort": { + "ble_unsupported": "Obs\u0142uga Bluetooth wymaga oprogramowania w wersji {ble_min_version} lub nowszej." + }, + "step": { + "init": { + "data": { + "ble_scanner_mode": "Tryb skanera Bluetooth" + }, + "description": "Skanowanie Bluetooth mo\u017ce by\u0107 aktywne lub pasywne. Gdy jest aktywne, Shelly \u017c\u0105da danych z pobliskich urz\u0105dze\u0144; z pasywnym Shelly otrzymuje niechciane dane z pobliskich urz\u0105dze\u0144." + } + } } } \ No newline at end of file diff --git a/homeassistant/components/shelly/translations/pt.json b/homeassistant/components/shelly/translations/pt.json index 43f18a5c9d6..a1658f8d8a7 100644 --- a/homeassistant/components/shelly/translations/pt.json +++ b/homeassistant/components/shelly/translations/pt.json @@ -1,10 +1,11 @@ { "config": { "abort": { - "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", + "reauth_successful": "Reautentica\u00e7\u00e3o bem sucedida" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, @@ -19,9 +20,15 @@ "username": "Nome de Utilizador" } }, + "reauth_confirm": { + "data": { + "password": "Palavra-passe", + "username": "Nome de Utilizador" + } + }, "user": { "data": { - "host": "Servidor" + "host": "Endere\u00e7o" } } } diff --git a/homeassistant/components/shelly/translations/sk.json b/homeassistant/components/shelly/translations/sk.json index 1a36257a3a5..b6285c6ab98 100644 --- a/homeassistant/components/shelly/translations/sk.json +++ b/homeassistant/components/shelly/translations/sk.json @@ -2,6 +2,8 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", + "reauth_unsuccessful": "Op\u00e4tovn\u00e1 autentifik\u00e1cia bola ne\u00faspe\u0161n\u00e1, odstr\u00e1\u0148te integr\u00e1ciu a znova ju nastavte.", "unsupported_firmware": "Zariadenie pou\u017e\u00edva nepodporovan\u00fa verziu firmv\u00e9ru." }, "error": { @@ -23,7 +25,8 @@ }, "reauth_confirm": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } }, "user": { @@ -59,6 +62,14 @@ "options": { "abort": { "ble_unsupported": "Podpora Bluetooth vy\u017eaduje verziu firmv\u00e9ru {ble_min_version} alebo nov\u0161iu." + }, + "step": { + "init": { + "data": { + "ble_scanner_mode": "Re\u017eim skenovania Bluetooth" + }, + "description": "Bluetooth skenovanie m\u00f4\u017ee by\u0165 akt\u00edvne alebo pas\u00edvne. Ke\u010f je akt\u00edvna, Shelly po\u017eaduje \u00fadaje z bl\u00edzkych zariaden\u00ed; s pas\u00edvnym, Shelly prij\u00edma nevy\u017eiadan\u00e9 d\u00e1ta z okolit\u00fdch zariaden\u00ed." + } } } } \ No newline at end of file diff --git a/homeassistant/components/shelly/update.py b/homeassistant/components/shelly/update.py index d801d0d03b3..58bd3b4b8b1 100644 --- a/homeassistant/components/shelly/update.py +++ b/homeassistant/components/shelly/update.py @@ -251,9 +251,7 @@ class RpcUpdateEntity(ShellyRpcAttributeEntity, UpdateEntity): @property def latest_version(self) -> str | None: """Latest version available for install.""" - new_version = self.entity_description.latest_version( - self.coordinator.device.status[self.key][self.entity_description.sub_key], - ) + new_version = self.entity_description.latest_version(self.sub_status) if new_version: return cast(str, new_version) diff --git a/homeassistant/components/shelly/utils.py b/homeassistant/components/shelly/utils.py index e13395999b1..b048b219e6b 100644 --- a/homeassistant/components/shelly/utils.py +++ b/homeassistant/components/shelly/utils.py @@ -298,7 +298,7 @@ def get_rpc_channel_name(device: RpcDevice, key: str) -> str: entity_name = device.config[key].get("name", device_name) if entity_name is None: - if key.startswith(("input:", "switch:")): + if key.startswith(("input:", "light:", "switch:")): return f"{device_name} {key.replace(':', '_')}" return device_name @@ -398,3 +398,19 @@ def device_update_info( dev_registry.async_update_device( device.id, sw_version=shellydevice.firmware_version ) + + +def brightness_to_percentage(brightness: int) -> int: + """Convert brightness level to percentage.""" + return int(100 * (brightness + 1) / 255) + + +def percentage_to_brightness(percentage: int) -> int: + """Convert percentage to brightness level.""" + return round(255 * percentage / 100) + + +def mac_address_from_name(name: str) -> str | None: + """Convert a name to a mac address.""" + mac = name.partition(".")[0].partition("-")[-1] + return mac.upper() if len(mac) == 12 else None diff --git a/homeassistant/components/shopping_list/__init__.py b/homeassistant/components/shopping_list/__init__.py index 0f7afe3240e..3c478a3c83a 100644 --- a/homeassistant/components/shopping_list/__init__.py +++ b/homeassistant/components/shopping_list/__init__.py @@ -25,6 +25,7 @@ from .const import ( SERVICE_COMPLETE_ITEM, SERVICE_INCOMPLETE_ALL, SERVICE_INCOMPLETE_ITEM, + SERVICE_REMOVE_ITEM, ) ATTR_COMPLETE = "complete" @@ -34,35 +35,9 @@ CONFIG_SCHEMA = vol.Schema({DOMAIN: {}}, extra=vol.ALLOW_EXTRA) ITEM_UPDATE_SCHEMA = vol.Schema({ATTR_COMPLETE: bool, ATTR_NAME: str}) PERSISTENCE = ".shopping_list.json" -SERVICE_ITEM_SCHEMA = vol.Schema({vol.Required(ATTR_NAME): vol.Any(None, cv.string)}) +SERVICE_ITEM_SCHEMA = vol.Schema({vol.Required(ATTR_NAME): cv.string}) SERVICE_LIST_SCHEMA = vol.Schema({}) -WS_TYPE_SHOPPING_LIST_ITEMS = "shopping_list/items" -WS_TYPE_SHOPPING_LIST_ADD_ITEM = "shopping_list/items/add" -WS_TYPE_SHOPPING_LIST_UPDATE_ITEM = "shopping_list/items/update" -WS_TYPE_SHOPPING_LIST_CLEAR_ITEMS = "shopping_list/items/clear" - -SCHEMA_WEBSOCKET_ITEMS = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend( - {vol.Required("type"): WS_TYPE_SHOPPING_LIST_ITEMS} -) - -SCHEMA_WEBSOCKET_ADD_ITEM = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend( - {vol.Required("type"): WS_TYPE_SHOPPING_LIST_ADD_ITEM, vol.Required("name"): str} -) - -SCHEMA_WEBSOCKET_UPDATE_ITEM = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend( - { - vol.Required("type"): WS_TYPE_SHOPPING_LIST_UPDATE_ITEM, - vol.Required("item_id"): str, - vol.Optional("name"): str, - vol.Optional("complete"): bool, - } -) - -SCHEMA_WEBSOCKET_CLEAR_ITEMS = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend( - {vol.Required("type"): WS_TYPE_SHOPPING_LIST_CLEAR_ITEMS} -) - async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Initialize the shopping list.""" @@ -85,26 +60,37 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b async def add_item_service(call: ServiceCall) -> None: """Add an item with `name`.""" data = hass.data[DOMAIN] - if (name := call.data.get(ATTR_NAME)) is not None: - await data.async_add(name) + await data.async_add(call.data[ATTR_NAME]) - async def complete_item_service(call: ServiceCall) -> None: - """Mark the item provided via `name` as completed.""" + async def remove_item_service(call: ServiceCall) -> None: + """Remove the first item with matching `name`.""" data = hass.data[DOMAIN] - if (name := call.data.get(ATTR_NAME)) is None: - return + name = call.data[ATTR_NAME] + try: item = [item for item in data.items if item["name"] == name][0] except IndexError: _LOGGER.error("Removing of item failed: %s cannot be found", name) + else: + await data.async_remove(item["id"]) + + async def complete_item_service(call: ServiceCall) -> None: + """Mark the first item with matching `name` as completed.""" + data = hass.data[DOMAIN] + name = call.data[ATTR_NAME] + + try: + item = [item for item in data.items if item["name"] == name][0] + except IndexError: + _LOGGER.error("Updating of item failed: %s cannot be found", name) else: await data.async_update(item["id"], {"name": name, "complete": True}) async def incomplete_item_service(call: ServiceCall) -> None: - """Mark the item provided via `name` as incomplete.""" + """Mark the first item with matching `name` as incomplete.""" data = hass.data[DOMAIN] - if (name := call.data.get(ATTR_NAME)) is None: - return + name = call.data[ATTR_NAME] + try: item = [item for item in data.items if item["name"] == name][0] except IndexError: @@ -130,6 +116,9 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b hass.services.async_register( DOMAIN, SERVICE_ADD_ITEM, add_item_service, schema=SERVICE_ITEM_SCHEMA ) + hass.services.async_register( + DOMAIN, SERVICE_REMOVE_ITEM, remove_item_service, schema=SERVICE_ITEM_SCHEMA + ) hass.services.async_register( DOMAIN, SERVICE_COMPLETE_ITEM, complete_item_service, schema=SERVICE_ITEM_SCHEMA ) @@ -167,36 +156,20 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b hass, "shopping-list", "shopping_list", "mdi:cart" ) - websocket_api.async_register_command( - hass, - WS_TYPE_SHOPPING_LIST_ITEMS, - websocket_handle_items, - SCHEMA_WEBSOCKET_ITEMS, - ) - websocket_api.async_register_command( - hass, - WS_TYPE_SHOPPING_LIST_ADD_ITEM, - websocket_handle_add, - SCHEMA_WEBSOCKET_ADD_ITEM, - ) - websocket_api.async_register_command( - hass, - WS_TYPE_SHOPPING_LIST_UPDATE_ITEM, - websocket_handle_update, - SCHEMA_WEBSOCKET_UPDATE_ITEM, - ) - websocket_api.async_register_command( - hass, - WS_TYPE_SHOPPING_LIST_CLEAR_ITEMS, - websocket_handle_clear, - SCHEMA_WEBSOCKET_CLEAR_ITEMS, - ) - + websocket_api.async_register_command(hass, websocket_handle_items) + websocket_api.async_register_command(hass, websocket_handle_add) + websocket_api.async_register_command(hass, websocket_handle_remove) + websocket_api.async_register_command(hass, websocket_handle_update) + websocket_api.async_register_command(hass, websocket_handle_clear) websocket_api.async_register_command(hass, websocket_handle_reorder) return True +class NoMatchingShoppingListItem(Exception): + """No matching item could be found in the shopping list.""" + + class ShoppingData: """Class to hold shopping list data.""" @@ -217,12 +190,28 @@ class ShoppingData: ) return item + async def async_remove(self, item_id, context=None): + """Remove a shopping list item.""" + item = next((itm for itm in self.items if itm["id"] == item_id), None) + + if item is None: + raise NoMatchingShoppingListItem + + self.items.remove(item) + await self.hass.async_add_executor_job(self.save) + self.hass.bus.async_fire( + EVENT_SHOPPING_LIST_UPDATED, + {"action": "remove", "item": item}, + context=context, + ) + return item + async def async_update(self, item_id, info, context=None): """Update a shopping list item.""" item = next((itm for itm in self.items if itm["id"] == item_id), None) if item is None: - raise KeyError + raise NoMatchingShoppingListItem info = ITEM_UPDATE_SCHEMA(info) item.update(info) @@ -265,7 +254,7 @@ class ShoppingData: # Append items by the order of passed in array. for item_id in item_ids: if item_id not in all_items_mapping: - raise KeyError + raise NoMatchingShoppingListItem new_items.append(all_items_mapping[item_id]) # Remove the item from mapping after it's appended in the result array. del all_items_mapping[item_id] @@ -275,7 +264,8 @@ class ShoppingData: # so all items left in the mapping should be checked items. if all_items_mapping[key]["complete"] is False: raise vol.Invalid( - "The item ids array doesn't contain all the unchecked shopping list items." + "The item ids array doesn't contain all the unchecked shopping list" + " items." ) new_items.append(all_items_mapping[key]) self.items = new_items @@ -325,7 +315,7 @@ class UpdateShoppingListItemView(http.HomeAssistantView): try: item = await request.app["hass"].data[DOMAIN].async_update(item_id, data) return self.json(item) - except KeyError: + except NoMatchingShoppingListItem: return self.json_message("Item not found", HTTPStatus.NOT_FOUND) except vol.Invalid: return self.json_message("Item not found", HTTPStatus.BAD_REQUEST) @@ -358,35 +348,72 @@ class ClearCompletedItemsView(http.HomeAssistantView): @callback +@websocket_api.websocket_command({vol.Required("type"): "shopping_list/items"}) def websocket_handle_items( hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any], ) -> None: - """Handle get shopping_list items.""" + """Handle getting shopping_list items.""" connection.send_message( websocket_api.result_message(msg["id"], hass.data[DOMAIN].items) ) +@websocket_api.websocket_command( + {vol.Required("type"): "shopping_list/items/add", vol.Required("name"): str} +) @websocket_api.async_response async def websocket_handle_add( hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any], ) -> None: - """Handle add item to shopping_list.""" + """Handle adding item to shopping_list.""" item = await hass.data[DOMAIN].async_add(msg["name"], connection.context(msg)) connection.send_message(websocket_api.result_message(msg["id"], item)) +@websocket_api.websocket_command( + {vol.Required("type"): "shopping_list/items/remove", vol.Required("item_id"): str} +) +@websocket_api.async_response +async def websocket_handle_remove( + hass: HomeAssistant, + connection: websocket_api.ActiveConnection, + msg: dict[str, Any], +) -> None: + """Handle removing shopping_list item.""" + msg_id = msg.pop("id") + item_id = msg.pop("item_id") + msg.pop("type") + + try: + item = await hass.data[DOMAIN].async_remove(item_id, connection.context(msg)) + except NoMatchingShoppingListItem: + connection.send_message( + websocket_api.error_message(msg_id, "item_not_found", "Item not found") + ) + return + + connection.send_message(websocket_api.result_message(msg_id, item)) + + +@websocket_api.websocket_command( + { + vol.Required("type"): "shopping_list/items/update", + vol.Required("item_id"): str, + vol.Optional("name"): str, + vol.Optional("complete"): bool, + } +) @websocket_api.async_response async def websocket_handle_update( hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any], ) -> None: - """Handle update shopping_list item.""" + """Handle updating shopping_list item.""" msg_id = msg.pop("id") item_id = msg.pop("item_id") msg.pop("type") @@ -396,13 +423,16 @@ async def websocket_handle_update( item = await hass.data[DOMAIN].async_update( item_id, data, connection.context(msg) ) - connection.send_message(websocket_api.result_message(msg_id, item)) - except KeyError: + except NoMatchingShoppingListItem: connection.send_message( websocket_api.error_message(msg_id, "item_not_found", "Item not found") ) + return + + connection.send_message(websocket_api.result_message(msg_id, item)) +@websocket_api.websocket_command({vol.Required("type"): "shopping_list/items/clear"}) @websocket_api.async_response async def websocket_handle_clear( hass: HomeAssistant, @@ -429,12 +459,15 @@ def websocket_handle_reorder( msg_id = msg.pop("id") try: hass.data[DOMAIN].async_reorder(msg.pop("item_ids"), connection.context(msg)) - connection.send_result(msg_id) - except KeyError: + except NoMatchingShoppingListItem: connection.send_error( msg_id, websocket_api.const.ERR_NOT_FOUND, "One or more item id(s) not found.", ) + return except vol.Invalid as err: connection.send_error(msg_id, websocket_api.const.ERR_INVALID_FORMAT, f"{err}") + return + + connection.send_result(msg_id) diff --git a/homeassistant/components/shopping_list/const.py b/homeassistant/components/shopping_list/const.py index fffc1064226..05dc05137c0 100644 --- a/homeassistant/components/shopping_list/const.py +++ b/homeassistant/components/shopping_list/const.py @@ -3,6 +3,7 @@ DOMAIN = "shopping_list" EVENT_SHOPPING_LIST_UPDATED = "shopping_list_updated" SERVICE_ADD_ITEM = "add_item" +SERVICE_REMOVE_ITEM = "remove_item" SERVICE_COMPLETE_ITEM = "complete_item" SERVICE_INCOMPLETE_ITEM = "incomplete_item" SERVICE_COMPLETE_ALL = "complete_all" diff --git a/homeassistant/components/shopping_list/intent.py b/homeassistant/components/shopping_list/intent.py index 4f5a39171b8..c709322e0b7 100644 --- a/homeassistant/components/shopping_list/intent.py +++ b/homeassistant/components/shopping_list/intent.py @@ -1,4 +1,6 @@ """Intents for the Shopping List integration.""" +from __future__ import annotations + from homeassistant.helpers import intent import homeassistant.helpers.config_validation as cv @@ -20,7 +22,7 @@ class AddItemIntent(intent.IntentHandler): intent_type = INTENT_ADD_ITEM slot_schema = {"item": cv.string} - async def async_handle(self, intent_obj): + async def async_handle(self, intent_obj: intent.Intent): """Handle the intent.""" slots = self.async_validate_slots(intent_obj.slots) item = slots["item"]["value"] @@ -38,7 +40,7 @@ class ListTopItemsIntent(intent.IntentHandler): intent_type = INTENT_LAST_ITEMS slot_schema = {"item": cv.string} - async def async_handle(self, intent_obj): + async def async_handle(self, intent_obj: intent.Intent): """Handle the intent.""" items = intent_obj.hass.data[DOMAIN].items[-5:] response = intent_obj.create_response() diff --git a/homeassistant/components/shopping_list/services.yaml b/homeassistant/components/shopping_list/services.yaml index 0af388cfcb1..c41bc1333dc 100644 --- a/homeassistant/components/shopping_list/services.yaml +++ b/homeassistant/components/shopping_list/services.yaml @@ -10,9 +10,21 @@ add_item: selector: text: +remove_item: + name: Remove item + description: Remove the first item with matching name from the shopping list. + fields: + name: + name: Name + description: The name of the item to remove. + required: true + example: Beer + selector: + text: + complete_item: name: Complete item - description: Mark an item as completed in the shopping list. + description: Mark the first item with matching name as completed in the shopping list. fields: name: name: Name @@ -24,7 +36,7 @@ complete_item: incomplete_item: name: Incomplete item - description: Marks an item as incomplete in the shopping list. + description: Mark the first item with matching name as incomplete in the shopping list. fields: name: description: The name of the item to mark as incomplete. @@ -35,11 +47,11 @@ incomplete_item: complete_all: name: Complete all - description: Marks all items as completed in the shopping list. It does not remove the items. + description: Mark all items as completed in the shopping list (without removing them from the list). incomplete_all: name: Incomplete all - description: Marks all items as incomplete in the shopping list. + description: Mark all items as incomplete in the shopping list. clear_completed_items: name: Clear completed items diff --git a/homeassistant/components/sia/translations/de.json b/homeassistant/components/sia/translations/de.json index 385e19b9c64..3aa36279ce1 100644 --- a/homeassistant/components/sia/translations/de.json +++ b/homeassistant/components/sia/translations/de.json @@ -1,7 +1,7 @@ { "config": { "error": { - "invalid_account_format": "Das Konto ist kein Hex-Wert. Bitte verwende nur 0-9 und A-F.", + "invalid_account_format": "Das Konto ist kein Hex-Wert. Bitte verwende nur 0\u20139 und A\u2013F.", "invalid_account_length": "Das Konto hat nicht die richtige L\u00e4nge. Es muss zwischen 3 und 16 Zeichen lang sein.", "invalid_key_format": "Der Schl\u00fcssel ist kein Hex-Wert, bitte verwende nur 0-9 und A-F.", "invalid_key_length": "Der Schl\u00fcssel hat nicht die richtige L\u00e4nge. Er muss 16, 24 oder 32 Hex-Zeichen lang sein.", @@ -38,11 +38,11 @@ "step": { "options": { "data": { - "ignore_timestamps": "Ignorieren der Zeitstempelpr\u00fcfung der SIA-Ereignisse", + "ignore_timestamps": "Ignorieren der Zeitstempelpr\u00fcfung der SIA Ereignisse", "zones": "Anzahl an Zonen f\u00fcr das Konto" }, "description": "Stelle die Optionen f\u00fcr das Konto {account} ein:", - "title": "Optionen f\u00fcr das SIA-Setup." + "title": "Optionen f\u00fcr das SIA Setup." } } } diff --git a/homeassistant/components/sia/translations/ko.json b/homeassistant/components/sia/translations/ko.json index cb0897c7873..5035d862c03 100644 --- a/homeassistant/components/sia/translations/ko.json +++ b/homeassistant/components/sia/translations/ko.json @@ -6,7 +6,15 @@ "invalid_key_format": "\ud0a4\uac12\uc774 16\uc9c4\uc218 \uac12\uc774 \uc544\ub2d9\ub2c8\ub2e4. 0-9 \ubc0f A-F\ub9cc \uc0ac\uc6a9\ud558\uc2ed\uc2dc\uc624.", "invalid_key_length": "\ud0a4\uac12\uc758 \uae38\uc774\uac00 \uc801\uc808\uce58 \uc54a\uc2b5\ub2c8\ub2e4. 16, 24 \ub610\ub294 32\uac1c\uc758 16\uc9c4\uc218\ubb38\uc790\uc5ec\uc57c \ub429\ub2c8\ub2e4.", "invalid_ping": "\ud551 \uac04\uaca9\uc740 1\ubd84\uc5d0\uc11c 1440\ubd84 \uc0ac\uc774\uc5ec\uc57c \ud569\ub2c8\ub2e4.", - "invalid_zones": "\ucd5c\uc18c\ud55c 1\uac1c\uc758 \uc601\uc5ed\uc774 \uc788\uc5b4\uc57c \ud569\ub2c8\ub2e4." + "invalid_zones": "\ucd5c\uc18c\ud55c 1\uac1c\uc758 \uc601\uc5ed\uc774 \uc788\uc5b4\uc57c \ud569\ub2c8\ub2e4.", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "port": "\ud3ec\ud2b8" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/sia/translations/sk.json b/homeassistant/components/sia/translations/sk.json index 4f1a4b7b38a..defb7306228 100644 --- a/homeassistant/components/sia/translations/sk.json +++ b/homeassistant/components/sia/translations/sk.json @@ -6,23 +6,43 @@ "invalid_key_format": "K\u013e\u00fa\u010d nie je hexadecim\u00e1lna hodnota, pou\u017e\u00edvajte len 0-9 a A-F.", "invalid_key_length": "K\u013e\u00fa\u010d nem\u00e1 spr\u00e1vnu d\u013a\u017eku, mus\u00ed ma\u0165 16, 24 alebo 32 hexadecim\u00e1lnych znakov.", "invalid_ping": "Interval pingu mus\u00ed by\u0165 medzi 1 a 1440 min\u00fatami.", + "invalid_zones": "Mus\u00ed existova\u0165 aspo\u0148 1 z\u00f3na.", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "additional_account": { "data": { "account": "ID \u00fa\u010dtu", - "ping_interval": "Ping Interval (min)" + "additional_account": "\u010eal\u0161ie \u00fa\u010dty", + "encryption_key": "\u0160ifrovac\u00ed k\u013e\u00fa\u010d", + "ping_interval": "Ping Interval (min)", + "zones": "Po\u010det z\u00f3n pre \u00fa\u010det" }, "title": "Pridajte \u010fal\u0161\u00ed \u00fa\u010det k aktu\u00e1lnemu portu." }, "user": { "data": { "account": "ID \u00fa\u010dtu", + "additional_account": "\u010eal\u0161ie \u00fa\u010dty", + "encryption_key": "\u0160ifrovac\u00ed k\u013e\u00fa\u010d", "ping_interval": "Ping Interval (min)", "port": "Port", - "protocol": "Protokol" - } + "protocol": "Protokol", + "zones": "Po\u010det z\u00f3n pre \u00fa\u010det" + }, + "title": "Vytvorte pripojenie pre popla\u0161n\u00e9 syst\u00e9my zalo\u017een\u00e9 na SIA." + } + } + }, + "options": { + "step": { + "options": { + "data": { + "ignore_timestamps": "Ignorovanie kontroly \u010dasovej zna\u010dky udalost\u00ed SIA", + "zones": "Po\u010det z\u00f3n pre \u00fa\u010det" + }, + "description": "Nastavte mo\u017enosti pre \u00fa\u010det: {account}", + "title": "Mo\u017enosti nastavenia SIA." } } } diff --git a/homeassistant/components/signal_messenger/notify.py b/homeassistant/components/signal_messenger/notify.py index 7f48d83ba19..25dae1617c3 100644 --- a/homeassistant/components/signal_messenger/notify.py +++ b/homeassistant/components/signal_messenger/notify.py @@ -151,7 +151,8 @@ class SignalNotificationService(BaseNotificationService): > attachment_size_limit ): raise ValueError( - "Attachment too large (Content-Length reports {}). Max size: {} bytes".format( + "Attachment too large (Content-Length reports {}). Max size: {}" + " bytes".format( int(str(resp.headers.get("Content-Length"))), CONF_MAX_ALLOWED_DOWNLOAD_SIZE_BYTES, ) @@ -163,9 +164,8 @@ class SignalNotificationService(BaseNotificationService): size += len(chunk) if size > attachment_size_limit: raise ValueError( - "Attachment too large (Stream reports {}). Max size: {} bytes".format( - size, CONF_MAX_ALLOWED_DOWNLOAD_SIZE_BYTES - ) + "Attachment too large (Stream reports {}). Max size: {}" + " bytes".format(size, CONF_MAX_ALLOWED_DOWNLOAD_SIZE_BYTES) ) chunks.extend(chunk) diff --git a/homeassistant/components/simplepush/translations/ko.json b/homeassistant/components/simplepush/translations/ko.json new file mode 100644 index 00000000000..9ee47d96f7e --- /dev/null +++ b/homeassistant/components/simplepush/translations/ko.json @@ -0,0 +1,17 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "name": "\uc774\ub984" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/simplepush/translations/pt.json b/homeassistant/components/simplepush/translations/pt.json index d7e598b33e4..9ecd0c7de35 100644 --- a/homeassistant/components/simplepush/translations/pt.json +++ b/homeassistant/components/simplepush/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "user": { diff --git a/homeassistant/components/simplepush/translations/sk.json b/homeassistant/components/simplepush/translations/sk.json index 6d26a95a166..b1d44cd6bb1 100644 --- a/homeassistant/components/simplepush/translations/sk.json +++ b/homeassistant/components/simplepush/translations/sk.json @@ -9,9 +9,19 @@ "step": { "user": { "data": { - "name": "N\u00e1zov" + "device_key": "K\u013e\u00fa\u010d zariadenia v\u00e1\u0161ho zariadenia", + "event": "Udalos\u0165 pre udalosti.", + "name": "N\u00e1zov", + "password": "Heslo \u0161ifrovania pou\u017e\u00edvan\u00e9ho va\u0161\u00edm zariaden\u00edm", + "salt": "So\u013e pou\u017e\u00edvan\u00e1 vo va\u0161om zariaden\u00ed." } } } + }, + "issues": { + "removed_yaml": { + "description": "Konfigur\u00e1cia Simplepush pomocou YAML bola odstr\u00e1nen\u00e1. \n\n Asistent dom\u00e1cnosti nepou\u017e\u00edva va\u0161u existuj\u00facu konfigur\u00e1ciu YAML. \n\n Odstr\u00e1\u0148te konfigur\u00e1ciu Simplepush YAML zo s\u00faboru configuration.yaml a re\u0161tartujte Home Assistant, aby ste tento probl\u00e9m vyrie\u0161ili.", + "title": "Konfigur\u00e1cia Simplepush YAML bola odstr\u00e1nen\u00e1" + } } } \ No newline at end of file diff --git a/homeassistant/components/simplisafe/sensor.py b/homeassistant/components/simplisafe/sensor.py index 39566434f27..949d8890398 100644 --- a/homeassistant/components/simplisafe/sensor.py +++ b/homeassistant/components/simplisafe/sensor.py @@ -11,7 +11,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import TEMP_FAHRENHEIT +from homeassistant.const import UnitOfTemperature from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -42,7 +42,7 @@ class SimplisafeFreezeSensor(SimpliSafeEntity, SensorEntity): """Define a SimpliSafe freeze sensor entity.""" _attr_device_class = SensorDeviceClass.TEMPERATURE - _attr_native_unit_of_measurement = TEMP_FAHRENHEIT + _attr_native_unit_of_measurement = UnitOfTemperature.FAHRENHEIT _attr_state_class = SensorStateClass.MEASUREMENT def __init__( diff --git a/homeassistant/components/simplisafe/translations/sk.json b/homeassistant/components/simplisafe/translations/sk.json index 93b939b88e1..a70e0ee0f22 100644 --- a/homeassistant/components/simplisafe/translations/sk.json +++ b/homeassistant/components/simplisafe/translations/sk.json @@ -2,18 +2,21 @@ "config": { "abort": { "already_configured": "Toto konto SimpliSafe sa u\u017e pou\u017e\u00edva.", - "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", + "wrong_account": "Poskytnut\u00e9 poverenia pou\u017e\u00edvate\u013ea sa nezhoduj\u00fa s t\u00fdmto \u00fa\u010dtom SimpliSafe." }, "error": { "identifier_exists": "\u00da\u010det je u\u017e zaregistrovan\u00fd", "invalid_auth": "Neplatn\u00e9 overenie", + "invalid_auth_code_length": "Autoriza\u010dn\u00e9 k\u00f3dy SimpliSafe maj\u00fa d\u013a\u017eku 45 znakov", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { "data": { "auth_code": "Autoriza\u010dn\u00fd k\u00f3d" - } + }, + "description": "SimpliSafe overuje pou\u017e\u00edvate\u013eov prostredn\u00edctvom svojej webovej aplik\u00e1cie. Kv\u00f4li technick\u00fdm obmedzeniam je na konci tohto procesu manu\u00e1lny krok; pred spusten\u00edm sa uistite, \u017ee ste si pre\u010d\u00edtali [dokument\u00e1ciu](http://home-assistant.io/integrations/simplisafe#getting-an-authorization-code). \n\nKe\u010f budete pripraven\u00ed, kliknut\u00edm [sem]({url}) otvorte webov\u00fa aplik\u00e1ciu SimpliSafe a zadajte svoje poverenia. Ak ste sa u\u017e vo svojom prehliada\u010di prihl\u00e1sili do SimpliSafe, mo\u017eno budete chcie\u0165 otvori\u0165 nov\u00fa kartu a potom do nej skop\u00edrujte/prilepte vy\u0161\u0161ie uveden\u00fa adresu URL. \n\nPo dokon\u010den\u00ed procesu sa vr\u00e1\u0165te sem a zadajte autoriza\u010dn\u00fd k\u00f3d z adresy URL com.simplisafe.mobile." } } }, @@ -22,5 +25,15 @@ "description": "Aktualizujte v\u0161etky automatiz\u00e1cie alebo skripty, ktor\u00e9 pou\u017e\u00edvaj\u00fa t\u00fato slu\u017ebu, aby namiesto nej pou\u017e\u00edvali slu\u017ebu `{alternate_service}` s ID cie\u013eovej entity `{alternate_target}`. Potom kliknite na tla\u010didlo SUBMIT (Odosla\u0165) ni\u017e\u0161ie a ozna\u010dte tento probl\u00e9m ako vyrie\u0161en\u00fd.", "title": "Slu\u017eba {deprecated_service} sa odstra\u0148uje" } + }, + "options": { + "step": { + "init": { + "data": { + "code": "K\u00f3d (pou\u017eit\u00fd v rozhran\u00ed Home Assistant)" + }, + "title": "Nakonfigurujte SimpliSafe" + } + } } } \ No newline at end of file diff --git a/homeassistant/components/siren/translations/sk.json b/homeassistant/components/siren/translations/sk.json new file mode 100644 index 00000000000..50303faf105 --- /dev/null +++ b/homeassistant/components/siren/translations/sk.json @@ -0,0 +1,3 @@ +{ + "title": "Sir\u00e9na" +} \ No newline at end of file diff --git a/homeassistant/components/skybeacon/sensor.py b/homeassistant/components/skybeacon/sensor.py index 367c5b87a10..0ff6ebd41cd 100644 --- a/homeassistant/components/skybeacon/sensor.py +++ b/homeassistant/components/skybeacon/sensor.py @@ -21,7 +21,7 @@ from homeassistant.const import ( EVENT_HOMEASSISTANT_STOP, PERCENTAGE, STATE_UNKNOWN, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv @@ -105,7 +105,7 @@ class SkybeaconTemp(SensorEntity): """Representation of a Skybeacon temperature sensor.""" _attr_device_class = SensorDeviceClass.TEMPERATURE - _attr_native_unit_of_measurement = TEMP_CELSIUS + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS def __init__(self, name, mon): """Initialize a sensor.""" diff --git a/homeassistant/components/skybell/coordinator.py b/homeassistant/components/skybell/coordinator.py index 26545609bd5..55e34df5c63 100644 --- a/homeassistant/components/skybell/coordinator.py +++ b/homeassistant/components/skybell/coordinator.py @@ -11,7 +11,7 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda from .const import LOGGER -class SkybellDataUpdateCoordinator(DataUpdateCoordinator): +class SkybellDataUpdateCoordinator(DataUpdateCoordinator[None]): """Data update coordinator for the Skybell integration.""" config_entry: ConfigEntry diff --git a/homeassistant/components/skybell/translations/ko.json b/homeassistant/components/skybell/translations/ko.json new file mode 100644 index 00000000000..2f4feeac5f1 --- /dev/null +++ b/homeassistant/components/skybell/translations/ko.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638" + }, + "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" + }, + "user": { + "data": { + "email": "\uc774\uba54\uc77c", + "password": "\ube44\ubc00\ubc88\ud638" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/skybell/translations/nl.json b/homeassistant/components/skybell/translations/nl.json index 17c417b4ef9..daca32186d2 100644 --- a/homeassistant/components/skybell/translations/nl.json +++ b/homeassistant/components/skybell/translations/nl.json @@ -14,6 +14,7 @@ "data": { "password": "Wachtwoord" }, + "description": "Werk je wachtwoord bij voor {email}", "title": "Integratie herauthenticeren" }, "user": { diff --git a/homeassistant/components/skybell/translations/pt.json b/homeassistant/components/skybell/translations/pt.json index 0818ea34699..0efc4094522 100644 --- a/homeassistant/components/skybell/translations/pt.json +++ b/homeassistant/components/skybell/translations/pt.json @@ -4,9 +4,15 @@ "already_configured": "Conta j\u00e1 configurada" }, "step": { + "reauth_confirm": { + "data": { + "password": "Palavra-passe" + }, + "title": "Reautenticar integra\u00e7\u00e3o" + }, "user": { "data": { - "email": "Email", + "email": "", "password": "Palavra-passe" } } diff --git a/homeassistant/components/skybell/translations/sk.json b/homeassistant/components/skybell/translations/sk.json index e3d6831b30d..5ca96405a31 100644 --- a/homeassistant/components/skybell/translations/sk.json +++ b/homeassistant/components/skybell/translations/sk.json @@ -24,5 +24,11 @@ } } } + }, + "issues": { + "removed_yaml": { + "description": "Konfigur\u00e1cia Skybell pomocou YAML bola odstr\u00e1nen\u00e1. \n\n Asistent dom\u00e1cnosti nepou\u017e\u00edva va\u0161u existuj\u00facu konfigur\u00e1ciu YAML. \n\n Ak chcete tento probl\u00e9m vyrie\u0161i\u0165, odstr\u00e1\u0148te konfigur\u00e1ciu YAML zo s\u00faboru configuration.yaml a re\u0161tartujte aplik\u00e1ciu Home Assistant.", + "title": "Konfigur\u00e1cia Skybell YAML bola odstr\u00e1nen\u00e1" + } } } \ No newline at end of file diff --git a/homeassistant/components/slack/translations/sk.json b/homeassistant/components/slack/translations/sk.json index 7d542d6c565..e30eb565a95 100644 --- a/homeassistant/components/slack/translations/sk.json +++ b/homeassistant/components/slack/translations/sk.json @@ -17,8 +17,12 @@ "username": "U\u017e\u00edvate\u013esk\u00e9 meno" }, "data_description": { + "api_key": "The Slack API token to use for sending Slack messages.", + "default_channel": "Kan\u00e1l, na ktor\u00fd sa m\u00e1 odosla\u0165 pr\u00edspevok, ak pri odosielan\u00ed spr\u00e1vy nie je zadan\u00fd \u017eiadny kan\u00e1l.", + "icon": "Ako ikonu pre zadan\u00e9 pou\u017e\u00edvate\u013esk\u00e9 meno pou\u017eite jeden z emotikonov slu\u017eby Slack.", "username": "Home Assistant odo\u0161le pr\u00edspevok na Slack pomocou zadan\u00e9ho pou\u017e\u00edvate\u013esk\u00e9ho mena." - } + }, + "description": "Pozrite si dokument\u00e1ciu o z\u00edskan\u00ed k\u013e\u00fa\u010da Slack API." } } } diff --git a/homeassistant/components/sleepiq/number.py b/homeassistant/components/sleepiq/number.py index 01ba90360ba..1f8ef80a1f1 100644 --- a/homeassistant/components/sleepiq/number.py +++ b/homeassistant/components/sleepiq/number.py @@ -47,7 +47,10 @@ async def _async_set_actuator_position( def _get_actuator_name(bed: SleepIQBed, actuator: SleepIQActuator) -> str: if actuator.side: - return f"SleepNumber {bed.name} {actuator.side_full} {actuator.actuator_full} {ENTITY_TYPES[ACTUATOR]}" + return ( + "SleepNumber" + f" {bed.name} {actuator.side_full} {actuator.actuator_full} {ENTITY_TYPES[ACTUATOR]}" + ) return f"SleepNumber {bed.name} {actuator.actuator_full} {ENTITY_TYPES[ACTUATOR]}" diff --git a/homeassistant/components/sleepiq/translations/de.json b/homeassistant/components/sleepiq/translations/de.json index 280b0bf2ba7..8a1c0d25c8e 100644 --- a/homeassistant/components/sleepiq/translations/de.json +++ b/homeassistant/components/sleepiq/translations/de.json @@ -13,7 +13,7 @@ "data": { "password": "Passwort" }, - "description": "Die SleepIQ-Integration muss dein Konto {username} erneut authentifizieren.", + "description": "Die SleepIQ Integration muss dein Konto {username} erneut authentifizieren.", "title": "Integration erneut authentifizieren" }, "user": { diff --git a/homeassistant/components/sleepiq/translations/ko.json b/homeassistant/components/sleepiq/translations/ko.json new file mode 100644 index 00000000000..e3fd074ac08 --- /dev/null +++ b/homeassistant/components/sleepiq/translations/ko.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638" + }, + "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" + }, + "user": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sleepiq/translations/pt.json b/homeassistant/components/sleepiq/translations/pt.json index cf42abdb666..102dbe88615 100644 --- a/homeassistant/components/sleepiq/translations/pt.json +++ b/homeassistant/components/sleepiq/translations/pt.json @@ -5,7 +5,7 @@ "reauth_successful": "Reautentica\u00e7\u00e3o bem sucedida" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida" }, "step": { diff --git a/homeassistant/components/sleepiq/translations/sk.json b/homeassistant/components/sleepiq/translations/sk.json index 249ddf45926..9db1c30e1eb 100644 --- a/homeassistant/components/sleepiq/translations/sk.json +++ b/homeassistant/components/sleepiq/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", @@ -12,6 +13,7 @@ "data": { "password": "Heslo" }, + "description": "Integr\u00e1cia SleepIQ vy\u017eaduje op\u00e4tovn\u00e9 overenie v\u00e1\u0161ho \u00fa\u010dtu {username}.", "title": "Znova overi\u0165 integr\u00e1ciu" }, "user": { diff --git a/homeassistant/components/slimproto/translations/ko.json b/homeassistant/components/slimproto/translations/ko.json new file mode 100644 index 00000000000..416fb8d164e --- /dev/null +++ b/homeassistant/components/slimproto/translations/ko.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/slimproto/translations/sk.json b/homeassistant/components/slimproto/translations/sk.json new file mode 100644 index 00000000000..7342d09d253 --- /dev/null +++ b/homeassistant/components/slimproto/translations/sk.json @@ -0,0 +1,15 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, + "step": { + "user": { + "few": "Pr\u00e1zdnych", + "many": "Pr\u00e1zdnych", + "one": "Pr\u00e1zdny", + "other": "Pr\u00e1zdny" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sma/sensor.py b/homeassistant/components/sma/sensor.py index b06ec499a24..2987d2648c2 100644 --- a/homeassistant/components/sma/sensor.py +++ b/homeassistant/components/sma/sensor.py @@ -11,7 +11,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ENERGY_KILO_WATT_HOUR, POWER_WATT +from homeassistant.const import UnitOfEnergy, UnitOfPower from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -70,10 +70,10 @@ class SMAsensor(CoordinatorEntity, SensorEntity): self._config_entry_unique_id = config_entry_unique_id self._attr_device_info = device_info - if self.native_unit_of_measurement == ENERGY_KILO_WATT_HOUR: + if self.native_unit_of_measurement == UnitOfEnergy.KILO_WATT_HOUR: self._attr_state_class = SensorStateClass.TOTAL_INCREASING self._attr_device_class = SensorDeviceClass.ENERGY - if self.native_unit_of_measurement == POWER_WATT: + if self.native_unit_of_measurement == UnitOfPower.WATT: self._attr_state_class = SensorStateClass.MEASUREMENT self._attr_device_class = SensorDeviceClass.POWER diff --git a/homeassistant/components/sma/translations/sk.json b/homeassistant/components/sma/translations/sk.json index 0196a2c43f3..21884c2cad5 100644 --- a/homeassistant/components/sma/translations/sk.json +++ b/homeassistant/components/sma/translations/sk.json @@ -7,7 +7,8 @@ "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", "cannot_retrieve_device_info": "\u00daspe\u0161ne pripojen\u00e9, ale nie je mo\u017en\u00e9 z\u00edska\u0165 inform\u00e1cie o zariaden\u00ed", - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { @@ -17,7 +18,9 @@ "password": "Heslo", "ssl": "Pou\u017e\u00edva SSL certifik\u00e1t", "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" - } + }, + "description": "Zadajte inform\u00e1cie o svojom zariaden\u00ed SMA.", + "title": "Nastavenie SMA Solar" } } } diff --git a/homeassistant/components/smappee/sensor.py b/homeassistant/components/smappee/sensor.py index ff258677b3e..951aab1bde1 100644 --- a/homeassistant/components/smappee/sensor.py +++ b/homeassistant/components/smappee/sensor.py @@ -10,12 +10,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - ELECTRIC_POTENTIAL_VOLT, - ENERGY_KILO_WATT_HOUR, - ENERGY_WATT_HOUR, - POWER_WATT, -) +from homeassistant.const import UnitOfElectricPotential, UnitOfEnergy, UnitOfPower from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -53,7 +48,7 @@ TREND_SENSORS: tuple[SmappeePollingSensorEntityDescription, ...] = ( SmappeePollingSensorEntityDescription( key="total_power", name="Total consumption - Active power", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, sensor_id="total_power", device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, @@ -62,7 +57,7 @@ TREND_SENSORS: tuple[SmappeePollingSensorEntityDescription, ...] = ( SmappeePollingSensorEntityDescription( key="alwayson", name="Always on - Active power", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, sensor_id="alwayson", device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, @@ -70,7 +65,7 @@ TREND_SENSORS: tuple[SmappeePollingSensorEntityDescription, ...] = ( SmappeePollingSensorEntityDescription( key="power_today", name="Total consumption - Today", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, sensor_id="power_today", device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, @@ -78,7 +73,7 @@ TREND_SENSORS: tuple[SmappeePollingSensorEntityDescription, ...] = ( SmappeePollingSensorEntityDescription( key="power_current_hour", name="Total consumption - Current hour", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, sensor_id="power_current_hour", device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, @@ -86,7 +81,7 @@ TREND_SENSORS: tuple[SmappeePollingSensorEntityDescription, ...] = ( SmappeePollingSensorEntityDescription( key="power_last_5_minutes", name="Total consumption - Last 5 minutes", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, sensor_id="power_last_5_minutes", device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, @@ -94,7 +89,7 @@ TREND_SENSORS: tuple[SmappeePollingSensorEntityDescription, ...] = ( SmappeePollingSensorEntityDescription( key="alwayson_today", name="Always on - Today", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, sensor_id="alwayson_today", device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, @@ -104,7 +99,7 @@ REACTIVE_SENSORS: tuple[SmappeeSensorEntityDescription, ...] = ( SmappeeSensorEntityDescription( key="total_reactive_power", name="Total consumption - Reactive power", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, sensor_id="total_reactive_power", device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, @@ -114,7 +109,7 @@ SOLAR_SENSORS: tuple[SmappeePollingSensorEntityDescription, ...] = ( SmappeePollingSensorEntityDescription( key="solar_power", name="Total production - Active power", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, sensor_id="solar_power", device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, @@ -123,7 +118,7 @@ SOLAR_SENSORS: tuple[SmappeePollingSensorEntityDescription, ...] = ( SmappeePollingSensorEntityDescription( key="solar_today", name="Total production - Today", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, sensor_id="solar_today", device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, @@ -131,7 +126,7 @@ SOLAR_SENSORS: tuple[SmappeePollingSensorEntityDescription, ...] = ( SmappeePollingSensorEntityDescription( key="solar_current_hour", name="Total production - Current hour", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, sensor_id="solar_current_hour", device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, @@ -141,7 +136,7 @@ VOLTAGE_SENSORS: tuple[SmappeeVoltageSensorEntityDescription, ...] = ( SmappeeVoltageSensorEntityDescription( key="phase_voltages_a", name="Phase voltages - A", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, sensor_id="phase_voltage_a", device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, @@ -150,7 +145,7 @@ VOLTAGE_SENSORS: tuple[SmappeeVoltageSensorEntityDescription, ...] = ( SmappeeVoltageSensorEntityDescription( key="phase_voltages_b", name="Phase voltages - B", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, sensor_id="phase_voltage_b", device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, @@ -159,7 +154,7 @@ VOLTAGE_SENSORS: tuple[SmappeeVoltageSensorEntityDescription, ...] = ( SmappeeVoltageSensorEntityDescription( key="phase_voltages_c", name="Phase voltages - C", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, sensor_id="phase_voltage_c", device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, @@ -168,7 +163,7 @@ VOLTAGE_SENSORS: tuple[SmappeeVoltageSensorEntityDescription, ...] = ( SmappeeVoltageSensorEntityDescription( key="line_voltages_a", name="Line voltages - A", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, sensor_id="line_voltage_a", device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, @@ -177,7 +172,7 @@ VOLTAGE_SENSORS: tuple[SmappeeVoltageSensorEntityDescription, ...] = ( SmappeeVoltageSensorEntityDescription( key="line_voltages_b", name="Line voltages - B", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, sensor_id="line_voltage_b", device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, @@ -186,7 +181,7 @@ VOLTAGE_SENSORS: tuple[SmappeeVoltageSensorEntityDescription, ...] = ( SmappeeVoltageSensorEntityDescription( key="line_voltages_c", name="Line voltages - C", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, sensor_id="line_voltage_c", device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, @@ -254,7 +249,7 @@ async def async_setup_entry( description=SmappeeSensorEntityDescription( key="load", name=measurement.name, - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, sensor_id=measurement_id, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, @@ -317,7 +312,7 @@ async def async_setup_entry( description=SmappeeSensorEntityDescription( key="switch", name=f"{actuator.name} - energy today", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, sensor_id=actuator_id, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, diff --git a/homeassistant/components/smappee/translations/en_GB.json b/homeassistant/components/smappee/translations/en-GB.json similarity index 100% rename from homeassistant/components/smappee/translations/en_GB.json rename to homeassistant/components/smappee/translations/en-GB.json diff --git a/homeassistant/components/smappee/translations/pt.json b/homeassistant/components/smappee/translations/pt.json index 75c24278a8c..947280f7710 100644 --- a/homeassistant/components/smappee/translations/pt.json +++ b/homeassistant/components/smappee/translations/pt.json @@ -2,15 +2,15 @@ "config": { "abort": { "already_configured_device": "O dispositivo j\u00e1 est\u00e1 configurado", - "authorize_url_timeout": "Tempo excedido a gerar um URL de autoriza\u00e7\u00e3o", - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "authorize_url_timeout": "Excedido o tempo limite para gerar um URL de autoriza\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "missing_configuration": "O componente n\u00e3o est\u00e1 configurado. Por favor, siga a documenta\u00e7\u00e3o.", - "no_url_available": "Nenhum URL dispon\u00edvel. Para obter informa\u00e7\u00f5es sobre esse erro, [verifique a sec\u00e7\u00e3o de ajuda]({docs_url})" + "no_url_available": "Nenhum URL est\u00e1 dispon\u00edvel. Para obter informa\u00e7\u00f5es sobre esse erro, [consulte a sec\u00e7\u00e3o de ajuda]({docs_url})" }, "step": { "local": { "data": { - "host": "Servidor" + "host": "Endere\u00e7o" } }, "pick_implementation": { diff --git a/homeassistant/components/smappee/translations/sk.json b/homeassistant/components/smappee/translations/sk.json index d135a72ae63..be45ac508b4 100644 --- a/homeassistant/components/smappee/translations/sk.json +++ b/homeassistant/components/smappee/translations/sk.json @@ -2,6 +2,8 @@ "config": { "abort": { "already_configured_device": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "already_configured_local_device": "Lok\u00e1lne zariadenie (zariadenia) je u\u017e nakonfigurovan\u00e9. Pred konfigur\u00e1ciou cloudov\u00e9ho zariadenia ich najsk\u00f4r odstr\u00e1\u0148te.", + "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL.", "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_mdns": "Nepodporovan\u00e9 zariadenie pre integr\u00e1ciu Smappee.", "missing_configuration": "Komponent nie je nakonfigurovan\u00fd. Postupujte pod\u013ea dokument\u00e1cie.", @@ -9,13 +11,24 @@ }, "flow_title": "{name}", "step": { + "environment": { + "data": { + "environment": "Prostredie" + }, + "description": "Nastavte si Smappee na integr\u00e1ciu s Home Assistant." + }, "local": { "data": { "host": "Hostite\u013e" - } + }, + "description": "Zadajte hostite\u013ea na spustenie lok\u00e1lnej integr\u00e1cie Smappee" }, "pick_implementation": { "title": "Vyberte met\u00f3du overenia" + }, + "zeroconf_confirm": { + "description": "Chcete prida\u0165 zariadenie Smappee so s\u00e9riov\u00fdm \u010d\u00edslom `{serialnumber}` do Home Assistant?", + "title": "Objaven\u00e9 zariadenie Smappee" } } } diff --git a/homeassistant/components/smart_meter_texas/sensor.py b/homeassistant/components/smart_meter_texas/sensor.py index 3ebfc643dfa..57a88f7a409 100644 --- a/homeassistant/components/smart_meter_texas/sensor.py +++ b/homeassistant/components/smart_meter_texas/sensor.py @@ -7,7 +7,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_ADDRESS, ENERGY_KILO_WATT_HOUR +from homeassistant.const import CONF_ADDRESS, UnitOfEnergy from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.restore_state import RestoreEntity @@ -45,7 +45,7 @@ class SmartMeterTexasSensor(CoordinatorEntity, RestoreEntity, SensorEntity): _attr_device_class = SensorDeviceClass.ENERGY _attr_state_class = SensorStateClass.TOTAL_INCREASING - _attr_native_unit_of_measurement = ENERGY_KILO_WATT_HOUR + _attr_native_unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR def __init__(self, meter: Meter, coordinator: DataUpdateCoordinator) -> None: """Initialize the sensor.""" diff --git a/homeassistant/components/smart_meter_texas/translations/pt.json b/homeassistant/components/smart_meter_texas/translations/pt.json index 7953cf5625c..6e1218d3245 100644 --- a/homeassistant/components/smart_meter_texas/translations/pt.json +++ b/homeassistant/components/smart_meter_texas/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, diff --git a/homeassistant/components/smartthings/__init__.py b/homeassistant/components/smartthings/__init__.py index f5b462e9642..60cc2cea0d4 100644 --- a/homeassistant/components/smartthings/__init__.py +++ b/homeassistant/components/smartthings/__init__.py @@ -92,7 +92,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: if not validate_webhook_requirements(hass): _LOGGER.warning( - "The 'base_url' of the 'http' integration must be configured and start with 'https://'" + "The 'base_url' of the 'http' integration must be configured and start with" + " 'https://'" ) return False @@ -135,7 +136,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: await device.status.refresh() except ClientResponseError: _LOGGER.debug( - "Unable to update status for device: %s (%s), the device will be excluded", + ( + "Unable to update status for device: %s (%s), the device will" + " be excluded" + ), device.label, device.device_id, exc_info=True, @@ -161,7 +165,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: except ClientResponseError as ex: if ex.status in (HTTPStatus.UNAUTHORIZED, HTTPStatus.FORBIDDEN): _LOGGER.exception( - "Unable to setup configuration entry '%s' - please reconfigure the integration", + ( + "Unable to setup configuration entry '%s' - please reconfigure the" + " integration" + ), entry.title, ) remove_entry = True @@ -194,7 +201,10 @@ async def async_get_entry_scenes(entry: ConfigEntry, api): except ClientResponseError as ex: if ex.status == HTTPStatus.FORBIDDEN: _LOGGER.exception( - "Unable to load scenes for configuration entry '%s' because the access token does not have the required access", + ( + "Unable to load scenes for configuration entry '%s' because the" + " access token does not have the required access" + ), entry.title, ) else: @@ -237,7 +247,10 @@ async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None: app_count = sum(1 for entry in all_entries if entry.data[CONF_APP_ID] == app_id) if app_count > 1: _LOGGER.debug( - "App %s was not removed because it is in use by other configuration entries", + ( + "App %s was not removed because it is in use by other configuration" + " entries" + ), app_id, ) return diff --git a/homeassistant/components/smartthings/climate.py b/homeassistant/components/smartthings/climate.py index 8a516dfc356..f311fb2a44d 100644 --- a/homeassistant/components/smartthings/climate.py +++ b/homeassistant/components/smartthings/climate.py @@ -19,7 +19,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS, TEMP_FAHRENHEIT +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -71,7 +71,7 @@ STATE_TO_AC_MODE = { HVACMode.FAN_ONLY: "fanOnly", } -UNIT_MAP = {"C": TEMP_CELSIUS, "F": TEMP_FAHRENHEIT} +UNIT_MAP = {"C": UnitOfTemperature.CELSIUS, "F": UnitOfTemperature.FAHRENHEIT} _LOGGER = logging.getLogger(__name__) @@ -236,7 +236,10 @@ class SmartThingsThermostat(SmartThingsEntity, ClimateEntity): modes.add(state) else: _LOGGER.debug( - "Device %s (%s) returned an invalid supported thermostat mode: %s", + ( + "Device %s (%s) returned an invalid supported thermostat" + " mode: %s" + ), self._device.label, self._device.device_id, mode, diff --git a/homeassistant/components/smartthings/config_flow.py b/homeassistant/components/smartthings/config_flow.py index 9a11fe43e02..336eb99f6ee 100644 --- a/homeassistant/components/smartthings/config_flow.py +++ b/homeassistant/components/smartthings/config_flow.py @@ -66,7 +66,9 @@ class SmartThingsFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): reason="invalid_webhook_url", description_placeholders={ "webhook_url": webhook_url, - "component_url": "https://www.home-assistant.io/integrations/smartthings/", + "component_url": ( + "https://www.home-assistant.io/integrations/smartthings/" + ), }, ) @@ -216,7 +218,9 @@ class SmartThingsFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): errors=errors, description_placeholders={ "token_url": "https://account.smartthings.com/tokens", - "component_url": "https://www.home-assistant.io/integrations/smartthings/", + "component_url": ( + "https://www.home-assistant.io/integrations/smartthings/" + ), }, ) diff --git a/homeassistant/components/smartthings/sensor.py b/homeassistant/components/smartthings/sensor.py index a51ee47f0dc..286986829f0 100644 --- a/homeassistant/components/smartthings/sensor.py +++ b/homeassistant/components/smartthings/sensor.py @@ -16,15 +16,14 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( AREA_SQUARE_METERS, CONCENTRATION_PARTS_PER_MILLION, - ELECTRIC_POTENTIAL_VOLT, - ENERGY_KILO_WATT_HOUR, LIGHT_LUX, - MASS_KILOGRAMS, PERCENTAGE, - POWER_WATT, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, - VOLUME_CUBIC_METERS, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfMass, + UnitOfPower, + UnitOfTemperature, + UnitOfVolume, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory @@ -87,7 +86,7 @@ CAPABILITY_TO_SENSORS: dict[str, list[Map]] = { Map( Attribute.bmi_measurement, "Body Mass Index", - f"{MASS_KILOGRAMS}/{AREA_SQUARE_METERS}", + f"{UnitOfMass.KILOGRAMS}/{AREA_SQUARE_METERS}", None, SensorStateClass.MEASUREMENT, None, @@ -97,7 +96,7 @@ CAPABILITY_TO_SENSORS: dict[str, list[Map]] = { Map( Attribute.body_weight_measurement, "Body Weight", - MASS_KILOGRAMS, + UnitOfMass.KILOGRAMS, SensorDeviceClass.WEIGHT, SensorStateClass.MEASUREMENT, None, @@ -198,7 +197,7 @@ CAPABILITY_TO_SENSORS: dict[str, list[Map]] = { Map( Attribute.energy, "Energy Meter", - ENERGY_KILO_WATT_HOUR, + UnitOfEnergy.KILO_WATT_HOUR, SensorDeviceClass.ENERGY, SensorStateClass.TOTAL_INCREASING, None, @@ -228,7 +227,7 @@ CAPABILITY_TO_SENSORS: dict[str, list[Map]] = { Map( Attribute.gas_meter, "Gas Meter", - ENERGY_KILO_WATT_HOUR, + UnitOfEnergy.KILO_WATT_HOUR, SensorDeviceClass.ENERGY, SensorStateClass.MEASUREMENT, None, @@ -247,8 +246,8 @@ CAPABILITY_TO_SENSORS: dict[str, list[Map]] = { Map( Attribute.gas_meter_volume, "Gas Meter Volume", - VOLUME_CUBIC_METERS, - SensorDeviceClass.VOLUME, + UnitOfVolume.CUBIC_METERS, + SensorDeviceClass.GAS, SensorStateClass.MEASUREMENT, None, ), @@ -320,7 +319,7 @@ CAPABILITY_TO_SENSORS: dict[str, list[Map]] = { Map( Attribute.power, "Power Meter", - POWER_WATT, + UnitOfPower.WATT, SensorDeviceClass.POWER, SensorStateClass.MEASUREMENT, None, @@ -506,7 +505,7 @@ CAPABILITY_TO_SENSORS: dict[str, list[Map]] = { Map( Attribute.voltage, "Voltage Measurement", - ELECTRIC_POTENTIAL_VOLT, + UnitOfElectricPotential.VOLT, SensorDeviceClass.VOLTAGE, SensorStateClass.MEASUREMENT, None, @@ -536,7 +535,7 @@ CAPABILITY_TO_SENSORS: dict[str, list[Map]] = { ], } -UNITS = {"C": TEMP_CELSIUS, "F": TEMP_FAHRENHEIT} +UNITS = {"C": UnitOfTemperature.CELSIUS, "F": UnitOfTemperature.FAHRENHEIT} THREE_AXIS_NAMES = ["X Coordinate", "Y Coordinate", "Z Coordinate"] POWER_CONSUMPTION_REPORT_NAMES = [ @@ -745,8 +744,8 @@ class SmartThingsPowerConsumptionSensor(SmartThingsEntity, SensorEntity): def native_unit_of_measurement(self): """Return the unit this state is expressed in.""" if self.report_name == "power": - return POWER_WATT - return ENERGY_KILO_WATT_HOUR + return UnitOfPower.WATT + return UnitOfEnergy.KILO_WATT_HOUR @property def extra_state_attributes(self): diff --git a/homeassistant/components/smartthings/smartapp.py b/homeassistant/components/smartthings/smartapp.py index adf0426e9a2..b6bd05740e6 100644 --- a/homeassistant/components/smartthings/smartapp.py +++ b/homeassistant/components/smartthings/smartapp.py @@ -338,7 +338,10 @@ async def smartapp_sync_subscriptions( try: await api.delete_subscription(installed_app_id, sub.subscription_id) _LOGGER.debug( - "Removed subscription for '%s' under app '%s' because it was no longer needed", + ( + "Removed subscription for '%s' under app '%s' because it was no" + " longer needed" + ), sub.capability, installed_app_id, ) @@ -361,9 +364,11 @@ async def smartapp_sync_subscriptions( capability_count = len(capabilities) if capability_count > SUBSCRIPTION_WARNING_LIMIT: _LOGGER.warning( - "Some device attributes may not receive push updates and there may be subscription " - "creation failures under app '%s' because %s subscriptions are required but " - "there is a limit of %s per app", + ( + "Some device attributes may not receive push updates and there may be" + " subscription creation failures under app '%s' because %s" + " subscriptions are required but there is a limit of %s per app" + ), installed_app_id, capability_count, SUBSCRIPTION_WARNING_LIMIT, diff --git a/homeassistant/components/smartthings/strings.json b/homeassistant/components/smartthings/strings.json index dbc78dd54e2..7fbf966fa89 100644 --- a/homeassistant/components/smartthings/strings.json +++ b/homeassistant/components/smartthings/strings.json @@ -21,13 +21,13 @@ }, "abort": { "invalid_webhook_url": "Home Assistant is not configured correctly to receive updates from SmartThings. The webhook URL is invalid:\n> {webhook_url}\n\nPlease update your configuration per the [instructions]({component_url}), restart Home Assistant, and try again.", - "no_available_locations": "There are no available SmartThings Locations to setup in Home Assistant." + "no_available_locations": "There are no available SmartThings Locations to set up in Home Assistant." }, "error": { "token_invalid_format": "The token must be in the UID/GUID format", "token_unauthorized": "The token is invalid or no longer authorized.", "token_forbidden": "The token does not have the required OAuth scopes.", - "app_setup_error": "Unable to setup the SmartApp. Please try again.", + "app_setup_error": "Unable to set up the SmartApp. Please try again.", "webhook_error": "SmartThings could not validate the webhook URL. Please ensure the webhook URL is reachable from the internet and try again." } } diff --git a/homeassistant/components/smartthings/translations/ca.json b/homeassistant/components/smartthings/translations/ca.json index 7765eec4c13..c68e1766f64 100644 --- a/homeassistant/components/smartthings/translations/ca.json +++ b/homeassistant/components/smartthings/translations/ca.json @@ -2,10 +2,10 @@ "config": { "abort": { "invalid_webhook_url": "Home Assistant not est\u00e0 configurat correctament per a rebre actualitzacions de SmartThings.\nEl seg\u00fcent URL webhook no \u00e9s v\u00e0lid:\n> {webhook_url}\n\nActualitza la teva configuraci\u00f3 segons les [instruccions]({component_url}), reinicia Home Assistant i torna-ho a provar.", - "no_available_locations": "No hi ha ubicacions SmartThings configurables amb Home Assistant." + "no_available_locations": "No hi ha ubicacions SmartThings a configurar a Home Assistant." }, "error": { - "app_setup_error": "No s'ha pogut configurar SmartApp. Siusplau, torna-ho a provar.", + "app_setup_error": "No s'ha pogut configurar SmartApp. Torna-ho a provar.", "token_forbidden": "El token d'autenticaci\u00f3 no t\u00e9 cont\u00e9 els apartats OAuth obligatoris.", "token_invalid_format": "El token d'autenticaci\u00f3 ha d'estar en format UID/GUID", "token_unauthorized": "El token d'autenticaci\u00f3 no \u00e9s v\u00e0lid o ja no est\u00e0 autoritzat.", diff --git a/homeassistant/components/smartthings/translations/de.json b/homeassistant/components/smartthings/translations/de.json index b6a97013784..44859afd418 100644 --- a/homeassistant/components/smartthings/translations/de.json +++ b/homeassistant/components/smartthings/translations/de.json @@ -5,7 +5,7 @@ "no_available_locations": "In Home Assistant sind keine SmartThings-Standorte zum Einrichten verf\u00fcgbar." }, "error": { - "app_setup_error": "SmartApp kann nicht eingerichtet werden. Bitte versuche es erneut.", + "app_setup_error": "Smart App kann nicht eingerichtet werden. Bitte versuche es erneut.", "token_forbidden": "Das Token verf\u00fcgt nicht \u00fcber die erforderlichen OAuth-Bereiche.", "token_invalid_format": "Das Token muss im UID/GUID-Format vorliegen.", "token_unauthorized": "Das Token ist ung\u00fcltig oder nicht mehr autorisiert.", diff --git a/homeassistant/components/smartthings/translations/en_GB.json b/homeassistant/components/smartthings/translations/en-GB.json similarity index 100% rename from homeassistant/components/smartthings/translations/en_GB.json rename to homeassistant/components/smartthings/translations/en-GB.json diff --git a/homeassistant/components/smartthings/translations/en.json b/homeassistant/components/smartthings/translations/en.json index ded899ad4aa..ec49a38ca65 100644 --- a/homeassistant/components/smartthings/translations/en.json +++ b/homeassistant/components/smartthings/translations/en.json @@ -2,10 +2,10 @@ "config": { "abort": { "invalid_webhook_url": "Home Assistant is not configured correctly to receive updates from SmartThings. The webhook URL is invalid:\n> {webhook_url}\n\nPlease update your configuration per the [instructions]({component_url}), restart Home Assistant, and try again.", - "no_available_locations": "There are no available SmartThings Locations to setup in Home Assistant." + "no_available_locations": "There are no available SmartThings Locations to set up in Home Assistant." }, "error": { - "app_setup_error": "Unable to setup the SmartApp. Please try again.", + "app_setup_error": "Unable to set up the SmartApp. Please try again.", "token_forbidden": "The token does not have the required OAuth scopes.", "token_invalid_format": "The token must be in the UID/GUID format", "token_unauthorized": "The token is invalid or no longer authorized.", diff --git a/homeassistant/components/smartthings/translations/he.json b/homeassistant/components/smartthings/translations/he.json index 73f9c8491e1..b73162134be 100644 --- a/homeassistant/components/smartthings/translations/he.json +++ b/homeassistant/components/smartthings/translations/he.json @@ -19,14 +19,14 @@ "data": { "access_token": "\u05d0\u05e1\u05d9\u05de\u05d5\u05df \u05d2\u05d9\u05e9\u05d4" }, - "description": "\u05e0\u05d0 \u05dc\u05d4\u05d6\u05d9\u05df SmartThings [\u05d0\u05e1\u05d9\u05de\u05d5\u05df \u05d2\u05d9\u05e9\u05d4 \u05d0\u05d9\u05e9\u05d9\u05ea]({token_url}) \u05e9\u05e0\u05d5\u05e6\u05e8 \u05dc\u05e4\u05d9 [\u05d4\u05d5\u05e8\u05d0\u05d5\u05ea]({component_url}). \u05e4\u05e2\u05d5\u05dc\u05d4 \u05d6\u05d5 \u05ea\u05e9\u05de\u05e9 \u05dc\u05d9\u05e6\u05d9\u05e8\u05ea \u05d4\u05e9\u05d9\u05dc\u05d5\u05d1 \u05e9\u05dc Home Assistant \u05d1\u05d7\u05e9\u05d1\u05d5\u05df SmartThings \u05e9\u05dc\u05da.", + "description": "\u05e0\u05d0 \u05dc\u05d4\u05d6\u05d9\u05df SmartThings [\u05d0\u05e1\u05d9\u05de\u05d5\u05df \u05d2\u05d9\u05e9\u05d4 \u05d0\u05d9\u05e9\u05d9\u05ea]({token_url}) \u05e9\u05e0\u05d5\u05e6\u05e8 \u05dc\u05e4\u05d9 [\u05d4\u05d5\u05e8\u05d0\u05d5\u05ea]({component_url}). \u05e4\u05e2\u05d5\u05dc\u05d4 \u05d6\u05d5 \u05ea\u05e9\u05de\u05e9 \u05dc\u05d9\u05e6\u05d9\u05e8\u05ea \u05d4\u05e9\u05d9\u05dc\u05d5\u05d1 \u05e9\u05dc Home Assistant \u05d1\u05d7\u05e9\u05d1\u05d5\u05df SmartThings \u05e9\u05dc\u05da.", "title": "\u05d4\u05d6\u05e0\u05ea \u05d0\u05e1\u05d9\u05de\u05d5\u05df \u05d2\u05d9\u05e9\u05d4 \u05d0\u05d9\u05e9\u05d9\u05ea" }, "select_location": { "data": { "location_id": "\u05de\u05d9\u05e7\u05d5\u05dd" }, - "description": "\u05e0\u05d0 \u05dc\u05d1\u05d7\u05d5\u05e8 \u05d0\u05ea \u05de\u05d9\u05e7\u05d5\u05dd \u05d4-SmartThings \u05e9\u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05d5\u05e1\u05d9\u05e3 \u05dc-Home Assistant. \u05dc\u05d0\u05d7\u05e8 \u05de\u05db\u05df \u05d9\u05e4\u05ea\u05d7 \u05d7\u05dc\u05d5\u05df \u05d7\u05d3\u05e9 \u05d5\u05e0\u05d1\u05e7\u05e9 \u05de\u05de\u05da \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05d5\u05dc\u05d0\u05e9\u05e8 \u05d4\u05ea\u05e7\u05e0\u05d4 \u05e9\u05dc \u05e9\u05d9\u05dc\u05d5\u05d1 Home Assistant \u05d1\u05de\u05d9\u05e7\u05d5\u05dd \u05e9\u05e0\u05d1\u05d7\u05e8.", + "description": "\u05e0\u05d0 \u05dc\u05d1\u05d7\u05d5\u05e8 \u05d0\u05ea \u05de\u05d9\u05e7\u05d5\u05dd \u05d4-SmartThings \u05e9\u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05d5\u05e1\u05d9\u05e3 \u05dc-Home Assistant. \u05dc\u05d0\u05d7\u05e8 \u05de\u05db\u05df \u05d9\u05e4\u05ea\u05d7 \u05d7\u05dc\u05d5\u05df \u05d7\u05d3\u05e9 \u05d5\u05e0\u05d1\u05e7\u05e9 \u05de\u05de\u05da \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05d5\u05dc\u05d0\u05e9\u05e8 \u05d4\u05ea\u05e7\u05e0\u05d4 \u05e9\u05dc \u05e9\u05d9\u05dc\u05d5\u05d1 Home Assistant \u05d1\u05de\u05d9\u05e7\u05d5\u05dd \u05e9\u05e0\u05d1\u05d7\u05e8.", "title": "\u05d1\u05d7\u05d9\u05e8\u05ea \u05de\u05d9\u05e7\u05d5\u05dd" }, "user": { diff --git a/homeassistant/components/smartthings/translations/no.json b/homeassistant/components/smartthings/translations/no.json index ca8c6f81eda..ddeb865b2bf 100644 --- a/homeassistant/components/smartthings/translations/no.json +++ b/homeassistant/components/smartthings/translations/no.json @@ -5,7 +5,7 @@ "no_available_locations": "Det er ingen tilgjengelige SmartThings-plasseringer \u00e5 konfigurere i Home Assistant." }, "error": { - "app_setup_error": "Kan ikke konfigurere SmartApp. Vennligst pr\u00f8v p\u00e5 nytt.", + "app_setup_error": "Kan ikke sette opp SmartApp. V\u00e6r s\u00e5 snill, pr\u00f8v p\u00e5 nytt.", "token_forbidden": "Tokenet har ikke de n\u00f8dvendige OAuth-omfangene", "token_invalid_format": "Token m\u00e5 v\u00e6re i UID/GUID format", "token_unauthorized": "Tokenet er ugyldig eller er ikke lenger godkjent", diff --git a/homeassistant/components/smartthings/translations/sk.json b/homeassistant/components/smartthings/translations/sk.json index 0f2b3e1f616..4cea1c1f614 100644 --- a/homeassistant/components/smartthings/translations/sk.json +++ b/homeassistant/components/smartthings/translations/sk.json @@ -1,17 +1,36 @@ { "config": { + "abort": { + "invalid_webhook_url": "Home Assistant nie je spr\u00e1vne nakonfigurovan\u00fd na prij\u00edmanie aktualiz\u00e1ci\u00ed zo SmartThings. Webov\u00e1 adresa webhooku je neplatn\u00e1:\n > {webhook_url} \n\nAktualizujte svoju konfigur\u00e1ciu pod\u013ea [pokynov]({component_url}), re\u0161tartujte Home Assistant a sk\u00faste to znova.", + "no_available_locations": "V aplik\u00e1cii Home Assistant nie s\u00fa k dispoz\u00edcii \u017eiadne umiestnenia SmartThings na nastavenie." + }, + "error": { + "app_setup_error": "Nie je mo\u017en\u00e9 nastavi\u0165 aplik\u00e1ciu SmartApp. Pros\u00edm sk\u00faste znova.", + "token_forbidden": "Token nem\u00e1 po\u017eadovan\u00e9 rozsahy OAuth.", + "token_invalid_format": "Token mus\u00ed by\u0165 vo form\u00e1te UID/GUID", + "token_unauthorized": "Token je neplatn\u00fd alebo u\u017e nie je autorizovan\u00fd.", + "webhook_error": "SmartThings nedok\u00e1zal overi\u0165 webov\u00fa adresu webhooku. Uistite sa, \u017ee adresa URL webhooku je dostupn\u00e1 z internetu a sk\u00faste to znova." + }, "step": { + "authorize": { + "title": "Autorizova\u0165 Home Assistant" + }, "pat": { "data": { "access_token": "Pr\u00edstupov\u00fd token" - } + }, + "description": "Zadajte [osobn\u00fd pr\u00edstupov\u00fd token] SmartThings ({token_url}), ktor\u00fd bol vytvoren\u00fd pod\u013ea [pokynov] ({component_url}). Toto sa pou\u017eije na vytvorenie integr\u00e1cie Home Assistant v r\u00e1mci v\u00e1\u0161ho \u00fa\u010dtu SmartThings.", + "title": "Zadajte osobn\u00fd pr\u00edstupov\u00fd token" }, "select_location": { "data": { "location_id": "Umiestnenie" - } + }, + "description": "Vyberte umiestnenie SmartThings, ktor\u00e9 chcete prida\u0165 do aplik\u00e1cie Home Assistant. N\u00e1sledne otvor\u00edme nov\u00e9 okno a po\u017eiadame v\u00e1s o prihl\u00e1senie a autoriz\u00e1ciu in\u0161tal\u00e1cie integr\u00e1cie Home Assistant do zvolen\u00e9ho miesta.", + "title": "Vyberte umiestnenie" }, "user": { + "description": "SmartThings bude nakonfigurovan\u00fd na odosielanie aktualiz\u00e1ci\u00ed push do Home Asistent na adrese:\n > {webhook_url} \n\nAk to nie je spr\u00e1vne, aktualizujte svoju konfigur\u00e1ciu, re\u0161tartujte Home Assistant a sk\u00faste to znova.", "title": "Potvr\u010fte URL sp\u00e4tn\u00e9ho volania" } } diff --git a/homeassistant/components/smarttub/climate.py b/homeassistant/components/smarttub/climate.py index 0afebbd6433..a938bde6fd1 100644 --- a/homeassistant/components/smarttub/climate.py +++ b/homeassistant/components/smarttub/climate.py @@ -14,7 +14,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.util.unit_conversion import TemperatureConverter @@ -63,7 +63,7 @@ class SmartTubThermostat(SmartTubEntity, ClimateEntity): _attr_supported_features = ( ClimateEntityFeature.PRESET_MODE | ClimateEntityFeature.TARGET_TEMPERATURE ) - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS def __init__(self, coordinator, spa): """Initialize the entity.""" @@ -88,7 +88,7 @@ class SmartTubThermostat(SmartTubEntity, ClimateEntity): """Return the minimum temperature.""" min_temp = DEFAULT_MIN_TEMP return TemperatureConverter.convert( - min_temp, TEMP_CELSIUS, self.temperature_unit + min_temp, UnitOfTemperature.CELSIUS, self.temperature_unit ) @property @@ -96,7 +96,7 @@ class SmartTubThermostat(SmartTubEntity, ClimateEntity): """Return the maximum temperature.""" max_temp = DEFAULT_MAX_TEMP return TemperatureConverter.convert( - max_temp, TEMP_CELSIUS, self.temperature_unit + max_temp, UnitOfTemperature.CELSIUS, self.temperature_unit ) @property diff --git a/homeassistant/components/smarttub/translations/pt.json b/homeassistant/components/smarttub/translations/pt.json index c6325f3dba3..7d59d0b769e 100644 --- a/homeassistant/components/smarttub/translations/pt.json +++ b/homeassistant/components/smarttub/translations/pt.json @@ -10,7 +10,7 @@ "step": { "user": { "data": { - "email": "Email", + "email": "", "password": "Palavra-passe" } } diff --git a/homeassistant/components/smarttub/translations/sk.json b/homeassistant/components/smarttub/translations/sk.json index ebc9c699c89..0144077dae6 100644 --- a/homeassistant/components/smarttub/translations/sk.json +++ b/homeassistant/components/smarttub/translations/sk.json @@ -9,6 +9,7 @@ }, "step": { "reauth_confirm": { + "description": "Integr\u00e1cia SmartTub vy\u017eaduje op\u00e4tovn\u00e9 overenie v\u00e1\u0161ho \u00fa\u010dtu", "title": "Znova overi\u0165 integr\u00e1ciu" }, "user": { diff --git a/homeassistant/components/smarty/sensor.py b/homeassistant/components/smarty/sensor.py index 1c76fe3bfb9..57d681594cf 100644 --- a/homeassistant/components/smarty/sensor.py +++ b/homeassistant/components/smarty/sensor.py @@ -7,7 +7,7 @@ import logging from pysmarty import Smarty from homeassistant.components.sensor import SensorDeviceClass, SensorEntity -from homeassistant.const import TEMP_CELSIUS +from homeassistant.const import UnitOfTemperature from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -78,7 +78,7 @@ class SupplyAirTemperatureSensor(SmartySensor): super().__init__( name=f"{name} Supply Air Temperature", device_class=SensorDeviceClass.TEMPERATURE, - unit_of_measurement=TEMP_CELSIUS, + unit_of_measurement=UnitOfTemperature.CELSIUS, smarty=smarty, ) @@ -96,7 +96,7 @@ class ExtractAirTemperatureSensor(SmartySensor): super().__init__( name=f"{name} Extract Air Temperature", device_class=SensorDeviceClass.TEMPERATURE, - unit_of_measurement=TEMP_CELSIUS, + unit_of_measurement=UnitOfTemperature.CELSIUS, smarty=smarty, ) @@ -114,7 +114,7 @@ class OutdoorAirTemperatureSensor(SmartySensor): super().__init__( name=f"{name} Outdoor Air Temperature", device_class=SensorDeviceClass.TEMPERATURE, - unit_of_measurement=TEMP_CELSIUS, + unit_of_measurement=UnitOfTemperature.CELSIUS, smarty=smarty, ) diff --git a/homeassistant/components/smhi/translations/ko.json b/homeassistant/components/smhi/translations/ko.json index 6f1cbeda859..5bfbd17803f 100644 --- a/homeassistant/components/smhi/translations/ko.json +++ b/homeassistant/components/smhi/translations/ko.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, "error": { "wrong_location": "\uc2a4\uc6e8\ub374 \uc9c0\uc5ed \uc804\uc6a9\uc785\ub2c8\ub2e4" }, diff --git a/homeassistant/components/smhi/translations/sk.json b/homeassistant/components/smhi/translations/sk.json index 60b95d94721..dc2c4402c29 100644 --- a/homeassistant/components/smhi/translations/sk.json +++ b/homeassistant/components/smhi/translations/sk.json @@ -3,12 +3,16 @@ "abort": { "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" }, + "error": { + "wrong_location": "Lokalita iba pre \u0160v\u00e9dsko" + }, "step": { "user": { "data": { "latitude": "Zemepisn\u00e1 \u0161\u00edrka", "longitude": "Zemepisn\u00e1 d\u013a\u017eka" - } + }, + "title": "Lokalita vo \u0160v\u00e9dsku" } } } diff --git a/homeassistant/components/sms/translations/pt.json b/homeassistant/components/sms/translations/pt.json index 4ccc36bcc2a..0255d1387d8 100644 --- a/homeassistant/components/sms/translations/pt.json +++ b/homeassistant/components/sms/translations/pt.json @@ -5,7 +5,7 @@ "single_instance_allowed": "J\u00e1 configurado. Apenas uma \u00fanica configura\u00e7\u00e3o \u00e9 poss\u00edvel." }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "unknown": "Erro inesperado" }, "step": { diff --git a/homeassistant/components/smtp/notify.py b/homeassistant/components/smtp/notify.py index 866d7980d08..300e74a2cd6 100644 --- a/homeassistant/components/smtp/notify.py +++ b/homeassistant/components/smtp/notify.py @@ -154,16 +154,17 @@ class MailNotificationService(BaseNotificationService): server = self.connect() except (smtplib.socket.gaierror, ConnectionRefusedError): _LOGGER.exception( - "SMTP server not found or refused connection (%s:%s). " - "Please check the IP address, hostname, and availability of your SMTP server", + ( + "SMTP server not found or refused connection (%s:%s). Please check" + " the IP address, hostname, and availability of your SMTP server" + ), self._server, self._port, ) except smtplib.SMTPAuthenticationError: _LOGGER.exception( - "Login not possible. " - "Please check your setting and/or your credentials" + "Login not possible. Please check your setting and/or your credentials" ) return False diff --git a/homeassistant/components/snmp/manifest.json b/homeassistant/components/snmp/manifest.json index 1ffcb04ebda..182f4aac544 100644 --- a/homeassistant/components/snmp/manifest.json +++ b/homeassistant/components/snmp/manifest.json @@ -2,7 +2,7 @@ "domain": "snmp", "name": "SNMP", "documentation": "https://www.home-assistant.io/integrations/snmp", - "requirements": ["pysnmplib==5.0.15"], + "requirements": ["pysnmplib==5.0.20"], "codeowners": [], "iot_class": "local_polling", "loggers": ["pyasn1", "pysmi", "pysnmp"] diff --git a/homeassistant/components/snmp/sensor.py b/homeassistant/components/snmp/sensor.py index a582eef01fa..7fd192304a8 100644 --- a/homeassistant/components/snmp/sensor.py +++ b/homeassistant/components/snmp/sensor.py @@ -4,6 +4,7 @@ from __future__ import annotations from datetime import timedelta import logging +from pysnmp.error import PySnmpError import pysnmp.hlapi.asyncio as hlapi from pysnmp.hlapi.asyncio import ( CommunityData, @@ -11,6 +12,7 @@ from pysnmp.hlapi.asyncio import ( ObjectIdentity, ObjectType, SnmpEngine, + Udp6TransportTarget, UdpTransportTarget, UsmUserData, getCmd, @@ -106,6 +108,17 @@ async def async_setup_platform( default_value = config.get(CONF_DEFAULT_VALUE) unique_id = config.get(CONF_UNIQUE_ID) + try: + # Try IPv4 first. + target = UdpTransportTarget((host, port), timeout=DEFAULT_TIMEOUT) + except PySnmpError: + # Then try IPv6. + try: + target = Udp6TransportTarget((host, port), timeout=DEFAULT_TIMEOUT) + except PySnmpError as err: + _LOGGER.error("Invalid SNMP host: %s", err) + return + if version == "3": if not authkey: @@ -122,20 +135,18 @@ async def async_setup_platform( authProtocol=getattr(hlapi, MAP_AUTH_PROTOCOLS[authproto]), privProtocol=getattr(hlapi, MAP_PRIV_PROTOCOLS[privproto]), ), - UdpTransportTarget((host, port), timeout=DEFAULT_TIMEOUT), + target, ContextData(), ] else: request_args = [ SnmpEngine(), CommunityData(community, mpModel=SNMP_VERSIONS[version]), - UdpTransportTarget((host, port), timeout=DEFAULT_TIMEOUT), + target, ContextData(), ] - - errindication, _, _, _ = await getCmd( - *request_args, ObjectType(ObjectIdentity(baseoid)) - ) + get_result = await getCmd(*request_args, ObjectType(ObjectIdentity(baseoid))) + errindication, _, _, _ = await get_result if errindication and not accept_errors: _LOGGER.error("Please check the details in the configuration file") @@ -194,9 +205,10 @@ class SnmpData: async def async_update(self): """Get the latest data from the remote SNMP capable host.""" - errindication, errstatus, errindex, restable = await getCmd( + get_result = await getCmd( *self._request_args, ObjectType(ObjectIdentity(self._baseoid)) ) + errindication, errstatus, errindex, restable = await get_result if errindication and not self._accept_errors: _LOGGER.error("SNMP error: %s", errindication) diff --git a/homeassistant/components/snmp/switch.py b/homeassistant/components/snmp/switch.py index feb87e7e253..bd5078c4349 100644 --- a/homeassistant/components/snmp/switch.py +++ b/homeassistant/components/snmp/switch.py @@ -259,9 +259,10 @@ class SnmpSwitch(SwitchEntity): async def async_update(self) -> None: """Update the state.""" - errindication, errstatus, errindex, restable = await getCmd( + get_result = await getCmd( *self._request_args, ObjectType(ObjectIdentity(self._baseoid)) ) + errindication, errstatus, errindex, restable = await get_result if errindication: _LOGGER.error("SNMP error: %s", errindication) diff --git a/homeassistant/components/snooz/fan.py b/homeassistant/components/snooz/fan.py index 8ad2372924b..d8c8f54d7bb 100644 --- a/homeassistant/components/snooz/fan.py +++ b/homeassistant/components/snooz/fan.py @@ -115,5 +115,6 @@ class SnoozFan(FanEntity, RestoreEntity): self._async_write_state_changed() elif result.status != SnoozCommandResultStatus.CANCELLED: raise HomeAssistantError( - f"Command {command} failed with status {result.status.name} after {result.duration}" + f"Command {command} failed with status {result.status.name} after" + f" {result.duration}" ) diff --git a/homeassistant/components/snooz/translations/en.json b/homeassistant/components/snooz/translations/en.json index a536a87be5b..b86a7130ac8 100644 --- a/homeassistant/components/snooz/translations/en.json +++ b/homeassistant/components/snooz/translations/en.json @@ -11,7 +11,7 @@ }, "step": { "bluetooth_confirm": { - "description": "Do you want to setup {name}?" + "description": "Do you want to set up {name}?" }, "pairing_timeout": { "description": "The device did not enter pairing mode. Click Submit to try again.\n\n### Troubleshooting\n1. Check that the device isn't connected to the mobile app.\n2. Unplug the device for 5 seconds, then plug it back in." @@ -20,7 +20,7 @@ "data": { "address": "Device" }, - "description": "Choose a device to setup" + "description": "Choose a device to set up" } } } diff --git a/homeassistant/components/snooz/translations/it.json b/homeassistant/components/snooz/translations/it.json index 7f3dc5b8191..6ad3eb7788b 100644 --- a/homeassistant/components/snooz/translations/it.json +++ b/homeassistant/components/snooz/translations/it.json @@ -20,7 +20,7 @@ "data": { "address": "Dispositivo" }, - "description": "Seleziona un dispositivo da configurare" + "description": "Scegli un dispositivo da configurare" } } } diff --git a/homeassistant/components/snooz/translations/ko.json b/homeassistant/components/snooz/translations/ko.json new file mode 100644 index 00000000000..1a59c05b30c --- /dev/null +++ b/homeassistant/components/snooz/translations/ko.json @@ -0,0 +1,9 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", + "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/snooz/translations/no.json b/homeassistant/components/snooz/translations/no.json index c16e7ce6d94..3494a2f37dd 100644 --- a/homeassistant/components/snooz/translations/no.json +++ b/homeassistant/components/snooz/translations/no.json @@ -11,7 +11,7 @@ }, "step": { "bluetooth_confirm": { - "description": "Vil du konfigurere {name}?" + "description": "Vil du sette opp {name} ?" }, "pairing_timeout": { "description": "Enheten gikk ikke i sammenkoblingsmodus. Klikk p\u00e5 Send for \u00e5 pr\u00f8ve igjen. \n\n ### Feils\u00f8king\n 1. Sjekk at enheten ikke er koblet til mobilappen.\n 2. Koble fra enheten i 5 sekunder, og koble den deretter til igjen." diff --git a/homeassistant/components/snooz/translations/pt.json b/homeassistant/components/snooz/translations/pt.json new file mode 100644 index 00000000000..c6a15010072 --- /dev/null +++ b/homeassistant/components/snooz/translations/pt.json @@ -0,0 +1,9 @@ +{ + "config": { + "abort": { + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", + "already_in_progress": "O processo de configura\u00e7\u00e3o j\u00e1 est\u00e1 a decorrer", + "no_devices_found": "Nenhum dispositivo encontrado na rede" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/snooz/translations/sk.json b/homeassistant/components/snooz/translations/sk.json index b121bbc35a3..f44a101d16e 100644 --- a/homeassistant/components/snooz/translations/sk.json +++ b/homeassistant/components/snooz/translations/sk.json @@ -6,10 +6,16 @@ "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" }, "flow_title": "{name}", + "progress": { + "wait_for_pairing_mode": "Ak chcete dokon\u010di\u0165 nastavenie, uve\u010fte toto zariadenie do re\u017eimu p\u00e1rovania. \n\n ### Ako vst\u00fapi\u0165 do re\u017eimu p\u00e1rovania\n 1. Vyn\u00fati\u0165 ukon\u010denie mobiln\u00fdch aplik\u00e1ci\u00ed SNOOZ.\n 2. Stla\u010dte a podr\u017ete tla\u010didlo nap\u00e1jania na zariaden\u00ed. Uvo\u013enite, ke\u010f svetl\u00e1 za\u010dn\u00fa blika\u0165 (pribli\u017ene 5 sek\u00fand)." + }, "step": { "bluetooth_confirm": { "description": "Chcete nastavi\u0165 {name}?" }, + "pairing_timeout": { + "description": "Zariadenie nepre\u0161lo do re\u017eimu p\u00e1rovania. Kliknite na Odosla\u0165 a sk\u00faste to znova. \n\n ### Rie\u0161enie probl\u00e9mov\n 1. Skontrolujte, \u010di zariadenie nie je pripojen\u00e9 k mobilnej aplik\u00e1cii.\n 2. Odpojte zariadenie na 5 sek\u00fand a potom ho znova zapojte." + }, "user": { "data": { "address": "Zaradenie" diff --git a/homeassistant/components/solaredge/const.py b/homeassistant/components/solaredge/const.py index fd989fc8a2a..6d95f8b6aec 100644 --- a/homeassistant/components/solaredge/const.py +++ b/homeassistant/components/solaredge/const.py @@ -3,7 +3,7 @@ from datetime import timedelta import logging from homeassistant.components.sensor import SensorDeviceClass, SensorStateClass -from homeassistant.const import ENERGY_WATT_HOUR, PERCENTAGE, POWER_WATT +from homeassistant.const import PERCENTAGE, UnitOfEnergy, UnitOfPower from .models import SolarEdgeSensorEntityDescription @@ -34,7 +34,7 @@ SENSOR_TYPES = [ name="Lifetime energy", icon="mdi:solar-power", state_class=SensorStateClass.TOTAL, - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, device_class=SensorDeviceClass.ENERGY, ), SolarEdgeSensorEntityDescription( @@ -43,7 +43,7 @@ SENSOR_TYPES = [ name="Energy this year", entity_registry_enabled_default=False, icon="mdi:solar-power", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, device_class=SensorDeviceClass.ENERGY, ), SolarEdgeSensorEntityDescription( @@ -52,7 +52,7 @@ SENSOR_TYPES = [ name="Energy this month", entity_registry_enabled_default=False, icon="mdi:solar-power", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, device_class=SensorDeviceClass.ENERGY, ), SolarEdgeSensorEntityDescription( @@ -61,7 +61,7 @@ SENSOR_TYPES = [ name="Energy today", entity_registry_enabled_default=False, icon="mdi:solar-power", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, device_class=SensorDeviceClass.ENERGY, ), SolarEdgeSensorEntityDescription( @@ -70,7 +70,7 @@ SENSOR_TYPES = [ name="Current Power", icon="mdi:solar-power", state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, ), SolarEdgeSensorEntityDescription( @@ -143,7 +143,7 @@ SENSOR_TYPES = [ name="Imported Energy", entity_registry_enabled_default=False, state_class=SensorStateClass.TOTAL_INCREASING, - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, device_class=SensorDeviceClass.ENERGY, ), SolarEdgeSensorEntityDescription( @@ -152,7 +152,7 @@ SENSOR_TYPES = [ name="Production Energy", entity_registry_enabled_default=False, state_class=SensorStateClass.TOTAL_INCREASING, - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, device_class=SensorDeviceClass.ENERGY, ), SolarEdgeSensorEntityDescription( @@ -161,7 +161,7 @@ SENSOR_TYPES = [ name="Consumption Energy", entity_registry_enabled_default=False, state_class=SensorStateClass.TOTAL_INCREASING, - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, device_class=SensorDeviceClass.ENERGY, ), SolarEdgeSensorEntityDescription( @@ -170,7 +170,7 @@ SENSOR_TYPES = [ name="SelfConsumption Energy", entity_registry_enabled_default=False, state_class=SensorStateClass.TOTAL_INCREASING, - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, device_class=SensorDeviceClass.ENERGY, ), SolarEdgeSensorEntityDescription( @@ -179,7 +179,7 @@ SENSOR_TYPES = [ name="Exported Energy", entity_registry_enabled_default=False, state_class=SensorStateClass.TOTAL_INCREASING, - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, device_class=SensorDeviceClass.ENERGY, ), SolarEdgeSensorEntityDescription( diff --git a/homeassistant/components/solaredge/coordinator.py b/homeassistant/components/solaredge/coordinator.py index 5def236598a..839f2cf37eb 100644 --- a/homeassistant/components/solaredge/coordinator.py +++ b/homeassistant/components/solaredge/coordinator.py @@ -1,7 +1,7 @@ """Provides the data update coordinators for SolarEdge.""" from __future__ import annotations -from abc import abstractmethod +from abc import ABC, abstractmethod from datetime import date, datetime, timedelta from typing import Any @@ -21,7 +21,7 @@ from .const import ( ) -class SolarEdgeDataService: +class SolarEdgeDataService(ABC): """Get and update the latest data.""" coordinator: DataUpdateCoordinator @@ -262,7 +262,8 @@ class SolarEdgePowerFlowDataService(SolarEdgeDataService): if "connections" not in power_flow: LOGGER.debug( - "Missing connections in power flow data. Assuming site does not have any" + "Missing connections in power flow data. Assuming site does not" + " have any" ) return diff --git a/homeassistant/components/solaredge/translations/de.json b/homeassistant/components/solaredge/translations/de.json index 247187f35af..0c05f596c0b 100644 --- a/homeassistant/components/solaredge/translations/de.json +++ b/homeassistant/components/solaredge/translations/de.json @@ -14,7 +14,7 @@ "data": { "api_key": "API-Schl\u00fcssel", "name": "Der Name dieser Installation", - "site_id": "Die SolarEdge-Site-ID" + "site_id": "Die SolarEdge Site-ID" }, "title": "Definiere die API-Parameter f\u00fcr diese Installation" } diff --git a/homeassistant/components/solaredge/translations/sk.json b/homeassistant/components/solaredge/translations/sk.json index c954ca5b347..8283504652a 100644 --- a/homeassistant/components/solaredge/translations/sk.json +++ b/homeassistant/components/solaredge/translations/sk.json @@ -6,14 +6,17 @@ "error": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "could_not_connect": "Nepodarilo sa pripoji\u0165 k rozhraniu solaredge API", - "invalid_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d" + "invalid_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d", + "site_not_active": "Str\u00e1nka nie je akt\u00edvna" }, "step": { "user": { "data": { "api_key": "API k\u013e\u00fa\u010d", - "name": "N\u00e1zov tejto in\u0161tal\u00e1cie" - } + "name": "N\u00e1zov tejto in\u0161tal\u00e1cie", + "site_id": "Identifik\u00e1tor SolarEdge" + }, + "title": "Definujte parametre API pre t\u00fato in\u0161tal\u00e1ciu" } } } diff --git a/homeassistant/components/solaredge_local/sensor.py b/homeassistant/components/solaredge_local/sensor.py index 361ab8e5ce7..29c7f1ee6f3 100644 --- a/homeassistant/components/solaredge_local/sensor.py +++ b/homeassistant/components/solaredge_local/sensor.py @@ -21,13 +21,12 @@ from homeassistant.components.sensor import ( from homeassistant.const import ( CONF_IP_ADDRESS, CONF_NAME, - ELECTRIC_CURRENT_AMPERE, - ELECTRIC_POTENTIAL_VOLT, - ENERGY_WATT_HOUR, - FREQUENCY_HERTZ, - POWER_WATT, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, + UnitOfElectricCurrent, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfFrequency, + UnitOfPower, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv @@ -63,49 +62,56 @@ SENSOR_TYPES: tuple[SolarEdgeLocalSensorEntityDescription, ...] = ( SolarEdgeLocalSensorEntityDescription( key="gridvoltage", name="Grid Voltage", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + device_class=SensorDeviceClass.VOLTAGE, icon="mdi:current-ac", ), SolarEdgeLocalSensorEntityDescription( key="dcvoltage", name="DC Voltage", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + device_class=SensorDeviceClass.VOLTAGE, icon="mdi:current-dc", ), SolarEdgeLocalSensorEntityDescription( key="gridfrequency", name="Grid Frequency", - native_unit_of_measurement=FREQUENCY_HERTZ, - icon="mdi:current-ac", + native_unit_of_measurement=UnitOfFrequency.HERTZ, + device_class=SensorDeviceClass.FREQUENCY, ), SolarEdgeLocalSensorEntityDescription( key="currentPower", name="Current Power", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, + device_class=SensorDeviceClass.POWER, icon="mdi:solar-power", ), SolarEdgeLocalSensorEntityDescription( key="energyThisMonth", name="Energy This Month", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, + device_class=SensorDeviceClass.ENERGY, icon="mdi:solar-power", ), SolarEdgeLocalSensorEntityDescription( key="energyThisYear", name="Energy This Year", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, + device_class=SensorDeviceClass.ENERGY, icon="mdi:solar-power", ), SolarEdgeLocalSensorEntityDescription( key="energyToday", name="Energy Today", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, + device_class=SensorDeviceClass.ENERGY, icon="mdi:solar-power", ), SolarEdgeLocalSensorEntityDescription( key="energyTotal", name="Lifetime Energy", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, + device_class=SensorDeviceClass.ENERGY, icon="mdi:solar-power", ), SolarEdgeLocalSensorEntityDescription( @@ -118,26 +124,29 @@ SENSOR_TYPES: tuple[SolarEdgeLocalSensorEntityDescription, ...] = ( SolarEdgeLocalSensorEntityDescription( key="optimizercurrent", name="Average Optimizer Current", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, + device_class=SensorDeviceClass.CURRENT, icon="mdi:solar-panel", ), SolarEdgeLocalSensorEntityDescription( key="optimizerpower", name="Average Optimizer Power", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, + device_class=SensorDeviceClass.POWER, icon="mdi:solar-panel", ), SolarEdgeLocalSensorEntityDescription( key="optimizertemperature", name="Average Optimizer Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, icon="mdi:solar-panel", device_class=SensorDeviceClass.TEMPERATURE, ), SolarEdgeLocalSensorEntityDescription( key="optimizervoltage", name="Average Optimizer Voltage", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + device_class=SensorDeviceClass.VOLTAGE, icon="mdi:solar-panel", ), ) @@ -145,7 +154,7 @@ SENSOR_TYPES: tuple[SolarEdgeLocalSensorEntityDescription, ...] = ( SENSOR_TYPE_INVERTER_TEMPERATURE = SolarEdgeLocalSensorEntityDescription( key="invertertemperature", name="Inverter Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, extra_attribute="operating_mode", device_class=SensorDeviceClass.TEMPERATURE, ) @@ -154,13 +163,15 @@ SENSOR_TYPES_ENERGY_IMPORT: tuple[SolarEdgeLocalSensorEntityDescription, ...] = SolarEdgeLocalSensorEntityDescription( key="currentPowerimport", name="current import Power", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, + device_class=SensorDeviceClass.POWER, icon="mdi:arrow-collapse-down", ), SolarEdgeLocalSensorEntityDescription( key="totalEnergyimport", name="total import Energy", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, + device_class=SensorDeviceClass.ENERGY, icon="mdi:counter", ), ) @@ -169,13 +180,15 @@ SENSOR_TYPES_ENERGY_EXPORT: tuple[SolarEdgeLocalSensorEntityDescription, ...] = SolarEdgeLocalSensorEntityDescription( key="currentPowerexport", name="current export Power", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, + device_class=SensorDeviceClass.POWER, icon="mdi:arrow-expand-up", ), SolarEdgeLocalSensorEntityDescription( key="totalEnergyexport", name="total export Energy", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, + device_class=SensorDeviceClass.ENERGY, icon="mdi:counter", ), ) @@ -220,7 +233,9 @@ def setup_platform( # Changing inverter temperature unit. inverter_temp_description = copy(SENSOR_TYPE_INVERTER_TEMPERATURE) if status.inverters.primary.temperature.units.farenheit: - inverter_temp_description.native_unit_of_measurement = TEMP_FAHRENHEIT + inverter_temp_description.native_unit_of_measurement = ( + UnitOfTemperature.FAHRENHEIT + ) # Create entities entities = [ diff --git a/homeassistant/components/solarlog/__init__.py b/homeassistant/components/solarlog/__init__.py index 22c0e3d87d7..d35da5b8a90 100644 --- a/homeassistant/components/solarlog/__init__.py +++ b/homeassistant/components/solarlog/__init__.py @@ -64,7 +64,10 @@ class SolarlogData(update_coordinator.DataUpdateCoordinator): ) self.logger.debug( - "Connection to Solarlog successful. Retrieving latest Solarlog update of %s", + ( + "Connection to Solarlog successful. Retrieving latest Solarlog update" + " of %s" + ), data.time, ) diff --git a/homeassistant/components/solarlog/const.py b/homeassistant/components/solarlog/const.py index 769064089b2..059dab3da78 100644 --- a/homeassistant/components/solarlog/const.py +++ b/homeassistant/components/solarlog/const.py @@ -11,10 +11,10 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.const import ( - ELECTRIC_POTENTIAL_VOLT, - ENERGY_KILO_WATT_HOUR, PERCENTAGE, - POWER_WATT, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfPower, ) from homeassistant.util.dt import as_local @@ -43,27 +43,29 @@ SENSOR_TYPES: tuple[SolarLogSensorEntityDescription, ...] = ( key="power_ac", name="power AC", icon="mdi:solar-power", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, + device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), SolarLogSensorEntityDescription( key="power_dc", name="power DC", icon="mdi:solar-power", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, + device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), SolarLogSensorEntityDescription( key="voltage_ac", name="voltage AC", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, ), SolarLogSensorEntityDescription( key="voltage_dc", name="voltage DC", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, ), @@ -71,7 +73,7 @@ SENSOR_TYPES: tuple[SolarLogSensorEntityDescription, ...] = ( key="yield_day", name="yield day", icon="mdi:solar-power", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, value=lambda value: round(value / 1000, 3), ), @@ -79,7 +81,7 @@ SENSOR_TYPES: tuple[SolarLogSensorEntityDescription, ...] = ( key="yield_yesterday", name="yield yesterday", icon="mdi:solar-power", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, value=lambda value: round(value / 1000, 3), ), @@ -87,7 +89,7 @@ SENSOR_TYPES: tuple[SolarLogSensorEntityDescription, ...] = ( key="yield_month", name="yield month", icon="mdi:solar-power", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, value=lambda value: round(value / 1000, 3), ), @@ -95,7 +97,7 @@ SENSOR_TYPES: tuple[SolarLogSensorEntityDescription, ...] = ( key="yield_year", name="yield year", icon="mdi:solar-power", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, value=lambda value: round(value / 1000, 3), ), @@ -103,7 +105,7 @@ SENSOR_TYPES: tuple[SolarLogSensorEntityDescription, ...] = ( key="yield_total", name="yield total", icon="mdi:solar-power", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL, value=lambda value: round(value / 1000, 3), @@ -111,42 +113,42 @@ SENSOR_TYPES: tuple[SolarLogSensorEntityDescription, ...] = ( SolarLogSensorEntityDescription( key="consumption_ac", name="consumption AC", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), SolarLogSensorEntityDescription( key="consumption_day", name="consumption day", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, value=lambda value: round(value / 1000, 3), ), SolarLogSensorEntityDescription( key="consumption_yesterday", name="consumption yesterday", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, value=lambda value: round(value / 1000, 3), ), SolarLogSensorEntityDescription( key="consumption_month", name="consumption month", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, value=lambda value: round(value / 1000, 3), ), SolarLogSensorEntityDescription( key="consumption_year", name="consumption year", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, value=lambda value: round(value / 1000, 3), ), SolarLogSensorEntityDescription( key="consumption_total", name="consumption total", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL, value=lambda value: round(value / 1000, 3), @@ -155,14 +157,14 @@ SENSOR_TYPES: tuple[SolarLogSensorEntityDescription, ...] = ( key="total_power", name="installed peak power", icon="mdi:solar-power", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, ), SolarLogSensorEntityDescription( key="alternator_loss", name="alternator loss", icon="mdi:solar-power", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), @@ -187,7 +189,7 @@ SENSOR_TYPES: tuple[SolarLogSensorEntityDescription, ...] = ( key="power_available", name="power available", icon="mdi:solar-power", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), diff --git a/homeassistant/components/solarlog/translations/pt.json b/homeassistant/components/solarlog/translations/pt.json index a37c510a366..94f4c6be1ab 100644 --- a/homeassistant/components/solarlog/translations/pt.json +++ b/homeassistant/components/solarlog/translations/pt.json @@ -5,12 +5,12 @@ }, "error": { "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", - "cannot_connect": "Falha na liga\u00e7\u00e3o, por favor verifique o endere\u00e7o do servidor" + "cannot_connect": "A liga\u00e7\u00e3o falhou, por favor verifique o endere\u00e7o do servidor" }, "step": { "user": { "data": { - "host": "Servidor" + "host": "Endere\u00e7o" } } } diff --git a/homeassistant/components/solarlog/translations/sk.json b/homeassistant/components/solarlog/translations/sk.json index 35dd47181e4..943dfa6e511 100644 --- a/homeassistant/components/solarlog/translations/sk.json +++ b/homeassistant/components/solarlog/translations/sk.json @@ -10,8 +10,10 @@ "step": { "user": { "data": { - "host": "Hostite\u013e" - } + "host": "Hostite\u013e", + "name": "Prefix, ktor\u00fd sa m\u00e1 pou\u017ei\u0165 pre va\u0161e senzory Solar-Log" + }, + "title": "Definujte svoje pripojenie Solar-Log" } } } diff --git a/homeassistant/components/solax/sensor.py b/homeassistant/components/solax/sensor.py index 307d3c4c373..2a923c3b725 100644 --- a/homeassistant/components/solax/sensor.py +++ b/homeassistant/components/solax/sensor.py @@ -16,13 +16,13 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - ELECTRIC_CURRENT_AMPERE, - ELECTRIC_POTENTIAL_VOLT, - ENERGY_KILO_WATT_HOUR, - FREQUENCY_HERTZ, PERCENTAGE, - POWER_WATT, - TEMP_CELSIUS, + UnitOfElectricCurrent, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfFrequency, + UnitOfPower, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.exceptions import PlatformNotReady @@ -40,37 +40,37 @@ SENSOR_DESCRIPTIONS: dict[tuple[Units, bool], SensorEntityDescription] = { (Units.C, False): SensorEntityDescription( key=f"{Units.C}_{False}", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, ), (Units.KWH, False): SensorEntityDescription( key=f"{Units.KWH}_{False}", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, state_class=SensorStateClass.MEASUREMENT, ), (Units.KWH, True): SensorEntityDescription( key=f"{Units.KWH}_{True}", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, state_class=SensorStateClass.TOTAL_INCREASING, ), (Units.V, False): SensorEntityDescription( key=f"{Units.V}_{False}", device_class=SensorDeviceClass.VOLTAGE, - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, state_class=SensorStateClass.MEASUREMENT, ), (Units.A, False): SensorEntityDescription( key=f"{Units.A}_{False}", device_class=SensorDeviceClass.CURRENT, - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, state_class=SensorStateClass.MEASUREMENT, ), (Units.W, False): SensorEntityDescription( key=f"{Units.W}_{False}", device_class=SensorDeviceClass.POWER, - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, state_class=SensorStateClass.MEASUREMENT, ), (Units.PERCENT, False): SensorEntityDescription( @@ -82,7 +82,7 @@ SENSOR_DESCRIPTIONS: dict[tuple[Units, bool], SensorEntityDescription] = { (Units.HZ, False): SensorEntityDescription( key=f"{Units.HZ}_{False}", device_class=SensorDeviceClass.FREQUENCY, - native_unit_of_measurement=FREQUENCY_HERTZ, + native_unit_of_measurement=UnitOfFrequency.HERTZ, state_class=SensorStateClass.MEASUREMENT, ), (Units.NONE, False): SensorEntityDescription( diff --git a/homeassistant/components/solax/translations/ko.json b/homeassistant/components/solax/translations/ko.json new file mode 100644 index 00000000000..86d2295dc88 --- /dev/null +++ b/homeassistant/components/solax/translations/ko.json @@ -0,0 +1,17 @@ +{ + "config": { + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "ip_address": "IP \uc8fc\uc18c", + "password": "\ube44\ubc00\ubc88\ud638", + "port": "\ud3ec\ud2b8" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/solax/translations/pt.json b/homeassistant/components/solax/translations/pt.json index 916bd3daced..65fd698bc68 100644 --- a/homeassistant/components/solax/translations/pt.json +++ b/homeassistant/components/solax/translations/pt.json @@ -1,7 +1,7 @@ { "config": { "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "user": { diff --git a/homeassistant/components/solax/translations/sk.json b/homeassistant/components/solax/translations/sk.json index ba1e9330c9b..6de017f1549 100644 --- a/homeassistant/components/solax/translations/sk.json +++ b/homeassistant/components/solax/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "error": { - "cannot_connect": "Nepodarilo sa pripoji\u0165" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { diff --git a/homeassistant/components/soma/__init__.py b/homeassistant/components/soma/__init__.py index 1b3478b2897..7172704c796 100644 --- a/homeassistant/components/soma/__init__.py +++ b/homeassistant/components/soma/__init__.py @@ -88,7 +88,10 @@ def soma_api_call(api_call): if self.is_available: self.is_available = False _LOGGER.warning( - "Device is unreachable (%s). Error while fetching the state: %s", + ( + "Device is unreachable (%s). Error while fetching the" + " state: %s" + ), self.name, response_from_api["msg"], ) diff --git a/homeassistant/components/soma/cover.py b/homeassistant/components/soma/cover.py index 116f88aa20e..ecb004083df 100644 --- a/homeassistant/components/soma/cover.py +++ b/homeassistant/components/soma/cover.py @@ -98,7 +98,8 @@ class SomaTilt(SomaEntity, CoverEntity): response = self.api.set_shade_position(self.device["mac"], target_api_position) if not is_api_response_success(response): raise HomeAssistantError( - f'Error while setting the cover position ({self.name}): {response["msg"]}' + f"Error while setting the cover position ({self.name}):" + f' {response["msg"]}' ) self.set_position(kwargs[ATTR_TILT_POSITION]) @@ -169,7 +170,8 @@ class SomaShade(SomaEntity, CoverEntity): ) if not is_api_response_success(response): raise HomeAssistantError( - f'Error while setting the cover position ({self.name}): {response["msg"]}' + f"Error while setting the cover position ({self.name}):" + f' {response["msg"]}' ) async def async_update(self) -> None: diff --git a/homeassistant/components/soma/translations/en_GB.json b/homeassistant/components/soma/translations/en-GB.json similarity index 100% rename from homeassistant/components/soma/translations/en_GB.json rename to homeassistant/components/soma/translations/en-GB.json diff --git a/homeassistant/components/soma/translations/pt.json b/homeassistant/components/soma/translations/pt.json index 9199c825f4a..8b6bb409628 100644 --- a/homeassistant/components/soma/translations/pt.json +++ b/homeassistant/components/soma/translations/pt.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_setup": "J\u00e1 configurado. Apenas uma \u00fanica configura\u00e7\u00e3o \u00e9 poss\u00edvel.", - "authorize_url_timeout": "Tempo excedido a gerar um URL de autoriza\u00e7\u00e3o" + "authorize_url_timeout": "Excedido o tempo limite para gerar um URL de autoriza\u00e7\u00e3o" }, "create_entry": { "default": "Autenticado com sucesso" diff --git a/homeassistant/components/soma/translations/sk.json b/homeassistant/components/soma/translations/sk.json index 5afd4e7cbb8..1e213d9cf98 100644 --- a/homeassistant/components/soma/translations/sk.json +++ b/homeassistant/components/soma/translations/sk.json @@ -3,7 +3,9 @@ "abort": { "already_setup": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia.", "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL.", - "connection_error": "Nepodarilo sa pripoji\u0165" + "connection_error": "Nepodarilo sa pripoji\u0165", + "missing_configuration": "Komponent Soma nie je nakonfigurovan\u00fd. Postupujte pod\u013ea dokument\u00e1cie.", + "result_error": "SOMA Connect odpovedal chybov\u00fdm stavom." }, "create_entry": { "default": "\u00daspe\u0161ne overen\u00e9" @@ -13,7 +15,9 @@ "data": { "host": "Hostite\u013e", "port": "Port" - } + }, + "description": "Zadajte nastavenia pripojenia v\u00e1\u0161ho SOMA Connect.", + "title": "SOMA Connect" } } } diff --git a/homeassistant/components/somfy/translations/en_GB.json b/homeassistant/components/somfy/translations/en-GB.json similarity index 100% rename from homeassistant/components/somfy/translations/en_GB.json rename to homeassistant/components/somfy/translations/en-GB.json diff --git a/homeassistant/components/somfy/translations/pt.json b/homeassistant/components/somfy/translations/pt.json index 592ccd85589..505c23c7d73 100644 --- a/homeassistant/components/somfy/translations/pt.json +++ b/homeassistant/components/somfy/translations/pt.json @@ -1,9 +1,9 @@ { "config": { "abort": { - "authorize_url_timeout": "Tempo excedido a gerar um URL de autoriza\u00e7\u00e3o", + "authorize_url_timeout": "Excedido o tempo limite para gerar um URL de autoriza\u00e7\u00e3o", "missing_configuration": "O componente n\u00e3o est\u00e1 configurado. Por favor, siga a documenta\u00e7\u00e3o.", - "no_url_available": "Nenhum URL dispon\u00edvel. Para obter informa\u00e7\u00f5es sobre esse erro, [verifique a sec\u00e7\u00e3o de ajuda]({docs_url})", + "no_url_available": "Nenhum URL est\u00e1 dispon\u00edvel. Para obter informa\u00e7\u00f5es sobre esse erro, [consulte a sec\u00e7\u00e3o de ajuda]({docs_url})", "single_instance_allowed": "J\u00e1 configurado. Apenas uma \u00fanica configura\u00e7\u00e3o \u00e9 poss\u00edvel." }, "create_entry": { diff --git a/homeassistant/components/somfy_mylink/translations/de.json b/homeassistant/components/somfy_mylink/translations/de.json index 303337542f0..d173c8c7883 100644 --- a/homeassistant/components/somfy_mylink/translations/de.json +++ b/homeassistant/components/somfy_mylink/translations/de.json @@ -36,7 +36,7 @@ "reverse": "Jalousie ist invertiert" }, "description": "Konfiguriere die Optionen f\u00fcr `{target_name}`", - "title": "MyLink-Cover konfigurieren" + "title": "MyLink Cover konfigurieren" } } } diff --git a/homeassistant/components/somfy_mylink/translations/pt.json b/homeassistant/components/somfy_mylink/translations/pt.json index 2baecf39acf..28fb3c06441 100644 --- a/homeassistant/components/somfy_mylink/translations/pt.json +++ b/homeassistant/components/somfy_mylink/translations/pt.json @@ -11,7 +11,7 @@ }, "options": { "abort": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" } } } \ No newline at end of file diff --git a/homeassistant/components/somfy_mylink/translations/sk.json b/homeassistant/components/somfy_mylink/translations/sk.json index 8fccd88d983..787441ab16a 100644 --- a/homeassistant/components/somfy_mylink/translations/sk.json +++ b/homeassistant/components/somfy_mylink/translations/sk.json @@ -23,6 +23,21 @@ "options": { "abort": { "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, + "step": { + "init": { + "data": { + "target_id": "Konfigur\u00e1cia mo\u017enost\u00ed pre \u017eal\u00fazie." + }, + "title": "Nakonfigurujte mo\u017enosti MyLink" + }, + "target_config": { + "data": { + "reverse": "\u017dal\u00fazie s\u00fa invertovan\u00e9" + }, + "description": "Nakonfigurujte mo\u017enosti pre `{target_name}`", + "title": "Nakonfigurujte MyLink Cover" + } } } } \ No newline at end of file diff --git a/homeassistant/components/sonarr/sensor.py b/homeassistant/components/sonarr/sensor.py index fe440b01be0..1c4b9afb08d 100644 --- a/homeassistant/components/sonarr/sensor.py +++ b/homeassistant/components/sonarr/sensor.py @@ -14,9 +14,13 @@ from aiopyarr import ( SonarrWantedMissing, ) -from homeassistant.components.sensor import SensorEntity, SensorEntityDescription +from homeassistant.components.sensor import ( + SensorDeviceClass, + SensorEntity, + SensorEntityDescription, +) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import DATA_GIGABYTES +from homeassistant.const import UnitOfInformation from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import StateType @@ -49,7 +53,9 @@ def get_disk_space_attr(disks: list[Diskspace]) -> dict[str, str]: free = disk.freeSpace / 1024**3 total = disk.totalSpace / 1024**3 usage = free / total * 100 - attrs[disk.path] = f"{free:.2f}/{total:.2f}{DATA_GIGABYTES} ({usage:.2f}%)" + attrs[ + disk.path + ] = f"{free:.2f}/{total:.2f}{UnitOfInformation.GIGABYTES} ({usage:.2f}%)" return attrs @@ -93,7 +99,8 @@ SENSOR_TYPES: dict[str, SonarrSensorEntityDescription[Any]] = { key="diskspace", name="Disk space", icon="mdi:harddisk", - native_unit_of_measurement=DATA_GIGABYTES, + native_unit_of_measurement=UnitOfInformation.GIGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, entity_registry_enabled_default=False, value_fn=lambda data: f"{sum(disk.freeSpace for disk in data) / 1024**3:.2f}", attributes_fn=get_disk_space_attr, @@ -115,7 +122,9 @@ SENSOR_TYPES: dict[str, SonarrSensorEntityDescription[Any]] = { entity_registry_enabled_default=False, value_fn=len, attributes_fn=lambda data: { - i.title: f"{getattr(i.statistics,'episodeFileCount', 0)}/{getattr(i.statistics, 'episodeCount', 0)} Episodes" + i.title: ( + f"{getattr(i.statistics,'episodeFileCount', 0)}/{getattr(i.statistics, 'episodeCount', 0)} Episodes" + ) for i in data }, ), diff --git a/homeassistant/components/sonarr/translations/de.json b/homeassistant/components/sonarr/translations/de.json index 4559b75c655..6bedaa2418d 100644 --- a/homeassistant/components/sonarr/translations/de.json +++ b/homeassistant/components/sonarr/translations/de.json @@ -12,7 +12,7 @@ "flow_title": "{name}", "step": { "reauth_confirm": { - "description": "Die Sonarr-Integration muss manuell erneut mit der Sonarr-API authentifiziert werden, die unter folgender Adresse gehostet wird: {url}", + "description": "Die Sonarr Integration muss manuell erneut mit der Sonarr-API authentifiziert werden, die unter folgender Adresse gehostet wird: {url}", "title": "Integration erneut authentifizieren" }, "user": { diff --git a/homeassistant/components/sonarr/translations/ko.json b/homeassistant/components/sonarr/translations/ko.json index b7389a03043..5afc4c5d2e2 100644 --- a/homeassistant/components/sonarr/translations/ko.json +++ b/homeassistant/components/sonarr/translations/ko.json @@ -18,6 +18,7 @@ "user": { "data": { "api_key": "API \ud0a4", + "url": "URL \uc8fc\uc18c", "verify_ssl": "SSL \uc778\uc99d\uc11c \ud655\uc778" } } diff --git a/homeassistant/components/sonarr/translations/pt.json b/homeassistant/components/sonarr/translations/pt.json index 9001b9c2c3f..abb005b49dc 100644 --- a/homeassistant/components/sonarr/translations/pt.json +++ b/homeassistant/components/sonarr/translations/pt.json @@ -6,7 +6,7 @@ "unknown": "Erro inesperado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida" }, "flow_title": "Sonarr: {name}", diff --git a/homeassistant/components/sonarr/translations/sk.json b/homeassistant/components/sonarr/translations/sk.json index 23b61b9c00f..737fe34198f 100644 --- a/homeassistant/components/sonarr/translations/sk.json +++ b/homeassistant/components/sonarr/translations/sk.json @@ -12,6 +12,7 @@ "flow_title": "{name}", "step": { "reauth_confirm": { + "description": "Integr\u00e1ciu Sonarru je potrebn\u00e9 manu\u00e1lne op\u00e4tovne overi\u0165 pomocou rozhrania Sonarr API hos\u0165ovan\u00e9ho na: {host}", "title": "Znova overi\u0165 integr\u00e1ciu" }, "user": { @@ -27,6 +28,7 @@ "step": { "init": { "data": { + "upcoming_days": "Po\u010det nadch\u00e1dzaj\u00facich dn\u00ed na zobrazenie", "wanted_max_items": "Maxim\u00e1lny po\u010det po\u017eadovan\u00fdch polo\u017eiek na zobrazenie" } } diff --git a/homeassistant/components/songpal/media_player.py b/homeassistant/components/songpal/media_player.py index 56b5c2d60b0..6b7cf73b28b 100644 --- a/homeassistant/components/songpal/media_player.py +++ b/homeassistant/components/songpal/media_player.py @@ -52,7 +52,8 @@ async def async_setup_platform( ) -> None: """Set up from legacy configuration file. Obsolete.""" _LOGGER.error( - "Configuring Songpal through media_player platform is no longer supported. Convert to songpal platform or UI configuration" + "Configuring Songpal through media_player platform is no longer supported." + " Convert to songpal platform or UI configuration" ) diff --git a/homeassistant/components/songpal/translations/pt.json b/homeassistant/components/songpal/translations/pt.json index db0e0c2a137..9d8c8f54779 100644 --- a/homeassistant/components/songpal/translations/pt.json +++ b/homeassistant/components/songpal/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" } } } \ No newline at end of file diff --git a/homeassistant/components/songpal/translations/sk.json b/homeassistant/components/songpal/translations/sk.json index a81b795afc4..589ee54e556 100644 --- a/homeassistant/components/songpal/translations/sk.json +++ b/homeassistant/components/songpal/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "not_songpal_device": "Nie je to zariadenie Songpal" }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165" @@ -10,6 +11,11 @@ "step": { "init": { "description": "Chcete nastavi\u0165 {name} ({host})?" + }, + "user": { + "data": { + "endpoint": "Koncov\u00fd bod" + } } } } diff --git a/homeassistant/components/sonos/__init__.py b/homeassistant/components/sonos/__init__.py index f0dd8e668fa..2f003e4bde9 100644 --- a/homeassistant/components/sonos/__init__.py +++ b/homeassistant/components/sonos/__init__.py @@ -136,7 +136,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: if deprecated_address := config.get(CONF_INTERFACE_ADDR): _LOGGER.warning( - "'%s' is deprecated, enable %s in the Network integration (https://www.home-assistant.io/integrations/network/)", + ( + "'%s' is deprecated, enable %s in the Network integration" + " (https://www.home-assistant.io/integrations/network/)" + ), CONF_INTERFACE_ADDR, deprecated_address, ) @@ -365,7 +368,8 @@ class SonosDiscoveryManager: """Handle discovery via ssdp or zeroconf.""" if self._manual_config_required: _LOGGER.warning( - "Automatic discovery is working, Sonos hosts in configuration.yaml are not needed" + "Automatic discovery is working, Sonos hosts in configuration.yaml are" + " not needed" ) self._manual_config_required = False if model in DISCOVERY_IGNORED_MODELS: diff --git a/homeassistant/components/sonos/entity.py b/homeassistant/components/sonos/entity.py index 0955fb0e82d..ac7b96ec965 100644 --- a/homeassistant/components/sonos/entity.py +++ b/homeassistant/components/sonos/entity.py @@ -58,12 +58,21 @@ class SonosEntity(Entity): """Poll the entity if subscriptions fail.""" if not self.speaker.subscriptions_failed: if soco_config.EVENT_ADVERTISE_IP: - listener_msg = f"{self.speaker.subscription_address} (advertising as {soco_config.EVENT_ADVERTISE_IP})" + listener_msg = ( + f"{self.speaker.subscription_address}" + f" (advertising as {soco_config.EVENT_ADVERTISE_IP})" + ) else: listener_msg = self.speaker.subscription_address - message = f"{self.speaker.zone_name} cannot reach {listener_msg}, falling back to polling, functionality may be limited" + message = ( + f"{self.speaker.zone_name} cannot reach {listener_msg}," + " falling back to polling, functionality may be limited" + ) log_link_msg = f", see {SUB_FAIL_URL} for more details" - notification_link_msg = f'.\n\nSee Sonos documentation for more details.' + notification_link_msg = ( + f'.\n\nSee Sonos documentation' + " for more details." + ) _LOGGER.warning(message + log_link_msg) persistent_notification.async_create( self.hass, diff --git a/homeassistant/components/sonos/helpers.py b/homeassistant/components/sonos/helpers.py index 9e8fcbd6a12..552d104786e 100644 --- a/homeassistant/components/sonos/helpers.py +++ b/homeassistant/components/sonos/helpers.py @@ -94,7 +94,7 @@ def soco_error( def _find_target_identifier(instance: Any, fallback_soco: SoCo | None) -> str | None: - """Extract the the best available target identifier from the provided instance object.""" + """Extract the best available target identifier from the provided instance object.""" if entity_id := getattr(instance, "entity_id", None): # SonosEntity instance return entity_id diff --git a/homeassistant/components/sonos/speaker.py b/homeassistant/components/sonos/speaker.py index 38d37e7cfd4..5460230b66f 100644 --- a/homeassistant/components/sonos/speaker.py +++ b/homeassistant/components/sonos/speaker.py @@ -657,7 +657,10 @@ class SonosSpeaker: return if "BattChg" not in battery_dict: _LOGGER.debug( - "Unknown device properties update for %s (%s), please report an issue: '%s'", + ( + "Unknown device properties update for %s (%s)," + " please report an issue: '%s'" + ), self.zone_name, self.model_name, more_info, @@ -1047,7 +1050,8 @@ class SonosSpeaker: speakers_set = {s for s in speakers if s.soco_snapshot} if missing_snapshots := set(speakers) - speakers_set: raise HomeAssistantError( - f"Restore failed, speakers are missing snapshots: {[s.zone_name for s in missing_snapshots]}" + "Restore failed, speakers are missing snapshots:" + f" {[s.zone_name for s in missing_snapshots]}" ) if with_group: diff --git a/homeassistant/components/sonos/switch.py b/homeassistant/components/sonos/switch.py index bbeda6edb63..537f25830b3 100644 --- a/homeassistant/components/sonos/switch.py +++ b/homeassistant/components/sonos/switch.py @@ -255,7 +255,10 @@ class SonosAlarmEntity(SonosEntity, SwitchEntity): @property def name(self) -> str: """Return the name of the sensor.""" - return f"{self.alarm.recurrence.capitalize()} alarm {str(self.alarm.start_time)[:5]}" + return ( + f"{self.alarm.recurrence.capitalize()} alarm" + f" {str(self.alarm.start_time)[:5]}" + ) async def _async_fallback_poll(self) -> None: """Call the central alarm polling method.""" @@ -424,7 +427,10 @@ def async_migrate_speech_enhancement_entity_unique_id( if len(speech_enhancement_entries) > 1: _LOGGER.warning( - "Migration of Speech Enhancement switches on %s failed, manual cleanup required: %s", + ( + "Migration of Speech Enhancement switches on %s failed," + " manual cleanup required: %s" + ), speaker.zone_name, [e.entity_id for e in speech_enhancement_entries], ) diff --git a/homeassistant/components/sonos/translations/lt.json b/homeassistant/components/sonos/translations/lt.json new file mode 100644 index 00000000000..fc1b49c1b25 --- /dev/null +++ b/homeassistant/components/sonos/translations/lt.json @@ -0,0 +1,9 @@ +{ + "config": { + "step": { + "confirm": { + "description": "Ar norite nustatyti \u201eSonos\u201c?" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sonos/translations/sk.json b/homeassistant/components/sonos/translations/sk.json index 99798036ffd..e6857536f93 100644 --- a/homeassistant/components/sonos/translations/sk.json +++ b/homeassistant/components/sonos/translations/sk.json @@ -2,7 +2,13 @@ "config": { "abort": { "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", + "not_sonos_device": "Zisten\u00e9 zariadenie nie je zariadenie Sonos", "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, + "step": { + "confirm": { + "description": "Chcete nastavi\u0165 Sonos?" + } } } } \ No newline at end of file diff --git a/homeassistant/components/soundtouch/translations/ko.json b/homeassistant/components/soundtouch/translations/ko.json index fc4995bcf2c..54eae29c72e 100644 --- a/homeassistant/components/soundtouch/translations/ko.json +++ b/homeassistant/components/soundtouch/translations/ko.json @@ -1,5 +1,11 @@ { "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/soundtouch/translations/pt.json b/homeassistant/components/soundtouch/translations/pt.json index 91786f4b324..6d81843965d 100644 --- a/homeassistant/components/soundtouch/translations/pt.json +++ b/homeassistant/components/soundtouch/translations/pt.json @@ -4,12 +4,12 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "user": { "data": { - "host": "Servidor" + "host": "Endere\u00e7o" } } } diff --git a/homeassistant/components/soundtouch/translations/sk.json b/homeassistant/components/soundtouch/translations/sk.json index c8e33ffce5e..f8175204781 100644 --- a/homeassistant/components/soundtouch/translations/sk.json +++ b/homeassistant/components/soundtouch/translations/sk.json @@ -11,7 +11,17 @@ "data": { "host": "Hostite\u013e" } + }, + "zeroconf_confirm": { + "description": "Chyst\u00e1te sa prida\u0165 zariadenie SoundTouch s n\u00e1zvom `{name}` do Home Assistant.", + "title": "Potvr\u010fte pridanie zariadenia Bose SoundTouch" } } + }, + "issues": { + "deprecated_yaml": { + "description": "Konfigur\u00e1cia Bose SoundTouch pomocou YAML sa odstra\u0148uje. \n\n Va\u0161a existuj\u00faca konfigur\u00e1cia YAML bola importovan\u00e1 do pou\u017e\u00edvate\u013esk\u00e9ho rozhrania automaticky. \n\n Odstr\u00e1\u0148te konfigur\u00e1ciu Bose SoundTouch YAML zo s\u00faboru configuration.yaml a re\u0161tartujte Home Assistant, aby ste tento probl\u00e9m vyrie\u0161ili.", + "title": "Konfigur\u00e1cia Bose SoundTouch YAML sa odstra\u0148uje" + } } } \ No newline at end of file diff --git a/homeassistant/components/speedtestdotnet/__init__.py b/homeassistant/components/speedtestdotnet/__init__.py index 8cf32def197..67abecdf5d0 100644 --- a/homeassistant/components/speedtestdotnet/__init__.py +++ b/homeassistant/components/speedtestdotnet/__init__.py @@ -1,51 +1,43 @@ """Support for testing internet speed via Speedtest.net.""" from __future__ import annotations -from datetime import timedelta -import logging +from functools import partial import speedtest from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_SCAN_INTERVAL, EVENT_HOMEASSISTANT_STARTED -from homeassistant.core import CoreState, HomeAssistant +from homeassistant.const import EVENT_HOMEASSISTANT_STARTED, Platform +from homeassistant.core import CoreState, Event, HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady -from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed -from .const import ( - CONF_MANUAL, - CONF_SERVER_ID, - DEFAULT_SCAN_INTERVAL, - DEFAULT_SERVER, - DOMAIN, - PLATFORMS, -) +from .const import DOMAIN +from .coordinator import SpeedTestDataCoordinator -_LOGGER = logging.getLogger(__name__) +PLATFORMS = [Platform.SENSOR] async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: """Set up the Speedtest.net component.""" - coordinator = SpeedTestDataCoordinator(hass, config_entry) - await coordinator.async_setup() - - async def _enable_scheduled_speedtests(*_): - """Activate the data update coordinator.""" - coordinator.update_interval = timedelta( - minutes=config_entry.options.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL) + try: + api = await hass.async_add_executor_job( + partial(speedtest.Speedtest, secure=True) ) - await coordinator.async_refresh() + coordinator = SpeedTestDataCoordinator(hass, config_entry, api) + await hass.async_add_executor_job(coordinator.update_servers) + except speedtest.SpeedtestException as err: + raise ConfigEntryNotReady from err - if not config_entry.options.get(CONF_MANUAL, False): - if hass.state == CoreState.running: - await _enable_scheduled_speedtests() - else: - # Running a speed test during startup can prevent - # integrations from being able to setup because it - # can saturate the network interface. - hass.bus.async_listen_once( - EVENT_HOMEASSISTANT_STARTED, _enable_scheduled_speedtests - ) + async def _request_refresh(event: Event) -> None: + """Request a refresh.""" + await coordinator.async_request_refresh() + + if hass.state == CoreState.running: + await coordinator.async_config_entry_first_refresh() + else: + # Running a speed test during startup can prevent + # integrations from being able to setup because it + # can saturate the network interface. + hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STARTED, _request_refresh) hass.data[DOMAIN] = coordinator @@ -56,99 +48,8 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: """Unload SpeedTest Entry from config_entry.""" - unload_ok = await hass.config_entries.async_unload_platforms( + if unload_ok := await hass.config_entries.async_unload_platforms( config_entry, PLATFORMS - ) - if unload_ok: + ): hass.data.pop(DOMAIN) return unload_ok - - -class SpeedTestDataCoordinator(DataUpdateCoordinator): - """Get the latest data from speedtest.net.""" - - def __init__(self, hass: HomeAssistant, config_entry: ConfigEntry) -> None: - """Initialize the data object.""" - self.hass = hass - self.config_entry: ConfigEntry = config_entry - self.api: speedtest.Speedtest | None = None - self.servers: dict[str, dict] = {DEFAULT_SERVER: {}} - super().__init__( - self.hass, - _LOGGER, - name=DOMAIN, - update_method=self.async_update, - ) - - def initialize(self) -> None: - """Initialize speedtest api.""" - self.api = speedtest.Speedtest(secure=True) - self.update_servers() - - def update_servers(self): - """Update list of test servers.""" - test_servers = self.api.get_servers() - test_servers_list = [] - for servers in test_servers.values(): - for server in servers: - test_servers_list.append(server) - for server in sorted( - test_servers_list, - key=lambda server: ( - server["country"], - server["name"], - server["sponsor"], - ), - ): - self.servers[ - f"{server['country']} - {server['sponsor']} - {server['name']}" - ] = server - - def update_data(self): - """Get the latest data from speedtest.net.""" - self.update_servers() - self.api.closest.clear() - if self.config_entry.options.get(CONF_SERVER_ID): - server_id = self.config_entry.options.get(CONF_SERVER_ID) - self.api.get_servers(servers=[server_id]) - - best_server = self.api.get_best_server() - _LOGGER.debug( - "Executing speedtest.net speed test with server_id: %s", - best_server["id"], - ) - self.api.download() - self.api.upload() - return self.api.results.dict() - - async def async_update(self) -> dict[str, str]: - """Update Speedtest data.""" - try: - return await self.hass.async_add_executor_job(self.update_data) - except speedtest.NoMatchedServers as err: - raise UpdateFailed("Selected server is not found.") from err - except speedtest.SpeedtestException as err: - raise UpdateFailed(err) from err - - async def async_setup(self) -> None: - """Set up SpeedTest.""" - try: - await self.hass.async_add_executor_job(self.initialize) - except speedtest.SpeedtestException as err: - raise ConfigEntryNotReady from err - - self.config_entry.async_on_unload( - self.config_entry.add_update_listener(options_updated_listener) - ) - - -async def options_updated_listener(hass: HomeAssistant, entry: ConfigEntry) -> None: - """Handle options update.""" - if entry.options[CONF_MANUAL]: - hass.data[DOMAIN].update_interval = None - return - - hass.data[DOMAIN].update_interval = timedelta( - minutes=entry.options[CONF_SCAN_INTERVAL] - ) - await hass.data[DOMAIN].async_request_refresh() diff --git a/homeassistant/components/speedtestdotnet/config_flow.py b/homeassistant/components/speedtestdotnet/config_flow.py index d82ac6bf728..58ef53b2c9c 100644 --- a/homeassistant/components/speedtestdotnet/config_flow.py +++ b/homeassistant/components/speedtestdotnet/config_flow.py @@ -6,16 +6,13 @@ from typing import Any import voluptuous as vol from homeassistant import config_entries -from homeassistant.const import CONF_SCAN_INTERVAL from homeassistant.core import callback from homeassistant.data_entry_flow import FlowResult from .const import ( - CONF_MANUAL, CONF_SERVER_ID, CONF_SERVER_NAME, DEFAULT_NAME, - DEFAULT_SCAN_INTERVAL, DEFAULT_SERVER, DOMAIN, ) @@ -30,7 +27,7 @@ class SpeedTestFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): @callback def async_get_options_flow( config_entry: config_entries.ConfigEntry, - ) -> config_entries.OptionsFlow: + ) -> SpeedTestOptionsFlowHandler: """Get the options flow for this handler.""" return SpeedTestOptionsFlowHandler(config_entry) @@ -78,15 +75,6 @@ class SpeedTestOptionsFlowHandler(config_entries.OptionsFlow): CONF_SERVER_NAME, default=self.config_entry.options.get(CONF_SERVER_NAME, DEFAULT_SERVER), ): vol.In(self._servers.keys()), - vol.Optional( - CONF_SCAN_INTERVAL, - default=self.config_entry.options.get( - CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL - ), - ): int, - vol.Optional( - CONF_MANUAL, default=self.config_entry.options.get(CONF_MANUAL, False) - ): bool, } return self.async_show_form( diff --git a/homeassistant/components/speedtestdotnet/const.py b/homeassistant/components/speedtestdotnet/const.py index e6ced462b45..0d2625bfe33 100644 --- a/homeassistant/components/speedtestdotnet/const.py +++ b/homeassistant/components/speedtestdotnet/const.py @@ -1,53 +1,12 @@ """Constants used by Speedtest.net.""" from __future__ import annotations -from collections.abc import Callable -from dataclasses import dataclass from typing import Final -from homeassistant.components.sensor import SensorEntityDescription, SensorStateClass -from homeassistant.const import ( - DATA_RATE_MEGABITS_PER_SECOND, - TIME_MILLISECONDS, - Platform, -) - DOMAIN: Final = "speedtestdotnet" - -@dataclass -class SpeedtestSensorEntityDescription(SensorEntityDescription): - """Class describing Speedtest sensor entities.""" - - value: Callable = round - - -SENSOR_TYPES: Final[tuple[SpeedtestSensorEntityDescription, ...]] = ( - SpeedtestSensorEntityDescription( - key="ping", - name="Ping", - native_unit_of_measurement=TIME_MILLISECONDS, - state_class=SensorStateClass.MEASUREMENT, - ), - SpeedtestSensorEntityDescription( - key="download", - name="Download", - native_unit_of_measurement=DATA_RATE_MEGABITS_PER_SECOND, - state_class=SensorStateClass.MEASUREMENT, - value=lambda value: round(value / 10**6, 2), - ), - SpeedtestSensorEntityDescription( - key="upload", - name="Upload", - native_unit_of_measurement=DATA_RATE_MEGABITS_PER_SECOND, - state_class=SensorStateClass.MEASUREMENT, - value=lambda value: round(value / 10**6, 2), - ), -) - CONF_SERVER_NAME: Final = "server_name" CONF_SERVER_ID: Final = "server_id" -CONF_MANUAL: Final = "manual" ATTR_BYTES_RECEIVED: Final = "bytes_received" ATTR_BYTES_SENT: Final = "bytes_sent" @@ -63,5 +22,3 @@ DEFAULT_SERVER: Final = "*Auto Detect" ATTRIBUTION: Final = "Data retrieved from Speedtest.net by Ookla" ICON: Final = "mdi:speedometer" - -PLATFORMS: Final = [Platform.SENSOR] diff --git a/homeassistant/components/speedtestdotnet/coordinator.py b/homeassistant/components/speedtestdotnet/coordinator.py new file mode 100644 index 00000000000..e07bb94342d --- /dev/null +++ b/homeassistant/components/speedtestdotnet/coordinator.py @@ -0,0 +1,81 @@ +"""Coordinator for speedtestdotnet.""" + +from datetime import timedelta +import logging +from typing import Any, cast + +import speedtest + +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed + +from .const import CONF_SERVER_ID, DEFAULT_SCAN_INTERVAL, DEFAULT_SERVER, DOMAIN + +_LOGGER = logging.getLogger(__name__) + + +class SpeedTestDataCoordinator(DataUpdateCoordinator[dict[str, Any]]): + """Get the latest data from speedtest.net.""" + + config_entry: ConfigEntry + + def __init__( + self, hass: HomeAssistant, config_entry: ConfigEntry, api: speedtest.Speedtest + ) -> None: + """Initialize the data object.""" + self.hass = hass + self.config_entry = config_entry + self.api = api + self.servers: dict[str, dict] = {DEFAULT_SERVER: {}} + super().__init__( + self.hass, + _LOGGER, + name=DOMAIN, + update_interval=timedelta(minutes=DEFAULT_SCAN_INTERVAL), + ) + + def update_servers(self) -> None: + """Update list of test servers.""" + test_servers = self.api.get_servers() + test_servers_list = [] + for servers in test_servers.values(): + for server in servers: + test_servers_list.append(server) + for server in sorted( + test_servers_list, + key=lambda server: ( + server["country"], + server["name"], + server["sponsor"], + ), + ): + self.servers[ + f"{server['country']} - {server['sponsor']} - {server['name']}" + ] = server + + def update_data(self) -> dict[str, Any]: + """Get the latest data from speedtest.net.""" + self.update_servers() + self.api.closest.clear() + if self.config_entry.options.get(CONF_SERVER_ID): + server_id = self.config_entry.options.get(CONF_SERVER_ID) + self.api.get_servers(servers=[server_id]) + + best_server = self.api.get_best_server() + _LOGGER.debug( + "Executing speedtest.net speed test with server_id: %s", + best_server["id"], + ) + self.api.download() + self.api.upload() + return cast(dict[str, Any], self.api.results.dict()) + + async def _async_update_data(self) -> dict[str, Any]: + """Update Speedtest data.""" + try: + return await self.hass.async_add_executor_job(self.update_data) + except speedtest.NoMatchedServers as err: + raise UpdateFailed("Selected server is not found.") from err + except speedtest.SpeedtestException as err: + raise UpdateFailed(err) from err diff --git a/homeassistant/components/speedtestdotnet/sensor.py b/homeassistant/components/speedtestdotnet/sensor.py index e1ba0f560bb..c3773802650 100644 --- a/homeassistant/components/speedtestdotnet/sensor.py +++ b/homeassistant/components/speedtestdotnet/sensor.py @@ -1,10 +1,17 @@ """Support for Speedtest.net internet speed testing sensor.""" from __future__ import annotations +from collections.abc import Callable +from dataclasses import dataclass from typing import Any, cast -from homeassistant.components.sensor import SensorEntity +from homeassistant.components.sensor import ( + SensorEntity, + SensorEntityDescription, + SensorStateClass, +) from homeassistant.config_entries import ConfigEntry +from homeassistant.const import UnitOfDataRate, UnitOfTime from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceEntryType from homeassistant.helpers.entity import DeviceInfo @@ -13,7 +20,6 @@ from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.helpers.typing import StateType from homeassistant.helpers.update_coordinator import CoordinatorEntity -from . import SpeedTestDataCoordinator from .const import ( ATTR_BYTES_RECEIVED, ATTR_BYTES_SENT, @@ -24,8 +30,38 @@ from .const import ( DEFAULT_NAME, DOMAIN, ICON, - SENSOR_TYPES, - SpeedtestSensorEntityDescription, +) +from .coordinator import SpeedTestDataCoordinator + + +@dataclass +class SpeedtestSensorEntityDescription(SensorEntityDescription): + """Class describing Speedtest sensor entities.""" + + value: Callable = round + + +SENSOR_TYPES: tuple[SpeedtestSensorEntityDescription, ...] = ( + SpeedtestSensorEntityDescription( + key="ping", + name="Ping", + native_unit_of_measurement=UnitOfTime.MILLISECONDS, + state_class=SensorStateClass.MEASUREMENT, + ), + SpeedtestSensorEntityDescription( + key="download", + name="Download", + native_unit_of_measurement=UnitOfDataRate.MEGABITS_PER_SECOND, + state_class=SensorStateClass.MEASUREMENT, + value=lambda value: round(value / 10**6, 2), + ), + SpeedtestSensorEntityDescription( + key="upload", + name="Upload", + native_unit_of_measurement=UnitOfDataRate.MEGABITS_PER_SECOND, + state_class=SensorStateClass.MEASUREMENT, + value=lambda value: round(value / 10**6, 2), + ), ) diff --git a/homeassistant/components/speedtestdotnet/strings.json b/homeassistant/components/speedtestdotnet/strings.json index c4dad30cb09..09515dfd4c8 100644 --- a/homeassistant/components/speedtestdotnet/strings.json +++ b/homeassistant/components/speedtestdotnet/strings.json @@ -13,8 +13,6 @@ "step": { "init": { "data": { - "scan_interval": "Update frequency (minutes)", - "manual": "Disable auto update", "server_name": "Select test server" } } diff --git a/homeassistant/components/speedtestdotnet/translations/en.json b/homeassistant/components/speedtestdotnet/translations/en.json index eab480073bc..8b487f5fa1e 100644 --- a/homeassistant/components/speedtestdotnet/translations/en.json +++ b/homeassistant/components/speedtestdotnet/translations/en.json @@ -5,7 +5,7 @@ }, "step": { "user": { - "description": "Do you want to start set up?" + "description": "Do you want to start setup?" } } }, diff --git a/homeassistant/components/speedtestdotnet/translations/it.json b/homeassistant/components/speedtestdotnet/translations/it.json index 07615fe093c..84d0a311de9 100644 --- a/homeassistant/components/speedtestdotnet/translations/it.json +++ b/homeassistant/components/speedtestdotnet/translations/it.json @@ -5,7 +5,7 @@ }, "step": { "user": { - "description": "Vuoi iniziare la configurazione?" + "description": "Vuoi avviare la configurazione?" } } }, diff --git a/homeassistant/components/speedtestdotnet/translations/pt.json b/homeassistant/components/speedtestdotnet/translations/pt.json index c299020ce9a..9094c60428c 100644 --- a/homeassistant/components/speedtestdotnet/translations/pt.json +++ b/homeassistant/components/speedtestdotnet/translations/pt.json @@ -5,7 +5,7 @@ }, "step": { "user": { - "description": "Quer dar inicio \u00e0 configura\u00e7\u00e3o?" + "description": "Quer dar in\u00edcio \u00e0 configura\u00e7\u00e3o?" } } } diff --git a/homeassistant/components/speedtestdotnet/translations/sk.json b/homeassistant/components/speedtestdotnet/translations/sk.json index a74820e4d4f..6356d6c6428 100644 --- a/homeassistant/components/speedtestdotnet/translations/sk.json +++ b/homeassistant/components/speedtestdotnet/translations/sk.json @@ -14,7 +14,8 @@ "init": { "data": { "manual": "Zak\u00e1za\u0165 automatick\u00fa aktualiz\u00e1ciu", - "scan_interval": "Frekvencia aktualiz\u00e1cie (min\u00faty)" + "scan_interval": "Frekvencia aktualiz\u00e1cie (min\u00faty)", + "server_name": "Vyberte testovac\u00ed server" } } } diff --git a/homeassistant/components/spider/climate.py b/homeassistant/components/spider/climate.py index 111c1e377cf..261d1565160 100644 --- a/homeassistant/components/spider/climate.py +++ b/homeassistant/components/spider/climate.py @@ -7,7 +7,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -40,7 +40,7 @@ async def async_setup_entry( class SpiderThermostat(ClimateEntity): """Representation of a thermostat.""" - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS def __init__(self, api, thermostat): """Initialize the thermostat.""" diff --git a/homeassistant/components/spider/sensor.py b/homeassistant/components/spider/sensor.py index d8006885553..259c0fa4974 100644 --- a/homeassistant/components/spider/sensor.py +++ b/homeassistant/components/spider/sensor.py @@ -7,7 +7,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ENERGY_KILO_WATT_HOUR, POWER_WATT +from homeassistant.const import UnitOfEnergy, UnitOfPower from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -32,7 +32,7 @@ async def async_setup_entry( class SpiderPowerPlugEnergy(SensorEntity): """Representation of a Spider Power Plug (energy).""" - _attr_native_unit_of_measurement = ENERGY_KILO_WATT_HOUR + _attr_native_unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR _attr_device_class = SensorDeviceClass.ENERGY _attr_state_class = SensorStateClass.TOTAL_INCREASING @@ -76,7 +76,7 @@ class SpiderPowerPlugPower(SensorEntity): _attr_device_class = SensorDeviceClass.POWER _attr_state_class = SensorStateClass.MEASUREMENT - _attr_native_unit_of_measurement = POWER_WATT + _attr_native_unit_of_measurement = UnitOfPower.WATT def __init__(self, api, power_plug) -> None: """Initialize the Spider Power Plug.""" diff --git a/homeassistant/components/spider/translations/sk.json b/homeassistant/components/spider/translations/sk.json index 52f9f59ae60..181e0955881 100644 --- a/homeassistant/components/spider/translations/sk.json +++ b/homeassistant/components/spider/translations/sk.json @@ -12,7 +12,8 @@ "data": { "password": "Heslo", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" - } + }, + "title": "Prihl\u00e1ste sa pomocou \u00fa\u010dtu mijn.ithodaalderop.nl" } } } diff --git a/homeassistant/components/spotify/manifest.json b/homeassistant/components/spotify/manifest.json index be662e969f2..79aedc4c866 100644 --- a/homeassistant/components/spotify/manifest.json +++ b/homeassistant/components/spotify/manifest.json @@ -2,7 +2,7 @@ "domain": "spotify", "name": "Spotify", "documentation": "https://www.home-assistant.io/integrations/spotify", - "requirements": ["spotipy==2.21.0"], + "requirements": ["spotipy==2.22.0"], "zeroconf": ["_spotify-connect._tcp.local."], "dependencies": ["application_credentials"], "codeowners": ["@frenck"], diff --git a/homeassistant/components/spotify/media_player.py b/homeassistant/components/spotify/media_player.py index 00354e84b92..d09b2795e88 100644 --- a/homeassistant/components/spotify/media_player.py +++ b/homeassistant/components/spotify/media_player.py @@ -378,7 +378,8 @@ class SpotifyMediaPlayer(MediaPlayerEntity): if not self._scope_ok: _LOGGER.debug( - "Spotify scopes are not set correctly, this can impact features such as media browsing" + "Spotify scopes are not set correctly, this can impact features such as" + " media browsing" ) raise NotImplementedError diff --git a/homeassistant/components/spotify/translations/en_GB.json b/homeassistant/components/spotify/translations/en-GB.json similarity index 100% rename from homeassistant/components/spotify/translations/en_GB.json rename to homeassistant/components/spotify/translations/en-GB.json diff --git a/homeassistant/components/spotify/translations/pt.json b/homeassistant/components/spotify/translations/pt.json index 0719e226cd6..df4133c8a53 100644 --- a/homeassistant/components/spotify/translations/pt.json +++ b/homeassistant/components/spotify/translations/pt.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "no_url_available": "Nenhum URL dispon\u00edvel. Para obter informa\u00e7\u00f5es sobre esse erro, [verifique a sec\u00e7\u00e3o de ajuda]({docs_url})", + "no_url_available": "Nenhum URL est\u00e1 dispon\u00edvel. Para obter informa\u00e7\u00f5es sobre esse erro, [consulte a sec\u00e7\u00e3o de ajuda]({docs_url})", "reauth_account_mismatch": "A conta Spotify com a qual foi autenticada n\u00e3o corresponde \u00e0 conta necess\u00e1ria para a reautentica\u00e7\u00e3o." }, "step": { diff --git a/homeassistant/components/spotify/translations/sk.json b/homeassistant/components/spotify/translations/sk.json index dc3096178d5..79e840e2ce6 100644 --- a/homeassistant/components/spotify/translations/sk.json +++ b/homeassistant/components/spotify/translations/sk.json @@ -1,7 +1,10 @@ { "config": { "abort": { - "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL." + "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL.", + "missing_configuration": "Integr\u00e1cia Spotify nie je nakonfigurovan\u00e1. Postupujte pod\u013ea dokument\u00e1cie.", + "no_url_available": "Nie je k dispoz\u00edcii \u017eiadna adresa URL. Inform\u00e1cie o tejto chybe n\u00e1jdete [pozrite si sekciu pomocn\u00edka]({docs_url})", + "reauth_account_mismatch": "\u00da\u010det Spotify overen\u00fd pomocou nezodpoved\u00e1 kontu, ktor\u00e9 vy\u017eaduje op\u00e4tovn\u00e9 overenie." }, "create_entry": { "default": "\u00daspe\u0161ne overen\u00e9 v slu\u017ebe Spotify." @@ -11,8 +14,20 @@ "title": "Vyberte met\u00f3du overenia" }, "reauth_confirm": { + "description": "Integr\u00e1cia slu\u017eby Spotify sa mus\u00ed znova overi\u0165 pomocou slu\u017eby Spotify pre \u00fa\u010det: {account}", "title": "Znova overi\u0165 integr\u00e1ciu" } } + }, + "issues": { + "removed_yaml": { + "description": "Konfigur\u00e1cia Spotify pomocou YAML bola odstr\u00e1nen\u00e1. \n\n Asistent dom\u00e1cnosti nepou\u017e\u00edva va\u0161u existuj\u00facu konfigur\u00e1ciu YAML. \n\n Ak chcete tento probl\u00e9m vyrie\u0161i\u0165, odstr\u00e1\u0148te konfigur\u00e1ciu YAML zo s\u00faboru configuration.yaml a re\u0161tartujte aplik\u00e1ciu Home Assistant.", + "title": "Konfigur\u00e1cia Spotify YAML bola odstr\u00e1nen\u00e1" + } + }, + "system_health": { + "info": { + "api_endpoint_reachable": "Koncov\u00fd bod Spotify API je dostupn\u00fd" + } } } \ No newline at end of file diff --git a/homeassistant/components/sql/translations/sk.json b/homeassistant/components/sql/translations/sk.json index 1db0d01443c..fc5ddef8ca4 100644 --- a/homeassistant/components/sql/translations/sk.json +++ b/homeassistant/components/sql/translations/sk.json @@ -13,11 +13,17 @@ "column": "St\u013apec", "db_url": "URL datab\u00e1zy", "name": "N\u00e1zov", - "unit_of_measurement": "Mern\u00e1 jednotka" + "query": "V\u00fdber po\u017eiadavky", + "unit_of_measurement": "Mern\u00e1 jednotka", + "value_template": "\u0160abl\u00f3na hodnoty" }, "data_description": { + "column": "St\u013apec pre vr\u00e1ten\u00fd dopyt, ktor\u00fd sa m\u00e1 prezentova\u0165 ako stav", "db_url": "Adresa URL datab\u00e1zy, nechajte pr\u00e1zdnu, ak chcete pou\u017ei\u0165 predvolen\u00fa datab\u00e1zu HA", - "unit_of_measurement": "Jednotka miery (volite\u013en\u00e9)" + "name": "N\u00e1zov, ktor\u00fd sa pou\u017eije pre polo\u017eku Config Entry a tie\u017e pre sn\u00edma\u010d", + "query": "Po\u017eiadavka, ktor\u00e1 sa m\u00e1 spusti\u0165, mus\u00ed za\u010d\u00edna\u0165 slovom 'SELECT'", + "unit_of_measurement": "Jednotka miery (volite\u013en\u00e9)", + "value_template": "\u0160abl\u00f3na hodnoty (volite\u013en\u00e9)" } } } @@ -33,11 +39,17 @@ "column": "St\u013apec", "db_url": "URL datab\u00e1zy", "name": "N\u00e1zov", - "unit_of_measurement": "Mern\u00e1 jednotka" + "query": "V\u00fdber po\u017eiadavky", + "unit_of_measurement": "Mern\u00e1 jednotka", + "value_template": "\u0160abl\u00f3na hodnoty" }, "data_description": { + "column": "St\u013apec pre vr\u00e1ten\u00fd dopyt, ktor\u00fd sa m\u00e1 prezentova\u0165 ako stav", "db_url": "Adresa URL datab\u00e1zy, nechajte pr\u00e1zdnu, ak chcete pou\u017ei\u0165 predvolen\u00fa datab\u00e1zu HA", - "unit_of_measurement": "Jednotka miery (volite\u013en\u00e9)" + "name": "N\u00e1zov, ktor\u00fd sa pou\u017eije pre polo\u017eku Config Entry a tie\u017e pre sn\u00edma\u010d", + "query": "Po\u017eiadavka, ktor\u00e1 sa m\u00e1 spusti\u0165, mus\u00ed za\u010d\u00edna\u0165 slovom 'SELECT'", + "unit_of_measurement": "Jednotka miery (volite\u013en\u00e9)", + "value_template": "\u0160abl\u00f3na hodnoty (volite\u013en\u00e9)" } } } diff --git a/homeassistant/components/squeezebox/media_player.py b/homeassistant/components/squeezebox/media_player.py index 394f57d1c2d..6a171ffe30f 100644 --- a/homeassistant/components/squeezebox/media_player.py +++ b/homeassistant/components/squeezebox/media_player.py @@ -590,7 +590,8 @@ class SqueezeBoxEntity(MediaPlayerEntity): async def async_sync(self, other_player): """Sync this Squeezebox player to another. Deprecated.""" _LOGGER.warning( - "Service squeezebox.sync is deprecated; use media_player.join_players instead" + "Service squeezebox.sync is deprecated; use media_player.join_players" + " instead" ) await self.async_join_players([other_player]) @@ -601,7 +602,8 @@ class SqueezeBoxEntity(MediaPlayerEntity): async def async_unsync(self): """Unsync this Squeezebox player. Deprecated.""" _LOGGER.warning( - "Service squeezebox.unsync is deprecated; use media_player.unjoin_player instead" + "Service squeezebox.unsync is deprecated; use media_player.unjoin_player" + " instead" ) await self.async_unjoin_player() diff --git a/homeassistant/components/squeezebox/translations/de.json b/homeassistant/components/squeezebox/translations/de.json index 295aeddfa86..a345aeedac7 100644 --- a/homeassistant/components/squeezebox/translations/de.json +++ b/homeassistant/components/squeezebox/translations/de.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Ger\u00e4t ist bereits konfiguriert", - "no_server_found": "Kein LMS-Server gefunden." + "no_server_found": "Kein LMS Server gefunden." }, "error": { "cannot_connect": "Verbindung fehlgeschlagen", diff --git a/homeassistant/components/squeezebox/translations/pt.json b/homeassistant/components/squeezebox/translations/pt.json index 86aa270e47d..3c18b68f84c 100644 --- a/homeassistant/components/squeezebox/translations/pt.json +++ b/homeassistant/components/squeezebox/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "no_server_found": "N\u00e3o foi poss\u00edvel descobrir automaticamente o servidor.", "unknown": "Erro inesperado" @@ -13,7 +13,7 @@ "step": { "edit": { "data": { - "host": "Servidor", + "host": "Endere\u00e7o", "password": "Palavra-passe", "port": "Porta", "username": "Utilizador" @@ -22,7 +22,7 @@ }, "user": { "data": { - "host": "Servidor" + "host": "Endere\u00e7o" } } } diff --git a/homeassistant/components/squeezebox/translations/sk.json b/homeassistant/components/squeezebox/translations/sk.json index 59f8b706503..1d98c564527 100644 --- a/homeassistant/components/squeezebox/translations/sk.json +++ b/homeassistant/components/squeezebox/translations/sk.json @@ -1,11 +1,13 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "no_server_found": "Nebol n\u00e1jden\u00fd \u017eiadny LMS server." }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie", + "no_server_found": "Server sa nepodarilo automaticky zisti\u0165.", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "flow_title": "{host}", diff --git a/homeassistant/components/srp_energy/sensor.py b/homeassistant/components/srp_energy/sensor.py index 61b9938e474..c4c7ec728e0 100644 --- a/homeassistant/components/srp_energy/sensor.py +++ b/homeassistant/components/srp_energy/sensor.py @@ -11,7 +11,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ENERGY_KILO_WATT_HOUR +from homeassistant.const import UnitOfEnergy from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed @@ -91,7 +91,7 @@ class SrpEntity(SensorEntity): self._name = SENSOR_NAME self.type = SENSOR_TYPE self.coordinator = coordinator - self._unit_of_measurement = ENERGY_KILO_WATT_HOUR + self._unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR self._state = None @property diff --git a/homeassistant/components/srp_energy/translations/pt.json b/homeassistant/components/srp_energy/translations/pt.json index 15c3b188dec..3e9b1704ec1 100644 --- a/homeassistant/components/srp_energy/translations/pt.json +++ b/homeassistant/components/srp_energy/translations/pt.json @@ -4,7 +4,7 @@ "single_instance_allowed": "J\u00e1 configurado. Apenas uma \u00fanica configura\u00e7\u00e3o \u00e9 poss\u00edvel." }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_account": "O ID da conta deve ser um n\u00famero de 9 d\u00edgitos", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" diff --git a/homeassistant/components/srp_energy/translations/sk.json b/homeassistant/components/srp_energy/translations/sk.json index 23ac0bfe739..9184f04b5f7 100644 --- a/homeassistant/components/srp_energy/translations/sk.json +++ b/homeassistant/components/srp_energy/translations/sk.json @@ -13,6 +13,7 @@ "user": { "data": { "id": "ID \u00fa\u010dtu", + "is_tou": "Je \u010das pou\u017e\u00edvania pl\u00e1nu", "password": "Heslo", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } diff --git a/homeassistant/components/ssdp/__init__.py b/homeassistant/components/ssdp/__init__.py index 8e037602b90..18ed063c8bc 100644 --- a/homeassistant/components/ssdp/__init__.py +++ b/homeassistant/components/ssdp/__init__.py @@ -424,7 +424,7 @@ class Scanner: source = fix_ipv6_address_scope_id(source) or source self._ssdp_listeners.append( SsdpListener( - async_callback=self._ssdp_listener_callback, + callback=self._ssdp_listener_callback, source=source, target=target, device_tracker=device_tracker, @@ -458,7 +458,7 @@ class Scanner: if _async_headers_match(combined_headers, lower_match_dict) ] - async def _ssdp_listener_callback( + def _ssdp_listener_callback( self, ssdp_device: SsdpDevice, dst: DeviceOrServiceType, @@ -469,15 +469,49 @@ class Scanner: "SSDP: ssdp_device: %s, dst: %s, source: %s", ssdp_device, dst, source ) + assert self._description_cache + location = ssdp_device.location - info_desc = None + _, info_desc = self._description_cache.peek_description_dict(location) + if info_desc is None: + # Fetch info desc in separate task and process from there. + self.hass.async_create_task( + self._ssdp_listener_process_with_lookup(ssdp_device, dst, source) + ) + return + + # Info desc known, process directly. + self._ssdp_listener_process(ssdp_device, dst, source, info_desc) + + async def _ssdp_listener_process_with_lookup( + self, + ssdp_device: SsdpDevice, + dst: DeviceOrServiceType, + source: SsdpSource, + ) -> None: + """Handle a device/service change.""" + location = ssdp_device.location + self._ssdp_listener_process( + ssdp_device, + dst, + source, + await self._async_get_description_dict(location), + ) + + def _ssdp_listener_process( + self, + ssdp_device: SsdpDevice, + dst: DeviceOrServiceType, + source: SsdpSource, + info_desc: Mapping[str, Any], + ) -> None: + """Handle a device/service change.""" + matching_domains: set[str] = set() combined_headers = ssdp_device.combined_headers(dst) callbacks = self._async_get_matching_callbacks(combined_headers) - matching_domains: set[str] = set() # If there are no changes from a search, do not trigger a config flow if source != SsdpSource.SEARCH_ALIVE: - info_desc = await self._async_get_description_dict(location) or {} matching_domains = self.integration_matchers.async_matching_domains( CaseInsensitiveDict(combined_headers.as_dict(), **info_desc) ) @@ -485,21 +519,24 @@ class Scanner: if not callbacks and not matching_domains: return - if info_desc is None: - info_desc = await self._async_get_description_dict(location) or {} discovery_info = discovery_info_from_headers_and_description( combined_headers, info_desc ) discovery_info.x_homeassistant_matching_domains = matching_domains - ssdp_change = SSDP_SOURCE_SSDP_CHANGE_MAPPING[source] - await _async_process_callbacks(callbacks, discovery_info, ssdp_change) + + if callbacks: + ssdp_change = SSDP_SOURCE_SSDP_CHANGE_MAPPING[source] + self.hass.async_create_task( + _async_process_callbacks(callbacks, discovery_info, ssdp_change) + ) # Config flows should only be created for alive/update messages from alive devices - if ssdp_change == SsdpChange.BYEBYE: + if source == SsdpSource.ADVERTISEMENT_BYEBYE: return _LOGGER.debug("Discovery info: %s", discovery_info) + location = ssdp_device.location for domain in matching_domains: _LOGGER.debug("Discovered %s at %s", domain, location) discovery_flow.async_create_flow( @@ -514,6 +551,13 @@ class Scanner: ) -> Mapping[str, str]: """Get description dict.""" assert self._description_cache is not None + + has_description, description = self._description_cache.peek_description_dict( + location + ) + if has_description: + return description or {} + return await self._description_cache.async_get_description_dict(location) or {} async def _async_headers_to_discovery_info( @@ -524,10 +568,9 @@ class Scanner: Building this is a bit expensive so we only do it on demand. """ assert self._description_cache is not None + location = headers["location"] - info_desc = ( - await self._description_cache.async_get_description_dict(location) or {} - ) + info_desc = await self._async_get_description_dict(location) return discovery_info_from_headers_and_description(headers, info_desc) async def async_get_discovery_info_by_udn_st( # pylint: disable=invalid-name @@ -663,11 +706,12 @@ async def _async_find_next_available_port(source: AddressTupleVXType) -> int: test_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) for port in range(UPNP_SERVER_MIN_PORT, UPNP_SERVER_MAX_PORT): + addr = (source[0],) + (port,) + source[2:] try: - test_socket.bind(source) + test_socket.bind(addr) return port except OSError: - if port == UPNP_SERVER_MAX_PORT: + if port == UPNP_SERVER_MAX_PORT - 1: raise raise RuntimeError("unreachable") diff --git a/homeassistant/components/ssdp/manifest.json b/homeassistant/components/ssdp/manifest.json index d532dc8f292..10f7a8e49a2 100644 --- a/homeassistant/components/ssdp/manifest.json +++ b/homeassistant/components/ssdp/manifest.json @@ -2,7 +2,7 @@ "domain": "ssdp", "name": "Simple Service Discovery Protocol (SSDP)", "documentation": "https://www.home-assistant.io/integrations/ssdp", - "requirements": ["async-upnp-client==0.32.3"], + "requirements": ["async-upnp-client==0.33.0"], "dependencies": ["network"], "after_dependencies": ["zeroconf"], "codeowners": [], diff --git a/homeassistant/components/starline/sensor.py b/homeassistant/components/starline/sensor.py index 588b9f93e08..1acddb27721 100644 --- a/homeassistant/components/starline/sensor.py +++ b/homeassistant/components/starline/sensor.py @@ -10,11 +10,11 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - ELECTRIC_POTENTIAL_VOLT, - LENGTH_KILOMETERS, PERCENTAGE, - TEMP_CELSIUS, - VOLUME_LITERS, + UnitOfElectricPotential, + UnitOfLength, + UnitOfTemperature, + UnitOfVolume, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -43,7 +43,8 @@ SENSOR_TYPES: tuple[StarlineSensorEntityDescription, ...] = ( StarlineSensorEntityDescription( key="battery", name_="Battery", - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + device_class=SensorDeviceClass.VOLTAGE, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, ), StarlineSensorEntityDescription( key="balance", @@ -54,13 +55,13 @@ SENSOR_TYPES: tuple[StarlineSensorEntityDescription, ...] = ( key="ctemp", name_="Interior Temperature", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, ), StarlineSensorEntityDescription( key="etemp", name_="Engine Temperature", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, ), StarlineSensorEntityDescription( key="gsm_lvl", @@ -80,7 +81,7 @@ SENSOR_TYPES: tuple[StarlineSensorEntityDescription, ...] = ( StarlineSensorEntityDescription( key="mileage", name_="Mileage", - native_unit_of_measurement=LENGTH_KILOMETERS, + native_unit_of_measurement=UnitOfLength.KILOMETERS, device_class=SensorDeviceClass.DISTANCE, icon="mdi:counter", ), @@ -160,7 +161,7 @@ class StarlineSensor(StarlineEntity, SensorEntity): if type_value == "percents": return PERCENTAGE if type_value == "litres": - return VOLUME_LITERS + return UnitOfVolume.LITERS return self.entity_description.native_unit_of_measurement @property diff --git a/homeassistant/components/starline/translations/sk.json b/homeassistant/components/starline/translations/sk.json index 79b2db36bb4..9dc300c6292 100644 --- a/homeassistant/components/starline/translations/sk.json +++ b/homeassistant/components/starline/translations/sk.json @@ -1,16 +1,23 @@ { "config": { "error": { + "error_auth_app": "Nespr\u00e1vne ID aplik\u00e1cie alebo Secret", "error_auth_mfa": "Nespr\u00e1vny k\u00f3d", "error_auth_user": "Nespr\u00e1vne u\u017e\u00edvate\u013esk\u00e9 meno alebo heslo" }, "step": { "auth_app": { "data": { - "app_id": "ID aplik\u00e1cie" - } + "app_id": "ID aplik\u00e1cie", + "app_secret": "Secret" + }, + "description": "ID aplik\u00e1cie a tajn\u00fd k\u00f3d z [\u00fa\u010dtu v\u00fdvoj\u00e1ra StarLine](https://my.starline.ru/developer)", + "title": "Prihlasovacie \u00fadaje aplik\u00e1cie" }, "auth_captcha": { + "data": { + "captcha_code": "K\u00f3d z obr\u00e1zku" + }, "description": "{captcha_img}", "title": "Captcha" }, @@ -25,7 +32,9 @@ "data": { "password": "Heslo", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" - } + }, + "description": "E-mail a heslo \u00fa\u010dtu StarLine", + "title": "Prihlasovacie \u00fadaje pou\u017e\u00edvate\u013ea" } } } diff --git a/homeassistant/components/startca/sensor.py b/homeassistant/components/startca/sensor.py index 28016a28d79..bf05b3a2210 100644 --- a/homeassistant/components/startca/sensor.py +++ b/homeassistant/components/startca/sensor.py @@ -12,6 +12,7 @@ import xmltodict from homeassistant.components.sensor import ( PLATFORM_SCHEMA, + SensorDeviceClass, SensorEntity, SensorEntityDescription, ) @@ -19,8 +20,8 @@ from homeassistant.const import ( CONF_API_KEY, CONF_MONITORED_VARIABLES, CONF_NAME, - DATA_GIGABYTES, PERCENTAGE, + UnitOfInformation, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.aiohttp_client import async_get_clientsession @@ -47,67 +48,78 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="usage_gb", name="Usage", - native_unit_of_measurement=DATA_GIGABYTES, + native_unit_of_measurement=UnitOfInformation.GIGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:download", ), SensorEntityDescription( key="limit", name="Data limit", - native_unit_of_measurement=DATA_GIGABYTES, + native_unit_of_measurement=UnitOfInformation.GIGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:download", ), SensorEntityDescription( key="used_download", name="Used Download", - native_unit_of_measurement=DATA_GIGABYTES, + native_unit_of_measurement=UnitOfInformation.GIGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:download", ), SensorEntityDescription( key="used_upload", name="Used Upload", - native_unit_of_measurement=DATA_GIGABYTES, + native_unit_of_measurement=UnitOfInformation.GIGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:upload", ), SensorEntityDescription( key="used_total", name="Used Total", - native_unit_of_measurement=DATA_GIGABYTES, + native_unit_of_measurement=UnitOfInformation.GIGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:download", ), SensorEntityDescription( key="grace_download", name="Grace Download", - native_unit_of_measurement=DATA_GIGABYTES, + native_unit_of_measurement=UnitOfInformation.GIGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:download", ), SensorEntityDescription( key="grace_upload", name="Grace Upload", - native_unit_of_measurement=DATA_GIGABYTES, + native_unit_of_measurement=UnitOfInformation.GIGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:upload", ), SensorEntityDescription( key="grace_total", name="Grace Total", - native_unit_of_measurement=DATA_GIGABYTES, + native_unit_of_measurement=UnitOfInformation.GIGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:download", ), SensorEntityDescription( key="total_download", name="Total Download", - native_unit_of_measurement=DATA_GIGABYTES, + native_unit_of_measurement=UnitOfInformation.GIGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:download", ), SensorEntityDescription( key="total_upload", name="Total Upload", - native_unit_of_measurement=DATA_GIGABYTES, + native_unit_of_measurement=UnitOfInformation.GIGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:download", ), SensorEntityDescription( key="used_remaining", name="Remaining", - native_unit_of_measurement=DATA_GIGABYTES, + native_unit_of_measurement=UnitOfInformation.GIGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:download", ), ) diff --git a/homeassistant/components/statistics/sensor.py b/homeassistant/components/statistics/sensor.py index 64e967604c1..38434c15a1c 100644 --- a/homeassistant/components/statistics/sensor.py +++ b/homeassistant/components/statistics/sensor.py @@ -191,9 +191,8 @@ def valid_state_characteristic_configuration(config: dict[str, Any]) -> dict[str not is_binary and characteristic not in STATS_NUMERIC_SUPPORT ): raise vol.ValueInvalid( - "The configured characteristic '{}' is not supported for the configured source sensor".format( - characteristic - ) + "The configured characteristic '{}' is not supported for the configured" + " source sensor".format(characteristic) ) return config diff --git a/homeassistant/components/statistics/translations/he.json b/homeassistant/components/statistics/translations/he.json index b5cb3b24754..1e84a1c8dda 100644 --- a/homeassistant/components/statistics/translations/he.json +++ b/homeassistant/components/statistics/translations/he.json @@ -1,7 +1,7 @@ { "issues": { "deprecation_warning_characteristic": { - "description": "\u05e4\u05e8\u05de\u05d8\u05e8 \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 `state_characteristic` \u05e9\u05dc \u05e9\u05d9\u05dc\u05d5\u05d1 \u05d4\u05e1\u05d8\u05d8\u05d9\u05e1\u05d8\u05d9\u05e7\u05d4 \u05d9\u05d4\u05e4\u05d5\u05da \u05dc\u05d7\u05d5\u05d1\u05d4.\n\n\u05d9\u05e9 \u05dc\u05d4\u05d5\u05e1\u05d9\u05e3 `state_characteristic: {characteristic}` \u05dc\u05ea\u05e6\u05d5\u05e8\u05d4 \u05e9\u05dc \u05d7\u05d9\u05d9\u05e9\u05df `{entity}` \u05db\u05d3\u05d9 \u05dc\u05e9\u05de\u05d5\u05e8 \u05e2\u05dc \u05d0\u05d5\u05e4\u05df \u05d4\u05e4\u05e2\u05d5\u05dc\u05d4 \u05d4\u05e0\u05d5\u05db\u05d7\u05d9.\n\n\u05d9\u05e9 \u05dc\u05e7\u05e8\u05d5\u05d0 \u05d0\u05ea \u05d4\u05ea\u05d9\u05e2\u05d5\u05d3 \u05e9\u05dc \u05e9\u05d9\u05dc\u05d5\u05d1 \u05d4\u05e1\u05d8\u05d8\u05d9\u05e1\u05d8\u05d9\u05e7\u05d4 \u05dc\u05e4\u05e8\u05d8\u05d9\u05dd \u05e0\u05d5\u05e1\u05e4\u05d9\u05dd: https://www.home-assistant.io/integrations/statistics/", + "description": "\u05e4\u05e8\u05de\u05d8\u05e8 \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 `state_characteristic` \u05e9\u05dc \u05e9\u05d9\u05dc\u05d5\u05d1 \u05d4\u05e1\u05d8\u05d8\u05d9\u05e1\u05d8\u05d9\u05e7\u05d4 \u05d9\u05d4\u05e4\u05d5\u05da \u05dc\u05d7\u05d5\u05d1\u05d4.\n\n\u05d9\u05e9 \u05dc\u05d4\u05d5\u05e1\u05d9\u05e3 `state_characteristic: {characteristic}` \u05dc\u05ea\u05e6\u05d5\u05e8\u05d4 \u05e9\u05dc \u05d7\u05d9\u05d9\u05e9\u05df `{entity}` \u05db\u05d3\u05d9 \u05dc\u05e9\u05de\u05d5\u05e8 \u05e2\u05dc \u05d0\u05d5\u05e4\u05df \u05d4\u05e4\u05e2\u05d5\u05dc\u05d4 \u05d4\u05e0\u05d5\u05db\u05d7\u05d9.\n\n\u05d9\u05e9 \u05dc\u05e7\u05e8\u05d5\u05d0 \u05d0\u05ea \u05d4\u05ea\u05d9\u05e2\u05d5\u05d3 \u05e9\u05dc \u05e9\u05d9\u05dc\u05d5\u05d1 \u05d4\u05e1\u05d8\u05d8\u05d9\u05e1\u05d8\u05d9\u05e7\u05d4 \u05dc\u05e4\u05e8\u05d8\u05d9\u05dd \u05e0\u05d5\u05e1\u05e4\u05d9\u05dd: https://www.home-assistant.io/integrations/statistics/", "title": "\u05d7\u05d5\u05d1\u05d4 'state_characteristic' \u05e9\u05d4\u05d5\u05e0\u05d7\u05d4 \u05e2\u05d1\u05d5\u05e8 \u05d9\u05e9\u05d5\u05ea \u05e1\u05d8\u05d8\u05d9\u05e1\u05d8\u05d9\u05e7\u05d4" }, "deprecation_warning_size": { diff --git a/homeassistant/components/statistics/translations/sk.json b/homeassistant/components/statistics/translations/sk.json new file mode 100644 index 00000000000..1d85cc34b81 --- /dev/null +++ b/homeassistant/components/statistics/translations/sk.json @@ -0,0 +1,12 @@ +{ + "issues": { + "deprecation_warning_characteristic": { + "description": "Konfigura\u010dn\u00fd parameter `state_characteristic` integr\u00e1cie \u0161tatist\u00edk sa stane povinn\u00fdm. \n\nAk chcete zachova\u0165 aktu\u00e1lne spr\u00e1vanie, pridajte `state_characteristic: {characteristic}` do konfigur\u00e1cie sn\u00edma\u010da `{entity}`. \n\n\u010eal\u0161ie podrobnosti n\u00e1jdete v dokument\u00e1cii integr\u00e1cie \u0161tatist\u00edk: https://www.home-assistant.io/integrations/statistics/", + "title": "Povinn\u00fd 'state_characteristic' predpokladan\u00fd pre entitu \u0161tatistiky" + }, + "deprecation_warning_size": { + "description": "Konfigura\u010dn\u00fd parameter `sampling_size` integr\u00e1cie \u0161tatist\u00edk bol doteraz predvolene nastaven\u00fd na hodnotu 20, ktor\u00e1 sa zmen\u00ed. \n\n Skontrolujte konfigur\u00e1ciu senzora `{entity}` a pridajte vhodn\u00e9 hranice, napr. `sampling_size: 20`, aby ste zachovali aktu\u00e1lne spr\u00e1vanie. Konfigur\u00e1cia integr\u00e1cie \u0161tatist\u00edk bude s verziou 2022.12.0 flexibilnej\u0161ia a bude akceptova\u0165 bu\u010f `sampling_size` alebo `max_age`, pr\u00edpadne obe nastavenia. Vy\u0161\u0161ie uveden\u00e1 po\u017eiadavka priprav\u00ed va\u0161u konfigur\u00e1ciu na t\u00fato inak z\u00e1sadn\u00fa zmenu. \n\n\u010eal\u0161ie podrobnosti n\u00e1jdete v dokument\u00e1cii integr\u00e1cie \u0161tatist\u00edk: https://www.home-assistant.io/integrations/statistics/", + "title": "Implicitn\u00e1 'sampling_size' predpokladan\u00e1 pre \u0161tatistick\u00fa entitu" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/steam_online/coordinator.py b/homeassistant/components/steam_online/coordinator.py index f210c449fa6..30178fa2b82 100644 --- a/homeassistant/components/steam_online/coordinator.py +++ b/homeassistant/components/steam_online/coordinator.py @@ -2,6 +2,7 @@ from __future__ import annotations from datetime import timedelta +from typing import Union import steam from steam.api import _interface_method as INTMethod @@ -15,7 +16,9 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda from .const import CONF_ACCOUNTS, DOMAIN, LOGGER -class SteamDataUpdateCoordinator(DataUpdateCoordinator): +class SteamDataUpdateCoordinator( + DataUpdateCoordinator[dict[str, dict[str, Union[str, int]]]] +): """Data update coordinator for the Steam integration.""" config_entry: ConfigEntry diff --git a/homeassistant/components/steam_online/sensor.py b/homeassistant/components/steam_online/sensor.py index 307dfac0542..70225a90a1c 100644 --- a/homeassistant/components/steam_online/sensor.py +++ b/homeassistant/components/steam_online/sensor.py @@ -3,6 +3,7 @@ from __future__ import annotations from datetime import datetime from time import localtime, mktime +from typing import Optional, cast from homeassistant.components.sensor import SensorEntity, SensorEntityDescription from homeassistant.config_entries import ConfigEntry @@ -56,17 +57,17 @@ class SteamSensor(SteamEntity, SensorEntity): """Return the state of the sensor.""" if self.entity_description.key in self.coordinator.data: player = self.coordinator.data[self.entity_description.key] - return STEAM_STATUSES[player["personastate"]] + return STEAM_STATUSES[cast(int, player["personastate"])] return None @property - def extra_state_attributes(self) -> dict[str, str | datetime]: + def extra_state_attributes(self) -> dict[str, str | int | datetime]: """Return the state attributes of the sensor.""" if self.entity_description.key not in self.coordinator.data: return {} player = self.coordinator.data[self.entity_description.key] - attrs: dict[str, str | datetime] = {} + attrs: dict[str, str | int | datetime] = {} if game := player.get("gameextrainfo"): attrs["game"] = game if game_id := player.get("gameid"): @@ -76,9 +77,9 @@ class SteamSensor(SteamEntity, SensorEntity): attrs["game_image_main"] = f"{game_url}{STEAM_MAIN_IMAGE_FILE}" if info := self._get_game_icon(player): attrs["game_icon"] = f"{STEAM_ICON_URL}{game_id}/{info}.jpg" - self._attr_name = player["personaname"] - self._attr_entity_picture = player["avatarmedium"] - if last_online := player.get("lastlogoff"): + self._attr_name = str(player["personaname"]) or None + self._attr_entity_picture = str(player["avatarmedium"]) or None + if last_online := cast(Optional[int], player.get("lastlogoff")): attrs["last_online"] = utc_from_timestamp(mktime(localtime(last_online))) if level := self.coordinator.data[self.entity_description.key]["level"]: attrs["level"] = level diff --git a/homeassistant/components/steam_online/translations/sk.json b/homeassistant/components/steam_online/translations/sk.json index ef684e5b4ca..906e4f66683 100644 --- a/homeassistant/components/steam_online/translations/sk.json +++ b/homeassistant/components/steam_online/translations/sk.json @@ -11,6 +11,10 @@ "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { + "reauth_confirm": { + "description": "Integr\u00e1ciu Steam je potrebn\u00e9 manu\u00e1lne znova overi\u0165 \n\n Svoj k\u013e\u00fa\u010d n\u00e1jdete tu: {api_key_url}", + "title": "Znova overi\u0165 integr\u00e1ciu" + }, "user": { "data": { "account": "ID \u00fa\u010dtu Steam", @@ -19,5 +23,23 @@ "description": "Na vyh\u013eadanie ID \u00fa\u010dtu Steam pou\u017eite {account_id_url}" } } + }, + "issues": { + "removed_yaml": { + "description": "Konfigur\u00e1cia Steamu pomocou YAML bola odstr\u00e1nen\u00e1. \n\n Asistent dom\u00e1cnosti nepou\u017e\u00edva va\u0161u existuj\u00facu konfigur\u00e1ciu YAML. \n\n Ak chcete tento probl\u00e9m vyrie\u0161i\u0165, odstr\u00e1\u0148te konfigur\u00e1ciu YAML zo s\u00faboru configuration.yaml a re\u0161tartujte aplik\u00e1ciu Home Assistant.", + "title": "Konfigur\u00e1cia Steam YAML bola odstr\u00e1nen\u00e1" + } + }, + "options": { + "error": { + "unauthorized": "Obmedzen\u00fd zoznam priate\u013eov: Pozrite si dokument\u00e1ciu o tom, ako zobrazi\u0165 v\u0161etk\u00fdch ostatn\u00fdch priate\u013eov" + }, + "step": { + "init": { + "data": { + "accounts": "N\u00e1zvy monitorovan\u00fdch \u00fa\u010dtov" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/steamist/sensor.py b/homeassistant/components/steamist/sensor.py index 0f9008e63dd..17cc0e8c272 100644 --- a/homeassistant/components/steamist/sensor.py +++ b/homeassistant/components/steamist/sensor.py @@ -13,7 +13,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT, TIME_MINUTES +from homeassistant.const import UnitOfTemperature, UnitOfTime from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -25,8 +25,8 @@ _KEY_MINUTES_REMAIN = "minutes_remain" _KEY_TEMP = "temp" UNIT_MAPPINGS = { - "C": TEMP_CELSIUS, - "F": TEMP_FAHRENHEIT, + "C": UnitOfTemperature.CELSIUS, + "F": UnitOfTemperature.FAHRENHEIT, } @@ -48,7 +48,7 @@ SENSORS: tuple[SteamistSensorEntityDescription, ...] = ( SteamistSensorEntityDescription( key=_KEY_MINUTES_REMAIN, name="Steam Minutes Remain", - native_unit_of_measurement=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, value_fn=lambda status: status.minutes_remain, ), SteamistSensorEntityDescription( diff --git a/homeassistant/components/steamist/strings.json b/homeassistant/components/steamist/strings.json index 7ad5913e718..a3cd4879c6a 100644 --- a/homeassistant/components/steamist/strings.json +++ b/homeassistant/components/steamist/strings.json @@ -14,7 +14,7 @@ } }, "discovery_confirm": { - "description": "Do you want to setup {name} ({ipaddress})?" + "description": "Do you want to set up {name} ({ipaddress})?" } }, "error": { diff --git a/homeassistant/components/steamist/translations/en.json b/homeassistant/components/steamist/translations/en.json index 9b2dfd791bf..77e01937781 100644 --- a/homeassistant/components/steamist/translations/en.json +++ b/homeassistant/components/steamist/translations/en.json @@ -14,7 +14,7 @@ "flow_title": "{name} ({ipaddress})", "step": { "discovery_confirm": { - "description": "Do you want to setup {name} ({ipaddress})?" + "description": "Do you want to set up {name} ({ipaddress})?" }, "pick_device": { "data": { diff --git a/homeassistant/components/steamist/translations/ko.json b/homeassistant/components/steamist/translations/ko.json new file mode 100644 index 00000000000..9d264213a17 --- /dev/null +++ b/homeassistant/components/steamist/translations/ko.json @@ -0,0 +1,21 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/steamist/translations/no.json b/homeassistant/components/steamist/translations/no.json index 2d96a527ce6..e054553cd94 100644 --- a/homeassistant/components/steamist/translations/no.json +++ b/homeassistant/components/steamist/translations/no.json @@ -14,7 +14,7 @@ "flow_title": "{name} ( {ipaddress} )", "step": { "discovery_confirm": { - "description": "Vil du konfigurere {name} ( {ipaddress} )?" + "description": "Vil du sette opp {name} ( {ipaddress} )?" }, "pick_device": { "data": { diff --git a/homeassistant/components/steamist/translations/pt-BR.json b/homeassistant/components/steamist/translations/pt-BR.json index b099c4b36f7..bf03c2c0e6c 100644 --- a/homeassistant/components/steamist/translations/pt-BR.json +++ b/homeassistant/components/steamist/translations/pt-BR.json @@ -14,7 +14,7 @@ "flow_title": "{name} ({ipaddress})", "step": { "discovery_confirm": { - "description": "Deseja configurar {name} ( {ipaddress} )?" + "description": "Deseja configurar {name} ({ipaddress})?" }, "pick_device": { "data": { diff --git a/homeassistant/components/steamist/translations/pt.json b/homeassistant/components/steamist/translations/pt.json index 6ead5b65917..e30999a7749 100644 --- a/homeassistant/components/steamist/translations/pt.json +++ b/homeassistant/components/steamist/translations/pt.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "error": { "unknown": "Erro inesperado" @@ -9,7 +9,7 @@ "step": { "user": { "data": { - "host": "Servidor" + "host": "Endere\u00e7o" } } } diff --git a/homeassistant/components/steamist/translations/sk.json b/homeassistant/components/steamist/translations/sk.json index 65a40c229d6..391f2b06d7d 100644 --- a/homeassistant/components/steamist/translations/sk.json +++ b/homeassistant/components/steamist/translations/sk.json @@ -4,10 +4,12 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", "cannot_connect": "Nepodarilo sa pripoji\u0165", - "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" + "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", + "not_steamist_device": "Nie steamist zariadenie" }, "error": { - "cannot_connect": "Nepodarilo sa pripoji\u0165" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "flow_title": "{name} ({ipaddress})", "step": { @@ -22,7 +24,8 @@ "user": { "data": { "host": "Hostite\u013e" - } + }, + "description": "Ak nech\u00e1te hostite\u013ea pr\u00e1zdneho, na vyh\u013ead\u00e1vanie zariaden\u00ed sa pou\u017eije zis\u0165ovanie." } } } diff --git a/homeassistant/components/stiebel_eltron/climate.py b/homeassistant/components/stiebel_eltron/climate.py index 25c24c50ece..88cce6c52d7 100644 --- a/homeassistant/components/stiebel_eltron/climate.py +++ b/homeassistant/components/stiebel_eltron/climate.py @@ -10,7 +10,7 @@ from homeassistant.components.climate import ( ClimateEntityFeature, HVACMode, ) -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType @@ -75,7 +75,7 @@ class StiebelEltron(ClimateEntity): _attr_supported_features = ( ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE ) - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS def __init__(self, name, ste_data): """Initialize the unit.""" diff --git a/homeassistant/components/stookalert/translations/ko.json b/homeassistant/components/stookalert/translations/ko.json new file mode 100644 index 00000000000..e1300423811 --- /dev/null +++ b/homeassistant/components/stookalert/translations/ko.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "\uc11c\ube44\uc2a4\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/stookalert/translations/sk.json b/homeassistant/components/stookalert/translations/sk.json index f04d4a327f4..4f050d06763 100644 --- a/homeassistant/components/stookalert/translations/sk.json +++ b/homeassistant/components/stookalert/translations/sk.json @@ -2,6 +2,13 @@ "config": { "abort": { "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1" + }, + "step": { + "user": { + "data": { + "province": "Provincia" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/stream/recorder.py b/homeassistant/components/stream/recorder.py index fffbd489757..258457a3d82 100644 --- a/homeassistant/components/stream/recorder.py +++ b/homeassistant/components/stream/recorder.py @@ -180,7 +180,10 @@ class RecorderOutput(StreamOutput): write_transform_matrix_and_rename(video_path) except FileNotFoundError: _LOGGER.error( - "Error writing to '%s'. There are likely multiple recordings writing to the same file", + ( + "Error writing to '%s'. There are likely multiple recordings" + " writing to the same file" + ), video_path, ) diff --git a/homeassistant/components/stream/worker.py b/homeassistant/components/stream/worker.py index aa49a6de7ae..308340f74a6 100644 --- a/homeassistant/components/stream/worker.py +++ b/homeassistant/components/stream/worker.py @@ -442,7 +442,8 @@ class TimestampValidator: gap = packet.time_base * (prev_dts - packet.dts) if gap > MAX_TIMESTAMP_GAP: raise StreamWorkerError( - f"Timestamp overflow detected: last dts = {prev_dts}, dts = {packet.dts}" + f"Timestamp overflow detected: last dts = {prev_dts}, dts =" + f" {packet.dts}" ) return False self._last_dts[packet.stream] = packet.dts @@ -496,7 +497,8 @@ def stream_worker( container = av.open(source, options=pyav_options, timeout=SOURCE_TIMEOUT) except av.AVError as err: raise StreamWorkerError( - f"Error opening stream ({err.type}, {err.strerror}) {redact_credentials(str(source))}" + f"Error opening stream ({err.type}, {err.strerror})" + f" {redact_credentials(str(source))}" ) from err try: video_stream = container.streams.video[0] diff --git a/homeassistant/components/streamlabswater/sensor.py b/homeassistant/components/streamlabswater/sensor.py index 1a1070d1ea8..42cf2bb588f 100644 --- a/homeassistant/components/streamlabswater/sensor.py +++ b/homeassistant/components/streamlabswater/sensor.py @@ -4,7 +4,7 @@ from __future__ import annotations from datetime import timedelta from homeassistant.components.sensor import SensorDeviceClass, SensorEntity -from homeassistant.const import VOLUME_GALLONS +from homeassistant.const import UnitOfVolume from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType @@ -80,7 +80,8 @@ class StreamlabsUsageData: class StreamLabsDailyUsage(SensorEntity): """Monitors the daily water usage.""" - _attr_device_class = SensorDeviceClass.VOLUME + _attr_device_class = SensorDeviceClass.WATER + _attr_native_unit_of_measurement = UnitOfVolume.GALLONS def __init__(self, location_name, streamlabs_usage_data): """Initialize the daily water usage device.""" @@ -89,25 +90,15 @@ class StreamLabsDailyUsage(SensorEntity): self._state = None @property - def name(self): + def name(self) -> str: """Return the name for daily usage.""" return f"{self._location_name} {NAME_DAILY_USAGE}" - @property - def icon(self): - """Return the daily usage icon.""" - return WATER_ICON - @property def native_value(self): """Return the current daily usage.""" return self._streamlabs_usage_data.get_daily_usage() - @property - def native_unit_of_measurement(self): - """Return gallons as the unit measurement for water.""" - return VOLUME_GALLONS - def update(self) -> None: """Retrieve the latest daily usage.""" self._streamlabs_usage_data.update() @@ -117,7 +108,7 @@ class StreamLabsMonthlyUsage(StreamLabsDailyUsage): """Monitors the monthly water usage.""" @property - def name(self): + def name(self) -> str: """Return the name for monthly usage.""" return f"{self._location_name} {NAME_MONTHLY_USAGE}" @@ -131,7 +122,7 @@ class StreamLabsYearlyUsage(StreamLabsDailyUsage): """Monitors the yearly water usage.""" @property - def name(self): + def name(self) -> str: """Return the name for yearly usage.""" return f"{self._location_name} {NAME_YEARLY_USAGE}" diff --git a/homeassistant/components/subaru/sensor.py b/homeassistant/components/subaru/sensor.py index 9db2d329210..046c3b4393e 100644 --- a/homeassistant/components/subaru/sensor.py +++ b/homeassistant/components/subaru/sensor.py @@ -13,14 +13,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - LENGTH_KILOMETERS, - LENGTH_MILES, - PERCENTAGE, - PRESSURE_HPA, - VOLUME_GALLONS, - VOLUME_LITERS, -) +from homeassistant.const import PERCENTAGE, UnitOfLength, UnitOfPressure, UnitOfVolume from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import entity_registry as er from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -55,8 +48,8 @@ _LOGGER = logging.getLogger(__name__) FUEL_CONSUMPTION_LITERS_PER_HUNDRED_KILOMETERS = "L/100km" FUEL_CONSUMPTION_MILES_PER_GALLON = "mi/gal" -L_PER_GAL = VolumeConverter.convert(1, VOLUME_GALLONS, VOLUME_LITERS) -KM_PER_MI = DistanceConverter.convert(1, LENGTH_MILES, LENGTH_KILOMETERS) +L_PER_GAL = VolumeConverter.convert(1, UnitOfVolume.GALLONS, UnitOfVolume.LITERS) +KM_PER_MI = DistanceConverter.convert(1, UnitOfLength.MILES, UnitOfLength.KILOMETERS) # Sensor available to "Subaru Safety Plus" subscribers with Gen1 or Gen2 vehicles SAFETY_SENSORS = [ @@ -65,7 +58,7 @@ SAFETY_SENSORS = [ device_class=SensorDeviceClass.DISTANCE, icon="mdi:road-variant", name="Odometer", - native_unit_of_measurement=LENGTH_KILOMETERS, + native_unit_of_measurement=UnitOfLength.KILOMETERS, state_class=SensorStateClass.TOTAL_INCREASING, ), ] @@ -84,35 +77,35 @@ API_GEN_2_SENSORS = [ device_class=SensorDeviceClass.DISTANCE, icon="mdi:gas-station", name="Range", - native_unit_of_measurement=LENGTH_KILOMETERS, + native_unit_of_measurement=UnitOfLength.KILOMETERS, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=sc.TIRE_PRESSURE_FL, device_class=SensorDeviceClass.PRESSURE, name="Tire pressure FL", - native_unit_of_measurement=PRESSURE_HPA, + native_unit_of_measurement=UnitOfPressure.HPA, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=sc.TIRE_PRESSURE_FR, device_class=SensorDeviceClass.PRESSURE, name="Tire pressure FR", - native_unit_of_measurement=PRESSURE_HPA, + native_unit_of_measurement=UnitOfPressure.HPA, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=sc.TIRE_PRESSURE_RL, device_class=SensorDeviceClass.PRESSURE, name="Tire pressure RL", - native_unit_of_measurement=PRESSURE_HPA, + native_unit_of_measurement=UnitOfPressure.HPA, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key=sc.TIRE_PRESSURE_RR, device_class=SensorDeviceClass.PRESSURE, name="Tire pressure RR", - native_unit_of_measurement=PRESSURE_HPA, + native_unit_of_measurement=UnitOfPressure.HPA, state_class=SensorStateClass.MEASUREMENT, ), ] @@ -124,7 +117,7 @@ EV_SENSORS = [ device_class=SensorDeviceClass.DISTANCE, icon="mdi:ev-station", name="EV range", - native_unit_of_measurement=LENGTH_MILES, + native_unit_of_measurement=UnitOfLength.MILES, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( diff --git a/homeassistant/components/subaru/translations/pt.json b/homeassistant/components/subaru/translations/pt.json index b5563108ab3..2e3748caaf4 100644 --- a/homeassistant/components/subaru/translations/pt.json +++ b/homeassistant/components/subaru/translations/pt.json @@ -2,10 +2,10 @@ "config": { "abort": { "already_configured": "Conta j\u00e1 configurada", - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida" }, "step": { diff --git a/homeassistant/components/subaru/translations/sk.json b/homeassistant/components/subaru/translations/sk.json index d3a5c8afd3a..09015a0901e 100644 --- a/homeassistant/components/subaru/translations/sk.json +++ b/homeassistant/components/subaru/translations/sk.json @@ -10,29 +10,50 @@ "cannot_connect": "Nepodarilo sa pripoji\u0165", "incorrect_pin": "Nespr\u00e1vny PIN", "incorrect_validation_code": "Nespr\u00e1vny overovac\u00ed k\u00f3d", - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "two_factor_request_failed": "\u017diados\u0165 o k\u00f3d 2FA zlyhala, sk\u00faste to znova" }, "step": { "pin": { "data": { "pin": "PIN" - } + }, + "description": "Zadajte svoj PIN MySubaru\n POZN\u00c1MKA: V\u0161etky vozidl\u00e1 na \u00fa\u010dte musia ma\u0165 rovnak\u00fd PIN", + "title": "Konfigur\u00e1cia Subaru Starlink" }, "two_factor": { - "description": "Vy\u017eaduje sa dvojfaktorov\u00e9 overenie" + "data": { + "contact_method": "Vyberte sp\u00f4sob kontaktovania:" + }, + "description": "Vy\u017eaduje sa dvojfaktorov\u00e9 overenie", + "title": "Konfigur\u00e1cia Subaru Starlink" }, "two_factor_validate": { "data": { "validation_code": "Overovac\u00ed k\u00f3d" }, - "description": "Zadajte prijat\u00fd overovac\u00ed k\u00f3d" + "description": "Zadajte prijat\u00fd overovac\u00ed k\u00f3d", + "title": "Konfigur\u00e1cia Subaru Starlink" }, "user": { "data": { "country": "Vyberte krajinu", "password": "Heslo", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" - } + }, + "description": "Zadajte svoje poverenia MySubaru\n POZN\u00c1MKA: \u00davodn\u00e9 nastavenie m\u00f4\u017ee trva\u0165 a\u017e 30 sek\u00fand", + "title": "Konfigur\u00e1cia Subaru Starlink" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "update_enabled": "Aktiv\u00e1cia dotazu na vozidlo" + }, + "description": "Ke\u010f je aktivovan\u00fd, polling vozidla odo\u0161le do v\u00e1\u0161ho vozidla ka\u017ed\u00e9 2 hodiny dia\u013ekov\u00fd pr\u00edkaz na z\u00edskanie nov\u00fdch \u00fadajov senzora. Bez dotazovania vozidla sa nov\u00e9 \u00fadaje zo sn\u00edma\u010da prijm\u00fa len vtedy, ke\u010f vozidlo automaticky odo\u0161le \u00fadaje (zvy\u010dajne po vypnut\u00ed motora).", + "title": "Mo\u017enosti Subaru Starlink" } } } diff --git a/homeassistant/components/suez_water/sensor.py b/homeassistant/components/suez_water/sensor.py index 4c0fe16a197..43075276be6 100644 --- a/homeassistant/components/suez_water/sensor.py +++ b/homeassistant/components/suez_water/sensor.py @@ -13,7 +13,7 @@ from homeassistant.components.sensor import ( SensorDeviceClass, SensorEntity, ) -from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, VOLUME_LITERS +from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, UnitOfVolume from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -25,9 +25,6 @@ SCAN_INTERVAL = timedelta(hours=12) CONF_COUNTER_ID = "counter_id" -NAME = "Suez Water Client" -ICON = "mdi:water-pump" - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( { vol.Required(CONF_USERNAME): cv.string, @@ -64,67 +61,55 @@ def setup_platform( class SuezSensor(SensorEntity): """Representation of a Sensor.""" - _attr_name = NAME - _attr_icon = ICON - _attr_native_unit_of_measurement = VOLUME_LITERS - _attr_device_class = SensorDeviceClass.VOLUME + _attr_name = "Suez Water Client" + _attr_icon = "mdi:water-pump" + _attr_native_unit_of_measurement = UnitOfVolume.LITERS + _attr_device_class = SensorDeviceClass.WATER - def __init__(self, client): + def __init__(self, client: SuezClient) -> None: """Initialize the data object.""" - self._attributes = {} - self._state = None - self._available = None self.client = client - - @property - def native_value(self): - """Return the state of the sensor.""" - return self._state - - @property - def extra_state_attributes(self): - """Return the state attributes.""" - return self._attributes + self._attr_extra_state_attributes = {} def _fetch_data(self): """Fetch latest data from Suez.""" try: self.client.update() # _state holds the volume of consumed water during previous day - self._state = self.client.state - self._available = True + self._attr_native_value = self.client.state + self._attr_available = True self._attr_attribution = self.client.attributes["attribution"] - self._attributes["this_month_consumption"] = {} + self._attr_extra_state_attributes["this_month_consumption"] = {} for item in self.client.attributes["thisMonthConsumption"]: - self._attributes["this_month_consumption"][ + self._attr_extra_state_attributes["this_month_consumption"][ item ] = self.client.attributes["thisMonthConsumption"][item] - self._attributes["previous_month_consumption"] = {} + self._attr_extra_state_attributes["previous_month_consumption"] = {} for item in self.client.attributes["previousMonthConsumption"]: - self._attributes["previous_month_consumption"][ + self._attr_extra_state_attributes["previous_month_consumption"][ item ] = self.client.attributes["previousMonthConsumption"][item] - self._attributes["highest_monthly_consumption"] = self.client.attributes[ - "highestMonthlyConsumption" - ] - self._attributes["last_year_overall"] = self.client.attributes[ - "lastYearOverAll" - ] - self._attributes["this_year_overall"] = self.client.attributes[ - "thisYearOverAll" - ] - self._attributes["history"] = {} + self._attr_extra_state_attributes[ + "highest_monthly_consumption" + ] = self.client.attributes["highestMonthlyConsumption"] + self._attr_extra_state_attributes[ + "last_year_overall" + ] = self.client.attributes["lastYearOverAll"] + self._attr_extra_state_attributes[ + "this_year_overall" + ] = self.client.attributes["thisYearOverAll"] + self._attr_extra_state_attributes["history"] = {} for item in self.client.attributes["history"]: - self._attributes["history"][item] = self.client.attributes["history"][ + self._attr_extra_state_attributes["history"][ item - ] + ] = self.client.attributes["history"][item] except PySuezError: - self._available = False + self._attr_available = False _LOGGER.warning("Unable to fetch data") def update(self) -> None: - """Return the latest collected data from Linky.""" + """Return the latest collected data from Suez.""" self._fetch_data() - _LOGGER.debug("Suez data state is: %s", self._state) + _LOGGER.debug("Suez data state is: %s", self.native_value) diff --git a/homeassistant/components/sun/translations/en.json b/homeassistant/components/sun/translations/en.json index 367fdab917c..4c0378cc7f0 100644 --- a/homeassistant/components/sun/translations/en.json +++ b/homeassistant/components/sun/translations/en.json @@ -5,7 +5,7 @@ }, "step": { "user": { - "description": "Do you want to start set up?" + "description": "Do you want to start setup?" } } }, diff --git a/homeassistant/components/sun/translations/it.json b/homeassistant/components/sun/translations/it.json index 48f0e4a8d90..c596b49bf60 100644 --- a/homeassistant/components/sun/translations/it.json +++ b/homeassistant/components/sun/translations/it.json @@ -5,7 +5,7 @@ }, "step": { "user": { - "description": "Vuoi iniziare la configurazione?" + "description": "Vuoi avviare la configurazione?" } } }, diff --git a/homeassistant/components/sun/translations/ko.json b/homeassistant/components/sun/translations/ko.json index f918b879acf..18340a933f5 100644 --- a/homeassistant/components/sun/translations/ko.json +++ b/homeassistant/components/sun/translations/ko.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." + }, "step": { "user": { "description": "\uc124\uc815\uc744 \uc2dc\uc791\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" diff --git a/homeassistant/components/sun/translations/pt.json b/homeassistant/components/sun/translations/pt.json index d050eb7c5f5..7f28db216eb 100644 --- a/homeassistant/components/sun/translations/pt.json +++ b/homeassistant/components/sun/translations/pt.json @@ -2,7 +2,7 @@ "config": { "step": { "user": { - "description": "Quer dar inicio \u00e0 configura\u00e7\u00e3o?" + "description": "Quer dar in\u00edcio \u00e0 configura\u00e7\u00e3o?" } } }, diff --git a/homeassistant/components/surepetcare/__init__.py b/homeassistant/components/surepetcare/__init__.py index 2e6125bc502..ec5d9f63920 100644 --- a/homeassistant/components/surepetcare/__init__.py +++ b/homeassistant/components/surepetcare/__init__.py @@ -159,7 +159,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return unload_ok -class SurePetcareDataCoordinator(DataUpdateCoordinator): +class SurePetcareDataCoordinator(DataUpdateCoordinator[dict[int, SurepyEntity]]): """Handle Surepetcare data.""" def __init__(self, entry: ConfigEntry, hass: HomeAssistant) -> None: diff --git a/homeassistant/components/surepetcare/entity.py b/homeassistant/components/surepetcare/entity.py index 301479c4b95..75d7f4e1c30 100644 --- a/homeassistant/components/surepetcare/entity.py +++ b/homeassistant/components/surepetcare/entity.py @@ -26,7 +26,7 @@ class SurePetcareEntity(CoordinatorEntity[SurePetcareDataCoordinator]): self._id = surepetcare_id - surepy_entity: SurepyEntity = coordinator.data[surepetcare_id] + surepy_entity = coordinator.data[surepetcare_id] if surepy_entity.name: self._device_name = surepy_entity.name.capitalize() diff --git a/homeassistant/components/surepetcare/sensor.py b/homeassistant/components/surepetcare/sensor.py index 1ae22710060..534f2396751 100644 --- a/homeassistant/components/surepetcare/sensor.py +++ b/homeassistant/components/surepetcare/sensor.py @@ -9,7 +9,7 @@ from surepy.enums import EntityType from homeassistant.components.sensor import SensorDeviceClass, SensorEntity from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_VOLTAGE, PERCENTAGE, VOLUME_MILLILITERS +from homeassistant.const import ATTR_VOLTAGE, PERCENTAGE, UnitOfVolume from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -88,7 +88,7 @@ class Felaqua(SurePetcareEntity, SensorEntity): """Sure Petcare Felaqua.""" _attr_device_class = SensorDeviceClass.VOLUME - _attr_native_unit_of_measurement = VOLUME_MILLILITERS + _attr_native_unit_of_measurement = UnitOfVolume.MILLILITERS def __init__( self, surepetcare_id: int, coordinator: SurePetcareDataCoordinator @@ -96,7 +96,7 @@ class Felaqua(SurePetcareEntity, SensorEntity): """Initialize a Sure Petcare Felaqua sensor.""" super().__init__(surepetcare_id, coordinator) - surepy_entity: SurepyFelaqua = coordinator.data[surepetcare_id] + surepy_entity = cast(SurepyFelaqua, coordinator.data[surepetcare_id]) self._attr_name = self._device_name self._attr_unique_id = self._device_id diff --git a/homeassistant/components/surepetcare/translations/ko.json b/homeassistant/components/surepetcare/translations/ko.json new file mode 100644 index 00000000000..dd9aa05261e --- /dev/null +++ b/homeassistant/components/surepetcare/translations/ko.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/surepetcare/translations/pt.json b/homeassistant/components/surepetcare/translations/pt.json index 565b9f6c0e8..ae106dbda05 100644 --- a/homeassistant/components/surepetcare/translations/pt.json +++ b/homeassistant/components/surepetcare/translations/pt.json @@ -1,7 +1,7 @@ { "config": { "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, diff --git a/homeassistant/components/switch/__init__.py b/homeassistant/components/switch/__init__.py index 7387685187a..6eb2a275e18 100644 --- a/homeassistant/components/switch/__init__.py +++ b/homeassistant/components/switch/__init__.py @@ -93,17 +93,17 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: class SwitchEntityDescription(ToggleEntityDescription): """A class that describes switch entities.""" - device_class: SwitchDeviceClass | str | None = None + device_class: SwitchDeviceClass | None = None class SwitchEntity(ToggleEntity): """Base class for switch entities.""" entity_description: SwitchEntityDescription - _attr_device_class: SwitchDeviceClass | str | None + _attr_device_class: SwitchDeviceClass | None @property - def device_class(self) -> SwitchDeviceClass | str | None: + def device_class(self) -> SwitchDeviceClass | None: """Return the class of this entity.""" if hasattr(self, "_attr_device_class"): return self._attr_device_class diff --git a/homeassistant/components/switch/translations/sk.json b/homeassistant/components/switch/translations/sk.json index 297ea3159d0..b818661ddc0 100644 --- a/homeassistant/components/switch/translations/sk.json +++ b/homeassistant/components/switch/translations/sk.json @@ -1,9 +1,14 @@ { "device_automation": { "action_type": { + "toggle": "Prepn\u00fa\u0165 {entity_name}", "turn_off": "Vypn\u00fa\u0165 {entity_name}", "turn_on": "Zapn\u00fa\u0165 {entity_name}" }, + "condition_type": { + "is_off": "{entity_name} je vypnut\u00e9", + "is_on": "{entity_name} je zapnut\u00e9" + }, "trigger_type": { "changed_states": "{entity_name} zapnut\u00e9 alebo vypnut\u00e9", "turned_off": "{entity_name} vypnut\u00e1", diff --git a/homeassistant/components/switch_as_x/translations/sk.json b/homeassistant/components/switch_as_x/translations/sk.json index 64f2700a3fb..f2022c2217e 100644 --- a/homeassistant/components/switch_as_x/translations/sk.json +++ b/homeassistant/components/switch_as_x/translations/sk.json @@ -5,8 +5,10 @@ "data": { "entity_id": "Prep\u00edna\u010d", "target_domain": "Nov\u00fd typ" - } + }, + "description": "Vyberte prep\u00edna\u010d, ktor\u00fd chcete zobrazi\u0165 v aplik\u00e1cii Home Assistant ako svetlo, kryt alebo \u010doko\u013evek in\u00e9. P\u00f4vodn\u00fd sp\u00edna\u010d bude skryt\u00fd." } } - } + }, + "title": "Zmena typu zariadenia prep\u00edna\u010da" } \ No newline at end of file diff --git a/homeassistant/components/switchbee/__init__.py b/homeassistant/components/switchbee/__init__.py index 5848477ec71..79b2e449a14 100644 --- a/homeassistant/components/switchbee/__init__.py +++ b/homeassistant/components/switchbee/__init__.py @@ -2,7 +2,8 @@ from __future__ import annotations -from switchbee.api import CentralUnitAPI, SwitchBeeError +from switchbee.api.central_unit import SwitchBeeError +from switchbee.api.polling import CentralUnitPolling from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME, Platform @@ -29,7 +30,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: user = entry.data[CONF_USERNAME] password = entry.data[CONF_PASSWORD] websession = async_get_clientsession(hass, verify_ssl=False) - api = CentralUnitAPI(central_unit, user, password, websession) + api = CentralUnitPolling(central_unit, user, password, websession) try: await api.connect() except SwitchBeeError as exp: diff --git a/homeassistant/components/switchbee/button.py b/homeassistant/components/switchbee/button.py index 175aa3af26e..39be264992e 100644 --- a/homeassistant/components/switchbee/button.py +++ b/homeassistant/components/switchbee/button.py @@ -1,6 +1,6 @@ """Support for SwitchBee scenario button.""" -from switchbee.api import SwitchBeeError +from switchbee.api.central_unit import SwitchBeeError from switchbee.device import ApiStateCommand, DeviceType from homeassistant.components.button import ButtonEntity diff --git a/homeassistant/components/switchbee/climate.py b/homeassistant/components/switchbee/climate.py index 8d0024b75ed..efc9c25d4bd 100644 --- a/homeassistant/components/switchbee/climate.py +++ b/homeassistant/components/switchbee/climate.py @@ -3,7 +3,7 @@ from __future__ import annotations from typing import Any -from switchbee.api import SwitchBeeDeviceOfflineError, SwitchBeeError +from switchbee.api.central_unit import SwitchBeeDeviceOfflineError, SwitchBeeError from switchbee.const import ( ApiAttribute, ThermostatFanSpeed, @@ -23,7 +23,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS, TEMP_FAHRENHEIT +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -65,8 +65,8 @@ HVAC_ACTION_SB_TO_HASS = { } HVAC_UNIT_SB_TO_HASS = { - ThermostatTemperatureUnit.CELSIUS: TEMP_CELSIUS, - ThermostatTemperatureUnit.FAHRENHEIT: TEMP_FAHRENHEIT, + ThermostatTemperatureUnit.CELSIUS: UnitOfTemperature.CELSIUS, + ThermostatTemperatureUnit.FAHRENHEIT: UnitOfTemperature.FAHRENHEIT, } SUPPORTED_FAN_MODES = [FAN_AUTO, FAN_HIGH, FAN_MEDIUM, FAN_LOW] diff --git a/homeassistant/components/switchbee/config_flow.py b/homeassistant/components/switchbee/config_flow.py index 967af101360..cb9cc27c1d0 100644 --- a/homeassistant/components/switchbee/config_flow.py +++ b/homeassistant/components/switchbee/config_flow.py @@ -4,7 +4,8 @@ from __future__ import annotations import logging from typing import Any -from switchbee.api import CentralUnitAPI, SwitchBeeError +from switchbee.api.central_unit import SwitchBeeError +from switchbee.api.polling import CentralUnitPolling import voluptuous as vol from homeassistant import config_entries @@ -33,7 +34,7 @@ async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> str: """Validate the user input allows us to connect.""" websession = async_get_clientsession(hass, verify_ssl=False) - api = CentralUnitAPI( + api = CentralUnitPolling( data[CONF_HOST], data[CONF_USERNAME], data[CONF_PASSWORD], websession ) try: diff --git a/homeassistant/components/switchbee/coordinator.py b/homeassistant/components/switchbee/coordinator.py index 3dee30bac0e..e8453afdbb8 100644 --- a/homeassistant/components/switchbee/coordinator.py +++ b/homeassistant/components/switchbee/coordinator.py @@ -6,7 +6,8 @@ from collections.abc import Mapping from datetime import timedelta import logging -from switchbee.api import CentralUnitAPI, SwitchBeeError +from switchbee.api.central_unit import SwitchBeeError +from switchbee.api.polling import CentralUnitPolling from switchbee.device import DeviceType, SwitchBeeBaseDevice from homeassistant.core import HomeAssistant @@ -24,10 +25,10 @@ class SwitchBeeCoordinator(DataUpdateCoordinator[Mapping[int, SwitchBeeBaseDevic def __init__( self, hass: HomeAssistant, - swb_api: CentralUnitAPI, + swb_api: CentralUnitPolling, ) -> None: """Initialize.""" - self.api: CentralUnitAPI = swb_api + self.api: CentralUnitPolling = swb_api self._reconnect_counts: int = 0 self.mac_formatted: str | None = ( None if self.api.mac is None else format_mac(self.api.mac) diff --git a/homeassistant/components/switchbee/cover.py b/homeassistant/components/switchbee/cover.py index ea5494f7f5b..a9eed00801a 100644 --- a/homeassistant/components/switchbee/cover.py +++ b/homeassistant/components/switchbee/cover.py @@ -4,7 +4,7 @@ from __future__ import annotations from typing import Any -from switchbee.api import SwitchBeeError, SwitchBeeTokenError +from switchbee.api.central_unit import SwitchBeeError, SwitchBeeTokenError from switchbee.const import SomfyCommand from switchbee.device import SwitchBeeShutter, SwitchBeeSomfy @@ -144,7 +144,8 @@ class SwitchBeeCoverEntity(SwitchBeeDeviceEntity[SwitchBeeShutter], CoverEntity) await self.coordinator.api.set_state(self._device.id, kwargs[ATTR_POSITION]) except (SwitchBeeError, SwitchBeeTokenError) as exp: raise HomeAssistantError( - f"Failed to set {self.name} position to {kwargs[ATTR_POSITION]}, error: {str(exp)}" + f"Failed to set {self.name} position to {kwargs[ATTR_POSITION]}, error:" + f" {str(exp)}" ) from exp self._get_coordinator_device().position = kwargs[ATTR_POSITION] diff --git a/homeassistant/components/switchbee/entity.py b/homeassistant/components/switchbee/entity.py index 28248667c50..7e5df69fb6d 100644 --- a/homeassistant/components/switchbee/entity.py +++ b/homeassistant/components/switchbee/entity.py @@ -3,7 +3,7 @@ import logging from typing import Generic, TypeVar, cast from switchbee import SWITCHBEE_BRAND -from switchbee.api import SwitchBeeDeviceOfflineError, SwitchBeeError +from switchbee.api.central_unit import SwitchBeeDeviceOfflineError, SwitchBeeError from switchbee.device import DeviceType, SwitchBeeBaseDevice from homeassistant.helpers.entity import DeviceInfo @@ -98,7 +98,10 @@ class SwitchBeeDeviceEntity(SwitchBeeEntity[_DeviceTypeT]): if self._is_online: _LOGGER.warning( - "%s device is not responding, check the status in the SwitchBee mobile app", + ( + "%s device is not responding, check the status in the SwitchBee" + " mobile app" + ), self.name, ) self._is_online = False diff --git a/homeassistant/components/switchbee/light.py b/homeassistant/components/switchbee/light.py index 7bcf64598c1..0ff14a45e2f 100644 --- a/homeassistant/components/switchbee/light.py +++ b/homeassistant/components/switchbee/light.py @@ -4,7 +4,7 @@ from __future__ import annotations from typing import Any -from switchbee.api import SwitchBeeDeviceOfflineError, SwitchBeeError +from switchbee.api.central_unit import SwitchBeeDeviceOfflineError, SwitchBeeError from switchbee.device import ApiStateCommand, DeviceType, SwitchBeeDimmer from homeassistant.components.light import ATTR_BRIGHTNESS, ColorMode, LightEntity diff --git a/homeassistant/components/switchbee/manifest.json b/homeassistant/components/switchbee/manifest.json index f7bcfef85a4..27201e45090 100644 --- a/homeassistant/components/switchbee/manifest.json +++ b/homeassistant/components/switchbee/manifest.json @@ -3,7 +3,7 @@ "name": "SwitchBee", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/switchbee", - "requirements": ["pyswitchbee==1.6.1"], + "requirements": ["pyswitchbee==1.7.3"], "codeowners": ["@jafar-atili"], "iot_class": "local_polling" } diff --git a/homeassistant/components/switchbee/strings.json b/homeassistant/components/switchbee/strings.json index 36fbb36d065..2abeee6dd7e 100644 --- a/homeassistant/components/switchbee/strings.json +++ b/homeassistant/components/switchbee/strings.json @@ -2,7 +2,7 @@ "config": { "step": { "user": { - "description": "Setup SwitchBee integration with Home Assistant.", + "description": "Set up SwitchBee integration with Home Assistant.", "data": { "host": "[%key:common::config_flow::data::host%]", "username": "[%key:common::config_flow::data::username%]", diff --git a/homeassistant/components/switchbee/switch.py b/homeassistant/components/switchbee/switch.py index 48fee37449c..9ed0e6ea9c0 100644 --- a/homeassistant/components/switchbee/switch.py +++ b/homeassistant/components/switchbee/switch.py @@ -4,7 +4,7 @@ from __future__ import annotations from typing import Any, TypeVar, Union -from switchbee.api import SwitchBeeDeviceOfflineError, SwitchBeeError +from switchbee.api.central_unit import SwitchBeeDeviceOfflineError, SwitchBeeError from switchbee.device import ( ApiStateCommand, SwitchBeeGroupSwitch, diff --git a/homeassistant/components/switchbee/translations/en.json b/homeassistant/components/switchbee/translations/en.json index 555d0e03f34..3fb62521186 100644 --- a/homeassistant/components/switchbee/translations/en.json +++ b/homeassistant/components/switchbee/translations/en.json @@ -15,7 +15,7 @@ "password": "Password", "username": "Username" }, - "description": "Setup SwitchBee integration with Home Assistant." + "description": "Set up SwitchBee integration with Home Assistant." } } } diff --git a/homeassistant/components/switchbee/translations/it.json b/homeassistant/components/switchbee/translations/it.json index 4694ab92b85..4946e84795b 100644 --- a/homeassistant/components/switchbee/translations/it.json +++ b/homeassistant/components/switchbee/translations/it.json @@ -15,7 +15,7 @@ "password": "Password", "username": "Nome utente" }, - "description": "Imposta l'integrazione di SwitchBee con Home Assistant." + "description": "Configura l'integrazione SwitchBee con Home Assistant." } } } diff --git a/homeassistant/components/switchbee/translations/ko.json b/homeassistant/components/switchbee/translations/ko.json index d9b021bccbc..9ba063c37dd 100644 --- a/homeassistant/components/switchbee/translations/ko.json +++ b/homeassistant/components/switchbee/translations/ko.json @@ -1,8 +1,18 @@ { "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, "step": { "user": { "data": { + "host": "\ud638\uc2a4\ud2b8", + "password": "\ube44\ubc00\ubc88\ud638", "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" } } diff --git a/homeassistant/components/switchbee/translations/pt.json b/homeassistant/components/switchbee/translations/pt.json new file mode 100644 index 00000000000..7e727215891 --- /dev/null +++ b/homeassistant/components/switchbee/translations/pt.json @@ -0,0 +1,21 @@ +{ + "config": { + "abort": { + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" + }, + "error": { + "cannot_connect": "A liga\u00e7\u00e3o falhou", + "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", + "unknown": "Erro inesperado" + }, + "step": { + "user": { + "data": { + "host": "Endere\u00e7o", + "password": "Palavra-passe", + "username": "Nome de Utilizador" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/switchbee/translations/sk.json b/homeassistant/components/switchbee/translations/sk.json index 666f6e28840..2a090ba3d37 100644 --- a/homeassistant/components/switchbee/translations/sk.json +++ b/homeassistant/components/switchbee/translations/sk.json @@ -14,7 +14,8 @@ "host": "Hostite\u013e", "password": "Heslo", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" - } + }, + "description": "Nastavenie integr\u00e1cie SwitchBee s aplik\u00e1ciou Home Assistant." } } } diff --git a/homeassistant/components/switchbot/__init__.py b/homeassistant/components/switchbot/__init__.py index 58e77dfe1bf..5d4f29b9dfe 100644 --- a/homeassistant/components/switchbot/__init__.py +++ b/homeassistant/components/switchbot/__init__.py @@ -19,6 +19,8 @@ from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers import device_registry as dr from .const import ( + CONF_ENCRYPTION_KEY, + CONF_KEY_ID, CONF_RETRY_COUNT, CONNECTABLE_SUPPORTED_MODEL_TYPES, DEFAULT_RETRY_COUNT, @@ -42,6 +44,12 @@ PLATFORMS_BY_TYPE = { SupportedModels.HYGROMETER.value: [Platform.SENSOR], SupportedModels.CONTACT.value: [Platform.BINARY_SENSOR, Platform.SENSOR], SupportedModels.MOTION.value: [Platform.BINARY_SENSOR, Platform.SENSOR], + SupportedModels.HUMIDIFIER.value: [Platform.HUMIDIFIER, Platform.SENSOR], + SupportedModels.LOCK.value: [ + Platform.BINARY_SENSOR, + Platform.LOCK, + Platform.SENSOR, + ], } CLASS_BY_DEVICE = { SupportedModels.CEILING_LIGHT.value: switchbot.SwitchbotCeilingLight, @@ -50,6 +58,8 @@ CLASS_BY_DEVICE = { SupportedModels.PLUG.value: switchbot.SwitchbotPlugMini, SupportedModels.BULB.value: switchbot.SwitchbotBulb, SupportedModels.LIGHT_STRIP.value: switchbot.SwitchbotLightStrip, + SupportedModels.HUMIDIFIER.value: switchbot.SwitchbotHumidifier, + SupportedModels.LOCK.value: switchbot.SwitchbotLock, } @@ -61,7 +71,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: assert entry.unique_id is not None hass.data.setdefault(DOMAIN, {}) if CONF_ADDRESS not in entry.data and CONF_MAC in entry.data: - # Bleak uses addresses not mac addresses which are are actually + # Bleak uses addresses not mac addresses which are actually # UUIDs on some platforms (MacOS). mac = entry.data[CONF_MAC] if "-" not in mac: @@ -84,7 +94,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: address: str = entry.data[CONF_ADDRESS] ble_device = bluetooth.async_ble_device_from_address( hass, address.upper(), connectable - ) or await switchbot.get_device(address) + ) if not ble_device: raise ConfigEntryNotReady( f"Could not find Switchbot {sensor_type} with address {address}" @@ -92,11 +102,24 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: await switchbot.close_stale_connections(ble_device) cls = CLASS_BY_DEVICE.get(sensor_type, switchbot.SwitchbotDevice) - device = cls( - device=ble_device, - password=entry.data.get(CONF_PASSWORD), - retry_count=entry.options[CONF_RETRY_COUNT], - ) + if cls is switchbot.SwitchbotLock: + try: + device = switchbot.SwitchbotLock( + device=ble_device, + key_id=entry.data.get(CONF_KEY_ID), + encryption_key=entry.data.get(CONF_ENCRYPTION_KEY), + retry_count=entry.options[CONF_RETRY_COUNT], + ) + except ValueError as error: + raise ConfigEntryNotReady( + "Invalid encryption configuration provided" + ) from error + else: + device = cls( + device=ble_device, + password=entry.data.get(CONF_PASSWORD), + retry_count=entry.options[CONF_RETRY_COUNT], + ) coordinator = hass.data[DOMAIN][entry.entry_id] = SwitchbotDataUpdateCoordinator( hass, diff --git a/homeassistant/components/switchbot/binary_sensor.py b/homeassistant/components/switchbot/binary_sensor.py index 296cd4c1800..b6439d13eb4 100644 --- a/homeassistant/components/switchbot/binary_sensor.py +++ b/homeassistant/components/switchbot/binary_sensor.py @@ -44,6 +44,28 @@ BINARY_SENSOR_TYPES: dict[str, BinarySensorEntityDescription] = { name="Light", device_class=BinarySensorDeviceClass.LIGHT, ), + "door_open": BinarySensorEntityDescription( + key="door_status", + name="Door status", + device_class=BinarySensorDeviceClass.DOOR, + ), + "unclosed_alarm": BinarySensorEntityDescription( + key="unclosed_alarm", + name="Door unclosed alarm", + entity_category=EntityCategory.DIAGNOSTIC, + device_class=BinarySensorDeviceClass.PROBLEM, + ), + "unlocked_alarm": BinarySensorEntityDescription( + key="unlocked_alarm", + name="Door unlocked alarm", + entity_category=EntityCategory.DIAGNOSTIC, + device_class=BinarySensorDeviceClass.PROBLEM, + ), + "auto_lock_paused": BinarySensorEntityDescription( + key="auto_lock_paused", + name="Door auto-lock paused", + entity_category=EntityCategory.DIAGNOSTIC, + ), } @@ -54,7 +76,7 @@ async def async_setup_entry( coordinator: SwitchbotDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] async_add_entities( SwitchBotBinarySensor(coordinator, binary_sensor) - for binary_sensor in coordinator.data["data"] + for binary_sensor in coordinator.device.parsed_data if binary_sensor in BINARY_SENSOR_TYPES ) @@ -77,4 +99,4 @@ class SwitchBotBinarySensor(SwitchbotEntity, BinarySensorEntity): @property def is_on(self) -> bool: """Return the state of the sensor.""" - return self.data["data"][self._sensor] + return self.parsed_data[self._sensor] diff --git a/homeassistant/components/switchbot/config_flow.py b/homeassistant/components/switchbot/config_flow.py index c46a9b2d501..6ba0e463718 100644 --- a/homeassistant/components/switchbot/config_flow.py +++ b/homeassistant/components/switchbot/config_flow.py @@ -4,7 +4,14 @@ from __future__ import annotations import logging from typing import Any -from switchbot import SwitchBotAdvertisement, parse_advertisement_data +from switchbot import ( + SwitchbotAccountConnectionError, + SwitchBotAdvertisement, + SwitchbotAuthenticationError, + SwitchbotLock, + SwitchbotModel, + parse_advertisement_data, +) import voluptuous as vol from homeassistant.components.bluetooth import ( @@ -12,11 +19,18 @@ from homeassistant.components.bluetooth import ( async_discovered_service_info, ) from homeassistant.config_entries import ConfigEntry, ConfigFlow, OptionsFlow -from homeassistant.const import CONF_ADDRESS, CONF_PASSWORD, CONF_SENSOR_TYPE +from homeassistant.const import ( + CONF_ADDRESS, + CONF_PASSWORD, + CONF_SENSOR_TYPE, + CONF_USERNAME, +) from homeassistant.core import callback from homeassistant.data_entry_flow import AbortFlow, FlowResult from .const import ( + CONF_ENCRYPTION_KEY, + CONF_KEY_ID, CONF_RETRY_COUNT, CONNECTABLE_SUPPORTED_MODEL_TYPES, DEFAULT_RETRY_COUNT, @@ -66,7 +80,7 @@ class SwitchbotConfigFlow(ConfigFlow, domain=DOMAIN): self, discovery_info: BluetoothServiceInfoBleak ) -> FlowResult: """Handle the bluetooth discovery step.""" - _LOGGER.debug("Discovered bluetooth device: %s", discovery_info) + _LOGGER.debug("Discovered bluetooth device: %s", discovery_info.as_dict()) await self.async_set_unique_id(format_unique_id(discovery_info.address)) self._abort_if_unique_id_configured() parsed = parse_advertisement_data( @@ -87,6 +101,8 @@ class SwitchbotConfigFlow(ConfigFlow, domain=DOMAIN): "name": data["modelFriendlyName"], "address": short_address(discovery_info.address), } + if model_name == SwitchbotModel.LOCK: + return await self.async_step_lock_choose_method() if self._discovered_adv.data["isEncrypted"]: return await self.async_step_password() return await self.async_step_confirm() @@ -144,6 +160,90 @@ class SwitchbotConfigFlow(ConfigFlow, domain=DOMAIN): }, ) + async def async_step_lock_auth( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle the SwitchBot API auth step.""" + errors = {} + assert self._discovered_adv is not None + if user_input is not None: + try: + key_details = await self.hass.async_add_executor_job( + SwitchbotLock.retrieve_encryption_key, + self._discovered_adv.address, + user_input[CONF_USERNAME], + user_input[CONF_PASSWORD], + ) + except SwitchbotAccountConnectionError as ex: + raise AbortFlow("cannot_connect") from ex + except SwitchbotAuthenticationError: + errors = {"base": "auth_failed"} + else: + return await self.async_step_lock_key(key_details) + + user_input = user_input or {} + return self.async_show_form( + step_id="lock_auth", + errors=errors, + data_schema=vol.Schema( + { + vol.Required( + CONF_USERNAME, default=user_input.get(CONF_USERNAME) + ): str, + vol.Required(CONF_PASSWORD): str, + } + ), + description_placeholders={ + "name": name_from_discovery(self._discovered_adv), + }, + ) + + async def async_step_lock_choose_method( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle the SwitchBot API chose method step.""" + assert self._discovered_adv is not None + + return self.async_show_menu( + step_id="lock_choose_method", + menu_options=["lock_auth", "lock_key"], + description_placeholders={ + "name": name_from_discovery(self._discovered_adv), + }, + ) + + async def async_step_lock_key( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle the encryption key step.""" + errors = {} + assert self._discovered_adv is not None + if user_input is not None: + if not await SwitchbotLock.verify_encryption_key( + self._discovered_adv.device, + user_input[CONF_KEY_ID], + user_input[CONF_ENCRYPTION_KEY], + ): + errors = { + "base": "encryption_key_invalid", + } + else: + return await self._async_create_entry_from_discovery(user_input) + + return self.async_show_form( + step_id="lock_key", + errors=errors, + data_schema=vol.Schema( + { + vol.Required(CONF_KEY_ID): str, + vol.Required(CONF_ENCRYPTION_KEY): str, + } + ), + description_placeholders={ + "name": name_from_discovery(self._discovered_adv), + }, + ) + @callback def _async_discover_devices(self) -> None: current_addresses = self._async_current_ids() @@ -188,6 +288,8 @@ class SwitchbotConfigFlow(ConfigFlow, domain=DOMAIN): if user_input is not None: device_adv = self._discovered_advs[user_input[CONF_ADDRESS]] await self._async_set_device(device_adv) + if device_adv.data.get("modelName") == SwitchbotModel.LOCK: + return await self.async_step_lock_choose_method() if device_adv.data["isEncrypted"]: return await self.async_step_password() return await self._async_create_entry_from_discovery(user_input) @@ -198,6 +300,8 @@ class SwitchbotConfigFlow(ConfigFlow, domain=DOMAIN): # or simply confirm it device_adv = list(self._discovered_advs.values())[0] await self._async_set_device(device_adv) + if device_adv.data.get("modelName") == SwitchbotModel.LOCK: + return await self.async_step_lock_choose_method() if device_adv.data["isEncrypted"]: return await self.async_step_password() return await self.async_step_confirm() diff --git a/homeassistant/components/switchbot/const.py b/homeassistant/components/switchbot/const.py index ecd86e1bef5..3d606d93169 100644 --- a/homeassistant/components/switchbot/const.py +++ b/homeassistant/components/switchbot/const.py @@ -23,6 +23,8 @@ class SupportedModels(StrEnum): CONTACT = "contact" PLUG = "plug" MOTION = "motion" + HUMIDIFIER = "humidifier" + LOCK = "lock" CONNECTABLE_SUPPORTED_MODEL_TYPES = { @@ -32,6 +34,8 @@ CONNECTABLE_SUPPORTED_MODEL_TYPES = { SwitchbotModel.COLOR_BULB: SupportedModels.BULB, SwitchbotModel.LIGHT_STRIP: SupportedModels.LIGHT_STRIP, SwitchbotModel.CEILING_LIGHT: SupportedModels.CEILING_LIGHT, + SwitchbotModel.HUMIDIFIER: SupportedModels.HUMIDIFIER, + SwitchbotModel.LOCK: SupportedModels.LOCK, } NON_CONNECTABLE_SUPPORTED_MODEL_TYPES = { @@ -54,6 +58,8 @@ DEFAULT_RETRY_COUNT = 3 # Config Options CONF_RETRY_COUNT = "retry_count" +CONF_KEY_ID = "key_id" +CONF_ENCRYPTION_KEY = "encryption_key" # Deprecated config Entry Options to be removed in 2023.4 CONF_TIME_BETWEEN_UPDATE_COMMAND = "update_time" diff --git a/homeassistant/components/switchbot/coordinator.py b/homeassistant/components/switchbot/coordinator.py index f68c1effc0c..77586c4202d 100644 --- a/homeassistant/components/switchbot/coordinator.py +++ b/homeassistant/components/switchbot/coordinator.py @@ -8,12 +8,13 @@ from typing import TYPE_CHECKING, Any import async_timeout import switchbot +from switchbot import SwitchbotModel from homeassistant.components import bluetooth -from homeassistant.components.bluetooth.passive_update_coordinator import ( - PassiveBluetoothDataUpdateCoordinator, +from homeassistant.components.bluetooth.active_update_coordinator import ( + ActiveBluetoothDataUpdateCoordinator, ) -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import CoreState, HomeAssistant, callback if TYPE_CHECKING: from bleak.backends.device import BLEDevice @@ -24,15 +25,9 @@ _LOGGER = logging.getLogger(__name__) DEVICE_STARTUP_TIMEOUT = 30 -def flatten_sensors_data(sensor): - """Deconstruct SwitchBot library temp object C/Fº readings from dictionary.""" - if "temp" in sensor["data"]: - sensor["data"]["temperature"] = sensor["data"]["temp"]["c"] - - return sensor - - -class SwitchbotDataUpdateCoordinator(PassiveBluetoothDataUpdateCoordinator): +class SwitchbotDataUpdateCoordinator( + ActiveBluetoothDataUpdateCoordinator[dict[str, Any]] +): """Class to manage fetching switchbot data.""" def __init__( @@ -44,25 +39,50 @@ class SwitchbotDataUpdateCoordinator(PassiveBluetoothDataUpdateCoordinator): base_unique_id: str, device_name: str, connectable: bool, - model: str, + model: SwitchbotModel, ) -> None: """Initialize global switchbot data updater.""" super().__init__( - hass, - logger, - ble_device.address, - bluetooth.BluetoothScanningMode.ACTIVE, - connectable, + hass=hass, + logger=logger, + address=ble_device.address, + needs_poll_method=self._needs_poll, + poll_method=self._async_update, + mode=bluetooth.BluetoothScanningMode.ACTIVE, + connectable=connectable, ) self.ble_device = ble_device self.device = device - self.data: dict[str, Any] = {} self.device_name = device_name self.base_unique_id = base_unique_id self.model = model self._ready_event = asyncio.Event() self._was_unavailable = True + @callback + def _needs_poll( + self, + service_info: bluetooth.BluetoothServiceInfoBleak, + seconds_since_last_poll: float | None, + ) -> bool: + # Only poll if hass is running, we need to poll, + # and we actually have a way to connect to the device + return ( + self.hass.state == CoreState.running + and self.device.poll_needed(seconds_since_last_poll) + and bool( + bluetooth.async_ble_device_from_address( + self.hass, service_info.device.address, connectable=True + ) + ) + ) + + async def _async_update( + self, service_info: bluetooth.BluetoothServiceInfoBleak + ) -> dict[str, Any]: + """Poll the device.""" + return await self.device.update() + @callback def _async_handle_unavailable( self, service_info: bluetooth.BluetoothServiceInfoBleak @@ -81,17 +101,18 @@ class SwitchbotDataUpdateCoordinator(PassiveBluetoothDataUpdateCoordinator): self.ble_device = service_info.device if not ( adv := switchbot.parse_advertisement_data( - service_info.device, service_info.advertisement + service_info.device, service_info.advertisement, self.model ) ): return if "modelName" in adv.data: self._ready_event.set() - _LOGGER.debug("%s: Switchbot data: %s", self.ble_device.address, self.data) + _LOGGER.debug( + "%s: Switchbot data: %s", self.ble_device.address, self.device.data + ) if not self.device.advertisement_changed(adv) and not self._was_unavailable: return self._was_unavailable = False - self.data = flatten_sensors_data(adv.data) self.device.update_from_advertisement(adv) super()._async_handle_bluetooth_event(service_info, change) diff --git a/homeassistant/components/switchbot/cover.py b/homeassistant/components/switchbot/cover.py index 696c9455f28..7450653e9a9 100644 --- a/homeassistant/components/switchbot/cover.py +++ b/homeassistant/components/switchbot/cover.py @@ -98,7 +98,7 @@ class SwitchBotCurtainEntity(SwitchbotEntity, CoverEntity, RestoreEntity): @callback def _handle_coordinator_update(self) -> None: """Handle updated data from the coordinator.""" - self._attr_current_cover_position = self.data["data"]["position"] - self._attr_is_closed = self.data["data"]["position"] <= 20 - self._attr_is_opening = self.data["data"]["inMotion"] + self._attr_current_cover_position = self.parsed_data["position"] + self._attr_is_closed = self.parsed_data["position"] <= 20 + self._attr_is_opening = self.parsed_data["inMotion"] self.async_write_ha_state() diff --git a/homeassistant/components/switchbot/entity.py b/homeassistant/components/switchbot/entity.py index fcf0bdc4da2..60e7528dba6 100644 --- a/homeassistant/components/switchbot/entity.py +++ b/homeassistant/components/switchbot/entity.py @@ -1,11 +1,11 @@ """An abstract class common to all Switchbot entities.""" from __future__ import annotations -from abc import abstractmethod from collections.abc import Mapping +import logging from typing import Any -from switchbot import SwitchbotDevice +from switchbot import Switchbot, SwitchbotDevice from homeassistant.components.bluetooth.passive_update_coordinator import ( PassiveBluetoothCoordinatorEntity, @@ -13,11 +13,13 @@ from homeassistant.components.bluetooth.passive_update_coordinator import ( from homeassistant.const import ATTR_CONNECTIONS from homeassistant.core import callback from homeassistant.helpers import device_registry as dr -from homeassistant.helpers.entity import DeviceInfo +from homeassistant.helpers.entity import DeviceInfo, ToggleEntity from .const import MANUFACTURER from .coordinator import SwitchbotDataUpdateCoordinator +_LOGGER = logging.getLogger(__name__) + class SwitchbotEntity(PassiveBluetoothCoordinatorEntity): """Generic entity encapsulating common features of Switchbot device.""" @@ -51,20 +53,16 @@ class SwitchbotEntity(PassiveBluetoothCoordinatorEntity): ) @property - def data(self) -> dict[str, Any]: - """Return coordinator data for this entity.""" - return self.coordinator.data + def parsed_data(self) -> dict[str, Any]: + """Return parsed device data for this entity.""" + return self.coordinator.device.parsed_data @property def extra_state_attributes(self) -> Mapping[Any, Any]: """Return the state attributes.""" return {"last_run_success": self._last_run_success} - -class SwitchbotSubscribeEntity(SwitchbotEntity): - """Base class for Switchbot entities that use subscribe.""" - - @abstractmethod + @callback def _async_update_attrs(self) -> None: """Update the entity attributes.""" @@ -85,3 +83,27 @@ class SwitchbotSubscribeEntity(SwitchbotEntity): Only used by the generic entity update service. """ await self._device.update() + + +class SwitchbotSwitchedEntity(SwitchbotEntity, ToggleEntity): + """Base class for Switchbot entities that can be turned on and off.""" + + _device: Switchbot + + async def async_turn_on(self, **kwargs: Any) -> None: + """Turn device on.""" + _LOGGER.debug("Turn Switchbot device on %s", self._address) + + self._last_run_success = bool(await self._device.turn_on()) + if self._last_run_success: + self._attr_is_on = True + self.async_write_ha_state() + + async def async_turn_off(self, **kwargs: Any) -> None: + """Turn device off.""" + _LOGGER.debug("Turn Switchbot device off %s", self._address) + + self._last_run_success = bool(await self._device.turn_off()) + if self._last_run_success: + self._attr_is_on = False + self.async_write_ha_state() diff --git a/homeassistant/components/switchbot/humidifier.py b/homeassistant/components/switchbot/humidifier.py new file mode 100644 index 00000000000..2bb71bacea1 --- /dev/null +++ b/homeassistant/components/switchbot/humidifier.py @@ -0,0 +1,72 @@ +"""Support for Switchbot humidifier.""" +from __future__ import annotations + +import logging + +import switchbot + +from homeassistant.components.humidifier import ( + MODE_AUTO, + MODE_NORMAL, + HumidifierDeviceClass, + HumidifierEntity, + HumidifierEntityFeature, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_platform + +from .const import DOMAIN +from .coordinator import SwitchbotDataUpdateCoordinator +from .entity import SwitchbotSwitchedEntity + +PARALLEL_UPDATES = 0 +_LOGGER = logging.getLogger(__name__) + + +async def async_setup_entry( + hass: HomeAssistant, + entry: ConfigEntry, + async_add_entities: entity_platform.AddEntitiesCallback, +) -> None: + """Set up Switchbot based on a config entry.""" + coordinator: SwitchbotDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] + async_add_entities([SwitchBotHumidifier(coordinator)]) + + +class SwitchBotHumidifier(SwitchbotSwitchedEntity, HumidifierEntity): + """Representation of a Switchbot humidifier.""" + + _attr_supported_features = HumidifierEntityFeature.MODES + _attr_device_class = HumidifierDeviceClass.HUMIDIFIER + _attr_available_modes = [MODE_NORMAL, MODE_AUTO] + _device: switchbot.SwitchbotHumidifier + _attr_min_humidity = 1 + + @property + def is_on(self) -> bool | None: + """Return true if device is on.""" + return self._device.is_on() + + @property + def mode(self) -> str: + """Return the humidity we try to reach.""" + return MODE_AUTO if self._device.is_auto() else MODE_NORMAL + + @property + def target_humidity(self) -> int | None: + """Return the humidity we try to reach.""" + return self._device.get_target_humidity() + + async def async_set_humidity(self, humidity: int) -> None: + """Set new target humidity.""" + self._last_run_success = bool(await self._device.set_level(humidity)) + self.async_write_ha_state() + + async def async_set_mode(self, mode: str) -> None: + """Set new target humidity.""" + if mode == MODE_AUTO: + self._last_run_success = await self._device.async_set_auto() + else: + self._last_run_success = await self._device.async_set_manual() + self.async_write_ha_state() diff --git a/homeassistant/components/switchbot/light.py b/homeassistant/components/switchbot/light.py index 0b4f748f1b2..5dc99c5d6f1 100644 --- a/homeassistant/components/switchbot/light.py +++ b/homeassistant/components/switchbot/light.py @@ -22,7 +22,7 @@ from homeassistant.util.color import ( from .const import DOMAIN from .coordinator import SwitchbotDataUpdateCoordinator -from .entity import SwitchbotSubscribeEntity +from .entity import SwitchbotEntity SWITCHBOT_COLOR_MODE_TO_HASS = { SwitchBotColorMode.RGB: ColorMode.RGB, @@ -42,7 +42,7 @@ async def async_setup_entry( async_add_entities([SwitchbotLightEntity(coordinator)]) -class SwitchbotLightEntity(SwitchbotSubscribeEntity, LightEntity): +class SwitchbotLightEntity(SwitchbotEntity, LightEntity): """Representation of switchbot light bulb.""" _device: SwitchbotBaseLight diff --git a/homeassistant/components/switchbot/lock.py b/homeassistant/components/switchbot/lock.py new file mode 100644 index 00000000000..738755ae636 --- /dev/null +++ b/homeassistant/components/switchbot/lock.py @@ -0,0 +1,55 @@ +"""Support for SwitchBot lock platform.""" +from typing import Any + +import switchbot +from switchbot.const import LockStatus + +from homeassistant.components.lock import LockEntity +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from .const import DOMAIN +from .coordinator import SwitchbotDataUpdateCoordinator +from .entity import SwitchbotEntity + + +async def async_setup_entry( + hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback +) -> None: + """Set up Switchbot lock based on a config entry.""" + coordinator: SwitchbotDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] + async_add_entities([(SwitchBotLock(coordinator))]) + + +# noinspection PyAbstractClass +class SwitchBotLock(SwitchbotEntity, LockEntity): + """Representation of a Switchbot lock.""" + + _device: switchbot.SwitchbotLock + + def __init__(self, coordinator: SwitchbotDataUpdateCoordinator) -> None: + """Initialize the entity.""" + super().__init__(coordinator) + self._async_update_attrs() + + def _async_update_attrs(self) -> None: + """Update the entity attributes.""" + status = self._device.get_lock_status() + self._attr_is_locked = status is LockStatus.LOCKED + self._attr_is_locking = status is LockStatus.LOCKING + self._attr_is_unlocking = status is LockStatus.UNLOCKING + self._attr_is_jammed = status in { + LockStatus.LOCKING_STOP, + LockStatus.UNLOCKING_STOP, + } + + async def async_lock(self, **kwargs: Any) -> None: + """Lock the lock.""" + self._last_run_success = await self._device.lock() + self.async_write_ha_state() + + async def async_unlock(self, **kwargs: Any) -> None: + """Unlock the lock.""" + self._last_run_success = await self._device.unlock() + self.async_write_ha_state() diff --git a/homeassistant/components/switchbot/manifest.json b/homeassistant/components/switchbot/manifest.json index 34dfbddbf2e..c7c50e5cf6e 100644 --- a/homeassistant/components/switchbot/manifest.json +++ b/homeassistant/components/switchbot/manifest.json @@ -2,7 +2,7 @@ "domain": "switchbot", "name": "SwitchBot", "documentation": "https://www.home-assistant.io/integrations/switchbot", - "requirements": ["PySwitchbot==0.23.2"], + "requirements": ["PySwitchbot==0.36.1"], "config_flow": true, "dependencies": ["bluetooth"], "codeowners": [ @@ -10,7 +10,8 @@ "@danielhiversen", "@RenierM26", "@murtas", - "@Eloston" + "@Eloston", + "@dsypniewski" ], "bluetooth": [ { @@ -24,6 +25,18 @@ { "service_uuid": "cba20d00-224d-11e6-9fb8-0002a5d5c51b", "connectable": false + }, + { + "manufacturer_id": 2409, + "connectable": false + }, + { + "manufacturer_id": 89, + "connectable": true + }, + { + "manufacturer_id": 741, + "connectable": true } ], "iot_class": "local_push", diff --git a/homeassistant/components/switchbot/sensor.py b/homeassistant/components/switchbot/sensor.py index 9b1baf805bb..f31f7486b72 100644 --- a/homeassistant/components/switchbot/sensor.py +++ b/homeassistant/components/switchbot/sensor.py @@ -12,7 +12,8 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( PERCENTAGE, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, - TEMP_CELSIUS, + UnitOfPower, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory @@ -56,7 +57,6 @@ SENSOR_TYPES: dict[str, SensorEntityDescription] = { name="Light level", native_unit_of_measurement="Level", state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.ILLUMINANCE, ), "humidity": SensorEntityDescription( key="humidity", @@ -68,10 +68,17 @@ SENSOR_TYPES: dict[str, SensorEntityDescription] = { "temperature": SensorEntityDescription( key="temperature", name="Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, device_class=SensorDeviceClass.TEMPERATURE, ), + "power": SensorEntityDescription( + key="power", + name="Power", + native_unit_of_measurement=UnitOfPower.WATT, + state_class=SensorStateClass.MEASUREMENT, + device_class=SensorDeviceClass.POWER, + ), } @@ -81,11 +88,8 @@ async def async_setup_entry( """Set up Switchbot sensor based on a config entry.""" coordinator: SwitchbotDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] entities = [ - SwitchBotSensor( - coordinator, - sensor, - ) - for sensor in coordinator.data["data"] + SwitchBotSensor(coordinator, sensor) + for sensor in coordinator.device.parsed_data if sensor in SENSOR_TYPES ] entities.append(SwitchbotRSSISensor(coordinator, "rssi")) @@ -109,7 +113,7 @@ class SwitchBotSensor(SwitchbotEntity, SensorEntity): @property def native_value(self) -> str | int | None: """Return the state of the sensor.""" - return self.data["data"][self._sensor] + return self.parsed_data[self._sensor] class SwitchbotRSSISensor(SwitchBotSensor): diff --git a/homeassistant/components/switchbot/strings.json b/homeassistant/components/switchbot/strings.json index c7b0744c579..08fd960334a 100644 --- a/homeassistant/components/switchbot/strings.json +++ b/homeassistant/components/switchbot/strings.json @@ -8,16 +8,40 @@ } }, "confirm": { - "description": "Do you want to setup {name}?" + "description": "Do you want to set up {name}?" }, "password": { "description": "The {name} device requires a password", "data": { "password": "[%key:common::config_flow::data::password%]" } + }, + "lock_key": { + "description": "The {name} device requires encryption key, details on how to obtain it can be found in the documentation.", + "data": { + "key_id": "Key ID", + "encryption_key": "Encryption key" + } + }, + "lock_auth": { + "description": "Please provide your SwitchBot app username and password. This data won't be saved and only used to retrieve your locks encryption key.", + "data": { + "username": "[%key:common::config_flow::data::username%]", + "password": "[%key:common::config_flow::data::password%]" + } + }, + "lock_choose_method": { + "description": "A SwitchBot lock can be set up in Home Assistant in two different ways.\n\nYou can enter the key id and encryption key yourself, or Home Assistant can import them from your SwitchBot account.", + "menu_options": { + "lock_auth": "SwitchBot account (recommended)", + "lock_key": "Enter lock encryption key manually" + } } }, - "error": {}, + "error": { + "encryption_key_invalid": "Key ID or Encryption key is invalid", + "auth_failed": "Authentication failed" + }, "abort": { "already_configured_device": "[%key:common::config_flow::abort::already_configured_device%]", "no_unconfigured_devices": "No unconfigured devices found.", diff --git a/homeassistant/components/switchbot/switch.py b/homeassistant/components/switchbot/switch.py index c4bbc2af1e0..67749ea0c5a 100644 --- a/homeassistant/components/switchbot/switch.py +++ b/homeassistant/components/switchbot/switch.py @@ -2,7 +2,6 @@ from __future__ import annotations import logging -from typing import Any import switchbot @@ -15,7 +14,7 @@ from homeassistant.helpers.restore_state import RestoreEntity from .const import DOMAIN from .coordinator import SwitchbotDataUpdateCoordinator -from .entity import SwitchbotEntity +from .entity import SwitchbotSwitchedEntity # Initialize the logger _LOGGER = logging.getLogger(__name__) @@ -32,7 +31,7 @@ async def async_setup_entry( async_add_entities([SwitchBotSwitch(coordinator)]) -class SwitchBotSwitch(SwitchbotEntity, SwitchEntity, RestoreEntity): +class SwitchBotSwitch(SwitchbotSwitchedEntity, SwitchEntity, RestoreEntity): """Representation of a Switchbot switch.""" _attr_device_class = SwitchDeviceClass.SWITCH @@ -51,24 +50,6 @@ class SwitchBotSwitch(SwitchbotEntity, SwitchEntity, RestoreEntity): self._attr_is_on = last_state.state == STATE_ON self._last_run_success = last_state.attributes.get("last_run_success") - async def async_turn_on(self, **kwargs: Any) -> None: - """Turn device on.""" - _LOGGER.info("Turn Switchbot bot on %s", self._address) - - self._last_run_success = bool(await self._device.turn_on()) - if self._last_run_success: - self._attr_is_on = True - self.async_write_ha_state() - - async def async_turn_off(self, **kwargs: Any) -> None: - """Turn device off.""" - _LOGGER.info("Turn Switchbot bot off %s", self._address) - - self._last_run_success = bool(await self._device.turn_off()) - if self._last_run_success: - self._attr_is_on = False - self.async_write_ha_state() - @property def assumed_state(self) -> bool: """Return true if unable to access real state of entity.""" diff --git a/homeassistant/components/switchbot/translations/de.json b/homeassistant/components/switchbot/translations/de.json index fa62c6fa343..b6f637f36df 100644 --- a/homeassistant/components/switchbot/translations/de.json +++ b/homeassistant/components/switchbot/translations/de.json @@ -4,7 +4,7 @@ "already_configured_device": "Ger\u00e4t ist bereits konfiguriert", "cannot_connect": "Verbindung fehlgeschlagen", "no_unconfigured_devices": "Keine unkonfigurierten Ger\u00e4te gefunden.", - "switchbot_unsupported_type": "Nicht unterst\u00fctzter Switchbot-Typ.", + "switchbot_unsupported_type": "Nicht unterst\u00fctzter Switchbot Typ.", "unknown": "Unerwarteter Fehler" }, "flow_title": "{name} ({address})", diff --git a/homeassistant/components/switchbot/translations/en.json b/homeassistant/components/switchbot/translations/en.json index e262ed53175..b5658e58d6b 100644 --- a/homeassistant/components/switchbot/translations/en.json +++ b/homeassistant/components/switchbot/translations/en.json @@ -7,10 +7,35 @@ "switchbot_unsupported_type": "Unsupported Switchbot Type.", "unknown": "Unexpected error" }, + "error": { + "auth_failed": "Authentication failed", + "encryption_key_invalid": "Key ID or Encryption key is invalid" + }, "flow_title": "{name} ({address})", "step": { "confirm": { - "description": "Do you want to setup {name}?" + "description": "Do you want to set up {name}?" + }, + "lock_auth": { + "data": { + "password": "Password", + "username": "Username" + }, + "description": "Please provide your SwitchBot app username and password. This data won't be saved and only used to retrieve your locks encryption key." + }, + "lock_choose_method": { + "description": "A SwitchBot lock can be set up in Home Assistant in two different ways.\n\nYou can enter the key id and encryption key yourself, or Home Assistant can import them from your SwitchBot account.", + "menu_options": { + "lock_auth": "SwitchBot account (recommended)", + "lock_key": "Enter lock encryption key manually" + } + }, + "lock_key": { + "data": { + "encryption_key": "Encryption key", + "key_id": "Key ID" + }, + "description": "The {name} device requires encryption key, details on how to obtain it can be found in the documentation." }, "password": { "data": { @@ -22,7 +47,18 @@ "data": { "address": "Device address" } + }, + "lock_key": { + "description": "The {name} device requires encryption key, details on how to obtain it can be found in the documentation.", + "data": { + "key_id": "Key ID", + "encryption_key": "Encryption key" } + } + }, + "error": { + "key_id_invalid": "Key ID or Encryption key is invalid", + "encryption_key_invalid": "Key ID or Encryption key is invalid" } }, "options": { diff --git a/homeassistant/components/switchbot/translations/ko.json b/homeassistant/components/switchbot/translations/ko.json new file mode 100644 index 00000000000..36317f9ef27 --- /dev/null +++ b/homeassistant/components/switchbot/translations/ko.json @@ -0,0 +1,16 @@ +{ + "config": { + "abort": { + "already_configured_device": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "password": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/switchbot/translations/no.json b/homeassistant/components/switchbot/translations/no.json index bb3ca766c70..be6a932f116 100644 --- a/homeassistant/components/switchbot/translations/no.json +++ b/homeassistant/components/switchbot/translations/no.json @@ -10,7 +10,7 @@ "flow_title": "{name} ( {address} )", "step": { "confirm": { - "description": "Vil du konfigurere {name}?" + "description": "Vil du sette opp {name} ?" }, "password": { "data": { diff --git a/homeassistant/components/switchbot/translations/pt.json b/homeassistant/components/switchbot/translations/pt.json new file mode 100644 index 00000000000..92f53ccad05 --- /dev/null +++ b/homeassistant/components/switchbot/translations/pt.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "password": { + "data": { + "password": "Palavra-passe" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/switchbot/translations/sk.json b/homeassistant/components/switchbot/translations/sk.json index 20403a167ff..3c42c629e7b 100644 --- a/homeassistant/components/switchbot/translations/sk.json +++ b/homeassistant/components/switchbot/translations/sk.json @@ -4,8 +4,15 @@ "already_configured_device": "Zariadenie u\u017e je nakonfigurovan\u00e9", "cannot_connect": "Nepodarilo sa pripoji\u0165", "no_unconfigured_devices": "Nena\u0161li sa \u017eiadne nenakonfigurovan\u00e9 zariadenia.", + "switchbot_unsupported_type": "Nepodporovan\u00fd typ Switchbot.", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, + "error": { + "few": "Pr\u00e1zdnych", + "many": "Pr\u00e1zdnych", + "one": "Pr\u00e1zdny", + "other": "Pr\u00e1zdny" + }, "flow_title": "{name} ({address})", "step": { "confirm": { @@ -23,5 +30,14 @@ } } } + }, + "options": { + "step": { + "init": { + "data": { + "retry_count": "Po\u010det opakovan\u00ed" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/switcher_kis/__init__.py b/homeassistant/components/switcher_kis/__init__.py index 39710be4857..bc352989799 100644 --- a/homeassistant/components/switcher_kis/__init__.py +++ b/homeassistant/components/switcher_kis/__init__.py @@ -122,7 +122,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return True -class SwitcherDataUpdateCoordinator(update_coordinator.DataUpdateCoordinator): +class SwitcherDataUpdateCoordinator( + update_coordinator.DataUpdateCoordinator[SwitcherBase] +): """Switcher device data update coordinator.""" def __init__( @@ -138,10 +140,11 @@ class SwitcherDataUpdateCoordinator(update_coordinator.DataUpdateCoordinator): self.entry = entry self.data = device - async def _async_update_data(self) -> None: + async def _async_update_data(self) -> SwitcherBase: """Mark device offline if no data.""" raise update_coordinator.UpdateFailed( - f"Device {self.name} did not send update for {MAX_UPDATE_INTERVAL_SEC} seconds" + f"Device {self.name} did not send update for" + f" {MAX_UPDATE_INTERVAL_SEC} seconds" ) @property diff --git a/homeassistant/components/switcher_kis/button.py b/homeassistant/components/switcher_kis/button.py index 756acc1366e..8ecd12bc56a 100644 --- a/homeassistant/components/switcher_kis/button.py +++ b/homeassistant/components/switcher_kis/button.py @@ -153,6 +153,5 @@ class SwitcherThermostatButtonEntity( self.coordinator.last_update_success = False self.async_write_ha_state() raise HomeAssistantError( - f"Call api for {self.name} failed, " - f"response/error: {response or error}" + f"Call api for {self.name} failed, response/error: {response or error}" ) diff --git a/homeassistant/components/switcher_kis/climate.py b/homeassistant/components/switcher_kis/climate.py index 01f4c80da31..57d4d9977f2 100644 --- a/homeassistant/components/switcher_kis/climate.py +++ b/homeassistant/components/switcher_kis/climate.py @@ -26,7 +26,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import device_registry @@ -102,7 +102,7 @@ class SwitcherClimateEntity( self._attr_min_temp = remote.min_temperature self._attr_max_temp = remote.max_temperature self._attr_target_temperature_step = 1 - self._attr_temperature_unit = TEMP_CELSIUS + self._attr_temperature_unit = UnitOfTemperature.CELSIUS self._attr_hvac_modes = [HVACMode.OFF] for mode in remote.modes_features: diff --git a/homeassistant/components/switcher_kis/sensor.py b/homeassistant/components/switcher_kis/sensor.py index 34a4de3e9d3..c75d27d67d4 100644 --- a/homeassistant/components/switcher_kis/sensor.py +++ b/homeassistant/components/switcher_kis/sensor.py @@ -11,7 +11,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ELECTRIC_CURRENT_AMPERE, POWER_WATT +from homeassistant.const import UnitOfElectricCurrent, UnitOfPower from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import device_registry from homeassistant.helpers.dispatcher import async_dispatcher_connect @@ -38,13 +38,13 @@ class AttributeDescription: POWER_SENSORS = { "power_consumption": AttributeDescription( name="Power Consumption", - unit=POWER_WATT, + unit=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), "electric_current": AttributeDescription( name="Electric Current", - unit=ELECTRIC_CURRENT_AMPERE, + unit=UnitOfElectricCurrent.AMPERE, device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, ), diff --git a/homeassistant/components/switcher_kis/translations/en.json b/homeassistant/components/switcher_kis/translations/en.json index f05becffed3..1f858b1dfb5 100644 --- a/homeassistant/components/switcher_kis/translations/en.json +++ b/homeassistant/components/switcher_kis/translations/en.json @@ -6,7 +6,7 @@ }, "step": { "confirm": { - "description": "Do you want to start set up?" + "description": "Do you want to start setup?" } } } diff --git a/homeassistant/components/switcher_kis/translations/it.json b/homeassistant/components/switcher_kis/translations/it.json index 0278fe07bfe..278f85c5cff 100644 --- a/homeassistant/components/switcher_kis/translations/it.json +++ b/homeassistant/components/switcher_kis/translations/it.json @@ -6,7 +6,7 @@ }, "step": { "confirm": { - "description": "Vuoi iniziare la configurazione?" + "description": "Vuoi avviare la configurazione?" } } } diff --git a/homeassistant/components/switcher_kis/translations/ko.json b/homeassistant/components/switcher_kis/translations/ko.json index 20ad990e862..e5ae04d6e5c 100644 --- a/homeassistant/components/switcher_kis/translations/ko.json +++ b/homeassistant/components/switcher_kis/translations/ko.json @@ -1,5 +1,9 @@ { "config": { + "abort": { + "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4", + "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." + }, "step": { "confirm": { "description": "\uc124\uc815\uc744 \uc2dc\uc791\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" diff --git a/homeassistant/components/syncthing/__init__.py b/homeassistant/components/syncthing/__init__.py index 3950f908e85..15f9bc7d307 100644 --- a/homeassistant/components/syncthing/__init__.py +++ b/homeassistant/components/syncthing/__init__.py @@ -153,7 +153,10 @@ class SyncthingClient: ) except aiosyncthing.exceptions.SyncthingError: _LOGGER.info( - "The syncthing server '%s' is not available. Sleeping %i seconds and retrying", + ( + "The syncthing server '%s' is not available. Sleeping %i" + " seconds and retrying" + ), self._client.url, RECONNECT_INTERVAL.total_seconds(), ) diff --git a/homeassistant/components/syncthing/strings.json b/homeassistant/components/syncthing/strings.json index 36d1a688a70..8d7039c115c 100644 --- a/homeassistant/components/syncthing/strings.json +++ b/homeassistant/components/syncthing/strings.json @@ -3,7 +3,7 @@ "step": { "user": { "data": { - "title": "Setup Syncthing integration", + "title": "Set up Syncthing integration", "url": "[%key:common::config_flow::data::url%]", "verify_ssl": "[%key:common::config_flow::data::verify_ssl%]", "token": "Token" diff --git a/homeassistant/components/syncthing/translations/de.json b/homeassistant/components/syncthing/translations/de.json index a753ae08cd9..ecd199cfe45 100644 --- a/homeassistant/components/syncthing/translations/de.json +++ b/homeassistant/components/syncthing/translations/de.json @@ -10,7 +10,7 @@ "step": { "user": { "data": { - "title": "Syncthing-Integration einrichten", + "title": "Syncthing Integration einrichten", "token": "Token", "url": "URL", "verify_ssl": "SSL-Zertifikat \u00fcberpr\u00fcfen" diff --git a/homeassistant/components/syncthing/translations/en.json b/homeassistant/components/syncthing/translations/en.json index e25581817a9..25b6a9e59de 100644 --- a/homeassistant/components/syncthing/translations/en.json +++ b/homeassistant/components/syncthing/translations/en.json @@ -10,7 +10,7 @@ "step": { "user": { "data": { - "title": "Setup Syncthing integration", + "title": "Set up Syncthing integration", "token": "Token", "url": "URL", "verify_ssl": "Verify SSL certificate" diff --git a/homeassistant/components/syncthing/translations/it.json b/homeassistant/components/syncthing/translations/it.json index 061be57e295..063576feb28 100644 --- a/homeassistant/components/syncthing/translations/it.json +++ b/homeassistant/components/syncthing/translations/it.json @@ -10,7 +10,7 @@ "step": { "user": { "data": { - "title": "Configurazione integrazione Syncthing", + "title": "Configura l'integrazione di Syncthing", "token": "Token", "url": "URL", "verify_ssl": "Verifica il certificato SSL" diff --git a/homeassistant/components/syncthing/translations/ko.json b/homeassistant/components/syncthing/translations/ko.json new file mode 100644 index 00000000000..a91a286920a --- /dev/null +++ b/homeassistant/components/syncthing/translations/ko.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "\uc11c\ube44\uc2a4\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "url": "URL \uc8fc\uc18c", + "verify_ssl": "SSL \uc778\uc99d\uc11c \ud655\uc778" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/syncthing/translations/no.json b/homeassistant/components/syncthing/translations/no.json index 00a4de27e7f..78078b900e6 100644 --- a/homeassistant/components/syncthing/translations/no.json +++ b/homeassistant/components/syncthing/translations/no.json @@ -10,7 +10,7 @@ "step": { "user": { "data": { - "title": "Setup Syncthing integrasjon", + "title": "Sett opp Syncthing-integrasjon", "token": "Token", "url": "URL", "verify_ssl": "Verifisere SSL-sertifikat" diff --git a/homeassistant/components/syncthing/translations/pt-BR.json b/homeassistant/components/syncthing/translations/pt-BR.json index d7f246e1aa6..94b76c92ca4 100644 --- a/homeassistant/components/syncthing/translations/pt-BR.json +++ b/homeassistant/components/syncthing/translations/pt-BR.json @@ -10,7 +10,7 @@ "step": { "user": { "data": { - "title": "Configurar integra\u00e7\u00e3o do Syncthing", + "title": "Configurar a integra\u00e7\u00e3o do Syncthing", "token": "Token", "url": "URL", "verify_ssl": "Verifique o certificado SSL" diff --git a/homeassistant/components/syncthing/translations/sk.json b/homeassistant/components/syncthing/translations/sk.json index 18c5c12cd5e..b731cffd060 100644 --- a/homeassistant/components/syncthing/translations/sk.json +++ b/homeassistant/components/syncthing/translations/sk.json @@ -10,6 +10,7 @@ "step": { "user": { "data": { + "title": "Nastavenie integr\u00e1cie Syncthing", "token": "Token", "url": "URL", "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" diff --git a/homeassistant/components/synology_dsm/binary_sensor.py b/homeassistant/components/synology_dsm/binary_sensor.py index ac930467442..7511953de90 100644 --- a/homeassistant/components/synology_dsm/binary_sensor.py +++ b/homeassistant/components/synology_dsm/binary_sensor.py @@ -2,7 +2,6 @@ from __future__ import annotations from dataclasses import dataclass -from typing import Any from synology_dsm.api.core.security import SynoCoreSecurity from synology_dsm.api.storage.storage import SynoStorage @@ -17,10 +16,10 @@ from homeassistant.const import CONF_DISKS from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from . import SynoApi from .const import DOMAIN +from .coordinator import SynologyDSMCentralUpdateCoordinator from .entity import ( SynologyDSMBaseEntity, SynologyDSMDeviceEntity, @@ -89,7 +88,9 @@ async def async_setup_entry( async_add_entities(entities) -class SynoDSMBinarySensor(SynologyDSMBaseEntity, BinarySensorEntity): +class SynoDSMBinarySensor( + SynologyDSMBaseEntity[SynologyDSMCentralUpdateCoordinator], BinarySensorEntity +): """Mixin for binary sensor specific attributes.""" entity_description: SynologyDSMBinarySensorEntityDescription @@ -97,7 +98,7 @@ class SynoDSMBinarySensor(SynologyDSMBaseEntity, BinarySensorEntity): def __init__( self, api: SynoApi, - coordinator: DataUpdateCoordinator[dict[str, dict[str, Any]]], + coordinator: SynologyDSMCentralUpdateCoordinator, description: SynologyDSMBinarySensorEntityDescription, ) -> None: """Initialize the Synology DSM binary_sensor entity.""" @@ -131,7 +132,7 @@ class SynoDSMStorageBinarySensor(SynologyDSMDeviceEntity, SynoDSMBinarySensor): def __init__( self, api: SynoApi, - coordinator: DataUpdateCoordinator[dict[str, dict[str, Any]]], + coordinator: SynologyDSMCentralUpdateCoordinator, description: SynologyDSMBinarySensorEntityDescription, device_id: str | None = None, ) -> None: diff --git a/homeassistant/components/synology_dsm/camera.py b/homeassistant/components/synology_dsm/camera.py index 6dac67cf72d..65520968e1d 100644 --- a/homeassistant/components/synology_dsm/camera.py +++ b/homeassistant/components/synology_dsm/camera.py @@ -20,7 +20,6 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from . import SynoApi from .const import ( @@ -29,6 +28,7 @@ from .const import ( DOMAIN, SIGNAL_CAMERA_SOURCE_CHANGED, ) +from .coordinator import SynologyDSMCameraUpdateCoordinator from .entity import SynologyDSMBaseEntity, SynologyDSMEntityDescription from .models import SynologyDSMData @@ -54,17 +54,16 @@ async def async_setup_entry( ) -class SynoDSMCamera(SynologyDSMBaseEntity, Camera): +class SynoDSMCamera(SynologyDSMBaseEntity[SynologyDSMCameraUpdateCoordinator], Camera): """Representation a Synology camera.""" _attr_supported_features = CameraEntityFeature.STREAM - coordinator: DataUpdateCoordinator[dict[str, dict[str, SynoCamera]]] entity_description: SynologyDSMCameraEntityDescription def __init__( self, api: SynoApi, - coordinator: DataUpdateCoordinator[dict[str, dict[str, SynoCamera]]], + coordinator: SynologyDSMCameraUpdateCoordinator, camera_id: str, ) -> None: """Initialize a Synology camera.""" diff --git a/homeassistant/components/synology_dsm/common.py b/homeassistant/components/synology_dsm/common.py index f57262e2a57..847fc6061c3 100644 --- a/homeassistant/components/synology_dsm/common.py +++ b/homeassistant/components/synology_dsm/common.py @@ -98,7 +98,8 @@ class SynoApi: self._with_surveillance_station = False self.dsm.reset(SynoSurveillanceStation.API_KEY) LOGGER.info( - "Surveillance Station found, but disabled due to missing user permissions" + "Surveillance Station found, but disabled due to missing user" + " permissions" ) LOGGER.debug( diff --git a/homeassistant/components/synology_dsm/coordinator.py b/homeassistant/components/synology_dsm/coordinator.py index e2f0f0741f4..9090b945d44 100644 --- a/homeassistant/components/synology_dsm/coordinator.py +++ b/homeassistant/components/synology_dsm/coordinator.py @@ -3,7 +3,7 @@ from __future__ import annotations from datetime import timedelta import logging -from typing import Any +from typing import Any, TypeVar import async_timeout from synology_dsm.api.surveillance_station.camera import SynoCamera @@ -23,9 +23,10 @@ from .const import ( ) _LOGGER = logging.getLogger(__name__) +_DataT = TypeVar("_DataT") -class SynologyDSMUpdateCoordinator(DataUpdateCoordinator): +class SynologyDSMUpdateCoordinator(DataUpdateCoordinator[_DataT]): """DataUpdateCoordinator base class for synology_dsm.""" def __init__( @@ -46,7 +47,9 @@ class SynologyDSMUpdateCoordinator(DataUpdateCoordinator): ) -class SynologyDSMSwitchUpdateCoordinator(SynologyDSMUpdateCoordinator): +class SynologyDSMSwitchUpdateCoordinator( + SynologyDSMUpdateCoordinator[dict[str, dict[str, Any]]] +): """DataUpdateCoordinator to gather data for a synology_dsm switch devices.""" def __init__( @@ -78,7 +81,7 @@ class SynologyDSMSwitchUpdateCoordinator(SynologyDSMUpdateCoordinator): } -class SynologyDSMCentralUpdateCoordinator(SynologyDSMUpdateCoordinator): +class SynologyDSMCentralUpdateCoordinator(SynologyDSMUpdateCoordinator[None]): """DataUpdateCoordinator to gather data for a synology_dsm central device.""" def __init__( @@ -106,7 +109,9 @@ class SynologyDSMCentralUpdateCoordinator(SynologyDSMUpdateCoordinator): return None -class SynologyDSMCameraUpdateCoordinator(SynologyDSMUpdateCoordinator): +class SynologyDSMCameraUpdateCoordinator( + SynologyDSMUpdateCoordinator[dict[str, dict[str, SynoCamera]]] +): """DataUpdateCoordinator to gather data for a synology_dsm cameras.""" def __init__( diff --git a/homeassistant/components/synology_dsm/entity.py b/homeassistant/components/synology_dsm/entity.py index 1404d933020..0cc17222802 100644 --- a/homeassistant/components/synology_dsm/entity.py +++ b/homeassistant/components/synology_dsm/entity.py @@ -2,16 +2,19 @@ from __future__ import annotations from dataclasses import dataclass -from typing import Any +from typing import Any, TypeVar from homeassistant.helpers.entity import DeviceInfo, EntityDescription -from homeassistant.helpers.update_coordinator import ( - CoordinatorEntity, - DataUpdateCoordinator, -) +from homeassistant.helpers.update_coordinator import CoordinatorEntity from .common import SynoApi from .const import ATTRIBUTION, DOMAIN +from .coordinator import ( + SynologyDSMCentralUpdateCoordinator, + SynologyDSMUpdateCoordinator, +) + +_CoordinatorT = TypeVar("_CoordinatorT", bound=SynologyDSMUpdateCoordinator[Any]) @dataclass @@ -26,9 +29,7 @@ class SynologyDSMEntityDescription(EntityDescription, SynologyDSMRequiredKeysMix """Generic Synology DSM entity description.""" -class SynologyDSMBaseEntity( - CoordinatorEntity[DataUpdateCoordinator[dict[str, dict[str, Any]]]] -): +class SynologyDSMBaseEntity(CoordinatorEntity[_CoordinatorT]): """Representation of a Synology NAS entry.""" entity_description: SynologyDSMEntityDescription @@ -38,7 +39,7 @@ class SynologyDSMBaseEntity( def __init__( self, api: SynoApi, - coordinator: DataUpdateCoordinator[dict[str, dict[str, Any]]], + coordinator: _CoordinatorT, description: SynologyDSMEntityDescription, ) -> None: """Initialize the Synology DSM entity.""" @@ -67,13 +68,15 @@ class SynologyDSMBaseEntity( await super().async_added_to_hass() -class SynologyDSMDeviceEntity(SynologyDSMBaseEntity): +class SynologyDSMDeviceEntity( + SynologyDSMBaseEntity[SynologyDSMCentralUpdateCoordinator] +): """Representation of a Synology NAS disk or volume entry.""" def __init__( self, api: SynoApi, - coordinator: DataUpdateCoordinator[dict[str, dict[str, Any]]], + coordinator: SynologyDSMCentralUpdateCoordinator, description: SynologyDSMEntityDescription, device_id: str | None = None, ) -> None: diff --git a/homeassistant/components/synology_dsm/sensor.py b/homeassistant/components/synology_dsm/sensor.py index 6a2a92b9fd5..0a0779b1979 100644 --- a/homeassistant/components/synology_dsm/sensor.py +++ b/homeassistant/components/synology_dsm/sensor.py @@ -18,20 +18,19 @@ from homeassistant.components.sensor import ( from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONF_DISKS, - DATA_MEGABYTES, - DATA_RATE_KILOBYTES_PER_SECOND, - DATA_TERABYTES, PERCENTAGE, - TEMP_CELSIUS, + UnitOfDataRate, + UnitOfInformation, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from homeassistant.util.dt import utcnow from . import SynoApi from .const import CONF_VOLUMES, DOMAIN, ENTITY_UNIT_LOAD +from .coordinator import SynologyDSMCentralUpdateCoordinator from .entity import ( SynologyDSMBaseEntity, SynologyDSMDeviceEntity, @@ -116,7 +115,8 @@ UTILISATION_SENSORS: tuple[SynologyDSMSensorEntityDescription, ...] = ( api_key=SynoCoreUtilization.API_KEY, key="memory_size", name="Memory Size", - native_unit_of_measurement=DATA_MEGABYTES, + native_unit_of_measurement=UnitOfInformation.MEGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:memory", entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, @@ -125,7 +125,8 @@ UTILISATION_SENSORS: tuple[SynologyDSMSensorEntityDescription, ...] = ( api_key=SynoCoreUtilization.API_KEY, key="memory_cached", name="Memory Cached", - native_unit_of_measurement=DATA_MEGABYTES, + native_unit_of_measurement=UnitOfInformation.MEGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:memory", entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, @@ -134,7 +135,8 @@ UTILISATION_SENSORS: tuple[SynologyDSMSensorEntityDescription, ...] = ( api_key=SynoCoreUtilization.API_KEY, key="memory_available_swap", name="Memory Available (Swap)", - native_unit_of_measurement=DATA_MEGABYTES, + native_unit_of_measurement=UnitOfInformation.MEGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:memory", state_class=SensorStateClass.MEASUREMENT, ), @@ -142,7 +144,8 @@ UTILISATION_SENSORS: tuple[SynologyDSMSensorEntityDescription, ...] = ( api_key=SynoCoreUtilization.API_KEY, key="memory_available_real", name="Memory Available (Real)", - native_unit_of_measurement=DATA_MEGABYTES, + native_unit_of_measurement=UnitOfInformation.MEGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:memory", state_class=SensorStateClass.MEASUREMENT, ), @@ -150,7 +153,8 @@ UTILISATION_SENSORS: tuple[SynologyDSMSensorEntityDescription, ...] = ( api_key=SynoCoreUtilization.API_KEY, key="memory_total_swap", name="Memory Total (Swap)", - native_unit_of_measurement=DATA_MEGABYTES, + native_unit_of_measurement=UnitOfInformation.MEGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:memory", state_class=SensorStateClass.MEASUREMENT, ), @@ -158,7 +162,8 @@ UTILISATION_SENSORS: tuple[SynologyDSMSensorEntityDescription, ...] = ( api_key=SynoCoreUtilization.API_KEY, key="memory_total_real", name="Memory Total (Real)", - native_unit_of_measurement=DATA_MEGABYTES, + native_unit_of_measurement=UnitOfInformation.MEGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:memory", state_class=SensorStateClass.MEASUREMENT, ), @@ -166,7 +171,8 @@ UTILISATION_SENSORS: tuple[SynologyDSMSensorEntityDescription, ...] = ( api_key=SynoCoreUtilization.API_KEY, key="network_up", name="Upload Throughput", - native_unit_of_measurement=DATA_RATE_KILOBYTES_PER_SECOND, + native_unit_of_measurement=UnitOfDataRate.KILOBYTES_PER_SECOND, + device_class=SensorDeviceClass.DATA_RATE, icon="mdi:upload", state_class=SensorStateClass.MEASUREMENT, ), @@ -174,7 +180,8 @@ UTILISATION_SENSORS: tuple[SynologyDSMSensorEntityDescription, ...] = ( api_key=SynoCoreUtilization.API_KEY, key="network_down", name="Download Throughput", - native_unit_of_measurement=DATA_RATE_KILOBYTES_PER_SECOND, + native_unit_of_measurement=UnitOfDataRate.KILOBYTES_PER_SECOND, + device_class=SensorDeviceClass.DATA_RATE, icon="mdi:download", state_class=SensorStateClass.MEASUREMENT, ), @@ -190,7 +197,8 @@ STORAGE_VOL_SENSORS: tuple[SynologyDSMSensorEntityDescription, ...] = ( api_key=SynoStorage.API_KEY, key="volume_size_total", name="Total Size", - native_unit_of_measurement=DATA_TERABYTES, + native_unit_of_measurement=UnitOfInformation.TERABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:chart-pie", entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, @@ -199,7 +207,8 @@ STORAGE_VOL_SENSORS: tuple[SynologyDSMSensorEntityDescription, ...] = ( api_key=SynoStorage.API_KEY, key="volume_size_used", name="Used Space", - native_unit_of_measurement=DATA_TERABYTES, + native_unit_of_measurement=UnitOfInformation.TERABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:chart-pie", state_class=SensorStateClass.MEASUREMENT, ), @@ -214,7 +223,7 @@ STORAGE_VOL_SENSORS: tuple[SynologyDSMSensorEntityDescription, ...] = ( api_key=SynoStorage.API_KEY, key="volume_disk_temp_avg", name="Average Disk Temp", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, entity_category=EntityCategory.DIAGNOSTIC, ), @@ -222,7 +231,7 @@ STORAGE_VOL_SENSORS: tuple[SynologyDSMSensorEntityDescription, ...] = ( api_key=SynoStorage.API_KEY, key="volume_disk_temp_max", name="Maximum Disk Temp", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, entity_registry_enabled_default=False, entity_category=EntityCategory.DIAGNOSTIC, @@ -248,7 +257,7 @@ STORAGE_DISK_SENSORS: tuple[SynologyDSMSensorEntityDescription, ...] = ( api_key=SynoStorage.API_KEY, key="disk_temp", name="Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, @@ -260,7 +269,7 @@ INFORMATION_SENSORS: tuple[SynologyDSMSensorEntityDescription, ...] = ( api_key=SynoDSMInformation.API_KEY, key="temperature", name="Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, @@ -319,7 +328,9 @@ async def async_setup_entry( async_add_entities(entities) -class SynoDSMSensor(SynologyDSMBaseEntity, SensorEntity): +class SynoDSMSensor( + SynologyDSMBaseEntity[SynologyDSMCentralUpdateCoordinator], SensorEntity +): """Mixin for sensor specific attributes.""" entity_description: SynologyDSMSensorEntityDescription @@ -327,7 +338,7 @@ class SynoDSMSensor(SynologyDSMBaseEntity, SensorEntity): def __init__( self, api: SynoApi, - coordinator: DataUpdateCoordinator[dict[str, dict[str, Any]]], + coordinator: SynologyDSMCentralUpdateCoordinator, description: SynologyDSMSensorEntityDescription, ) -> None: """Initialize the Synology DSM sensor entity.""" @@ -347,11 +358,11 @@ class SynoDSMUtilSensor(SynoDSMSensor): return None # Data (RAM) - if self.native_unit_of_measurement == DATA_MEGABYTES: + if self.native_unit_of_measurement == UnitOfInformation.MEGABYTES: return round(attr / 1024.0**2, 1) # Network - if self.native_unit_of_measurement == DATA_RATE_KILOBYTES_PER_SECOND: + if self.native_unit_of_measurement == UnitOfDataRate.KILOBYTES_PER_SECOND: return round(attr / 1024.0, 1) # CPU load average @@ -374,7 +385,7 @@ class SynoDSMStorageSensor(SynologyDSMDeviceEntity, SynoDSMSensor): def __init__( self, api: SynoApi, - coordinator: DataUpdateCoordinator[dict[str, dict[str, Any]]], + coordinator: SynologyDSMCentralUpdateCoordinator, description: SynologyDSMSensorEntityDescription, device_id: str | None = None, ) -> None: @@ -389,7 +400,7 @@ class SynoDSMStorageSensor(SynologyDSMDeviceEntity, SynoDSMSensor): return None # Data (disk space) - if self.native_unit_of_measurement == DATA_TERABYTES: + if self.native_unit_of_measurement == UnitOfInformation.TERABYTES: return round(attr / 1024.0**4, 2) return attr @@ -401,7 +412,7 @@ class SynoDSMInfoSensor(SynoDSMSensor): def __init__( self, api: SynoApi, - coordinator: DataUpdateCoordinator[dict[str, dict[str, Any]]], + coordinator: SynologyDSMCentralUpdateCoordinator, description: SynologyDSMSensorEntityDescription, ) -> None: """Initialize the Synology SynoDSMInfoSensor entity.""" diff --git a/homeassistant/components/synology_dsm/service.py b/homeassistant/components/synology_dsm/service.py index 0cb2bf7d822..9797b808617 100644 --- a/homeassistant/components/synology_dsm/service.py +++ b/homeassistant/components/synology_dsm/service.py @@ -43,7 +43,10 @@ async def async_setup_services(hass: HomeAssistant) -> None: return LOGGER.debug("%s DSM with serial %s", call.service, serial) LOGGER.warning( - "The %s service is deprecated and will be removed in future release. Please use the corresponding button entity", + ( + "The %s service is deprecated and will be removed in future" + " release. Please use the corresponding button entity" + ), call.service, ) dsm_device = hass.data[DOMAIN][serial] diff --git a/homeassistant/components/synology_dsm/strings.json b/homeassistant/components/synology_dsm/strings.json index 6b13226aaee..09574f82f9e 100644 --- a/homeassistant/components/synology_dsm/strings.json +++ b/homeassistant/components/synology_dsm/strings.json @@ -19,7 +19,7 @@ } }, "link": { - "description": "Do you want to setup {name} ({host})?", + "description": "Do you want to set up {name} ({host})?", "data": { "ssl": "[%key:common::config_flow::data::ssl%]", "verify_ssl": "[%key:common::config_flow::data::verify_ssl%]", diff --git a/homeassistant/components/synology_dsm/switch.py b/homeassistant/components/synology_dsm/switch.py index 26909ceddd9..245b8804488 100644 --- a/homeassistant/components/synology_dsm/switch.py +++ b/homeassistant/components/synology_dsm/switch.py @@ -12,10 +12,10 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from . import SynoApi from .const import DOMAIN +from .coordinator import SynologyDSMSwitchUpdateCoordinator from .entity import SynologyDSMBaseEntity, SynologyDSMEntityDescription from .models import SynologyDSMData @@ -54,17 +54,18 @@ async def async_setup_entry( ) -class SynoDSMSurveillanceHomeModeToggle(SynologyDSMBaseEntity, SwitchEntity): +class SynoDSMSurveillanceHomeModeToggle( + SynologyDSMBaseEntity[SynologyDSMSwitchUpdateCoordinator], SwitchEntity +): """Representation a Synology Surveillance Station Home Mode toggle.""" - coordinator: DataUpdateCoordinator[dict[str, dict[str, bool]]] entity_description: SynologyDSMSwitchEntityDescription def __init__( self, api: SynoApi, version: str, - coordinator: DataUpdateCoordinator[dict[str, dict[str, bool]]], + coordinator: SynologyDSMSwitchUpdateCoordinator, description: SynologyDSMSwitchEntityDescription, ) -> None: """Initialize a Synology Surveillance Station Home Mode.""" @@ -78,7 +79,7 @@ class SynoDSMSurveillanceHomeModeToggle(SynologyDSMBaseEntity, SwitchEntity): @property def is_on(self) -> bool: """Return the state.""" - return self.coordinator.data["switches"][self.entity_description.key] + return self.coordinator.data["switches"][self.entity_description.key] # type: ignore[no-any-return] async def async_turn_on(self, **kwargs: Any) -> None: """Turn on Home mode.""" diff --git a/homeassistant/components/synology_dsm/translations/en.json b/homeassistant/components/synology_dsm/translations/en.json index 267eb772e13..72eec8ff461 100644 --- a/homeassistant/components/synology_dsm/translations/en.json +++ b/homeassistant/components/synology_dsm/translations/en.json @@ -28,7 +28,7 @@ "username": "Username", "verify_ssl": "Verify SSL certificate" }, - "description": "Do you want to setup {name} ({host})?" + "description": "Do you want to set up {name} ({host})?" }, "reauth_confirm": { "data": { diff --git a/homeassistant/components/synology_dsm/translations/it.json b/homeassistant/components/synology_dsm/translations/it.json index e0337ffaea9..d7be58085e6 100644 --- a/homeassistant/components/synology_dsm/translations/it.json +++ b/homeassistant/components/synology_dsm/translations/it.json @@ -28,7 +28,7 @@ "username": "Nome utente", "verify_ssl": "Verifica il certificato SSL" }, - "description": "Vuoi impostare {name} ({host})?" + "description": "Vuoi configurare {name} ({host})?" }, "reauth_confirm": { "data": { diff --git a/homeassistant/components/synology_dsm/translations/ko.json b/homeassistant/components/synology_dsm/translations/ko.json index 5de99988192..69f477ee92a 100644 --- a/homeassistant/components/synology_dsm/translations/ko.json +++ b/homeassistant/components/synology_dsm/translations/ko.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" }, "error": { "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", @@ -28,6 +29,13 @@ }, "description": "{name} ({host})\uc744(\ub97c) \uc124\uc815\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" }, + "reauth_confirm": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + }, + "title": "\uc2dc\ub180\ub85c\uc9c0 DSM \ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" + }, "user": { "data": { "host": "\ud638\uc2a4\ud2b8", diff --git a/homeassistant/components/synology_dsm/translations/no.json b/homeassistant/components/synology_dsm/translations/no.json index 8ce16f6019d..3e0b77d51f9 100644 --- a/homeassistant/components/synology_dsm/translations/no.json +++ b/homeassistant/components/synology_dsm/translations/no.json @@ -28,7 +28,7 @@ "username": "Brukernavn", "verify_ssl": "Verifisere SSL-sertifikat" }, - "description": "Vil du konfigurere {name} ({host})?" + "description": "Vil du sette opp {name} ( {host} )?" }, "reauth_confirm": { "data": { diff --git a/homeassistant/components/synology_dsm/translations/pt.json b/homeassistant/components/synology_dsm/translations/pt.json index 66df18026ea..284e5d6e919 100644 --- a/homeassistant/components/synology_dsm/translations/pt.json +++ b/homeassistant/components/synology_dsm/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, diff --git a/homeassistant/components/synology_dsm/translations/sk.json b/homeassistant/components/synology_dsm/translations/sk.json index a530e78e9b8..b50e70872b5 100644 --- a/homeassistant/components/synology_dsm/translations/sk.json +++ b/homeassistant/components/synology_dsm/translations/sk.json @@ -2,11 +2,14 @@ "config": { "abort": { "already_configured": "Zariadenie je u\u017e nakonfigurovan\u00e9", - "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", + "reconfigure_successful": "Rekonfigur\u00e1cia bola \u00faspe\u0161n\u00e1" }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie", + "missing_data": "Ch\u00fdbaj\u00face \u00fadaje: sk\u00faste to znova nesk\u00f4r alebo pou\u017eite in\u00fa konfigur\u00e1ciu", + "otp_failed": "Dvojstup\u0148ov\u00e9 overenie zlyhalo, sk\u00faste to znova s nov\u00fdm pr\u00edstupov\u00fdm k\u00f3dom", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "flow_title": "{name} ({host})", @@ -14,7 +17,8 @@ "2sa": { "data": { "otp_code": "K\u00f3d" - } + }, + "title": "Synology DSM: dvojstup\u0148ov\u00e9 overenie" }, "link": { "data": { @@ -49,7 +53,9 @@ "step": { "init": { "data": { - "scan_interval": "Min\u00faty medzi skenovaniami" + "scan_interval": "Min\u00faty medzi skenovaniami", + "snap_profile_type": "\u00darove\u0148 kvality sn\u00edmok kamery (0:vysok\u00e1 1:stredn\u00e1 2:n\u00edzka)", + "timeout": "\u010casov\u00fd limit (v sekund\u00e1ch)" } } } diff --git a/homeassistant/components/synology_dsm/update.py b/homeassistant/components/synology_dsm/update.py index 445e682651c..18200fb30d9 100644 --- a/homeassistant/components/synology_dsm/update.py +++ b/homeassistant/components/synology_dsm/update.py @@ -14,6 +14,7 @@ from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import DOMAIN +from .coordinator import SynologyDSMCentralUpdateCoordinator from .entity import SynologyDSMBaseEntity, SynologyDSMEntityDescription from .models import SynologyDSMData @@ -46,7 +47,9 @@ async def async_setup_entry( ) -class SynoDSMUpdateEntity(SynologyDSMBaseEntity, UpdateEntity): +class SynoDSMUpdateEntity( + SynologyDSMBaseEntity[SynologyDSMCentralUpdateCoordinator], UpdateEntity +): """Mixin for update entity specific attributes.""" entity_description: SynologyDSMUpdateEntityEntityDescription diff --git a/homeassistant/components/system_bridge/__init__.py b/homeassistant/components/system_bridge/__init__.py index 392273dee45..a8d3a4372ca 100644 --- a/homeassistant/components/system_bridge/__init__.py +++ b/homeassistant/components/system_bridge/__init__.py @@ -66,7 +66,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: try: if not await version.check_supported(): raise ConfigEntryNotReady( - f"You are not running a supported version of System Bridge. Please update to {SUPPORTED_VERSION} or higher." + "You are not running a supported version of System Bridge. Please" + f" update to {SUPPORTED_VERSION} or higher." ) except AuthenticationException as exception: _LOGGER.error("Authentication failed for %s: %s", entry.title, exception) diff --git a/homeassistant/components/system_bridge/sensor.py b/homeassistant/components/system_bridge/sensor.py index ab34e6b91b5..bc02c9f1cda 100644 --- a/homeassistant/components/system_bridge/sensor.py +++ b/homeassistant/components/system_bridge/sensor.py @@ -15,15 +15,13 @@ from homeassistant.components.sensor import ( from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONF_PORT, - DATA_GIGABYTES, - ELECTRIC_POTENTIAL_VOLT, - FREQUENCY_GIGAHERTZ, - FREQUENCY_HERTZ, - FREQUENCY_MEGAHERTZ, PERCENTAGE, - POWER_WATT, REVOLUTIONS_PER_MINUTE, - TEMP_CELSIUS, + UnitOfElectricPotential, + UnitOfFrequency, + UnitOfInformation, + UnitOfPower, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -138,7 +136,8 @@ BASE_SENSOR_TYPES: tuple[SystemBridgeSensorEntityDescription, ...] = ( key="cpu_speed", name="CPU Speed", state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=FREQUENCY_GIGAHERTZ, + native_unit_of_measurement=UnitOfFrequency.GIGAHERTZ, + device_class=SensorDeviceClass.FREQUENCY, icon="mdi:speedometer", value=cpu_speed, ), @@ -148,7 +147,7 @@ BASE_SENSOR_TYPES: tuple[SystemBridgeSensorEntityDescription, ...] = ( entity_registry_enabled_default=False, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, value=lambda data: data.cpu.temperature, ), SystemBridgeSensorEntityDescription( @@ -157,7 +156,7 @@ BASE_SENSOR_TYPES: tuple[SystemBridgeSensorEntityDescription, ...] = ( entity_registry_enabled_default=False, device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, value=lambda data: data.cpu.voltage, ), SystemBridgeSensorEntityDescription( @@ -171,7 +170,8 @@ BASE_SENSOR_TYPES: tuple[SystemBridgeSensorEntityDescription, ...] = ( key="memory_free", name="Memory Free", state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=DATA_GIGABYTES, + native_unit_of_measurement=UnitOfInformation.GIGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:memory", value=memory_free, ), @@ -188,7 +188,8 @@ BASE_SENSOR_TYPES: tuple[SystemBridgeSensorEntityDescription, ...] = ( name="Memory Used", entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=DATA_GIGABYTES, + native_unit_of_measurement=UnitOfInformation.GIGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:memory", value=memory_used, ), @@ -345,7 +346,8 @@ async def async_setup_entry( key=f"display_{display['name']}_refresh_rate", name=f"Display {display['name']} Refresh Rate", state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=FREQUENCY_HERTZ, + native_unit_of_measurement=UnitOfFrequency.HERTZ, + device_class=SensorDeviceClass.FREQUENCY, icon="mdi:monitor", value=lambda data, k=display["key"]: getattr( data.display, f"{k}_refresh_rate" @@ -374,7 +376,8 @@ async def async_setup_entry( name=f"{gpu['name']} Clock Speed", entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=FREQUENCY_MEGAHERTZ, + native_unit_of_measurement=UnitOfFrequency.MEGAHERTZ, + device_class=SensorDeviceClass.FREQUENCY, icon="mdi:speedometer", value=lambda data, k=gpu["key"]: gpu_core_clock_speed(data, k), ), @@ -387,7 +390,8 @@ async def async_setup_entry( name=f"{gpu['name']} Memory Clock Speed", entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=FREQUENCY_MEGAHERTZ, + native_unit_of_measurement=UnitOfFrequency.MEGAHERTZ, + device_class=SensorDeviceClass.FREQUENCY, icon="mdi:speedometer", value=lambda data, k=gpu["key"]: gpu_memory_clock_speed(data, k), ), @@ -399,7 +403,8 @@ async def async_setup_entry( key=f"gpu_{index}_memory_free", name=f"{gpu['name']} Memory Free", state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=DATA_GIGABYTES, + native_unit_of_measurement=UnitOfInformation.GIGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:memory", value=lambda data, k=gpu["key"]: gpu_memory_free(data, k), ), @@ -426,7 +431,8 @@ async def async_setup_entry( name=f"{gpu['name']} Memory Used", entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=DATA_GIGABYTES, + native_unit_of_measurement=UnitOfInformation.GIGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:memory", value=lambda data, k=gpu["key"]: gpu_memory_used(data, k), ), @@ -455,7 +461,7 @@ async def async_setup_entry( entity_registry_enabled_default=False, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, value=lambda data, k=gpu["key"]: getattr(data.gpu, f"{k}_power"), ), entry.data[CONF_PORT], @@ -468,7 +474,7 @@ async def async_setup_entry( entity_registry_enabled_default=False, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, value=lambda data, k=gpu["key"]: getattr( data.gpu, f"{k}_temperature" ), diff --git a/homeassistant/components/system_bridge/translations/sk.json b/homeassistant/components/system_bridge/translations/sk.json index 97b3c8c2677..6f553361042 100644 --- a/homeassistant/components/system_bridge/translations/sk.json +++ b/homeassistant/components/system_bridge/translations/sk.json @@ -15,7 +15,8 @@ "authenticate": { "data": { "api_key": "API k\u013e\u00fa\u010d" - } + }, + "description": "Zadajte k\u013e\u00fa\u010d API, ktor\u00fd ste nastavili vo svojej konfigur\u00e1cii pre {name}." }, "user": { "data": { diff --git a/homeassistant/components/system_health/__init__.py b/homeassistant/components/system_health/__init__.py index 4821537fc8b..9f45108d61e 100644 --- a/homeassistant/components/system_health/__init__.py +++ b/homeassistant/components/system_health/__init__.py @@ -37,7 +37,8 @@ def async_register_info( Deprecated. """ _LOGGER.warning( - "Calling system_health.async_register_info is deprecated; Add a system_health platform instead" + "Calling system_health.async_register_info is deprecated; Add a system_health" + " platform instead" ) hass.data.setdefault(DOMAIN, {}) SystemHealthRegistration(hass, domain).async_register_info(info_callback) diff --git a/homeassistant/components/systemmonitor/sensor.py b/homeassistant/components/systemmonitor/sensor.py index d16e2ac3190..8dc04e7da86 100644 --- a/homeassistant/components/systemmonitor/sensor.py +++ b/homeassistant/components/systemmonitor/sensor.py @@ -25,14 +25,13 @@ from homeassistant.const import ( CONF_RESOURCES, CONF_SCAN_INTERVAL, CONF_TYPE, - DATA_GIBIBYTES, - DATA_MEBIBYTES, - DATA_RATE_MEGABYTES_PER_SECOND, EVENT_HOMEASSISTANT_STOP, PERCENTAGE, STATE_OFF, STATE_ON, - TEMP_CELSIUS, + UnitOfDataRate, + UnitOfInformation, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant, callback import homeassistant.helpers.config_validation as cv @@ -76,14 +75,16 @@ SENSOR_TYPES: dict[str, SysMonitorSensorEntityDescription] = { "disk_free": SysMonitorSensorEntityDescription( key="disk_free", name="Disk free", - native_unit_of_measurement=DATA_GIBIBYTES, + native_unit_of_measurement=UnitOfInformation.GIBIBYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:harddisk", state_class=SensorStateClass.MEASUREMENT, ), "disk_use": SysMonitorSensorEntityDescription( key="disk_use", name="Disk use", - native_unit_of_measurement=DATA_GIBIBYTES, + native_unit_of_measurement=UnitOfInformation.GIBIBYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:harddisk", state_class=SensorStateClass.MEASUREMENT, ), @@ -132,14 +133,16 @@ SENSOR_TYPES: dict[str, SysMonitorSensorEntityDescription] = { "memory_free": SysMonitorSensorEntityDescription( key="memory_free", name="Memory free", - native_unit_of_measurement=DATA_MEBIBYTES, + native_unit_of_measurement=UnitOfInformation.MEBIBYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:memory", state_class=SensorStateClass.MEASUREMENT, ), "memory_use": SysMonitorSensorEntityDescription( key="memory_use", name="Memory use", - native_unit_of_measurement=DATA_MEBIBYTES, + native_unit_of_measurement=UnitOfInformation.MEBIBYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:memory", state_class=SensorStateClass.MEASUREMENT, ), @@ -153,7 +156,8 @@ SENSOR_TYPES: dict[str, SysMonitorSensorEntityDescription] = { "network_in": SysMonitorSensorEntityDescription( key="network_in", name="Network in", - native_unit_of_measurement=DATA_MEBIBYTES, + native_unit_of_measurement=UnitOfInformation.MEBIBYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:server-network", state_class=SensorStateClass.TOTAL_INCREASING, mandatory_arg=True, @@ -161,7 +165,8 @@ SENSOR_TYPES: dict[str, SysMonitorSensorEntityDescription] = { "network_out": SysMonitorSensorEntityDescription( key="network_out", name="Network out", - native_unit_of_measurement=DATA_MEBIBYTES, + native_unit_of_measurement=UnitOfInformation.MEBIBYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:server-network", state_class=SensorStateClass.TOTAL_INCREASING, mandatory_arg=True, @@ -183,16 +188,16 @@ SENSOR_TYPES: dict[str, SysMonitorSensorEntityDescription] = { "throughput_network_in": SysMonitorSensorEntityDescription( key="throughput_network_in", name="Network throughput in", - native_unit_of_measurement=DATA_RATE_MEGABYTES_PER_SECOND, - icon="mdi:server-network", + native_unit_of_measurement=UnitOfDataRate.MEGABYTES_PER_SECOND, + device_class=SensorDeviceClass.DATA_RATE, state_class=SensorStateClass.MEASUREMENT, mandatory_arg=True, ), "throughput_network_out": SysMonitorSensorEntityDescription( key="throughput_network_out", name="Network throughput out", - native_unit_of_measurement=DATA_RATE_MEGABYTES_PER_SECOND, - icon="mdi:server-network", + native_unit_of_measurement=UnitOfDataRate.MEGABYTES_PER_SECOND, + device_class=SensorDeviceClass.DATA_RATE, state_class=SensorStateClass.MEASUREMENT, mandatory_arg=True, ), @@ -213,21 +218,23 @@ SENSOR_TYPES: dict[str, SysMonitorSensorEntityDescription] = { "processor_temperature": SysMonitorSensorEntityDescription( key="processor_temperature", name="Processor temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), "swap_free": SysMonitorSensorEntityDescription( key="swap_free", name="Swap free", - native_unit_of_measurement=DATA_MEBIBYTES, + native_unit_of_measurement=UnitOfInformation.MEBIBYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:harddisk", state_class=SensorStateClass.MEASUREMENT, ), "swap_use": SysMonitorSensorEntityDescription( key="swap_use", name="Swap use", - native_unit_of_measurement=DATA_MEBIBYTES, + native_unit_of_measurement=UnitOfInformation.MEBIBYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:harddisk", state_class=SensorStateClass.MEASUREMENT, ), @@ -400,7 +407,10 @@ async def async_setup_sensor_registry_updates( """Update all sensors in one executor jump.""" if _update_lock.locked(): _LOGGER.warning( - "Updating systemmonitor took longer than the scheduled update interval %s", + ( + "Updating systemmonitor took longer than the scheduled update" + " interval %s" + ), scan_interval, ) return diff --git a/homeassistant/components/tado/__init__.py b/homeassistant/components/tado/__init__.py index 4b4d46883ce..9146d4d83d1 100644 --- a/homeassistant/components/tado/__init__.py +++ b/homeassistant/components/tado/__init__.py @@ -282,7 +282,10 @@ class TadoConnector: ): """Set a zone overlay.""" _LOGGER.debug( - "Set overlay for zone %s: overlay_mode=%s, temp=%s, duration=%s, type=%s, mode=%s fan_speed=%s swing=%s", + ( + "Set overlay for zone %s: overlay_mode=%s, temp=%s, duration=%s," + " type=%s, mode=%s fan_speed=%s swing=%s" + ), zone_id, overlay_mode, temperature, diff --git a/homeassistant/components/tado/climate.py b/homeassistant/components/tado/climate.py index 0e8b968ca7c..b913993a4e1 100644 --- a/homeassistant/components/tado/climate.py +++ b/homeassistant/components/tado/climate.py @@ -16,7 +16,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, PRECISION_TENTHS, TEMP_CELSIUS +from homeassistant.const import ATTR_TEMPERATURE, PRECISION_TENTHS, UnitOfTemperature from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import config_validation as cv, entity_platform from homeassistant.helpers.dispatcher import async_dispatcher_connect @@ -213,7 +213,7 @@ def create_climate_entity(tado, name: str, zone_id: int, device_info: dict): class TadoClimate(TadoZoneEntity, ClimateEntity): """Representation of a Tado climate entity.""" - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS def __init__( self, @@ -598,7 +598,10 @@ class TadoClimate(TadoZoneEntity, ClimateEntity): ) _LOGGER.debug( - "Switching to %s for zone %s (%d) with temperature %s °C and duration %s using overlay %s", + ( + "Switching to %s for zone %s (%d) with temperature %s °C and duration" + " %s using overlay %s" + ), self._current_tado_hvac_mode, self.zone_name, self.zone_id, diff --git a/homeassistant/components/tado/entity.py b/homeassistant/components/tado/entity.py index 40e06313fa8..11de7ceb314 100644 --- a/homeassistant/components/tado/entity.py +++ b/homeassistant/components/tado/entity.py @@ -67,7 +67,9 @@ class TadoZoneEntity(Entity): def device_info(self) -> DeviceInfo: """Return the device_info of the device.""" return DeviceInfo( - configuration_url=f"https://app.tado.com/en/main/home/zoneV2/{self.zone_id}", + configuration_url=( + f"https://app.tado.com/en/main/home/zoneV2/{self.zone_id}" + ), identifiers={(DOMAIN, self._device_zone_id)}, name=self.zone_name, manufacturer=DEFAULT_NAME, diff --git a/homeassistant/components/tado/sensor.py b/homeassistant/components/tado/sensor.py index 6d11e15a9fd..fdcc9c7db3e 100644 --- a/homeassistant/components/tado/sensor.py +++ b/homeassistant/components/tado/sensor.py @@ -7,7 +7,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import PERCENTAGE, TEMP_CELSIUS +from homeassistant.const import PERCENTAGE, UnitOfTemperature from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -139,7 +139,7 @@ class TadoHomeSensor(TadoHomeEntity, SensorEntity): def native_unit_of_measurement(self): """Return the unit of measurement.""" if self.home_variable in ["temperature", "outdoor temperature"]: - return TEMP_CELSIUS + return UnitOfTemperature.CELSIUS if self.home_variable == "solar percentage": return PERCENTAGE if self.home_variable == "weather condition": @@ -248,7 +248,7 @@ class TadoZoneSensor(TadoZoneEntity, SensorEntity): def native_unit_of_measurement(self): """Return the unit of measurement.""" if self.zone_variable == "temperature": - return TEMP_CELSIUS + return UnitOfTemperature.CELSIUS if self.zone_variable == "humidity": return PERCENTAGE if self.zone_variable == "heating": diff --git a/homeassistant/components/tado/translations/pt.json b/homeassistant/components/tado/translations/pt.json index 7953cf5625c..6e1218d3245 100644 --- a/homeassistant/components/tado/translations/pt.json +++ b/homeassistant/components/tado/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, diff --git a/homeassistant/components/tado/translations/sk.json b/homeassistant/components/tado/translations/sk.json index 1ed85afb928..b09f89fc166 100644 --- a/homeassistant/components/tado/translations/sk.json +++ b/homeassistant/components/tado/translations/sk.json @@ -5,15 +5,29 @@ }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "no_homes": "S t\u00fdmto \u00fa\u010dtom tado nie s\u00fa prepojen\u00e9 \u017eiadne dom\u00e1cnosti.", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { "data": { - "password": "Heslo" + "password": "Heslo", + "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" }, "title": "Pripojte sa k svojmu \u00fa\u010dtu Tado" } } + }, + "options": { + "step": { + "init": { + "data": { + "fallback": "Vyberte z\u00e1lo\u017en\u00fd re\u017eim." + }, + "description": "Z\u00e1lo\u017en\u00fd re\u017eim v\u00e1m umo\u017e\u0148uje vybra\u0165 si, kedy sa vr\u00e1tite k inteligentn\u00e9mu rozvrhu z manu\u00e1lneho prekrytia z\u00f3ny. (NEXT_TIME_BLOCK:= Zme\u0148te pri \u010fal\u0161ej zmene inteligentn\u00e9ho rozvrhu; MANUAL:= Neme\u0148te, k\u00fdm nezru\u0161\u00edte; TADO_DEFAULT:= Zme\u0148te na z\u00e1klade v\u00e1\u0161ho nastavenia v aplik\u00e1cii Tado).", + "title": "Upravte mo\u017enosti Tado." + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tado/water_heater.py b/homeassistant/components/tado/water_heater.py index 92551df8109..f7a1dcd0966 100644 --- a/homeassistant/components/tado/water_heater.py +++ b/homeassistant/components/tado/water_heater.py @@ -9,7 +9,7 @@ from homeassistant.components.water_heater import ( WaterHeaterEntityFeature, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import config_validation as cv, entity_platform from homeassistant.helpers.dispatcher import async_dispatcher_connect @@ -199,7 +199,7 @@ class TadoWaterHeater(TadoZoneEntity, WaterHeaterEntity): @property def temperature_unit(self): """Return the unit of measurement used by the platform.""" - return TEMP_CELSIUS + return UnitOfTemperature.CELSIUS @property def min_temp(self): diff --git a/homeassistant/components/tag/translations/sk.json b/homeassistant/components/tag/translations/sk.json new file mode 100644 index 00000000000..55ef9373f14 --- /dev/null +++ b/homeassistant/components/tag/translations/sk.json @@ -0,0 +1,3 @@ +{ + "title": "Zna\u010dka" +} \ No newline at end of file diff --git a/homeassistant/components/tailscale/translations/ko.json b/homeassistant/components/tailscale/translations/ko.json index cd273d60ca0..9f8b2174f25 100644 --- a/homeassistant/components/tailscale/translations/ko.json +++ b/homeassistant/components/tailscale/translations/ko.json @@ -1,11 +1,22 @@ { "config": { + "abort": { + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, "step": { "reauth_confirm": { + "data": { + "api_key": "API \ud0a4" + }, "description": "Tailscale API \ud1a0\ud070\uc740 90\uc77c \ub3d9\uc548 \uc720\ud6a8\ud569\ub2c8\ub2e4. https://login.tailscale.com/admin/settings/authkeys\uc5d0\uc11c \uc0c8\ub85c\uc6b4 Tailscale API \ud0a4\ub97c \uc0dd\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." }, "user": { "data": { + "api_key": "API \ud0a4", "tailnet": "\ud14c\uc77c\ub137" } } diff --git a/homeassistant/components/tailscale/translations/sk.json b/homeassistant/components/tailscale/translations/sk.json index b09983030d2..9749d94778d 100644 --- a/homeassistant/components/tailscale/translations/sk.json +++ b/homeassistant/components/tailscale/translations/sk.json @@ -11,12 +11,15 @@ "reauth_confirm": { "data": { "api_key": "API k\u013e\u00fa\u010d" - } + }, + "description": "Tokeny rozhrania Tailscale API s\u00fa platn\u00e9 90 dn\u00ed. Nov\u00fd k\u013e\u00fa\u010d Tailscale API si m\u00f4\u017eete vytvori\u0165 na https://login.tailscale.com/admin/settings/authkeys." }, "user": { "data": { - "api_key": "API k\u013e\u00fa\u010d" - } + "api_key": "API k\u013e\u00fa\u010d", + "tailnet": "Tailnet" + }, + "description": "T\u00e1to integr\u00e1cia monitoruje va\u0161u sie\u0165 Tailscale, **NESMIE** spr\u00edstupni\u0165 v\u00e1\u0161ho dom\u00e1ceho asistenta cez Tailscale VPN. \n\nNa overenie pomocou slu\u017eby Tailscale si budete musie\u0165 vytvori\u0165 k\u013e\u00fa\u010d API na adrese {authkeys_url}. \n\nTailnet je n\u00e1zov va\u0161ej siete Tailscale. N\u00e1jdete ho v \u013eavom hornom rohu na paneli spr\u00e1vy Tailscale (ved\u013ea loga Tailscale)." } } } diff --git a/homeassistant/components/tankerkoenig/__init__.py b/homeassistant/components/tankerkoenig/__init__.py index 3db67b4c8be..3ffa2ff4576 100644 --- a/homeassistant/components/tankerkoenig/__init__.py +++ b/homeassistant/components/tankerkoenig/__init__.py @@ -7,110 +7,28 @@ from math import ceil import pytankerkoenig from requests.exceptions import RequestException -import voluptuous as vol -from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry -from homeassistant.const import ( - ATTR_ID, - CONF_API_KEY, - CONF_LATITUDE, - CONF_LOCATION, - CONF_LONGITUDE, - CONF_NAME, - CONF_RADIUS, - CONF_SCAN_INTERVAL, - CONF_SHOW_ON_MAP, - Platform, -) +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import ATTR_ID, CONF_API_KEY, CONF_SHOW_ON_MAP, Platform from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady import homeassistant.helpers.config_validation as cv from homeassistant.helpers.device_registry import DeviceEntryType from homeassistant.helpers.entity import DeviceInfo -from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.update_coordinator import ( CoordinatorEntity, DataUpdateCoordinator, UpdateFailed, ) -from .const import ( - CONF_FUEL_TYPES, - CONF_STATIONS, - DEFAULT_RADIUS, - DEFAULT_SCAN_INTERVAL, - DOMAIN, - FUEL_TYPES, -) +from .const import CONF_FUEL_TYPES, CONF_STATIONS, DEFAULT_SCAN_INTERVAL, DOMAIN _LOGGER = logging.getLogger(__name__) -CONFIG_SCHEMA = vol.Schema( - vol.All( - cv.deprecated(DOMAIN), - { - DOMAIN: vol.Schema( - { - vol.Required(CONF_API_KEY): cv.string, - vol.Optional( - CONF_SCAN_INTERVAL, default=DEFAULT_SCAN_INTERVAL - ): cv.time_period, - vol.Optional(CONF_FUEL_TYPES, default=FUEL_TYPES): vol.All( - cv.ensure_list, [vol.In(FUEL_TYPES)] - ), - vol.Inclusive( - CONF_LATITUDE, - "coordinates", - "Latitude and longitude must exist together", - ): cv.latitude, - vol.Inclusive( - CONF_LONGITUDE, - "coordinates", - "Latitude and longitude must exist together", - ): cv.longitude, - vol.Optional(CONF_RADIUS, default=DEFAULT_RADIUS): vol.All( - cv.positive_int, vol.Range(min=1) - ), - vol.Optional(CONF_STATIONS, default=[]): vol.All( - cv.ensure_list, [cv.string] - ), - vol.Optional(CONF_SHOW_ON_MAP, default=True): cv.boolean, - } - ) - }, - ), - extra=vol.ALLOW_EXTRA, -) PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR] - -async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: - """Set the tankerkoenig component up.""" - if DOMAIN not in config: - return True - - conf = config[DOMAIN] - hass.async_create_task( - hass.config_entries.flow.async_init( - DOMAIN, - context={"source": SOURCE_IMPORT}, - data={ - CONF_NAME: "Home", - CONF_API_KEY: conf[CONF_API_KEY], - CONF_FUEL_TYPES: conf[CONF_FUEL_TYPES], - CONF_LOCATION: { - "latitude": conf.get(CONF_LATITUDE, hass.config.latitude), - "longitude": conf.get(CONF_LONGITUDE, hass.config.longitude), - }, - CONF_RADIUS: conf[CONF_RADIUS], - CONF_STATIONS: conf[CONF_STATIONS], - CONF_SHOW_ON_MAP: conf[CONF_SHOW_ON_MAP], - }, - ) - ) - - return True +CONFIG_SCHEMA = cv.removed(DOMAIN, raise_if_present=False) async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: diff --git a/homeassistant/components/tankerkoenig/config_flow.py b/homeassistant/components/tankerkoenig/config_flow.py index e3d273825a5..79f6349f0cb 100644 --- a/homeassistant/components/tankerkoenig/config_flow.py +++ b/homeassistant/components/tankerkoenig/config_flow.py @@ -16,7 +16,7 @@ from homeassistant.const import ( CONF_NAME, CONF_RADIUS, CONF_SHOW_ON_MAP, - LENGTH_KILOMETERS, + UnitOfLength, ) from homeassistant.core import HomeAssistant, callback from homeassistant.data_entry_flow import FlowResult @@ -67,37 +67,6 @@ class FlowHandler(config_entries.ConfigFlow, domain=DOMAIN): """Get the options flow for this handler.""" return OptionsFlowHandler(config_entry) - async def async_step_import(self, config: dict[str, Any]) -> FlowResult: - """Import YAML configuration.""" - await self.async_set_unique_id( - f"{config[CONF_LOCATION][CONF_LATITUDE]}_{config[CONF_LOCATION][CONF_LONGITUDE]}" - ) - self._abort_if_unique_id_configured() - - selected_station_ids: list[str] = [] - # add all nearby stations - nearby_stations = await async_get_nearby_stations(self.hass, config) - for station in nearby_stations.get("stations", []): - selected_station_ids.append(station["id"]) - - # add all manual added stations - for station_id in config[CONF_STATIONS]: - selected_station_ids.append(station_id) - - return self._create_entry( - data={ - CONF_NAME: "Home", - CONF_API_KEY: config[CONF_API_KEY], - CONF_FUEL_TYPES: config[CONF_FUEL_TYPES], - CONF_LOCATION: config[CONF_LOCATION], - CONF_RADIUS: config[CONF_RADIUS], - CONF_STATIONS: selected_station_ids, - }, - options={ - CONF_SHOW_ON_MAP: config[CONF_SHOW_ON_MAP], - }, - ) - async def async_step_user( self, user_input: dict[str, Any] | None = None ) -> FlowResult: @@ -118,9 +87,10 @@ class FlowHandler(config_entries.ConfigFlow, domain=DOMAIN): if len(stations := data.get("stations", [])) == 0: return self._show_form_user(user_input, errors={CONF_RADIUS: "no_stations"}) for station in stations: - self._stations[ - station["id"] - ] = f"{station['brand']} {station['street']} {station['houseNumber']} - ({station['dist']}km)" + self._stations[station["id"]] = ( + f"{station['brand']} {station['street']} {station['houseNumber']} -" + f" ({station['dist']}km)" + ) self._data = user_input @@ -204,7 +174,7 @@ class FlowHandler(config_entries.ConfigFlow, domain=DOMAIN): min=1.0, max=25, step=0.1, - unit_of_measurement=LENGTH_KILOMETERS, + unit_of_measurement=UnitOfLength.KILOMETERS, ), ), } @@ -268,9 +238,10 @@ class OptionsFlowHandler(config_entries.OptionsFlow): ) if stations := nearby_stations.get("stations"): for station in stations: - self._stations[ - station["id"] - ] = f"{station['brand']} {station['street']} {station['houseNumber']} - ({station['dist']}km)" + self._stations[station["id"]] = ( + f"{station['brand']} {station['street']} {station['houseNumber']} -" + f" ({station['dist']}km)" + ) # add possible extra selected stations from import for selected_station in self.config_entry.data[CONF_STATIONS]: diff --git a/homeassistant/components/tankerkoenig/translations/de.json b/homeassistant/components/tankerkoenig/translations/de.json index 2a23f855e57..95e3405e861 100644 --- a/homeassistant/components/tankerkoenig/translations/de.json +++ b/homeassistant/components/tankerkoenig/translations/de.json @@ -6,7 +6,7 @@ }, "error": { "invalid_auth": "Ung\u00fcltige Authentifizierung", - "no_stations": "Konnte keine Station in Reichweite finden." + "no_stations": "Es konnte keine Station in Reichweite gefunden werden" }, "step": { "reauth_confirm": { diff --git a/homeassistant/components/tankerkoenig/translations/ko.json b/homeassistant/components/tankerkoenig/translations/ko.json new file mode 100644 index 00000000000..f3ac166d34f --- /dev/null +++ b/homeassistant/components/tankerkoenig/translations/ko.json @@ -0,0 +1,24 @@ +{ + "config": { + "abort": { + "already_configured": "\uc704\uce58\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "API \ud0a4" + } + }, + "user": { + "data": { + "api_key": "API \ud0a4", + "location": "\uc704\uce58" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tankerkoenig/translations/sk.json b/homeassistant/components/tankerkoenig/translations/sk.json index 0be1d37c18b..f102459e461 100644 --- a/homeassistant/components/tankerkoenig/translations/sk.json +++ b/homeassistant/components/tankerkoenig/translations/sk.json @@ -5,7 +5,8 @@ "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "no_stations": "V dosahu sa nepodarilo n\u00e1js\u0165 \u017eiadnu stanicu." }, "step": { "reauth_confirm": { @@ -16,15 +17,31 @@ "select_station": { "data": { "stations": "Stanice" - } + }, + "description": "n\u00e1jden\u00fdch {stations_count} stan\u00edc v okruhu", + "title": "Vyberte stanice, ktor\u00e9 chcete prida\u0165" }, "user": { "data": { "api_key": "API k\u013e\u00fa\u010d", + "fuel_types": "Druhy paliva", "location": "Umiestnenie", - "radius": "Polomer vyh\u013ead\u00e1vania" + "name": "N\u00e1zov regi\u00f3nu", + "radius": "Polomer vyh\u013ead\u00e1vania", + "stations": "\u010eal\u0161ie \u010derpacie stanice" } } } + }, + "options": { + "step": { + "init": { + "data": { + "show_on_map": "Zobrazi\u0165 stanice na mape", + "stations": "Stanice" + }, + "title": "Mo\u017enosti Tankerkoenig" + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tasmota/discovery.py b/homeassistant/components/tasmota/discovery.py index 8afac859b88..4ab3f780ad3 100644 --- a/homeassistant/components/tasmota/discovery.py +++ b/homeassistant/components/tasmota/discovery.py @@ -85,7 +85,10 @@ def warn_if_topic_duplicated( for _, cfg in offenders ] _LOGGER.warning( - "Multiple Tasmota devices are sharing the same topic '%s'. Offending devices: %s", + ( + "Multiple Tasmota devices are sharing the same topic '%s'. Offending" + " devices: %s" + ), command_topic, ", ".join(offender_strings), ) diff --git a/homeassistant/components/tasmota/fan.py b/homeassistant/components/tasmota/fan.py index 8a1deee7d60..38ba5fcc476 100644 --- a/homeassistant/components/tasmota/fan.py +++ b/homeassistant/components/tasmota/fan.py @@ -65,6 +65,7 @@ class TasmotaFan( """Representation of a Tasmota fan.""" _attr_supported_features = FanEntityFeature.SET_SPEED + _fan_speed = tasmota_const.FAN_SPEED_MEDIUM _tasmota_entity: tasmota_fan.TasmotaFan def __init__(self, **kwds: Any) -> None: @@ -84,6 +85,9 @@ class TasmotaFan( def fan_state_updated(self, state: int, **kwargs: Any) -> None: """Handle state updates.""" self._state = state + if self._state is not None and self._state != 0: + # Store the last known fan speed + self._fan_speed = state self.async_write_ha_state() @property @@ -121,7 +125,7 @@ class TasmotaFan( await self.async_set_percentage( percentage or ordered_list_item_to_percentage( - ORDERED_NAMED_FAN_SPEEDS, tasmota_const.FAN_SPEED_MEDIUM + ORDERED_NAMED_FAN_SPEEDS, self._fan_speed ) ) diff --git a/homeassistant/components/tasmota/sensor.py b/homeassistant/components/tasmota/sensor.py index 09b44eca1ee..4b84bb8f86a 100644 --- a/homeassistant/components/tasmota/sensor.py +++ b/homeassistant/components/tasmota/sensor.py @@ -19,25 +19,21 @@ from homeassistant.const import ( CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, CONCENTRATION_PARTS_PER_BILLION, CONCENTRATION_PARTS_PER_MILLION, - ELECTRIC_CURRENT_AMPERE, - ELECTRIC_POTENTIAL_VOLT, - ENERGY_KILO_WATT_HOUR, - FREQUENCY_HERTZ, - LENGTH_CENTIMETERS, LIGHT_LUX, - MASS_KILOGRAMS, PERCENTAGE, - POWER_VOLT_AMPERE, - POWER_WATT, - PRESSURE_HPA, SIGNAL_STRENGTH_DECIBELS, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, - SPEED_KILOMETERS_PER_HOUR, - SPEED_METERS_PER_SECOND, - SPEED_MILES_PER_HOUR, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, - TEMP_KELVIN, + UnitOfApparentPower, + UnitOfElectricCurrent, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfFrequency, + UnitOfLength, + UnitOfMass, + UnitOfPower, + UnitOfPressure, + UnitOfSpeed, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect @@ -213,25 +209,25 @@ SENSOR_UNIT_MAP = { hc.CONCENTRATION_MICROGRAMS_PER_CUBIC_METER: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, hc.CONCENTRATION_PARTS_PER_BILLION: CONCENTRATION_PARTS_PER_BILLION, hc.CONCENTRATION_PARTS_PER_MILLION: CONCENTRATION_PARTS_PER_MILLION, - hc.ELECTRICAL_CURRENT_AMPERE: ELECTRIC_CURRENT_AMPERE, - hc.ELECTRICAL_VOLT_AMPERE: POWER_VOLT_AMPERE, - hc.ENERGY_KILO_WATT_HOUR: ENERGY_KILO_WATT_HOUR, - hc.FREQUENCY_HERTZ: FREQUENCY_HERTZ, - hc.LENGTH_CENTIMETERS: LENGTH_CENTIMETERS, + hc.ELECTRICAL_CURRENT_AMPERE: UnitOfElectricCurrent.AMPERE, + hc.ELECTRICAL_VOLT_AMPERE: UnitOfApparentPower.VOLT_AMPERE, + hc.ENERGY_KILO_WATT_HOUR: UnitOfEnergy.KILO_WATT_HOUR, + hc.FREQUENCY_HERTZ: UnitOfFrequency.HERTZ, + hc.LENGTH_CENTIMETERS: UnitOfLength.CENTIMETERS, hc.LIGHT_LUX: LIGHT_LUX, - hc.MASS_KILOGRAMS: MASS_KILOGRAMS, + hc.MASS_KILOGRAMS: UnitOfMass.KILOGRAMS, hc.PERCENTAGE: PERCENTAGE, - hc.POWER_WATT: POWER_WATT, - hc.PRESSURE_HPA: PRESSURE_HPA, + hc.POWER_WATT: UnitOfPower.WATT, + hc.PRESSURE_HPA: UnitOfPressure.HPA, hc.SIGNAL_STRENGTH_DECIBELS: SIGNAL_STRENGTH_DECIBELS, hc.SIGNAL_STRENGTH_DECIBELS_MILLIWATT: SIGNAL_STRENGTH_DECIBELS_MILLIWATT, - hc.SPEED_KILOMETERS_PER_HOUR: SPEED_KILOMETERS_PER_HOUR, - hc.SPEED_METERS_PER_SECOND: SPEED_METERS_PER_SECOND, - hc.SPEED_MILES_PER_HOUR: SPEED_MILES_PER_HOUR, - hc.TEMP_CELSIUS: TEMP_CELSIUS, - hc.TEMP_FAHRENHEIT: TEMP_FAHRENHEIT, - hc.TEMP_KELVIN: TEMP_KELVIN, - hc.VOLT: ELECTRIC_POTENTIAL_VOLT, + hc.SPEED_KILOMETERS_PER_HOUR: UnitOfSpeed.KILOMETERS_PER_HOUR, + hc.SPEED_METERS_PER_SECOND: UnitOfSpeed.METERS_PER_SECOND, + hc.SPEED_MILES_PER_HOUR: UnitOfSpeed.MILES_PER_HOUR, + hc.TEMP_CELSIUS: UnitOfTemperature.CELSIUS, + hc.TEMP_FAHRENHEIT: UnitOfTemperature.FAHRENHEIT, + hc.TEMP_KELVIN: UnitOfTemperature.KELVIN, + hc.VOLT: UnitOfElectricPotential.VOLT, } @@ -294,7 +290,7 @@ class TasmotaSensor(TasmotaAvailability, TasmotaDiscoveryUpdate, SensorEntity): self.async_write_ha_state() @property - def device_class(self) -> str | None: + def device_class(self) -> SensorDeviceClass | None: """Return the device class of the sensor.""" class_or_icon = SENSOR_DEVICE_CLASS_ICON_MAP.get( self._tasmota_entity.quantity, {} diff --git a/homeassistant/components/tasmota/translations/sk.json b/homeassistant/components/tasmota/translations/sk.json index c294bc45d7c..491adc6a697 100644 --- a/homeassistant/components/tasmota/translations/sk.json +++ b/homeassistant/components/tasmota/translations/sk.json @@ -2,6 +2,29 @@ "config": { "abort": { "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, + "error": { + "invalid_discovery_topic": "Neplatn\u00fd Discovery topic prefix." + }, + "step": { + "config": { + "data": { + "discovery_prefix": "Discovery topic prefix" + } + }, + "confirm": { + "description": "Chcete nastavi\u0165 Tasmotu?" + } + } + }, + "issues": { + "topic_duplicated": { + "description": "Nieko\u013eko zariaden\u00ed Tasmota zdie\u013ea t\u00e9mu {topic}. \n\n Zariadenia Tasmota s t\u00fdmto probl\u00e9mom: {offenders}.", + "title": "Nieko\u013eko zariaden\u00ed Tasmota zdie\u013ea rovnak\u00fa t\u00e9mu" + }, + "topic_no_prefix": { + "description": "Zariadenie Tasmota {name} s IP {ip} neobsahuje `%prefix%` vo svojej celej t\u00e9me. \n\nEntity pre tieto zariadenia s\u00fa zak\u00e1zan\u00e9, k\u00fdm sa neoprav\u00ed konfigur\u00e1cia.", + "title": "Zariadenie Tasmota {name} m\u00e1 neplatn\u00fa t\u00e9mu MQTT" } } } \ No newline at end of file diff --git a/homeassistant/components/tautulli/coordinator.py b/homeassistant/components/tautulli/coordinator.py index a71a7580740..d6e827acd8e 100644 --- a/homeassistant/components/tautulli/coordinator.py +++ b/homeassistant/components/tautulli/coordinator.py @@ -24,7 +24,7 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda from .const import DOMAIN, LOGGER -class TautulliDataUpdateCoordinator(DataUpdateCoordinator): +class TautulliDataUpdateCoordinator(DataUpdateCoordinator[None]): """Data update coordinator for the Tautulli integration.""" config_entry: ConfigEntry diff --git a/homeassistant/components/tautulli/sensor.py b/homeassistant/components/tautulli/sensor.py index 7109199fdcd..35e195b063c 100644 --- a/homeassistant/components/tautulli/sensor.py +++ b/homeassistant/components/tautulli/sensor.py @@ -13,12 +13,13 @@ from pytautulli import ( ) from homeassistant.components.sensor import ( + SensorDeviceClass, SensorEntity, SensorEntityDescription, SensorStateClass, ) from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry -from homeassistant.const import DATA_KILOBITS, PERCENTAGE +from homeassistant.const import PERCENTAGE, UnitOfInformation from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory, EntityDescription from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -101,7 +102,8 @@ SENSOR_TYPES: tuple[TautulliSensorEntityDescription, ...] = ( key="total_bandwidth", name="Total bandwidth", entity_category=EntityCategory.DIAGNOSTIC, - native_unit_of_measurement=DATA_KILOBITS, + native_unit_of_measurement=UnitOfInformation.KILOBITS, + device_class=SensorDeviceClass.DATA_SIZE, state_class=SensorStateClass.MEASUREMENT, value_fn=lambda home_stats, activity, _: cast(int, activity.total_bandwidth), ), @@ -109,7 +111,8 @@ SENSOR_TYPES: tuple[TautulliSensorEntityDescription, ...] = ( key="lan_bandwidth", name="LAN bandwidth", entity_category=EntityCategory.DIAGNOSTIC, - native_unit_of_measurement=DATA_KILOBITS, + native_unit_of_measurement=UnitOfInformation.KILOBITS, + device_class=SensorDeviceClass.DATA_SIZE, entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, value_fn=lambda home_stats, activity, _: cast(int, activity.lan_bandwidth), @@ -118,7 +121,8 @@ SENSOR_TYPES: tuple[TautulliSensorEntityDescription, ...] = ( key="wan_bandwidth", name="WAN bandwidth", entity_category=EntityCategory.DIAGNOSTIC, - native_unit_of_measurement=DATA_KILOBITS, + native_unit_of_measurement=UnitOfInformation.KILOBITS, + device_class=SensorDeviceClass.DATA_SIZE, entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, value_fn=lambda home_stats, activity, _: cast(int, activity.wan_bandwidth), diff --git a/homeassistant/components/tautulli/translations/ko.json b/homeassistant/components/tautulli/translations/ko.json index effc12fad5a..5d861c7d219 100644 --- a/homeassistant/components/tautulli/translations/ko.json +++ b/homeassistant/components/tautulli/translations/ko.json @@ -1,8 +1,24 @@ { "config": { + "abort": { + "already_configured": "\uc11c\ube44\uc2a4\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, "step": { + "reauth_confirm": { + "data": { + "api_key": "API \ud0a4" + } + }, "user": { "data": { + "api_key": "API \ud0a4", + "url": "URL \uc8fc\uc18c", "verify_ssl": "SSL \uc778\uc99d\uc11c \ud655\uc778" }, "description": "API \ud0a4\ub97c \ucc3e\uc73c\ub824\uba74 Tautulli \uc6f9 \ud398\uc774\uc9c0\ub97c \uc5f4\uace0 \uc124\uc815\uc73c\ub85c \uc774\ub3d9\ud55c \ub2e4\uc74c \uc6f9 \uc778\ud130\ud398\uc774\uc2a4\ub85c \uc774\ub3d9\ud569\ub2c8\ub2e4. API \ud0a4\ub294 \ud574\ub2f9 \ud398\uc774\uc9c0\uc758 \ub9e8 \uc544\ub798\uc5d0 \uc788\uc2b5\ub2c8\ub2e4.\n\nURL\uc758 \uc608: '''http://192.168.0.10:8181''''(\uae30\ubcf8\ud3ec\ud2b8: 8181)" diff --git a/homeassistant/components/tautulli/translations/pt.json b/homeassistant/components/tautulli/translations/pt.json index 43d522f0ab1..9b540474fb8 100644 --- a/homeassistant/components/tautulli/translations/pt.json +++ b/homeassistant/components/tautulli/translations/pt.json @@ -1,7 +1,10 @@ { "config": { + "abort": { + "already_configured": "O servi\u00e7o j\u00e1 est\u00e1 configurado" + }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "unknown": "Erro inesperado" }, "step": { diff --git a/homeassistant/components/tautulli/translations/sk.json b/homeassistant/components/tautulli/translations/sk.json index 769b6125412..14b55dd2133 100644 --- a/homeassistant/components/tautulli/translations/sk.json +++ b/homeassistant/components/tautulli/translations/sk.json @@ -13,14 +13,17 @@ "reauth_confirm": { "data": { "api_key": "API k\u013e\u00fa\u010d" - } + }, + "description": "Ak chcete n\u00e1js\u0165 svoj k\u013e\u00fa\u010d API, otvorte webov\u00fa str\u00e1nku Tautulli a prejdite na Nastavenia a potom na webov\u00e9 rozhranie. K\u013e\u00fa\u010d API bude v spodnej \u010dasti tejto str\u00e1nky.", + "title": "Znova overte Tautulliho" }, "user": { "data": { "api_key": "API k\u013e\u00fa\u010d", "url": "URL", "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" - } + }, + "description": "Ak chcete n\u00e1js\u0165 svoj k\u013e\u00fa\u010d API, otvorte webov\u00fa str\u00e1nku Tautulli a prejdite na Nastavenia a potom na webov\u00e9 rozhranie. K\u013e\u00fa\u010d API bude v spodnej \u010dasti tejto str\u00e1nky. \n\n Pr\u00edklad adresy URL: ```http://192.168.0.10:8181```, pri\u010dom 8181 je predvolen\u00fd port." } } } diff --git a/homeassistant/components/tcp/common.py b/homeassistant/components/tcp/common.py index d2d40358970..31f230d3b23 100644 --- a/homeassistant/components/tcp/common.py +++ b/homeassistant/components/tcp/common.py @@ -131,8 +131,10 @@ class TcpEntity(Entity): readable, _, _ = select.select([sock], [], [], self._config[CONF_TIMEOUT]) if not readable: _LOGGER.warning( - "Timeout (%s second(s)) waiting for a response after " - "sending %r to %s on port %s", + ( + "Timeout (%s second(s)) waiting for a response after " + "sending %r to %s on port %s" + ), self._config[CONF_TIMEOUT], self._config[CONF_PAYLOAD], self._config[CONF_HOST], diff --git a/homeassistant/components/ted5000/sensor.py b/homeassistant/components/ted5000/sensor.py index e579ac6c359..5c3e1d30c4a 100644 --- a/homeassistant/components/ted5000/sensor.py +++ b/homeassistant/components/ted5000/sensor.py @@ -11,15 +11,17 @@ import xmltodict from homeassistant.components.sensor import ( PLATFORM_SCHEMA, + SensorDeviceClass, SensorEntity, + SensorEntityDescription, SensorStateClass, ) from homeassistant.const import ( CONF_HOST, CONF_NAME, CONF_PORT, - ELECTRIC_POTENTIAL_VOLT, - POWER_WATT, + UnitOfElectricPotential, + UnitOfPower, ) from homeassistant.core import HomeAssistant from homeassistant.helpers import config_validation as cv @@ -42,6 +44,21 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( } ) +SENSORS = [ + SensorEntityDescription( + key="power", + native_unit_of_measurement=UnitOfPower.WATT, + device_class=SensorDeviceClass.POWER, + state_class=SensorStateClass.MEASUREMENT, + ), + SensorEntityDescription( + key="voltage", + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + device_class=SensorDeviceClass.VOLTAGE, + state_class=SensorStateClass.MEASUREMENT, + ), +] + def setup_platform( hass: HomeAssistant, @@ -50,9 +67,9 @@ def setup_platform( discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up the Ted5000 sensor.""" - host = config.get(CONF_HOST) - port = config.get(CONF_PORT) - name = config.get(CONF_NAME) + host: str = config[CONF_HOST] + port: int = config[CONF_PORT] + name: str = config[CONF_NAME] url = f"http://{host}:{port}/api/LiveData.xml" gateway = Ted5000Gateway(url) @@ -60,43 +77,38 @@ def setup_platform( # Get MUT information to create the sensors. gateway.update() - dev = [] + entities = [] for mtu in gateway.data: - dev.append(Ted5000Sensor(gateway, name, mtu, POWER_WATT)) - dev.append(Ted5000Sensor(gateway, name, mtu, ELECTRIC_POTENTIAL_VOLT)) + for description in SENSORS: + entities.append(Ted5000Sensor(gateway, name, mtu, description)) - add_entities(dev) + add_entities(entities) class Ted5000Sensor(SensorEntity): """Implementation of a Ted5000 sensor.""" - _attr_state_class = SensorStateClass.MEASUREMENT - - def __init__(self, gateway, name, mtu, unit): + def __init__( + self, + gateway: Ted5000Gateway, + name: str, + mtu: int, + description: SensorEntityDescription, + ) -> None: """Initialize the sensor.""" - units = {POWER_WATT: "power", ELECTRIC_POTENTIAL_VOLT: "voltage"} self._gateway = gateway - self._name = f"{name} mtu{mtu} {units[unit]}" + self._attr_name = f"{name} mtu{mtu} {description.key}" self._mtu = mtu - self._unit = unit + self.entity_description = description self.update() @property - def name(self): - """Return the name of the sensor.""" - return self._name - - @property - def native_unit_of_measurement(self): - """Return the unit the value is expressed in.""" - return self._unit - - @property - def native_value(self): + def native_value(self) -> int | float | None: """Return the state of the resources.""" - with suppress(KeyError): - return self._gateway.data[self._mtu][self._unit] + if unit := self.entity_description.native_unit_of_measurement: + with suppress(KeyError): + return self._gateway.data[self._mtu][unit] + return None def update(self) -> None: """Get the latest data from REST API.""" @@ -106,13 +118,13 @@ class Ted5000Sensor(SensorEntity): class Ted5000Gateway: """The class for handling the data retrieval.""" - def __init__(self, url): + def __init__(self, url: str) -> None: """Initialize the data object.""" self.url = url - self.data = {} + self.data: dict[int, dict[str, int | float]] = {} @Throttle(MIN_TIME_BETWEEN_UPDATES) - def update(self): + def update(self) -> None: """Get the latest data from the Ted5000 XML API.""" try: @@ -128,6 +140,6 @@ class Ted5000Gateway: voltage = int(doc["LiveData"]["Voltage"]["MTU%d" % mtu]["VoltageNow"]) self.data[mtu] = { - POWER_WATT: power, - ELECTRIC_POTENTIAL_VOLT: voltage / 10, + UnitOfPower.WATT: power, + UnitOfElectricPotential.VOLT: voltage / 10, } diff --git a/homeassistant/components/telegram_bot/__init__.py b/homeassistant/components/telegram_bot/__init__.py index f4738334745..32be8cbd935 100644 --- a/homeassistant/components/telegram_bot/__init__.py +++ b/homeassistant/components/telegram_bot/__init__.py @@ -1008,7 +1008,10 @@ class BaseTelegramBotEntity: if from_user in self.allowed_chat_ids or from_chat in self.allowed_chat_ids: return True _LOGGER.error( - "Unauthorized update - neither user id %s nor chat id %s is in allowed chats: %s", + ( + "Unauthorized update - neither user id %s nor chat id %s is in allowed" + " chats: %s" + ), from_user, from_chat, self.allowed_chat_ids, diff --git a/homeassistant/components/tellduslive/sensor.py b/homeassistant/components/tellduslive/sensor.py index ed3efeb4344..2c3ae3588ab 100644 --- a/homeassistant/components/tellduslive/sensor.py +++ b/homeassistant/components/tellduslive/sensor.py @@ -10,13 +10,14 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - LENGTH_MILLIMETERS, LIGHT_LUX, PERCENTAGE, - POWER_WATT, - SPEED_METERS_PER_SECOND, - TEMP_CELSIUS, UV_INDEX, + UnitOfPower, + UnitOfPrecipitationDepth, + UnitOfPressure, + UnitOfSpeed, + UnitOfTemperature, UnitOfVolumetricFlux, ) from homeassistant.core import HomeAssistant @@ -43,7 +44,7 @@ SENSOR_TYPES: dict[str, SensorEntityDescription] = { SENSOR_TYPE_TEMPERATURE: SensorEntityDescription( key=SENSOR_TYPE_TEMPERATURE, name="Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), @@ -64,8 +65,8 @@ SENSOR_TYPES: dict[str, SensorEntityDescription] = { SENSOR_TYPE_RAINTOTAL: SensorEntityDescription( key=SENSOR_TYPE_RAINTOTAL, name="Rain total", - native_unit_of_measurement=LENGTH_MILLIMETERS, - icon="mdi:water", + native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + device_class=SensorDeviceClass.PRECIPITATION, state_class=SensorStateClass.TOTAL_INCREASING, ), SENSOR_TYPE_WINDDIRECTION: SensorEntityDescription( @@ -75,15 +76,15 @@ SENSOR_TYPES: dict[str, SensorEntityDescription] = { SENSOR_TYPE_WINDAVERAGE: SensorEntityDescription( key=SENSOR_TYPE_WINDAVERAGE, name="Wind average", - native_unit_of_measurement=SPEED_METERS_PER_SECOND, - device_class=SensorDeviceClass.SPEED, + native_unit_of_measurement=UnitOfSpeed.METERS_PER_SECOND, + device_class=SensorDeviceClass.WIND_SPEED, state_class=SensorStateClass.MEASUREMENT, ), SENSOR_TYPE_WINDGUST: SensorEntityDescription( key=SENSOR_TYPE_WINDGUST, name="Wind gust", - native_unit_of_measurement=SPEED_METERS_PER_SECOND, - device_class=SensorDeviceClass.SPEED, + native_unit_of_measurement=UnitOfSpeed.METERS_PER_SECOND, + device_class=SensorDeviceClass.WIND_SPEED, state_class=SensorStateClass.MEASUREMENT, ), SENSOR_TYPE_UV: SensorEntityDescription( @@ -95,7 +96,8 @@ SENSOR_TYPES: dict[str, SensorEntityDescription] = { SENSOR_TYPE_WATT: SensorEntityDescription( key=SENSOR_TYPE_WATT, name="Power", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, + device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), SENSOR_TYPE_LUMINANCE: SensorEntityDescription( @@ -108,14 +110,14 @@ SENSOR_TYPES: dict[str, SensorEntityDescription] = { SENSOR_TYPE_DEW_POINT: SensorEntityDescription( key=SENSOR_TYPE_DEW_POINT, name="Dew Point", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), SENSOR_TYPE_BAROMETRIC_PRESSURE: SensorEntityDescription( key=SENSOR_TYPE_BAROMETRIC_PRESSURE, name="Barometric Pressure", - native_unit_of_measurement="kPa", + native_unit_of_measurement=UnitOfPressure.KPA, state_class=SensorStateClass.MEASUREMENT, ), } diff --git a/homeassistant/components/tellduslive/translations/de.json b/homeassistant/components/tellduslive/translations/de.json index 78662ca4536..790f7ea6dad 100644 --- a/homeassistant/components/tellduslive/translations/de.json +++ b/homeassistant/components/tellduslive/translations/de.json @@ -11,7 +11,7 @@ }, "step": { "auth": { - "description": "So verkn\u00fcpfst du dein TelldusLive-Konto: \n 1. Dr\u00fccke auf den Link unten \n 2. Melde dich bei Telldus Live an \n 3. Autorisiere ** {app_name} ** (dr\u00fccke auf ** Yes **). \n 4. Komme hierher zur\u00fcck und kdr\u00fccke auf **SENDEN**. \n\n [Link TelldusLive-Konto]({auth_url})", + "description": "So verkn\u00fcpfst du dein TelldusLive Konto: \n 1. Dr\u00fccke auf den Link unten \n 2. Melde dich bei Telldus Live an \n 3. Autorisiere ** {app_name} ** (dr\u00fccke auf ** Yes **). \n 4. Komme hierher zur\u00fcck und dr\u00fccke auf **SENDEN**. \n\n [Link TelldusLive-Konto]({auth_url})", "title": "Authentifiziere dich gegen TelldusLive" }, "user": { diff --git a/homeassistant/components/tellduslive/translations/en_GB.json b/homeassistant/components/tellduslive/translations/en-GB.json similarity index 100% rename from homeassistant/components/tellduslive/translations/en_GB.json rename to homeassistant/components/tellduslive/translations/en-GB.json diff --git a/homeassistant/components/tellduslive/translations/pt.json b/homeassistant/components/tellduslive/translations/pt.json index e8fda8a7647..fa3900d9681 100644 --- a/homeassistant/components/tellduslive/translations/pt.json +++ b/homeassistant/components/tellduslive/translations/pt.json @@ -16,7 +16,7 @@ }, "user": { "data": { - "host": "Servidor" + "host": "Endere\u00e7o" }, "description": "Vazio", "title": "Escolher endpoint." diff --git a/homeassistant/components/tellduslive/translations/sk.json b/homeassistant/components/tellduslive/translations/sk.json index e0af50871da..c5a08a26a4d 100644 --- a/homeassistant/components/tellduslive/translations/sk.json +++ b/homeassistant/components/tellduslive/translations/sk.json @@ -3,17 +3,23 @@ "abort": { "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1", "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL.", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba", "unknown_authorize_url_generation": "Nezn\u00e1ma chyba pri generovan\u00ed autorizovanej adresy URL." }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { + "auth": { + "description": "Ak chcete prepoji\u0165 svoj \u00fa\u010det TelldusLive:\n 1. Kliknite na odkaz ni\u017e\u0161ie\n 2. Prihl\u00e1ste sa do Telldus Live\n 3. Autorizujte **{app_name}** (kliknite na **\u00c1no**).\n 4. Vr\u00e1\u0165te sa sem a kliknite na **ODOSLA\u0164**. \n\n [Prepoji\u0165 \u00fa\u010det TelldusLive]({auth_url})", + "title": "Overenie pomocou TelldusLive" + }, "user": { "data": { "host": "Hostite\u013e" }, - "description": "Pr\u00e1zdne" + "description": "Pr\u00e1zdne", + "title": "Vyberte koncov\u00fd bod." } } } diff --git a/homeassistant/components/tellstick/sensor.py b/homeassistant/components/tellstick/sensor.py index bfd433e1674..cef118e721d 100644 --- a/homeassistant/components/tellstick/sensor.py +++ b/homeassistant/components/tellstick/sensor.py @@ -19,7 +19,7 @@ from homeassistant.const import ( CONF_NAME, CONF_PROTOCOL, PERCENTAGE, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv @@ -37,7 +37,7 @@ CONF_ONLY_NAMED = "only_named" CONF_TEMPERATURE_SCALE = "temperature_scale" DEFAULT_DATATYPE_MASK = 127 -DEFAULT_TEMPERATURE_SCALE = TEMP_CELSIUS +DEFAULT_TEMPERATURE_SCALE = UnitOfTemperature.CELSIUS PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( { diff --git a/homeassistant/components/temper/sensor.py b/homeassistant/components/temper/sensor.py index 606227ee7f5..fd867527153 100644 --- a/homeassistant/components/temper/sensor.py +++ b/homeassistant/components/temper/sensor.py @@ -15,7 +15,7 @@ from homeassistant.const import ( CONF_NAME, CONF_OFFSET, DEVICE_DEFAULT_NAME, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -74,7 +74,7 @@ class TemperSensor(SensorEntity): """Representation of a Temper temperature sensor.""" _attr_device_class = SensorDeviceClass.TEMPERATURE - _attr_native_unit_of_measurement = TEMP_CELSIUS + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS def __init__(self, temper_device, name, scaling): """Initialize the sensor.""" diff --git a/homeassistant/components/template/config.py b/homeassistant/components/template/config.py index 51755bfd0d3..77eaec13da5 100644 --- a/homeassistant/components/template/config.py +++ b/homeassistant/components/template/config.py @@ -92,7 +92,8 @@ async def async_validate_config(hass, config): if not legacy_warn_printed: legacy_warn_printed = True logging.getLogger(__name__).warning( - "The entity definition format under template: differs from the platform " + "The entity definition format under template: differs from the" + " platform " "configuration format. See " "https://www.home-assistant.io/integrations/template#configuration-for-trigger-based-template-sensors" ) diff --git a/homeassistant/components/template/light.py b/homeassistant/components/template/light.py index 27dcbf0e014..b403034208a 100644 --- a/homeassistant/components/template/light.py +++ b/homeassistant/components/template/light.py @@ -474,7 +474,10 @@ class LightTemplate(TemplateEntity, LightEntity): if not isinstance(effect_list, list): _LOGGER.error( - "Received invalid effect list: %s for entity %s. Expected list of strings", + ( + "Received invalid effect list: %s for entity %s. Expected list of" + " strings" + ), effect_list, self.entity_id, ) @@ -545,7 +548,10 @@ class LightTemplate(TemplateEntity, LightEntity): self._temperature = temperature else: _LOGGER.error( - "Received invalid color temperature : %s for entity %s. Expected: %s-%s", + ( + "Received invalid color temperature : %s for entity %s." + " Expected: %s-%s" + ), temperature, self.entity_id, self.min_mireds, @@ -554,7 +560,10 @@ class LightTemplate(TemplateEntity, LightEntity): self._temperature = None except ValueError: _LOGGER.error( - "Template must supply an integer temperature within the range for this light, or 'None'", + ( + "Template must supply an integer temperature within the range for" + " this light, or 'None'" + ), exc_info=True, ) self._temperature = None @@ -586,7 +595,10 @@ class LightTemplate(TemplateEntity, LightEntity): self._color = (h_str, s_str) elif h_str is not None and s_str is not None: _LOGGER.error( - "Received invalid hs_color : (%s, %s) for entity %s. Expected: (0-360, 0-100)", + ( + "Received invalid hs_color : (%s, %s) for entity %s. Expected:" + " (0-360, 0-100)" + ), h_str, s_str, self.entity_id, @@ -609,7 +621,10 @@ class LightTemplate(TemplateEntity, LightEntity): self._max_mireds = int(render) except ValueError: _LOGGER.error( - "Template must supply an integer temperature within the range for this light, or 'None'", + ( + "Template must supply an integer temperature within the range for" + " this light, or 'None'" + ), exc_info=True, ) self._max_mireds = None @@ -624,7 +639,10 @@ class LightTemplate(TemplateEntity, LightEntity): self._min_mireds = int(render) except ValueError: _LOGGER.error( - "Template must supply an integer temperature within the range for this light, or 'None'", + ( + "Template must supply an integer temperature within the range for" + " this light, or 'None'" + ), exc_info=True, ) self._min_mireds = None diff --git a/homeassistant/components/template/sensor.py b/homeassistant/components/template/sensor.py index b52953c1a8a..a7f78ad1804 100644 --- a/homeassistant/components/template/sensor.py +++ b/homeassistant/components/template/sensor.py @@ -99,8 +99,9 @@ def extra_validation_checks(val): """Run extra validation checks.""" if CONF_TRIGGER in val: raise vol.Invalid( - "You can only add triggers to template entities if they are defined under `template:`. " - "See the template documentation for more information: https://www.home-assistant.io/integrations/template/" + "You can only add triggers to template entities if they are defined under" + " `template:`. See the template documentation for more information:" + " https://www.home-assistant.io/integrations/template/" ) if CONF_SENSORS not in val and SENSOR_DOMAIN not in val: diff --git a/homeassistant/components/tesla_wall_connector/__init__.py b/homeassistant/components/tesla_wall_connector/__init__.py index b1d5811c610..179576334a9 100644 --- a/homeassistant/components/tesla_wall_connector/__init__.py +++ b/homeassistant/components/tesla_wall_connector/__init__.py @@ -62,7 +62,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: ) from ex except WallConnectorConnectionError as ex: raise UpdateFailed( - f"Could not fetch data from Tesla WallConnector at {hostname}: Cannot connect" + f"Could not fetch data from Tesla WallConnector at {hostname}: Cannot" + " connect" ) from ex except WallConnectorError as ex: raise UpdateFailed( diff --git a/homeassistant/components/tesla_wall_connector/sensor.py b/homeassistant/components/tesla_wall_connector/sensor.py index a36ce979eee..fbad129dd17 100644 --- a/homeassistant/components/tesla_wall_connector/sensor.py +++ b/homeassistant/components/tesla_wall_connector/sensor.py @@ -10,11 +10,11 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - ELECTRIC_CURRENT_AMPERE, - ELECTRIC_POTENTIAL_VOLT, - ENERGY_WATT_HOUR, - FREQUENCY_HERTZ, - TEMP_CELSIUS, + UnitOfElectricCurrent, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfFrequency, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory @@ -48,7 +48,7 @@ WALL_CONNECTOR_SENSORS = [ WallConnectorSensorDescription( key="handle_temp_c", name=prefix_entity_name("Handle Temperature"), - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, value_fn=lambda data: round(data[WALLCONNECTOR_DATA_VITALS].handle_temp_c, 1), device_class=SensorDeviceClass.TEMPERATURE, entity_category=EntityCategory.DIAGNOSTIC, @@ -57,7 +57,7 @@ WALL_CONNECTOR_SENSORS = [ WallConnectorSensorDescription( key="grid_v", name=prefix_entity_name("Grid Voltage"), - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, value_fn=lambda data: round(data[WALLCONNECTOR_DATA_VITALS].grid_v, 1), device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, @@ -66,66 +66,73 @@ WALL_CONNECTOR_SENSORS = [ WallConnectorSensorDescription( key="grid_hz", name=prefix_entity_name("Grid Frequency"), - native_unit_of_measurement=FREQUENCY_HERTZ, + native_unit_of_measurement=UnitOfFrequency.HERTZ, value_fn=lambda data: round(data[WALLCONNECTOR_DATA_VITALS].grid_hz, 3), + device_class=SensorDeviceClass.FREQUENCY, state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, ), WallConnectorSensorDescription( key="current_a_a", name=prefix_entity_name("Phase A Current"), - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, value_fn=lambda data: data[WALLCONNECTOR_DATA_VITALS].currentA_a, + device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, ), WallConnectorSensorDescription( key="current_b_a", name=prefix_entity_name("Phase B Current"), - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, value_fn=lambda data: data[WALLCONNECTOR_DATA_VITALS].currentB_a, + device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, ), WallConnectorSensorDescription( key="current_c_a", name=prefix_entity_name("Phase C Current"), - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, value_fn=lambda data: data[WALLCONNECTOR_DATA_VITALS].currentC_a, + device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, ), WallConnectorSensorDescription( key="voltage_a_v", name=prefix_entity_name("Phase A Voltage"), - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, value_fn=lambda data: data[WALLCONNECTOR_DATA_VITALS].voltageA_v, + device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, ), WallConnectorSensorDescription( key="voltage_b_v", name=prefix_entity_name("Phase B Voltage"), - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, value_fn=lambda data: data[WALLCONNECTOR_DATA_VITALS].voltageB_v, + device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, ), WallConnectorSensorDescription( key="voltage_c_v", name=prefix_entity_name("Phase C Voltage"), - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, value_fn=lambda data: data[WALLCONNECTOR_DATA_VITALS].voltageC_v, + device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, ), WallConnectorSensorDescription( key="energy_kWh", name=prefix_entity_name("Energy"), - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, value_fn=lambda data: data[WALLCONNECTOR_DATA_LIFETIME].energy_wh, - state_class=SensorStateClass.TOTAL_INCREASING, device_class=SensorDeviceClass.ENERGY, + state_class=SensorStateClass.TOTAL_INCREASING, ), ] diff --git a/homeassistant/components/tesla_wall_connector/translations/ko.json b/homeassistant/components/tesla_wall_connector/translations/ko.json new file mode 100644 index 00000000000..85281856809 --- /dev/null +++ b/homeassistant/components/tesla_wall_connector/translations/ko.json @@ -0,0 +1,18 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tesla_wall_connector/translations/pt.json b/homeassistant/components/tesla_wall_connector/translations/pt.json index 8578f969852..d448cabd080 100644 --- a/homeassistant/components/tesla_wall_connector/translations/pt.json +++ b/homeassistant/components/tesla_wall_connector/translations/pt.json @@ -6,7 +6,7 @@ "step": { "user": { "data": { - "host": "Servidor" + "host": "Endere\u00e7o" } } } diff --git a/homeassistant/components/tesla_wall_connector/translations/sk.json b/homeassistant/components/tesla_wall_connector/translations/sk.json index 336708e4a3c..5c071a116ab 100644 --- a/homeassistant/components/tesla_wall_connector/translations/sk.json +++ b/homeassistant/components/tesla_wall_connector/translations/sk.json @@ -12,7 +12,8 @@ "user": { "data": { "host": "Hostite\u013e" - } + }, + "title": "Nakonfigurujte Tesla Wall Connector" } } } diff --git a/homeassistant/components/text/__init__.py b/homeassistant/components/text/__init__.py index 32054734e8e..1cd980e71cc 100644 --- a/homeassistant/components/text/__init__.py +++ b/homeassistant/components/text/__init__.py @@ -65,7 +65,8 @@ async def _async_set_value(entity: TextEntity, service_call: ServiceCall) -> Non value = service_call.data[ATTR_VALUE] if len(value) < entity.min: raise ValueError( - f"Value {value} for {entity.name} is too short (minimum length {entity.min})" + f"Value {value} for {entity.name} is too short (minimum length" + f" {entity.min})" ) if len(value) > entity.max: raise ValueError( diff --git a/homeassistant/components/text/translations/ca.json b/homeassistant/components/text/translations/ca.json index 19b840cdb8f..b9d42d7b241 100644 --- a/homeassistant/components/text/translations/ca.json +++ b/homeassistant/components/text/translations/ca.json @@ -1,3 +1,8 @@ { + "device_automation": { + "action_type": { + "set_value": "Estableix el valor de {entity_name}" + } + }, "title": "Text" } \ No newline at end of file diff --git a/homeassistant/components/text/translations/de.json b/homeassistant/components/text/translations/de.json index 19b840cdb8f..b6b3a6faeec 100644 --- a/homeassistant/components/text/translations/de.json +++ b/homeassistant/components/text/translations/de.json @@ -1,3 +1,8 @@ { + "device_automation": { + "action_type": { + "set_value": "Wert f\u00fcr {entity_name} setzen" + } + }, "title": "Text" } \ No newline at end of file diff --git a/homeassistant/components/text/translations/el.json b/homeassistant/components/text/translations/el.json index 89853ef69f6..6f8cca7d2c5 100644 --- a/homeassistant/components/text/translations/el.json +++ b/homeassistant/components/text/translations/el.json @@ -1,3 +1,8 @@ { + "device_automation": { + "action_type": { + "set_value": "\u039f\u03c1\u03b9\u03c3\u03bc\u03cc\u03c2 \u03c4\u03b9\u03bc\u03ae\u03c2 \u03b3\u03b9\u03b1 {entity_name}" + } + }, "title": "\u039a\u03b5\u03af\u03bc\u03b5\u03bd\u03bf" } \ No newline at end of file diff --git a/homeassistant/components/text/translations/es.json b/homeassistant/components/text/translations/es.json index 07996c47b61..c881998aae6 100644 --- a/homeassistant/components/text/translations/es.json +++ b/homeassistant/components/text/translations/es.json @@ -1,3 +1,8 @@ { + "device_automation": { + "action_type": { + "set_value": "Establecer valor para {entity_name}" + } + }, "title": "Texto" } \ No newline at end of file diff --git a/homeassistant/components/text/translations/et.json b/homeassistant/components/text/translations/et.json index 6ac4d0d7b31..e2c618d6db1 100644 --- a/homeassistant/components/text/translations/et.json +++ b/homeassistant/components/text/translations/et.json @@ -1,3 +1,8 @@ { + "device_automation": { + "action_type": { + "set_value": "Olemi {entity_name} v\u00e4\u00e4rtuse m\u00e4\u00e4ramine" + } + }, "title": "Tekst" } \ No newline at end of file diff --git a/homeassistant/components/text/translations/he.json b/homeassistant/components/text/translations/he.json new file mode 100644 index 00000000000..36019637feb --- /dev/null +++ b/homeassistant/components/text/translations/he.json @@ -0,0 +1,8 @@ +{ + "device_automation": { + "action_type": { + "set_value": "\u05d4\u05d2\u05d3\u05e8\u05ea \u05e2\u05e8\u05da \u05e2\u05d1\u05d5\u05e8 {entity_name}" + } + }, + "title": "\u05d8\u05e7\u05e1\u05d8" +} \ No newline at end of file diff --git a/homeassistant/components/text/translations/hu.json b/homeassistant/components/text/translations/hu.json new file mode 100644 index 00000000000..a888240d684 --- /dev/null +++ b/homeassistant/components/text/translations/hu.json @@ -0,0 +1,8 @@ +{ + "device_automation": { + "action_type": { + "set_value": "{entity_name} \u00e9rt\u00e9k\u00e9nek be\u00e1ll\u00edt\u00e1sa" + } + }, + "title": "Sz\u00f6veg" +} \ No newline at end of file diff --git a/homeassistant/components/text/translations/id.json b/homeassistant/components/text/translations/id.json index ca3ba59d316..83d77595274 100644 --- a/homeassistant/components/text/translations/id.json +++ b/homeassistant/components/text/translations/id.json @@ -1,3 +1,8 @@ { + "device_automation": { + "action_type": { + "set_value": "Tetapkan nilai untuk {entity_name}" + } + }, "title": "Teks" } \ No newline at end of file diff --git a/homeassistant/components/text/translations/it.json b/homeassistant/components/text/translations/it.json new file mode 100644 index 00000000000..c847eb8a35b --- /dev/null +++ b/homeassistant/components/text/translations/it.json @@ -0,0 +1,8 @@ +{ + "device_automation": { + "action_type": { + "set_value": "Imposta valore per {entity_name}" + } + }, + "title": "Testo" +} \ No newline at end of file diff --git a/homeassistant/components/text/translations/ko.json b/homeassistant/components/text/translations/ko.json new file mode 100644 index 00000000000..76c44953f58 --- /dev/null +++ b/homeassistant/components/text/translations/ko.json @@ -0,0 +1,8 @@ +{ + "device_automation": { + "action_type": { + "set_value": "{entity_name}\uc758 \uac12 \uc124\uc815\ud558\uae30" + } + }, + "title": "\ud14d\uc2a4\ud2b8" +} \ No newline at end of file diff --git a/homeassistant/components/text/translations/no.json b/homeassistant/components/text/translations/no.json index 6ac4d0d7b31..331693a0b92 100644 --- a/homeassistant/components/text/translations/no.json +++ b/homeassistant/components/text/translations/no.json @@ -1,3 +1,8 @@ { + "device_automation": { + "action_type": { + "set_value": "Angi verdi for {entity_name}" + } + }, "title": "Tekst" } \ No newline at end of file diff --git a/homeassistant/components/text/translations/pl.json b/homeassistant/components/text/translations/pl.json new file mode 100644 index 00000000000..f2e9727d647 --- /dev/null +++ b/homeassistant/components/text/translations/pl.json @@ -0,0 +1,8 @@ +{ + "device_automation": { + "action_type": { + "set_value": "ustaw warto\u015b\u0107 dla {entity_name}" + } + }, + "title": "Tekst" +} \ No newline at end of file diff --git a/homeassistant/components/text/translations/pt-BR.json b/homeassistant/components/text/translations/pt-BR.json index 07996c47b61..48467cbb1bc 100644 --- a/homeassistant/components/text/translations/pt-BR.json +++ b/homeassistant/components/text/translations/pt-BR.json @@ -1,3 +1,8 @@ { + "device_automation": { + "action_type": { + "set_value": "Definir valor para {entity_name}" + } + }, "title": "Texto" } \ No newline at end of file diff --git a/homeassistant/components/text/translations/ru.json b/homeassistant/components/text/translations/ru.json index 87a2a7629be..c15d2aee7ae 100644 --- a/homeassistant/components/text/translations/ru.json +++ b/homeassistant/components/text/translations/ru.json @@ -1,3 +1,8 @@ { + "device_automation": { + "action_type": { + "set_value": "\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0434\u043b\u044f {entity_name}" + } + }, "title": "\u0422\u0435\u043a\u0441\u0442" } \ No newline at end of file diff --git a/homeassistant/components/text/translations/sk.json b/homeassistant/components/text/translations/sk.json new file mode 100644 index 00000000000..6ea08131a96 --- /dev/null +++ b/homeassistant/components/text/translations/sk.json @@ -0,0 +1,8 @@ +{ + "device_automation": { + "action_type": { + "set_value": "Nastavi\u0165 hodnotu pre {entity_name}" + } + }, + "title": "Text" +} \ No newline at end of file diff --git a/homeassistant/components/text/translations/zh-Hant.json b/homeassistant/components/text/translations/zh-Hant.json index b6aaf2f6191..9375080295a 100644 --- a/homeassistant/components/text/translations/zh-Hant.json +++ b/homeassistant/components/text/translations/zh-Hant.json @@ -1,3 +1,8 @@ { + "device_automation": { + "action_type": { + "set_value": "{entity_name} \u8a2d\u5b9a\u503c" + } + }, "title": "\u6587\u5b57" } \ No newline at end of file diff --git a/homeassistant/components/tfiac/climate.py b/homeassistant/components/tfiac/climate.py index 73e52ca1584..2e764b5c637 100644 --- a/homeassistant/components/tfiac/climate.py +++ b/homeassistant/components/tfiac/climate.py @@ -23,7 +23,7 @@ from homeassistant.components.climate import ( ClimateEntityFeature, HVACMode, ) -from homeassistant.const import ATTR_TEMPERATURE, CONF_HOST, TEMP_FAHRENHEIT +from homeassistant.const import ATTR_TEMPERATURE, CONF_HOST, UnitOfTemperature from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -84,7 +84,7 @@ class TfiacClimate(ClimateEntity): | ClimateEntityFeature.SWING_MODE | ClimateEntityFeature.TARGET_TEMPERATURE ) - _attr_temperature_unit = TEMP_FAHRENHEIT + _attr_temperature_unit = UnitOfTemperature.FAHRENHEIT def __init__(self, hass, client): """Init class.""" diff --git a/homeassistant/components/thermobeacon/manifest.json b/homeassistant/components/thermobeacon/manifest.json index 3e105eff138..01e66bbbb94 100644 --- a/homeassistant/components/thermobeacon/manifest.json +++ b/homeassistant/components/thermobeacon/manifest.json @@ -28,9 +28,15 @@ "manufacturer_data_start": [0], "connectable": false }, + { + "service_uuid": "0000fff0-0000-1000-8000-00805f9b34fb", + "manufacturer_id": 27, + "manufacturer_data_start": [0], + "connectable": false + }, { "local_name": "ThermoBeacon", "connectable": false } ], - "requirements": ["thermobeacon-ble==0.4.0"], + "requirements": ["thermobeacon-ble==0.6.0"], "dependencies": ["bluetooth"], "codeowners": ["@bdraco"], "iot_class": "local_push" diff --git a/homeassistant/components/thermobeacon/sensor.py b/homeassistant/components/thermobeacon/sensor.py index 900aacbf7a7..b651fb2cea0 100644 --- a/homeassistant/components/thermobeacon/sensor.py +++ b/homeassistant/components/thermobeacon/sensor.py @@ -23,10 +23,10 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.const import ( - ELECTRIC_POTENTIAL_VOLT, PERCENTAGE, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, - TEMP_CELSIUS, + UnitOfElectricPotential, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory @@ -67,7 +67,7 @@ SENSOR_DESCRIPTIONS = { ): SensorEntityDescription( key=f"{ThermoBeaconSensorDeviceClass.TEMPERATURE}_{Units.TEMP_CELSIUS}", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, ), ( @@ -76,7 +76,7 @@ SENSOR_DESCRIPTIONS = { ): SensorEntityDescription( key=f"{ThermoBeaconSensorDeviceClass.VOLTAGE}_{Units.ELECTRIC_POTENTIAL_VOLT}", device_class=SensorDeviceClass.VOLTAGE, - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, state_class=SensorStateClass.MEASUREMENT, ), } diff --git a/homeassistant/components/thermobeacon/translations/en.json b/homeassistant/components/thermobeacon/translations/en.json index ebd9760c161..afe859ca766 100644 --- a/homeassistant/components/thermobeacon/translations/en.json +++ b/homeassistant/components/thermobeacon/translations/en.json @@ -9,13 +9,13 @@ "flow_title": "{name}", "step": { "bluetooth_confirm": { - "description": "Do you want to setup {name}?" + "description": "Do you want to set up {name}?" }, "user": { "data": { "address": "Device" }, - "description": "Choose a device to setup" + "description": "Choose a device to set up" } } } diff --git a/homeassistant/components/thermobeacon/translations/it.json b/homeassistant/components/thermobeacon/translations/it.json index 7784ed3a240..97113c57103 100644 --- a/homeassistant/components/thermobeacon/translations/it.json +++ b/homeassistant/components/thermobeacon/translations/it.json @@ -15,7 +15,7 @@ "data": { "address": "Dispositivo" }, - "description": "Seleziona un dispositivo da configurare" + "description": "Scegli un dispositivo da configurare" } } } diff --git a/homeassistant/components/thermobeacon/translations/ko.json b/homeassistant/components/thermobeacon/translations/ko.json new file mode 100644 index 00000000000..1a59c05b30c --- /dev/null +++ b/homeassistant/components/thermobeacon/translations/ko.json @@ -0,0 +1,9 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", + "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/thermobeacon/translations/no.json b/homeassistant/components/thermobeacon/translations/no.json index 0bf8b1695ec..38ab3d096f2 100644 --- a/homeassistant/components/thermobeacon/translations/no.json +++ b/homeassistant/components/thermobeacon/translations/no.json @@ -9,7 +9,7 @@ "flow_title": "{name}", "step": { "bluetooth_confirm": { - "description": "Vil du konfigurere {name}?" + "description": "Vil du sette opp {name} ?" }, "user": { "data": { diff --git a/homeassistant/components/thermobeacon/translations/pt.json b/homeassistant/components/thermobeacon/translations/pt.json index 5a10362e52b..a91d8b08e67 100644 --- a/homeassistant/components/thermobeacon/translations/pt.json +++ b/homeassistant/components/thermobeacon/translations/pt.json @@ -1,6 +1,9 @@ { "config": { "abort": { + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", + "already_in_progress": "O processo de configura\u00e7\u00e3o j\u00e1 est\u00e1 a decorrer", + "no_devices_found": "Nenhum dispositivo encontrado na rede", "not_supported": "Dispositivo n\u00e3o suportado" } } diff --git a/homeassistant/components/thermopro/sensor.py b/homeassistant/components/thermopro/sensor.py index 22a6e2f086a..8a2e68f1625 100644 --- a/homeassistant/components/thermopro/sensor.py +++ b/homeassistant/components/thermopro/sensor.py @@ -27,7 +27,7 @@ from homeassistant.components.sensor import ( from homeassistant.const import ( PERCENTAGE, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -42,7 +42,7 @@ SENSOR_DESCRIPTIONS = { ): SensorEntityDescription( key=f"{ThermoProSensorDeviceClass.TEMPERATURE}_{Units.TEMP_CELSIUS}", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, ), (ThermoProSensorDeviceClass.HUMIDITY, Units.PERCENTAGE): SensorEntityDescription( diff --git a/homeassistant/components/thermopro/translations/en.json b/homeassistant/components/thermopro/translations/en.json index d24df64f135..f99ec0bbe63 100644 --- a/homeassistant/components/thermopro/translations/en.json +++ b/homeassistant/components/thermopro/translations/en.json @@ -8,13 +8,13 @@ "flow_title": "{name}", "step": { "bluetooth_confirm": { - "description": "Do you want to setup {name}?" + "description": "Do you want to set up {name}?" }, "user": { "data": { "address": "Device" }, - "description": "Choose a device to setup" + "description": "Choose a device to set up" } } } diff --git a/homeassistant/components/thermopro/translations/it.json b/homeassistant/components/thermopro/translations/it.json index 501b5095826..3ede7709c00 100644 --- a/homeassistant/components/thermopro/translations/it.json +++ b/homeassistant/components/thermopro/translations/it.json @@ -14,7 +14,7 @@ "data": { "address": "Dispositivo" }, - "description": "Seleziona un dispositivo da configurare" + "description": "Scegli un dispositivo da configurare" } } } diff --git a/homeassistant/components/thermopro/translations/ko.json b/homeassistant/components/thermopro/translations/ko.json new file mode 100644 index 00000000000..1a59c05b30c --- /dev/null +++ b/homeassistant/components/thermopro/translations/ko.json @@ -0,0 +1,9 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", + "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/thermopro/translations/no.json b/homeassistant/components/thermopro/translations/no.json index 28ec4582177..0a44ef08d80 100644 --- a/homeassistant/components/thermopro/translations/no.json +++ b/homeassistant/components/thermopro/translations/no.json @@ -8,7 +8,7 @@ "flow_title": "{name}", "step": { "bluetooth_confirm": { - "description": "Vil du konfigurere {name}?" + "description": "Vil du sette opp {name} ?" }, "user": { "data": { diff --git a/homeassistant/components/thermopro/translations/pt.json b/homeassistant/components/thermopro/translations/pt.json new file mode 100644 index 00000000000..c6a15010072 --- /dev/null +++ b/homeassistant/components/thermopro/translations/pt.json @@ -0,0 +1,9 @@ +{ + "config": { + "abort": { + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", + "already_in_progress": "O processo de configura\u00e7\u00e3o j\u00e1 est\u00e1 a decorrer", + "no_devices_found": "Nenhum dispositivo encontrado na rede" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/thermoworks_smoke/sensor.py b/homeassistant/components/thermoworks_smoke/sensor.py index 2297883ec6d..d5621c661d2 100644 --- a/homeassistant/components/thermoworks_smoke/sensor.py +++ b/homeassistant/components/thermoworks_smoke/sensor.py @@ -24,7 +24,7 @@ from homeassistant.const import ( CONF_EXCLUDE, CONF_MONITORED_CONDITIONS, CONF_PASSWORD, - TEMP_FAHRENHEIT, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv @@ -115,7 +115,7 @@ class ThermoworksSmokeSensor(SensorEntity): self._attr_name = "{name} {sensor}".format( name=mgr.name(serial), sensor=SENSOR_TYPES[sensor_type] ) - self._attr_native_unit_of_measurement = TEMP_FAHRENHEIT + self._attr_native_unit_of_measurement = UnitOfTemperature.FAHRENHEIT self._attr_unique_id = f"{serial}-{sensor_type}" self._attr_device_class = SensorDeviceClass.TEMPERATURE self.update_unit() diff --git a/homeassistant/components/threshold/translations/sk.json b/homeassistant/components/threshold/translations/sk.json index fcfc77b7cf2..39fda381566 100644 --- a/homeassistant/components/threshold/translations/sk.json +++ b/homeassistant/components/threshold/translations/sk.json @@ -1,20 +1,38 @@ { "config": { + "error": { + "need_lower_upper": "Spodn\u00e1 a horn\u00e1 hranica nem\u00f4\u017eu by\u0165 obe pr\u00e1zdne" + }, "step": { "user": { "data": { - "entity_id": "Vstupn\u00fd sn\u00edma\u010d" - } + "entity_id": "Vstupn\u00fd sn\u00edma\u010d", + "hysteresis": "Hyster\u00e9za", + "lower": "Doln\u00e1 hranica", + "name": "N\u00e1zov", + "upper": "Horn\u00e1 hranica" + }, + "description": "Vytvorte bin\u00e1rny senzor, ktor\u00fd sa zap\u00edna a vyp\u00edna v z\u00e1vislosti od hodnoty sn\u00edma\u010da \n\nNakonfigurovan\u00fd iba spodn\u00fd limit \u2013 Zapnite, ke\u010f je hodnota vstupn\u00e9ho sn\u00edma\u010da ni\u017e\u0161ia ako spodn\u00fd limit.\nNakonfigurovan\u00e1 iba horn\u00e1 hranica - Zapne sa, ke\u010f je hodnota vstupn\u00e9ho sn\u00edma\u010da v\u00e4\u010d\u0161ia ako horn\u00e1 hranica.\nSpodn\u00e1 aj horn\u00e1 hranica nakonfigurovan\u00e1 - Zapne sa, ke\u010f je hodnota vstupn\u00e9ho sn\u00edma\u010da v rozsahu [doln\u00e1 hranica .. horn\u00e1 hranica].", + "title": "Pridajte prahov\u00fd sn\u00edma\u010d" } } }, "options": { + "error": { + "need_lower_upper": "Spodn\u00e1 a horn\u00e1 hranica nem\u00f4\u017eu by\u0165 obe pr\u00e1zdne" + }, "step": { "init": { "data": { - "entity_id": "Vstupn\u00fd sn\u00edma\u010d" - } + "entity_id": "Vstupn\u00fd sn\u00edma\u010d", + "hysteresis": "Hyster\u00e9za", + "lower": "Doln\u00e1 hranica", + "name": "N\u00e1zov", + "upper": "Horn\u00e1 hranica" + }, + "description": "Nakonfigurovan\u00e1 iba spodn\u00e1 hranica \u2013 zapnite, ke\u010f je hodnota vstupn\u00e9ho sn\u00edma\u010da ni\u017e\u0161ia ako spodn\u00e1 hranica.\nKonfigurovan\u00e1 iba horn\u00e1 hranica - Zapne sa, ke\u010f je hodnota vstupn\u00e9ho sn\u00edma\u010da v\u00e4\u010d\u0161ia ako horn\u00e1 hranica.\nSpodn\u00e1 aj horn\u00e1 hranica nakonfigurovan\u00e1 - Zapne sa, ke\u010f je hodnota vstupn\u00e9ho sn\u00edma\u010da v rozsahu [doln\u00e1 hranica .. horn\u00e1 hranica]." } } - } + }, + "title": "Prahov\u00fd sn\u00edma\u010d" } \ No newline at end of file diff --git a/homeassistant/components/tibber/diagnostics.py b/homeassistant/components/tibber/diagnostics.py index d9e04502509..75c76636f5f 100644 --- a/homeassistant/components/tibber/diagnostics.py +++ b/homeassistant/components/tibber/diagnostics.py @@ -17,15 +17,17 @@ async def async_get_config_entry_diagnostics( diagnostics_data = {} - homes = {} + homes = [] for home in tibber_connection.get_homes(only_active=False): - homes[home.home_id] = { - "last_data_timestamp": home.last_data_timestamp, - "has_active_subscription": home.has_active_subscription, - "has_real_time_consumption": home.has_real_time_consumption, - "last_cons_data_timestamp": home.last_cons_data_timestamp, - "country": home.country, - } + homes.append( + { + "last_data_timestamp": home.last_data_timestamp, + "has_active_subscription": home.has_active_subscription, + "has_real_time_consumption": home.has_real_time_consumption, + "last_cons_data_timestamp": home.last_cons_data_timestamp, + "country": home.country, + } + ) diagnostics_data["homes"] = homes return diagnostics_data diff --git a/homeassistant/components/tibber/manifest.json b/homeassistant/components/tibber/manifest.json index cc296d06ea8..2082d6ddf30 100644 --- a/homeassistant/components/tibber/manifest.json +++ b/homeassistant/components/tibber/manifest.json @@ -3,7 +3,7 @@ "domain": "tibber", "name": "Tibber", "documentation": "https://www.home-assistant.io/integrations/tibber", - "requirements": ["pyTibber==0.26.6"], + "requirements": ["pyTibber==0.26.7"], "codeowners": ["@danielhiversen"], "quality_scale": "silver", "config_flow": true, diff --git a/homeassistant/components/tibber/sensor.py b/homeassistant/components/tibber/sensor.py index 31106990a03..fb21ee10aeb 100644 --- a/homeassistant/components/tibber/sensor.py +++ b/homeassistant/components/tibber/sensor.py @@ -26,13 +26,13 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - ELECTRIC_CURRENT_AMPERE, - ELECTRIC_POTENTIAL_VOLT, - ENERGY_KILO_WATT_HOUR, EVENT_HOMEASSISTANT_STOP, PERCENTAGE, - POWER_WATT, SIGNAL_STRENGTH_DECIBELS, + UnitOfElectricCurrent, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfPower, ) from homeassistant.core import Event, HomeAssistant, callback from homeassistant.exceptions import PlatformNotReady @@ -61,122 +61,122 @@ RT_SENSORS: tuple[SensorEntityDescription, ...] = ( key="averagePower", name="average power", device_class=SensorDeviceClass.POWER, - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, ), SensorEntityDescription( key="power", name="power", device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, ), SensorEntityDescription( key="powerProduction", name="power production", device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, ), SensorEntityDescription( key="minPower", name="min power", device_class=SensorDeviceClass.POWER, - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, ), SensorEntityDescription( key="maxPower", name="max power", device_class=SensorDeviceClass.POWER, - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, ), SensorEntityDescription( key="accumulatedConsumption", name="accumulated consumption", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, state_class=SensorStateClass.TOTAL, ), SensorEntityDescription( key="accumulatedConsumptionLastHour", name="accumulated consumption current hour", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, state_class=SensorStateClass.TOTAL_INCREASING, ), SensorEntityDescription( key="estimatedHourConsumption", name="Estimated consumption current hour", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, ), SensorEntityDescription( key="accumulatedProduction", name="accumulated production", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, state_class=SensorStateClass.TOTAL, ), SensorEntityDescription( key="accumulatedProductionLastHour", name="accumulated production current hour", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, state_class=SensorStateClass.TOTAL_INCREASING, ), SensorEntityDescription( key="lastMeterConsumption", name="last meter consumption", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, state_class=SensorStateClass.TOTAL_INCREASING, ), SensorEntityDescription( key="lastMeterProduction", name="last meter production", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, state_class=SensorStateClass.TOTAL_INCREASING, ), SensorEntityDescription( key="voltagePhase1", name="voltage phase1", device_class=SensorDeviceClass.VOLTAGE, - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="voltagePhase2", name="voltage phase2", device_class=SensorDeviceClass.VOLTAGE, - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="voltagePhase3", name="voltage phase3", device_class=SensorDeviceClass.VOLTAGE, - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="currentL1", name="current L1", device_class=SensorDeviceClass.CURRENT, - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="currentL2", name="current L2", device_class=SensorDeviceClass.CURRENT, - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( key="currentL3", name="current L3", device_class=SensorDeviceClass.CURRENT, - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( @@ -219,7 +219,7 @@ SENSORS: tuple[SensorEntityDescription, ...] = ( key="peak_hour", name="Monthly peak hour consumption", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, ), SensorEntityDescription( key="peak_hour_time", @@ -230,7 +230,7 @@ SENSORS: tuple[SensorEntityDescription, ...] = ( key="month_cons", name="Monthly net consumption", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, state_class=SensorStateClass.TOTAL_INCREASING, ), ) @@ -553,7 +553,7 @@ class TibberRtDataCoordinator(DataUpdateCoordinator): return self.data.get("data", {}).get("liveMeasurement") -class TibberDataCoordinator(DataUpdateCoordinator): +class TibberDataCoordinator(DataUpdateCoordinator[None]): """Handle Tibber data and insert statistics.""" def __init__(self, hass: HomeAssistant, tibber_connection: tibber.Tibber) -> None: @@ -575,12 +575,12 @@ class TibberDataCoordinator(DataUpdateCoordinator): async def _insert_statistics(self) -> None: """Insert Tibber statistics.""" for home in self._tibber_connection.get_homes(): - sensors = [] + sensors: list[tuple[str, bool, str]] = [] if home.hourly_consumption_data: - sensors.append(("consumption", False, ENERGY_KILO_WATT_HOUR)) + sensors.append(("consumption", False, UnitOfEnergy.KILO_WATT_HOUR)) sensors.append(("totalCost", False, home.currency)) if home.hourly_production_data: - sensors.append(("production", True, ENERGY_KILO_WATT_HOUR)) + sensors.append(("production", True, UnitOfEnergy.KILO_WATT_HOUR)) sensors.append(("profit", True, home.currency)) for sensor_type, is_production, unit in sensors: diff --git a/homeassistant/components/tibber/translations/pt.json b/homeassistant/components/tibber/translations/pt.json index 50b5806457b..00a58ec610b 100644 --- a/homeassistant/components/tibber/translations/pt.json +++ b/homeassistant/components/tibber/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "O servi\u00e7o j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_access_token": "Token de acesso inv\u00e1lido" }, "step": { diff --git a/homeassistant/components/tibber/translations/sk.json b/homeassistant/components/tibber/translations/sk.json index bb83eca3402..a7adbdfd0b9 100644 --- a/homeassistant/components/tibber/translations/sk.json +++ b/homeassistant/components/tibber/translations/sk.json @@ -12,7 +12,8 @@ "user": { "data": { "access_token": "Pr\u00edstupov\u00fd token" - } + }, + "description": "Zadajte svoj pr\u00edstupov\u00fd token z https://developer.tibber.com/settings/accesstoken" } } } diff --git a/homeassistant/components/tile/diagnostics.py b/homeassistant/components/tile/diagnostics.py index 8654bf44680..dda2c3367e3 100644 --- a/homeassistant/components/tile/diagnostics.py +++ b/homeassistant/components/tile/diagnostics.py @@ -5,14 +5,13 @@ from typing import Any from homeassistant.components.diagnostics import async_redact_data from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE +from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE, CONF_UUID from homeassistant.core import HomeAssistant from . import TileData from .const import DOMAIN CONF_ALTITUDE = "altitude" -CONF_UUID = "uuid" TO_REDACT = { CONF_ALTITUDE, diff --git a/homeassistant/components/tile/translations/ko.json b/homeassistant/components/tile/translations/ko.json index d592fef112c..ba8ff0bba62 100644 --- a/homeassistant/components/tile/translations/ko.json +++ b/homeassistant/components/tile/translations/ko.json @@ -1,12 +1,18 @@ { "config": { "abort": { - "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" }, "error": { "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" }, "step": { + "reauth_confirm": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638" + } + }, "user": { "data": { "password": "\ube44\ubc00\ubc88\ud638", diff --git a/homeassistant/components/tile/translations/pt.json b/homeassistant/components/tile/translations/pt.json index e883bd875e1..c86472cb6ba 100644 --- a/homeassistant/components/tile/translations/pt.json +++ b/homeassistant/components/tile/translations/pt.json @@ -15,7 +15,7 @@ "user": { "data": { "password": "Palavra-passe", - "username": "Email" + "username": "" }, "title": "Configurar Tile" } diff --git a/homeassistant/components/tile/translations/sk.json b/homeassistant/components/tile/translations/sk.json index c14faaae46e..7a0c3bf4cd5 100644 --- a/homeassistant/components/tile/translations/sk.json +++ b/homeassistant/components/tile/translations/sk.json @@ -11,13 +11,25 @@ "reauth_confirm": { "data": { "password": "Heslo" - } + }, + "title": "Znova overte dla\u017edicu" }, "user": { "data": { "password": "Heslo", "username": "Email" - } + }, + "title": "Konfigur\u00e1cia dla\u017edice" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "show_inactive": "Zobrazi\u0165 neakt\u00edvne dla\u017edice" + }, + "title": "Konfigur\u00e1cia dla\u017edice" } } } diff --git a/homeassistant/components/tilt_ble/sensor.py b/homeassistant/components/tilt_ble/sensor.py index bddae2ca027..be0e9e66037 100644 --- a/homeassistant/components/tilt_ble/sensor.py +++ b/homeassistant/components/tilt_ble/sensor.py @@ -19,7 +19,7 @@ from homeassistant.components.sensor import ( SensorEntityDescription, SensorStateClass, ) -from homeassistant.const import SIGNAL_STRENGTH_DECIBELS_MILLIWATT, TEMP_FAHRENHEIT +from homeassistant.const import SIGNAL_STRENGTH_DECIBELS_MILLIWATT, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.sensor import sensor_device_info_to_hass_device_info @@ -30,7 +30,7 @@ SENSOR_DESCRIPTIONS = { (DeviceClass.TEMPERATURE, Units.TEMP_FAHRENHEIT): SensorEntityDescription( key=f"{DeviceClass.TEMPERATURE}_{Units.TEMP_FAHRENHEIT}", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_FAHRENHEIT, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, state_class=SensorStateClass.MEASUREMENT, ), (DeviceClass.SPECIFIC_GRAVITY, Units.SPECIFIC_GRAVITY): SensorEntityDescription( diff --git a/homeassistant/components/tilt_ble/translations/en.json b/homeassistant/components/tilt_ble/translations/en.json index d24df64f135..f99ec0bbe63 100644 --- a/homeassistant/components/tilt_ble/translations/en.json +++ b/homeassistant/components/tilt_ble/translations/en.json @@ -8,13 +8,13 @@ "flow_title": "{name}", "step": { "bluetooth_confirm": { - "description": "Do you want to setup {name}?" + "description": "Do you want to set up {name}?" }, "user": { "data": { "address": "Device" }, - "description": "Choose a device to setup" + "description": "Choose a device to set up" } } } diff --git a/homeassistant/components/tilt_ble/translations/it.json b/homeassistant/components/tilt_ble/translations/it.json index 501b5095826..3ede7709c00 100644 --- a/homeassistant/components/tilt_ble/translations/it.json +++ b/homeassistant/components/tilt_ble/translations/it.json @@ -14,7 +14,7 @@ "data": { "address": "Dispositivo" }, - "description": "Seleziona un dispositivo da configurare" + "description": "Scegli un dispositivo da configurare" } } } diff --git a/homeassistant/components/tilt_ble/translations/ko.json b/homeassistant/components/tilt_ble/translations/ko.json new file mode 100644 index 00000000000..1a59c05b30c --- /dev/null +++ b/homeassistant/components/tilt_ble/translations/ko.json @@ -0,0 +1,9 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", + "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tilt_ble/translations/no.json b/homeassistant/components/tilt_ble/translations/no.json index 28ec4582177..0a44ef08d80 100644 --- a/homeassistant/components/tilt_ble/translations/no.json +++ b/homeassistant/components/tilt_ble/translations/no.json @@ -8,7 +8,7 @@ "flow_title": "{name}", "step": { "bluetooth_confirm": { - "description": "Vil du konfigurere {name}?" + "description": "Vil du sette opp {name} ?" }, "user": { "data": { diff --git a/homeassistant/components/tilt_ble/translations/pt.json b/homeassistant/components/tilt_ble/translations/pt.json new file mode 100644 index 00000000000..c6a15010072 --- /dev/null +++ b/homeassistant/components/tilt_ble/translations/pt.json @@ -0,0 +1,9 @@ +{ + "config": { + "abort": { + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", + "already_in_progress": "O processo de configura\u00e7\u00e3o j\u00e1 est\u00e1 a decorrer", + "no_devices_found": "Nenhum dispositivo encontrado na rede" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tmb/sensor.py b/homeassistant/components/tmb/sensor.py index cd9453ed430..dd94b4c11b7 100644 --- a/homeassistant/components/tmb/sensor.py +++ b/homeassistant/components/tmb/sensor.py @@ -9,7 +9,7 @@ from tmb import IBus import voluptuous as vol from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity -from homeassistant.const import CONF_NAME, TIME_MINUTES +from homeassistant.const import CONF_NAME, UnitOfTime from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -81,7 +81,7 @@ class TMBSensor(SensorEntity): self._stop = stop self._line = line.upper() self._name = name - self._unit = TIME_MINUTES + self._unit = UnitOfTime.MINUTES self._state = None @property @@ -124,5 +124,6 @@ class TMBSensor(SensorEntity): self._state = self._ibus_client.get_stop_forecast(self._stop, self._line) except HTTPError: _LOGGER.error( - "Unable to fetch data from TMB API. Please check your API keys are valid" + "Unable to fetch data from TMB API. Please check your API keys are" + " valid" ) diff --git a/homeassistant/components/tod/translations/sk.json b/homeassistant/components/tod/translations/sk.json index af15f92c2f2..3af4638a64a 100644 --- a/homeassistant/components/tod/translations/sk.json +++ b/homeassistant/components/tod/translations/sk.json @@ -3,9 +3,24 @@ "step": { "user": { "data": { + "after_time": "\u010cas zapnutia", + "before_time": "\u010cas vypnutia", "name": "N\u00e1zov" + }, + "description": "Vytvorte bin\u00e1rny senzor, ktor\u00fd sa zap\u00edna alebo vyp\u00edna v z\u00e1vislosti od \u010dasu.", + "title": "Pridanie sn\u00edma\u010da denn\u00e9ho \u010dasu" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "after_time": "\u010cas zapnutia", + "before_time": "\u010cas vypnutia" } } } - } + }, + "title": "Sn\u00edma\u010d dennej doby" } \ No newline at end of file diff --git a/homeassistant/components/todoist/calendar.py b/homeassistant/components/todoist/calendar.py index 30391f90d03..9e14a87c82d 100644 --- a/homeassistant/components/todoist/calendar.py +++ b/homeassistant/components/todoist/calendar.py @@ -3,6 +3,7 @@ from __future__ import annotations from datetime import date, datetime, timedelta, timezone import logging +from typing import Any from todoist.api import TodoistAPI import voluptuous as vol @@ -57,7 +58,7 @@ from .const import ( SUMMARY, TASKS, ) -from .types import DueDate +from .types import CalData, CustomProject, DueDate, ProjectData, TodoistEvent _LOGGER = logging.getLogger(__name__) @@ -137,8 +138,8 @@ def setup_platform( for project in projects: # Project is an object, not a dict! # Because of that, we convert what we need to a dict. - project_data = {CONF_NAME: project[NAME], CONF_ID: project[ID]} - project_devices.append(TodoistProjectEntity(hass, project_data, labels, api)) + project_data: ProjectData = {CONF_NAME: project[NAME], CONF_ID: project[ID]} + project_devices.append(TodoistProjectEntity(project_data, labels, api)) # Cache the names so we can easily look up name->ID. project_id_lookup[project[NAME].lower()] = project[ID] @@ -150,7 +151,7 @@ def setup_platform( collaborator_id_lookup[collaborator[FULL_NAME].lower()] = collaborator[ID] # Check config for more projects. - extra_projects = config[CONF_EXTRA_PROJECTS] + extra_projects: list[CustomProject] = config[CONF_EXTRA_PROJECTS] for project in extra_projects: # Special filter: By date project_due_date = project.get(CONF_PROJECT_DUE_DATE) @@ -169,7 +170,6 @@ def setup_platform( # Create the custom project and add it to the devices array. project_devices.append( TodoistProjectEntity( - hass, project, labels, api, @@ -277,14 +277,13 @@ class TodoistProjectEntity(CalendarEntity): def __init__( self, - hass, - data, - labels, - token, - due_date_days=None, - whitelisted_labels=None, - whitelisted_projects=None, - ): + data: ProjectData, + labels: list[str], + token: TodoistAPI, + due_date_days: int | None = None, + whitelisted_labels: list[str] | None = None, + whitelisted_projects: list[int] | None = None, + ) -> None: """Create the Todoist Calendar Entity.""" self.data = TodoistProjectData( data, @@ -294,17 +293,19 @@ class TodoistProjectEntity(CalendarEntity): whitelisted_labels, whitelisted_projects, ) - self._cal_data = {} + self._cal_data: CalData = {} self._name = data[CONF_NAME] - self._attr_unique_id = data.get(CONF_ID) + self._attr_unique_id = ( + str(data[CONF_ID]) if data.get(CONF_ID) is not None else None + ) @property - def event(self) -> CalendarEvent: + def event(self) -> CalendarEvent | None: """Return the next upcoming event.""" return self.data.calendar_event @property - def name(self): + def name(self) -> str: """Return the name of the entity.""" return self._name @@ -326,7 +327,7 @@ class TodoistProjectEntity(CalendarEntity): return await self.data.async_get_events(hass, start_date, end_date) @property - def extra_state_attributes(self): + def extra_state_attributes(self) -> dict[str, Any] | None: """Return the device state attributes.""" if self.data.event is None: # No tasks, we don't REALLY need to show anything. @@ -376,13 +377,13 @@ class TodoistProjectData: def __init__( self, - project_data, - labels, - api, - due_date_days=None, - whitelisted_labels=None, - whitelisted_projects=None, - ): + project_data: ProjectData, + labels: list[str], + api: TodoistAPI, + due_date_days: int | None = None, + whitelisted_labels: list[str] | None = None, + whitelisted_projects: list[int] | None = None, + ) -> None: """Initialize a Todoist Project.""" self.event = None @@ -395,26 +396,23 @@ class TodoistProjectData: self._labels = labels # Not tracked: order, indent, comment_count. - self.all_project_tasks = [] + self.all_project_tasks: list[TodoistEvent] = [] # The days a task can be due (for making lists of everything # due today, or everything due in the next week, for example). + self._due_date_days: timedelta | None = None if due_date_days is not None: self._due_date_days = timedelta(days=due_date_days) - else: - self._due_date_days = None # Only tasks with one of these labels will be included. + self._label_whitelist: list[str] = [] if whitelisted_labels is not None: self._label_whitelist = whitelisted_labels - else: - self._label_whitelist = [] # This project includes only projects with these names. + self._project_id_whitelist: list[int] = [] if whitelisted_projects is not None: self._project_id_whitelist = whitelisted_projects - else: - self._project_id_whitelist = [] @property def calendar_event(self) -> CalendarEvent | None: @@ -502,7 +500,7 @@ class TodoistProjectData: return task @staticmethod - def select_best_task(project_tasks): + def select_best_task(project_tasks: list[TodoistEvent]) -> TodoistEvent: """ Search through a list of events for the "best" event to select. @@ -562,14 +560,16 @@ class TodoistProjectData: continue if proposed_event[PRIORITY] == event[PRIORITY] and ( - proposed_event[END] < event[END] + event[END] is not None and proposed_event[END] < event[END] ): event = proposed_event continue return event - async def async_get_events(self, hass, start_date, end_date): + async def async_get_events( + self, hass: HomeAssistant, start_date: datetime, end_date: datetime + ) -> list[CalendarEvent]: """Get all tasks in a specific time frame.""" if self._id is None: project_task_data = [ @@ -594,13 +594,14 @@ class TodoistProjectData: ) if not due_date: continue - midnight = dt.as_utc( - dt.parse_datetime( - due_date.strftime("%Y-%m-%d") - + "T00:00:00" - + self._api.state["user"]["tz_info"]["gmt_string"] - ) + gmt_string = self._api.state["user"]["tz_info"]["gmt_string"] + local_midnight = dt.parse_datetime( + due_date.strftime(f"%Y-%m-%dT00:00:00{gmt_string}") ) + if local_midnight is not None: + midnight = dt.as_utc(local_midnight) + else: + midnight = due_date.replace(hour=0, minute=0, second=0, microsecond=0) if start_date < due_date < end_date: due_date_value: datetime | date = due_date diff --git a/homeassistant/components/todoist/const.py b/homeassistant/components/todoist/const.py index 26108128ca0..021111b48d7 100644 --- a/homeassistant/components/todoist/const.py +++ b/homeassistant/components/todoist/const.py @@ -1,35 +1,37 @@ """Constants for the Todoist component.""" -CONF_EXTRA_PROJECTS = "custom_projects" -CONF_PROJECT_DUE_DATE = "due_date_days" -CONF_PROJECT_LABEL_WHITELIST = "labels" -CONF_PROJECT_WHITELIST = "include_projects" +from typing import Final + +CONF_EXTRA_PROJECTS: Final = "custom_projects" +CONF_PROJECT_DUE_DATE: Final = "due_date_days" +CONF_PROJECT_LABEL_WHITELIST: Final = "labels" +CONF_PROJECT_WHITELIST: Final = "include_projects" # Calendar Platform: Does this calendar event last all day? -ALL_DAY = "all_day" +ALL_DAY: Final = "all_day" # Attribute: All tasks in this project -ALL_TASKS = "all_tasks" +ALL_TASKS: Final = "all_tasks" # Todoist API: "Completed" flag -- 1 if complete, else 0 -CHECKED = "checked" +CHECKED: Final = "checked" # Attribute: Is this task complete? -COMPLETED = "completed" +COMPLETED: Final = "completed" # Todoist API: What is this task about? # Service Call: What is this task about? -CONTENT = "content" +CONTENT: Final = "content" # Calendar Platform: Get a calendar event's description -DESCRIPTION = "description" +DESCRIPTION: Final = "description" # Calendar Platform: Used in the '_get_date()' method -DATETIME = "dateTime" -DUE = "due" +DATETIME: Final = "dateTime" +DUE: Final = "due" # Service Call: When is this task due (in natural language)? -DUE_DATE_STRING = "due_date_string" +DUE_DATE_STRING: Final = "due_date_string" # Service Call: The language of DUE_DATE_STRING -DUE_DATE_LANG = "due_date_lang" +DUE_DATE_LANG: Final = "due_date_lang" # Service Call: When should user be reminded of this task (in natural language)? -REMINDER_DATE_STRING = "reminder_date_string" +REMINDER_DATE_STRING: Final = "reminder_date_string" # Service Call: The language of REMINDER_DATE_STRING -REMINDER_DATE_LANG = "reminder_date_lang" +REMINDER_DATE_LANG: Final = "reminder_date_lang" # Service Call: The available options of DUE_DATE_LANG -DUE_DATE_VALID_LANGS = [ +DUE_DATE_VALID_LANGS: Final = [ "en", "da", "pl", @@ -47,45 +49,45 @@ DUE_DATE_VALID_LANGS = [ ] # Attribute: When is this task due? # Service Call: When is this task due? -DUE_DATE = "due_date" +DUE_DATE: Final = "due_date" # Service Call: When should user be reminded of this task? -REMINDER_DATE = "reminder_date" +REMINDER_DATE: Final = "reminder_date" # Attribute: Is this task due today? -DUE_TODAY = "due_today" +DUE_TODAY: Final = "due_today" # Calendar Platform: When a calendar event ends -END = "end" +END: Final = "end" # Todoist API: Look up a Project/Label/Task ID -ID = "id" +ID: Final = "id" # Todoist API: Fetch all labels # Service Call: What are the labels attached to this task? -LABELS = "labels" +LABELS: Final = "labels" # Todoist API: "Name" value -NAME = "name" +NAME: Final = "name" # Todoist API: "Full Name" value -FULL_NAME = "full_name" +FULL_NAME: Final = "full_name" # Attribute: Is this task overdue? -OVERDUE = "overdue" +OVERDUE: Final = "overdue" # Attribute: What is this task's priority? # Todoist API: Get a task's priority # Service Call: What is this task's priority? -PRIORITY = "priority" +PRIORITY: Final = "priority" # Todoist API: Look up the Project ID a Task belongs to -PROJECT_ID = "project_id" +PROJECT_ID: Final = "project_id" # Service Call: What Project do you want a Task added to? -PROJECT_NAME = "project" +PROJECT_NAME: Final = "project" # Todoist API: Fetch all Projects -PROJECTS = "projects" +PROJECTS: Final = "projects" # Calendar Platform: When does a calendar event start? -START = "start" +START: Final = "start" # Calendar Platform: What is the next calendar event about? -SUMMARY = "summary" +SUMMARY: Final = "summary" # Todoist API: Fetch all Tasks -TASKS = "items" +TASKS: Final = "items" # Todoist API: "responsible" for a Task -ASSIGNEE = "assignee" +ASSIGNEE: Final = "assignee" # Todoist API: Collaborators in shared projects -COLLABORATORS = "collaborators" +COLLABORATORS: Final = "collaborators" -DOMAIN = "todoist" +DOMAIN: Final = "todoist" -SERVICE_NEW_TASK = "new_task" +SERVICE_NEW_TASK: Final = "new_task" diff --git a/homeassistant/components/todoist/types.py b/homeassistant/components/todoist/types.py index b9409e44daa..fd3b2e889ca 100644 --- a/homeassistant/components/todoist/types.py +++ b/homeassistant/components/todoist/types.py @@ -1,6 +1,7 @@ """Types for the Todoist component.""" from __future__ import annotations +from datetime import datetime from typing import TypedDict @@ -12,3 +13,40 @@ class DueDate(TypedDict): lang: str string: str timezone: str | None + + +class ProjectData(TypedDict): + """Dict representing project data.""" + + name: str + id: int | None + + +class CustomProject(TypedDict): + """Dict representing a custom project.""" + + name: str + due_date_days: int | None + include_projects: list[str] | None + labels: list[str] | None + + +class CalData(TypedDict, total=False): + """Dict representing calendar data in todoist.""" + + all_tasks: list[str] + + +class TodoistEvent(TypedDict): + """Dict representing a todoist event.""" + + all_day: bool + completed: bool + description: str + due_today: bool + end: datetime | None + labels: list[str] + overdue: bool + priority: int + start: datetime + summary: str diff --git a/homeassistant/components/tolo/climate.py b/homeassistant/components/tolo/climate.py index e82fe34ab84..3afc641ba22 100644 --- a/homeassistant/components/tolo/climate.py +++ b/homeassistant/components/tolo/climate.py @@ -14,7 +14,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, TEMP_CELSIUS +from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -55,7 +55,7 @@ class SaunaClimate(ToloSaunaCoordinatorEntity, ClimateEntity): | ClimateEntityFeature.FAN_MODE ) _attr_target_temperature_step = 1 - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS def __init__( self, coordinator: ToloSaunaUpdateCoordinator, entry: ConfigEntry diff --git a/homeassistant/components/tolo/number.py b/homeassistant/components/tolo/number.py index a6767a50814..853f06ea8c1 100644 --- a/homeassistant/components/tolo/number.py +++ b/homeassistant/components/tolo/number.py @@ -10,7 +10,7 @@ from tololib.message_info import SettingsInfo from homeassistant.components.number import NumberEntity, NumberEntityDescription from homeassistant.config_entries import ConfigEntry -from homeassistant.const import TIME_MINUTES +from homeassistant.const import UnitOfTime from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -43,7 +43,7 @@ NUMBERS = ( key="power_timer", icon="mdi:power-settings", name="Power Timer", - native_unit_of_measurement=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, native_max_value=POWER_TIMER_MAX, getter=lambda settings: settings.power_timer, setter=lambda client, value: client.set_power_timer(value), @@ -52,7 +52,7 @@ NUMBERS = ( key="salt_bath_timer", icon="mdi:shaker-outline", name="Salt Bath Timer", - native_unit_of_measurement=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, native_max_value=SALT_BATH_TIMER_MAX, getter=lambda settings: settings.salt_bath_timer, setter=lambda client, value: client.set_salt_bath_timer(value), @@ -61,7 +61,7 @@ NUMBERS = ( key="fan_timer", icon="mdi:fan-auto", name="Fan Timer", - native_unit_of_measurement=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, native_max_value=FAN_TIMER_MAX, getter=lambda settings: settings.fan_timer, setter=lambda client, value: client.set_fan_timer(value), diff --git a/homeassistant/components/tolo/select.py b/homeassistant/components/tolo/select.py index a0cdb0ed128..10fb45722cb 100644 --- a/homeassistant/components/tolo/select.py +++ b/homeassistant/components/tolo/select.py @@ -27,11 +27,11 @@ async def async_setup_entry( class ToloLampModeSelect(ToloSaunaCoordinatorEntity, SelectEntity): """TOLO Sauna lamp mode select.""" - _attr_device_class = "tolo__lamp_mode" _attr_entity_category = EntityCategory.CONFIG _attr_icon = "mdi:lightbulb-multiple-outline" _attr_name = "Lamp Mode" _attr_options = [lamp_mode.name.lower() for lamp_mode in LampMode] + _attr_translation_key = "lamp_mode" def __init__( self, coordinator: ToloSaunaUpdateCoordinator, entry: ConfigEntry diff --git a/homeassistant/components/tolo/sensor.py b/homeassistant/components/tolo/sensor.py index 714667cc1f8..6d496b416ff 100644 --- a/homeassistant/components/tolo/sensor.py +++ b/homeassistant/components/tolo/sensor.py @@ -13,7 +13,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import PERCENTAGE, TEMP_CELSIUS, TIME_MINUTES +from homeassistant.const import PERCENTAGE, UnitOfTemperature, UnitOfTime from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -54,7 +54,7 @@ SENSORS = ( device_class=SensorDeviceClass.TEMPERATURE, entity_category=EntityCategory.DIAGNOSTIC, name="Tank Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, getter=lambda status: status.tank_temperature, availability_checker=None, ), @@ -63,7 +63,7 @@ SENSORS = ( entity_category=EntityCategory.DIAGNOSTIC, icon="mdi:power-settings", name="Power Timer", - native_unit_of_measurement=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, getter=lambda status: status.power_timer, availability_checker=lambda settings, status: status.power_on and settings.power_timer is not None, @@ -73,7 +73,7 @@ SENSORS = ( entity_category=EntityCategory.DIAGNOSTIC, icon="mdi:shaker-outline", name="Salt Bath Timer", - native_unit_of_measurement=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, getter=lambda status: status.salt_bath_timer, availability_checker=lambda settings, status: status.salt_bath_on and settings.salt_bath_timer is not None, @@ -83,7 +83,7 @@ SENSORS = ( entity_category=EntityCategory.DIAGNOSTIC, icon="mdi:fan-auto", name="Fan Timer", - native_unit_of_measurement=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, getter=lambda status: status.fan_timer, availability_checker=lambda settings, status: status.fan_on and settings.fan_timer is not None, diff --git a/homeassistant/components/tolo/strings.json b/homeassistant/components/tolo/strings.json index 7a8ed22da16..5e6647edae4 100644 --- a/homeassistant/components/tolo/strings.json +++ b/homeassistant/components/tolo/strings.json @@ -18,5 +18,15 @@ "abort": { "already_configured": "[%key:common::config_flow::abort::already_configured_device%]" } + }, + "entity": { + "select": { + "lamp_mode": { + "state": { + "automatic": "Automatic", + "manual": "Manual" + } + } + } } } diff --git a/homeassistant/components/tolo/strings.select.json b/homeassistant/components/tolo/strings.select.json deleted file mode 100644 index 72a7a2c00e2..00000000000 --- a/homeassistant/components/tolo/strings.select.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "state": { - "tolo__lamp_mode": { - "automatic": "automatic", - "manual": "manual" - } - } -} diff --git a/homeassistant/components/tolo/translations/ca.json b/homeassistant/components/tolo/translations/ca.json index 567f4467f0b..636f35ad803 100644 --- a/homeassistant/components/tolo/translations/ca.json +++ b/homeassistant/components/tolo/translations/ca.json @@ -18,5 +18,15 @@ "description": "Introdueix el nom d'amfitri\u00f3 o l'adre\u00e7a IP del dispositiu TOLO Sauna." } } + }, + "entity": { + "select": { + "lamp_mode": { + "state": { + "automatic": "Autom\u00e0tic", + "manual": "Manual" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tolo/translations/de.json b/homeassistant/components/tolo/translations/de.json index 3fc78255507..c5bb0d96212 100644 --- a/homeassistant/components/tolo/translations/de.json +++ b/homeassistant/components/tolo/translations/de.json @@ -18,5 +18,15 @@ "description": "Gib den Hostnamen oder die IP-Adresse deines TOLO Sauna-Ger\u00e4ts ein." } } + }, + "entity": { + "select": { + "lamp_mode": { + "state": { + "automatic": "Automatisch", + "manual": "Manuell" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tolo/translations/el.json b/homeassistant/components/tolo/translations/el.json index 16604a30baa..679493b3746 100644 --- a/homeassistant/components/tolo/translations/el.json +++ b/homeassistant/components/tolo/translations/el.json @@ -18,5 +18,15 @@ "description": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03bf \u03cc\u03bd\u03bf\u03bc\u03b1 \u03ba\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03bf\u03cd \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae \u03ae \u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 TOLO Sauna." } } + }, + "entity": { + "select": { + "lamp_mode": { + "state": { + "automatic": "\u0391\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03bf", + "manual": "\u03a7\u03b5\u03b9\u03c1\u03bf\u03ba\u03af\u03bd\u03b7\u03c4\u03bf" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tolo/translations/en.json b/homeassistant/components/tolo/translations/en.json index dea5a3b30df..b637f7c89fb 100644 --- a/homeassistant/components/tolo/translations/en.json +++ b/homeassistant/components/tolo/translations/en.json @@ -9,7 +9,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Do you want to start set up?" + "description": "Do you want to start setup?" }, "user": { "data": { @@ -18,5 +18,15 @@ "description": "Enter the hostname or IP address of your TOLO Sauna device." } } + }, + "entity": { + "select": { + "lamp_mode": { + "state": { + "automatic": "Automatic", + "manual": "Manual" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tolo/translations/es.json b/homeassistant/components/tolo/translations/es.json index 9f08b482d3e..a10bb46ac6b 100644 --- a/homeassistant/components/tolo/translations/es.json +++ b/homeassistant/components/tolo/translations/es.json @@ -18,5 +18,15 @@ "description": "Introduce el nombre de host o la direcci\u00f3n IP de tu dispositivo TOLO Sauna." } } + }, + "entity": { + "select": { + "lamp_mode": { + "state": { + "automatic": "Autom\u00e1tico", + "manual": "Manual" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tolo/translations/et.json b/homeassistant/components/tolo/translations/et.json index ed09907df82..a03e2a9f04d 100644 --- a/homeassistant/components/tolo/translations/et.json +++ b/homeassistant/components/tolo/translations/et.json @@ -18,5 +18,15 @@ "description": "Sisesta oma TOLO Sauna seadme hostinimi v\u00f5i IP-aadress." } } + }, + "entity": { + "select": { + "lamp_mode": { + "state": { + "automatic": "Automaatne", + "manual": "K\u00e4sitsi" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tolo/translations/hu.json b/homeassistant/components/tolo/translations/hu.json index 246edba220d..18ce0d6936b 100644 --- a/homeassistant/components/tolo/translations/hu.json +++ b/homeassistant/components/tolo/translations/hu.json @@ -18,5 +18,15 @@ "description": "Adja meg a TOLO Sauna eszk\u00f6z\u00e9nek hostnev\u00e9t vagy IP-c\u00edm\u00e9t." } } + }, + "entity": { + "select": { + "lamp_mode": { + "state": { + "automatic": "Automatikus", + "manual": "Manu\u00e1lis" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tolo/translations/id.json b/homeassistant/components/tolo/translations/id.json index 88e22f45b0b..2c781074bfe 100644 --- a/homeassistant/components/tolo/translations/id.json +++ b/homeassistant/components/tolo/translations/id.json @@ -18,5 +18,15 @@ "description": "Masukkan nama host atau alamat IP perangkat TOLO Sauna." } } + }, + "entity": { + "select": { + "lamp_mode": { + "state": { + "automatic": "Otomatis", + "manual": "Manual" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tolo/translations/it.json b/homeassistant/components/tolo/translations/it.json index 169aaa7ebce..c29ffb0dfaf 100644 --- a/homeassistant/components/tolo/translations/it.json +++ b/homeassistant/components/tolo/translations/it.json @@ -9,7 +9,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Vuoi iniziare la configurazione?" + "description": "Vuoi avviare la configurazione?" }, "user": { "data": { @@ -18,5 +18,15 @@ "description": "Digita il nome host o l'indirizzo IP del tuo dispositivo TOLO Sauna." } } + }, + "entity": { + "select": { + "lamp_mode": { + "state": { + "automatic": "Automatico", + "manual": "Manuale" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tolo/translations/ko.json b/homeassistant/components/tolo/translations/ko.json index 20ad990e862..e22707a16a5 100644 --- a/homeassistant/components/tolo/translations/ko.json +++ b/homeassistant/components/tolo/translations/ko.json @@ -1,8 +1,19 @@ { "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" + }, "step": { "confirm": { "description": "\uc124\uc815\uc744 \uc2dc\uc791\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" + }, + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8" + } } } } diff --git a/homeassistant/components/tolo/translations/no.json b/homeassistant/components/tolo/translations/no.json index 16b9c0f2ead..8c3063b1e91 100644 --- a/homeassistant/components/tolo/translations/no.json +++ b/homeassistant/components/tolo/translations/no.json @@ -18,5 +18,15 @@ "description": "Skriv inn vertsnavnet eller IP-adressen til TOLO Sauna-enheten." } } + }, + "entity": { + "select": { + "lamp_mode": { + "state": { + "automatic": "Automatisk", + "manual": "Manuell" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tolo/translations/pl.json b/homeassistant/components/tolo/translations/pl.json index 0bf5b5f75f5..f8562eb750b 100644 --- a/homeassistant/components/tolo/translations/pl.json +++ b/homeassistant/components/tolo/translations/pl.json @@ -18,5 +18,15 @@ "description": "Wprowad\u017a nazw\u0119 hosta lub adres IP urz\u0105dzenia TOLO Sauna." } } + }, + "entity": { + "select": { + "lamp_mode": { + "state": { + "automatic": "automatyczny", + "manual": "r\u0119czny" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tolo/translations/pt-BR.json b/homeassistant/components/tolo/translations/pt-BR.json index 457fb339b2d..0b144188868 100644 --- a/homeassistant/components/tolo/translations/pt-BR.json +++ b/homeassistant/components/tolo/translations/pt-BR.json @@ -18,5 +18,15 @@ "description": "Digite o nome do host ou o endere\u00e7o IP do seu dispositivo TOLO Sauna." } } + }, + "entity": { + "select": { + "lamp_mode": { + "state": { + "automatic": "Autom\u00e1tico", + "manual": "Manual" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tolo/translations/ru.json b/homeassistant/components/tolo/translations/ru.json index e9ff9c6552f..37365418211 100644 --- a/homeassistant/components/tolo/translations/ru.json +++ b/homeassistant/components/tolo/translations/ru.json @@ -18,5 +18,15 @@ "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043c\u044f \u0445\u043e\u0441\u0442\u0430 \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430." } } + }, + "entity": { + "select": { + "lamp_mode": { + "state": { + "automatic": "\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438", + "manual": "\u0412\u0440\u0443\u0447\u043d\u0443\u044e" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tolo/translations/select.sk.json b/homeassistant/components/tolo/translations/select.sk.json new file mode 100644 index 00000000000..f4681a00cd9 --- /dev/null +++ b/homeassistant/components/tolo/translations/select.sk.json @@ -0,0 +1,8 @@ +{ + "state": { + "tolo__lamp_mode": { + "automatic": "automatick\u00fd", + "manual": "manu\u00e1lny" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tolo/translations/sk.json b/homeassistant/components/tolo/translations/sk.json index dc2e106293a..8d501aefe7d 100644 --- a/homeassistant/components/tolo/translations/sk.json +++ b/homeassistant/components/tolo/translations/sk.json @@ -18,5 +18,15 @@ "description": "Zadajte n\u00e1zov hostite\u013ea alebo IP adresu v\u00e1\u0161ho zariadenia TOLO Sauna." } } + }, + "entity": { + "select": { + "lamp_mode": { + "state": { + "automatic": "Automaticky", + "manual": "Manu\u00e1lny" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tolo/translations/zh-Hant.json b/homeassistant/components/tolo/translations/zh-Hant.json index 96cef3b9605..250f484d84d 100644 --- a/homeassistant/components/tolo/translations/zh-Hant.json +++ b/homeassistant/components/tolo/translations/zh-Hant.json @@ -18,5 +18,15 @@ "description": "\u8f38\u5165 TOLO Sauna \u8a2d\u5099\u4e4b\u4e3b\u6a5f\u540d\u7a31\u6216 IP \u4f4d\u5740\u3002" } } + }, + "entity": { + "select": { + "lamp_mode": { + "state": { + "automatic": "\u81ea\u52d5", + "manual": "\u624b\u52d5" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/sensor.py b/homeassistant/components/tomorrowio/sensor.py index a174d983131..2b7d466d2f0 100644 --- a/homeassistant/components/tomorrowio/sensor.py +++ b/homeassistant/components/tomorrowio/sensor.py @@ -24,9 +24,8 @@ from homeassistant.const import ( CONCENTRATION_PARTS_PER_MILLION, CONF_API_KEY, CONF_NAME, - IRRADIATION_BTUS_PER_HOUR_SQUARE_FOOT, - IRRADIATION_WATTS_PER_SQUARE_METER, PERCENTAGE, + UnitOfIrradiance, UnitOfLength, UnitOfPressure, UnitOfSpeed, @@ -122,9 +121,10 @@ SENSOR_TYPES = ( TomorrowioSensorEntityDescription( key=TMRW_ATTR_SOLAR_GHI, name="Global Horizontal Irradiance", - unit_imperial=IRRADIATION_BTUS_PER_HOUR_SQUARE_FOOT, - unit_metric=IRRADIATION_WATTS_PER_SQUARE_METER, + unit_imperial=UnitOfIrradiance.BTUS_PER_HOUR_SQUARE_FOOT, + unit_metric=UnitOfIrradiance.WATTS_PER_SQUARE_METER, imperial_conversion=(1 / 3.15459), + device_class=SensorDeviceClass.IRRADIANCE, ), # Data comes in as km, convert to miles for imperial TomorrowioSensorEntityDescription( @@ -169,7 +169,9 @@ SENSOR_TYPES = ( key=TMRW_ATTR_PRECIPITATION_TYPE, name="Precipitation Type", value_map=PrecipitationType, - device_class="tomorrowio__precipitation_type", + device_class=SensorDeviceClass.ENUM, + options=["freezing_rain", "ice_pellets", "none", "rain", "snow"], + translation_key="precipitation_type", icon="mdi:weather-snowy-rainy", ), # Data comes in as ppb, convert to µg/m^3 @@ -233,7 +235,16 @@ SENSOR_TYPES = ( key=TMRW_ATTR_EPA_HEALTH_CONCERN, name="US EPA Health Concern", value_map=HealthConcernType, - device_class="tomorrowio__health_concern", + device_class=SensorDeviceClass.ENUM, + options=[ + "good", + "hazardous", + "moderate", + "unhealthy_for_sensitive_groups", + "unhealthy", + "very_unhealthy", + ], + translation_key="health_concern", icon="mdi:hospital", ), TomorrowioSensorEntityDescription( @@ -250,28 +261,43 @@ SENSOR_TYPES = ( key=TMRW_ATTR_CHINA_HEALTH_CONCERN, name="China MEP Health Concern", value_map=HealthConcernType, - device_class="tomorrowio__health_concern", + device_class=SensorDeviceClass.ENUM, + options=[ + "good", + "hazardous", + "moderate", + "unhealthy_for_sensitive_groups", + "unhealthy", + "very_unhealthy", + ], + translation_key="health_concern", icon="mdi:hospital", ), TomorrowioSensorEntityDescription( key=TMRW_ATTR_POLLEN_TREE, name="Tree Pollen Index", value_map=PollenIndex, - device_class="tomorrowio__pollen_index", + device_class=SensorDeviceClass.ENUM, + options=["high", "low", "medium", "none", "very_high", "very_low"], + translation_key="pollen_index", icon="mdi:flower-pollen", ), TomorrowioSensorEntityDescription( key=TMRW_ATTR_POLLEN_WEED, name="Weed Pollen Index", value_map=PollenIndex, - device_class="tomorrowio__pollen_index", + device_class=SensorDeviceClass.ENUM, + options=["high", "low", "medium", "none", "very_high", "very_low"], + translation_key="pollen_index", icon="mdi:flower-pollen", ), TomorrowioSensorEntityDescription( key=TMRW_ATTR_POLLEN_GRASS, name="Grass Pollen Index", value_map=PollenIndex, - device_class="tomorrowio__pollen_index", + device_class=SensorDeviceClass.ENUM, + options=["high", "low", "medium", "none", "very_high", "very_low"], + translation_key="pollen_index", icon="mdi:flower-pollen", ), TomorrowioSensorEntityDescription( diff --git a/homeassistant/components/tomorrowio/strings.json b/homeassistant/components/tomorrowio/strings.json index 30b729e6ef1..3ae70f214bb 100644 --- a/homeassistant/components/tomorrowio/strings.json +++ b/homeassistant/components/tomorrowio/strings.json @@ -27,5 +27,38 @@ } } } + }, + "entity": { + "sensor": { + "health_concern": { + "state": { + "good": "Good", + "moderate": "Moderate", + "unhealthy_for_sensitive_groups": "Unhealthy for Sensitive Groups", + "unhealthy": "Unhealthy", + "very_unhealthy": "Very Unhealthy", + "hazardous": "Hazardous" + } + }, + "pollen_index": { + "state": { + "none": "None", + "very_low": "Very Low", + "low": "Low", + "medium": "Medium", + "high": "High", + "very_high": "Very High" + } + }, + "precipitation_type": { + "state": { + "none": "None", + "rain": "Rain", + "snow": "Snow", + "freezing_rain": "Freezing Rain", + "ice_pellets": "Ice Pellets" + } + } + } } } diff --git a/homeassistant/components/tomorrowio/strings.sensor.json b/homeassistant/components/tomorrowio/strings.sensor.json deleted file mode 100644 index a1ee6d1b744..00000000000 --- a/homeassistant/components/tomorrowio/strings.sensor.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "state": { - "tomorrowio__pollen_index": { - "none": "None", - "very_low": "Very Low", - "low": "Low", - "medium": "Medium", - "high": "High", - "very_high": "Very High" - }, - "tomorrowio__health_concern": { - "good": "Good", - "moderate": "Moderate", - "unhealthy_for_sensitive_groups": "Unhealthy for Sensitive Groups", - "unhealthy": "Unhealthy", - "very_unhealthy": "Very Unhealthy", - "hazardous": "Hazardous" - }, - "tomorrowio__precipitation_type": { - "none": "None", - "rain": "Rain", - "snow": "Snow", - "freezing_rain": "Freezing Rain", - "ice_pellets": "Ice Pellets" - } - } -} diff --git a/homeassistant/components/tomorrowio/translations/ca.json b/homeassistant/components/tomorrowio/translations/ca.json index 6a1289fd7a4..64a3457b044 100644 --- a/homeassistant/components/tomorrowio/translations/ca.json +++ b/homeassistant/components/tomorrowio/translations/ca.json @@ -17,6 +17,39 @@ } } }, + "entity": { + "sensor": { + "health_concern": { + "state": { + "good": "Bo", + "hazardous": "Perill\u00f3s", + "moderate": "Moderat", + "unhealthy": "No saludable", + "unhealthy_for_sensitive_groups": "No saludable per a grups sensibles", + "very_unhealthy": "Gens saludable" + } + }, + "pollen_index": { + "state": { + "high": "Alt", + "low": "Baix", + "medium": "Mitj\u00e0", + "none": "Cap", + "very_high": "Molt alt", + "very_low": "Molt baix" + } + }, + "precipitation_type": { + "state": { + "freezing_rain": "Pluja congelada", + "ice_pellets": "Gran\u00eds", + "none": "Cap", + "rain": "Pluja", + "snow": "Neu" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/tomorrowio/translations/de.json b/homeassistant/components/tomorrowio/translations/de.json index d65b1115e4b..ad5c14dd4e0 100644 --- a/homeassistant/components/tomorrowio/translations/de.json +++ b/homeassistant/components/tomorrowio/translations/de.json @@ -17,6 +17,39 @@ } } }, + "entity": { + "sensor": { + "health_concern": { + "state": { + "good": "Gut", + "hazardous": "Gef\u00e4hrlich", + "moderate": "M\u00e4\u00dfig", + "unhealthy": "Ungesund", + "unhealthy_for_sensitive_groups": "Ungesund f\u00fcr sensible Gruppen", + "very_unhealthy": "Sehr ungesund" + } + }, + "pollen_index": { + "state": { + "high": "Hoch", + "low": "Niedrig", + "medium": "Mittel", + "none": "Keine", + "very_high": "Sehr hoch", + "very_low": "Sehr niedrig" + } + }, + "precipitation_type": { + "state": { + "freezing_rain": "Gefrierender Regen", + "ice_pellets": "Graupel", + "none": "Keine", + "rain": "Regen", + "snow": "Schnee" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/tomorrowio/translations/el.json b/homeassistant/components/tomorrowio/translations/el.json index aecdca57c9f..a6e4d7ff959 100644 --- a/homeassistant/components/tomorrowio/translations/el.json +++ b/homeassistant/components/tomorrowio/translations/el.json @@ -17,6 +17,39 @@ } } }, + "entity": { + "sensor": { + "health_concern": { + "state": { + "good": "\u039a\u03b1\u03bb\u03cc", + "hazardous": "\u0395\u03c0\u03b9\u03ba\u03af\u03bd\u03b4\u03c5\u03bd\u03bf", + "moderate": "\u039c\u03ad\u03c4\u03c1\u03b9\u03bf", + "unhealthy": "\u0391\u03bd\u03b8\u03c5\u03b3\u03b9\u03b5\u03b9\u03bd\u03cc", + "unhealthy_for_sensitive_groups": "\u0391\u03bd\u03b8\u03c5\u03b3\u03b9\u03b5\u03b9\u03bd\u03cc \u03b3\u03b9\u03b1 \u03b5\u03c5\u03b1\u03af\u03c3\u03b8\u03b7\u03c4\u03b5\u03c2 \u03bf\u03bc\u03ac\u03b4\u03b5\u03c2", + "very_unhealthy": "\u03a0\u03bf\u03bb\u03cd \u0391\u03bd\u03b8\u03c5\u03b3\u03b9\u03b5\u03b9\u03bd\u03cc" + } + }, + "pollen_index": { + "state": { + "high": "\u03a5\u03c8\u03b7\u03bb\u03cc", + "low": "\u03a7\u03b1\u03bc\u03b7\u03bb\u03cc", + "medium": "\u039c\u03b5\u03c3\u03b1\u03af\u03bf", + "none": "\u03a4\u03af\u03c0\u03bf\u03c4\u03b1", + "very_high": "\u03a0\u03bf\u03bb\u03cd \u03c5\u03c8\u03b7\u03bb\u03cc", + "very_low": "\u03a0\u03bf\u03bb\u03cd \u03c7\u03b1\u03bc\u03b7\u03bb\u03cc" + } + }, + "precipitation_type": { + "state": { + "freezing_rain": "\u03a0\u03b1\u03b3\u03c9\u03bc\u03ad\u03bd\u03b7 \u03b2\u03c1\u03bf\u03c7\u03ae", + "ice_pellets": "\u03a7\u03b1\u03bb\u03ac\u03b6\u03b9", + "none": "\u03a4\u03af\u03c0\u03bf\u03c4\u03b1", + "rain": "\u0392\u03c1\u03bf\u03c7\u03ae", + "snow": "\u03a7\u03b9\u03cc\u03bd\u03b9" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/tomorrowio/translations/en.json b/homeassistant/components/tomorrowio/translations/en.json index f3706dd6a73..15d1aadeaaf 100644 --- a/homeassistant/components/tomorrowio/translations/en.json +++ b/homeassistant/components/tomorrowio/translations/en.json @@ -17,6 +17,39 @@ } } }, + "entity": { + "sensor": { + "health_concern": { + "state": { + "good": "Good", + "hazardous": "Hazardous", + "moderate": "Moderate", + "unhealthy": "Unhealthy", + "unhealthy_for_sensitive_groups": "Unhealthy for Sensitive Groups", + "very_unhealthy": "Very Unhealthy" + } + }, + "pollen_index": { + "state": { + "high": "High", + "low": "Low", + "medium": "Medium", + "none": "None", + "very_high": "Very High", + "very_low": "Very Low" + } + }, + "precipitation_type": { + "state": { + "freezing_rain": "Freezing Rain", + "ice_pellets": "Ice Pellets", + "none": "None", + "rain": "Rain", + "snow": "Snow" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/tomorrowio/translations/es.json b/homeassistant/components/tomorrowio/translations/es.json index c18fdffebbc..50a918a6119 100644 --- a/homeassistant/components/tomorrowio/translations/es.json +++ b/homeassistant/components/tomorrowio/translations/es.json @@ -17,6 +17,39 @@ } } }, + "entity": { + "sensor": { + "health_concern": { + "state": { + "good": "Bueno", + "hazardous": "Peligroso", + "moderate": "Moderado", + "unhealthy": "Poco saludable", + "unhealthy_for_sensitive_groups": "No es saludable para grupos sensibles", + "very_unhealthy": "Muy poco saludable" + } + }, + "pollen_index": { + "state": { + "high": "Alto", + "low": "Bajo", + "medium": "Medio", + "none": "Ninguno", + "very_high": "Muy alto", + "very_low": "Muy bajo" + } + }, + "precipitation_type": { + "state": { + "freezing_rain": "Lluvia helada", + "ice_pellets": "Granizo", + "none": "Ninguno", + "rain": "Lluvia", + "snow": "Nieve" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/tomorrowio/translations/et.json b/homeassistant/components/tomorrowio/translations/et.json index 0ae1ea43448..3270fbe7e14 100644 --- a/homeassistant/components/tomorrowio/translations/et.json +++ b/homeassistant/components/tomorrowio/translations/et.json @@ -17,6 +17,39 @@ } } }, + "entity": { + "sensor": { + "health_concern": { + "state": { + "good": "Hea", + "hazardous": "Ohtlik", + "moderate": "M\u00f5\u00f5dukas", + "unhealthy": "Ebatervislik", + "unhealthy_for_sensitive_groups": "Ebatervislik riskir\u00fchmale", + "very_unhealthy": "V\u00e4ga ebatervislik" + } + }, + "pollen_index": { + "state": { + "high": "K\u00f5rge", + "low": "Madal", + "medium": "Keskmine", + "none": "Puudub", + "very_high": "V\u00e4ga k\u00f5rge", + "very_low": "V\u00e4ga madal" + } + }, + "precipitation_type": { + "state": { + "freezing_rain": "J\u00e4\u00e4vihm", + "ice_pellets": "J\u00e4\u00e4kruubid", + "none": "Puudub", + "rain": "Vihm", + "snow": "Lumi" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/tomorrowio/translations/hu.json b/homeassistant/components/tomorrowio/translations/hu.json index 4d906cd1e03..4a8744405fe 100644 --- a/homeassistant/components/tomorrowio/translations/hu.json +++ b/homeassistant/components/tomorrowio/translations/hu.json @@ -17,6 +17,39 @@ } } }, + "entity": { + "sensor": { + "health_concern": { + "state": { + "good": "J\u00f3", + "hazardous": "Vesz\u00e9lyes", + "moderate": "M\u00e9rs\u00e9kelt", + "unhealthy": "Eg\u00e9szs\u00e9gtelen", + "unhealthy_for_sensitive_groups": "Eg\u00e9szs\u00e9gtelen \u00e9rz\u00e9keny csoportok sz\u00e1m\u00e1ra", + "very_unhealthy": "Nagyon eg\u00e9szs\u00e9gtelen" + } + }, + "pollen_index": { + "state": { + "high": "Magas", + "low": "Alacsony", + "medium": "K\u00f6zepes", + "none": "Nincs", + "very_high": "Nagyon magas", + "very_low": "Nagyon alacsony" + } + }, + "precipitation_type": { + "state": { + "freezing_rain": "Havas es\u0151", + "ice_pellets": "\u00d3nos es\u0151", + "none": "Nincs", + "rain": "Es\u0151", + "snow": "Havaz\u00e1s" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/tomorrowio/translations/id.json b/homeassistant/components/tomorrowio/translations/id.json index 364e697783f..aadfb6c453f 100644 --- a/homeassistant/components/tomorrowio/translations/id.json +++ b/homeassistant/components/tomorrowio/translations/id.json @@ -17,6 +17,39 @@ } } }, + "entity": { + "sensor": { + "health_concern": { + "state": { + "good": "Bagus", + "hazardous": "Berbahaya", + "moderate": "Sedang", + "unhealthy": "Tidak Sehat", + "unhealthy_for_sensitive_groups": "Tidak Sehat untuk Kelompok Sensitif", + "very_unhealthy": "Sangat Tidak Sehat" + } + }, + "pollen_index": { + "state": { + "high": "Tinggi", + "low": "Rendah", + "medium": "Sedang", + "none": "Tidak Ada", + "very_high": "Sangat Tinggi", + "very_low": "Sangat Rendah" + } + }, + "precipitation_type": { + "state": { + "freezing_rain": "Hujan Beku", + "ice_pellets": "Hujan Es", + "none": "Tidak Ada", + "rain": "Hujan", + "snow": "Salju" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/tomorrowio/translations/it.json b/homeassistant/components/tomorrowio/translations/it.json index 8446eba5336..2158b4a6fd8 100644 --- a/homeassistant/components/tomorrowio/translations/it.json +++ b/homeassistant/components/tomorrowio/translations/it.json @@ -17,6 +17,39 @@ } } }, + "entity": { + "sensor": { + "health_concern": { + "state": { + "good": "Buono", + "hazardous": "Pericoloso", + "moderate": "Moderato", + "unhealthy": "Malsano", + "unhealthy_for_sensitive_groups": "Malsano per i gruppi sensibili", + "very_unhealthy": "Molto malsano" + } + }, + "pollen_index": { + "state": { + "high": "Alto", + "low": "Basso", + "medium": "Medio", + "none": "Nessuno", + "very_high": "Molto alto", + "very_low": "Molto basso" + } + }, + "precipitation_type": { + "state": { + "freezing_rain": "Pioggia gelata", + "ice_pellets": "Pellet di ghiaccio", + "none": "Nessuno", + "rain": "Pioggia", + "snow": "Neve" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/tomorrowio/translations/ko.json b/homeassistant/components/tomorrowio/translations/ko.json new file mode 100644 index 00000000000..0bd274537f1 --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/ko.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_api_key": "API \ud0a4\uac00 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "api_key": "API \ud0a4", + "location": "\uc704\uce58", + "name": "\uc774\ub984" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/nl.json b/homeassistant/components/tomorrowio/translations/nl.json index 8b6b585ef11..aa2141c202c 100644 --- a/homeassistant/components/tomorrowio/translations/nl.json +++ b/homeassistant/components/tomorrowio/translations/nl.json @@ -17,6 +17,29 @@ } } }, + "entity": { + "sensor": { + "health_concern": { + "state": { + "good": "Goed" + } + }, + "pollen_index": { + "state": { + "high": "Hoog", + "low": "Laag", + "none": "Geen" + } + }, + "precipitation_type": { + "state": { + "none": "Geen", + "rain": "Regen", + "snow": "Sneeuw" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/tomorrowio/translations/no.json b/homeassistant/components/tomorrowio/translations/no.json index acab85a03a4..32bef37e41c 100644 --- a/homeassistant/components/tomorrowio/translations/no.json +++ b/homeassistant/components/tomorrowio/translations/no.json @@ -17,6 +17,39 @@ } } }, + "entity": { + "sensor": { + "health_concern": { + "state": { + "good": "God", + "hazardous": "Risikabel", + "moderate": "Moderat", + "unhealthy": "Usunt", + "unhealthy_for_sensitive_groups": "Usunt for sensitive grupper", + "very_unhealthy": "Veldig usunt" + } + }, + "pollen_index": { + "state": { + "high": "H\u00f8y", + "low": "Lav", + "medium": "Medium", + "none": "Ingen", + "very_high": "Veldig h\u00f8y", + "very_low": "Veldig lav" + } + }, + "precipitation_type": { + "state": { + "freezing_rain": "Underkj\u00f8lt regn", + "ice_pellets": "Is pellets", + "none": "Ingen", + "rain": "Regn", + "snow": "Sn\u00f8" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/tomorrowio/translations/pl.json b/homeassistant/components/tomorrowio/translations/pl.json index b715d9e2cab..77ea5e55dd4 100644 --- a/homeassistant/components/tomorrowio/translations/pl.json +++ b/homeassistant/components/tomorrowio/translations/pl.json @@ -17,6 +17,39 @@ } } }, + "entity": { + "sensor": { + "health_concern": { + "state": { + "good": "dobra", + "hazardous": "niebezpieczne", + "moderate": "umiarkowane", + "unhealthy": "niezdrowe", + "unhealthy_for_sensitive_groups": "niezdrowe dla wra\u017cliwych grup", + "very_unhealthy": "bardzo niezdrowe" + } + }, + "pollen_index": { + "state": { + "high": "wysoki", + "low": "niski", + "medium": "\u015bredni", + "none": "brak", + "very_high": "bardzo wysoki", + "very_low": "bardzo niski" + } + }, + "precipitation_type": { + "state": { + "freezing_rain": "marzn\u0105cy deszcz", + "ice_pellets": "granulki lodu", + "none": "brak", + "rain": "deszcz", + "snow": "\u015bnieg" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/tomorrowio/translations/pt-BR.json b/homeassistant/components/tomorrowio/translations/pt-BR.json index d0b7a33abc9..69b5f0fb894 100644 --- a/homeassistant/components/tomorrowio/translations/pt-BR.json +++ b/homeassistant/components/tomorrowio/translations/pt-BR.json @@ -17,6 +17,39 @@ } } }, + "entity": { + "sensor": { + "health_concern": { + "state": { + "good": "Bom", + "hazardous": "Perigoso", + "moderate": "Moderado", + "unhealthy": "Insalubre", + "unhealthy_for_sensitive_groups": "N\u00e3o saud\u00e1vel para grupos sens\u00edveis", + "very_unhealthy": "Muito insalubre" + } + }, + "pollen_index": { + "state": { + "high": "Alto", + "low": "Baixo", + "medium": "M\u00e9dia", + "none": "Nenhum", + "very_high": "Muito alto", + "very_low": "Muito baixo" + } + }, + "precipitation_type": { + "state": { + "freezing_rain": "Chuva congelante", + "ice_pellets": "Pellets de gelo", + "none": "Nenhum", + "rain": "Chuva", + "snow": "Neve" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/tomorrowio/translations/pt.json b/homeassistant/components/tomorrowio/translations/pt.json index 1e8c651e5e8..6d54783aaec 100644 --- a/homeassistant/components/tomorrowio/translations/pt.json +++ b/homeassistant/components/tomorrowio/translations/pt.json @@ -1,7 +1,7 @@ { "config": { "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "user": { diff --git a/homeassistant/components/tomorrowio/translations/ru.json b/homeassistant/components/tomorrowio/translations/ru.json index eeb7eb58488..1354f159d82 100644 --- a/homeassistant/components/tomorrowio/translations/ru.json +++ b/homeassistant/components/tomorrowio/translations/ru.json @@ -17,6 +17,39 @@ } } }, + "entity": { + "sensor": { + "health_concern": { + "state": { + "good": "\u0425\u043e\u0440\u043e\u0448\u043e", + "hazardous": "\u041e\u043f\u0430\u0441\u043d\u043e", + "moderate": "\u0421\u0440\u0435\u0434\u043d\u0435", + "unhealthy": "\u0412\u0440\u0435\u0434\u043d\u043e", + "unhealthy_for_sensitive_groups": "\u0412\u0440\u0435\u0434\u043d\u043e \u0434\u043b\u044f \u0443\u044f\u0437\u0432\u0438\u043c\u044b\u0445 \u0433\u0440\u0443\u043f\u043f", + "very_unhealthy": "\u041e\u0447\u0435\u043d\u044c \u0432\u0440\u0435\u0434\u043d\u043e" + } + }, + "pollen_index": { + "state": { + "high": "\u0412\u044b\u0441\u043e\u043a\u0438\u0439", + "low": "\u041d\u0438\u0437\u043a\u0438\u0439", + "medium": "\u0421\u0440\u0435\u0434\u043d\u0438\u0439", + "none": "\u041e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442", + "very_high": "\u041e\u0447\u0435\u043d\u044c \u0432\u044b\u0441\u043e\u043a\u0438\u0439", + "very_low": "\u041e\u0447\u0435\u043d\u044c \u043d\u0438\u0437\u043a\u0438\u0439" + } + }, + "precipitation_type": { + "state": { + "freezing_rain": "\u041b\u0435\u0434\u044f\u043d\u043e\u0439 \u0434\u043e\u0436\u0434\u044c", + "ice_pellets": "\u041b\u0435\u0434\u044f\u043d\u0430\u044f \u043a\u0440\u0443\u043f\u0430", + "none": "\u0411\u0435\u0437 \u043e\u0441\u0430\u0434\u043a\u043e\u0432", + "rain": "\u0414\u043e\u0436\u0434\u044c", + "snow": "\u0421\u043d\u0435\u0433" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/tomorrowio/translations/sensor.ca.json b/homeassistant/components/tomorrowio/translations/sensor.ca.json index 161978d1919..520f14655ea 100644 --- a/homeassistant/components/tomorrowio/translations/sensor.ca.json +++ b/homeassistant/components/tomorrowio/translations/sensor.ca.json @@ -4,7 +4,7 @@ "good": "Bo", "hazardous": "Perill\u00f3s", "moderate": "Moderat", - "unhealthy": "Poc saludable", + "unhealthy": "No saludable", "unhealthy_for_sensitive_groups": "No saludable per a grups sensibles", "very_unhealthy": "Gens saludable" }, diff --git a/homeassistant/components/tomorrowio/translations/sensor.sk.json b/homeassistant/components/tomorrowio/translations/sensor.sk.json index 45600dcb861..89bae460600 100644 --- a/homeassistant/components/tomorrowio/translations/sensor.sk.json +++ b/homeassistant/components/tomorrowio/translations/sensor.sk.json @@ -1,13 +1,25 @@ { "state": { + "tomorrowio__health_concern": { + "good": "Dobr\u00e9", + "hazardous": "Nebezpe\u010dn\u00e9", + "moderate": "Mierne", + "unhealthy": "Nezdrav\u00e9", + "unhealthy_for_sensitive_groups": "Nezdrav\u00e9 pre citliv\u00e9 skupiny", + "very_unhealthy": "Ve\u013emi nezdrav\u00e9" + }, "tomorrowio__pollen_index": { "high": "Vysok\u00fd", - "low": "N\u00edzka", - "medium": "Stredn\u00fd" + "low": "N\u00edzky", + "medium": "Stredn\u00fd", + "none": "\u017diadne", + "very_high": "Ve\u013emi vysok\u00e9", + "very_low": "Ve\u013emi n\u00edzke" }, "tomorrowio__precipitation_type": { "freezing_rain": "Mrzn\u00faci d\u00e1\u017e\u010f", "ice_pellets": "\u013dadovec", + "none": "\u017diadne", "rain": "D\u00e1\u017e\u010f", "snow": "Sneh" } diff --git a/homeassistant/components/tomorrowio/translations/sk.json b/homeassistant/components/tomorrowio/translations/sk.json index 15048d51d1e..f15b7bc34ec 100644 --- a/homeassistant/components/tomorrowio/translations/sk.json +++ b/homeassistant/components/tomorrowio/translations/sk.json @@ -11,10 +11,54 @@ "data": { "api_key": "API k\u013e\u00fa\u010d", "location": "Umiestnenie", - "name": "Meno" + "name": "N\u00e1zov" }, "description": "Ak chcete z\u00edska\u0165 k\u013e\u00fa\u010d API, zaregistrujte sa na [Tomorrow.io] (https://app.tomorrow.io/signup)." } } + }, + "entity": { + "sensor": { + "health_concern": { + "state": { + "good": "Dobr\u00e9", + "hazardous": "Nebezpe\u010dn\u00e9", + "moderate": "Mierne", + "unhealthy": "Nezdrav\u00e9", + "unhealthy_for_sensitive_groups": "Nezdrav\u00e9 pre citliv\u00e9 skupiny", + "very_unhealthy": "Ve\u013emi nezdrav\u00e9" + } + }, + "pollen_index": { + "state": { + "high": "Vysok\u00e9", + "low": "N\u00edzke", + "medium": "Stredn\u00e9", + "none": "\u017diadne", + "very_high": "Ve\u013emi vysok\u00e9", + "very_low": "Ve\u013emi n\u00edzke" + } + }, + "precipitation_type": { + "state": { + "freezing_rain": "Mrzn\u00faci d\u00e1\u017e\u010f", + "ice_pellets": "\u013dadovec", + "none": "\u017diadne", + "rain": "D\u00e1\u017e\u010f", + "snow": "Sneh" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "timestep": "Min. medzi predpove\u010fami NowCast" + }, + "description": "Ak sa rozhodnete povoli\u0165 entitu progn\u00f3zy \u201enowcast\u201c, m\u00f4\u017eete nakonfigurova\u0165 po\u010det min\u00fat medzi jednotliv\u00fdmi progn\u00f3zami. Po\u010det poskytnut\u00fdch predpoved\u00ed z\u00e1vis\u00ed od po\u010dtu min\u00fat vybrat\u00fdch medzi predpove\u010fami.", + "title": "Aktualizujte mo\u017enosti Tomorrow.io" + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/zh-Hant.json b/homeassistant/components/tomorrowio/translations/zh-Hant.json index 00f5d5e0fcc..cb21ab23ebb 100644 --- a/homeassistant/components/tomorrowio/translations/zh-Hant.json +++ b/homeassistant/components/tomorrowio/translations/zh-Hant.json @@ -17,6 +17,39 @@ } } }, + "entity": { + "sensor": { + "health_concern": { + "state": { + "good": "\u826f\u597d", + "hazardous": "\u5371\u96aa", + "moderate": "\u4e2d\u7b49", + "unhealthy": "\u4e0d\u5065\u5eb7", + "unhealthy_for_sensitive_groups": "\u5c0d\u654f\u611f\u65cf\u7fa4\u4e0d\u5065\u5eb7", + "very_unhealthy": "\u975e\u5e38\u4e0d\u5065\u5eb7" + } + }, + "pollen_index": { + "state": { + "high": "\u9ad8", + "low": "\u4f4e", + "medium": "\u4e2d", + "none": "\u7121", + "very_high": "\u6975\u9ad8", + "very_low": "\u6975\u4f4e" + } + }, + "precipitation_type": { + "state": { + "freezing_rain": "\u51cd\u96e8", + "ice_pellets": "\u51b0\u73e0", + "none": "\u7121", + "rain": "\u4e0b\u96e8", + "snow": "\u4e0b\u96ea" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/toon/climate.py b/homeassistant/components/toon/climate.py index 4216d1c13fa..cc51bb03fec 100644 --- a/homeassistant/components/toon/climate.py +++ b/homeassistant/components/toon/climate.py @@ -21,7 +21,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -50,7 +50,7 @@ class ToonThermostatDevice(ToonDisplayDeviceEntity, ClimateEntity): _attr_supported_features = ( ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE ) - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS def __init__( self, diff --git a/homeassistant/components/toon/sensor.py b/homeassistant/components/toon/sensor.py index 9178cc6c01a..3b06f5d38b9 100644 --- a/homeassistant/components/toon/sensor.py +++ b/homeassistant/components/toon/sensor.py @@ -11,11 +11,11 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - ENERGY_KILO_WATT_HOUR, PERCENTAGE, - POWER_WATT, - TEMP_CELSIUS, - VOLUME_CUBIC_METERS, + UnitOfEnergy, + UnitOfPower, + UnitOfTemperature, + UnitOfVolume, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -132,7 +132,7 @@ SENSOR_ENTITIES: tuple[ToonSensorEntityDescription, ...] = ( name="Temperature", section="thermostat", measurement="current_display_temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, @@ -164,7 +164,7 @@ SENSOR_ENTITIES: tuple[ToonSensorEntityDescription, ...] = ( section="gas_usage", measurement="day_average", device_class=SensorDeviceClass.GAS, - native_unit_of_measurement=VOLUME_CUBIC_METERS, + native_unit_of_measurement=UnitOfVolume.CUBIC_METERS, entity_registry_enabled_default=False, cls=ToonGasMeterDeviceSensor, ), @@ -174,7 +174,7 @@ SENSOR_ENTITIES: tuple[ToonSensorEntityDescription, ...] = ( section="gas_usage", measurement="day_usage", device_class=SensorDeviceClass.GAS, - native_unit_of_measurement=VOLUME_CUBIC_METERS, + native_unit_of_measurement=UnitOfVolume.CUBIC_METERS, cls=ToonGasMeterDeviceSensor, ), ToonSensorEntityDescription( @@ -193,7 +193,7 @@ SENSOR_ENTITIES: tuple[ToonSensorEntityDescription, ...] = ( name="Gas Meter", section="gas_usage", measurement="meter", - native_unit_of_measurement=VOLUME_CUBIC_METERS, + native_unit_of_measurement=UnitOfVolume.CUBIC_METERS, state_class=SensorStateClass.TOTAL_INCREASING, device_class=SensorDeviceClass.GAS, cls=ToonGasMeterDeviceSensor, @@ -212,7 +212,7 @@ SENSOR_ENTITIES: tuple[ToonSensorEntityDescription, ...] = ( name="Average Power Usage", section="power_usage", measurement="average", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, entity_registry_enabled_default=False, cls=ToonElectricityMeterDeviceSensor, @@ -222,7 +222,7 @@ SENSOR_ENTITIES: tuple[ToonSensorEntityDescription, ...] = ( name="Average Daily Energy Usage", section="power_usage", measurement="day_average", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, entity_registry_enabled_default=False, cls=ToonElectricityMeterDeviceSensor, @@ -243,7 +243,7 @@ SENSOR_ENTITIES: tuple[ToonSensorEntityDescription, ...] = ( name="Energy Usage Today", section="power_usage", measurement="day_usage", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, cls=ToonElectricityMeterDeviceSensor, ), @@ -252,7 +252,7 @@ SENSOR_ENTITIES: tuple[ToonSensorEntityDescription, ...] = ( name="Electricity Meter Feed IN Tariff 1", section="power_usage", measurement="meter_high", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, cls=ToonElectricityMeterDeviceSensor, @@ -262,7 +262,7 @@ SENSOR_ENTITIES: tuple[ToonSensorEntityDescription, ...] = ( name="Electricity Meter Feed IN Tariff 2", section="power_usage", measurement="meter_low", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, cls=ToonElectricityMeterDeviceSensor, @@ -272,7 +272,7 @@ SENSOR_ENTITIES: tuple[ToonSensorEntityDescription, ...] = ( name="Current Power Usage", section="power_usage", measurement="current", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, cls=ToonElectricityMeterDeviceSensor, @@ -282,7 +282,7 @@ SENSOR_ENTITIES: tuple[ToonSensorEntityDescription, ...] = ( name="Electricity Meter Feed OUT Tariff 1", section="power_usage", measurement="meter_produced_high", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, cls=ToonElectricityMeterDeviceSensor, @@ -292,7 +292,7 @@ SENSOR_ENTITIES: tuple[ToonSensorEntityDescription, ...] = ( name="Electricity Meter Feed OUT Tariff 2", section="power_usage", measurement="meter_produced_low", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, cls=ToonElectricityMeterDeviceSensor, @@ -312,7 +312,7 @@ SENSOR_ENTITIES: tuple[ToonSensorEntityDescription, ...] = ( name="Average Daily Water Usage", section="water_usage", measurement="day_average", - native_unit_of_measurement=VOLUME_CUBIC_METERS, + native_unit_of_measurement=UnitOfVolume.CUBIC_METERS, icon="mdi:water", entity_registry_enabled_default=False, cls=ToonWaterMeterDeviceSensor, @@ -323,7 +323,7 @@ SENSOR_ENTITIES: tuple[ToonSensorEntityDescription, ...] = ( name="Water Usage Today", section="water_usage", measurement="day_usage", - native_unit_of_measurement=VOLUME_CUBIC_METERS, + native_unit_of_measurement=UnitOfVolume.CUBIC_METERS, icon="mdi:water", entity_registry_enabled_default=False, cls=ToonWaterMeterDeviceSensor, @@ -334,7 +334,7 @@ SENSOR_ENTITIES: tuple[ToonSensorEntityDescription, ...] = ( name="Water Meter", section="water_usage", measurement="meter", - native_unit_of_measurement=VOLUME_CUBIC_METERS, + native_unit_of_measurement=UnitOfVolume.CUBIC_METERS, icon="mdi:water", entity_registry_enabled_default=False, state_class=SensorStateClass.TOTAL_INCREASING, @@ -372,7 +372,7 @@ SENSOR_ENTITIES_SOLAR: tuple[ToonSensorEntityDescription, ...] = ( name="Current Solar Power Production", section="power_usage", measurement="current_solar", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, cls=ToonSolarDeviceSensor, @@ -382,7 +382,7 @@ SENSOR_ENTITIES_SOLAR: tuple[ToonSensorEntityDescription, ...] = ( name="Max Solar Power Production Today", section="power_usage", measurement="day_max_solar", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, cls=ToonSolarDeviceSensor, ), @@ -391,7 +391,7 @@ SENSOR_ENTITIES_SOLAR: tuple[ToonSensorEntityDescription, ...] = ( name="Solar Power Production to Grid", section="power_usage", measurement="current_produced", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, cls=ToonSolarDeviceSensor, @@ -401,7 +401,7 @@ SENSOR_ENTITIES_SOLAR: tuple[ToonSensorEntityDescription, ...] = ( name="Solar Energy Produced Today", section="power_usage", measurement="day_produced_solar", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, cls=ToonSolarDeviceSensor, @@ -411,7 +411,7 @@ SENSOR_ENTITIES_SOLAR: tuple[ToonSensorEntityDescription, ...] = ( name="Energy Produced To Grid Today", section="power_usage", measurement="day_to_grid_usage", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, entity_registry_enabled_default=False, cls=ToonSolarDeviceSensor, @@ -421,7 +421,7 @@ SENSOR_ENTITIES_SOLAR: tuple[ToonSensorEntityDescription, ...] = ( name="Energy Usage From Grid Today", section="power_usage", measurement="day_from_grid_usage", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, entity_registry_enabled_default=False, cls=ToonSolarDeviceSensor, @@ -431,7 +431,7 @@ SENSOR_ENTITIES_SOLAR: tuple[ToonSensorEntityDescription, ...] = ( name="Average Solar Power Production to Grid", section="power_usage", measurement="average_produced", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, entity_registry_enabled_default=False, cls=ToonSolarDeviceSensor, diff --git a/homeassistant/components/toon/translations/en_GB.json b/homeassistant/components/toon/translations/en-GB.json similarity index 100% rename from homeassistant/components/toon/translations/en_GB.json rename to homeassistant/components/toon/translations/en-GB.json diff --git a/homeassistant/components/toon/translations/pt.json b/homeassistant/components/toon/translations/pt.json index c82065e2550..32b1ff907ab 100644 --- a/homeassistant/components/toon/translations/pt.json +++ b/homeassistant/components/toon/translations/pt.json @@ -1,9 +1,9 @@ { "config": { "abort": { - "authorize_url_timeout": "Tempo excedido a gerar um URL de autoriza\u00e7\u00e3o", + "authorize_url_timeout": "Excedido o tempo limite para gerar um URL de autoriza\u00e7\u00e3o", "missing_configuration": "O componente n\u00e3o est\u00e1 configurado. Por favor, siga a documenta\u00e7\u00e3o.", - "no_url_available": "Nenhum URL dispon\u00edvel. Para obter informa\u00e7\u00f5es sobre esse erro, [verifique a sec\u00e7\u00e3o de ajuda]({docs_url})", + "no_url_available": "Nenhum URL est\u00e1 dispon\u00edvel. Para obter informa\u00e7\u00f5es sobre esse erro, [consulte a sec\u00e7\u00e3o de ajuda]({docs_url})", "unknown_authorize_url_generation": "Erro desconhecido ao gerar um URL de autoriza\u00e7\u00e3o." }, "step": { diff --git a/homeassistant/components/toon/translations/sk.json b/homeassistant/components/toon/translations/sk.json index 7ff07bff835..0015de016e2 100644 --- a/homeassistant/components/toon/translations/sk.json +++ b/homeassistant/components/toon/translations/sk.json @@ -4,6 +4,7 @@ "already_configured": "Vybran\u00e1 dohoda je u\u017e nakonfigurovan\u00e1.", "authorize_url_timeout": "\u010casov\u00fd limit generovania autorizovanej adresy URL.", "missing_configuration": "Komponent nie je nakonfigurovan\u00fd. Postupujte pod\u013ea dokument\u00e1cie.", + "no_agreements": "Tento \u00fa\u010det nem\u00e1 \u017eiadne Toon displeje.", "no_url_available": "Nie je k dispoz\u00edcii \u017eiadna adresa URL. Inform\u00e1cie o tejto chybe n\u00e1jdete [pozrite si sekciu pomocn\u00edka]({docs_url})", "unknown_authorize_url_generation": "Nezn\u00e1ma chyba pri generovan\u00ed autorizovanej adresy URL." }, @@ -14,6 +15,9 @@ }, "description": "Vyberte adresu zmluvy, ktor\u00fa chcete prida\u0165.", "title": "Vyberte svoju dohodu" + }, + "pick_implementation": { + "title": "V\u00fdber klienta na overenie" } } } diff --git a/homeassistant/components/totalconnect/__init__.py b/homeassistant/components/totalconnect/__init__.py index 87977e5c1db..858ed3121d7 100644 --- a/homeassistant/components/totalconnect/__init__.py +++ b/homeassistant/components/totalconnect/__init__.py @@ -78,10 +78,12 @@ async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None: client.locations[location_id].auto_bypass_low_battery = bypass -class TotalConnectDataUpdateCoordinator(DataUpdateCoordinator): +class TotalConnectDataUpdateCoordinator(DataUpdateCoordinator[None]): """Class to fetch data from TotalConnect.""" - def __init__(self, hass: HomeAssistant, client): + config_entry: ConfigEntry + + def __init__(self, hass: HomeAssistant, client: TotalConnectClient) -> None: """Initialize.""" self.hass = hass self.client = client @@ -89,11 +91,11 @@ class TotalConnectDataUpdateCoordinator(DataUpdateCoordinator): hass, logger=_LOGGER, name=DOMAIN, update_interval=SCAN_INTERVAL ) - async def _async_update_data(self): + async def _async_update_data(self) -> None: """Update data.""" await self.hass.async_add_executor_job(self.sync_update_data) - def sync_update_data(self): + def sync_update_data(self) -> None: """Fetch synchronous data from TotalConnect.""" try: for location_id in self.client.locations: diff --git a/homeassistant/components/totalconnect/alarm_control_panel.py b/homeassistant/components/totalconnect/alarm_control_panel.py index 5798f4d31d3..6f9e579ef2c 100644 --- a/homeassistant/components/totalconnect/alarm_control_panel.py +++ b/homeassistant/components/totalconnect/alarm_control_panel.py @@ -24,6 +24,7 @@ from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import CoordinatorEntity +from . import TotalConnectDataUpdateCoordinator from .const import DOMAIN SERVICE_ALARM_ARM_AWAY_INSTANT = "arm_away_instant" @@ -36,7 +37,7 @@ async def async_setup_entry( """Set up TotalConnect alarm panels based on a config entry.""" alarms = [] - coordinator = hass.data[DOMAIN][entry.entry_id] + coordinator: TotalConnectDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] for location_id, location in coordinator.client.locations.items(): location_name = location.location_name @@ -68,7 +69,9 @@ async def async_setup_entry( ) -class TotalConnectAlarm(CoordinatorEntity, alarm.AlarmControlPanelEntity): +class TotalConnectAlarm( + CoordinatorEntity[TotalConnectDataUpdateCoordinator], alarm.AlarmControlPanelEntity +): """Represent an TotalConnect status.""" _attr_supported_features = ( @@ -77,7 +80,13 @@ class TotalConnectAlarm(CoordinatorEntity, alarm.AlarmControlPanelEntity): | AlarmControlPanelEntityFeature.ARM_NIGHT ) - def __init__(self, coordinator, name, location_id, partition_id): + def __init__( + self, + coordinator: TotalConnectDataUpdateCoordinator, + name, + location_id, + partition_id, + ): """Initialize the TotalConnect status.""" super().__init__(coordinator) self._location_id = location_id @@ -85,7 +94,7 @@ class TotalConnectAlarm(CoordinatorEntity, alarm.AlarmControlPanelEntity): self._partition_id = partition_id self._partition = self._location.partitions[partition_id] self._device = self._location.devices[self._location.security_device_id] - self._state = None + self._state: str | None = None self._attr_extra_state_attributes = {} """ @@ -122,7 +131,7 @@ class TotalConnectAlarm(CoordinatorEntity, alarm.AlarmControlPanelEntity): "triggered_zone": None, } - state = None + state: str | None = None if self._partition.arming_state.is_disarmed(): state = STATE_ALARM_DISARMED elif self._partition.arming_state.is_armed_night(): diff --git a/homeassistant/components/totalconnect/translations/sk.json b/homeassistant/components/totalconnect/translations/sk.json index e3b18794804..1e45b1bca64 100644 --- a/homeassistant/components/totalconnect/translations/sk.json +++ b/homeassistant/components/totalconnect/translations/sk.json @@ -2,6 +2,7 @@ "config": { "abort": { "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", + "no_locations": "Pre tohto pou\u017e\u00edvate\u013ea nie s\u00fa dostupn\u00e9 \u017eiadne miesta, skontrolujte nastavenia TotalConnect", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { @@ -13,9 +14,11 @@ "data": { "usercode": "Pou\u017e\u00edvate\u013esk\u00fd k\u00f3d" }, - "description": "Zadajte pou\u017e\u00edvate\u013esk\u00fd k\u00f3d pre tohto pou\u017e\u00edvate\u013ea na adrese {location_id}" + "description": "Zadajte pou\u017e\u00edvate\u013esk\u00fd k\u00f3d pre tohto pou\u017e\u00edvate\u013ea na adrese {location_id}", + "title": "Pou\u017e\u00edvate\u013esk\u00e9 k\u00f3dy miesta" }, "reauth_confirm": { + "description": "Total Connect potrebuje znova overi\u0165 v\u00e1\u0161 \u00fa\u010det", "title": "Znova overi\u0165 integr\u00e1ciu" }, "user": { @@ -25,5 +28,16 @@ } } } + }, + "options": { + "step": { + "init": { + "data": { + "auto_bypass_low_battery": "Auto bypass slabej bat\u00e9rie" + }, + "description": "Automaticky bypass z\u00f3ny v momente, ke\u010f hl\u00e1si slab\u00fa bat\u00e9riu.", + "title": "Mo\u017enosti TotalConnect" + } + } } } \ No newline at end of file diff --git a/homeassistant/components/touchline/climate.py b/homeassistant/components/touchline/climate.py index 4ce1c4553cb..ed3d4500db1 100644 --- a/homeassistant/components/touchline/climate.py +++ b/homeassistant/components/touchline/climate.py @@ -12,7 +12,7 @@ from homeassistant.components.climate import ( ClimateEntityFeature, HVACMode, ) -from homeassistant.const import ATTR_TEMPERATURE, CONF_HOST, TEMP_CELSIUS +from homeassistant.const import ATTR_TEMPERATURE, CONF_HOST, UnitOfTemperature from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -68,7 +68,7 @@ class Touchline(ClimateEntity): _attr_supported_features = ( ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE ) - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS def __init__(self, touchline_thermostat): """Initialize the Touchline device.""" diff --git a/homeassistant/components/tplink/config_flow.py b/homeassistant/components/tplink/config_flow.py index 8b05f90041a..a783c7b902f 100644 --- a/homeassistant/components/tplink/config_flow.py +++ b/homeassistant/components/tplink/config_flow.py @@ -115,7 +115,9 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): } self._discovered_devices = await async_discover_devices(self.hass) devices_name = { - formatted_mac: f"{device.alias} {device.model} ({device.host}) {formatted_mac}" + formatted_mac: ( + f"{device.alias} {device.model} ({device.host}) {formatted_mac}" + ) for formatted_mac, device in self._discovered_devices.items() if formatted_mac not in configured_devices } diff --git a/homeassistant/components/tplink/coordinator.py b/homeassistant/components/tplink/coordinator.py index 2b33f817c63..20ec422c372 100644 --- a/homeassistant/components/tplink/coordinator.py +++ b/homeassistant/components/tplink/coordinator.py @@ -15,7 +15,7 @@ _LOGGER = logging.getLogger(__name__) REQUEST_REFRESH_DELAY = 0.35 -class TPLinkDataUpdateCoordinator(DataUpdateCoordinator): +class TPLinkDataUpdateCoordinator(DataUpdateCoordinator[None]): """DataUpdateCoordinator to gather data for a specific TPLink device.""" def __init__( diff --git a/homeassistant/components/tplink/sensor.py b/homeassistant/components/tplink/sensor.py index 7ba28702114..7471ed8982b 100644 --- a/homeassistant/components/tplink/sensor.py +++ b/homeassistant/components/tplink/sensor.py @@ -15,10 +15,10 @@ from homeassistant.components.sensor import ( from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( ATTR_VOLTAGE, - ELECTRIC_CURRENT_AMPERE, - ELECTRIC_POTENTIAL_VOLT, - ENERGY_KILO_WATT_HOUR, - POWER_WATT, + UnitOfElectricCurrent, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfPower, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -46,7 +46,7 @@ class TPLinkSensorEntityDescription(SensorEntityDescription): ENERGY_SENSORS: tuple[TPLinkSensorEntityDescription, ...] = ( TPLinkSensorEntityDescription( key=ATTR_CURRENT_POWER_W, - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, name="Current Consumption", @@ -55,7 +55,7 @@ ENERGY_SENSORS: tuple[TPLinkSensorEntityDescription, ...] = ( ), TPLinkSensorEntityDescription( key=ATTR_TOTAL_ENERGY_KWH, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, name="Total Consumption", @@ -64,7 +64,7 @@ ENERGY_SENSORS: tuple[TPLinkSensorEntityDescription, ...] = ( ), TPLinkSensorEntityDescription( key=ATTR_TODAY_ENERGY_KWH, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, name="Today's Consumption", @@ -72,7 +72,7 @@ ENERGY_SENSORS: tuple[TPLinkSensorEntityDescription, ...] = ( ), TPLinkSensorEntityDescription( key=ATTR_VOLTAGE, - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, name="Voltage", @@ -81,7 +81,7 @@ ENERGY_SENSORS: tuple[TPLinkSensorEntityDescription, ...] = ( ), TPLinkSensorEntityDescription( key=ATTR_CURRENT_A, - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, name="Current", diff --git a/homeassistant/components/tplink/strings.json b/homeassistant/components/tplink/strings.json index 4f3b34beb9c..afc595a3adc 100644 --- a/homeassistant/components/tplink/strings.json +++ b/homeassistant/components/tplink/strings.json @@ -14,7 +14,7 @@ } }, "discovery_confirm": { - "description": "Do you want to setup {name} {model} ({host})?" + "description": "Do you want to set up {name} {model} ({host})?" } }, "error": { diff --git a/homeassistant/components/tplink/translations/en.json b/homeassistant/components/tplink/translations/en.json index 0697974e708..54ab540bb50 100644 --- a/homeassistant/components/tplink/translations/en.json +++ b/homeassistant/components/tplink/translations/en.json @@ -10,7 +10,7 @@ "flow_title": "{name} {model} ({host})", "step": { "discovery_confirm": { - "description": "Do you want to setup {name} {model} ({host})?" + "description": "Do you want to set up {name} {model} ({host})?" }, "pick_device": { "data": { diff --git a/homeassistant/components/tplink/translations/ko.json b/homeassistant/components/tplink/translations/ko.json index f063f54207c..f88c2253d9e 100644 --- a/homeassistant/components/tplink/translations/ko.json +++ b/homeassistant/components/tplink/translations/ko.json @@ -1,11 +1,20 @@ { "config": { "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4" }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" + }, "step": { "discovery_confirm": { "description": "{name} {model} ( {host} )\uc744(\ub97c) \uc124\uc815\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" + }, + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8" + } } } } diff --git a/homeassistant/components/tplink/translations/no.json b/homeassistant/components/tplink/translations/no.json index 8ae66b3dce1..19b09bd4590 100644 --- a/homeassistant/components/tplink/translations/no.json +++ b/homeassistant/components/tplink/translations/no.json @@ -10,7 +10,7 @@ "flow_title": "{name} {model} ( {host} )", "step": { "discovery_confirm": { - "description": "Vil du konfigurere {name} {model} ( {host} )?" + "description": "Vil du sette opp {name} {model} ( {host} )?" }, "pick_device": { "data": { diff --git a/homeassistant/components/tplink/translations/pt.json b/homeassistant/components/tplink/translations/pt.json index d4507db3a55..59efcde239b 100644 --- a/homeassistant/components/tplink/translations/pt.json +++ b/homeassistant/components/tplink/translations/pt.json @@ -4,7 +4,7 @@ "no_devices_found": "Nenhum dispositivo TP-Link encontrado na rede." }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" } } } \ No newline at end of file diff --git a/homeassistant/components/tplink/translations/sk.json b/homeassistant/components/tplink/translations/sk.json index db96518e108..9c1c0b268d7 100644 --- a/homeassistant/components/tplink/translations/sk.json +++ b/homeassistant/components/tplink/translations/sk.json @@ -20,7 +20,8 @@ "user": { "data": { "host": "Hostite\u013e" - } + }, + "description": "Ak nech\u00e1te hostite\u013ea pr\u00e1zdneho, na vyh\u013ead\u00e1vanie zariaden\u00ed sa pou\u017eije zis\u0165ovanie." } } } diff --git a/homeassistant/components/traccar/strings.json b/homeassistant/components/traccar/strings.json index d8d100e1c2f..62bcf608852 100644 --- a/homeassistant/components/traccar/strings.json +++ b/homeassistant/components/traccar/strings.json @@ -12,7 +12,7 @@ "webhook_not_internet_accessible": "[%key:common::config_flow::abort::webhook_not_internet_accessible%]" }, "create_entry": { - "default": "To send events to Home Assistant, you will need to setup the webhook feature in Traccar.\n\nUse the following URL: `{webhook_url}`\n\nSee [the documentation]({docs_url}) for further details." + "default": "To send events to Home Assistant, you will need to set up the webhook feature in Traccar.\n\nUse the following URL: `{webhook_url}`\n\nSee [the documentation]({docs_url}) for further details." } } } diff --git a/homeassistant/components/traccar/translations/ca.json b/homeassistant/components/traccar/translations/ca.json index 3f8a1b423b3..6124982a2b7 100644 --- a/homeassistant/components/traccar/translations/ca.json +++ b/homeassistant/components/traccar/translations/ca.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "La teva inst\u00e0ncia de Home Assistant ha de ser accessible des d'Internet per poder rebre missatges webhook." }, "create_entry": { - "default": "Per enviar esdeveniments a Home Assistant, haur\u00e0s de configurar l'opci\u00f3 webhook de Traccar.\n\nUtilitza el seg\u00fcent URL: `{webhook_url}`\n\nConsulta la [documentaci\u00f3]({docs_url}) per a m\u00e9s detalls." + "default": "Per enviar esdeveniments a Home Assistant, has de configurar l'opci\u00f3 webhook de Traccar.\n\nUtilitza el seg\u00fcent URL: `{webhook_url}`\n\nConsulta la [documentaci\u00f3]({docs_url}) per a m\u00e9s detalls." }, "step": { "user": { diff --git a/homeassistant/components/traccar/translations/en.json b/homeassistant/components/traccar/translations/en.json index 5a6d8eb2cca..55d09eacd40 100644 --- a/homeassistant/components/traccar/translations/en.json +++ b/homeassistant/components/traccar/translations/en.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "Your Home Assistant instance needs to be accessible from the internet to receive webhook messages." }, "create_entry": { - "default": "To send events to Home Assistant, you will need to setup the webhook feature in Traccar.\n\nUse the following URL: `{webhook_url}`\n\nSee [the documentation]({docs_url}) for further details." + "default": "To send events to Home Assistant, you will need to set up the webhook feature in Traccar.\n\nUse the following URL: `{webhook_url}`\n\nSee [the documentation]({docs_url}) for further details." }, "step": { "user": { diff --git a/homeassistant/components/traccar/translations/es.json b/homeassistant/components/traccar/translations/es.json index a3710a80a50..65eed2a0ae0 100644 --- a/homeassistant/components/traccar/translations/es.json +++ b/homeassistant/components/traccar/translations/es.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "Tu instancia de Home Assistant debe estar accesible desde Internet para recibir mensajes webhook." }, "create_entry": { - "default": "Para enviar eventos a Home Assistant, deber\u00e1s configurar la funci\u00f3n de webhook en Traccar. \n\nUsa la siguiente URL: `{webhook_url}` \n\nConsulta [la documentaci\u00f3n]({docs_url}) para obtener m\u00e1s detalles." + "default": "Para enviar eventos a Home Assistant, necesitar\u00e1s configurar la funci\u00f3n de webhook en Traccar. \n\nUsa la siguiente URL: `{webhook_url}` \n\nConsulta [la documentaci\u00f3n]({docs_url}) para obtener m\u00e1s detalles." }, "step": { "user": { diff --git a/homeassistant/components/traccar/translations/it.json b/homeassistant/components/traccar/translations/it.json index c70dff73bea..7f885ad4b10 100644 --- a/homeassistant/components/traccar/translations/it.json +++ b/homeassistant/components/traccar/translations/it.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "L'istanza di Home Assistant deve essere accessibile da Internet per ricevere messaggi webhook." }, "create_entry": { - "default": "Per inviare eventi a Home Assistant, devi impostare la funzione webhook in Traccar.\n\nUsa il seguente URL: `{webhook_url}`.\n\nVedi [la documentazione]({docs_url}) per ulteriori dettagli." + "default": "Per inviare eventi a Home Assistant, dovrai configurare la funzione webhook in Traccar. \n\n Utilizza il seguente URL: `{webhook_url}` \n\n Consulta [la documentazione]({docs_url}) per ulteriori dettagli." }, "step": { "user": { diff --git a/homeassistant/components/traccar/translations/ko.json b/homeassistant/components/traccar/translations/ko.json index aa5e2a65736..7828664ea12 100644 --- a/homeassistant/components/traccar/translations/ko.json +++ b/homeassistant/components/traccar/translations/ko.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "cloud_not_connected": "Home Assistant \ud074\ub77c\uc6b0\ub4dc\uc5d0 \uc5f0\uacb0\ub418\uc5b4 \uc788\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.", "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.", "webhook_not_internet_accessible": "\uc6f9 \ud6c5 \uba54\uc2dc\uc9c0\ub97c \ubc1b\uc73c\ub824\uba74 \uc778\ud130\ub137\uc5d0\uc11c Home Assistant \uc778\uc2a4\ud134\uc2a4\uc5d0 \uc811\uadfc\ud560 \uc218 \uc788\uc5b4\uc57c \ud569\ub2c8\ub2e4." }, diff --git a/homeassistant/components/traccar/translations/no.json b/homeassistant/components/traccar/translations/no.json index 1e16d41880e..40df77a9480 100644 --- a/homeassistant/components/traccar/translations/no.json +++ b/homeassistant/components/traccar/translations/no.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "Home Assistant forekomsten din m\u00e5 v\u00e6re tilgjengelig fra internett for \u00e5 kunne motta webhook meldinger" }, "create_entry": { - "default": "For \u00e5 sende hendelser til Home Assistant, m\u00e5 du konfigurere webhook-funksjonen i Traccar. \n\n Bruk f\u00f8lgende URL: \"{webhook_url}\" \n\n Se [dokumentasjonen] ({docs_url}) for mer informasjon." + "default": "For \u00e5 sende hendelser til Home Assistant, m\u00e5 du sette opp webhook-funksjonen i Traccar. \n\n Bruk f\u00f8lgende URL: ` {webhook_url} ` \n\n Se [dokumentasjonen]( {docs_url} ) for ytterligere detaljer." }, "step": { "user": { diff --git a/homeassistant/components/traccar/translations/pt-BR.json b/homeassistant/components/traccar/translations/pt-BR.json index 1fc86514bd1..3382763b94d 100644 --- a/homeassistant/components/traccar/translations/pt-BR.json +++ b/homeassistant/components/traccar/translations/pt-BR.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "Sua inst\u00e2ncia do Home Assistant precisa estar acess\u00edvel pela Internet para receber mensagens de webhook." }, "create_entry": { - "default": "Para enviar eventos ao Home Assistant, voc\u00ea precisar\u00e1 configurar o recurso de webhook no Traccar. \n\n Use o seguinte URL: ` {webhook_url} ` \n\n Veja [a documenta\u00e7\u00e3o]({docs_url}) para mais detalhes." + "default": "Para enviar eventos ao Home Assistant, voc\u00ea precisar\u00e1 configurar o recurso de webhook no Traccar. \n\n Use o seguinte URL: `{webhook_url}` \n\n Veja [a documenta\u00e7\u00e3o]({docs_url}) para mais detalhes." }, "step": { "user": { diff --git a/homeassistant/components/traccar/translations/sk.json b/homeassistant/components/traccar/translations/sk.json index 933f73976d2..e3fb9fe8544 100644 --- a/homeassistant/components/traccar/translations/sk.json +++ b/homeassistant/components/traccar/translations/sk.json @@ -4,6 +4,15 @@ "cloud_not_connected": "Nie je pripojen\u00e9 k Home Assistant Cloud.", "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia.", "webhook_not_internet_accessible": "Va\u0161a in\u0161tancia Home Assistant mus\u00ed by\u0165 pr\u00edstupn\u00e1 z internetu, aby ste mohli prij\u00edma\u0165 spr\u00e1vy webhooku." + }, + "create_entry": { + "default": "Ak chcete odosiela\u0165 udalosti do aplik\u00e1cie Home Assistant, mus\u00edte v aplik\u00e1cii Traccar nastavi\u0165 funkciu webhook.\n\nPou\u017eite nasleduj\u00facu adresu URL: `{webhook_url}`\n\n\u010eal\u0161ie podrobnosti n\u00e1jdete v [dokument\u00e1cii]({docs_url})." + }, + "step": { + "user": { + "description": "Naozaj chcete nastavi\u0165 Traccar?", + "title": "Nastavti\u0165 Traccar" + } } } } \ No newline at end of file diff --git a/homeassistant/components/tractive/__init__.py b/homeassistant/components/tractive/__init__.py index cc0b2b2b6cb..96fc718c67d 100644 --- a/homeassistant/components/tractive/__init__.py +++ b/homeassistant/components/tractive/__init__.py @@ -226,7 +226,10 @@ class TractiveClient: except aiotractive.exceptions.TractiveError: _LOGGER.debug( - "Tractive is not available. Internet connection is down? Sleeping %i seconds and retrying", + ( + "Tractive is not available. Internet connection is down?" + " Sleeping %i seconds and retrying" + ), RECONNECT_INTERVAL.total_seconds(), ) self._last_hw_time = 0 diff --git a/homeassistant/components/tractive/sensor.py b/homeassistant/components/tractive/sensor.py index c412502d8d9..cc45bde085d 100644 --- a/homeassistant/components/tractive/sensor.py +++ b/homeassistant/components/tractive/sensor.py @@ -10,7 +10,7 @@ from homeassistant.components.sensor import ( SensorEntityDescription, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_BATTERY_LEVEL, PERCENTAGE, TIME_MINUTES +from homeassistant.const import ATTR_BATTERY_LEVEL, PERCENTAGE, UnitOfTime from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import EntityCategory @@ -145,21 +145,21 @@ SENSOR_TYPES: tuple[TractiveSensorEntityDescription, ...] = ( # More states are available by polling the data key=ATTR_TRACKER_STATE, name="Tracker state", - device_class="tractive__tracker_state", + translation_key="tracker_state", entity_class=TractiveHardwareSensor, ), TractiveSensorEntityDescription( key=ATTR_MINUTES_ACTIVE, name="Minutes active", icon="mdi:clock-time-eight-outline", - native_unit_of_measurement=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, entity_class=TractiveActivitySensor, ), TractiveSensorEntityDescription( key=ATTR_DAILY_GOAL, name="Daily goal", icon="mdi:flag-checkered", - native_unit_of_measurement=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, entity_class=TractiveActivitySensor, ), ) diff --git a/homeassistant/components/tractive/strings.json b/homeassistant/components/tractive/strings.json index 7eba02e7f67..f63f0cdf0af 100644 --- a/homeassistant/components/tractive/strings.json +++ b/homeassistant/components/tractive/strings.json @@ -17,5 +17,17 @@ "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]", "reauth_failed_existing": "Could not update the config entry, please remove the integration and set it up again." } + }, + "entity": { + "sensor": { + "tracker_state": { + "state": { + "not_reporting": "Not reporting", + "operational": "Operational", + "system_shutdown_user": "System shutdown user", + "system_startup": "System startup" + } + } + } } } diff --git a/homeassistant/components/tractive/strings.sensor.json b/homeassistant/components/tractive/strings.sensor.json deleted file mode 100644 index 48a747961c0..00000000000 --- a/homeassistant/components/tractive/strings.sensor.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "state": { - "tractive__tracker_state": { - "not_reporting": "Not reporting", - "operational": "Operational", - "system_shutdown_user": "System shutdown user", - "system_startup": "System startup" - } - } -} diff --git a/homeassistant/components/tractive/translations/ca.json b/homeassistant/components/tractive/translations/ca.json index 0641dd2737b..357c1ee35f4 100644 --- a/homeassistant/components/tractive/translations/ca.json +++ b/homeassistant/components/tractive/translations/ca.json @@ -17,5 +17,17 @@ } } } + }, + "entity": { + "sensor": { + "tracker_state": { + "state": { + "not_reporting": "Sense informaci\u00f3", + "operational": "Operatiu", + "system_shutdown_user": "Aturada de sistema d'usuari", + "system_startup": "Engegada del sistema" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tractive/translations/de.json b/homeassistant/components/tractive/translations/de.json index cad80fd36a8..37a96f6d32b 100644 --- a/homeassistant/components/tractive/translations/de.json +++ b/homeassistant/components/tractive/translations/de.json @@ -17,5 +17,17 @@ } } } + }, + "entity": { + "sensor": { + "tracker_state": { + "state": { + "not_reporting": "Keine Meldung", + "operational": "Betriebsbereit", + "system_shutdown_user": "Benutzer zum Herunterfahren des Systems", + "system_startup": "Systemstart" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tractive/translations/el.json b/homeassistant/components/tractive/translations/el.json index b76849ed295..773a394147a 100644 --- a/homeassistant/components/tractive/translations/el.json +++ b/homeassistant/components/tractive/translations/el.json @@ -17,5 +17,17 @@ } } } + }, + "entity": { + "sensor": { + "tracker_state": { + "state": { + "not_reporting": "\u03a7\u03c9\u03c1\u03af\u03c2 \u03b1\u03bd\u03b1\u03c6\u03bf\u03c1\u03ac", + "operational": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03b9\u03ba\u03cc", + "system_shutdown_user": "\u03a7\u03c1\u03ae\u03c3\u03c4\u03b7\u03c2 \u03c4\u03b5\u03c1\u03bc\u03b1\u03c4\u03b9\u03c3\u03bc\u03bf\u03cd \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1\u03c2 \u03c3\u03c5\u03c3\u03c4\u03ae\u03bc\u03b1\u03c4\u03bf\u03c2", + "system_startup": "\u0395\u03ba\u03ba\u03af\u03bd\u03b7\u03c3\u03b7 \u03c3\u03c5\u03c3\u03c4\u03ae\u03bc\u03b1\u03c4\u03bf\u03c2" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tractive/translations/en.json b/homeassistant/components/tractive/translations/en.json index dcb3a128ac4..edf452a6f22 100644 --- a/homeassistant/components/tractive/translations/en.json +++ b/homeassistant/components/tractive/translations/en.json @@ -17,5 +17,17 @@ } } } + }, + "entity": { + "sensor": { + "tracker_state": { + "state": { + "not_reporting": "Not reporting", + "operational": "Operational", + "system_shutdown_user": "System shutdown user", + "system_startup": "System startup" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tractive/translations/es.json b/homeassistant/components/tractive/translations/es.json index 1e61714ff9c..becaf139776 100644 --- a/homeassistant/components/tractive/translations/es.json +++ b/homeassistant/components/tractive/translations/es.json @@ -17,5 +17,17 @@ } } } + }, + "entity": { + "sensor": { + "tracker_state": { + "state": { + "not_reporting": "No informando", + "operational": "Operativo", + "system_shutdown_user": "Usuario de apagado del sistema", + "system_startup": "Inicio del sistema" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tractive/translations/et.json b/homeassistant/components/tractive/translations/et.json index 67adf622ebe..c30975f2846 100644 --- a/homeassistant/components/tractive/translations/et.json +++ b/homeassistant/components/tractive/translations/et.json @@ -17,5 +17,17 @@ } } } + }, + "entity": { + "sensor": { + "tracker_state": { + "state": { + "not_reporting": "Ei vasta", + "operational": "Tegevuses", + "system_shutdown_user": "S\u00fcsteemi sulgemise kasutaja", + "system_startup": "S\u00fcsteemi k\u00e4ivitamine" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tractive/translations/hu.json b/homeassistant/components/tractive/translations/hu.json index 5b1d9a35512..050b68113e3 100644 --- a/homeassistant/components/tractive/translations/hu.json +++ b/homeassistant/components/tractive/translations/hu.json @@ -17,5 +17,17 @@ } } } + }, + "entity": { + "sensor": { + "tracker_state": { + "state": { + "not_reporting": "Nem jelentkezik", + "operational": "\u00dczemk\u00e9pes", + "system_shutdown_user": "Rendszerle\u00e1ll\u00edt\u00e1s felhaszn\u00e1l\u00f3ja", + "system_startup": "Rendszerind\u00edt\u00e1s" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tractive/translations/id.json b/homeassistant/components/tractive/translations/id.json index 91843f1c37f..b99cdad6e1c 100644 --- a/homeassistant/components/tractive/translations/id.json +++ b/homeassistant/components/tractive/translations/id.json @@ -17,5 +17,17 @@ } } } + }, + "entity": { + "sensor": { + "tracker_state": { + "state": { + "not_reporting": "Tidak melaporkan", + "operational": "Operasional", + "system_shutdown_user": "Pengguna mematikan sistem", + "system_startup": "Sistem mulai" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tractive/translations/it.json b/homeassistant/components/tractive/translations/it.json index 5774d564098..376950f88d2 100644 --- a/homeassistant/components/tractive/translations/it.json +++ b/homeassistant/components/tractive/translations/it.json @@ -17,5 +17,17 @@ } } } + }, + "entity": { + "sensor": { + "tracker_state": { + "state": { + "not_reporting": "Non segnalante", + "operational": "Operativo", + "system_shutdown_user": "Spegnimento del sistema da parte dell'utente", + "system_startup": "Avvio del sistema" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tractive/translations/ko.json b/homeassistant/components/tractive/translations/ko.json new file mode 100644 index 00000000000..fab7e511034 --- /dev/null +++ b/homeassistant/components/tractive/translations/ko.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "email": "\uc774\uba54\uc77c", + "password": "\ube44\ubc00\ubc88\ud638" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tractive/translations/no.json b/homeassistant/components/tractive/translations/no.json index 3e5061e027d..cda3e5ac128 100644 --- a/homeassistant/components/tractive/translations/no.json +++ b/homeassistant/components/tractive/translations/no.json @@ -17,5 +17,17 @@ } } } + }, + "entity": { + "sensor": { + "tracker_state": { + "state": { + "not_reporting": "Rapporterer ikke", + "operational": "Operasjonell", + "system_shutdown_user": "Bruker av systemavslutning", + "system_startup": "Systemoppstart" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tractive/translations/pl.json b/homeassistant/components/tractive/translations/pl.json index 99379115ef1..4986ab24b5e 100644 --- a/homeassistant/components/tractive/translations/pl.json +++ b/homeassistant/components/tractive/translations/pl.json @@ -17,5 +17,17 @@ } } } + }, + "entity": { + "sensor": { + "tracker_state": { + "state": { + "not_reporting": "nie raportuje", + "operational": "sprawny", + "system_shutdown_user": "wy\u0142\u0105czanie systemu", + "system_startup": "uruchamianie systemu" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tractive/translations/pt-BR.json b/homeassistant/components/tractive/translations/pt-BR.json index d33bdf4f9aa..0725019693f 100644 --- a/homeassistant/components/tractive/translations/pt-BR.json +++ b/homeassistant/components/tractive/translations/pt-BR.json @@ -17,5 +17,17 @@ } } } + }, + "entity": { + "sensor": { + "tracker_state": { + "state": { + "not_reporting": "N\u00e3o reportando", + "operational": "Operacional", + "system_shutdown_user": "Usu\u00e1rio de desligamento do sistema", + "system_startup": "Inicializa\u00e7\u00e3o do sistema" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tractive/translations/pt.json b/homeassistant/components/tractive/translations/pt.json index 7430480cc09..fa97ba44eb9 100644 --- a/homeassistant/components/tractive/translations/pt.json +++ b/homeassistant/components/tractive/translations/pt.json @@ -7,7 +7,7 @@ "step": { "user": { "data": { - "email": "Email", + "email": "", "password": "Palavra-passe" } } diff --git a/homeassistant/components/tractive/translations/ru.json b/homeassistant/components/tractive/translations/ru.json index 89042b79b5e..101415883c9 100644 --- a/homeassistant/components/tractive/translations/ru.json +++ b/homeassistant/components/tractive/translations/ru.json @@ -17,5 +17,17 @@ } } } + }, + "entity": { + "sensor": { + "tracker_state": { + "state": { + "not_reporting": "\u041d\u0435 \u0441\u043e\u043e\u0431\u0449\u0430\u0435\u0442\u0441\u044f", + "operational": "\u042d\u043a\u0441\u043f\u043b\u0443\u0430\u0442\u0438\u0440\u0443\u0435\u0442\u0441\u044f", + "system_shutdown_user": "\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0432\u044b\u043a\u043b\u044e\u0447\u0438\u043b \u0441\u0438\u0441\u0442\u0435\u043c\u0443", + "system_startup": "\u0417\u0430\u043f\u0443\u0441\u043a \u0441\u0438\u0441\u0442\u0435\u043c\u044b" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tractive/translations/sensor.sk.json b/homeassistant/components/tractive/translations/sensor.sk.json index 547d8fcc7c9..254085f868e 100644 --- a/homeassistant/components/tractive/translations/sensor.sk.json +++ b/homeassistant/components/tractive/translations/sensor.sk.json @@ -1,6 +1,9 @@ { "state": { "tractive__tracker_state": { + "not_reporting": "Nehl\u00e1si sa", + "operational": "Prev\u00e1dzka", + "system_shutdown_user": "Vypnutie syst\u00e9mu pou\u017e\u00edvate\u013eom", "system_startup": "Spustenie syst\u00e9mu" } } diff --git a/homeassistant/components/tractive/translations/sk.json b/homeassistant/components/tractive/translations/sk.json index 6a01efb5eb7..f2a9a20a992 100644 --- a/homeassistant/components/tractive/translations/sk.json +++ b/homeassistant/components/tractive/translations/sk.json @@ -2,6 +2,7 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "reauth_failed_existing": "Nepodarilo sa aktualizova\u0165 polo\u017eku konfigur\u00e1cie, odstr\u00e1\u0148te integr\u00e1ciu a znova ju nastavte.", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { @@ -16,5 +17,17 @@ } } } + }, + "entity": { + "sensor": { + "tracker_state": { + "state": { + "not_reporting": "\u017diadna hl\u00e1\u0161ka", + "operational": "Prev\u00e1dzka", + "system_shutdown_user": "Vypnutie syst\u00e9mu pou\u017e\u00edvate\u013eom", + "system_startup": "Spustenie syst\u00e9mu" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tractive/translations/zh-Hant.json b/homeassistant/components/tractive/translations/zh-Hant.json index 8c9ec055f63..6fb402627eb 100644 --- a/homeassistant/components/tractive/translations/zh-Hant.json +++ b/homeassistant/components/tractive/translations/zh-Hant.json @@ -17,5 +17,17 @@ } } } + }, + "entity": { + "sensor": { + "tracker_state": { + "state": { + "not_reporting": "\u672a\u56de\u5831", + "operational": "\u53ef\u64cd\u4f5c", + "system_shutdown_user": "\u7cfb\u7d71\u95dc\u6a5f\u7528\u6236", + "system_startup": "\u7cfb\u7d71\u555f\u52d5" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tradfri/sensor.py b/homeassistant/components/tradfri/sensor.py index acda0bec06d..3b92abfad77 100644 --- a/homeassistant/components/tradfri/sensor.py +++ b/homeassistant/components/tradfri/sensor.py @@ -18,8 +18,8 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, PERCENTAGE, - TIME_HOURS, Platform, + UnitOfTime, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import entity_registry @@ -78,6 +78,7 @@ SENSOR_DESCRIPTIONS_BATTERY: tuple[TradfriSensorEntityDescription, ...] = ( TradfriSensorEntityDescription( key="battery_level", device_class=SensorDeviceClass.BATTERY, + state_class=SensorStateClass.MEASUREMENT, native_unit_of_measurement=PERCENTAGE, value=lambda device: cast(int, device.device_info.battery_level), ), @@ -89,6 +90,7 @@ SENSOR_DESCRIPTIONS_FAN: tuple[TradfriSensorEntityDescription, ...] = ( key="aqi", name="air quality", device_class=SensorDeviceClass.AQI, + state_class=SensorStateClass.MEASUREMENT, native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, value=_get_air_quality, ), @@ -96,7 +98,7 @@ SENSOR_DESCRIPTIONS_FAN: tuple[TradfriSensorEntityDescription, ...] = ( key="filter_life_remaining", name="filter time left", state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=TIME_HOURS, + native_unit_of_measurement=UnitOfTime.HOURS, icon="mdi:clock-outline", value=_get_filter_time_left, ), diff --git a/homeassistant/components/tradfri/translations/de.json b/homeassistant/components/tradfri/translations/de.json index 2278f30cab6..770b2046225 100644 --- a/homeassistant/components/tradfri/translations/de.json +++ b/homeassistant/components/tradfri/translations/de.json @@ -5,7 +5,7 @@ "already_in_progress": "Der Konfigurationsablauf wird bereits ausgef\u00fchrt" }, "error": { - "cannot_authenticate": "Authentifizierung nicht m\u00f6glich, ist das Gateway mit einem anderen Server wie z.B. Homekit gekoppelt?", + "cannot_authenticate": "Authentifizierung nicht m\u00f6glich, ist das Gateway mit einem anderen Server wie z.B. HomeKit gekoppelt?", "cannot_connect": "Verbindung fehlgeschlagen", "invalid_key": "Registrierung mit angegebenem Schl\u00fcssel fehlgeschlagen. Wenn dies weiterhin geschieht, starte den Gateway neu.", "timeout": "Timeout bei der \u00dcberpr\u00fcfung des Codes." diff --git a/homeassistant/components/tradfri/translations/sk.json b/homeassistant/components/tradfri/translations/sk.json index 55ad1f9e49d..4909c81baf3 100644 --- a/homeassistant/components/tradfri/translations/sk.json +++ b/homeassistant/components/tradfri/translations/sk.json @@ -6,7 +6,9 @@ }, "error": { "cannot_authenticate": "Nemo\u017eno overi\u0165, je br\u00e1na sp\u00e1rovan\u00e1 s in\u00fdm serverom, ako je napr\u00edklad Homekit?", - "cannot_connect": "Nepodarilo sa pripoji\u0165" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_key": "Registr\u00e1cia pomocou poskytnut\u00e9ho k\u013e\u00fa\u010da zlyhala. Ak sa to bude opakova\u0165, sk\u00faste br\u00e1nu re\u0161tartova\u0165.", + "timeout": "\u010casov\u00fd limit na overenie k\u00f3du." }, "step": { "auth": { @@ -14,6 +16,7 @@ "host": "Hostite\u013e", "security_code": "Bezpe\u010dnostn\u00fd k\u00f3d" }, + "description": "Bezpe\u010dnostn\u00fd k\u00f3d n\u00e1jdete na zadnej strane va\u0161ej br\u00e1ny.", "title": "Zadajte bezpe\u010dnostn\u00fd k\u00f3d" } } diff --git a/homeassistant/components/trafikverket_ferry/translations/cs.json b/homeassistant/components/trafikverket_ferry/translations/cs.json index 89ded7d388c..eceef5e7aec 100644 --- a/homeassistant/components/trafikverket_ferry/translations/cs.json +++ b/homeassistant/components/trafikverket_ferry/translations/cs.json @@ -6,6 +6,7 @@ }, "error": { "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", + "incorrect_api_key": "Neplatn\u00fd kl\u00ed\u010d API pro vybran\u00fd \u00fa\u010det", "invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed" } } diff --git a/homeassistant/components/trafikverket_ferry/translations/pt.json b/homeassistant/components/trafikverket_ferry/translations/pt.json index 8cd0d6c0842..fa32b8d7db4 100644 --- a/homeassistant/components/trafikverket_ferry/translations/pt.json +++ b/homeassistant/components/trafikverket_ferry/translations/pt.json @@ -1,7 +1,7 @@ { "config": { "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "user": { diff --git a/homeassistant/components/trafikverket_ferry/translations/sk.json b/homeassistant/components/trafikverket_ferry/translations/sk.json index 6c2b006092a..235b7b8eb67 100644 --- a/homeassistant/components/trafikverket_ferry/translations/sk.json +++ b/homeassistant/components/trafikverket_ferry/translations/sk.json @@ -1,12 +1,14 @@ { "config": { "abort": { - "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", "incorrect_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d pre vybran\u00fd \u00fa\u010det", - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "invalid_route": "Nepodarilo sa n\u00e1js\u0165 trasu s poskytnut\u00fdmi inform\u00e1ciami" }, "step": { "reauth_confirm": { @@ -17,7 +19,9 @@ "user": { "data": { "api_key": "API k\u013e\u00fa\u010d", + "from": "Z pr\u00edstavu", "time": "\u010cas", + "to": "Do pr\u00edstavu", "weekday": "Pracovn\u00e9 dni" } } diff --git a/homeassistant/components/trafikverket_train/__init__.py b/homeassistant/components/trafikverket_train/__init__.py index c1c756be9ed..0bedb7141c6 100644 --- a/homeassistant/components/trafikverket_train/__init__.py +++ b/homeassistant/components/trafikverket_train/__init__.py @@ -25,7 +25,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: if "Invalid authentication" in error.args[0]: raise ConfigEntryAuthFailed from error raise ConfigEntryNotReady( - f"Problem when trying station {entry.data[CONF_FROM]} to {entry.data[CONF_TO]}. Error: {error} " + f"Problem when trying station {entry.data[CONF_FROM]} to" + f" {entry.data[CONF_TO]}. Error: {error} " ) from error hass.data.setdefault(DOMAIN, {})[entry.entry_id] = { diff --git a/homeassistant/components/trafikverket_train/translations/ko.json b/homeassistant/components/trafikverket_train/translations/ko.json index 82cb120d44d..42ef3b1464f 100644 --- a/homeassistant/components/trafikverket_train/translations/ko.json +++ b/homeassistant/components/trafikverket_train/translations/ko.json @@ -1,10 +1,23 @@ { "config": { + "abort": { + "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, "step": { "reauth_confirm": { "data": { "api_key": "API \ud0a4" } + }, + "user": { + "data": { + "api_key": "API \ud0a4" + } } } } diff --git a/homeassistant/components/trafikverket_train/translations/sk.json b/homeassistant/components/trafikverket_train/translations/sk.json index f04bf4d5e32..43a214d3a70 100644 --- a/homeassistant/components/trafikverket_train/translations/sk.json +++ b/homeassistant/components/trafikverket_train/translations/sk.json @@ -1,11 +1,16 @@ { "config": { "abort": { - "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd" + "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", - "incorrect_api_key": "Neplatn\u00fd k\u013e\u00fa\u010d API pre vybran\u00e9 konto" + "incorrect_api_key": "Neplatn\u00fd k\u013e\u00fa\u010d API pre vybran\u00e9 konto", + "invalid_auth": "Neplatn\u00e9 overenie", + "invalid_station": "Nepodarilo sa n\u00e1js\u0165 stanicu so zadan\u00fdm n\u00e1zvom", + "invalid_time": "Zadan\u00fd neplatn\u00fd \u010das", + "more_stations": "Na\u0161lo sa viacero stan\u00edc so zadan\u00fdm n\u00e1zvom" }, "step": { "reauth_confirm": { @@ -16,6 +21,9 @@ "user": { "data": { "api_key": "API k\u013e\u00fa\u010d", + "from": "Zo stanice", + "time": "\u010cas (volite\u013en\u00e9)", + "to": "Do stanice", "weekday": "Dni" } } diff --git a/homeassistant/components/trafikverket_weatherstation/sensor.py b/homeassistant/components/trafikverket_weatherstation/sensor.py index 4053ce7cbc4..d50ba66d8e0 100644 --- a/homeassistant/components/trafikverket_weatherstation/sensor.py +++ b/homeassistant/components/trafikverket_weatherstation/sensor.py @@ -13,10 +13,10 @@ from homeassistant.components.sensor import ( from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( DEGREE, - LENGTH_MILLIMETERS, PERCENTAGE, - SPEED_METERS_PER_SECOND, - TEMP_CELSIUS, + UnitOfPrecipitationDepth, + UnitOfSpeed, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceEntryType @@ -49,8 +49,7 @@ SENSOR_TYPES: tuple[TrafikverketSensorEntityDescription, ...] = ( key="air_temp", api_key="air_temp", name="Air temperature", - native_unit_of_measurement=TEMP_CELSIUS, - icon="mdi:thermometer", + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), @@ -58,8 +57,7 @@ SENSOR_TYPES: tuple[TrafikverketSensorEntityDescription, ...] = ( key="road_temp", api_key="road_temp", name="Road temperature", - native_unit_of_measurement=TEMP_CELSIUS, - icon="mdi:thermometer", + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), @@ -88,17 +86,16 @@ SENSOR_TYPES: tuple[TrafikverketSensorEntityDescription, ...] = ( key="wind_speed", api_key="windforce", name="Wind speed", - native_unit_of_measurement=SPEED_METERS_PER_SECOND, - device_class=SensorDeviceClass.SPEED, - icon="mdi:weather-windy", + native_unit_of_measurement=UnitOfSpeed.METERS_PER_SECOND, + device_class=SensorDeviceClass.WIND_SPEED, state_class=SensorStateClass.MEASUREMENT, ), TrafikverketSensorEntityDescription( key="wind_speed_max", api_key="windforcemax", name="Wind speed max", - native_unit_of_measurement=SPEED_METERS_PER_SECOND, - device_class=SensorDeviceClass.SPEED, + native_unit_of_measurement=UnitOfSpeed.METERS_PER_SECOND, + device_class=SensorDeviceClass.WIND_SPEED, icon="mdi:weather-windy-variant", entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, @@ -117,8 +114,8 @@ SENSOR_TYPES: tuple[TrafikverketSensorEntityDescription, ...] = ( key="precipitation_amount", api_key="precipitation_amount", name="Precipitation amount", - native_unit_of_measurement=LENGTH_MILLIMETERS, - icon="mdi:cup-water", + native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + device_class=SensorDeviceClass.PRECIPITATION, state_class=SensorStateClass.MEASUREMENT, ), TrafikverketSensorEntityDescription( diff --git a/homeassistant/components/trafikverket_weatherstation/translations/ko.json b/homeassistant/components/trafikverket_weatherstation/translations/ko.json index 97d9717113c..c89bf06a7fe 100644 --- a/homeassistant/components/trafikverket_weatherstation/translations/ko.json +++ b/homeassistant/components/trafikverket_weatherstation/translations/ko.json @@ -1,12 +1,18 @@ { "config": { + "abort": { + "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "invalid_station": "\uc9c0\uc815\ub41c \uc774\ub984\uc758 \uae30\uc0c1 \uad00\uce21\uc18c\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4.", "more_stations": "\uc9c0\uc815\ub41c \uc774\ub984\uc744 \uac00\uc9c4 \uc5ec\ub7ec \uae30\uc0c1 \uad00\uce21\uc18c\ub97c \ucc3e\uc558\uc2b5\ub2c8\ub2e4." }, "step": { "user": { "data": { + "api_key": "API \ud0a4", "station": "\uc2a4\ud14c\uc774\uc158" } } diff --git a/homeassistant/components/trafikverket_weatherstation/translations/pt.json b/homeassistant/components/trafikverket_weatherstation/translations/pt.json index 8cd0d6c0842..fa32b8d7db4 100644 --- a/homeassistant/components/trafikverket_weatherstation/translations/pt.json +++ b/homeassistant/components/trafikverket_weatherstation/translations/pt.json @@ -1,7 +1,7 @@ { "config": { "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "user": { diff --git a/homeassistant/components/transmission/__init__.py b/homeassistant/components/transmission/__init__.py index 8f81382012d..e2c8a3ebfd4 100644 --- a/homeassistant/components/transmission/__init__.py +++ b/homeassistant/components/transmission/__init__.py @@ -2,18 +2,18 @@ from __future__ import annotations from datetime import timedelta +from functools import partial import logging from typing import Any -import transmissionrpc -from transmissionrpc.error import TransmissionError +import transmission_rpc +from transmission_rpc.error import TransmissionError import voluptuous as vol from homeassistant.config_entries import ConfigEntry, ConfigEntryState from homeassistant.const import ( CONF_HOST, CONF_ID, - CONF_NAME, CONF_PASSWORD, CONF_PORT, CONF_SCAN_INTERVAL, @@ -25,7 +25,6 @@ from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady from homeassistant.helpers import config_validation as cv, selector from homeassistant.helpers.dispatcher import dispatcher_send from homeassistant.helpers.event import async_track_time_interval -from homeassistant.helpers.issue_registry import IssueSeverity, create_issue from .const import ( ATTR_DELETE_DATA, @@ -55,13 +54,11 @@ _LOGGER = logging.getLogger(__name__) SERVICE_BASE_SCHEMA = vol.Schema( { vol.Exclusive(CONF_ENTRY_ID, "identifier"): selector.ConfigEntrySelector(), - vol.Exclusive(CONF_NAME, "identifier"): selector.TextSelector(), } ) SERVICE_ADD_TORRENT_SCHEMA = vol.All( SERVICE_BASE_SCHEMA.extend({vol.Required(ATTR_TORRENT): cv.string}), - cv.has_at_least_one_key(CONF_ENTRY_ID, CONF_NAME), ) @@ -71,13 +68,11 @@ SERVICE_REMOVE_TORRENT_SCHEMA = vol.All( vol.Required(CONF_ID): cv.positive_int, vol.Optional(ATTR_DELETE_DATA, default=DEFAULT_DELETE_DATA): cv.boolean, } - ), - cv.has_at_least_one_key(CONF_ENTRY_ID, CONF_NAME), + ) ) SERVICE_START_TORRENT_SCHEMA = vol.All( SERVICE_BASE_SCHEMA.extend({vol.Required(CONF_ID): cv.positive_int}), - cv.has_at_least_one_key(CONF_ENTRY_ID, CONF_NAME), ) SERVICE_STOP_TORRENT_SCHEMA = vol.All( @@ -85,8 +80,7 @@ SERVICE_STOP_TORRENT_SCHEMA = vol.All( { vol.Required(CONF_ID): cv.positive_int, } - ), - cv.has_at_least_one_key(CONF_ENTRY_ID, CONF_NAME), + ) ) CONFIG_SCHEMA = cv.removed(DOMAIN, raise_if_present=False) @@ -132,7 +126,13 @@ async def get_api(hass, entry): try: api = await hass.async_add_executor_job( - transmissionrpc.Client, host, port, username, password + partial( + transmission_rpc.Client, + username=username, + password=password, + host=host, + port=port, + ) ) _LOGGER.debug("Successfully connected to %s", host) return api @@ -158,27 +158,6 @@ def _get_client(hass: HomeAssistant, data: dict[str, Any]) -> TransmissionClient ): return hass.data[DOMAIN][entry_id] - # to be removed once name key is removed - if CONF_NAME in data: - create_issue( - hass, - DOMAIN, - "deprecated_key", - breaks_in_ha_version="2023.1.0", - is_fixable=True, - is_persistent=True, - severity=IssueSeverity.WARNING, - translation_key="deprecated_key", - ) - - _LOGGER.warning( - 'The "name" key in the Transmission services is deprecated and will be removed in "2023.1.0"; ' - 'use the "entry_id" key instead to identity which entry to call' - ) - for entry in hass.config_entries.async_entries(DOMAIN): - if entry.data[CONF_NAME] == data[CONF_NAME]: - return hass.data[DOMAIN][entry.entry_id] - return None @@ -189,7 +168,7 @@ class TransmissionClient: """Initialize the Transmission RPC API.""" self.hass = hass self.config_entry = config_entry - self.tm_api: transmissionrpc.Client = None + self.tm_api: transmission_rpc.Client = None self._tm_data: TransmissionData = None self.unsub_timer = None @@ -332,18 +311,18 @@ class TransmissionClient: class TransmissionData: """Get the latest data and update the states.""" - def __init__(self, hass, config, api: transmissionrpc.Client): + def __init__(self, hass, config, api: transmission_rpc.Client): """Initialize the Transmission RPC API.""" self.hass = hass self.config = config - self.data: transmissionrpc.Session = None + self.data: transmission_rpc.Session = None self.available: bool = True - self._all_torrents: list[transmissionrpc.Torrent] = [] - self._api: transmissionrpc.Client = api - self._completed_torrents: list[transmissionrpc.Torrent] = [] - self._session: transmissionrpc.Session = None - self._started_torrents: list[transmissionrpc.Torrent] = [] - self._torrents: list[transmissionrpc.Torrent] = [] + self._all_torrents: list[transmission_rpc.Torrent] = [] + self._api: transmission_rpc.Client = api + self._completed_torrents: list[transmission_rpc.Torrent] = [] + self._session: transmission_rpc.Session = None + self._started_torrents: list[transmission_rpc.Torrent] = [] + self._torrents: list[transmission_rpc.Torrent] = [] @property def host(self): @@ -356,7 +335,7 @@ class TransmissionData: return f"{DATA_UPDATED}-{self.host}" @property - def torrents(self) -> list[transmissionrpc.Torrent]: + def torrents(self) -> list[transmission_rpc.Torrent]: """Get the list of torrents.""" return self._torrents diff --git a/homeassistant/components/transmission/manifest.json b/homeassistant/components/transmission/manifest.json index 8f4fabc529d..386a92ac83c 100644 --- a/homeassistant/components/transmission/manifest.json +++ b/homeassistant/components/transmission/manifest.json @@ -3,7 +3,7 @@ "name": "Transmission", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/transmission", - "requirements": ["transmissionrpc==0.11"], + "requirements": ["transmission-rpc==3.4.0"], "codeowners": ["@engrbm87", "@JPHutchins"], "iot_class": "local_polling", "loggers": ["transmissionrpc"] diff --git a/homeassistant/components/transmission/sensor.py b/homeassistant/components/transmission/sensor.py index 0472ce221fb..7f8128e060c 100644 --- a/homeassistant/components/transmission/sensor.py +++ b/homeassistant/components/transmission/sensor.py @@ -3,11 +3,11 @@ from __future__ import annotations from contextlib import suppress -from transmissionrpc.torrent import Torrent +from transmission_rpc.torrent import Torrent -from homeassistant.components.sensor import SensorEntity +from homeassistant.components.sensor import SensorDeviceClass, SensorEntity from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_NAME, DATA_RATE_MEGABYTES_PER_SECOND, STATE_IDLE +from homeassistant.const import CONF_NAME, STATE_IDLE, UnitOfDataRate from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -97,10 +97,8 @@ class TransmissionSensor(SensorEntity): class TransmissionSpeedSensor(TransmissionSensor): """Representation of a Transmission speed sensor.""" - @property - def native_unit_of_measurement(self): - """Return the unit of measurement of this entity, if any.""" - return DATA_RATE_MEGABYTES_PER_SECOND + _attr_device_class = SensorDeviceClass.DATA_RATE + _attr_native_unit_of_measurement = UnitOfDataRate.MEGABYTES_PER_SECOND def update(self) -> None: """Get the latest data from Transmission and updates the state.""" diff --git a/homeassistant/components/transmission/services.yaml b/homeassistant/components/transmission/services.yaml index 2fd4793c785..66f4daf200f 100644 --- a/homeassistant/components/transmission/services.yaml +++ b/homeassistant/components/transmission/services.yaml @@ -8,12 +8,6 @@ add_torrent: selector: config_entry: integration: transmission - name: - name: Name - description: Instance name as entered during entry config - example: Transmission - selector: - text: torrent: name: Torrent description: URL, magnet link or Base64 encoded file. diff --git a/homeassistant/components/transmission/strings.json b/homeassistant/components/transmission/strings.json index a8ba9e5fcb3..2cf9fafff48 100644 --- a/homeassistant/components/transmission/strings.json +++ b/homeassistant/components/transmission/strings.json @@ -2,7 +2,7 @@ "config": { "step": { "user": { - "title": "Setup Transmission Client", + "title": "Set up Transmission Client", "data": { "name": "[%key:common::config_flow::data::name%]", "host": "[%key:common::config_flow::data::host%]", @@ -40,18 +40,5 @@ } } } - }, - "issues": { - "deprecated_key": { - "title": "The name key in Transmission services is being removed", - "fix_flow": { - "step": { - "confirm": { - "title": "The name key in Transmission services is being removed", - "description": "Update any automations or scripts that use this service and replace the name key with the entry_id key." - } - } - } - } } } diff --git a/homeassistant/components/transmission/translations/ca.json b/homeassistant/components/transmission/translations/ca.json index f982fcc9dd2..29c4c0a35d2 100644 --- a/homeassistant/components/transmission/translations/ca.json +++ b/homeassistant/components/transmission/translations/ca.json @@ -25,7 +25,7 @@ "port": "Port", "username": "Nom d'usuari" }, - "title": "Configuraci\u00f3 del client de Transmission" + "title": "Configuraci\u00f3 del client Transmission" } } }, diff --git a/homeassistant/components/transmission/translations/en.json b/homeassistant/components/transmission/translations/en.json index 46ab2c64ad9..ff2a6b779e7 100644 --- a/homeassistant/components/transmission/translations/en.json +++ b/homeassistant/components/transmission/translations/en.json @@ -25,7 +25,7 @@ "port": "Port", "username": "Username" }, - "title": "Setup Transmission Client" + "title": "Set up Transmission Client" } } }, diff --git a/homeassistant/components/transmission/translations/he.json b/homeassistant/components/transmission/translations/he.json index 73c5f6c7384..31a8887945c 100644 --- a/homeassistant/components/transmission/translations/he.json +++ b/homeassistant/components/transmission/translations/he.json @@ -25,5 +25,10 @@ } } } + }, + "issues": { + "deprecated_key": { + "title": "\u05de\u05e4\u05ea\u05d7 \u05d4\u05e9\u05dd \u05d1\u05e9\u05d9\u05e8\u05d5\u05ea\u05d9 \u05e9\u05d9\u05d3\u05d5\u05e8 \u05de\u05d5\u05e1\u05e8" + } } } \ No newline at end of file diff --git a/homeassistant/components/transmission/translations/it.json b/homeassistant/components/transmission/translations/it.json index 41eaa4efdf5..f920833f56e 100644 --- a/homeassistant/components/transmission/translations/it.json +++ b/homeassistant/components/transmission/translations/it.json @@ -25,7 +25,7 @@ "port": "Porta", "username": "Nome utente" }, - "title": "Configura client di Trasmissione" + "title": "Configura il Client di Trasmissione" } } }, diff --git a/homeassistant/components/transmission/translations/ko.json b/homeassistant/components/transmission/translations/ko.json index 898a3710350..f5d9db19b4a 100644 --- a/homeassistant/components/transmission/translations/ko.json +++ b/homeassistant/components/transmission/translations/ko.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" }, "error": { "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", @@ -9,6 +10,12 @@ "name_exists": "\uc774\ub984\uc774 \uc774\ubbf8 \uc874\uc7ac\ud569\ub2c8\ub2e4" }, "step": { + "reauth_confirm": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638" + }, + "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" + }, "user": { "data": { "host": "\ud638\uc2a4\ud2b8", diff --git a/homeassistant/components/transmission/translations/no.json b/homeassistant/components/transmission/translations/no.json index 2a03ee48b74..89e3b1ce9c5 100644 --- a/homeassistant/components/transmission/translations/no.json +++ b/homeassistant/components/transmission/translations/no.json @@ -25,7 +25,7 @@ "port": "Port", "username": "Brukernavn" }, - "title": "Oppsett av Transmission-klient" + "title": "Sett opp Transmission Client" } } }, diff --git a/homeassistant/components/transmission/translations/pt.json b/homeassistant/components/transmission/translations/pt.json index 399596eff8b..bf794e4dba2 100644 --- a/homeassistant/components/transmission/translations/pt.json +++ b/homeassistant/components/transmission/translations/pt.json @@ -5,7 +5,7 @@ "reauth_successful": "Reautentica\u00e7\u00e3o bem sucedida" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "name_exists": "Nome j\u00e1 existe" }, @@ -18,7 +18,7 @@ }, "user": { "data": { - "host": "Servidor", + "host": "Endere\u00e7o", "name": "Nome", "password": "Palavra-passe", "port": "Porta", diff --git a/homeassistant/components/transmission/translations/sk.json b/homeassistant/components/transmission/translations/sk.json index 178b0043359..c688d9a4906 100644 --- a/homeassistant/components/transmission/translations/sk.json +++ b/homeassistant/components/transmission/translations/sk.json @@ -24,7 +24,8 @@ "password": "Heslo", "port": "Port", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" - } + }, + "title": "Nastavenie klienta prenosu" } } }, @@ -37,15 +38,19 @@ "title": "K\u013e\u00fa\u010d s n\u00e1zvom v slu\u017eb\u00e1ch prenosu sa odstra\u0148uje" } } - } + }, + "title": "K\u013e\u00fa\u010d s n\u00e1zvom v slu\u017eb\u00e1ch prenosu sa odstra\u0148uje" } }, "options": { "step": { "init": { "data": { + "limit": "Limit", + "order": "Poradie", "scan_interval": "Frekvencia aktualiz\u00e1cie" - } + }, + "title": "Konfigur\u00e1cia mo\u017enost\u00ed Transmission" } } } diff --git a/homeassistant/components/transport_nsw/sensor.py b/homeassistant/components/transport_nsw/sensor.py index 116dd5c0923..83fa590429f 100644 --- a/homeassistant/components/transport_nsw/sensor.py +++ b/homeassistant/components/transport_nsw/sensor.py @@ -7,7 +7,7 @@ from TransportNSW import TransportNSW import voluptuous as vol from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity -from homeassistant.const import ATTR_MODE, CONF_API_KEY, CONF_NAME, TIME_MINUTES +from homeassistant.const import ATTR_MODE, CONF_API_KEY, CONF_NAME, UnitOfTime from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -106,7 +106,7 @@ class TransportNSWSensor(SensorEntity): @property def native_unit_of_measurement(self): """Return the unit this state is expressed in.""" - return TIME_MINUTES + return UnitOfTime.MINUTES @property def icon(self): diff --git a/homeassistant/components/travisci/sensor.py b/homeassistant/components/travisci/sensor.py index ab1cd4a6b03..b33f7315d17 100644 --- a/homeassistant/components/travisci/sensor.py +++ b/homeassistant/components/travisci/sensor.py @@ -18,7 +18,7 @@ from homeassistant.const import ( CONF_API_KEY, CONF_MONITORED_CONDITIONS, CONF_SCAN_INTERVAL, - TIME_SECONDS, + UnitOfTime, ) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv @@ -43,7 +43,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="last_build_duration", name="Last Build Duration", - native_unit_of_measurement=TIME_SECONDS, + native_unit_of_measurement=UnitOfTime.SECONDS, icon="mdi:timelapse", ), SensorEntityDescription( @@ -106,9 +106,7 @@ def setup_platform( _LOGGER.error("Unable to connect to Travis CI service: %s", str(ex)) persistent_notification.create( hass, - "Error: {}
" - "You will need to restart hass after fixing." - "".format(ex), + f"Error: {ex}
You will need to restart hass after fixing.", title=NOTIFICATION_TITLE, notification_id=NOTIFICATION_ID, ) diff --git a/homeassistant/components/tts/__init__.py b/homeassistant/components/tts/__init__.py index 5914512a315..c07df07ae4f 100644 --- a/homeassistant/components/tts/__init__.py +++ b/homeassistant/components/tts/__init__.py @@ -158,7 +158,8 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: base_url = conf.get(CONF_BASE_URL) if base_url is not None: _LOGGER.warning( - "TTS base_url option is deprecated. Configure internal/external URL instead" + "TTS base_url option is deprecated. Configure internal/external URL" + " instead" ) hass.data[BASE_URL_KEY] = base_url @@ -241,7 +242,9 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # Register the service description service_desc = { CONF_NAME: f"Say a TTS message with {p_type}", - CONF_DESCRIPTION: f"Say something using text-to-speech on a media player with {p_type}.", + CONF_DESCRIPTION: ( + f"Say something using text-to-speech on a media player with {p_type}." + ), CONF_FIELDS: services_dict[SERVICE_SAY][CONF_FIELDS], } async_set_service_schema(hass, DOMAIN, service_name, service_desc) diff --git a/homeassistant/components/tuya/climate.py b/homeassistant/components/tuya/climate.py index 20e36028dba..64151195b01 100644 --- a/homeassistant/components/tuya/climate.py +++ b/homeassistant/components/tuya/climate.py @@ -18,7 +18,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT +from homeassistant.const import UnitOfTemperature from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -147,16 +147,16 @@ class TuyaClimateEntity(TuyaEntity, ClimateEntity): ) or all( dpcode in device.status for dpcode in (DPCode.TEMP_SET, DPCode.TEMP_SET_F) ): - prefered_temperature_unit = TEMP_CELSIUS + prefered_temperature_unit = UnitOfTemperature.CELSIUS if any( "f" in device.status[dpcode].lower() for dpcode in (DPCode.C_F, DPCode.TEMP_UNIT_CONVERT) if isinstance(device.status.get(dpcode), str) ): - prefered_temperature_unit = TEMP_FAHRENHEIT + prefered_temperature_unit = UnitOfTemperature.FAHRENHEIT # Default to Celsius - self._attr_temperature_unit = TEMP_CELSIUS + self._attr_temperature_unit = UnitOfTemperature.CELSIUS # Figure out current temperature, use preferred unit or what is available celsius_type = self.find_dpcode( @@ -166,13 +166,16 @@ class TuyaClimateEntity(TuyaEntity, ClimateEntity): (DPCode.TEMP_CURRENT_F, DPCode.UPPER_TEMP_F), dptype=DPType.INTEGER ) if farhenheit_type and ( - prefered_temperature_unit == TEMP_FAHRENHEIT - or (prefered_temperature_unit == TEMP_CELSIUS and not celsius_type) + prefered_temperature_unit == UnitOfTemperature.FAHRENHEIT + or ( + prefered_temperature_unit == UnitOfTemperature.CELSIUS + and not celsius_type + ) ): - self._attr_temperature_unit = TEMP_FAHRENHEIT + self._attr_temperature_unit = UnitOfTemperature.FAHRENHEIT self._current_temperature = farhenheit_type elif celsius_type: - self._attr_temperature_unit = TEMP_CELSIUS + self._attr_temperature_unit = UnitOfTemperature.CELSIUS self._current_temperature = celsius_type # Figure out setting temperature, use preferred unit or what is available @@ -183,8 +186,11 @@ class TuyaClimateEntity(TuyaEntity, ClimateEntity): DPCode.TEMP_SET_F, dptype=DPType.INTEGER, prefer_function=True ) if farhenheit_type and ( - prefered_temperature_unit == TEMP_FAHRENHEIT - or (prefered_temperature_unit == TEMP_CELSIUS and not celsius_type) + prefered_temperature_unit == UnitOfTemperature.FAHRENHEIT + or ( + prefered_temperature_unit == UnitOfTemperature.CELSIUS + and not celsius_type + ) ): self._set_temperature = farhenheit_type elif celsius_type: @@ -335,7 +341,8 @@ class TuyaClimateEntity(TuyaEntity, ClimateEntity): """Set new target temperature.""" if self._set_temperature is None: raise RuntimeError( - "Cannot set target temperature, device doesn't provide methods to set it" + "Cannot set target temperature, device doesn't provide methods to" + " set it" ) self._send_command( diff --git a/homeassistant/components/tuya/const.py b/homeassistant/components/tuya/const.py index 50e55cd8d77..75ccffce685 100644 --- a/homeassistant/components/tuya/const.py +++ b/homeassistant/components/tuya/const.py @@ -14,29 +14,18 @@ from homeassistant.const import ( CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER, CONCENTRATION_PARTS_PER_BILLION, CONCENTRATION_PARTS_PER_MILLION, - ELECTRIC_CURRENT_AMPERE, - ELECTRIC_CURRENT_MILLIAMPERE, - ELECTRIC_POTENTIAL_MILLIVOLT, - ELECTRIC_POTENTIAL_VOLT, - ENERGY_KILO_WATT_HOUR, - ENERGY_WATT_HOUR, LIGHT_LUX, PERCENTAGE, - POWER_KILO_WATT, - POWER_WATT, - PRESSURE_BAR, - PRESSURE_HPA, - PRESSURE_INHG, - PRESSURE_MBAR, - PRESSURE_PA, - PRESSURE_PSI, SIGNAL_STRENGTH_DECIBELS, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, - VOLUME_CUBIC_FEET, - VOLUME_CUBIC_METERS, Platform, + UnitOfElectricCurrent, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfPower, + UnitOfPressure, + UnitOfTemperature, + UnitOfVolume, ) DOMAIN = "tuya" @@ -84,33 +73,6 @@ PLATFORMS = [ ] -class TuyaDeviceClass(StrEnum): - """Tuya specific device classes, used for translations.""" - - AIR_QUALITY = "tuya__air_quality" - CURTAIN_MODE = "tuya__curtain_mode" - CURTAIN_MOTOR_MODE = "tuya__curtain_motor_mode" - BASIC_ANTI_FLICKR = "tuya__basic_anti_flickr" - BASIC_NIGHTVISION = "tuya__basic_nightvision" - COUNTDOWN = "tuya__countdown" - DECIBEL_SENSITIVITY = "tuya__decibel_sensitivity" - FAN_ANGLE = "tuya__fan_angle" - FINGERBOT_MODE = "tuya__fingerbot_mode" - HUMIDIFIER_SPRAY_MODE = "tuya__humidifier_spray_mode" - HUMIDIFIER_LEVEL = "tuya__humidifier_level" - HUMIDIFIER_MOODLIGHTING = "tuya__humidifier_moodlighting" - IPC_WORK_MODE = "tuya__ipc_work_mode" - LED_TYPE = "tuya__led_type" - LIGHT_MODE = "tuya__light_mode" - MOTION_SENSITIVITY = "tuya__motion_sensitivity" - RECORD_MODE = "tuya__record_mode" - RELAY_STATUS = "tuya__relay_status" - STATUS = "tuya__status" - VACUUM_CISTERN = "tuya__vacuum_cistern" - VACUUM_COLLECTION = "tuya__vacuum_collection" - VACUUM_MODE = "tuya__vacuum_mode" - - class WorkMode(StrEnum): """Work modes.""" @@ -458,34 +420,34 @@ UNITS = ( conversion_fn=lambda x: x / 1000, ), UnitOfMeasurement( - unit=ELECTRIC_CURRENT_AMPERE, + unit=UnitOfElectricCurrent.AMPERE, aliases={"a", "ampere"}, device_classes={SensorDeviceClass.CURRENT}, ), UnitOfMeasurement( - unit=ELECTRIC_CURRENT_MILLIAMPERE, + unit=UnitOfElectricCurrent.MILLIAMPERE, aliases={"ma", "milliampere"}, device_classes={SensorDeviceClass.CURRENT}, - conversion_unit=ELECTRIC_CURRENT_AMPERE, + conversion_unit=UnitOfElectricCurrent.AMPERE, conversion_fn=lambda x: x / 1000, ), UnitOfMeasurement( - unit=ENERGY_WATT_HOUR, + unit=UnitOfEnergy.WATT_HOUR, aliases={"wh", "watthour"}, device_classes={SensorDeviceClass.ENERGY}, ), UnitOfMeasurement( - unit=ENERGY_KILO_WATT_HOUR, + unit=UnitOfEnergy.KILO_WATT_HOUR, aliases={"kwh", "kilowatt-hour", "kW·h"}, device_classes={SensorDeviceClass.ENERGY}, ), UnitOfMeasurement( - unit=VOLUME_CUBIC_FEET, + unit=UnitOfVolume.CUBIC_FEET, aliases={"ft3"}, device_classes={SensorDeviceClass.GAS}, ), UnitOfMeasurement( - unit=VOLUME_CUBIC_METERS, + unit=UnitOfVolume.CUBIC_METERS, aliases={"m3"}, device_classes={SensorDeviceClass.GAS}, ), @@ -494,11 +456,6 @@ UNITS = ( aliases={"lux"}, device_classes={SensorDeviceClass.ILLUMINANCE}, ), - UnitOfMeasurement( - unit="lm", - aliases={"lum", "lumen"}, - device_classes={SensorDeviceClass.ILLUMINANCE}, - ), UnitOfMeasurement( unit=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, aliases={"ug/m3", "µg/m3", "ug/m³"}, @@ -532,40 +489,40 @@ UNITS = ( conversion_fn=lambda x: x * 1000, ), UnitOfMeasurement( - unit=POWER_WATT, + unit=UnitOfPower.WATT, aliases={"watt"}, device_classes={SensorDeviceClass.POWER}, ), UnitOfMeasurement( - unit=POWER_KILO_WATT, + unit=UnitOfPower.KILO_WATT, aliases={"kilowatt"}, device_classes={SensorDeviceClass.POWER}, ), UnitOfMeasurement( - unit=PRESSURE_BAR, + unit=UnitOfPressure.BAR, device_classes={SensorDeviceClass.PRESSURE}, ), UnitOfMeasurement( - unit=PRESSURE_MBAR, + unit=UnitOfPressure.MBAR, aliases={"millibar"}, device_classes={SensorDeviceClass.PRESSURE}, ), UnitOfMeasurement( - unit=PRESSURE_HPA, + unit=UnitOfPressure.HPA, aliases={"hpa", "hectopascal"}, device_classes={SensorDeviceClass.PRESSURE}, ), UnitOfMeasurement( - unit=PRESSURE_INHG, + unit=UnitOfPressure.INHG, aliases={"inhg"}, device_classes={SensorDeviceClass.PRESSURE}, ), UnitOfMeasurement( - unit=PRESSURE_PSI, + unit=UnitOfPressure.PSI, device_classes={SensorDeviceClass.PRESSURE}, ), UnitOfMeasurement( - unit=PRESSURE_PA, + unit=UnitOfPressure.PA, device_classes={SensorDeviceClass.PRESSURE}, ), UnitOfMeasurement( @@ -579,25 +536,25 @@ UNITS = ( device_classes={SensorDeviceClass.SIGNAL_STRENGTH}, ), UnitOfMeasurement( - unit=TEMP_CELSIUS, + unit=UnitOfTemperature.CELSIUS, aliases={"°c", "c", "celsius", "℃"}, device_classes={SensorDeviceClass.TEMPERATURE}, ), UnitOfMeasurement( - unit=TEMP_FAHRENHEIT, + unit=UnitOfTemperature.FAHRENHEIT, aliases={"°f", "f", "fahrenheit"}, device_classes={SensorDeviceClass.TEMPERATURE}, ), UnitOfMeasurement( - unit=ELECTRIC_POTENTIAL_VOLT, + unit=UnitOfElectricPotential.VOLT, aliases={"volt"}, device_classes={SensorDeviceClass.VOLTAGE}, ), UnitOfMeasurement( - unit=ELECTRIC_POTENTIAL_MILLIVOLT, + unit=UnitOfElectricPotential.MILLIVOLT, aliases={"mv", "millivolt"}, device_classes={SensorDeviceClass.VOLTAGE}, - conversion_unit=ELECTRIC_POTENTIAL_VOLT, + conversion_unit=UnitOfElectricPotential.VOLT, conversion_fn=lambda x: x / 1000, ), ) diff --git a/homeassistant/components/tuya/number.py b/homeassistant/components/tuya/number.py index 21c6dcee6e1..8a65c06b70e 100644 --- a/homeassistant/components/tuya/number.py +++ b/homeassistant/components/tuya/number.py @@ -9,7 +9,7 @@ from homeassistant.components.number import ( NumberEntityDescription, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import TIME_MINUTES +from homeassistant.const import UnitOfTime from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import EntityCategory @@ -146,7 +146,7 @@ NUMBERS: dict[str, tuple[NumberEntityDescription, ...]] = { key=DPCode.COOK_TIME, name="Cook time", icon="mdi:timer", - native_unit_of_measurement=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, entity_category=EntityCategory.CONFIG, ), NumberEntityDescription( diff --git a/homeassistant/components/tuya/select.py b/homeassistant/components/tuya/select.py index 36b08868e0f..57059a3bcc7 100644 --- a/homeassistant/components/tuya/select.py +++ b/homeassistant/components/tuya/select.py @@ -12,7 +12,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from . import HomeAssistantTuyaData from .base import TuyaEntity -from .const import DOMAIN, TUYA_DISCOVERY_NEW, DPCode, DPType, TuyaDeviceClass +from .const import DOMAIN, TUYA_DISCOVERY_NEW, DPCode, DPType # All descriptions can be found here. Mostly the Enum data types in the # default instructions set of each category end up being a select. @@ -58,14 +58,14 @@ SELECTS: dict[str, tuple[SelectEntityDescription, ...]] = { SelectEntityDescription( key=DPCode.RELAY_STATUS, name="Power on behavior", - device_class=TuyaDeviceClass.RELAY_STATUS, entity_category=EntityCategory.CONFIG, + translation_key="relay_status", ), SelectEntityDescription( key=DPCode.LIGHT_MODE, name="Indicator light mode", - device_class=TuyaDeviceClass.LIGHT_MODE, entity_category=EntityCategory.CONFIG, + translation_key="light_mode", ), ), # Heater @@ -97,43 +97,43 @@ SELECTS: dict[str, tuple[SelectEntityDescription, ...]] = { SelectEntityDescription( key=DPCode.IPC_WORK_MODE, name="IPC mode", - device_class=TuyaDeviceClass.IPC_WORK_MODE, entity_category=EntityCategory.CONFIG, + translation_key="ipc_work_mode", ), SelectEntityDescription( key=DPCode.DECIBEL_SENSITIVITY, name="Sound detection densitivity", icon="mdi:volume-vibrate", - device_class=TuyaDeviceClass.DECIBEL_SENSITIVITY, entity_category=EntityCategory.CONFIG, + translation_key="decibel_sensitivity", ), SelectEntityDescription( key=DPCode.RECORD_MODE, name="Record mode", icon="mdi:record-rec", - device_class=TuyaDeviceClass.RECORD_MODE, entity_category=EntityCategory.CONFIG, + translation_key="record_mode", ), SelectEntityDescription( key=DPCode.BASIC_NIGHTVISION, name="Night vision", icon="mdi:theme-light-dark", - device_class=TuyaDeviceClass.BASIC_NIGHTVISION, entity_category=EntityCategory.CONFIG, + translation_key="basic_nightvision", ), SelectEntityDescription( key=DPCode.BASIC_ANTI_FLICKER, name="Anti-flicker", icon="mdi:image-outline", - device_class=TuyaDeviceClass.BASIC_ANTI_FLICKR, entity_category=EntityCategory.CONFIG, + translation_key="basic_anti_flicker", ), SelectEntityDescription( key=DPCode.MOTION_SENSITIVITY, name="Motion detection sensitivity", icon="mdi:motion-sensor", - device_class=TuyaDeviceClass.MOTION_SENSITIVITY, entity_category=EntityCategory.CONFIG, + translation_key="motion_sensitivity", ), ), # IoT Switch? @@ -142,14 +142,14 @@ SELECTS: dict[str, tuple[SelectEntityDescription, ...]] = { SelectEntityDescription( key=DPCode.RELAY_STATUS, name="Power on behavior", - device_class=TuyaDeviceClass.RELAY_STATUS, entity_category=EntityCategory.CONFIG, + translation_key="relay_status", ), SelectEntityDescription( key=DPCode.LIGHT_MODE, name="Indicator light mode", - device_class=TuyaDeviceClass.LIGHT_MODE, entity_category=EntityCategory.CONFIG, + translation_key="light_mode", ), ), # Dimmer Switch @@ -158,32 +158,32 @@ SELECTS: dict[str, tuple[SelectEntityDescription, ...]] = { SelectEntityDescription( key=DPCode.RELAY_STATUS, name="Power on behavior", - device_class=TuyaDeviceClass.RELAY_STATUS, entity_category=EntityCategory.CONFIG, + translation_key="relay_status", ), SelectEntityDescription( key=DPCode.LIGHT_MODE, name="Indicator light mode", - device_class=TuyaDeviceClass.LIGHT_MODE, entity_category=EntityCategory.CONFIG, + translation_key="light_mode", ), SelectEntityDescription( key=DPCode.LED_TYPE_1, name="Light source type", - device_class=TuyaDeviceClass.LED_TYPE, entity_category=EntityCategory.CONFIG, + translation_key="led_type", ), SelectEntityDescription( key=DPCode.LED_TYPE_2, name="Light 2 source type", - device_class=TuyaDeviceClass.LED_TYPE, entity_category=EntityCategory.CONFIG, + translation_key="led_type", ), SelectEntityDescription( key=DPCode.LED_TYPE_3, name="Light 3 source type", - device_class=TuyaDeviceClass.LED_TYPE, entity_category=EntityCategory.CONFIG, + translation_key="led_type", ), ), # Dimmer @@ -192,14 +192,14 @@ SELECTS: dict[str, tuple[SelectEntityDescription, ...]] = { SelectEntityDescription( key=DPCode.LED_TYPE_1, name="Light source type", - device_class=TuyaDeviceClass.LED_TYPE, entity_category=EntityCategory.CONFIG, + translation_key="led_type", ), SelectEntityDescription( key=DPCode.LED_TYPE_2, name="Light 2 source type", - device_class=TuyaDeviceClass.LED_TYPE, entity_category=EntityCategory.CONFIG, + translation_key="led_type", ), ), # Fingerbot @@ -207,8 +207,8 @@ SELECTS: dict[str, tuple[SelectEntityDescription, ...]] = { SelectEntityDescription( key=DPCode.MODE, name="Mode", - device_class=TuyaDeviceClass.FINGERBOT_MODE, entity_category=EntityCategory.CONFIG, + translation_key="fingerbot_mode", ), ), # Robot Vacuum @@ -218,22 +218,22 @@ SELECTS: dict[str, tuple[SelectEntityDescription, ...]] = { key=DPCode.CISTERN, name="Water tank adjustment", entity_category=EntityCategory.CONFIG, - device_class=TuyaDeviceClass.VACUUM_CISTERN, icon="mdi:water-opacity", + translation_key="vacuum_cistern", ), SelectEntityDescription( key=DPCode.COLLECTION_MODE, name="Dust collection mode", entity_category=EntityCategory.CONFIG, - device_class=TuyaDeviceClass.VACUUM_COLLECTION, icon="mdi:air-filter", + translation_key="vacuum_collection", ), SelectEntityDescription( key=DPCode.MODE, name="Mode", entity_category=EntityCategory.CONFIG, - device_class=TuyaDeviceClass.VACUUM_MODE, icon="mdi:layers-outline", + translation_key="vacuum_mode", ), ), # Fan @@ -242,30 +242,30 @@ SELECTS: dict[str, tuple[SelectEntityDescription, ...]] = { SelectEntityDescription( key=DPCode.FAN_VERTICAL, name="Vertical swing flap angle", - device_class=TuyaDeviceClass.FAN_ANGLE, entity_category=EntityCategory.CONFIG, icon="mdi:format-vertical-align-center", + translation_key="fan_angle", ), SelectEntityDescription( key=DPCode.FAN_HORIZONTAL, name="Horizontal swing flap angle", - device_class=TuyaDeviceClass.FAN_ANGLE, entity_category=EntityCategory.CONFIG, icon="mdi:format-horizontal-align-center", + translation_key="fan_angle", ), SelectEntityDescription( key=DPCode.COUNTDOWN, name="Countdown", - device_class=TuyaDeviceClass.COUNTDOWN, entity_category=EntityCategory.CONFIG, icon="mdi:timer-cog-outline", + translation_key="countdown", ), SelectEntityDescription( key=DPCode.COUNTDOWN_SET, name="Countdown", - device_class=TuyaDeviceClass.COUNTDOWN, entity_category=EntityCategory.CONFIG, icon="mdi:timer-cog-outline", + translation_key="countdown", ), ), # Curtain @@ -274,15 +274,15 @@ SELECTS: dict[str, tuple[SelectEntityDescription, ...]] = { SelectEntityDescription( key=DPCode.CONTROL_BACK_MODE, name="Motor mode", - device_class=TuyaDeviceClass.CURTAIN_MOTOR_MODE, entity_category=EntityCategory.CONFIG, icon="mdi:swap-horizontal", + translation_key="curtain_motor_mode", ), SelectEntityDescription( key=DPCode.MODE, name="Mode", - device_class=TuyaDeviceClass.CURTAIN_MODE, entity_category=EntityCategory.CONFIG, + translation_key="curtain_mode", ), ), # Humidifier @@ -291,37 +291,37 @@ SELECTS: dict[str, tuple[SelectEntityDescription, ...]] = { SelectEntityDescription( key=DPCode.SPRAY_MODE, name="Spray mode", - device_class=TuyaDeviceClass.HUMIDIFIER_SPRAY_MODE, entity_category=EntityCategory.CONFIG, icon="mdi:spray", + translation_key="humidifier_spray_mode", ), SelectEntityDescription( key=DPCode.LEVEL, name="Spraying level", - device_class=TuyaDeviceClass.HUMIDIFIER_LEVEL, entity_category=EntityCategory.CONFIG, icon="mdi:spray", + translation_key="humidifier_level", ), SelectEntityDescription( key=DPCode.MOODLIGHTING, name="Moodlighting", - device_class=TuyaDeviceClass.HUMIDIFIER_MOODLIGHTING, entity_category=EntityCategory.CONFIG, icon="mdi:lightbulb-multiple", + translation_key="humidifier_moodlighting", ), SelectEntityDescription( key=DPCode.COUNTDOWN, name="Countdown", - device_class=TuyaDeviceClass.COUNTDOWN, entity_category=EntityCategory.CONFIG, icon="mdi:timer-cog-outline", + translation_key="countdown", ), SelectEntityDescription( key=DPCode.COUNTDOWN_SET, name="Countdown", - device_class=TuyaDeviceClass.COUNTDOWN, entity_category=EntityCategory.CONFIG, icon="mdi:timer-cog-outline", + translation_key="countdown", ), ), # Air Purifier @@ -330,16 +330,16 @@ SELECTS: dict[str, tuple[SelectEntityDescription, ...]] = { SelectEntityDescription( key=DPCode.COUNTDOWN, name="Countdown", - device_class=TuyaDeviceClass.COUNTDOWN, entity_category=EntityCategory.CONFIG, icon="mdi:timer-cog-outline", + translation_key="countdown", ), SelectEntityDescription( key=DPCode.COUNTDOWN_SET, name="Countdown", - device_class=TuyaDeviceClass.COUNTDOWN, entity_category=EntityCategory.CONFIG, icon="mdi:timer-cog-outline", + translation_key="countdown", ), ), } diff --git a/homeassistant/components/tuya/sensor.py b/homeassistant/components/tuya/sensor.py index 98969fe4c48..7b09ecfaf74 100644 --- a/homeassistant/components/tuya/sensor.py +++ b/homeassistant/components/tuya/sensor.py @@ -14,11 +14,11 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - ELECTRIC_CURRENT_AMPERE, - ELECTRIC_POTENTIAL_VOLT, PERCENTAGE, - POWER_KILO_WATT, - TIME_MINUTES, + UnitOfElectricCurrent, + UnitOfElectricPotential, + UnitOfPower, + UnitOfTime, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect @@ -34,7 +34,6 @@ from .const import ( TUYA_DISCOVERY_NEW, DPCode, DPType, - TuyaDeviceClass, UnitOfMeasurement, ) @@ -179,7 +178,6 @@ SENSORS: dict[str, tuple[TuyaSensorEntityDescription, ...]] = { TuyaSensorEntityDescription( key=DPCode.STATUS, name="Status", - device_class=TuyaDeviceClass.STATUS, ), ), # CO2 Detector @@ -434,12 +432,12 @@ SENSORS: dict[str, tuple[TuyaSensorEntityDescription, ...]] = { TuyaSensorEntityDescription( key=DPCode.STATUS, name="Status", - device_class=TuyaDeviceClass.STATUS, + translation_key="status", ), TuyaSensorEntityDescription( key=DPCode.REMAIN_TIME, name="Remaining time", - native_unit_of_measurement=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, icon="mdi:timer", ), ), @@ -668,7 +666,7 @@ SENSORS: dict[str, tuple[TuyaSensorEntityDescription, ...]] = { key=DPCode.PHASE_A, name="Phase A current", device_class=SensorDeviceClass.CURRENT, - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, state_class=SensorStateClass.MEASUREMENT, subkey="electriccurrent", ), @@ -677,7 +675,7 @@ SENSORS: dict[str, tuple[TuyaSensorEntityDescription, ...]] = { name="Phase A power", device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=POWER_KILO_WATT, + native_unit_of_measurement=UnitOfPower.KILO_WATT, subkey="power", ), TuyaSensorEntityDescription( @@ -685,14 +683,14 @@ SENSORS: dict[str, tuple[TuyaSensorEntityDescription, ...]] = { name="Phase A voltage", device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, subkey="voltage", ), TuyaSensorEntityDescription( key=DPCode.PHASE_B, name="Phase B current", device_class=SensorDeviceClass.CURRENT, - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, state_class=SensorStateClass.MEASUREMENT, subkey="electriccurrent", ), @@ -701,7 +699,7 @@ SENSORS: dict[str, tuple[TuyaSensorEntityDescription, ...]] = { name="Phase B power", device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=POWER_KILO_WATT, + native_unit_of_measurement=UnitOfPower.KILO_WATT, subkey="power", ), TuyaSensorEntityDescription( @@ -709,14 +707,14 @@ SENSORS: dict[str, tuple[TuyaSensorEntityDescription, ...]] = { name="Phase B voltage", device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, subkey="voltage", ), TuyaSensorEntityDescription( key=DPCode.PHASE_C, name="Phase C current", device_class=SensorDeviceClass.CURRENT, - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, state_class=SensorStateClass.MEASUREMENT, subkey="electriccurrent", ), @@ -725,7 +723,7 @@ SENSORS: dict[str, tuple[TuyaSensorEntityDescription, ...]] = { name="Phase C power", device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=POWER_KILO_WATT, + native_unit_of_measurement=UnitOfPower.KILO_WATT, subkey="power", ), TuyaSensorEntityDescription( @@ -733,7 +731,7 @@ SENSORS: dict[str, tuple[TuyaSensorEntityDescription, ...]] = { name="Phase C voltage", device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, subkey="voltage", ), ), @@ -750,7 +748,7 @@ SENSORS: dict[str, tuple[TuyaSensorEntityDescription, ...]] = { key=DPCode.PHASE_A, name="Phase A current", device_class=SensorDeviceClass.CURRENT, - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, state_class=SensorStateClass.MEASUREMENT, subkey="electriccurrent", ), @@ -759,7 +757,7 @@ SENSORS: dict[str, tuple[TuyaSensorEntityDescription, ...]] = { name="Phase A power", device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=POWER_KILO_WATT, + native_unit_of_measurement=UnitOfPower.KILO_WATT, subkey="power", ), TuyaSensorEntityDescription( @@ -767,14 +765,14 @@ SENSORS: dict[str, tuple[TuyaSensorEntityDescription, ...]] = { name="Phase A voltage", device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, subkey="voltage", ), TuyaSensorEntityDescription( key=DPCode.PHASE_B, name="Phase B current", device_class=SensorDeviceClass.CURRENT, - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, state_class=SensorStateClass.MEASUREMENT, subkey="electriccurrent", ), @@ -783,7 +781,7 @@ SENSORS: dict[str, tuple[TuyaSensorEntityDescription, ...]] = { name="Phase B power", device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=POWER_KILO_WATT, + native_unit_of_measurement=UnitOfPower.KILO_WATT, subkey="power", ), TuyaSensorEntityDescription( @@ -791,14 +789,14 @@ SENSORS: dict[str, tuple[TuyaSensorEntityDescription, ...]] = { name="Phase B voltage", device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, subkey="voltage", ), TuyaSensorEntityDescription( key=DPCode.PHASE_C, name="Phase C current", device_class=SensorDeviceClass.CURRENT, - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, state_class=SensorStateClass.MEASUREMENT, subkey="electriccurrent", ), @@ -807,7 +805,7 @@ SENSORS: dict[str, tuple[TuyaSensorEntityDescription, ...]] = { name="Phase C power", device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=POWER_KILO_WATT, + native_unit_of_measurement=UnitOfPower.KILO_WATT, subkey="power", ), TuyaSensorEntityDescription( @@ -815,7 +813,7 @@ SENSORS: dict[str, tuple[TuyaSensorEntityDescription, ...]] = { name="Phase C voltage", device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, subkey="voltage", ), ), @@ -973,7 +971,7 @@ SENSORS: dict[str, tuple[TuyaSensorEntityDescription, ...]] = { key=DPCode.AIR_QUALITY, name="Air quality", icon="mdi:air-filter", - device_class=TuyaDeviceClass.AIR_QUALITY, + translation_key="air_quality", ), ), # Fan diff --git a/homeassistant/components/tuya/strings.json b/homeassistant/components/tuya/strings.json index 0bb59615e6e..534ff1dc9ec 100644 --- a/homeassistant/components/tuya/strings.json +++ b/homeassistant/components/tuya/strings.json @@ -16,5 +16,201 @@ "invalid_auth": "[%key:common::config_flow::error::invalid_auth%]", "login_error": "Login error ({code}): {msg}" } + }, + "entity": { + "select": { + "basic_anti_flicker": { + "state": { + "0": "Disabled", + "1": "50 Hz", + "2": "60 Hz" + } + }, + "basic_nightvision": { + "state": { + "0": "Automatic", + "1": "[%key:common::state::off%]", + "2": "[%key:common::state::on%]" + } + }, + "decibel_sensitivity": { + "state": { + "0": "Low sensitivity", + "1": "High sensitivity" + } + }, + "ipc_work_mode": { + "state": { + "0": "Low power mode", + "1": "Continuous working mode" + } + }, + "led_type": { + "state": { + "halogen": "Halogen", + "incandescent": "Incandescent", + "led": "LED" + } + }, + "light_mode": { + "state": { + "none": "[%key:common::state::off%]", + "pos": "Indicate switch location", + "relay": "Indicate switch on/off state" + } + }, + "motion_sensitivity": { + "state": { + "0": "Low sensitivity", + "1": "Medium sensitivity", + "2": "High sensitivity" + } + }, + "record_mode": { + "state": { + "1": "Record events only", + "2": "Continuous recording" + } + }, + "relay_status": { + "state": { + "last": "Remember last state", + "memory": "Remember last state", + "off": "[%key:common::state::off%]", + "on": "[%key:common::state::on%]", + "power_off": "[%key:common::state::off%]", + "power_on": "[%key:common::state::on%]" + } + }, + "fingerbot_mode": { + "state": { + "click": "Push", + "switch": "Switch" + } + }, + "vacuum_cistern": { + "state": { + "low": "Low", + "middle": "Middle", + "high": "High", + "closed": "Closed" + } + }, + "vacuum_collection": { + "state": { + "small": "Small", + "middle": "Middle", + "large": "Large" + } + }, + "vacuum_mode": { + "state": { + "standby": "Standby", + "random": "Random", + "smart": "Smart", + "wall_follow": "Follow Wall", + "mop": "Mop", + "spiral": "Spiral", + "left_spiral": "Spiral Left", + "right_spiral": "Spiral Right", + "bow": "Bow", + "left_bow": "Bow Left", + "right_bow": "Bow Right", + "partial_bow": "Bow Partially", + "chargego": "Return to dock", + "single": "Single", + "zone": "Zone", + "pose": "Pose", + "point": "Point", + "part": "Part", + "pick_zone": "Pick Zone" + } + }, + "fan_angle": { + "state": { + "30": "30°", + "60": "60°", + "90": "90°" + } + }, + "curtain_mode": { + "state": { + "morning": "Morning", + "night": "Night" + } + }, + "curtain_motor_mode": { + "state": { + "forward": "Forward", + "back": "Back" + } + }, + "countdown": { + "state": { + "cancel": "Cancel", + "1h": "1 hour", + "2h": "2 hours", + "3h": "3 hours", + "4h": "4 hours", + "5h": "5 hours", + "6h": "6 hours" + } + }, + "humidifier_spray_mode": { + "state": { + "auto": "Auto", + "health": "Health", + "sleep": "Sleep", + "humidity": "Humidity", + "work": "Work" + } + }, + "humidifier_level": { + "state": { + "level_1": "Level 1", + "level_2": "Level 2", + "level_3": "Level 3", + "level_4": "Level 4", + "level_5": "Level 5", + "level_6": "Level 6", + "level_7": "Level 7", + "level_8": "Level 8", + "level_9": "Level 9", + "level_10": "Level 10" + } + }, + "humidifier_moodlighting": { + "state": { + "1": "Mood 1", + "2": "Mood 2", + "3": "Mood 3", + "4": "Mood 4", + "5": "Mood 5" + } + } + }, + "sensor": { + "status": { + "state": { + "boiling_temp": "Boiling temperature", + "cooling": "Cooling", + "heating_temp": "Heating temperature", + "heating": "Heating", + "reserve_1": "Reserve 1", + "reserve_2": "Reserve 2", + "reserve_3": "Reserve 3", + "standby": "Standby", + "warm": "Heat preservation" + } + }, + "air_quality": { + "state": { + "great": "Great", + "mild": "Mild", + "good": "Good", + "severe": "Severe" + } + } + } } } diff --git a/homeassistant/components/tuya/strings.select.json b/homeassistant/components/tuya/strings.select.json deleted file mode 100644 index 2ae5ff14c5b..00000000000 --- a/homeassistant/components/tuya/strings.select.json +++ /dev/null @@ -1,133 +0,0 @@ -{ - "state": { - "tuya__basic_anti_flickr": { - "0": "Disabled", - "1": "50 Hz", - "2": "60 Hz" - }, - "tuya__basic_nightvision": { - "0": "Automatic", - "1": "[%key:common::state::off%]", - "2": "[%key:common::state::on%]" - }, - "tuya__decibel_sensitivity": { - "0": "Low sensitivity", - "1": "High sensitivity" - }, - "tuya__ipc_work_mode": { - "0": "Low power mode", - "1": "Continuous working mode" - }, - "tuya__led_type": { - "halogen": "Halogen", - "incandescent": "Incandescent", - "led": "LED" - }, - "tuya__light_mode": { - "none": "[%key:common::state::off%]", - "pos": "Indicate switch location", - "relay": "Indicate switch on/off state" - }, - "tuya__motion_sensitivity": { - "0": "Low sensitivity", - "1": "Medium sensitivity", - "2": "High sensitivity" - }, - "tuya__record_mode": { - "1": "Record events only", - "2": "Continuous recording" - }, - "tuya__relay_status": { - "last": "Remember last state", - "memory": "Remember last state", - "off": "[%key:common::state::off%]", - "on": "[%key:common::state::on%]", - "power_off": "[%key:common::state::off%]", - "power_on": "[%key:common::state::on%]" - }, - "tuya__fingerbot_mode": { - "click": "Push", - "switch": "Switch" - }, - "tuya__vacuum_cistern": { - "low": "Low", - "middle": "Middle", - "high": "High", - "closed": "Closed" - }, - "tuya__vacuum_collection": { - "small": "Small", - "middle": "Middle", - "large": "Large" - }, - "tuya__vacuum_mode": { - "standby": "Standby", - "random": "Random", - "smart": "Smart", - "wall_follow": "Follow Wall", - "mop": "Mop", - "spiral": "Spiral", - "left_spiral": "Spiral Left", - "right_spiral": "Spiral Right", - "bow": "Bow", - "left_bow": "Bow Left", - "right_bow": "Bow Right", - "partial_bow": "Bow Partially", - "chargego": "Return to dock", - "single": "Single", - "zone": "Zone", - "pose": "Pose", - "point": "Point", - "part": "Part", - "pick_zone": "Pick Zone" - }, - "tuya__fan_angle": { - "30": "30°", - "60": "60°", - "90": "90°" - }, - "tuya__curtain_mode": { - "morning": "Morning", - "night": "Night" - }, - "tuya__curtain_motor_mode": { - "forward": "Forward", - "back": "Back" - }, - "tuya__countdown": { - "cancel": "Cancel", - "1h": "1 hour", - "2h": "2 hours", - "3h": "3 hours", - "4h": "4 hours", - "5h": "5 hours", - "6h": "6 hours" - }, - "tuya__humidifier_spray_mode": { - "auto": "Auto", - "health": "Health", - "sleep": "Sleep", - "humidity": "Humidity", - "work": "Work" - }, - "tuya__humidifier_level": { - "level_1": "Level 1", - "level_2": "Level 2", - "level_3": "Level 3", - "level_4": "Level 4", - "level_5": "Level 5", - "level_6": "Level 6", - "level_7": "Level 7", - "level_8": "Level 8", - "level_9": "Level 9", - "level_10": "Level 10" - }, - "tuya__humidifier_moodlighting": { - "1": "Mood 1", - "2": "Mood 2", - "3": "Mood 3", - "4": "Mood 4", - "5": "Mood 5" - } - } -} diff --git a/homeassistant/components/tuya/strings.sensor.json b/homeassistant/components/tuya/strings.sensor.json deleted file mode 100644 index a11aadba321..00000000000 --- a/homeassistant/components/tuya/strings.sensor.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "state": { - "tuya__status": { - "boiling_temp": "Boiling temperature", - "cooling": "Cooling", - "heating_temp": "Heating temperature", - "heating": "Heating", - "reserve_1": "Reserve 1", - "reserve_2": "Reserve 2", - "reserve_3": "Reserve 3", - "standby": "Standby", - "warm": "Heat preservation" - }, - "tuya__air_quality": { - "great": "Great", - "mild": "Mild", - "good": "Good", - "severe": "Severe" - } - } -} diff --git a/homeassistant/components/tuya/translations/bg.json b/homeassistant/components/tuya/translations/bg.json index 5bd4cc0b745..d1ae2178568 100644 --- a/homeassistant/components/tuya/translations/bg.json +++ b/homeassistant/components/tuya/translations/bg.json @@ -14,5 +14,131 @@ "description": "\u0412\u044a\u0432\u0435\u0434\u0435\u0442\u0435 \u0432\u0430\u0448\u0438\u0442\u0435 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u043e\u043d\u043d\u0438 \u0434\u0430\u043d\u043d\u0438 \u0437\u0430 Tuya" } } + }, + "entity": { + "select": { + "basic_anti_flicker": { + "state": { + "1": "50 Hz", + "2": "60 Hz" + } + }, + "basic_nightvision": { + "state": { + "0": "\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e", + "1": "\u0418\u0437\u043a\u043b.", + "2": "\u0412\u043a\u043b." + } + }, + "countdown": { + "state": { + "1h": "1 \u0447\u0430\u0441", + "2h": "2 \u0447\u0430\u0441\u0430", + "3h": "3 \u0447\u0430\u0441\u0430", + "4h": "4 \u0447\u0430\u0441\u0430", + "5h": "5 \u0447\u0430\u0441\u0430", + "6h": "6 \u0447\u0430\u0441\u0430", + "cancel": "\u041e\u0442\u043a\u0430\u0437" + } + }, + "curtain_mode": { + "state": { + "morning": "\u0421\u0443\u0442\u0440\u0438\u043d", + "night": "\u041d\u043e\u0449\u0435\u043c" + } + }, + "curtain_motor_mode": { + "state": { + "back": "\u041d\u0430\u0437\u0430\u0434", + "forward": "\u041d\u0430\u043f\u0440\u0435\u0434" + } + }, + "decibel_sensitivity": { + "state": { + "0": "\u041d\u0438\u0441\u043a\u0430 \u0447\u0443\u0432\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u043d\u043e\u0441\u0442", + "1": "\u0412\u0438\u0441\u043e\u043a\u0430 \u0447\u0443\u0432\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u043d\u043e\u0441\u0442" + } + }, + "fan_angle": { + "state": { + "30": "30\u00b0", + "60": "60\u00b0", + "90": "90\u00b0" + } + }, + "humidifier_level": { + "state": { + "level_1": "\u041d\u0438\u0432\u043e 1", + "level_10": "\u041d\u0438\u0432\u043e 10", + "level_2": "\u041d\u0438\u0432\u043e 2", + "level_3": "\u041d\u0438\u0432\u043e 3", + "level_4": "\u041d\u0438\u0432\u043e 4", + "level_5": "\u041d\u0438\u0432\u043e 5", + "level_6": "\u041d\u0438\u0432\u043e 6", + "level_7": "\u041d\u0438\u0432\u043e 7", + "level_8": "\u041d\u0438\u0432\u043e 8", + "level_9": "\u041d\u0438\u0432\u043e 9" + } + }, + "humidifier_spray_mode": { + "state": { + "auto": "\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e", + "humidity": "\u0412\u043b\u0430\u0436\u043d\u043e\u0441\u0442" + } + }, + "led_type": { + "state": { + "led": "LED" + } + }, + "light_mode": { + "state": { + "none": "\u0418\u0437\u043a\u043b." + } + }, + "motion_sensitivity": { + "state": { + "0": "\u041d\u0438\u0441\u043a\u0430 \u0447\u0443\u0432\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u043d\u043e\u0441\u0442", + "1": "\u0421\u0440\u0435\u0434\u043d\u0430 \u0447\u0443\u0432\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u043d\u043e\u0441\u0442", + "2": "\u0412\u0438\u0441\u043e\u043a\u0430 \u0447\u0443\u0432\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u043d\u043e\u0441\u0442" + } + }, + "record_mode": { + "state": { + "1": "\u0421\u0430\u043c\u043e \u0437\u0430\u043f\u0438\u0441 \u043d\u0430 \u0441\u044a\u0431\u0438\u0442\u0438\u044f", + "2": "\u041d\u0435\u043f\u0440\u0435\u043a\u044a\u0441\u043d\u0430\u0442 \u0437\u0430\u043f\u0438\u0441" + } + }, + "relay_status": { + "state": { + "last": "\u0417\u0430\u043f\u043e\u043c\u043d\u044f\u043d\u0435 \u043d\u0430 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u043e\u0442\u043e \u0441\u044a\u0441\u0442\u043e\u044f\u043d\u0438\u0435", + "memory": "\u0417\u0430\u043f\u043e\u043c\u043d\u044f\u043d\u0435 \u043d\u0430 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u043e\u0442\u043e \u0441\u044a\u0441\u0442\u043e\u044f\u043d\u0438\u0435", + "off": "\u0418\u0437\u043a\u043b.", + "on": "\u0412\u043a\u043b.", + "power_off": "\u0418\u0437\u043a\u043b.", + "power_on": "\u0412\u043a\u043b." + } + }, + "vacuum_mode": { + "state": { + "part": "\u0427\u0430\u0441\u0442", + "point": "\u0422\u043e\u0447\u043a\u0430", + "pose": "\u041f\u043e\u0437\u0430", + "standby": "\u0412 \u0433\u043e\u0442\u043e\u0432\u043d\u043e\u0441\u0442", + "zone": "\u0417\u043e\u043d\u0430" + } + } + }, + "sensor": { + "status": { + "state": { + "boiling_temp": "\u0422\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430 \u043d\u0430 \u043a\u0438\u043f\u0435\u043d\u0435", + "cooling": "\u041e\u0445\u043b\u0430\u0436\u0434\u0430\u043d\u0435", + "heating": "\u041e\u0442\u043e\u043f\u043b\u0435\u043d\u0438\u0435", + "heating_temp": "\u0422\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430 \u043d\u0430 \u043e\u0442\u043e\u043f\u043b\u0435\u043d\u0438\u0435", + "standby": "\u0412 \u0433\u043e\u0442\u043e\u0432\u043d\u043e\u0441\u0442" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tuya/translations/ca.json b/homeassistant/components/tuya/translations/ca.json index 52ef20e69b9..14e3867a015 100644 --- a/homeassistant/components/tuya/translations/ca.json +++ b/homeassistant/components/tuya/translations/ca.json @@ -16,5 +16,201 @@ "description": "Introdueix les teves credencial de Tuya" } } + }, + "entity": { + "select": { + "basic_anti_flicker": { + "state": { + "0": "Desactivat", + "1": "50 Hz", + "2": "60 Hz" + } + }, + "basic_nightvision": { + "state": { + "0": "Autom\u00e0tic", + "1": "OFF", + "2": "ON" + } + }, + "countdown": { + "state": { + "1h": "1 hora", + "2h": "2 hores", + "3h": "3 hores", + "4h": "4 hores", + "5h": "5 hores", + "6h": "6 hores", + "cancel": "Cancel\u00b7la" + } + }, + "curtain_mode": { + "state": { + "morning": "Mat\u00ed", + "night": "Nit" + } + }, + "curtain_motor_mode": { + "state": { + "back": "Enrere", + "forward": "Endavant" + } + }, + "decibel_sensitivity": { + "state": { + "0": "Sensibilitat baixa", + "1": "Sensibilitat alta" + } + }, + "fan_angle": { + "state": { + "30": "30\u00b0", + "60": "60\u00b0", + "90": "90\u00b0" + } + }, + "fingerbot_mode": { + "state": { + "click": "Prem", + "switch": "Interruptor" + } + }, + "humidifier_level": { + "state": { + "level_1": "Nivell 1", + "level_10": "Nivell 10", + "level_2": "Nivell 2", + "level_3": "Nivell 3", + "level_4": "Nivell 4", + "level_5": "Nivell 5", + "level_6": "Nivell 6", + "level_7": "Nivell 7", + "level_8": "Nivell 8", + "level_9": "Nivell 9" + } + }, + "humidifier_moodlighting": { + "state": { + "1": "Estat 1", + "2": "Estat 2", + "3": "Estat 3", + "4": "Estat 4", + "5": "Estat 5" + } + }, + "humidifier_spray_mode": { + "state": { + "auto": "Autom\u00e0tic", + "health": "Salut", + "humidity": "Humitat", + "sleep": "Dormint", + "work": "Feina" + } + }, + "ipc_work_mode": { + "state": { + "0": "Mode de baix consum", + "1": "Mode de funcionament continu" + } + }, + "led_type": { + "state": { + "halogen": "Halogen", + "incandescent": "Incandescent", + "led": "LED" + } + }, + "light_mode": { + "state": { + "none": "OFF", + "pos": "Indica la ubicaci\u00f3 de l'interruptor", + "relay": "Indica l'estat, ON/OFF, de l'interruptor" + } + }, + "motion_sensitivity": { + "state": { + "0": "Sensibilitat baixa", + "1": "Sensibilitat mitjana", + "2": "Sensibilitat alta" + } + }, + "record_mode": { + "state": { + "1": "Nom\u00e9s enregistra esdeveniments", + "2": "Enregistrament continu" + } + }, + "relay_status": { + "state": { + "last": "Recorda l'\u00faltim estat", + "memory": "Recorda l'\u00faltim estat", + "off": "OFF", + "on": "ON", + "power_off": "OFF", + "power_on": "ON" + } + }, + "vacuum_cistern": { + "state": { + "closed": "Tancada", + "high": "Alt", + "low": "Baix", + "middle": "Mitj\u00e0" + } + }, + "vacuum_collection": { + "state": { + "large": "Gran", + "middle": "Mitj\u00e0", + "small": "Petit" + } + }, + "vacuum_mode": { + "state": { + "bow": "Arc", + "chargego": "Retorna a la base", + "left_bow": "Arc a esquerra", + "left_spiral": "Espiral a esquerra", + "mop": "Frega", + "part": "Surt", + "partial_bow": "Arc parcial", + "pick_zone": "Tria zona", + "point": "Punt", + "pose": "Posa", + "random": "Aleatori", + "right_bow": "Arc a dreta", + "right_spiral": "Espiral a dreta", + "single": "Individual", + "smart": "Intel\u00b7ligent", + "spiral": "Espiral", + "standby": "En espera", + "wall_follow": "Segueix paret", + "zone": "Zona" + } + } + }, + "sensor": { + "air_quality": { + "state": { + "good": "Bo", + "great": "Genial", + "mild": "Mitj\u00e0", + "severe": "Sever" + } + }, + "status": { + "state": { + "boiling_temp": "Temperatura d'ebullici\u00f3", + "cooling": "Refredant", + "heating": "Escalfant", + "heating_temp": "Temperatura d'escalfament", + "reserve_1": "Reserva 1", + "reserve_2": "Reserva 2", + "reserve_3": "Reserva 3", + "standby": "En espera", + "warm": "Conservaci\u00f3 de calor" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tuya/translations/cs.json b/homeassistant/components/tuya/translations/cs.json index 0364baac560..f58ac158665 100644 --- a/homeassistant/components/tuya/translations/cs.json +++ b/homeassistant/components/tuya/translations/cs.json @@ -13,5 +13,28 @@ "description": "Zadejte sv\u00e9 p\u0159ihla\u0161ovac\u00ed \u00fadaje k Tuya." } } + }, + "entity": { + "select": { + "basic_nightvision": { + "state": { + "1": "Vypnuto", + "2": "Zapnuto" + } + }, + "light_mode": { + "state": { + "none": "Vypnuto" + } + }, + "relay_status": { + "state": { + "off": "Vypnuto", + "on": "Zapnuto", + "power_off": "Vypnuto", + "power_on": "Zapnuto" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tuya/translations/de.json b/homeassistant/components/tuya/translations/de.json index 492159d4d6c..16767ee1931 100644 --- a/homeassistant/components/tuya/translations/de.json +++ b/homeassistant/components/tuya/translations/de.json @@ -16,5 +16,201 @@ "description": "Gib deine Tuya-Anmeldeinformationen ein." } } + }, + "entity": { + "select": { + "basic_anti_flicker": { + "state": { + "0": "Deaktiviert", + "1": "50 Hz", + "2": "60 Hz" + } + }, + "basic_nightvision": { + "state": { + "0": "Automatisch", + "1": "Aus", + "2": "An" + } + }, + "countdown": { + "state": { + "1h": "1 Stunde", + "2h": "2 Stunden", + "3h": "3 Stunden", + "4h": "4 Stunden", + "5h": "5 Stunden", + "6h": "6 Stunden", + "cancel": "Abbrechen" + } + }, + "curtain_mode": { + "state": { + "morning": "Morgen", + "night": "Nacht" + } + }, + "curtain_motor_mode": { + "state": { + "back": "Zur\u00fcck", + "forward": "Vorw\u00e4rts" + } + }, + "decibel_sensitivity": { + "state": { + "0": "Geringe Empfindlichkeit", + "1": "Hohe Empfindlichkeit" + } + }, + "fan_angle": { + "state": { + "30": "30\u00b0", + "60": "60\u00b0", + "90": "90\u00b0" + } + }, + "fingerbot_mode": { + "state": { + "click": "Dr\u00fccken", + "switch": "Schalter" + } + }, + "humidifier_level": { + "state": { + "level_1": "Stufe 1", + "level_10": "Stufe 10", + "level_2": "Stufe 2", + "level_3": "Stufe 3", + "level_4": "Stufe 4", + "level_5": "Stufe 5", + "level_6": "Stufe 6", + "level_7": "Stufe 7", + "level_8": "Stufe 8", + "level_9": "Stufe 9" + } + }, + "humidifier_moodlighting": { + "state": { + "1": "Stimmung 1", + "2": "Stimmung 2", + "3": "Stimmung 3", + "4": "Stimmung 4", + "5": "Stimmung 5" + } + }, + "humidifier_spray_mode": { + "state": { + "auto": "Automatisch", + "health": "Gesundheit", + "humidity": "Luftfeuchtigkeit", + "sleep": "Schlafen", + "work": "Arbeit" + } + }, + "ipc_work_mode": { + "state": { + "0": "Energiesparmodus", + "1": "Kontinuierlicher Arbeitsmodus" + } + }, + "led_type": { + "state": { + "halogen": "Halogen", + "incandescent": "Gl\u00fchlampe", + "led": "LED" + } + }, + "light_mode": { + "state": { + "none": "An", + "pos": "Schalterposition anzeigen", + "relay": "Ein-/Ausschaltzustand anzeigen" + } + }, + "motion_sensitivity": { + "state": { + "0": "Geringe Empfindlichkeit", + "1": "Mittlere Empfindlichkeit", + "2": "Hohe Empfindlichkeit" + } + }, + "record_mode": { + "state": { + "1": "Nur Ereignisse aufzeichnen", + "2": "Kontinuierliche Aufnahme" + } + }, + "relay_status": { + "state": { + "last": "Letzten Zustand merken", + "memory": "Letzten Zustand merken", + "off": "An", + "on": "An", + "power_off": "An", + "power_on": "An" + } + }, + "vacuum_cistern": { + "state": { + "closed": "Geschlossen", + "high": "Hoch", + "low": "Niedrig", + "middle": "Mittel" + } + }, + "vacuum_collection": { + "state": { + "large": "Gro\u00df", + "middle": "Mittel", + "small": "Klein" + } + }, + "vacuum_mode": { + "state": { + "bow": "Bogen", + "chargego": "Zur\u00fcck zur Dockingstation", + "left_bow": "Bogen links", + "left_spiral": "Spirale links", + "mop": "Mopp", + "part": "Teil", + "partial_bow": "Bogen Teilweise", + "pick_zone": "Zone ausw\u00e4hlen", + "point": "Punkt", + "pose": "Pose", + "random": "Zuf\u00e4llig", + "right_bow": "Bogen rechts", + "right_spiral": "Spirale rechts", + "single": "Einzeln", + "smart": "Smart", + "spiral": "Spirale", + "standby": "Bereitschaft", + "wall_follow": "Wand folgen", + "zone": "Zone" + } + } + }, + "sensor": { + "air_quality": { + "state": { + "good": "Gut", + "great": "Gro\u00dfartig", + "mild": "Mild", + "severe": "Stark" + } + }, + "status": { + "state": { + "boiling_temp": "Siedetemperatur", + "cooling": "K\u00fchlen", + "heating": "Heizen", + "heating_temp": "Heiztemperatur", + "reserve_1": "Reserve 1", + "reserve_2": "Reserve 2", + "reserve_3": "Reserve 3", + "standby": "Bereitschaft", + "warm": "W\u00e4rmeerhalt" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tuya/translations/el.json b/homeassistant/components/tuya/translations/el.json index 7bbf8790316..f3384bf47b4 100644 --- a/homeassistant/components/tuya/translations/el.json +++ b/homeassistant/components/tuya/translations/el.json @@ -16,5 +16,201 @@ "description": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b1 \u03b4\u03b9\u03b1\u03c0\u03b9\u03c3\u03c4\u03b5\u03c5\u03c4\u03ae\u03c1\u03b9\u03ac \u03c3\u03b1\u03c2 Tuya" } } + }, + "entity": { + "select": { + "basic_anti_flicker": { + "state": { + "0": "\u0391\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03bf", + "1": "50 Hz", + "2": "60 Hz" + } + }, + "basic_nightvision": { + "state": { + "0": "\u0391\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03bf", + "1": "\u0391\u03bd\u03b5\u03bd\u03b5\u03c1\u03b3\u03cc", + "2": "\u0395\u03bd\u03b5\u03c1\u03b3\u03cc" + } + }, + "countdown": { + "state": { + "1h": "1 \u03ce\u03c1\u03b1", + "2h": "2 \u03ce\u03c1\u03b5\u03c2", + "3h": "3 \u03ce\u03c1\u03b5\u03c2", + "4h": "4 \u03ce\u03c1\u03b5\u03c2", + "5h": "5 \u03ce\u03c1\u03b5\u03c2", + "6h": "6 \u03ce\u03c1\u03b5\u03c2", + "cancel": "\u0391\u03ba\u03cd\u03c1\u03c9\u03c3\u03b7" + } + }, + "curtain_mode": { + "state": { + "morning": "\u03a0\u03c1\u03c9\u03af", + "night": "\u039d\u03cd\u03c7\u03c4\u03b1" + } + }, + "curtain_motor_mode": { + "state": { + "back": "\u03a0\u03af\u03c3\u03c9", + "forward": "\u0395\u03bc\u03c0\u03c1\u03cc\u03c2" + } + }, + "decibel_sensitivity": { + "state": { + "0": "\u03a7\u03b1\u03bc\u03b7\u03bb\u03ae \u03b5\u03c5\u03b1\u03b9\u03c3\u03b8\u03b7\u03c3\u03af\u03b1", + "1": "\u03a5\u03c8\u03b7\u03bb\u03ae \u03b5\u03c5\u03b1\u03b9\u03c3\u03b8\u03b7\u03c3\u03af\u03b1" + } + }, + "fan_angle": { + "state": { + "30": "30\u00b0", + "60": "60\u00b0", + "90": "90\u00b0" + } + }, + "fingerbot_mode": { + "state": { + "click": "\u03a0\u03af\u03b5\u03c3\u03b5", + "switch": "\u0394\u03b9\u03b1\u03ba\u03cc\u03c0\u03c4\u03b7\u03c2" + } + }, + "humidifier_level": { + "state": { + "level_1": "\u0395\u03c0\u03af\u03c0\u03b5\u03b4\u03bf 1", + "level_10": "\u0395\u03c0\u03af\u03c0\u03b5\u03b4\u03bf 10", + "level_2": "\u0395\u03c0\u03af\u03c0\u03b5\u03b4\u03bf 2", + "level_3": "\u0395\u03c0\u03af\u03c0\u03b5\u03b4\u03bf 3", + "level_4": "\u0395\u03c0\u03af\u03c0\u03b5\u03b4\u03bf 4", + "level_5": "\u0395\u03c0\u03af\u03c0\u03b5\u03b4\u03bf 5", + "level_6": "\u0395\u03c0\u03af\u03c0\u03b5\u03b4\u03bf 6", + "level_7": "\u0395\u03c0\u03af\u03c0\u03b5\u03b4\u03bf 7", + "level_8": "\u0395\u03c0\u03af\u03c0\u03b5\u03b4\u03bf 8", + "level_9": "\u0395\u03c0\u03af\u03c0\u03b5\u03b4\u03bf 9" + } + }, + "humidifier_moodlighting": { + "state": { + "1": "\u0394\u03b9\u03ac\u03b8\u03b5\u03c3\u03b7 1", + "2": "\u0394\u03b9\u03ac\u03b8\u03b5\u03c3\u03b7 2", + "3": "\u0394\u03b9\u03ac\u03b8\u03b5\u03c3\u03b7 3", + "4": "\u0394\u03b9\u03ac\u03b8\u03b5\u03c3\u03b7 4", + "5": "\u0394\u03b9\u03ac\u03b8\u03b5\u03c3\u03b7 5" + } + }, + "humidifier_spray_mode": { + "state": { + "auto": "\u0391\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03bf", + "health": "\u03a5\u03b3\u03b5\u03af\u03b1", + "humidity": "\u03a5\u03b3\u03c1\u03b1\u03c3\u03af\u03b1", + "sleep": "\u038e\u03c0\u03bd\u03bf\u03c2", + "work": "\u0395\u03c1\u03b3\u03b1\u03c3\u03af\u03b1" + } + }, + "ipc_work_mode": { + "state": { + "0": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03c7\u03b1\u03bc\u03b7\u03bb\u03ae\u03c2 \u03b9\u03c3\u03c7\u03cd\u03bf\u03c2", + "1": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03c3\u03c5\u03bd\u03b5\u03c7\u03bf\u03cd\u03c2 \u03b5\u03c1\u03b3\u03b1\u03c3\u03af\u03b1\u03c2" + } + }, + "led_type": { + "state": { + "halogen": "\u0391\u03bb\u03bf\u03b3\u03cc\u03bd\u03bf\u03c5", + "incandescent": "\u03a0\u03c5\u03c1\u03b1\u03ba\u03c4\u03ce\u03c3\u03b5\u03c9\u03c2", + "led": "LED" + } + }, + "light_mode": { + "state": { + "none": "\u0391\u03bd\u03b5\u03bd\u03b5\u03c1\u03b3\u03cc", + "pos": "\u03a5\u03c0\u03bf\u03b4\u03b5\u03af\u03be\u03c4\u03b5 \u03c4\u03b7 \u03b8\u03ad\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03b4\u03b9\u03b1\u03ba\u03cc\u03c0\u03c4\u03b7", + "relay": "\u0388\u03bd\u03b4\u03b5\u03b9\u03be\u03b7 \u03c4\u03b7\u03c2 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7\u03c2/\u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 \u03b4\u03b9\u03b1\u03ba\u03cc\u03c0\u03c4\u03b7" + } + }, + "motion_sensitivity": { + "state": { + "0": "\u03a7\u03b1\u03bc\u03b7\u03bb\u03ae \u03b5\u03c5\u03b1\u03b9\u03c3\u03b8\u03b7\u03c3\u03af\u03b1", + "1": "\u039c\u03b5\u03c3\u03b1\u03af\u03b1 \u03b5\u03c5\u03b1\u03b9\u03c3\u03b8\u03b7\u03c3\u03af\u03b1", + "2": "\u03a5\u03c8\u03b7\u03bb\u03ae \u03b5\u03c5\u03b1\u03b9\u03c3\u03b8\u03b7\u03c3\u03af\u03b1" + } + }, + "record_mode": { + "state": { + "1": "\u039a\u03b1\u03c4\u03b1\u03b3\u03c1\u03b1\u03c6\u03ae \u03c3\u03c5\u03bc\u03b2\u03ac\u03bd\u03c4\u03c9\u03bd \u03bc\u03cc\u03bd\u03bf", + "2": "\u03a3\u03c5\u03bd\u03b5\u03c7\u03ae\u03c2 \u03ba\u03b1\u03c4\u03b1\u03b3\u03c1\u03b1\u03c6\u03ae" + } + }, + "relay_status": { + "state": { + "last": "\u03a5\u03c0\u03b5\u03bd\u03b8\u03cd\u03bc\u03b9\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03c4\u03b5\u03bb\u03b5\u03c5\u03c4\u03b1\u03af\u03b1\u03c2 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2", + "memory": "\u03a5\u03c0\u03b5\u03bd\u03b8\u03cd\u03bc\u03b9\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03c4\u03b5\u03bb\u03b5\u03c5\u03c4\u03b1\u03af\u03b1\u03c2 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2", + "off": "\u0391\u03bd\u03b5\u03bd\u03b5\u03c1\u03b3\u03cc", + "on": "\u0395\u03bd\u03b5\u03c1\u03b3\u03cc", + "power_off": "\u0391\u03bd\u03b5\u03bd\u03b5\u03c1\u03b3\u03cc", + "power_on": "\u0395\u03bd\u03b5\u03c1\u03b3\u03cc" + } + }, + "vacuum_cistern": { + "state": { + "closed": "\u039a\u03bb\u03b5\u03b9\u03c3\u03c4\u03cc", + "high": "\u03a5\u03c8\u03b7\u03bb\u03cc", + "low": "\u03a7\u03b1\u03bc\u03b7\u03bb\u03cc", + "middle": "\u039c\u03b5\u03c3\u03b1\u03af\u03bf" + } + }, + "vacuum_collection": { + "state": { + "large": "\u039c\u03b5\u03b3\u03ac\u03bb\u03bf", + "middle": "\u039c\u03b5\u03c3\u03b1\u03af\u03bf", + "small": "\u039c\u03b9\u03ba\u03c1\u03cc" + } + }, + "vacuum_mode": { + "state": { + "bow": "\u03a4\u03cc\u03be\u03bf", + "chargego": "\u0395\u03c0\u03b9\u03c3\u03c4\u03c1\u03bf\u03c6\u03ae \u03c3\u03c4\u03b7 \u03b2\u03ac\u03c3\u03b7", + "left_bow": "\u03a4\u03cc\u03be\u03bf \u03b1\u03c1\u03b9\u03c3\u03c4\u03b5\u03c1\u03ac", + "left_spiral": "\u03a3\u03c0\u03b9\u03c1\u03ac\u03bb \u0391\u03c1\u03b9\u03c3\u03c4\u03b5\u03c1\u03ac", + "mop": "\u03a3\u03c6\u03bf\u03c5\u03b3\u03b3\u03ac\u03c1\u03b9\u03c3\u03bc\u03b1", + "part": "\u039c\u03ad\u03c1\u03bf\u03c2", + "partial_bow": "\u03a4\u03cc\u03be\u03bf \u03b5\u03bd \u03bc\u03ad\u03c1\u03b5\u03b9", + "pick_zone": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u0396\u03ce\u03bd\u03b7\u03c2", + "point": "\u03a3\u03b7\u03bc\u03b5\u03af\u03bf", + "pose": "\u03a3\u03c4\u03ac\u03c3\u03b7", + "random": "\u03a4\u03c5\u03c7\u03b1\u03af\u03bf", + "right_bow": "\u03a4\u03cc\u03be\u03bf \u0394\u03b5\u03be\u03b9\u03ac", + "right_spiral": "\u03a3\u03c0\u03b9\u03c1\u03ac\u03bb \u0394\u03b5\u03be\u03b9\u03ac", + "single": "\u039c\u03bf\u03bd\u03cc", + "smart": "\u0388\u03be\u03c5\u03c0\u03bd\u03bf", + "spiral": "\u03a3\u03c0\u03b9\u03c1\u03ac\u03bb", + "standby": "\u0391\u03bd\u03b1\u03bc\u03bf\u03bd\u03ae", + "wall_follow": "\u0391\u03ba\u03bf\u03bb\u03bf\u03cd\u03b8\u03b7\u03c3\u03b5 \u03c4\u03bf\u03bd \u03c4\u03bf\u03af\u03c7\u03bf", + "zone": "\u0396\u03ce\u03bd\u03b7" + } + } + }, + "sensor": { + "air_quality": { + "state": { + "good": "\u039a\u03b1\u03bb\u03ae", + "great": "\u0395\u03be\u03b1\u03b9\u03c1\u03b5\u03c4\u03b9\u03ba\u03ae", + "mild": "\u0389\u03c0\u03b9\u03b1", + "severe": "\u03a3\u03bf\u03b2\u03b1\u03c1\u03ae" + } + }, + "status": { + "state": { + "boiling_temp": "\u0398\u03b5\u03c1\u03bc\u03bf\u03ba\u03c1\u03b1\u03c3\u03af\u03b1 \u03b2\u03c1\u03b1\u03c3\u03bc\u03bf\u03cd", + "cooling": "\u03a8\u03cd\u03be\u03b7", + "heating": "\u0398\u03ad\u03c1\u03bc\u03b1\u03bd\u03c3\u03b7", + "heating_temp": "\u0398\u03b5\u03c1\u03bc\u03bf\u03ba\u03c1\u03b1\u03c3\u03af\u03b1 \u03b8\u03ad\u03c1\u03bc\u03b1\u03bd\u03c3\u03b7\u03c2", + "reserve_1": "\u039a\u03c1\u03ac\u03c4\u03b7\u03c3\u03b7 1", + "reserve_2": "\u039a\u03c1\u03ac\u03c4\u03b7\u03c3\u03b7 2", + "reserve_3": "\u039a\u03c1\u03ac\u03c4\u03b7\u03c3\u03b7 3", + "standby": "\u0391\u03bd\u03b1\u03bc\u03bf\u03bd\u03ae", + "warm": "\u0394\u03b9\u03b1\u03c4\u03ae\u03c1\u03b7\u03c3\u03b7 \u03b8\u03b5\u03c1\u03bc\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tuya/translations/en.json b/homeassistant/components/tuya/translations/en.json index e69872fd309..7ef7edac51f 100644 --- a/homeassistant/components/tuya/translations/en.json +++ b/homeassistant/components/tuya/translations/en.json @@ -16,5 +16,201 @@ "description": "Enter your Tuya credentials" } } + }, + "entity": { + "select": { + "basic_anti_flicker": { + "state": { + "0": "Disabled", + "1": "50 Hz", + "2": "60 Hz" + } + }, + "basic_nightvision": { + "state": { + "0": "Automatic", + "1": "Off", + "2": "On" + } + }, + "countdown": { + "state": { + "1h": "1 hour", + "2h": "2 hours", + "3h": "3 hours", + "4h": "4 hours", + "5h": "5 hours", + "6h": "6 hours", + "cancel": "Cancel" + } + }, + "curtain_mode": { + "state": { + "morning": "Morning", + "night": "Night" + } + }, + "curtain_motor_mode": { + "state": { + "back": "Back", + "forward": "Forward" + } + }, + "decibel_sensitivity": { + "state": { + "0": "Low sensitivity", + "1": "High sensitivity" + } + }, + "fan_angle": { + "state": { + "30": "30\u00b0", + "60": "60\u00b0", + "90": "90\u00b0" + } + }, + "fingerbot_mode": { + "state": { + "click": "Push", + "switch": "Switch" + } + }, + "humidifier_level": { + "state": { + "level_1": "Level 1", + "level_10": "Level 10", + "level_2": "Level 2", + "level_3": "Level 3", + "level_4": "Level 4", + "level_5": "Level 5", + "level_6": "Level 6", + "level_7": "Level 7", + "level_8": "Level 8", + "level_9": "Level 9" + } + }, + "humidifier_moodlighting": { + "state": { + "1": "Mood 1", + "2": "Mood 2", + "3": "Mood 3", + "4": "Mood 4", + "5": "Mood 5" + } + }, + "humidifier_spray_mode": { + "state": { + "auto": "Auto", + "health": "Health", + "humidity": "Humidity", + "sleep": "Sleep", + "work": "Work" + } + }, + "ipc_work_mode": { + "state": { + "0": "Low power mode", + "1": "Continuous working mode" + } + }, + "led_type": { + "state": { + "halogen": "Halogen", + "incandescent": "Incandescent", + "led": "LED" + } + }, + "light_mode": { + "state": { + "none": "Off", + "pos": "Indicate switch location", + "relay": "Indicate switch on/off state" + } + }, + "motion_sensitivity": { + "state": { + "0": "Low sensitivity", + "1": "Medium sensitivity", + "2": "High sensitivity" + } + }, + "record_mode": { + "state": { + "1": "Record events only", + "2": "Continuous recording" + } + }, + "relay_status": { + "state": { + "last": "Remember last state", + "memory": "Remember last state", + "off": "Off", + "on": "On", + "power_off": "Off", + "power_on": "On" + } + }, + "vacuum_cistern": { + "state": { + "closed": "Closed", + "high": "High", + "low": "Low", + "middle": "Middle" + } + }, + "vacuum_collection": { + "state": { + "large": "Large", + "middle": "Middle", + "small": "Small" + } + }, + "vacuum_mode": { + "state": { + "bow": "Bow", + "chargego": "Return to dock", + "left_bow": "Bow Left", + "left_spiral": "Spiral Left", + "mop": "Mop", + "part": "Part", + "partial_bow": "Bow Partially", + "pick_zone": "Pick Zone", + "point": "Point", + "pose": "Pose", + "random": "Random", + "right_bow": "Bow Right", + "right_spiral": "Spiral Right", + "single": "Single", + "smart": "Smart", + "spiral": "Spiral", + "standby": "Standby", + "wall_follow": "Follow Wall", + "zone": "Zone" + } + } + }, + "sensor": { + "air_quality": { + "state": { + "good": "Good", + "great": "Great", + "mild": "Mild", + "severe": "Severe" + } + }, + "status": { + "state": { + "boiling_temp": "Boiling temperature", + "cooling": "Cooling", + "heating": "Heating", + "heating_temp": "Heating temperature", + "reserve_1": "Reserve 1", + "reserve_2": "Reserve 2", + "reserve_3": "Reserve 3", + "standby": "Standby", + "warm": "Heat preservation" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tuya/translations/es.json b/homeassistant/components/tuya/translations/es.json index 6191d5acf12..70da25cee6b 100644 --- a/homeassistant/components/tuya/translations/es.json +++ b/homeassistant/components/tuya/translations/es.json @@ -16,5 +16,201 @@ "description": "Introduce tus credenciales Tuya" } } + }, + "entity": { + "select": { + "basic_anti_flicker": { + "state": { + "0": "Deshabilitado", + "1": "50 Hz", + "2": "60 Hz" + } + }, + "basic_nightvision": { + "state": { + "0": "Autom\u00e1tico", + "1": "Apagado", + "2": "Encendido" + } + }, + "countdown": { + "state": { + "1h": "1 hora", + "2h": "2 horas", + "3h": "3 horas", + "4h": "4 horas", + "5h": "5 horas", + "6h": "6 horas", + "cancel": "Cancelar" + } + }, + "curtain_mode": { + "state": { + "morning": "Ma\u00f1ana", + "night": "Noche" + } + }, + "curtain_motor_mode": { + "state": { + "back": "Atr\u00e1s", + "forward": "Adelante" + } + }, + "decibel_sensitivity": { + "state": { + "0": "Baja sensibilidad", + "1": "Alta sensibilidad" + } + }, + "fan_angle": { + "state": { + "30": "30\u00b0", + "60": "60\u00b0", + "90": "90\u00b0" + } + }, + "fingerbot_mode": { + "state": { + "click": "Pulsar", + "switch": "Interruptor" + } + }, + "humidifier_level": { + "state": { + "level_1": "Nivel 1", + "level_10": "Nivel 10", + "level_2": "Nivel 2", + "level_3": "Nivel 3", + "level_4": "Nivel 4", + "level_5": "Nivel 5", + "level_6": "Nivel 6", + "level_7": "Nivel 7", + "level_8": "Nivel 8", + "level_9": "Nivel 9" + } + }, + "humidifier_moodlighting": { + "state": { + "1": "Estado de \u00e1nimo 1", + "2": "Estado de \u00e1nimo 2", + "3": "Estado de \u00e1nimo 3", + "4": "Estado de \u00e1nimo 4", + "5": "Estado de \u00e1nimo 5" + } + }, + "humidifier_spray_mode": { + "state": { + "auto": "Autom\u00e1tico", + "health": "Salud", + "humidity": "Humedad", + "sleep": "Dormir", + "work": "Trabajo" + } + }, + "ipc_work_mode": { + "state": { + "0": "Modo de bajo consumo", + "1": "Modo de funcionamiento continuo" + } + }, + "led_type": { + "state": { + "halogen": "Hal\u00f3geno", + "incandescent": "Incandescente", + "led": "LED" + } + }, + "light_mode": { + "state": { + "none": "Apagado", + "pos": "Indicar la ubicaci\u00f3n del interruptor", + "relay": "Indicar el estado de encendido/apagado" + } + }, + "motion_sensitivity": { + "state": { + "0": "Baja sensibilidad", + "1": "Sensibilidad media", + "2": "Alta sensibilidad" + } + }, + "record_mode": { + "state": { + "1": "Grabar solo eventos", + "2": "Grabaci\u00f3n continua" + } + }, + "relay_status": { + "state": { + "last": "Recordar el \u00faltimo estado", + "memory": "Recordar el \u00faltimo estado", + "off": "Apagado", + "on": "Encendido", + "power_off": "Apagado", + "power_on": "Encendido" + } + }, + "vacuum_cistern": { + "state": { + "closed": "Cerrada", + "high": "Alta", + "low": "Baja", + "middle": "Media" + } + }, + "vacuum_collection": { + "state": { + "large": "Grande", + "middle": "Medio", + "small": "Peque\u00f1o" + } + }, + "vacuum_mode": { + "state": { + "bow": "Arco", + "chargego": "Volver a la base", + "left_bow": "Arco a la izquierda", + "left_spiral": "Espiral izquierda", + "mop": "Fregar", + "part": "Parte", + "partial_bow": "Arco parcialmente", + "pick_zone": "Elegir zona", + "point": "Punto", + "pose": "Pose", + "random": "Aleatorio", + "right_bow": "Arco a la derecha", + "right_spiral": "Espiral derecha", + "single": "\u00danico", + "smart": "Inteligente", + "spiral": "Espiral", + "standby": "En espera", + "wall_follow": "Seguir el muro", + "zone": "Zona" + } + } + }, + "sensor": { + "air_quality": { + "state": { + "good": "Bueno", + "great": "Excelente", + "mild": "Moderado", + "severe": "Severo" + } + }, + "status": { + "state": { + "boiling_temp": "Temperatura de ebullici\u00f3n", + "cooling": "Refrigeraci\u00f3n", + "heating": "Calefacci\u00f3n", + "heating_temp": "Temperatura de calefacci\u00f3n", + "reserve_1": "Reserva 1", + "reserve_2": "Reserva 2", + "reserve_3": "Reserva 3", + "standby": "En espera", + "warm": "Conservaci\u00f3n del calor" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tuya/translations/et.json b/homeassistant/components/tuya/translations/et.json index de84ef4aa2f..ae24c76edd4 100644 --- a/homeassistant/components/tuya/translations/et.json +++ b/homeassistant/components/tuya/translations/et.json @@ -16,5 +16,201 @@ "description": "Sisesta oma Tuya konto andmed." } } + }, + "entity": { + "select": { + "basic_anti_flicker": { + "state": { + "0": "Keelatud", + "1": "50 Hz", + "2": "60 Hz" + } + }, + "basic_nightvision": { + "state": { + "0": "Automaatne", + "1": "V\u00e4ljas", + "2": "Sees" + } + }, + "countdown": { + "state": { + "1h": "1 tund", + "2h": "2 tundi", + "3h": "3 tundi", + "4h": "4 tundi", + "5h": "5 tundi", + "6h": "6 tundi", + "cancel": "Loobu" + } + }, + "curtain_mode": { + "state": { + "morning": "Hommik", + "night": "\u00d6\u00f6" + } + }, + "curtain_motor_mode": { + "state": { + "back": "Tagasi", + "forward": "Edasi" + } + }, + "decibel_sensitivity": { + "state": { + "0": "Madal tundlikkus", + "1": "K\u00f5rge tundlikkus" + } + }, + "fan_angle": { + "state": { + "30": "30\u00b0", + "60": "60\u00b0", + "90": "90\u00b0" + } + }, + "fingerbot_mode": { + "state": { + "click": "Vajutus", + "switch": "L\u00fcliti" + } + }, + "humidifier_level": { + "state": { + "level_1": "Tase 1", + "level_10": "Tase 10", + "level_2": "Tase 2", + "level_3": "Tase 3", + "level_4": "Tase 4", + "level_5": "Tase 5", + "level_6": "Tase 6", + "level_7": "Tase 7", + "level_8": "Tase 8", + "level_9": "Tase 9" + } + }, + "humidifier_moodlighting": { + "state": { + "1": "Meeleolu 1", + "2": "Meeleolu 2", + "3": "Meeleolu 3", + "4": "Meeleolu 4", + "5": "Meeleolu 5" + } + }, + "humidifier_spray_mode": { + "state": { + "auto": "Automaatne", + "health": "Tervis", + "humidity": "Niiskus", + "sleep": "Uneaeg", + "work": "T\u00f6\u00f6aeg" + } + }, + "ipc_work_mode": { + "state": { + "0": "Madala energiatarbega re\u017eiim", + "1": "Pidev t\u00f6\u00f6re\u017eiim" + } + }, + "led_type": { + "state": { + "halogen": "Halogeenlamp", + "incandescent": "H\u00f5\u00f5glamp", + "led": "LED" + } + }, + "light_mode": { + "state": { + "none": "V\u00e4ljas", + "pos": "Kuva l\u00fcliti olekut", + "relay": "Kuva l\u00fcliti sees/v\u00e4ljas olekut" + } + }, + "motion_sensitivity": { + "state": { + "0": "Madal tundlikkus", + "1": "Keskmine tundlikkus", + "2": "K\u00f5rge tundlikkus" + } + }, + "record_mode": { + "state": { + "1": "Salvesta ainult s\u00fcndmused", + "2": "Pidev salvestamine" + } + }, + "relay_status": { + "state": { + "last": "J\u00e4ta viimane olek meelde", + "memory": "J\u00e4ta viimane olek meelde", + "off": "V\u00e4ljas", + "on": "Sees", + "power_off": "V\u00e4ljas", + "power_on": "Sees" + } + }, + "vacuum_cistern": { + "state": { + "closed": "Suletud", + "high": "K\u00f5rge", + "low": "Madal", + "middle": "Keskmine" + } + }, + "vacuum_collection": { + "state": { + "large": "Suur", + "middle": "Keskmine", + "small": "V\u00e4ike" + } + }, + "vacuum_mode": { + "state": { + "bow": "Kaar", + "chargego": "Tagasi kaadimisjaama", + "left_bow": "Keera vasakule", + "left_spiral": "Spiraal vasakule", + "mop": "Pese", + "part": "Osaline", + "partial_bow": "Osaline kaar", + "pick_zone": "Vali ala", + "point": "Punkt", + "pose": "Avalda", + "random": "Juhuslik", + "right_bow": "Kaar paremale", + "right_spiral": "Spiraal paremale", + "single": "\u00dchekordne", + "smart": "Nutikas", + "spiral": "Spiraalne", + "standby": "Ootel", + "wall_follow": "J\u00e4rgi seina", + "zone": "Ala" + } + } + }, + "sensor": { + "air_quality": { + "state": { + "good": "Hea", + "great": "Suurep\u00e4rane", + "mild": "Talutav", + "severe": "Ohtlik" + } + }, + "status": { + "state": { + "boiling_temp": "Keemistemperatuur", + "cooling": "Jahutamine", + "heating": "K\u00fcte", + "heating_temp": "K\u00fcttetemperatuur", + "reserve_1": "Reserv 1", + "reserve_2": "Reserv 2", + "reserve_3": "Reserv 3", + "standby": "Ootel", + "warm": "Soojussalvestus" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tuya/translations/he.json b/homeassistant/components/tuya/translations/he.json index 713f8eafc02..bcf8c3c75fe 100644 --- a/homeassistant/components/tuya/translations/he.json +++ b/homeassistant/components/tuya/translations/he.json @@ -16,5 +16,17 @@ "description": "\u05d4\u05d6\u05e0\u05ea \u05d0\u05d9\u05e9\u05d5\u05e8\u05d9 \u05d4-Tuya \u05e9\u05dc\u05da." } } + }, + "entity": { + "select": { + "relay_status": { + "state": { + "off": "\u05db\u05d1\u05d5\u05d9", + "on": "\u05de\u05d5\u05e4\u05e2\u05dc", + "power_off": "\u05db\u05d1\u05d5\u05d9", + "power_on": "\u05de\u05d5\u05e4\u05e2\u05dc" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tuya/translations/hu.json b/homeassistant/components/tuya/translations/hu.json index 37414cbebf9..af869ab6981 100644 --- a/homeassistant/components/tuya/translations/hu.json +++ b/homeassistant/components/tuya/translations/hu.json @@ -16,5 +16,201 @@ "description": "Adja meg Tuya hiteles\u00edt\u0151 adatait." } } + }, + "entity": { + "select": { + "basic_anti_flicker": { + "state": { + "0": "Letiltva", + "1": "50 Hz", + "2": "60 Hz" + } + }, + "basic_nightvision": { + "state": { + "0": "Automatikus", + "1": "Ki", + "2": "Be" + } + }, + "countdown": { + "state": { + "1h": "1 \u00f3ra", + "2h": "2 \u00f3ra", + "3h": "3 \u00f3ra", + "4h": "4 \u00f3ra", + "5h": "5 \u00f3ra", + "6h": "6 \u00f3ra", + "cancel": "M\u00e9gse" + } + }, + "curtain_mode": { + "state": { + "morning": "Reggel", + "night": "\u00c9jszaka" + } + }, + "curtain_motor_mode": { + "state": { + "back": "Vissza", + "forward": "El\u0151re" + } + }, + "decibel_sensitivity": { + "state": { + "0": "Alacsony \u00e9rz\u00e9kenys\u00e9g", + "1": "Magas \u00e9rz\u00e9kenys\u00e9g" + } + }, + "fan_angle": { + "state": { + "30": "30\u00b0", + "60": "60\u00b0", + "90": "90\u00b0" + } + }, + "fingerbot_mode": { + "state": { + "click": "Lenyom\u00e1s", + "switch": "Kapcsol\u00f3" + } + }, + "humidifier_level": { + "state": { + "level_1": "1. szint", + "level_10": "10. szint", + "level_2": "2. szint", + "level_3": "3. szint", + "level_4": "4. szint", + "level_5": "5. szint", + "level_6": "6. szint", + "level_7": "7. szint", + "level_8": "8. szint", + "level_9": "9. szint" + } + }, + "humidifier_moodlighting": { + "state": { + "1": "1. hangulat", + "2": "2. hangulat", + "3": "3. hangulat", + "4": "4. hangulat", + "5": "5. hangulat" + } + }, + "humidifier_spray_mode": { + "state": { + "auto": "Automatikus", + "health": "Eg\u00e9szs\u00e9ges", + "humidity": "P\u00e1r\u00e1s", + "sleep": "Alv\u00e1s", + "work": "Munka" + } + }, + "ipc_work_mode": { + "state": { + "0": "Alacsony fogyaszt\u00e1s\u00fa m\u00f3d", + "1": "Folyamatos \u00fczemm\u00f3d" + } + }, + "led_type": { + "state": { + "halogen": "Halog\u00e9n", + "incandescent": "Izz\u00f3", + "led": "LED" + } + }, + "light_mode": { + "state": { + "none": "Ki", + "pos": "A kapcsol\u00f3 hely\u00e9nek jelz\u00e9se", + "relay": "Be-/kikapcsolt \u00e1llapot jelz\u00e9se" + } + }, + "motion_sensitivity": { + "state": { + "0": "Alacsony \u00e9rz\u00e9kenys\u00e9g", + "1": "K\u00f6zepes \u00e9rz\u00e9kenys\u00e9g", + "2": "Magas \u00e9rz\u00e9kenys\u00e9g" + } + }, + "record_mode": { + "state": { + "1": "Csak esem\u00e9nyek r\u00f6gz\u00edt\u00e9se", + "2": "Folyamatos felv\u00e9tel" + } + }, + "relay_status": { + "state": { + "last": "Utols\u00f3 \u00e1llapot megjegyz\u00e9se", + "memory": "Utols\u00f3 \u00e1llapot megjegyz\u00e9se", + "off": "Ki", + "on": "Be", + "power_off": "Ki", + "power_on": "Be" + } + }, + "vacuum_cistern": { + "state": { + "closed": "Z\u00e1rva", + "high": "Magas", + "low": "Alacsony", + "middle": "K\u00f6zepes" + } + }, + "vacuum_collection": { + "state": { + "large": "Sok", + "middle": "K\u00f6zepes", + "small": "Kev\u00e9s" + } + }, + "vacuum_mode": { + "state": { + "bow": "Bow", + "chargego": "Dokkol\u00e1s", + "left_bow": "Bow balra", + "left_spiral": "Spir\u00e1l balra", + "mop": "Mop", + "part": "R\u00e9sz", + "partial_bow": "Bow R\u00e9szben", + "pick_zone": "Z\u00f3na kiv\u00e1laszt\u00e1s", + "point": "Pont", + "pose": "P\u00f3z", + "random": "V\u00e9letlenszer\u0171", + "right_bow": "Bow Jobbra", + "right_spiral": "Spir\u00e1l jobbra", + "single": "Egyszeri", + "smart": "Okos", + "spiral": "Spir\u00e1l", + "standby": "K\u00e9szenl\u00e9tben", + "wall_follow": "Fal ment\u00e9n", + "zone": "Z\u00f3na" + } + } + }, + "sensor": { + "air_quality": { + "state": { + "good": "J\u00f3", + "great": "Nagyszer\u0171", + "mild": "Enyhe", + "severe": "S\u00falyos" + } + }, + "status": { + "state": { + "boiling_temp": "Forral\u00e1si h\u0151m\u00e9rs\u00e9klet", + "cooling": "H\u0171t\u00e9s", + "heating": "F\u0171t\u00e9s", + "heating_temp": "F\u0171t\u00e9si h\u0151m\u00e9rs\u00e9klet", + "reserve_1": "1. tartal\u00e9k", + "reserve_2": "2. tartal\u00e9k", + "reserve_3": "3. tartal\u00e9k", + "standby": "K\u00e9szenl\u00e9tben", + "warm": "H\u0151megtart\u00e1s" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tuya/translations/id.json b/homeassistant/components/tuya/translations/id.json index bbbd74822eb..1777c17ae1a 100644 --- a/homeassistant/components/tuya/translations/id.json +++ b/homeassistant/components/tuya/translations/id.json @@ -16,5 +16,201 @@ "description": "Masukkan kredensial Tuya Anda." } } + }, + "entity": { + "select": { + "basic_anti_flicker": { + "state": { + "0": "Dinonaktifkan", + "1": "50Hz", + "2": "60Hz" + } + }, + "basic_nightvision": { + "state": { + "0": "Otomatis", + "1": "Mati", + "2": "Nyala" + } + }, + "countdown": { + "state": { + "1h": "1 jam", + "2h": "2 jam", + "3h": "3 jam", + "4h": "4 jam", + "5h": "5 jam", + "6h": "6 jam", + "cancel": "Batalkan" + } + }, + "curtain_mode": { + "state": { + "morning": "Pagi", + "night": "Malam" + } + }, + "curtain_motor_mode": { + "state": { + "back": "Mundur", + "forward": "Maju" + } + }, + "decibel_sensitivity": { + "state": { + "0": "Sensitivitas rendah", + "1": "Sensitivitas tinggi" + } + }, + "fan_angle": { + "state": { + "30": "30\u00b0", + "60": "60\u00b0", + "90": "90\u00b0" + } + }, + "fingerbot_mode": { + "state": { + "click": "Dorong", + "switch": "Sakelar" + } + }, + "humidifier_level": { + "state": { + "level_1": "Tingkat 1", + "level_10": "Tingkat 10", + "level_2": "Tingkat 2", + "level_3": "Tingkat 3", + "level_4": "Tingkat 4", + "level_5": "Tingkat 5", + "level_6": "Tingkat 6", + "level_7": "Tingkat 7", + "level_8": "Tingkat 8", + "level_9": "Tingkat 9" + } + }, + "humidifier_moodlighting": { + "state": { + "1": "Suasana 1", + "2": "Suasana 2", + "3": "Suasana 3", + "4": "Suasana 4", + "5": "Suasana 5" + } + }, + "humidifier_spray_mode": { + "state": { + "auto": "Otomatis", + "health": "Kesehatan", + "humidity": "Kelembaban", + "sleep": "Tidur", + "work": "Bekerja" + } + }, + "ipc_work_mode": { + "state": { + "0": "Mode daya rendah", + "1": "Mode kerja terus menerus" + } + }, + "led_type": { + "state": { + "halogen": "Halogen", + "incandescent": "Pijar", + "led": "LED" + } + }, + "light_mode": { + "state": { + "none": "Mati", + "pos": "Menunjukkan lokasi sakelar", + "relay": "Menunjukkan status sakelar nyala/mati" + } + }, + "motion_sensitivity": { + "state": { + "0": "Sensitivitas rendah", + "1": "Sensitivitas sedang", + "2": "Sensitivitas tinggi" + } + }, + "record_mode": { + "state": { + "1": "Rekam peristiwa saja", + "2": "Perekaman terus menerus" + } + }, + "relay_status": { + "state": { + "last": "Ingat status terakhir", + "memory": "Ingat status terakhir", + "off": "Mati", + "on": "Nyala", + "power_off": "Mati", + "power_on": "Nyala" + } + }, + "vacuum_cistern": { + "state": { + "closed": "Tutup", + "high": "Tinggi", + "low": "Rendah", + "middle": "Tengah" + } + }, + "vacuum_collection": { + "state": { + "large": "Besar", + "middle": "Tengah", + "small": "Kecil" + } + }, + "vacuum_mode": { + "state": { + "bow": "Zig Zag", + "chargego": "Kembali ke dock", + "left_bow": "Zig Zag Kiri", + "left_spiral": "Spiral Kiri", + "mop": "Pel", + "part": "Bagian", + "partial_bow": "Zig Zag Sebagian", + "pick_zone": "Pilih Zona", + "point": "Titik", + "pose": "Area", + "random": "Acak", + "right_bow": "Bungkuk Kanan", + "right_spiral": "Spiral Kanan", + "single": "Tunggal", + "smart": "Cerdas", + "spiral": "Spiral", + "standby": "Siaga", + "wall_follow": "Ikuti Dinding", + "zone": "Zona" + } + } + }, + "sensor": { + "air_quality": { + "state": { + "good": "Bagus", + "great": "Hebat", + "mild": "Ringan", + "severe": "Parah" + } + }, + "status": { + "state": { + "boiling_temp": "Suhu mendidih", + "cooling": "Mendinginkan", + "heating": "Memanaskan", + "heating_temp": "Suhu pemanas", + "reserve_1": "Cadangan 1", + "reserve_2": "Cadangan 2", + "reserve_3": "Cadangan 3", + "standby": "Siaga", + "warm": "Pelestarian panas" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tuya/translations/it.json b/homeassistant/components/tuya/translations/it.json index f8013882fb2..a7844b46b69 100644 --- a/homeassistant/components/tuya/translations/it.json +++ b/homeassistant/components/tuya/translations/it.json @@ -16,5 +16,201 @@ "description": "Inserisci le tue credenziali Tuya" } } + }, + "entity": { + "select": { + "basic_anti_flicker": { + "state": { + "0": "Disabilitato", + "1": "50 Hz", + "2": "60 Hz" + } + }, + "basic_nightvision": { + "state": { + "0": "Automatico", + "1": "Spento", + "2": "Acceso" + } + }, + "countdown": { + "state": { + "1h": "1 ora", + "2h": "2 ore", + "3h": "3 ore", + "4h": "4 ore", + "5h": "5 ore", + "6h": "6 ore", + "cancel": "Annulla" + } + }, + "curtain_mode": { + "state": { + "morning": "Mattina", + "night": "Notte" + } + }, + "curtain_motor_mode": { + "state": { + "back": "Indietro", + "forward": "Avanti" + } + }, + "decibel_sensitivity": { + "state": { + "0": "Bassa sensibilit\u00e0", + "1": "Alta sensibilit\u00e0" + } + }, + "fan_angle": { + "state": { + "30": "30\u00b0", + "60": "60\u00b0", + "90": "90\u00b0" + } + }, + "fingerbot_mode": { + "state": { + "click": "Premi", + "switch": "Commuta" + } + }, + "humidifier_level": { + "state": { + "level_1": "Livello 1", + "level_10": "Livello 10", + "level_2": "Livello 2", + "level_3": "Livello 3", + "level_4": "Livello 4", + "level_5": "Livello 5", + "level_6": "Livello 6", + "level_7": "Livello 7", + "level_8": "Livello 8", + "level_9": "Livello 9" + } + }, + "humidifier_moodlighting": { + "state": { + "1": "Umore 1", + "2": "Umore 2", + "3": "Umore 3", + "4": "Umore 4", + "5": "Umore 5" + } + }, + "humidifier_spray_mode": { + "state": { + "auto": "Auto", + "health": "Salute", + "humidity": "Umidit\u00e0", + "sleep": "Sonno", + "work": "Lavoro" + } + }, + "ipc_work_mode": { + "state": { + "0": "Modalit\u00e0 a basso consumo", + "1": "Modalit\u00e0 di lavoro continuo" + } + }, + "led_type": { + "state": { + "halogen": "Alogena", + "incandescent": "Incandescenza", + "led": "LED" + } + }, + "light_mode": { + "state": { + "none": "Spento", + "pos": "Indicare la posizione dell'interruttore", + "relay": "Indica lo stato di accensione/spegnimento dell'interruttore" + } + }, + "motion_sensitivity": { + "state": { + "0": "Bassa sensibilit\u00e0", + "1": "Sensibilit\u00e0 media", + "2": "Alta sensibilit\u00e0" + } + }, + "record_mode": { + "state": { + "1": "Registra solo gli eventi", + "2": "Registrazione continua" + } + }, + "relay_status": { + "state": { + "last": "Ricorda l'ultimo stato", + "memory": "Ricorda l'ultimo stato", + "off": "Spento", + "on": "Acceso", + "power_off": "Spento", + "power_on": "Acceso" + } + }, + "vacuum_cistern": { + "state": { + "closed": "Chiuso", + "high": "Alto", + "low": "Basso", + "middle": "Medio" + } + }, + "vacuum_collection": { + "state": { + "large": "Largo", + "middle": "Medio", + "small": "Piccolo" + } + }, + "vacuum_mode": { + "state": { + "bow": "Arco", + "chargego": "Ritorna alla base", + "left_bow": "Arco a sinistra", + "left_spiral": "Spirale a sinistra", + "mop": "Mocio", + "part": "Parte", + "partial_bow": "Arco Parziale", + "pick_zone": "Zona di prelievo", + "point": "Punto", + "pose": "Posa", + "random": "Casuale", + "right_bow": "Arco a destra", + "right_spiral": "Spirale a destra", + "single": "Singolo", + "smart": "Inteligente", + "spiral": "Spirale", + "standby": "Pausa", + "wall_follow": "Segui il muro", + "zone": "Zona" + } + } + }, + "sensor": { + "air_quality": { + "state": { + "good": "Buono", + "great": "Grande", + "mild": "Lieve", + "severe": "Grave" + } + }, + "status": { + "state": { + "boiling_temp": "Temperatura di ebollizione", + "cooling": "Raffreddamento", + "heating": "Riscaldamento", + "heating_temp": "Temperatura di riscaldamento", + "reserve_1": "Riserva 1", + "reserve_2": "Riserva 2", + "reserve_3": "Riserva 3", + "standby": "Pausa", + "warm": "Conservazione del calore" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tuya/translations/nl.json b/homeassistant/components/tuya/translations/nl.json index ed2ce07c226..c655330805b 100644 --- a/homeassistant/components/tuya/translations/nl.json +++ b/homeassistant/components/tuya/translations/nl.json @@ -16,5 +16,80 @@ "description": "Voer uw Tuya-inloggegevens in." } } + }, + "entity": { + "select": { + "basic_anti_flicker": { + "state": { + "0": "Uitgeschakeld", + "1": "50 Hz", + "2": "60 Hz" + } + }, + "basic_nightvision": { + "state": { + "1": "Uit", + "2": "Aan" + } + }, + "countdown": { + "state": { + "1h": "1 uur", + "2h": "2 uur", + "3h": "3 uur", + "4h": "4 uur", + "5h": "5 uur", + "6h": "6 uur" + } + }, + "curtain_mode": { + "state": { + "night": "Nacht" + } + }, + "fan_angle": { + "state": { + "30": "30\u00b0", + "60": "60\u00b0", + "90": "90\u00b0" + } + }, + "fingerbot_mode": { + "state": { + "switch": "Schakelaar" + } + }, + "humidifier_spray_mode": { + "state": { + "humidity": "Vochtigheid" + } + }, + "led_type": { + "state": { + "halogen": "Halogeen", + "incandescent": "Gloeilamp", + "led": "LED" + } + }, + "light_mode": { + "state": { + "none": "Uit" + } + }, + "relay_status": { + "state": { + "off": "Uit", + "on": "Aan", + "power_off": "Uit" + } + } + }, + "sensor": { + "air_quality": { + "state": { + "good": "Goed" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tuya/translations/no.json b/homeassistant/components/tuya/translations/no.json index 7c0137b80a6..bd82f46d674 100644 --- a/homeassistant/components/tuya/translations/no.json +++ b/homeassistant/components/tuya/translations/no.json @@ -16,5 +16,201 @@ "description": "Skriv inn Tuya -legitimasjonen din" } } + }, + "entity": { + "select": { + "basic_anti_flicker": { + "state": { + "0": "Deaktivert", + "1": "50 Hz", + "2": "60 Hz" + } + }, + "basic_nightvision": { + "state": { + "0": "Automatisk", + "1": "Av", + "2": "P\u00e5" + } + }, + "countdown": { + "state": { + "1h": "1 time", + "2h": "2 timer", + "3h": "3 timer", + "4h": "4 timer", + "5h": "5 timer", + "6h": "6 timer", + "cancel": "Avbryt" + } + }, + "curtain_mode": { + "state": { + "morning": "Morgen", + "night": "Natt" + } + }, + "curtain_motor_mode": { + "state": { + "back": "Tilbake", + "forward": "Framover" + } + }, + "decibel_sensitivity": { + "state": { + "0": "Lav f\u00f8lsomhet", + "1": "H\u00f8y f\u00f8lsomhet" + } + }, + "fan_angle": { + "state": { + "30": "30\u00b0", + "60": "60\u00b0", + "90": "90\u00b0" + } + }, + "fingerbot_mode": { + "state": { + "click": "Trykk", + "switch": "Bryter" + } + }, + "humidifier_level": { + "state": { + "level_1": "Niv\u00e5 1", + "level_10": "Niv\u00e5 10", + "level_2": "Niv\u00e5 2", + "level_3": "Niv\u00e5 3", + "level_4": "Niv\u00e5 4", + "level_5": "Niv\u00e5 5", + "level_6": "Niv\u00e5 6", + "level_7": "Niv\u00e5 7", + "level_8": "Niv\u00e5 8", + "level_9": "Niv\u00e5 9" + } + }, + "humidifier_moodlighting": { + "state": { + "1": "Stemning 1", + "2": "Stemning 2", + "3": "Stemning 3", + "4": "Stemning 4", + "5": "Stemning 5" + } + }, + "humidifier_spray_mode": { + "state": { + "auto": "Auto", + "health": "Helse", + "humidity": "Fuktighet", + "sleep": "Sove", + "work": "Arbeid" + } + }, + "ipc_work_mode": { + "state": { + "0": "Lav effekt modus", + "1": "Kontinuerlig arbeidsmodus" + } + }, + "led_type": { + "state": { + "halogen": "Halogen", + "incandescent": "Gl\u00f8dende", + "led": "LED" + } + }, + "light_mode": { + "state": { + "none": "Av", + "pos": "Angi bytteplassering", + "relay": "Indiker bryter p\u00e5/av-tilstand" + } + }, + "motion_sensitivity": { + "state": { + "0": "Lav f\u00f8lsomhet", + "1": "Middels f\u00f8lsomhet", + "2": "H\u00f8y f\u00f8lsomhet" + } + }, + "record_mode": { + "state": { + "1": "Registrer bare hendelser", + "2": "Kontinuerlig opptak" + } + }, + "relay_status": { + "state": { + "last": "Husk siste tilstand", + "memory": "Husk siste tilstand", + "off": "Av", + "on": "P\u00e5", + "power_off": "Av", + "power_on": "P\u00e5" + } + }, + "vacuum_cistern": { + "state": { + "closed": "Lukket", + "high": "H\u00f8y", + "low": "Lav", + "middle": "Midten" + } + }, + "vacuum_collection": { + "state": { + "large": "Stor", + "middle": "Midten", + "small": "Liten" + } + }, + "vacuum_mode": { + "state": { + "bow": "Bue", + "chargego": "Returner til dokken", + "left_bow": "Bue til venstre", + "left_spiral": "Spiral til venstre", + "mop": "Mopp", + "part": "Del", + "partial_bow": "Bue Delvis", + "pick_zone": "Velg sone", + "point": "Punkt", + "pose": "Posere", + "random": "Tilfeldig", + "right_bow": "Bue H\u00f8yre", + "right_spiral": "Spiral h\u00f8yre", + "single": "Enkelt", + "smart": "Smart", + "spiral": "Spiral", + "standby": "Ventemodus", + "wall_follow": "F\u00f8lg Wall", + "zone": "Sone" + } + } + }, + "sensor": { + "air_quality": { + "state": { + "good": "Bra", + "great": "Utmerket", + "mild": "Mild", + "severe": "Alvorlig" + } + }, + "status": { + "state": { + "boiling_temp": "Koketemperatur", + "cooling": "Kj\u00f8ling", + "heating": "Oppvarming", + "heating_temp": "Oppvarmingstemperatur", + "reserve_1": "Reserver 1", + "reserve_2": "Reserver 2", + "reserve_3": "Reserver 3", + "standby": "Vent litt", + "warm": "Varmekonservering" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tuya/translations/pl.json b/homeassistant/components/tuya/translations/pl.json index e7fe9caf65a..7604daa0b36 100644 --- a/homeassistant/components/tuya/translations/pl.json +++ b/homeassistant/components/tuya/translations/pl.json @@ -16,5 +16,201 @@ "description": "Wprowad\u017a dane uwierzytelniaj\u0105ce Tuya" } } + }, + "entity": { + "select": { + "basic_anti_flicker": { + "state": { + "0": "wy\u0142\u0105czony", + "1": "50 Hz", + "2": "60 Hz" + } + }, + "basic_nightvision": { + "state": { + "0": "automatyczny", + "1": "wy\u0142\u0105czony", + "2": "w\u0142\u0105czony" + } + }, + "countdown": { + "state": { + "1h": "1 godzina", + "2h": "2 godziny", + "3h": "3 godziny", + "4h": "4 godziny", + "5h": "5 godzin", + "6h": "6 godzin", + "cancel": "anuluj" + } + }, + "curtain_mode": { + "state": { + "morning": "ranek", + "night": "noc" + } + }, + "curtain_motor_mode": { + "state": { + "back": "do ty\u0142u", + "forward": "do przodu" + } + }, + "decibel_sensitivity": { + "state": { + "0": "niska czu\u0142o\u015b\u0107", + "1": "wysoka czu\u0142o\u015b\u0107" + } + }, + "fan_angle": { + "state": { + "30": "30\u00b0", + "60": "60\u00b0", + "90": "90\u00b0" + } + }, + "fingerbot_mode": { + "state": { + "click": "naci\u015bni\u0119cie", + "switch": "prze\u0142\u0105cznik" + } + }, + "humidifier_level": { + "state": { + "level_1": "poziom 1", + "level_10": "poziom 10", + "level_2": "poziom 2", + "level_3": "poziom 3", + "level_4": "poziom 4", + "level_5": "poziom 5", + "level_6": "poziom 6", + "level_7": "poziom 7", + "level_8": "poziom 8", + "level_9": "poziom 9" + } + }, + "humidifier_moodlighting": { + "state": { + "1": "nastr\u00f3j 1", + "2": "nastr\u00f3j 2", + "3": "nastr\u00f3j 3", + "4": "nastr\u00f3j 4", + "5": "nastr\u00f3j 5" + } + }, + "humidifier_spray_mode": { + "state": { + "auto": "automatyczny", + "health": "zdrowotny", + "humidity": "wilgotno\u015b\u0107", + "sleep": "noc", + "work": "praca" + } + }, + "ipc_work_mode": { + "state": { + "0": "tryb niskiego poboru mocy", + "1": "tryb pracy ci\u0105g\u0142ej" + } + }, + "led_type": { + "state": { + "halogen": "halogen", + "incandescent": "jarzeni\u00f3wka", + "led": "LED" + } + }, + "light_mode": { + "state": { + "none": "wy\u0142\u0105czony", + "pos": "wska\u017c lokalizacj\u0119 prze\u0142\u0105cznika", + "relay": "wska\u017c stan w\u0142./wy\u0142." + } + }, + "motion_sensitivity": { + "state": { + "0": "niska czu\u0142o\u015b\u0107", + "1": "\u015brednia czu\u0142o\u015b\u0107", + "2": "wysoka czu\u0142o\u015b\u0107" + } + }, + "record_mode": { + "state": { + "1": "nagrywaj tylko zdarzenia", + "2": "nagrywanie ci\u0105g\u0142e" + } + }, + "relay_status": { + "state": { + "last": "zapami\u0119taj ostatni stan", + "memory": "zapami\u0119taj ostatni stan", + "off": "wy\u0142\u0105czony", + "on": "w\u0142\u0105czony", + "power_off": "wy\u0142\u0105czony", + "power_on": "w\u0142\u0105czony" + } + }, + "vacuum_cistern": { + "state": { + "closed": "zamkni\u0119ta", + "high": "wysoki", + "low": "niski", + "middle": "\u015bredni" + } + }, + "vacuum_collection": { + "state": { + "large": "du\u017co", + "middle": "\u015brednio", + "small": "ma\u0142o" + } + }, + "vacuum_mode": { + "state": { + "bow": "\u0142uk", + "chargego": "powr\u00f3t do stacji dokuj\u0105cej", + "left_bow": "\u0142uk w lewo", + "left_spiral": "spirala w lewo", + "mop": "mop", + "part": "cz\u0119\u015bciowe", + "partial_bow": "cz\u0119\u015bciowy \u0142uk", + "pick_zone": "wybierz stref\u0119", + "point": "punkt", + "pose": "pozycja", + "random": "losowo", + "right_bow": "\u0142uk w prawo", + "right_spiral": "spirala w prawo", + "single": "pojedyncze", + "smart": "smart", + "spiral": "spirala", + "standby": "tryb czuwania", + "wall_follow": "wzd\u0142u\u017c \u015bciany", + "zone": "strefa" + } + } + }, + "sensor": { + "air_quality": { + "state": { + "good": "dobra", + "great": "\u015bwietna", + "mild": "umiarkowana", + "severe": "z\u0142a" + } + }, + "status": { + "state": { + "boiling_temp": "temperatura wrzenia", + "cooling": "ch\u0142odzenie", + "heating": "grzanie", + "heating_temp": "temperatura ogrzewania", + "reserve_1": "rezerwa 1", + "reserve_2": "rezerwa 2", + "reserve_3": "rezerwa 3", + "standby": "tryb czuwania", + "warm": "utrzymywanie ciep\u0142a" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tuya/translations/pt-BR.json b/homeassistant/components/tuya/translations/pt-BR.json index 5ae0382d0b8..df552b85d94 100644 --- a/homeassistant/components/tuya/translations/pt-BR.json +++ b/homeassistant/components/tuya/translations/pt-BR.json @@ -16,5 +16,201 @@ "description": "Digite sua credencial Tuya" } } + }, + "entity": { + "select": { + "basic_anti_flicker": { + "state": { + "0": "Desabilitado", + "1": "50 Hz", + "2": "60 Hz" + } + }, + "basic_nightvision": { + "state": { + "0": "Autom\u00e1tico", + "1": "Desligado", + "2": "Ligado" + } + }, + "countdown": { + "state": { + "1h": "1 hora", + "2h": "2 horas", + "3h": "3 horas", + "4h": "4 horas", + "5h": "5 horas", + "6h": "6 horas", + "cancel": "Cancelar" + } + }, + "curtain_mode": { + "state": { + "morning": "Manh\u00e3", + "night": "Noite" + } + }, + "curtain_motor_mode": { + "state": { + "back": "Voltar", + "forward": "Avan\u00e7ar" + } + }, + "decibel_sensitivity": { + "state": { + "0": "Baixa sensibilidade", + "1": "Alta sensibilidade" + } + }, + "fan_angle": { + "state": { + "30": "30\u00b0", + "60": "60\u00b0", + "90": "90\u00b0" + } + }, + "fingerbot_mode": { + "state": { + "click": "Empurre", + "switch": "Interruptor" + } + }, + "humidifier_level": { + "state": { + "level_1": "N\u00edvel 1", + "level_10": "N\u00edvel 10", + "level_2": "N\u00edvel 2", + "level_3": "N\u00edvel 3", + "level_4": "N\u00edvel 4", + "level_5": "N\u00edvel 5", + "level_6": "N\u00edvel 6", + "level_7": "N\u00edvel 7", + "level_8": "N\u00edvel 8", + "level_9": "N\u00edvel 9" + } + }, + "humidifier_moodlighting": { + "state": { + "1": "Ambiente 1", + "2": "Ambiente 2", + "3": "Ambiente 3", + "4": "Ambiente 4", + "5": "Ambiente 5" + } + }, + "humidifier_spray_mode": { + "state": { + "auto": "Auto", + "health": "Sa\u00fade", + "humidity": "Umidade", + "sleep": "Sono", + "work": "Trabalho" + } + }, + "ipc_work_mode": { + "state": { + "0": "Modo de baixo consumo", + "1": "Modo de trabalho cont\u00ednuo" + } + }, + "led_type": { + "state": { + "halogen": "Halog\u00eanio", + "incandescent": "Incandescente", + "led": "LED" + } + }, + "light_mode": { + "state": { + "none": "Desligado", + "pos": "Indique a localiza\u00e7\u00e3o do interruptor", + "relay": "Indica o estado de ligar/desligar" + } + }, + "motion_sensitivity": { + "state": { + "0": "Baixa sensibilidade", + "1": "M\u00e9dia sensibilidade", + "2": "Alta sensibilidade" + } + }, + "record_mode": { + "state": { + "1": "Gravar apenas eventos", + "2": "Grava\u00e7\u00e3o cont\u00ednua" + } + }, + "relay_status": { + "state": { + "last": "Lembrar \u00faltimo estado", + "memory": "Lembrar \u00faltimo estado", + "off": "Desligado", + "on": "Ligado", + "power_off": "Desligado", + "power_on": "Ligado" + } + }, + "vacuum_cistern": { + "state": { + "closed": "Fechado", + "high": "Alto", + "low": "Baixo", + "middle": "M\u00e9dio" + } + }, + "vacuum_collection": { + "state": { + "large": "Grande", + "middle": "M\u00e9dio", + "small": "Pequeno" + } + }, + "vacuum_mode": { + "state": { + "bow": "Arco", + "chargego": "Retorno \u00e0 base", + "left_bow": "Arco \u00e0 esquerda", + "left_spiral": "Espiral esquerda", + "mop": "Mop", + "part": "Parte", + "partial_bow": "Curve-se parcialmente", + "pick_zone": "Escolher zona", + "point": "Ponto", + "pose": "Pose", + "random": "Aleat\u00f3rio", + "right_bow": "Arco \u00e0 direita", + "right_spiral": "Espiral \u00e0 direita", + "single": "\u00danico", + "smart": "Smart", + "spiral": "Espiral", + "standby": "Em espera", + "wall_follow": "Siga a parede", + "zone": "Zona" + } + } + }, + "sensor": { + "air_quality": { + "state": { + "good": "Bom", + "great": "\u00d3timo", + "mild": "Suave", + "severe": "Forte" + } + }, + "status": { + "state": { + "boiling_temp": "Temperatura de ebuli\u00e7\u00e3o", + "cooling": "Resfriamento", + "heating": "Aquecimento", + "heating_temp": "Temperatura de aquecimento", + "reserve_1": "Reserva 1", + "reserve_2": "Reserva 2", + "reserve_3": "Reserva 3", + "standby": "Em espera", + "warm": "Preserva\u00e7\u00e3o do calor" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tuya/translations/ru.json b/homeassistant/components/tuya/translations/ru.json index f58a78d1bb5..df9b6a7117b 100644 --- a/homeassistant/components/tuya/translations/ru.json +++ b/homeassistant/components/tuya/translations/ru.json @@ -16,5 +16,201 @@ "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 Home Assistant \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 Tuya." } } + }, + "entity": { + "select": { + "basic_anti_flicker": { + "state": { + "0": "\u041e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u043e", + "1": "50 \u0413\u0446", + "2": "60 \u0413\u0446" + } + }, + "basic_nightvision": { + "state": { + "0": "\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438", + "1": "\u0412\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u043e", + "2": "\u0412\u043a\u043b\u044e\u0447\u0435\u043d\u043e" + } + }, + "countdown": { + "state": { + "1h": "1 \u0447\u0430\u0441", + "2h": "2 \u0447\u0430\u0441\u0430", + "3h": "3 \u0447\u0430\u0441\u0430", + "4h": "4 \u0447\u0430\u0441\u0430", + "5h": "5 \u0447\u0430\u0441\u043e\u0432", + "6h": "6 \u0447\u0430\u0441\u043e\u0432", + "cancel": "\u041e\u0442\u043c\u0435\u043d\u0430" + } + }, + "curtain_mode": { + "state": { + "morning": "\u0423\u0442\u0440\u043e", + "night": "\u041d\u043e\u0447\u044c" + } + }, + "curtain_motor_mode": { + "state": { + "back": "\u041d\u0430\u0437\u0430\u0434", + "forward": "\u0412\u043f\u0435\u0440\u0435\u0434" + } + }, + "decibel_sensitivity": { + "state": { + "0": "\u041d\u0438\u0437\u043a\u0430\u044f \u0447\u0443\u0432\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c", + "1": "\u0412\u044b\u0441\u043e\u043a\u0430\u044f \u0447\u0443\u0432\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c" + } + }, + "fan_angle": { + "state": { + "30": "30\u00b0", + "60": "60\u00b0", + "90": "90\u00b0" + } + }, + "fingerbot_mode": { + "state": { + "click": "\u041a\u043d\u043e\u043f\u043a\u0430", + "switch": "\u0412\u044b\u043a\u043b\u044e\u0447\u0430\u0442\u0435\u043b\u044c" + } + }, + "humidifier_level": { + "state": { + "level_1": "\u0423\u0440\u043e\u0432\u0435\u043d\u044c 1", + "level_10": "\u0423\u0440\u043e\u0432\u0435\u043d\u044c 10", + "level_2": "\u0423\u0440\u043e\u0432\u0435\u043d\u044c 2", + "level_3": "\u0423\u0440\u043e\u0432\u0435\u043d\u044c 3", + "level_4": "\u0423\u0440\u043e\u0432\u0435\u043d\u044c 4", + "level_5": "\u0423\u0440\u043e\u0432\u0435\u043d\u044c 5", + "level_6": "\u0423\u0440\u043e\u0432\u0435\u043d\u044c 6", + "level_7": "\u0423\u0440\u043e\u0432\u0435\u043d\u044c 7", + "level_8": "\u0423\u0440\u043e\u0432\u0435\u043d\u044c 8", + "level_9": "\u0423\u0440\u043e\u0432\u0435\u043d\u044c 9" + } + }, + "humidifier_moodlighting": { + "state": { + "1": "\u041d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u0435 1", + "2": "\u041d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u0435 2", + "3": "\u041d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u0435 3", + "4": "\u041d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u0435 4", + "5": "\u041d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u0435 5" + } + }, + "humidifier_spray_mode": { + "state": { + "auto": "\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438", + "health": "\u0417\u0434\u043e\u0440\u043e\u0432\u044c\u0435", + "humidity": "\u0412\u043b\u0430\u0436\u043d\u043e\u0441\u0442\u044c", + "sleep": "\u0421\u043e\u043d", + "work": "\u0420\u0430\u0431\u043e\u0442\u0430" + } + }, + "ipc_work_mode": { + "state": { + "0": "\u0420\u0435\u0436\u0438\u043c \u043d\u0438\u0437\u043a\u043e\u0433\u043e \u044d\u043d\u0435\u0440\u0433\u043e\u043f\u043e\u0442\u0440\u0435\u0431\u043b\u0435\u043d\u0438\u044f", + "1": "\u041d\u0435\u043f\u0440\u0435\u0440\u044b\u0432\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c \u0440\u0430\u0431\u043e\u0442\u044b" + } + }, + "led_type": { + "state": { + "halogen": "\u0413\u0430\u043b\u043e\u0433\u0435\u043d", + "incandescent": "\u041b\u0430\u043c\u043f\u0430 \u043d\u0430\u043a\u0430\u043b\u0438\u0432\u0430\u043d\u0438\u044f", + "led": "\u0421\u0432\u0435\u0442\u043e\u0434\u0438\u043e\u0434" + } + }, + "light_mode": { + "state": { + "none": "\u0412\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u043e", + "pos": "\u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u043c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0442\u0435\u043b\u044f", + "relay": "\u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f/\u0432\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f" + } + }, + "motion_sensitivity": { + "state": { + "0": "\u041d\u0438\u0437\u043a\u0430\u044f \u0447\u0443\u0432\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c", + "1": "\u0421\u0440\u0435\u0434\u043d\u044f\u044f \u0447\u0443\u0432\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c", + "2": "\u0412\u044b\u0441\u043e\u043a\u0430\u044f \u0447\u0443\u0432\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c" + } + }, + "record_mode": { + "state": { + "1": "\u0417\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u043e\u0431\u044b\u0442\u0438\u044f", + "2": "\u041d\u0435\u043f\u0440\u0435\u0440\u044b\u0432\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c" + } + }, + "relay_status": { + "state": { + "last": "\u0417\u0430\u043f\u043e\u043c\u043d\u0438\u0442\u044c \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435", + "memory": "\u0417\u0430\u043f\u043e\u043c\u043d\u0438\u0442\u044c \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435", + "off": "\u0412\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u043e", + "on": "\u0412\u043a\u043b\u044e\u0447\u0435\u043d\u043e", + "power_off": "\u0412\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u043e", + "power_on": "\u0412\u043a\u043b\u044e\u0447\u0435\u043d\u043e" + } + }, + "vacuum_cistern": { + "state": { + "closed": "\u0417\u0430\u043a\u0440\u044b\u0442\u043e", + "high": "\u0412\u044b\u0441\u043e\u043a\u043e\u0435", + "low": "\u041d\u0438\u0437\u043a\u043e\u0435", + "middle": "\u0421\u0440\u0435\u0434\u043d\u0435\u0435" + } + }, + "vacuum_collection": { + "state": { + "large": "\u0411\u043e\u043b\u044c\u0448\u043e\u0435", + "middle": "\u0421\u0440\u0435\u0434\u043d\u0435\u0435", + "small": "\u041d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0435" + } + }, + "vacuum_mode": { + "state": { + "bow": "\u041e\u0433\u0438\u0431\u0430\u0442\u044c", + "chargego": "\u0412\u0435\u0440\u043d\u0443\u0442\u044c \u043a \u0434\u043e\u043a-\u0441\u0442\u0430\u043d\u0446\u0438\u0438", + "left_bow": "\u041e\u0433\u0438\u0431\u0430\u0442\u044c \u0441\u043b\u0435\u0432\u0430", + "left_spiral": "\u0421\u043f\u0438\u0440\u0430\u043b\u044c \u0432\u043b\u0435\u0432\u043e", + "mop": "\u0428\u0432\u0430\u0431\u0440\u0430", + "part": "\u0427\u0430\u0441\u0442\u044c", + "partial_bow": "\u041e\u0433\u0438\u0431\u0430\u0442\u044c \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e", + "pick_zone": "\u0412\u044b\u0431\u0440\u0430\u0442\u044c \u0437\u043e\u043d\u0443", + "point": "\u0422\u043e\u0447\u043a\u0430", + "pose": "\u041f\u043e\u0437\u0438\u0446\u0438\u044f", + "random": "\u0421\u043b\u0443\u0447\u0430\u0439\u043d\u044b\u0439", + "right_bow": "\u041e\u0433\u0438\u0431\u0430\u0442\u044c \u0441\u043f\u0440\u0430\u0432\u0430", + "right_spiral": "\u0421\u043f\u0438\u0440\u0430\u043b\u044c \u0432\u043f\u0440\u0430\u0432\u043e", + "single": "\u041e\u0434\u0438\u043d\u043e\u0447\u043d\u044b\u0439", + "smart": "\u0418\u043d\u0442\u0435\u043b\u043b\u0435\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0439", + "spiral": "\u0421\u043f\u0438\u0440\u0430\u043b\u044c", + "standby": "\u041e\u0436\u0438\u0434\u0430\u043d\u0438\u0435", + "wall_follow": "\u0421\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u044c \u0437\u0430 \u0441\u0442\u0435\u043d\u043e\u0439", + "zone": "\u0417\u043e\u043d\u0430" + } + } + }, + "sensor": { + "air_quality": { + "state": { + "good": "\u0425\u043e\u0440\u043e\u0448\u0435\u0435", + "great": "\u041e\u0442\u043b\u0438\u0447\u043d\u043e\u0435", + "mild": "\u0423\u043c\u0435\u0440\u0435\u043d\u043d\u043e\u0435", + "severe": "\u041f\u043b\u043e\u0445\u043e\u0435" + } + }, + "status": { + "state": { + "boiling_temp": "\u0422\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430 \u043a\u0438\u043f\u0435\u043d\u0438\u044f", + "cooling": "\u041e\u0445\u043b\u0430\u0436\u0434\u0435\u043d\u0438\u0435", + "heating": "\u041e\u0431\u043e\u0433\u0440\u0435\u0432", + "heating_temp": "\u0422\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430 \u043e\u0431\u043e\u0433\u0440\u0435\u0432\u0430", + "reserve_1": "\u0420\u0435\u0437\u0435\u0440\u0432 1", + "reserve_2": "\u0420\u0435\u0437\u0435\u0440\u0432 2", + "reserve_3": "\u0420\u0435\u0437\u0435\u0440\u0432 3", + "standby": "\u041e\u0436\u0438\u0434\u0430\u043d\u0438\u0435", + "warm": "\u0421\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0442\u0435\u043f\u043b\u0430" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/tuya/translations/select.ko.json b/homeassistant/components/tuya/translations/select.ko.json index 022505e76e5..6ffc0299c81 100644 --- a/homeassistant/components/tuya/translations/select.ko.json +++ b/homeassistant/components/tuya/translations/select.ko.json @@ -4,9 +4,22 @@ "1": "50Hz", "2": "60Hz" }, + "tuya__basic_nightvision": { + "1": "\uaebc\uc9d0", + "2": "\ucf1c\uc9d0" + }, "tuya__fingerbot_mode": { "click": "\ud478\uc2dc", "switch": "\uc2a4\uc704\uce58" + }, + "tuya__light_mode": { + "none": "\uaebc\uc9d0" + }, + "tuya__relay_status": { + "off": "\uaebc\uc9d0", + "on": "\ucf1c\uc9d0", + "power_off": "\uaebc\uc9d0", + "power_on": "\ucf1c\uc9d0" } } } \ No newline at end of file diff --git a/homeassistant/components/tuya/translations/select.ru.json b/homeassistant/components/tuya/translations/select.ru.json index c2138e2490b..d9b75ee88a7 100644 --- a/homeassistant/components/tuya/translations/select.ru.json +++ b/homeassistant/components/tuya/translations/select.ru.json @@ -115,6 +115,7 @@ "left_spiral": "\u0421\u043f\u0438\u0440\u0430\u043b\u044c \u0432\u043b\u0435\u0432\u043e", "mop": "\u0428\u0432\u0430\u0431\u0440\u0430", "part": "\u0427\u0430\u0441\u0442\u044c", + "partial_bow": "\u041e\u0433\u0438\u0431\u0430\u0442\u044c \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e", "pick_zone": "\u0412\u044b\u0431\u0440\u0430\u0442\u044c \u0437\u043e\u043d\u0443", "point": "\u0422\u043e\u0447\u043a\u0430", "pose": "\u041f\u043e\u0437\u0438\u0446\u0438\u044f", diff --git a/homeassistant/components/tuya/translations/select.sk.json b/homeassistant/components/tuya/translations/select.sk.json index 94f4d8ec464..c6cf3710163 100644 --- a/homeassistant/components/tuya/translations/select.sk.json +++ b/homeassistant/components/tuya/translations/select.sk.json @@ -6,6 +6,7 @@ "2": "60 Hz" }, "tuya__basic_nightvision": { + "0": "Automaticky", "1": "Neakt\u00edvny", "2": "Akt\u00edvny" }, @@ -27,6 +28,7 @@ "forward": "Dopredu" }, "tuya__decibel_sensitivity": { + "0": "N\u00edzka citlivos\u0165", "1": "Vysok\u00e1 citlivos\u0165" }, "tuya__fan_angle": { @@ -35,10 +37,38 @@ "90": "90\u00b0" }, "tuya__fingerbot_mode": { + "click": "Stla\u010di\u0165", "switch": "Prep\u00edna\u010d" }, + "tuya__humidifier_level": { + "level_1": "\u00darove\u0148 1", + "level_10": "\u00darove\u0148 10", + "level_2": "\u00darove\u0148 2", + "level_3": "\u00darove\u0148 3", + "level_4": "\u00darove\u0148 4", + "level_5": "\u00darove\u0148 5", + "level_6": "\u00darove\u0148 6", + "level_7": "\u00darove\u0148 7", + "level_8": "\u00darove\u0148 8", + "level_9": "\u00darove\u0148 9" + }, + "tuya__humidifier_moodlighting": { + "1": "N\u00e1lada 1", + "2": "N\u00e1lada 2", + "3": "N\u00e1lada 3", + "4": "N\u00e1lada 4", + "5": "N\u00e1lada 5" + }, "tuya__humidifier_spray_mode": { - "humidity": "Vlhkos\u0165" + "auto": "Auto", + "health": "Zdravie", + "humidity": "Vlhkos\u0165", + "sleep": "Sp\u00e1nok", + "work": "\u010cinnos\u0165" + }, + "tuya__ipc_work_mode": { + "0": "Re\u017eim n\u00edzkej spotreby", + "1": "Nepretr\u017eit\u00fd pracovn\u00fd re\u017eim" }, "tuya__led_type": { "halogen": "Halog\u00e9n", @@ -46,13 +76,19 @@ "led": "LED" }, "tuya__light_mode": { - "none": "Neakt\u00edvny" + "none": "Neakt\u00edvny", + "pos": "Zobrazuje umiestnenie prep\u00edna\u010da", + "relay": "Ozna\u010duje stav zapnutia/vypnutia" }, "tuya__motion_sensitivity": { "0": "N\u00edzka citlivos\u0165", "1": "Stredn\u00e1 citlivos\u0165", "2": "Vysok\u00e1 citlivos\u0165" }, + "tuya__record_mode": { + "1": "Len z\u00e1znam udalost\u00ed", + "2": "Nepretr\u017eit\u00e9 nahr\u00e1vanie" + }, "tuya__relay_status": { "last": "Zapam\u00e4ta\u0165 posledn\u00fd stav", "memory": "Zapam\u00e4ta\u0165 posledn\u00fd stav", @@ -61,8 +97,37 @@ "power_off": "Neakt\u00edvny", "power_on": "Akt\u00edvny" }, + "tuya__vacuum_cistern": { + "closed": "Zatvoren\u00fd", + "high": "Vysok\u00e1", + "low": "N\u00edzka", + "middle": "Stredn\u00e1" + }, + "tuya__vacuum_collection": { + "large": "Ve\u013ek\u00fd", + "middle": "Stredn\u00fd", + "small": "Mal\u00fd" + }, "tuya__vacuum_mode": { - "random": "N\u00e1hodn\u00fd" + "bow": "Obl\u00fak", + "chargego": "N\u00e1vrat do doku", + "left_bow": "Obl\u00fak v\u013eavo", + "left_spiral": "\u0160pir\u00e1la v\u013eavo", + "mop": "Mop", + "part": "\u010cas\u0165", + "partial_bow": "Obl\u00fak \u010diasto\u010dne", + "pick_zone": "Vyberte z\u00f3nu", + "point": "Bod", + "pose": "Poz\u00edcia", + "random": "N\u00e1hodn\u00fd", + "right_bow": "Obl\u00fak vpravo", + "right_spiral": "\u0160pir\u00e1la vpravo", + "single": "Raz", + "smart": "Smart", + "spiral": "\u0160pir\u00e1la", + "standby": "Pohotovostn\u00fd re\u017eim", + "wall_follow": "Sledovanie steny", + "zone": "Z\u00f3na" } } } \ No newline at end of file diff --git a/homeassistant/components/tuya/translations/sensor.sk.json b/homeassistant/components/tuya/translations/sensor.sk.json index c1b9162966d..bdd9812ff54 100644 --- a/homeassistant/components/tuya/translations/sensor.sk.json +++ b/homeassistant/components/tuya/translations/sensor.sk.json @@ -1,5 +1,11 @@ { "state": { + "tuya__air_quality": { + "good": "Dobr\u00e9", + "great": "Skvel\u00e9", + "mild": "Mierne", + "severe": "Z\u00e1va\u017en\u00e9" + }, "tuya__status": { "boiling_temp": "Teplota varu", "cooling": "Chladenie", @@ -7,7 +13,9 @@ "heating_temp": "Teplota vykurovania", "reserve_1": "Rezerva 1", "reserve_2": "Rezerva 2", - "reserve_3": "Rezerva 3" + "reserve_3": "Rezerva 3", + "standby": "Pohotovostn\u00fd re\u017eim", + "warm": "Udr\u017eiavanie tepla" } } } \ No newline at end of file diff --git a/homeassistant/components/tuya/translations/sk.json b/homeassistant/components/tuya/translations/sk.json index 267c2bef955..eba69c3973a 100644 --- a/homeassistant/components/tuya/translations/sk.json +++ b/homeassistant/components/tuya/translations/sk.json @@ -7,8 +7,208 @@ "step": { "user": { "data": { + "access_id": "Tuya IoT Access ID", + "access_secret": "Tuya IoT Access Secret", "country_code": "Krajina", - "password": "Heslo" + "password": "Heslo", + "username": "\u00da\u010det" + }, + "description": "Zadajte svoje prihlasovacie \u00fadaje Tuya" + } + } + }, + "entity": { + "select": { + "basic_anti_flicker": { + "state": { + "0": "Zak\u00e1zan\u00e9", + "1": "50 Hz", + "2": "60 Hz" + } + }, + "basic_nightvision": { + "state": { + "0": "Automaticky", + "1": "Neakt\u00edvny", + "2": "Akt\u00edvny" + } + }, + "countdown": { + "state": { + "1h": "1 hodina", + "2h": "2 hodiny", + "3h": "3 hodiny", + "4h": "4 hodiny", + "5h": "5 hod\u00edn", + "6h": "6 hod\u00edn", + "cancel": "Zru\u0161i\u0165" + } + }, + "curtain_mode": { + "state": { + "morning": "R\u00e1no", + "night": "Noc" + } + }, + "curtain_motor_mode": { + "state": { + "back": "Sp\u00e4\u0165", + "forward": "Dopredu" + } + }, + "decibel_sensitivity": { + "state": { + "0": "N\u00edzka citlivos\u0165", + "1": "Vysok\u00e1 citlivos\u0165" + } + }, + "fan_angle": { + "state": { + "30": "30\u00b0", + "60": "60\u00b0", + "90": "90\u00b0" + } + }, + "fingerbot_mode": { + "state": { + "click": "Stla\u010di\u0165", + "switch": "Prep\u00edna\u010d" + } + }, + "humidifier_level": { + "state": { + "level_1": "\u00darove\u0148 1", + "level_10": "\u00darove\u0148 10", + "level_2": "\u00darove\u0148 2", + "level_3": "\u00darove\u0148 3", + "level_4": "\u00darove\u0148 4", + "level_5": "\u00darove\u0148 5", + "level_6": "\u00darove\u0148 6", + "level_7": "\u00darove\u0148 7", + "level_8": "\u00darove\u0148 8", + "level_9": "\u00darove\u0148 9" + } + }, + "humidifier_moodlighting": { + "state": { + "1": "N\u00e1lada 1", + "2": "N\u00e1lada 2", + "3": "N\u00e1lada 3", + "4": "N\u00e1lada 4", + "5": "N\u00e1lada 5" + } + }, + "humidifier_spray_mode": { + "state": { + "auto": "Auto", + "health": "Zdravie", + "humidity": "Vlhkos\u0165", + "sleep": "Sp\u00e1nok", + "work": "\u010cinnos\u0165" + } + }, + "ipc_work_mode": { + "state": { + "0": "Re\u017eim n\u00edzkej spotreby", + "1": "Nepretr\u017eit\u00fd pracovn\u00fd re\u017eim" + } + }, + "led_type": { + "state": { + "halogen": "Halog\u00e9n", + "incandescent": "\u017diarovka", + "led": "LED" + } + }, + "light_mode": { + "state": { + "none": "Neakt\u00edvny", + "pos": "Zobrazuje umiestnenie prep\u00edna\u010da", + "relay": "Ozna\u010duje stav zapnutia/vypnutia" + } + }, + "motion_sensitivity": { + "state": { + "0": "N\u00edzka citlivos\u0165", + "1": "Stredn\u00e1 citlivos\u0165", + "2": "Vysok\u00e1 citlivos\u0165" + } + }, + "record_mode": { + "state": { + "1": "Len z\u00e1znam udalost\u00ed", + "2": "Nepretr\u017eit\u00e9 nahr\u00e1vanie" + } + }, + "relay_status": { + "state": { + "last": "Zapam\u00e4ta\u0165 posledn\u00fd stav", + "memory": "Zapam\u00e4ta\u0165 posledn\u00fd stav", + "off": "Neakt\u00edvny", + "on": "Akt\u00edvny", + "power_off": "Neakt\u00edvny", + "power_on": "Akt\u00edvny" + } + }, + "vacuum_cistern": { + "state": { + "closed": "Zatvoren\u00fd", + "high": "Vysok\u00e1", + "low": "N\u00edzka", + "middle": "Stredn\u00e1" + } + }, + "vacuum_collection": { + "state": { + "large": "Ve\u013ek\u00fd", + "middle": "Stredn\u00fd", + "small": "Mal\u00fd" + } + }, + "vacuum_mode": { + "state": { + "bow": "Obl\u00fak", + "chargego": "N\u00e1vrat do doku", + "left_bow": "Obl\u00fak v\u013eavo", + "left_spiral": "\u0160pir\u00e1la v\u013eavo", + "mop": "Mop", + "part": "\u010cas\u0165", + "partial_bow": "Obl\u00fak \u010diasto\u010dne", + "pick_zone": "V\u00fdber z\u00f3ny", + "point": "Bod", + "pose": "Poz\u00edcia", + "random": "N\u00e1hodn\u00fd", + "right_bow": "Obl\u00fak vpravo", + "right_spiral": "\u0160pir\u00e1la vpravo", + "single": "Raz", + "smart": "Smart", + "spiral": "\u0160pir\u00e1la", + "standby": "Pohotovostn\u00fd re\u017eim", + "wall_follow": "Sledovanie steny", + "zone": "Z\u00f3na" + } + } + }, + "sensor": { + "air_quality": { + "state": { + "good": "Dobr\u00e9", + "great": "Skvel\u00e9", + "mild": "Mierne", + "severe": "Z\u00e1va\u017en\u00e9" + } + }, + "status": { + "state": { + "boiling_temp": "Teplota varu", + "cooling": "Chladenie", + "heating": "Vykurovanie", + "heating_temp": "Teplota vykurovania", + "reserve_1": "Rezerva 1", + "reserve_2": "Rezerva 2", + "reserve_3": "Rezerva 3", + "standby": "Pohotovostn\u00fd re\u017eim", + "warm": "Udr\u017eiavanie tepla" } } } diff --git a/homeassistant/components/tuya/translations/zh-Hant.json b/homeassistant/components/tuya/translations/zh-Hant.json index c255cb723a9..39a074afea6 100644 --- a/homeassistant/components/tuya/translations/zh-Hant.json +++ b/homeassistant/components/tuya/translations/zh-Hant.json @@ -16,5 +16,201 @@ "description": "\u8f38\u5165 Tuya \u6191\u8b49" } } + }, + "entity": { + "select": { + "basic_anti_flicker": { + "state": { + "0": "\u5df2\u95dc\u9589", + "1": "50 Hz", + "2": "60 Hz" + } + }, + "basic_nightvision": { + "state": { + "0": "\u81ea\u52d5", + "1": "\u95dc\u9589", + "2": "\u958b\u555f" + } + }, + "countdown": { + "state": { + "1h": "1 \u5c0f\u6642", + "2h": "2 \u5c0f\u6642", + "3h": "3 \u5c0f\u6642", + "4h": "4 \u5c0f\u6642", + "5h": "5 \u5c0f\u6642", + "6h": "6 \u5c0f\u6642", + "cancel": "\u53d6\u6d88" + } + }, + "curtain_mode": { + "state": { + "morning": "\u65e9\u6668", + "night": "\u591c\u9593" + } + }, + "curtain_motor_mode": { + "state": { + "back": "\u5f8c\u9000", + "forward": "\u524d\u9032" + } + }, + "decibel_sensitivity": { + "state": { + "0": "\u4f4e\u654f\u611f\u5ea6", + "1": "\u9ad8\u654f\u611f\u5ea6" + } + }, + "fan_angle": { + "state": { + "30": "30\u00b0", + "60": "60\u00b0", + "90": "90\u00b0" + } + }, + "fingerbot_mode": { + "state": { + "click": "\u63a8", + "switch": "\u958b\u95dc" + } + }, + "humidifier_level": { + "state": { + "level_1": "\u7b49\u7d1a 1", + "level_10": "\u7b49\u7d1a 10", + "level_2": "\u7b49\u7d1a 2", + "level_3": "\u7b49\u7d1a 3", + "level_4": "\u7b49\u7d1a 4", + "level_5": "\u7b49\u7d1a 5", + "level_6": "\u7b49\u7d1a 6", + "level_7": "\u7b49\u7d1a 7", + "level_8": "\u7b49\u7d1a 8", + "level_9": "\u7b49\u7d1a 9" + } + }, + "humidifier_moodlighting": { + "state": { + "1": "\u5fc3\u60c5\u60c5\u5883 1", + "2": "\u5fc3\u60c5\u60c5\u5883 2", + "3": "\u5fc3\u60c5\u60c5\u5883 3", + "4": "\u5fc3\u60c5\u60c5\u5883 4", + "5": "\u5fc3\u60c5\u60c5\u5883 5" + } + }, + "humidifier_spray_mode": { + "state": { + "auto": "\u81ea\u52d5", + "health": "\u5065\u5eb7\u6a21\u5f0f", + "humidity": "\u6fd5\u5ea6", + "sleep": "\u7761\u7720\u6a21\u5f0f", + "work": "\u5de5\u4f5c\u6a21\u5f0f" + } + }, + "ipc_work_mode": { + "state": { + "0": "\u4f4e\u529f\u8017\u6a21\u5f0f", + "1": "\u6301\u7e8c\u5de5\u4f5c\u6a21\u5f0f" + } + }, + "led_type": { + "state": { + "halogen": "\u9e75\u7d20\u71c8", + "incandescent": "\u767d\u71be\u71c8", + "led": "LED" + } + }, + "light_mode": { + "state": { + "none": "\u95dc\u9589", + "pos": "\u6307\u793a\u958b\u95dc\u4f4d\u7f6e", + "relay": "\u6307\u793a\u958b\u95dc\u958b\u555f/\u95dc\u9589\u72c0\u614b" + } + }, + "motion_sensitivity": { + "state": { + "0": "\u4f4e\u654f\u611f\u5ea6", + "1": "\u4e2d\u654f\u611f\u5ea6", + "2": "\u9ad8\u654f\u611f\u5ea6" + } + }, + "record_mode": { + "state": { + "1": "\u50c5\u8a18\u9304\u4e8b\u4ef6", + "2": "\u6301\u7e8c\u7d00\u9304" + } + }, + "relay_status": { + "state": { + "last": "\u8a18\u4f4f\u6700\u5f8c\u72c0\u614b", + "memory": "\u8a18\u4f4f\u6700\u5f8c\u72c0\u614b", + "off": "\u95dc\u9589", + "on": "\u958b\u555f", + "power_off": "\u95dc\u9589", + "power_on": "\u958b\u555f" + } + }, + "vacuum_cistern": { + "state": { + "closed": "\u95dc\u9589", + "high": "\u9ad8", + "low": "\u4f4e", + "middle": "\u4e2d" + } + }, + "vacuum_collection": { + "state": { + "large": "\u5927", + "middle": "\u4e2d", + "small": "\u5c0f" + } + }, + "vacuum_mode": { + "state": { + "bow": "\u5f27\u578b", + "chargego": "\u8fd4\u56de\u5145\u96fb", + "left_bow": "\u5de6\u5f27\u5f62", + "left_spiral": "\u5de6\u87ba\u65cb", + "mop": "\u62d6\u5730", + "part": "\u5c40\u90e8", + "partial_bow": "\u5c40\u90e8\u5f27\u5f62", + "pick_zone": "\u9078\u64c7\u5340\u57df", + "point": "\u5b9a\u9ede", + "pose": "\u8def\u5f91", + "random": "\u96a8\u6a5f", + "right_bow": "\u53f3\u5f27\u5f62", + "right_spiral": "\u53f3\u87ba\u65cb", + "single": "\u55ae\u6b21", + "smart": "\u667a\u80fd", + "spiral": "\u87ba\u65cb", + "standby": "\u5f85\u6a5f\u4e2d", + "wall_follow": "\u8ddf\u96a8\u7246\u9762", + "zone": "\u5340\u57df" + } + } + }, + "sensor": { + "air_quality": { + "state": { + "good": "\u826f\u597d", + "great": "\u6975\u4f73", + "mild": "\u8f15\u5fae", + "severe": "\u56b4\u91cd" + } + }, + "status": { + "state": { + "boiling_temp": "\u6cb8\u9a30\u6eab\u5ea6", + "cooling": "\u51b7\u6c23", + "heating": "\u6696\u6c23", + "heating_temp": "\u52a0\u71b1\u6eab\u5ea6", + "reserve_1": "\u9810\u7559 1", + "reserve_2": "\u9810\u7559 2", + "reserve_3": "\u9810\u7559 3", + "standby": "\u5f85\u547d", + "warm": "\u4fdd\u6eab" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/twentemilieu/translations/pt.json b/homeassistant/components/twentemilieu/translations/pt.json index 451ff82e74b..551b98112fa 100644 --- a/homeassistant/components/twentemilieu/translations/pt.json +++ b/homeassistant/components/twentemilieu/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "A localiza\u00e7\u00e3o j\u00e1 est\u00e1 configurada" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" } } } \ No newline at end of file diff --git a/homeassistant/components/twentemilieu/translations/sk.json b/homeassistant/components/twentemilieu/translations/sk.json index 41a4042d8b3..6296ddb1b90 100644 --- a/homeassistant/components/twentemilieu/translations/sk.json +++ b/homeassistant/components/twentemilieu/translations/sk.json @@ -6,6 +6,16 @@ "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_address": "Adresa sa nena\u0161la v servisnej oblasti Twente Milieu." + }, + "step": { + "user": { + "data": { + "house_letter": "Dom \u010d\u00edslo/dodatkov\u00e9", + "house_number": "\u010d\u00edslo domu", + "post_code": "Po\u0161tov\u00e9 smerovacie \u010d\u00edslo" + }, + "description": "Nastavte si Twente Milieu poskytuj\u00face inform\u00e1cie o zbere odpadu na va\u0161ej adrese." + } } } } \ No newline at end of file diff --git a/homeassistant/components/twilio/translations/de.json b/homeassistant/components/twilio/translations/de.json index 97c04631aa5..05fab6db200 100644 --- a/homeassistant/components/twilio/translations/de.json +++ b/homeassistant/components/twilio/translations/de.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "Deine Home Assistant-Instanz muss \u00fcber das Internet erreichbar sein, um Webhook-Nachrichten empfangen zu k\u00f6nnen." }, "create_entry": { - "default": "Um Ereignisse an Home Assistant zu senden, musst du [Webhooks mit Twilio]({twilio_url}) einrichten. \n\n F\u00fclle die folgenden Informationen aus: \n\n - URL: ` {webhook_url} ` \n - Methode: POST \n - Inhaltstyp: application / x-www-form-urlencoded \n\nLies in der [Dokumentation]({docs_url}), wie du Automationen f\u00fcr die Verarbeitung eingehender Daten konfigurierst." + "default": "Um Ereignisse an Home Assistant zu senden, musst du [Webhooks mit Twilio]({twilio_url}) einrichten. \n\n F\u00fclle die folgenden Informationen aus: \n\n - URL: ` {webhook_url} ` \n - Methode: POST \n - Inhaltstyp: application/x-www-form-urlencoded \n\nLies in der [Dokumentation]({docs_url}), wie du Automationen f\u00fcr die Verarbeitung eingehender Daten konfigurierst." }, "step": { "user": { diff --git a/homeassistant/components/twilio/translations/en.json b/homeassistant/components/twilio/translations/en.json index 2bf509d5ca1..6a4c8a4d442 100644 --- a/homeassistant/components/twilio/translations/en.json +++ b/homeassistant/components/twilio/translations/en.json @@ -10,7 +10,7 @@ }, "step": { "user": { - "description": "Do you want to start set up?", + "description": "Do you want to start setup?", "title": "Set up the Twilio Webhook" } } diff --git a/homeassistant/components/twilio/translations/it.json b/homeassistant/components/twilio/translations/it.json index 8fe5f6316f8..71e1414728f 100644 --- a/homeassistant/components/twilio/translations/it.json +++ b/homeassistant/components/twilio/translations/it.json @@ -10,7 +10,7 @@ }, "step": { "user": { - "description": "Vuoi iniziare la configurazione?", + "description": "Vuoi avviare la configurazione?", "title": "Configura il webhook di Twilio" } } diff --git a/homeassistant/components/twilio/translations/ko.json b/homeassistant/components/twilio/translations/ko.json index aeb7c09474d..431e53271fe 100644 --- a/homeassistant/components/twilio/translations/ko.json +++ b/homeassistant/components/twilio/translations/ko.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "cloud_not_connected": "Home Assistant \ud074\ub77c\uc6b0\ub4dc\uc5d0 \uc5f0\uacb0\ub418\uc5b4 \uc788\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.", "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.", "webhook_not_internet_accessible": "\uc6f9 \ud6c5 \uba54\uc2dc\uc9c0\ub97c \ubc1b\uc73c\ub824\uba74 \uc778\ud130\ub137\uc5d0\uc11c Home Assistant \uc778\uc2a4\ud134\uc2a4\uc5d0 \uc811\uadfc\ud560 \uc218 \uc788\uc5b4\uc57c \ud569\ub2c8\ub2e4." }, diff --git a/homeassistant/components/twilio/translations/sk.json b/homeassistant/components/twilio/translations/sk.json index 04cb32a1c4e..4e10b08f833 100644 --- a/homeassistant/components/twilio/translations/sk.json +++ b/homeassistant/components/twilio/translations/sk.json @@ -5,9 +5,13 @@ "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia.", "webhook_not_internet_accessible": "Va\u0161a in\u0161tancia Home Assistant mus\u00ed by\u0165 pr\u00edstupn\u00e1 z internetu, aby ste mohli prij\u00edma\u0165 spr\u00e1vy webhooku." }, + "create_entry": { + "default": "Ak chcete odosiela\u0165 udalosti dom\u00e1cemu asistentovi, budete musie\u0165 nastavi\u0165 [Webhooks with Twilio]({twilio_url}). \n\n Vypl\u0148te nasleduj\u00face inform\u00e1cie: \n\n - URL: `{webhook_url}`\n - Met\u00f3da: POST\n - Typ obsahu: application/x-www-form-urlencoded \n\nPozrite si [dokument\u00e1ciu]({docs_url}), ako nakonfigurova\u0165 automatiz\u00e1ciu na spracovanie prich\u00e1dzaj\u00facich \u00fadajov." + }, "step": { "user": { - "description": "Chcete za\u010da\u0165 nastavova\u0165?" + "description": "Chcete za\u010da\u0165 nastavova\u0165?", + "title": "Nastavte Twilio Webhook" } } } diff --git a/homeassistant/components/twinkly/strings.json b/homeassistant/components/twinkly/strings.json index 630497ef28c..9b4c8ebd778 100644 --- a/homeassistant/components/twinkly/strings.json +++ b/homeassistant/components/twinkly/strings.json @@ -7,7 +7,7 @@ } }, "discovery_confirm": { - "description": "Do you want to setup {name} - {model} ({host})?" + "description": "Do you want to set up {name} - {model} ({host})?" } }, "error": { diff --git a/homeassistant/components/twinkly/translations/en.json b/homeassistant/components/twinkly/translations/en.json index 720a258161f..9fb8846f944 100644 --- a/homeassistant/components/twinkly/translations/en.json +++ b/homeassistant/components/twinkly/translations/en.json @@ -8,7 +8,7 @@ }, "step": { "discovery_confirm": { - "description": "Do you want to setup {name} - {model} ({host})?" + "description": "Do you want to set up {name} - {model} ({host})?" }, "user": { "data": { diff --git a/homeassistant/components/twinkly/translations/no.json b/homeassistant/components/twinkly/translations/no.json index 72f88054aa7..1be0a1a41ca 100644 --- a/homeassistant/components/twinkly/translations/no.json +++ b/homeassistant/components/twinkly/translations/no.json @@ -8,7 +8,7 @@ }, "step": { "discovery_confirm": { - "description": "Vil du konfigurere {name} - {model} ( {host} )?" + "description": "Vil du sette opp {name} - {model} ( {host} )?" }, "user": { "data": { diff --git a/homeassistant/components/twinkly/translations/pt-BR.json b/homeassistant/components/twinkly/translations/pt-BR.json index 789802f44e9..9b25eb24cbb 100644 --- a/homeassistant/components/twinkly/translations/pt-BR.json +++ b/homeassistant/components/twinkly/translations/pt-BR.json @@ -8,7 +8,7 @@ }, "step": { "discovery_confirm": { - "description": "Deseja configurar {name} - {model} ( {host} )?" + "description": "Deseja configurar {name} - {model} ({host})?" }, "user": { "data": { diff --git a/homeassistant/components/twinkly/translations/pt.json b/homeassistant/components/twinkly/translations/pt.json index abed97c8933..c7b868d7b81 100644 --- a/homeassistant/components/twinkly/translations/pt.json +++ b/homeassistant/components/twinkly/translations/pt.json @@ -4,7 +4,7 @@ "device_exists": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" } } } \ No newline at end of file diff --git a/homeassistant/components/ubus/device_tracker.py b/homeassistant/components/ubus/device_tracker.py index 10a8ad61621..3c4c9fe1b9c 100644 --- a/homeassistant/components/ubus/device_tracker.py +++ b/homeassistant/components/ubus/device_tracker.py @@ -58,8 +58,7 @@ def _refresh_on_access_denied(func): return func(self, *args, **kwargs) except PermissionError: _LOGGER.warning( - "Invalid session detected." - " Trying to refresh session_id and re-run RPC" + "Invalid session detected. Trying to refresh session_id and re-run RPC" ) self.ubus.connect() diff --git a/homeassistant/components/uk_transport/sensor.py b/homeassistant/components/uk_transport/sensor.py index 28d0fd71142..cd1983f9479 100644 --- a/homeassistant/components/uk_transport/sensor.py +++ b/homeassistant/components/uk_transport/sensor.py @@ -10,7 +10,7 @@ import requests import voluptuous as vol from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity -from homeassistant.const import CONF_MODE, TIME_MINUTES +from homeassistant.const import CONF_MODE, UnitOfTime from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -107,7 +107,7 @@ class UkTransportSensor(SensorEntity): TRANSPORT_API_URL_BASE = "https://transportapi.com/v3/uk/" _attr_icon = "mdi:train" - _attr_native_unit_of_measurement = TIME_MINUTES + _attr_native_unit_of_measurement = UnitOfTime.MINUTES def __init__(self, name, api_app_id, api_app_key, url): """Initialize the sensor.""" diff --git a/homeassistant/components/ukraine_alarm/translations/ko.json b/homeassistant/components/ukraine_alarm/translations/ko.json index e9da77f866b..66ce880285c 100644 --- a/homeassistant/components/ukraine_alarm/translations/ko.json +++ b/homeassistant/components/ukraine_alarm/translations/ko.json @@ -5,6 +5,7 @@ "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", "max_regions": "\ucd5c\ub300 5\uac1c \uc9c0\uc5ed\uc744 \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.", "rate_limit": "\uc694\uccad\uc774 \ub108\ubb34 \ub9ce\uc2b5\ub2c8\ub2e4", + "timeout": "\uc5f0\uacb0 \uc124\uc815 \uc2dc\uac04 \ucd08\uacfc", "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" }, "step": { diff --git a/homeassistant/components/ukraine_alarm/translations/pt.json b/homeassistant/components/ukraine_alarm/translations/pt.json index e6c831ab07c..aa4ef2a899f 100644 --- a/homeassistant/components/ukraine_alarm/translations/pt.json +++ b/homeassistant/components/ukraine_alarm/translations/pt.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "timeout": "Tempo limite para estabelecer liga\u00e7\u00e3o" + "timeout": "Excedido o tempo limite para estabelecer liga\u00e7\u00e3o" } } } \ No newline at end of file diff --git a/homeassistant/components/ukraine_alarm/translations/sk.json b/homeassistant/components/ukraine_alarm/translations/sk.json index 04a10161b8b..8b861bd38dc 100644 --- a/homeassistant/components/ukraine_alarm/translations/sk.json +++ b/homeassistant/components/ukraine_alarm/translations/sk.json @@ -3,6 +3,8 @@ "abort": { "already_configured": "Umiestnenie u\u017e je nakonfigurovan\u00e9", "cannot_connect": "Nepodarilo sa pripoji\u0165", + "max_regions": "Je mo\u017en\u00e9 nakonfigurova\u0165 maxim\u00e1lne 5 oblast\u00ed", + "rate_limit": "Pr\u00edli\u0161 ve\u013ea po\u017eiadaviek", "timeout": "\u010casov\u00fd limit na nadviazanie spojenia", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, @@ -10,17 +12,20 @@ "community": { "data": { "region": "Regi\u00f3n" - } + }, + "description": "Ak chcete monitorova\u0165 nielen \u0161t\u00e1t a okres, vyberte si jeho konkr\u00e9tnu obec" }, "district": { "data": { "region": "Regi\u00f3n" - } + }, + "description": "Ak chcete sledova\u0165 nielen \u0161t\u00e1t, vyberte si jeho konkr\u00e9tny okres" }, "user": { "data": { "region": "Regi\u00f3n" - } + }, + "description": "Vyberte \u0161t\u00e1t, ktor\u00fd chcete sledova\u0165" } } } diff --git a/homeassistant/components/unifi/entity.py b/homeassistant/components/unifi/entity.py new file mode 100644 index 00000000000..79a80fad73c --- /dev/null +++ b/homeassistant/components/unifi/entity.py @@ -0,0 +1,158 @@ +"""UniFi entity representation.""" +from __future__ import annotations + +from abc import abstractmethod +from collections.abc import Callable +from dataclasses import dataclass +from typing import Generic, TypeVar + +import aiounifi +from aiounifi.interfaces.api_handlers import CallbackType, ItemEvent, UnsubscribeType +from aiounifi.interfaces.devices import Devices +from aiounifi.models.device import Device +from aiounifi.models.event import EventKey + +from homeassistant.core import callback +from homeassistant.helpers import entity_registry as er +from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.entity import DeviceInfo, Entity, EntityDescription + +from .controller import UniFiController + +DataT = TypeVar("DataT", bound=Device) +HandlerT = TypeVar("HandlerT", bound=Devices) +SubscriptionT = Callable[[CallbackType, ItemEvent], UnsubscribeType] + + +@dataclass +class UnifiDescription(Generic[HandlerT, DataT]): + """Validate and load entities from different UniFi handlers.""" + + allowed_fn: Callable[[UniFiController, str], bool] + api_handler_fn: Callable[[aiounifi.Controller], HandlerT] + available_fn: Callable[[UniFiController, str], bool] + device_info_fn: Callable[[aiounifi.Controller, str], DeviceInfo | None] + event_is_on: tuple[EventKey, ...] | None + event_to_subscribe: tuple[EventKey, ...] | None + name_fn: Callable[[DataT], str | None] + object_fn: Callable[[aiounifi.Controller, str], DataT] + supported_fn: Callable[[UniFiController, str], bool | None] + unique_id_fn: Callable[[UniFiController, str], str] + + +@dataclass +class UnifiEntityDescription(EntityDescription, UnifiDescription[HandlerT, DataT]): + """UniFi Entity Description.""" + + +class UnifiEntity(Entity, Generic[HandlerT, DataT]): + """Representation of a UniFi entity.""" + + entity_description: UnifiEntityDescription[HandlerT, DataT] + _attr_should_poll = False + + _attr_unique_id: str + + def __init__( + self, + obj_id: str, + controller: UniFiController, + description: UnifiEntityDescription[HandlerT, DataT], + ) -> None: + """Set up UniFi switch entity.""" + self._obj_id = obj_id + self.controller = controller + self.entity_description = description + + self._removed = False + + self._attr_available = description.available_fn(controller, obj_id) + self._attr_device_info = description.device_info_fn(controller.api, obj_id) + self._attr_unique_id = description.unique_id_fn(controller, obj_id) + + obj = description.object_fn(self.controller.api, obj_id) + self._attr_name = description.name_fn(obj) + self.async_initiate_state() + + async def async_added_to_hass(self) -> None: + """Register callbacks.""" + description = self.entity_description + handler = description.api_handler_fn(self.controller.api) + + # New data from handler + self.async_on_remove( + handler.subscribe( + self.async_signalling_callback, + id_filter=self._obj_id, + ) + ) + + # State change from controller or websocket + self.async_on_remove( + async_dispatcher_connect( + self.hass, + self.controller.signal_reachable, + self.async_signal_reachable_callback, + ) + ) + + # Config entry options updated + self.async_on_remove( + async_dispatcher_connect( + self.hass, + self.controller.signal_options_update, + self.async_signal_options_updated, + ) + ) + + @callback + def async_signalling_callback(self, event: ItemEvent, obj_id: str) -> None: + """Update the entity state.""" + if event == ItemEvent.DELETED and obj_id == self._obj_id: + self.hass.async_create_task(self.remove_item({self._obj_id})) + return + + description = self.entity_description + if not description.supported_fn(self.controller, self._obj_id): + self.hass.async_create_task(self.remove_item({self._obj_id})) + return + + self._attr_available = description.available_fn(self.controller, self._obj_id) + self.async_update_state(event, obj_id) + self.async_write_ha_state() + + @callback + def async_signal_reachable_callback(self) -> None: + """Call when controller connection state change.""" + self.async_signalling_callback(ItemEvent.ADDED, self._obj_id) + + async def async_signal_options_updated(self) -> None: + """Config entry options are updated, remove entity if option is disabled.""" + if not self.entity_description.allowed_fn(self.controller, self._obj_id): + await self.remove_item({self._obj_id}) + + async def remove_item(self, keys: set) -> None: + """Remove entity if object ID is part of set.""" + if self._obj_id not in keys or self._removed: + return + self._removed = True + if self.registry_entry: + er.async_get(self.hass).async_remove(self.entity_id) + else: + await self.async_remove(force_remove=True) + + @callback + @abstractmethod + def async_initiate_state(self) -> None: + """Initiate entity state. + + Perform additional actions setting up platform entity child class state. + """ + + @callback + @abstractmethod + def async_update_state(self, event: ItemEvent, obj_id: str) -> None: + """Update entity state. + + Perform additional actions updating platform entity child class state. + """ diff --git a/homeassistant/components/unifi/manifest.json b/homeassistant/components/unifi/manifest.json index c9e9464a317..34bba257a84 100644 --- a/homeassistant/components/unifi/manifest.json +++ b/homeassistant/components/unifi/manifest.json @@ -3,7 +3,7 @@ "name": "UniFi Network", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/unifi", - "requirements": ["aiounifi==42"], + "requirements": ["aiounifi==43"], "codeowners": ["@Kane610"], "quality_scale": "platinum", "ssdp": [ diff --git a/homeassistant/components/unifi/sensor.py b/homeassistant/components/unifi/sensor.py index ab750f6b33e..e320d1a0d4e 100644 --- a/homeassistant/components/unifi/sensor.py +++ b/homeassistant/components/unifi/sensor.py @@ -3,23 +3,146 @@ Support for bandwidth sensors of network clients. Support for uptime sensors of network clients. """ -from datetime import datetime, timedelta +from __future__ import annotations -from homeassistant.components.sensor import DOMAIN, SensorDeviceClass, SensorEntity +from collections.abc import Callable +from dataclasses import dataclass +from datetime import datetime, timedelta +from typing import Generic, TypeVar + +import aiounifi +from aiounifi.interfaces.api_handlers import ItemEvent +from aiounifi.interfaces.clients import Clients +from aiounifi.models.client import Client + +from homeassistant.components.sensor import ( + SensorDeviceClass, + SensorEntity, + SensorEntityDescription, +) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import DATA_MEGABYTES +from homeassistant.const import UnitOfInformation from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers import entity_registry as er +from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC from homeassistant.helpers.dispatcher import async_dispatcher_connect -from homeassistant.helpers.entity import EntityCategory +from homeassistant.helpers.entity import DeviceInfo, EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback import homeassistant.util.dt as dt_util from .const import DOMAIN as UNIFI_DOMAIN -from .unifi_client import UniFiClient +from .controller import UniFiController -RX_SENSOR = "rx" -TX_SENSOR = "tx" -UPTIME_SENSOR = "uptime" +_DataT = TypeVar("_DataT", bound=Client) +_HandlerT = TypeVar("_HandlerT", bound=Clients) + + +@callback +def async_client_rx_value_fn(controller: UniFiController, client: Client) -> float: + """Calculate if all apps are enabled.""" + if client.mac not in controller.wireless_clients: + return client.wired_rx_bytes_r / 1000000 + return client.rx_bytes_r / 1000000 + + +@callback +def async_client_tx_value_fn(controller: UniFiController, client: Client) -> float: + """Calculate if all apps are enabled.""" + if client.mac not in controller.wireless_clients: + return client.wired_tx_bytes_r / 1000000 + return client.tx_bytes_r / 1000000 + + +@callback +def async_client_uptime_value_fn( + controller: UniFiController, client: Client +) -> datetime: + """Calculate the uptime of the client.""" + if client.uptime < 1000000000: + return dt_util.now() - timedelta(seconds=client.uptime) + return dt_util.utc_from_timestamp(float(client.uptime)) + + +@callback +def async_client_device_info_fn(api: aiounifi.Controller, obj_id: str) -> DeviceInfo: + """Create device registry entry for client.""" + client = api.clients[obj_id] + return DeviceInfo( + connections={(CONNECTION_NETWORK_MAC, obj_id)}, + default_manufacturer=client.oui, + default_name=client.name or client.hostname, + ) + + +@dataclass +class UnifiEntityLoader(Generic[_HandlerT, _DataT]): + """Validate and load entities from different UniFi handlers.""" + + allowed_fn: Callable[[UniFiController, str], bool] + api_handler_fn: Callable[[aiounifi.Controller], _HandlerT] + available_fn: Callable[[UniFiController, str], bool] + device_info_fn: Callable[[aiounifi.Controller, str], DeviceInfo] + name_fn: Callable[[_DataT], str | None] + object_fn: Callable[[aiounifi.Controller, str], _DataT] + supported_fn: Callable[[UniFiController, str], bool | None] + unique_id_fn: Callable[[str], str] + value_fn: Callable[[UniFiController, _DataT], datetime | float] + + +@dataclass +class UnifiEntityDescription( + SensorEntityDescription, UnifiEntityLoader[_HandlerT, _DataT] +): + """Class describing UniFi sensor entity.""" + + +ENTITY_DESCRIPTIONS: tuple[UnifiEntityDescription, ...] = ( + UnifiEntityDescription[Clients, Client]( + key="Bandwidth sensor RX", + entity_category=EntityCategory.DIAGNOSTIC, + native_unit_of_measurement=UnitOfInformation.MEGABYTES, + has_entity_name=True, + allowed_fn=lambda controller, _: controller.option_allow_bandwidth_sensors, + api_handler_fn=lambda api: api.clients, + available_fn=lambda controller, _: controller.available, + device_info_fn=async_client_device_info_fn, + name_fn=lambda _: "RX", + object_fn=lambda api, obj_id: api.clients[obj_id], + supported_fn=lambda controller, _: controller.option_allow_bandwidth_sensors, + unique_id_fn=lambda obj_id: f"rx-{obj_id}", + value_fn=async_client_rx_value_fn, + ), + UnifiEntityDescription[Clients, Client]( + key="Bandwidth sensor TX", + entity_category=EntityCategory.DIAGNOSTIC, + native_unit_of_measurement=UnitOfInformation.MEGABYTES, + has_entity_name=True, + allowed_fn=lambda controller, _: controller.option_allow_bandwidth_sensors, + api_handler_fn=lambda api: api.clients, + available_fn=lambda controller, _: controller.available, + device_info_fn=async_client_device_info_fn, + name_fn=lambda _: "TX", + object_fn=lambda api, obj_id: api.clients[obj_id], + supported_fn=lambda controller, _: controller.option_allow_bandwidth_sensors, + unique_id_fn=lambda obj_id: f"tx-{obj_id}", + value_fn=async_client_tx_value_fn, + ), + UnifiEntityDescription[Clients, Client]( + key="Uptime sensor", + device_class=SensorDeviceClass.TIMESTAMP, + entity_category=EntityCategory.DIAGNOSTIC, + has_entity_name=True, + allowed_fn=lambda controller, _: controller.option_allow_uptime_sensors, + api_handler_fn=lambda api: api.clients, + available_fn=lambda controller, obj_id: controller.available, + device_info_fn=async_client_device_info_fn, + name_fn=lambda client: "Uptime", + object_fn=lambda api, obj_id: api.clients[obj_id], + supported_fn=lambda controller, _: controller.option_allow_uptime_sensors, + unique_id_fn=lambda obj_id: f"uptime-{obj_id}", + value_fn=async_client_uptime_value_fn, + ), +) async def async_setup_entry( @@ -28,158 +151,130 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up sensors for UniFi Network integration.""" - controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id] - controller.entities[DOMAIN] = { - RX_SENSOR: set(), - TX_SENSOR: set(), - UPTIME_SENSOR: set(), - } + controller: UniFiController = hass.data[UNIFI_DOMAIN][config_entry.entry_id] @callback - def items_added( - clients: set = controller.api.clients, devices: set = controller.api.devices + def async_load_entities(description: UnifiEntityDescription) -> None: + """Load and subscribe to UniFi devices.""" + entities: list[SensorEntity] = [] + api_handler = description.api_handler_fn(controller.api) + + @callback + def async_create_entity(event: ItemEvent, obj_id: str) -> None: + """Create UniFi entity.""" + if not description.allowed_fn( + controller, obj_id + ) or not description.supported_fn(controller, obj_id): + return + + entity = UnifiSensorEntity(obj_id, controller, description) + if event == ItemEvent.ADDED: + async_add_entities([entity]) + return + entities.append(entity) + + for obj_id in api_handler: + async_create_entity(ItemEvent.CHANGED, obj_id) + async_add_entities(entities) + + api_handler.subscribe(async_create_entity, ItemEvent.ADDED) + + for description in ENTITY_DESCRIPTIONS: + async_load_entities(description) + + +class UnifiSensorEntity(SensorEntity, Generic[_HandlerT, _DataT]): + """Base representation of a UniFi switch.""" + + entity_description: UnifiEntityDescription[_HandlerT, _DataT] + _attr_should_poll = False + + def __init__( + self, + obj_id: str, + controller: UniFiController, + description: UnifiEntityDescription[_HandlerT, _DataT], ) -> None: - """Update the values of the controller.""" - if controller.option_allow_bandwidth_sensors: - add_bandwidth_entities(controller, async_add_entities, clients) + """Set up UniFi switch entity.""" + self._obj_id = obj_id + self.controller = controller + self.entity_description = description - if controller.option_allow_uptime_sensors: - add_uptime_entities(controller, async_add_entities, clients) + self._removed = False - for signal in (controller.signal_update, controller.signal_options_update): - config_entry.async_on_unload( - async_dispatcher_connect(hass, signal, items_added) + self._attr_available = description.available_fn(controller, obj_id) + self._attr_device_info = description.device_info_fn(controller.api, obj_id) + self._attr_unique_id = description.unique_id_fn(obj_id) + + obj = description.object_fn(controller.api, obj_id) + self._attr_native_value = description.value_fn(controller, obj) + self._attr_name = description.name_fn(obj) + + async def async_added_to_hass(self) -> None: + """Register callbacks.""" + description = self.entity_description + handler = description.api_handler_fn(self.controller.api) + self.async_on_remove( + handler.subscribe( + self.async_signalling_callback, + ) + ) + self.async_on_remove( + async_dispatcher_connect( + self.hass, + self.controller.signal_reachable, + self.async_signal_reachable_callback, + ) + ) + self.async_on_remove( + async_dispatcher_connect( + self.hass, + self.controller.signal_options_update, + self.options_updated, + ) + ) + self.async_on_remove( + async_dispatcher_connect( + self.hass, + self.controller.signal_remove, + self.remove_item, + ) ) - items_added() - - -@callback -def add_bandwidth_entities(controller, async_add_entities, clients): - """Add new sensor entities from the controller.""" - sensors = [] - - for mac in clients: - for sensor_class in (UniFiRxBandwidthSensor, UniFiTxBandwidthSensor): - if mac in controller.entities[DOMAIN][sensor_class.TYPE]: - continue - - client = controller.api.clients[mac] - sensors.append(sensor_class(client, controller)) - - async_add_entities(sensors) - - -@callback -def add_uptime_entities(controller, async_add_entities, clients): - """Add new sensor entities from the controller.""" - sensors = [] - - for mac in clients: - if mac in controller.entities[DOMAIN][UniFiUpTimeSensor.TYPE]: - continue - - client = controller.api.clients[mac] - sensors.append(UniFiUpTimeSensor(client, controller)) - - async_add_entities(sensors) - - -class UniFiBandwidthSensor(UniFiClient, SensorEntity): - """UniFi Network bandwidth sensor base class.""" - - DOMAIN = DOMAIN - - _attr_entity_category = EntityCategory.DIAGNOSTIC - _attr_native_unit_of_measurement = DATA_MEGABYTES - - @property - def name(self) -> str: - """Return the name of the client.""" - return f"{super().name} {self.TYPE.upper()}" - - async def options_updated(self) -> None: - """Config entry options are updated, remove entity if option is disabled.""" - if not self.controller.option_allow_bandwidth_sensors: - await self.remove_item({self.client.mac}) - - -class UniFiRxBandwidthSensor(UniFiBandwidthSensor): - """Receiving bandwidth sensor.""" - - TYPE = RX_SENSOR - - @property - def native_value(self) -> int: - """Return the state of the sensor.""" - if self._is_wired: - return self.client.wired_rx_bytes_r / 1000000 - return self.client.rx_bytes_r / 1000000 - - -class UniFiTxBandwidthSensor(UniFiBandwidthSensor): - """Transmitting bandwidth sensor.""" - - TYPE = TX_SENSOR - - @property - def native_value(self) -> int: - """Return the state of the sensor.""" - if self._is_wired: - return self.client.wired_tx_bytes_r / 1000000 - return self.client.tx_bytes_r / 1000000 - - -class UniFiUpTimeSensor(UniFiClient, SensorEntity): - """UniFi Network client uptime sensor.""" - - DOMAIN = DOMAIN - TYPE = UPTIME_SENSOR - - _attr_device_class = SensorDeviceClass.TIMESTAMP - _attr_entity_category = EntityCategory.DIAGNOSTIC - - def __init__(self, client, controller): - """Set up tracked client.""" - super().__init__(client, controller) - - self.last_updated_time = self.client.uptime - @callback - def async_update_callback(self) -> None: - """Update sensor when time has changed significantly. - - This will help avoid unnecessary updates to the state machine. - """ - update_state = True - - if self.client.uptime < 1000000000: - if self.client.uptime > self.last_updated_time: - update_state = False - else: - if self.client.uptime <= self.last_updated_time: - update_state = False - - self.last_updated_time = self.client.uptime - - if not update_state: + def async_signalling_callback(self, event: ItemEvent, obj_id: str) -> None: + """Update the switch state.""" + if event == ItemEvent.DELETED and obj_id == self._obj_id: + self.hass.async_create_task(self.remove_item({self._obj_id})) return - super().async_update_callback() + description = self.entity_description + if not description.supported_fn(self.controller, self._obj_id): + self.hass.async_create_task(self.remove_item({self._obj_id})) + return - @property - def name(self) -> str: - """Return the name of the client.""" - return f"{super().name} {self.TYPE.capitalize()}" + obj = description.object_fn(self.controller.api, self._obj_id) + if (value := description.value_fn(self.controller, obj)) != self.native_value: + self._attr_native_value = value + self._attr_available = description.available_fn(self.controller, self._obj_id) + self.async_write_ha_state() - @property - def native_value(self) -> datetime: - """Return the uptime of the client.""" - if self.client.uptime < 1000000000: - return dt_util.now() - timedelta(seconds=self.client.uptime) - return dt_util.utc_from_timestamp(float(self.client.uptime)) + @callback + def async_signal_reachable_callback(self) -> None: + """Call when controller connection state change.""" + self.async_signalling_callback(ItemEvent.ADDED, self._obj_id) async def options_updated(self) -> None: """Config entry options are updated, remove entity if option is disabled.""" - if not self.controller.option_allow_uptime_sensors: - await self.remove_item({self.client.mac}) + if not self.entity_description.allowed_fn(self.controller, self._obj_id): + await self.remove_item({self._obj_id}) + + async def remove_item(self, keys: set) -> None: + """Remove entity if object ID is part of set.""" + if self._obj_id not in keys or self._removed: + return + self._removed = True + if self.registry_entry: + er.async_get(self.hass).async_remove(self.entity_id) + else: + await self.async_remove(force_remove=True) diff --git a/homeassistant/components/unifi/strings.json b/homeassistant/components/unifi/strings.json index 8d6df90b704..02b64f3c50e 100644 --- a/homeassistant/components/unifi/strings.json +++ b/homeassistant/components/unifi/strings.json @@ -27,7 +27,7 @@ }, "options": { "abort": { - "integration_not_setup": "UniFi integration is not setup" + "integration_not_setup": "UniFi integration is not set up" }, "step": { "device_tracker": { diff --git a/homeassistant/components/unifi/switch.py b/homeassistant/components/unifi/switch.py index 3e98f41188a..44007e4c1a8 100644 --- a/homeassistant/components/unifi/switch.py +++ b/homeassistant/components/unifi/switch.py @@ -9,14 +9,20 @@ from __future__ import annotations import asyncio from collections.abc import Callable, Coroutine from dataclasses import dataclass -from typing import Any, Generic, TypeVar +from typing import Any, Generic, TypeVar, Union import aiounifi -from aiounifi.interfaces.api_handlers import CallbackType, ItemEvent, UnsubscribeType +from aiounifi.interfaces.api_handlers import ( + APIHandler, + CallbackType, + ItemEvent, + UnsubscribeType, +) from aiounifi.interfaces.clients import Clients from aiounifi.interfaces.dpi_restriction_groups import DPIRestrictionGroups from aiounifi.interfaces.outlets import Outlets from aiounifi.interfaces.ports import Ports +from aiounifi.models.api import APIItem from aiounifi.models.client import Client, ClientBlockRequest from aiounifi.models.device import ( DeviceSetOutletRelayRequest, @@ -51,8 +57,8 @@ from .controller import UniFiController CLIENT_BLOCKED = (EventKey.WIRED_CLIENT_BLOCKED, EventKey.WIRELESS_CLIENT_BLOCKED) CLIENT_UNBLOCKED = (EventKey.WIRED_CLIENT_UNBLOCKED, EventKey.WIRELESS_CLIENT_UNBLOCKED) -Data = TypeVar("Data") -Handler = TypeVar("Handler") +_DataT = TypeVar("_DataT", bound=Union[APIItem, Outlet, Port]) +_HandlerT = TypeVar("_HandlerT", bound=Union[APIHandler, Outlets, Ports]) Subscription = Callable[[CallbackType, ItemEvent], UnsubscribeType] @@ -157,25 +163,27 @@ async def async_poe_port_control_fn( @dataclass -class UnifiEntityLoader(Generic[Handler, Data]): +class UnifiEntityLoader(Generic[_HandlerT, _DataT]): """Validate and load entities from different UniFi handlers.""" allowed_fn: Callable[[UniFiController, str], bool] - api_handler_fn: Callable[[aiounifi.Controller], Handler] + api_handler_fn: Callable[[aiounifi.Controller], _HandlerT] available_fn: Callable[[UniFiController, str], bool] control_fn: Callable[[aiounifi.Controller, str, bool], Coroutine[Any, Any, None]] device_info_fn: Callable[[aiounifi.Controller, str], DeviceInfo] event_is_on: tuple[EventKey, ...] | None event_to_subscribe: tuple[EventKey, ...] | None - is_on_fn: Callable[[aiounifi.Controller, Data], bool] - name_fn: Callable[[Data], str | None] - object_fn: Callable[[aiounifi.Controller, str], Data] + is_on_fn: Callable[[aiounifi.Controller, _DataT], bool] + name_fn: Callable[[_DataT], str | None] + object_fn: Callable[[aiounifi.Controller, str], _DataT] supported_fn: Callable[[aiounifi.Controller, str], bool | None] unique_id_fn: Callable[[str], str] @dataclass -class UnifiEntityDescription(SwitchEntityDescription, UnifiEntityLoader[Handler, Data]): +class UnifiEntityDescription( + SwitchEntityDescription, UnifiEntityLoader[_HandlerT, _DataT] +): """Class describing UniFi switch entity.""" custom_subscribe: Callable[[aiounifi.Controller], Subscription] | None = None @@ -307,17 +315,17 @@ async def async_setup_entry( async_load_entities(description) -class UnifiSwitchEntity(SwitchEntity): +class UnifiSwitchEntity(SwitchEntity, Generic[_HandlerT, _DataT]): """Base representation of a UniFi switch.""" - entity_description: UnifiEntityDescription + entity_description: UnifiEntityDescription[_HandlerT, _DataT] _attr_should_poll = False def __init__( self, obj_id: str, controller: UniFiController, - description: UnifiEntityDescription, + description: UnifiEntityDescription[_HandlerT, _DataT], ) -> None: """Set up UniFi switch entity.""" self._obj_id = obj_id diff --git a/homeassistant/components/unifi/translations/en.json b/homeassistant/components/unifi/translations/en.json index 6e36e517e16..8621ec0aba2 100644 --- a/homeassistant/components/unifi/translations/en.json +++ b/homeassistant/components/unifi/translations/en.json @@ -27,7 +27,7 @@ }, "options": { "abort": { - "integration_not_setup": "UniFi integration is not setup" + "integration_not_setup": "UniFi integration is not set up" }, "step": { "client_control": { diff --git a/homeassistant/components/unifi/translations/no.json b/homeassistant/components/unifi/translations/no.json index 339f39ff537..10edfef89ed 100644 --- a/homeassistant/components/unifi/translations/no.json +++ b/homeassistant/components/unifi/translations/no.json @@ -27,7 +27,7 @@ }, "options": { "abort": { - "integration_not_setup": "UniFi-integrasjon er ikke konfigurert" + "integration_not_setup": "UniFi-integrasjon er ikke satt opp" }, "step": { "client_control": { diff --git a/homeassistant/components/unifi/translations/sk.json b/homeassistant/components/unifi/translations/sk.json index e19794c773a..c5bb3cb2451 100644 --- a/homeassistant/components/unifi/translations/sk.json +++ b/homeassistant/components/unifi/translations/sk.json @@ -1,6 +1,8 @@ { "config": { "abort": { + "already_configured": "Str\u00e1nka siete UniFi je u\u017e nakonfigurovan\u00e1", + "configuration_updated": "Aktualizovan\u00e1 konfigur\u00e1cia", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { @@ -15,9 +17,63 @@ "host": "Hostite\u013e", "password": "Heslo", "port": "Port", + "site": "ID lokality", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno", "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" + }, + "title": "Nastavenie siete UniFi" + } + } + }, + "options": { + "abort": { + "integration_not_setup": "Integr\u00e1cia UniFi nie je nastaven\u00e1" + }, + "step": { + "client_control": { + "data": { + "block_client": "Klienti s riaden\u00fdm pr\u00edstupom k sieti", + "dpi_restrictions": "Povoli\u0165 kontrolu skup\u00edn obmedzen\u00ed DPI", + "poe_clients": "Povoli\u0165 POE kontrolu klientov" + }, + "description": "Nakonfigurujte ovl\u00e1dacie prvky klienta \n\n Vytvorte prep\u00edna\u010de pre s\u00e9riov\u00e9 \u010d\u00edsla, pre ktor\u00e9 chcete riadi\u0165 sie\u0165ov\u00fd pr\u00edstup.", + "title": "Mo\u017enosti siete UniFi 2/3" + }, + "device_tracker": { + "data": { + "detection_time": "\u010cas v sekund\u00e1ch od posledn\u00e9ho videnia a\u017e po zv\u00e1\u017eenie pre\u010d", + "ignore_wired_bug": "Zak\u00e1zanie logiky chyby k\u00e1blovej siete UniFi", + "ssid_filter": "Vyberte SSID na sledovanie bezdr\u00f4tov\u00fdch klientov", + "track_clients": "Sledovanie klientov siete", + "track_devices": "Sledovanie sie\u0165ov\u00fdch zariaden\u00ed (zariadenia Ubiquiti)", + "track_wired_clients": "Zahrn\u00fa\u0165 klientov pripojen\u00fdch k\u00e1blom" + }, + "description": "Konfigur\u00e1cia sledovania zariadenia", + "title": "Mo\u017enosti siete UniFi 1/3" + }, + "init": { + "data": { + "few": "Pr\u00e1zdnych", + "many": "Pr\u00e1zdnych", + "one": "Pr\u00e1zdny", + "other": "Pr\u00e1zdny" } + }, + "simple_options": { + "data": { + "block_client": "Klienti s riaden\u00fdm pr\u00edstupom k sieti", + "track_clients": "Sledovanie klientov siete", + "track_devices": "Sledovanie sie\u0165ov\u00fdch zariaden\u00ed (zariadenia Ubiquiti)" + }, + "description": "Nakonfigurujte integr\u00e1ciu siete UniFi" + }, + "statistics_sensors": { + "data": { + "allow_bandwidth_sensors": "Senzory vyu\u017eitia \u0161\u00edrky p\u00e1sma pre sie\u0165ov\u00fdch klientov", + "allow_uptime_sensors": "Sn\u00edma\u010de dostupnosti pre sie\u0165ov\u00fdch klientov" + }, + "description": "Konfigur\u00e1cia sn\u00edma\u010dov \u0161tatistiky", + "title": "Mo\u017enosti siete UniFi 3/3" } } } diff --git a/homeassistant/components/unifi/translations/zh-Hant.json b/homeassistant/components/unifi/translations/zh-Hant.json index c6cb4724697..7f18d212cd7 100644 --- a/homeassistant/components/unifi/translations/zh-Hant.json +++ b/homeassistant/components/unifi/translations/zh-Hant.json @@ -61,10 +61,10 @@ }, "statistics_sensors": { "data": { - "allow_bandwidth_sensors": "\u7db2\u8def\u5ba2\u6236\u7aef\u983b\u5bec\u7528\u91cf\u611f\u61c9\u5668", + "allow_bandwidth_sensors": "\u7db2\u8def\u5ba2\u6236\u7aef\u983b\u5bec\u7528\u91cf\u611f\u6e2c\u5668", "allow_uptime_sensors": "\u7db2\u8def\u5ba2\u6236\u7aef\u4e0a\u7dda\u6642\u9593\u611f\u6e2c\u5668" }, - "description": "\u8a2d\u5b9a\u7d71\u8a08\u6578\u64da\u611f\u61c9\u5668", + "description": "\u8a2d\u5b9a\u7d71\u8a08\u6578\u64da\u611f\u6e2c\u5668", "title": "UniFi \u7db2\u8def\u9078\u9805 3/3" } } diff --git a/homeassistant/components/unifi/unifi_client.py b/homeassistant/components/unifi/unifi_client.py index 82aece81b6d..6c13bb97852 100644 --- a/homeassistant/components/unifi/unifi_client.py +++ b/homeassistant/components/unifi/unifi_client.py @@ -54,5 +54,5 @@ class UniFiClient(UniFiClientBase): return DeviceInfo( connections={(CONNECTION_NETWORK_MAC, self.client.mac)}, default_manufacturer=self.client.oui, - default_name=self.name, + default_name=self.client.name or self.client.hostname, ) diff --git a/homeassistant/components/unifi/update.py b/homeassistant/components/unifi/update.py index 5f26cba57cd..6cff6b7932d 100644 --- a/homeassistant/components/unifi/update.py +++ b/homeassistant/components/unifi/update.py @@ -1,33 +1,99 @@ """Update entities for Ubiquiti network devices.""" from __future__ import annotations +from collections.abc import Callable, Coroutine +from dataclasses import dataclass import logging -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, Generic +import aiounifi from aiounifi.interfaces.api_handlers import ItemEvent -from aiounifi.models.device import DeviceUpgradeRequest +from aiounifi.interfaces.devices import Devices +from aiounifi.models.device import Device, DeviceUpgradeRequest from homeassistant.components.update import ( - DOMAIN, UpdateDeviceClass, UpdateEntity, + UpdateEntityDescription, UpdateEntityFeature, ) from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC -from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import ATTR_MANUFACTURER, DOMAIN as UNIFI_DOMAIN +from .entity import DataT, HandlerT, UnifiEntity, UnifiEntityDescription if TYPE_CHECKING: from .controller import UniFiController LOGGER = logging.getLogger(__name__) -DEVICE_UPDATE = "device_update" + +@callback +def async_device_available_fn(controller: UniFiController, obj_id: str) -> bool: + """Check if device is available.""" + device = controller.api.devices[obj_id] + return controller.available and not device.disabled + + +async def async_device_control_fn(api: aiounifi.Controller, obj_id: str) -> None: + """Control upgrade of device.""" + await api.request(DeviceUpgradeRequest.create(obj_id)) + + +@callback +def async_device_device_info_fn(api: aiounifi.Controller, obj_id: str) -> DeviceInfo: + """Create device registry entry for device.""" + device = api.devices[obj_id] + return DeviceInfo( + connections={(CONNECTION_NETWORK_MAC, device.mac)}, + manufacturer=ATTR_MANUFACTURER, + model=device.model, + name=device.name or None, + sw_version=device.version, + hw_version=str(device.board_revision), + ) + + +@dataclass +class UnifiEntityLoader(Generic[HandlerT, DataT]): + """Validate and load entities from different UniFi handlers.""" + + control_fn: Callable[[aiounifi.Controller, str], Coroutine[Any, Any, None]] + state_fn: Callable[[aiounifi.Controller, DataT], bool] + + +@dataclass +class UnifiUpdateEntityDescription( + UpdateEntityDescription, + UnifiEntityDescription[HandlerT, DataT], + UnifiEntityLoader[HandlerT, DataT], +): + """Class describing UniFi update entity.""" + + +ENTITY_DESCRIPTIONS: tuple[UnifiUpdateEntityDescription, ...] = ( + UnifiUpdateEntityDescription[Devices, Device]( + key="Upgrade device", + device_class=UpdateDeviceClass.FIRMWARE, + has_entity_name=True, + allowed_fn=lambda controller, obj_id: True, + api_handler_fn=lambda api: api.devices, + available_fn=async_device_available_fn, + control_fn=async_device_control_fn, + device_info_fn=async_device_device_info_fn, + event_is_on=None, + event_to_subscribe=None, + name_fn=lambda device: None, + object_fn=lambda api, obj_id: api.devices[obj_id], + state_fn=lambda api, device: device.state == 4, + supported_fn=lambda controller, obj_id: True, + unique_id_fn=lambda controller, obj_id: f"device_update-{obj_id}", + ), +) async def async_setup_entry( @@ -36,88 +102,67 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up update entities for UniFi Network integration.""" - controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id] - controller.entities[DOMAIN] = {DEVICE_UPDATE: set()} + controller: UniFiController = hass.data[UNIFI_DOMAIN][config_entry.entry_id] @callback - def async_add_update_entity(_: ItemEvent, obj_id: str) -> None: - """Add new device update entities from the controller.""" - async_add_entities([UnifiDeviceUpdateEntity(obj_id, controller)]) + def async_load_entities(description: UnifiUpdateEntityDescription) -> None: + """Load and subscribe to UniFi devices.""" + entities: list[UpdateEntity] = [] + api_handler = description.api_handler_fn(controller.api) - controller.api.devices.subscribe(async_add_update_entity, ItemEvent.ADDED) + @callback + def async_create_entity(event: ItemEvent, obj_id: str) -> None: + """Create UniFi entity.""" + if not description.allowed_fn( + controller, obj_id + ) or not description.supported_fn(controller.api, obj_id): + return - for device_id in controller.api.devices: - async_add_update_entity(ItemEvent.ADDED, device_id) + entity = UnifiDeviceUpdateEntity(obj_id, controller, description) + if event == ItemEvent.ADDED: + async_add_entities([entity]) + return + entities.append(entity) + + for obj_id in api_handler: + async_create_entity(ItemEvent.CHANGED, obj_id) + async_add_entities(entities) + + api_handler.subscribe(async_create_entity, ItemEvent.ADDED) + + for description in ENTITY_DESCRIPTIONS: + async_load_entities(description) -class UnifiDeviceUpdateEntity(UpdateEntity): - """Update entity for a UniFi network infrastructure device.""" +class UnifiDeviceUpdateEntity(UnifiEntity[HandlerT, DataT], UpdateEntity): + """Representation of a UniFi device update entity.""" - DOMAIN = DOMAIN - TYPE = DEVICE_UPDATE - _attr_device_class = UpdateDeviceClass.FIRMWARE - _attr_has_entity_name = True - - def __init__(self, obj_id: str, controller: UniFiController) -> None: - """Set up device update entity.""" - controller.entities[DOMAIN][DEVICE_UPDATE].add(obj_id) - self.controller = controller - self._obj_id = obj_id - self._attr_unique_id = f"{self.TYPE}-{obj_id}" + entity_description: UnifiUpdateEntityDescription[HandlerT, DataT] + @callback + def async_initiate_state(self) -> None: + """Initiate entity state.""" self._attr_supported_features = UpdateEntityFeature.PROGRESS - if controller.site_role == "admin": + if self.controller.site_role == "admin": self._attr_supported_features |= UpdateEntityFeature.INSTALL - device = controller.api.devices[obj_id] - self._attr_available = controller.available and not device.disabled - self._attr_in_progress = device.state == 4 - self._attr_installed_version = device.version - self._attr_latest_version = device.upgrade_to_firmware or device.version - - self._attr_device_info = DeviceInfo( - connections={(CONNECTION_NETWORK_MAC, obj_id)}, - manufacturer=ATTR_MANUFACTURER, - model=device.model, - name=device.name or None, - sw_version=device.version, - hw_version=device.board_revision, - ) - - async def async_added_to_hass(self) -> None: - """Entity created.""" - self.async_on_remove( - self.controller.api.devices.subscribe(self.async_signalling_callback) - ) - self.async_on_remove( - async_dispatcher_connect( - self.hass, - self.controller.signal_reachable, - self.async_signal_reachable_callback, - ) - ) - - async def async_will_remove_from_hass(self) -> None: - """Disconnect object when removed.""" - self.controller.entities[DOMAIN][DEVICE_UPDATE].remove(self._obj_id) - - @callback - def async_signalling_callback(self, event: ItemEvent, obj_id: str) -> None: - """Object has new event.""" - device = self.controller.api.devices[self._obj_id] - self._attr_available = self.controller.available and not device.disabled - self._attr_in_progress = device.state == 4 - self._attr_installed_version = device.version - self._attr_latest_version = device.upgrade_to_firmware or device.version - self.async_write_ha_state() - - @callback - def async_signal_reachable_callback(self) -> None: - """Call when controller connection state change.""" - self.async_signalling_callback(ItemEvent.ADDED, self._obj_id) + self.async_update_state(ItemEvent.ADDED, self._obj_id) async def async_install( self, version: str | None, backup: bool, **kwargs: Any ) -> None: """Install an update.""" - await self.controller.api.request(DeviceUpgradeRequest.create(self._obj_id)) + await self.entity_description.control_fn(self.controller.api, self._obj_id) + + @callback + def async_update_state(self, event: ItemEvent, obj_id: str) -> None: + """Update entity state. + + Update in_progress, installed_version and latest_version. + """ + description = self.entity_description + + obj = description.object_fn(self.controller.api, self._obj_id) + self._attr_in_progress = description.state_fn(self.controller.api, obj) + self._attr_installed_version = obj.version + self._attr_latest_version = obj.upgrade_to_firmware or obj.version diff --git a/homeassistant/components/unifiprotect/__init__.py b/homeassistant/components/unifiprotect/__init__.py index 4e659d39cc5..1e9f168820c 100644 --- a/homeassistant/components/unifiprotect/__init__.py +++ b/homeassistant/components/unifiprotect/__init__.py @@ -27,6 +27,7 @@ from .const import ( from .data import ProtectData, async_ufp_instance_for_config_entry_ids from .discovery import async_start_discovery from .migrate import async_migrate_data +from .repairs import async_create_repairs from .services import async_cleanup_services, async_setup_services from .utils import ( _async_unifi_mac_from_hass, @@ -121,6 +122,7 @@ async def _async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, data_service: ProtectData ) -> None: await async_migrate_data(hass, entry, data_service.api) + await async_create_repairs(hass, entry, data_service.api) await data_service.async_setup() if not data_service.last_update_success: diff --git a/homeassistant/components/unifiprotect/const.py b/homeassistant/components/unifiprotect/const.py index df4d8f77d99..3bc689666c7 100644 --- a/homeassistant/components/unifiprotect/const.py +++ b/homeassistant/components/unifiprotect/const.py @@ -48,7 +48,10 @@ DEVICES_WITH_ENTITIES = DEVICES_THAT_ADOPT | {ModelType.NVR} DEVICES_FOR_SUBSCRIBE = DEVICES_WITH_ENTITIES | {ModelType.EVENT} MIN_REQUIRED_PROTECT_V = Version("1.20.0") -OUTDATED_LOG_MESSAGE = "You are running v%s of UniFi Protect. Minimum required version is v%s. Please upgrade UniFi Protect and then retry" +OUTDATED_LOG_MESSAGE = ( + "You are running v%s of UniFi Protect. Minimum required version is v%s. Please" + " upgrade UniFi Protect and then retry" +) TYPE_EMPTY_VALUE = "" @@ -63,6 +66,7 @@ PLATFORMS = [ Platform.SELECT, Platform.SENSOR, Platform.SWITCH, + Platform.TEXT, ] DISPATCH_ADD = "add_device" diff --git a/homeassistant/components/unifiprotect/data.py b/homeassistant/components/unifiprotect/data.py index 20b5747a342..5db0941549b 100644 --- a/homeassistant/components/unifiprotect/data.py +++ b/homeassistant/components/unifiprotect/data.py @@ -240,7 +240,8 @@ class ProtectData: # alert user viewport needs restart so voice clients can get new options elif len(self.api.bootstrap.viewers) > 0 and isinstance(obj, Liveview): _LOGGER.warning( - "Liveviews updated. Restart Home Assistant to update Viewport select options" + "Liveviews updated. Restart Home Assistant to update Viewport select" + " options" ) @callback diff --git a/homeassistant/components/unifiprotect/migrate.py b/homeassistant/components/unifiprotect/migrate.py index 893ca3e458a..308e9903f89 100644 --- a/homeassistant/components/unifiprotect/migrate.py +++ b/homeassistant/components/unifiprotect/migrate.py @@ -146,7 +146,10 @@ async def async_migrate_device_ids( registry.async_update_entity(entity.entity_id, new_unique_id=new_unique_id) except ValueError as err: _LOGGER.warning( - "Could not migrate entity %s (old unique_id: %s, new unique_id: %s): %s", + ( + "Could not migrate entity %s (old unique_id: %s, new unique_id:" + " %s): %s" + ), entity.entity_id, entity.unique_id, new_unique_id, diff --git a/homeassistant/components/unifiprotect/models.py b/homeassistant/components/unifiprotect/models.py index 01d4820e9a9..b7913567504 100644 --- a/homeassistant/components/unifiprotect/models.py +++ b/homeassistant/components/unifiprotect/models.py @@ -83,10 +83,22 @@ class ProtectEventMixin(ProtectRequiredKeysMixin[T]): if value: event = self.get_event_obj(obj) value = event is not None + if not value: + _LOGGER.debug("%s (%s): missing event", self.name, obj.mac) if event is not None and self.ufp_smart_type is not None: value = self.ufp_smart_type in event.smart_detect_types + if not value: + _LOGGER.debug( + "%s (%s): %s not in %s", + self.name, + obj.mac, + self.ufp_smart_type, + event.smart_detect_types, + ) + if value: + _LOGGER.debug("%s (%s): value is on", self.name, obj.mac) return value diff --git a/homeassistant/components/unifiprotect/number.py b/homeassistant/components/unifiprotect/number.py index 3b80532a32c..f8575f39d86 100644 --- a/homeassistant/components/unifiprotect/number.py +++ b/homeassistant/components/unifiprotect/number.py @@ -14,7 +14,7 @@ from pyunifiprotect.data import ( from homeassistant.components.number import NumberEntity, NumberEntityDescription from homeassistant.config_entries import ConfigEntry -from homeassistant.const import PERCENTAGE, TIME_SECONDS +from homeassistant.const import PERCENTAGE, UnitOfTime from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import EntityCategory @@ -124,7 +124,7 @@ LIGHT_NUMBERS: tuple[ProtectNumberEntityDescription, ...] = ( name="Auto-shutoff Duration", icon="mdi:camera-timer", entity_category=EntityCategory.CONFIG, - native_unit_of_measurement=TIME_SECONDS, + native_unit_of_measurement=UnitOfTime.SECONDS, ufp_min=15, ufp_max=900, ufp_step=15, @@ -158,7 +158,7 @@ DOORLOCK_NUMBERS: tuple[ProtectNumberEntityDescription, ...] = ( name="Auto-lock Timeout", icon="mdi:walk", entity_category=EntityCategory.CONFIG, - native_unit_of_measurement=TIME_SECONDS, + native_unit_of_measurement=UnitOfTime.SECONDS, ufp_min=0, ufp_max=3600, ufp_step=15, diff --git a/homeassistant/components/unifiprotect/repairs.py b/homeassistant/components/unifiprotect/repairs.py index 98846113e5e..72f297c8c25 100644 --- a/homeassistant/components/unifiprotect/repairs.py +++ b/homeassistant/components/unifiprotect/repairs.py @@ -2,20 +2,93 @@ from __future__ import annotations -from typing import cast +from functools import partial +from itertools import chain +import logging +from typing import Any, cast from pyunifiprotect import ProtectApiClient import voluptuous as vol from homeassistant import data_entry_flow +from homeassistant.components.automation import ( + EVENT_AUTOMATION_RELOADED, + automations_with_entity, +) from homeassistant.components.repairs import ConfirmRepairFlow, RepairsFlow +from homeassistant.components.script import scripts_with_entity from homeassistant.config_entries import ConfigEntry +from homeassistant.const import Platform from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers.issue_registry import async_get as async_get_issue_registry +from homeassistant.helpers import entity_registry as er, issue_registry as ir +from homeassistant.helpers.issue_registry import ( + IssueSeverity, + async_get as async_get_issue_registry, +) -from .const import CONF_ALLOW_EA +from .const import CONF_ALLOW_EA, DOMAIN from .utils import async_create_api_client +_LOGGER = logging.getLogger(__name__) + + +async def async_create_repairs( + hass: HomeAssistant, entry: ConfigEntry, protect: ProtectApiClient +) -> None: + """Create any additional repairs for deprecations.""" + + await _deprecate_smart_sensor(hass, entry, protect) + entry.async_on_unload( + hass.bus.async_listen( + EVENT_AUTOMATION_RELOADED, + partial(_deprecate_smart_sensor, hass, entry, protect), + ) + ) + + +async def _deprecate_smart_sensor( + hass: HomeAssistant, + entry: ConfigEntry, + protect: ProtectApiClient, + *args: Any, + **kwargs: Any, +) -> None: + entity_registry = er.async_get(hass) + automations: dict[str, list[str]] = {} + scripts: dict[str, list[str]] = {} + for entity in er.async_entries_for_config_entry(entity_registry, entry.entry_id): + if ( + entity.domain == Platform.SENSOR + and entity.disabled_by is None + and "detected_object" in entity.unique_id + ): + entity_automations = automations_with_entity(hass, entity.entity_id) + entity_scripts = scripts_with_entity(hass, entity.entity_id) + if entity_automations: + automations[entity.entity_id] = entity_automations + if entity_scripts: + scripts[entity.entity_id] = entity_scripts + + if automations or scripts: + items = sorted( + set( + chain.from_iterable(list(automations.values()) + list(scripts.values())) + ) + ) + ir.async_create_issue( + hass, + DOMAIN, + "deprecate_smart_sensor", + is_fixable=False, + breaks_in_ha_version="2023.3.0", + severity=IssueSeverity.WARNING, + translation_key="deprecate_smart_sensor", + translation_placeholders={"items": "* `" + "`\n* `".join(items) + "`\n"}, + ) + else: + _LOGGER.debug("No found usages of Detected Object sensor") + ir.async_delete_issue(hass, DOMAIN, "deprecate_smart_sensor") + class EAConfirm(RepairsFlow): """Handler for an issue fixing flow.""" @@ -62,7 +135,7 @@ class EAConfirm(RepairsFlow): if await nvr.get_is_prerelease(): return await self.async_step_confirm() await self.hass.config_entries.async_reload(self._entry.entry_id) - return self.async_create_entry(title="", data={}) + return self.async_create_entry(data={}) async def async_step_confirm( self, user_input: dict[str, str] | None = None @@ -72,7 +145,7 @@ class EAConfirm(RepairsFlow): options = dict(self._entry.options) options[CONF_ALLOW_EA] = True self.hass.config_entries.async_update_entry(self._entry, options=options) - return self.async_create_entry(title="", data={}) + return self.async_create_entry(data={}) placeholders = self._async_get_placeholders() return self.async_show_form( diff --git a/homeassistant/components/unifiprotect/select.py b/homeassistant/components/unifiprotect/select.py index e398e6692b0..27c45d36b7c 100644 --- a/homeassistant/components/unifiprotect/select.py +++ b/homeassistant/components/unifiprotect/select.py @@ -32,7 +32,11 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import ATTR_ENTITY_ID from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import HomeAssistantError -from homeassistant.helpers import config_validation as cv, entity_platform +from homeassistant.helpers import ( + config_validation as cv, + entity_platform, + issue_registry as ir, +) from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import EntityCategory from homeassistant.util.dt import utcnow @@ -430,6 +434,20 @@ class ProtectSelects(ProtectDeviceEntity, SelectEntity): async def async_set_doorbell_message(self, message: str, duration: str) -> None: """Set LCD Message on Doorbell display.""" + ir.async_create_issue( + self.hass, + DOMAIN, + "deprecated_service_set_doorbell_message", + breaks_in_ha_version="2023.3.0", + is_fixable=True, + is_persistent=True, + severity=ir.IssueSeverity.WARNING, + translation_placeholders={ + "link": "https://www.home-assistant.io/integrations/text#service-textset_value" + }, + translation_key="deprecated_service_set_doorbell_message", + ) + if self.entity_description.device_class != DEVICE_CLASS_LCD_MESSAGE: raise HomeAssistantError("Not a doorbell text select entity") diff --git a/homeassistant/components/unifiprotect/sensor.py b/homeassistant/components/unifiprotect/sensor.py index 25f8e814536..1851b1ea776 100644 --- a/homeassistant/components/unifiprotect/sensor.py +++ b/homeassistant/components/unifiprotect/sensor.py @@ -26,15 +26,14 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - DATA_BYTES, - DATA_RATE_BYTES_PER_SECOND, - DATA_RATE_MEGABITS_PER_SECOND, - ELECTRIC_POTENTIAL_VOLT, LIGHT_LUX, PERCENTAGE, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, - TEMP_CELSIUS, - TIME_SECONDS, + UnitOfDataRate, + UnitOfElectricPotential, + UnitOfInformation, + UnitOfTemperature, + UnitOfTime, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect @@ -55,7 +54,6 @@ from .utils import async_dispatch_id as _ufpd, async_get_light_motion_current _LOGGER = logging.getLogger(__name__) OBJECT_TYPE_NONE = "none" DEVICE_CLASS_DETECTION = "unifiprotect__detection" -DEVICE_CLASS_LICENSE_PLATE = "unifiprotect__license_plate" @dataclass @@ -141,7 +139,8 @@ ALL_DEVICES_SENSORS: tuple[ProtectSensorEntityDescription, ...] = ( ProtectSensorEntityDescription( key="phy_rate", name="Link Speed", - native_unit_of_measurement=DATA_RATE_MEGABITS_PER_SECOND, + device_class=SensorDeviceClass.DATA_RATE, + native_unit_of_measurement=UnitOfDataRate.MEGABITS_PER_SECOND, entity_category=EntityCategory.DIAGNOSTIC, entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, @@ -173,7 +172,8 @@ CAMERA_SENSORS: tuple[ProtectSensorEntityDescription, ...] = ( ProtectSensorEntityDescription( key="storage_used", name="Storage Used", - native_unit_of_measurement=DATA_BYTES, + native_unit_of_measurement=UnitOfInformation.BYTES, + device_class=SensorDeviceClass.DATA_SIZE, entity_category=EntityCategory.DIAGNOSTIC, state_class=SensorStateClass.MEASUREMENT, ufp_value="stats.storage.used", @@ -181,7 +181,8 @@ CAMERA_SENSORS: tuple[ProtectSensorEntityDescription, ...] = ( ProtectSensorEntityDescription( key="write_rate", name="Disk Write Rate", - native_unit_of_measurement=DATA_RATE_BYTES_PER_SECOND, + device_class=SensorDeviceClass.DATA_RATE, + native_unit_of_measurement=UnitOfDataRate.BYTES_PER_SECOND, entity_category=EntityCategory.DIAGNOSTIC, state_class=SensorStateClass.MEASUREMENT, ufp_value="stats.storage.rate_per_second", @@ -191,7 +192,7 @@ CAMERA_SENSORS: tuple[ProtectSensorEntityDescription, ...] = ( key="voltage", name="Voltage", device_class=SensorDeviceClass.VOLTAGE, - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, entity_category=EntityCategory.DIAGNOSTIC, state_class=SensorStateClass.MEASUREMENT, ufp_value="voltage", @@ -269,7 +270,8 @@ CAMERA_DISABLED_SENSORS: tuple[ProtectSensorEntityDescription, ...] = ( ProtectSensorEntityDescription( key="stats_rx", name="Received Data", - native_unit_of_measurement=DATA_BYTES, + native_unit_of_measurement=UnitOfInformation.BYTES, + device_class=SensorDeviceClass.DATA_SIZE, entity_registry_enabled_default=False, entity_category=EntityCategory.DIAGNOSTIC, state_class=SensorStateClass.TOTAL_INCREASING, @@ -278,7 +280,8 @@ CAMERA_DISABLED_SENSORS: tuple[ProtectSensorEntityDescription, ...] = ( ProtectSensorEntityDescription( key="stats_tx", name="Transferred Data", - native_unit_of_measurement=DATA_BYTES, + native_unit_of_measurement=UnitOfInformation.BYTES, + device_class=SensorDeviceClass.DATA_SIZE, entity_registry_enabled_default=False, entity_category=EntityCategory.DIAGNOSTIC, state_class=SensorStateClass.TOTAL_INCREASING, @@ -317,7 +320,7 @@ SENSE_SENSORS: tuple[ProtectSensorEntityDescription, ...] = ( ProtectSensorEntityDescription( key="temperature_level", name="Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ufp_value="stats.temperature.value", @@ -479,7 +482,7 @@ NVR_SENSORS: tuple[ProtectSensorEntityDescription, ...] = ( ProtectSensorEntityDescription[NVR]( key="record_capacity", name="Recording Capacity", - native_unit_of_measurement=TIME_SECONDS, + native_unit_of_measurement=UnitOfTime.SECONDS, icon="mdi:record-rec", entity_category=EntityCategory.DIAGNOSTIC, state_class=SensorStateClass.MEASUREMENT, @@ -501,7 +504,7 @@ NVR_DISABLED_SENSORS: tuple[ProtectSensorEntityDescription, ...] = ( ProtectSensorEntityDescription( key="cpu_temperature", name="CPU Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, entity_registry_enabled_default=False, entity_category=EntityCategory.DIAGNOSTIC, @@ -534,11 +537,11 @@ EVENT_SENSORS: tuple[ProtectSensorEventEntityDescription, ...] = ( key="smart_obj_licenseplate", name="License Plate Detected", icon="mdi:car", - device_class=DEVICE_CLASS_LICENSE_PLATE, + translation_key="license_plate", + ufp_smart_type=SmartDetectObjectType.LICENSE_PLATE, ufp_value="is_smart_detected", ufp_required_field="can_detect_license_plate", ufp_event_obj="last_smart_detect_event", - ufp_smart_type=SmartDetectObjectType.LICENSE_PLATE, ), ) diff --git a/homeassistant/components/unifiprotect/strings.json b/homeassistant/components/unifiprotect/strings.json index abac7701279..2c0b894746e 100644 --- a/homeassistant/components/unifiprotect/strings.json +++ b/homeassistant/components/unifiprotect/strings.json @@ -24,7 +24,7 @@ }, "discovery_confirm": { "title": "UniFi Protect Discovered", - "description": "Do you want to setup {name} ({ip_address})? [%key:component::unifiprotect::config::step::user::description%]", + "description": "Do you want to set up {name} ({ip_address})? [%key:component::unifiprotect::config::step::user::description%]", "data": { "username": "[%key:common::config_flow::data::username%]", "password": "[%key:common::config_flow::data::password%]" @@ -75,6 +75,30 @@ "ea_setup_failed": { "title": "Setup error using Early Access version", "description": "You are using v{version} of UniFi Protect which is an Early Access version. An unrecoverable error occurred while trying to load the integration. Please [downgrade to a stable version](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) of UniFi Protect to continue using the integration.\n\nError: {error}" + }, + "deprecate_smart_sensor": { + "title": "Smart Detection Sensor Deprecated", + "description": "The unified \"Detected Object\" sensor for smart detections is now deprecated. It has been replaced with individual smart detection binary sensors for each smart detection type.\n\nBelow are the detected automations or scripts that use one or more of the deprecated entities:\n{items}\nThe above list may be incomplete and it does not include any template usages inside of dashboards. Please update any templates, automations or scripts accordingly." + }, + "deprecated_service_set_doorbell_message": { + "title": "set_doorbell_message is Deprecated", + "fix_flow": { + "step": { + "confirm": { + "title": "set_doorbell_message is Deprecated", + "description": "The `unifiprotect.set_doorbell_message` service is deprecated in favor of the new Doorbell Text entity added to each Doorbell device. It will be removed in v2023.3.0. Please update to use the [`text.set_value` service]({link})." + } + } + } + } + }, + "entity": { + "sensor": { + "license_plate": { + "state": { + "none": "Clear" + } + } } } } diff --git a/homeassistant/components/unifiprotect/strings.sensor.json b/homeassistant/components/unifiprotect/strings.sensor.json deleted file mode 100644 index ccb16e2d445..00000000000 --- a/homeassistant/components/unifiprotect/strings.sensor.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "state": { - "unifiprotect__license_plate": { - "none": "Clear" - } - } -} diff --git a/homeassistant/components/unifiprotect/text.py b/homeassistant/components/unifiprotect/text.py new file mode 100644 index 00000000000..49df4e3d907 --- /dev/null +++ b/homeassistant/components/unifiprotect/text.py @@ -0,0 +1,107 @@ +"""Text entities for UniFi Protect.""" +from __future__ import annotations + +from dataclasses import dataclass + +from pyunifiprotect.data import ( + Camera, + DoorbellMessageType, + ProtectAdoptableDeviceModel, + ProtectModelWithId, +) + +from homeassistant.components.text import TextEntity, TextEntityDescription +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.entity import EntityCategory +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from .const import DISPATCH_ADOPT, DOMAIN +from .data import ProtectData +from .entity import ProtectDeviceEntity, async_all_device_entities +from .models import PermRequired, ProtectSetableKeysMixin, T +from .utils import async_dispatch_id as _ufpd + + +@dataclass +class ProtectTextEntityDescription(ProtectSetableKeysMixin[T], TextEntityDescription): + """Describes UniFi Protect Text entity.""" + + +def _get_doorbell_current(obj: Camera) -> str | None: + if obj.lcd_message is None: + return obj.api.bootstrap.nvr.doorbell_settings.default_message_text + return obj.lcd_message.text + + +async def _set_doorbell_message(obj: Camera, message: str) -> None: + await obj.set_lcd_text(DoorbellMessageType.CUSTOM_MESSAGE, text=message) + + +CAMERA: tuple[ProtectTextEntityDescription, ...] = ( + ProtectTextEntityDescription( + key="doorbell", + name="Doorbell", + entity_category=EntityCategory.CONFIG, + ufp_value_fn=_get_doorbell_current, + ufp_set_method_fn=_set_doorbell_message, + ufp_required_field="feature_flags.has_lcd_screen", + ufp_perm=PermRequired.WRITE, + ), +) + + +async def async_setup_entry( + hass: HomeAssistant, + entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up sensors for UniFi Protect integration.""" + data: ProtectData = hass.data[DOMAIN][entry.entry_id] + + async def _add_new_device(device: ProtectAdoptableDeviceModel) -> None: + entities = async_all_device_entities( + data, + ProtectDeviceText, + camera_descs=CAMERA, + ufp_device=device, + ) + async_add_entities(entities) + + entry.async_on_unload( + async_dispatcher_connect(hass, _ufpd(entry, DISPATCH_ADOPT), _add_new_device) + ) + + entities: list[ProtectDeviceEntity] = async_all_device_entities( + data, + ProtectDeviceText, + camera_descs=CAMERA, + ) + + async_add_entities(entities) + + +class ProtectDeviceText(ProtectDeviceEntity, TextEntity): + """A Ubiquiti UniFi Protect Sensor.""" + + entity_description: ProtectTextEntityDescription + + def __init__( + self, + data: ProtectData, + device: ProtectAdoptableDeviceModel, + description: ProtectTextEntityDescription, + ) -> None: + """Initialize an UniFi Protect sensor.""" + super().__init__(data, device, description) + + @callback + def _async_update_device_from_protect(self, device: ProtectModelWithId) -> None: + super()._async_update_device_from_protect(device) + self._attr_native_value = self.entity_description.get_ufp_value(self.device) + + async def async_set_value(self, value: str) -> None: + """Change the value.""" + + await self.entity_description.ufp_set(self.device, value) diff --git a/homeassistant/components/unifiprotect/translations/ca.json b/homeassistant/components/unifiprotect/translations/ca.json index 31a15c62848..633024d0024 100644 --- a/homeassistant/components/unifiprotect/translations/ca.json +++ b/homeassistant/components/unifiprotect/translations/ca.json @@ -41,17 +41,44 @@ } } }, + "entity": { + "sensor": { + "license_plate": { + "state": { + "none": "Lliure" + } + } + } + }, "issues": { + "deprecate_smart_sensor": { + "description": "El sensor 'unified' \"Detected Object\" per a deteccions intel\u00b7ligents est\u00e0 obsolet. S'ha substitu\u00eft per sensors binaris de detecci\u00f3 intel\u00b7ligent individuals per a cada tipus de detecci\u00f3.\n\nA continuaci\u00f3 es mostren les automatitzacions i 'scripts' que utilitzen aquestes entitats obsoletes:\n{items}\nLa llista de sobre pot estar incompleta, ja que no inclou les plantilles que s'utilitzen als panells d'usuari. Actualitza les plantilles, automatitzacions i 'scripts' necessaris.", + "title": "Sensor de detecci\u00f3 intel\u00b7ligent obsolet" + }, + "deprecated_service_set_doorbell_message": { + "fix_flow": { + "step": { + "confirm": { + "description": "El servei `unifiprotect.set_doorbell_message` est\u00e0 obsolet, ha canviat per la nova entitat Doorbell Text que s'afegeix a cada dispositiu Doorbell. S'eliminar\u00e0 a la versi\u00f3 2023.3.0. Actualitza-ho perqu\u00e8 utilitzi el [servei `text.set_value`]({link}).", + "title": "set_doorbell_message est\u00e0 obsolet" + } + } + }, + "title": "set_doorbell_message est\u00e0 obsolet" + }, "ea_setup_failed": { + "description": "Est\u00e0s utilitzant la versi\u00f3 v{version} d'UniFi Protect, que \u00e9s una versi\u00f3 d'acc\u00e9s anticipat. S'ha produ\u00eft un error irrecuperable en intentar carregar la integraci\u00f3. [Baixa a una versi\u00f3 estable](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) d'UniFi Protect per continuar utilitzant la integraci\u00f3. \n\n Error: {error}", "title": "Error de configuraci\u00f3 amb la versi\u00f3 d'acc\u00e9s anticipat" }, "ea_warning": { "fix_flow": { "step": { "confirm": { + "description": "Est\u00e0s segur que vols executar versions no admeses d'UniFi Protect? Aix\u00f2 pot fer que la integraci\u00f3 de Home Assistant s'espatlli.", "title": "v{version} \u00e9s una versi\u00f3 d'acc\u00e9s anticipat" }, "start": { + "description": "Est\u00e0s utilitzant la versi\u00f3 v{version} d'UniFi Protect, que \u00e9s una versi\u00f3 d'acc\u00e9s anticipat. [Les versions d'acc\u00e9s anticipat no s\u00f3n compatibles amb Home Assistant](https://www.home-assistant.io/integrations/unifiprotect#about-unifi-early-access) i es recomana tornar a una versi\u00f3 estable el m\u00e9s aviat com possible. \n\nEn enviar aquest formulari, o b\u00e9 [has baixat la versi\u00f3 d'UniFi Protect](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) o b\u00e9 acceptes executar una versi\u00f3 no compatible.", "title": "v{version} \u00e9s una versi\u00f3 d'acc\u00e9s anticipat" } } @@ -64,6 +91,7 @@ "init": { "data": { "all_updates": "M\u00e8triques en temps real (ALERTA: augmenta considerablement l'\u00fas de CPU)", + "allow_ea": "Permet versions d'acc\u00e9s anticipat de Protect (ADVERT\u00c8NCIA: aix\u00f2 marcar\u00e0 la integraci\u00f3 com a no compatible)", "disable_rtsp": "Desactiva el flux RTSP", "max_media": "Nombre m\u00e0xim d'esdeveniments a carregar al navegador multim\u00e8dia (augmenta l'\u00fas de RAM)", "override_connection_host": "Substitueix l'amfitri\u00f3 de connexi\u00f3" diff --git a/homeassistant/components/unifiprotect/translations/de.json b/homeassistant/components/unifiprotect/translations/de.json index 8cb94d2cc01..a9ee4d9b709 100644 --- a/homeassistant/components/unifiprotect/translations/de.json +++ b/homeassistant/components/unifiprotect/translations/de.json @@ -41,11 +41,31 @@ } } }, + "entity": { + "sensor": { + "license_plate": { + "state": { + "none": "L\u00f6schen" + } + } + } + }, "issues": { "deprecate_smart_sensor": { - "description": "Der einheitliche Sensor \u201eErkanntes Objekt\u201c f\u00fcr intelligente Erkennungen ist jetzt veraltet. Er wurde durch einzelne bin\u00e4re Smart Detection Sensoren f\u00fcr jeden Smart Detection Typ ersetzt. Bitte aktualisiere alle Vorlagen oder Automatisierungen entsprechend.", + "description": "Der einheitliche Sensor \"Erkanntes Objekt\" f\u00fcr intelligente Erkennungen ist jetzt veraltet. Er wurde durch individuelle Bin\u00e4rsensoren f\u00fcr jeden intelligenten Erkennungstyp ersetzt.\n\nIm Folgenden findest du die erkannten Automatisierungen oder Skripte, die eine oder mehrere der veralteten Entit\u00e4ten verwenden:\n{items}\nDie obige Liste ist m\u00f6glicherweise unvollst\u00e4ndig und enth\u00e4lt keine Vorlagen, die innerhalb von Dashboards verwendet werden. Bitte aktualisiere alle Vorlagen, Automatisierungen oder Skripte entsprechend.", "title": "Smart Detection Sensor veraltet" }, + "deprecated_service_set_doorbell_message": { + "fix_flow": { + "step": { + "confirm": { + "description": "Der Dienst \"unifiprotect.set_doorbell_message\" wird zugunsten der neuen Entit\u00e4t \"Doorbell Text\", die zu jedem Doorbell-Ger\u00e4t hinzugef\u00fcgt wird, verworfen. Er wird in v2023.3.0 entfernt. Bitte aktualisiere, um den [`text.set_value`-Dienst]({link}) zu verwenden.", + "title": "set_doorbell_message ist veraltet" + } + } + }, + "title": "set_doorbell_message ist veraltet" + }, "ea_setup_failed": { "description": "Du verwendest v{version} von UniFi Protect, eine Early-Access-Version. Beim Versuch, die Integration zu laden, ist ein nicht behebbarer Fehler aufgetreten. Bitte f\u00fchre ein [Downgrade auf eine stabile Version](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) von UniFi Protect durch, um die Integration weiterhin zu verwenden. \n\nFehler: {error}", "title": "Einrichtungsfehler bei Verwendung der Early-Access Version" @@ -72,7 +92,7 @@ "data": { "all_updates": "Echtzeitmetriken (WARNUNG: Erh\u00f6ht die CPU-Auslastung erheblich)", "allow_ea": "Early-Access-Versionen von Protect zulassen (WARNUNG: Markiert deine Integration als nicht unterst\u00fctzt)", - "disable_rtsp": "RTSP-Stream deaktivieren", + "disable_rtsp": "RTSP Stream deaktivieren", "max_media": "Maximale Anzahl von Ereignissen, die f\u00fcr den Medienbrowser geladen werden (erh\u00f6ht die RAM-Nutzung)", "override_connection_host": "Verbindungshost \u00fcberschreiben" }, diff --git a/homeassistant/components/unifiprotect/translations/el.json b/homeassistant/components/unifiprotect/translations/el.json index 9c2d33e15d6..ae4803f4619 100644 --- a/homeassistant/components/unifiprotect/translations/el.json +++ b/homeassistant/components/unifiprotect/translations/el.json @@ -41,7 +41,20 @@ } } }, + "entity": { + "sensor": { + "license_plate": { + "state": { + "none": "\u0394\u03b5\u03bd \u03b5\u03bd\u03c4\u03bf\u03c0\u03af\u03c3\u03c4\u03b7\u03ba\u03b5" + } + } + } + }, "issues": { + "deprecate_smart_sensor": { + "description": "\u039f \u03b5\u03bd\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03bf\u03c2 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2 \"\u0395\u03bd\u03c4\u03bf\u03c0\u03b9\u03c3\u03bc\u03ad\u03bd\u03bf \u03b1\u03bd\u03c4\u03b9\u03ba\u03b5\u03af\u03bc\u03b5\u03bd\u03bf\" \u03b3\u03b9\u03b1 \u03ad\u03be\u03c5\u03c0\u03bd\u03b5\u03c2 \u03b1\u03bd\u03b9\u03c7\u03bd\u03b5\u03cd\u03c3\u03b5\u03b9\u03c2 \u03ad\u03c7\u03b5\u03b9 \u03c0\u03bb\u03ad\u03bf\u03bd \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b7\u03b8\u03b5\u03af. \u0388\u03c7\u03b5\u03b9 \u03b1\u03bd\u03c4\u03b9\u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03b1\u03b8\u03b5\u03af \u03bc\u03b5 \u03bc\u03b5\u03bc\u03bf\u03bd\u03c9\u03bc\u03ad\u03bd\u03bf\u03c5\u03c2 \u03b4\u03c5\u03b1\u03b4\u03b9\u03ba\u03bf\u03cd\u03c2 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b5\u03c2 \u03ad\u03be\u03c5\u03c0\u03bd\u03b7\u03c2 \u03b1\u03bd\u03af\u03c7\u03bd\u03b5\u03c5\u03c3\u03b7\u03c2 \u03b3\u03b9\u03b1 \u03ba\u03ac\u03b8\u03b5 \u03c4\u03cd\u03c0\u03bf \u03ad\u03be\u03c5\u03c0\u03bd\u03b7\u03c2 \u03b1\u03bd\u03af\u03c7\u03bd\u03b5\u03c5\u03c3\u03b7\u03c2. \u0395\u03bd\u03b7\u03bc\u03b5\u03c1\u03ce\u03c3\u03c4\u03b5 \u03b1\u03bd\u03ac\u03bb\u03bf\u03b3\u03b1 \u03c4\u03c5\u03c7\u03cc\u03bd \u03c0\u03c1\u03cc\u03c4\u03c5\u03c0\u03b1 \u03ae \u03b1\u03c5\u03c4\u03bf\u03bc\u03b1\u03c4\u03b9\u03c3\u03bc\u03bf\u03cd\u03c2.", + "title": "\u039f \u03ad\u03be\u03c5\u03c0\u03bd\u03bf\u03c2 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2 \u03b1\u03bd\u03af\u03c7\u03bd\u03b5\u03c5\u03c3\u03b7\u03c2 \u03ad\u03c7\u03b5\u03b9 \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b7\u03b8\u03b5\u03af" + }, "ea_setup_failed": { "description": "\u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b5 v{version} \u03c4\u03bf\u03c5 UniFi Protect \u03c0\u03bf\u03c5 \u03b5\u03af\u03bd\u03b1\u03b9 \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7 Early Access. \u03a0\u03b1\u03c1\u03bf\u03c5\u03c3\u03b9\u03ac\u03c3\u03c4\u03b7\u03ba\u03b5 \u03ad\u03bd\u03b1 \u03bc\u03b7 \u03b1\u03bd\u03b1\u03ba\u03c4\u03ae\u03c3\u03b9\u03bc\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03c3\u03c0\u03ac\u03b8\u03b5\u03b9\u03b1 \u03c6\u03cc\u03c1\u03c4\u03c9\u03c3\u03b7\u03c2 \u03c4\u03b7\u03c2 \u03b5\u03bd\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7\u03c2. \u039a\u03ac\u03bd\u03c4\u03b5 [\u03c5\u03c0\u03bf\u03b2\u03ac\u03b8\u03bc\u03b9\u03c3\u03b7 \u03c3\u03b5 \u03c3\u03c4\u03b1\u03b8\u03b5\u03c1\u03ae \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) \u03c4\u03bf\u03c5 UniFi Protect \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c3\u03c5\u03bd\u03b5\u03c7\u03af\u03c3\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03bd\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7. \n\n \u03a3\u03c6\u03ac\u03bb\u03bc\u03b1: {error}", "title": "\u03a3\u03c6\u03ac\u03bb\u03bc\u03b1 \u03b5\u03b3\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2 \u03bc\u03b5 \u03c7\u03c1\u03ae\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7\u03c2 Early Access" diff --git a/homeassistant/components/unifiprotect/translations/en.json b/homeassistant/components/unifiprotect/translations/en.json index 65a398375fe..d19bf73bab8 100644 --- a/homeassistant/components/unifiprotect/translations/en.json +++ b/homeassistant/components/unifiprotect/translations/en.json @@ -16,7 +16,7 @@ "password": "Password", "username": "Username" }, - "description": "Do you want to setup {name} ({ip_address})? You will need a local user created in your UniFi OS Console to log in with. Ubiquiti Cloud Users will not work. For more information: {local_user_documentation_url}", + "description": "Do you want to set up {name} ({ip_address})? You will need a local user created in your UniFi OS Console to log in with. Ubiquiti Cloud Users will not work. For more information: {local_user_documentation_url}", "title": "UniFi Protect Discovered" }, "reauth_confirm": { @@ -41,7 +41,31 @@ } } }, + "entity": { + "sensor": { + "license_plate": { + "state": { + "none": "Clear" + } + } + } + }, "issues": { + "deprecate_smart_sensor": { + "description": "The unified \"Detected Object\" sensor for smart detections is now deprecated. It has been replaced with individual smart detection binary sensors for each smart detection type.\n\nBelow are the detected automations or scripts that use one or more of the deprecated entities:\n{items}\nThe above list may be incomplete and it does not include any template usages inside of dashboards. Please update any templates, automations or scripts accordingly.", + "title": "Smart Detection Sensor Deprecated" + }, + "deprecated_service_set_doorbell_message": { + "fix_flow": { + "step": { + "confirm": { + "description": "The `unifiprotect.set_doorbell_message` service is deprecated in favor of the new Doorbell Text entity added to each Doorbell device. It will be removed in v2023.3.0. Please update to use the [`text.set_value` service]({link}).", + "title": "set_doorbell_message is Deprecated" + } + } + }, + "title": "set_doorbell_message is Deprecated" + }, "ea_setup_failed": { "description": "You are using v{version} of UniFi Protect which is an Early Access version. An unrecoverable error occurred while trying to load the integration. Please [downgrade to a stable version](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) of UniFi Protect to continue using the integration.\n\nError: {error}", "title": "Setup error using Early Access version" diff --git a/homeassistant/components/unifiprotect/translations/es.json b/homeassistant/components/unifiprotect/translations/es.json index f73a5db7c8b..70dd90596d2 100644 --- a/homeassistant/components/unifiprotect/translations/es.json +++ b/homeassistant/components/unifiprotect/translations/es.json @@ -41,11 +41,31 @@ } } }, + "entity": { + "sensor": { + "license_plate": { + "state": { + "none": "No detectado" + } + } + } + }, "issues": { "deprecate_smart_sensor": { - "description": "El sensor unificado \"Objeto detectado\" para detecciones inteligentes ahora est\u00e1 obsoleto. Ha sido reemplazado por sensores binarios de detecci\u00f3n inteligente individuales para cada tipo de detecci\u00f3n inteligente. Actualiza cualquier plantilla o automatizaci\u00f3n en consecuencia.", + "description": "El sensor unificado de \"Objeto detectado\" para detecciones inteligentes ahora est\u00e1 obsoleto. Ha sido reemplazado por sensores binarios de detecci\u00f3n inteligente individuales para cada tipo de detecci\u00f3n inteligente. \n\nA continuaci\u00f3n se muestran las automatizaciones o secuencias de comandos detectadas que usan una o m\u00e1s de las entidades obsoletas:\n{items}\nLa lista anterior puede estar incompleta y no incluye ning\u00fan uso de plantilla dentro de los paneles. Por favor, actualiza las plantillas, automatizaciones o scripts en consecuencia.", "title": "Sensor de detecci\u00f3n inteligente obsoleto" }, + "deprecated_service_set_doorbell_message": { + "fix_flow": { + "step": { + "confirm": { + "description": "El servicio `unifiprotect.set_doorbell_message` est\u00e1 en desuso en favor de la nueva entidad Doorbell Text agregada a cada dispositivo Doorbell. Se eliminar\u00e1 en v2023.3.0. Por favor, actualiza para usar el servicio [`text.set_value`]({link}).", + "title": "set_doorbell_message est\u00e1 obsoleto" + } + } + }, + "title": "set_doorbell_message est\u00e1 obsoleto" + }, "ea_setup_failed": { "description": "Est\u00e1s utilizando v{version} de UniFi Protect, que es una versi\u00f3n de Early Access. Se produjo un error irrecuperable al intentar cargar la integraci\u00f3n. Por favor, [cambia a una versi\u00f3n estable](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) de UniFi Protect para continuar usando la integraci\u00f3n. \n\nError: {error}", "title": "Error de configuraci\u00f3n al usar la versi\u00f3n Early Access" diff --git a/homeassistant/components/unifiprotect/translations/et.json b/homeassistant/components/unifiprotect/translations/et.json index f1a3ca6cf12..ed93a65b463 100644 --- a/homeassistant/components/unifiprotect/translations/et.json +++ b/homeassistant/components/unifiprotect/translations/et.json @@ -41,6 +41,15 @@ } } }, + "entity": { + "sensor": { + "license_plate": { + "state": { + "none": "Puudub" + } + } + } + }, "issues": { "deprecate_smart_sensor": { "description": "\u00dchtse \"Avastatud objekti\" andur arukate tuvastuste jaoks on n\u00fc\u00fcdseks kaotanud kehtivuse. See on asendatud iga aruka avastamise t\u00fc\u00fcbi jaoks eraldi aruka avastamise binaarsete anduritega. Palun ajakohasta vastavalt k\u00f5ik mallid v\u00f5i automaatika.", diff --git a/homeassistant/components/unifiprotect/translations/hu.json b/homeassistant/components/unifiprotect/translations/hu.json index 600eeec24c7..ab870ed0486 100644 --- a/homeassistant/components/unifiprotect/translations/hu.json +++ b/homeassistant/components/unifiprotect/translations/hu.json @@ -41,7 +41,20 @@ } } }, + "entity": { + "sensor": { + "license_plate": { + "state": { + "none": "Nincs" + } + } + } + }, "issues": { + "deprecate_smart_sensor": { + "description": "Az intelligens \u00e9szlel\u00e9sek egys\u00e9ges \"Detected Object\" \u00e9rz\u00e9kel\u0151je elavult. Hely\u00e9t az egyes intelligens \u00e9szlel\u00e9si t\u00edpusokhoz tartoz\u00f3 egyedi intelligens \u00e9szlel\u00e9si bin\u00e1ris \u00e9rz\u00e9kel\u0151k vett\u00e9k \u00e1t. K\u00e9rj\u00fck, ennek megfelel\u0151en friss\u00edtse a sablonokat \u00e9s automatiz\u00e1l\u00e1sokat.", + "title": "Az intelligens \u00e9rz\u00e9kel\u00e9si \u00e9rz\u00e9kel\u0151 elavult" + }, "ea_setup_failed": { "description": "Az UniFi Protect {version}. verzi\u00f3j\u00e1t haszn\u00e1lja, amely egy korai hozz\u00e1f\u00e9r\u00e9s\u0171 verzi\u00f3. Helyrehozhatatlan hiba t\u00f6rt\u00e9nt az integr\u00e1ci\u00f3 bet\u00f6lt\u00e9se k\u00f6zben. K\u00e9rem, [haszn\u00e1ljon stabil verzi\u00f3t](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) az integr\u00e1ci\u00f3 tov\u00e1bbi haszn\u00e1lat\u00e1hoz.\n\nHiba: {error}", "title": "Be\u00e1ll\u00edt\u00e1si hiba a korai hozz\u00e1f\u00e9r\u00e9s\u0171 verzi\u00f3 haszn\u00e1lat\u00e1val" diff --git a/homeassistant/components/unifiprotect/translations/id.json b/homeassistant/components/unifiprotect/translations/id.json index 6fcaf1c19fb..9869e57756b 100644 --- a/homeassistant/components/unifiprotect/translations/id.json +++ b/homeassistant/components/unifiprotect/translations/id.json @@ -41,6 +41,15 @@ } } }, + "entity": { + "sensor": { + "license_plate": { + "state": { + "none": "Tidak ada" + } + } + } + }, "issues": { "deprecate_smart_sensor": { "description": "Sensor \"Objek Terdeteksi\" terpadu untuk deteksi cerdas sekarang sudah tidak digunakan lagi. Sensor ini telah diganti dengan sensor biner deteksi cerdas individual untuk setiap jenis deteksi cerdas. Perbarui semua templat atau otomasi yang terkait.", diff --git a/homeassistant/components/unifiprotect/translations/it.json b/homeassistant/components/unifiprotect/translations/it.json index d37fbcea83c..4395ca069f8 100644 --- a/homeassistant/components/unifiprotect/translations/it.json +++ b/homeassistant/components/unifiprotect/translations/it.json @@ -16,7 +16,7 @@ "password": "Password", "username": "Nome utente" }, - "description": "Vuoi configurare {name} ({ip_address})? Avrai bisogno di un utente locale creato nella tua console UniFi OS con cui accedere. Gli utenti Ubiquiti Cloud non funzioneranno. Per ulteriori informazioni: {local_user_documentation_url}", + "description": "Vuoi configurare {name} ({ip_address})? \u00c8 necessario un utente locale creato nella console UniFi OS per accedere. Gli utenti di Ubiquiti Cloud non funzioneranno. Per maggiori informazioni: {local_user_documentation_url}", "title": "Rilevato UniFi Protect" }, "reauth_confirm": { @@ -36,12 +36,36 @@ "username": "Nome utente", "verify_ssl": "Verifica il certificato SSL" }, - "description": "Avrai bisogno di un utente locale creato nella tua console UniFi OS con cui accedere. Gli utenti Ubiquiti Cloud non funzioneranno. Per ulteriori informazioni: {local_user_documentation_url}", + "description": "\u00c8 necessario un utente locale creato nella console UniFi OS per accedere. Gli utenti di Ubiquiti Cloud non funzioneranno. Per maggiori informazioni: {local_user_documentation_url}", "title": "Configurazione UniFi Protect" } } }, + "entity": { + "sensor": { + "license_plate": { + "state": { + "none": "Libero" + } + } + } + }, "issues": { + "deprecate_smart_sensor": { + "description": "Il sensore unificato \"Oggetto rilevato\" per i rilevamenti intelligenti \u00e8 ora deprecato. \u00c8 stato sostituito con singoli sensori binari di rilevamento intelligente per ogni tipo di rilevamento intelligente. \n\nDi seguito sono riportate le automazioni o gli script rilevati che utilizzano una o pi\u00f9 delle entit\u00e0 deprecate:\n{items}\nL'elenco precedente potrebbe essere incompleto e non include alcun utilizzo di modelli all'interno delle plance. Si prega di aggiornare eventuali modelli, automazioni o script di conseguenza.", + "title": "Sensore di rilevamento intelligente deprecato" + }, + "deprecated_service_set_doorbell_message": { + "fix_flow": { + "step": { + "confirm": { + "description": "Il servizio `unifiprotect.set_doorbell_message` \u00e8 deprecato a favore della nuova entit\u00e0 Doorbell Text aggiunta a ciascun dispositivo Doorbell. Verr\u00e0 rimosso nella v2023.3.0. Aggiorna per utilizzare il [servizio `text.set_value`]({link}).", + "title": "set_doorbell_message \u00e8 deprecato" + } + } + }, + "title": "set_doorbell_message \u00e8 deprecato" + }, "ea_setup_failed": { "description": "Stai utilizzando v{version} di UniFi Protect che \u00e8 una versione ad accesso anticipato. Si \u00e8 verificato un errore irreversibile durante il tentativo di caricare l'integrazione. [Esegui la retrocessione ad una versione stabile](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) di UniFi Protect per continuare ad utilizzare l'integrazione. \n\nErrore: {error}", "title": "Errore di configurazione durante l'utilizzo della versione ad accesso anticipato" diff --git a/homeassistant/components/unifiprotect/translations/ko.json b/homeassistant/components/unifiprotect/translations/ko.json index 916dcc8a7e9..a3b5aca3512 100644 --- a/homeassistant/components/unifiprotect/translations/ko.json +++ b/homeassistant/components/unifiprotect/translations/ko.json @@ -1,8 +1,13 @@ { "config": { "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "discovery_started": "\uc7a5\uce58\uac80\uc0c9 \uc2dc\uc791" }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, "step": { "discovery_confirm": { "data": { @@ -11,7 +16,29 @@ }, "description": "{name} ( {ip_address} )\uc744(\ub97c) \uc124\uc815\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c? ", "title": "UniFi Protect \ubc1c\uacac" + }, + "reauth_confirm": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638", + "port": "\ud3ec\ud2b8", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + } + }, + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8", + "password": "\ube44\ubc00\ubc88\ud638", + "port": "\ud3ec\ud2b8", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984", + "verify_ssl": "SSL \uc778\uc99d\uc11c \ud655\uc778" + } } } + }, + "issues": { + "deprecate_smart_sensor": { + "description": "\uc2a4\ub9c8\ud2b8 \uac10\uc9c0\ub97c \uc704\ud55c \ud1b5\ud569 \"\uac10\uc9c0\ub41c \uac1c\uccb4\" \uc13c\uc11c\ub294 \uc774\uc81c \ub354 \uc774\uc0c1 \uc0ac\uc6a9\ub418\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. \uc2a4\ub9c8\ud2b8 \uac10\uc9c0 \uc720\ud615\ubcc4\ub85c \uac1c\ubcc4 \uc2a4\ub9c8\ud2b8 \uac10\uc9c0 \ubc14\uc774\ub108\ub9ac \uc13c\uc11c\ub85c \ub300\uccb4\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \uadf8\uc5d0 \ub530\ub77c \ud15c\ud50c\ub9bf\uc774\ub098 \uc790\ub3d9\ud654\ub97c \uc5c5\ub370\uc774\ud2b8\ud558\uc2ed\uc2dc\uc624.", + "title": "\uc2a4\ub9c8\ud2b8 \uac10\uc9c0 \uc13c\uc11c\ub294 \ub354 \uc774\uc0c1 \uc0ac\uc6a9\ub418\uc9c0 \uc54a\uc74c" + } } } \ No newline at end of file diff --git a/homeassistant/components/unifiprotect/translations/no.json b/homeassistant/components/unifiprotect/translations/no.json index 195b7da31a4..ceffb21fff2 100644 --- a/homeassistant/components/unifiprotect/translations/no.json +++ b/homeassistant/components/unifiprotect/translations/no.json @@ -16,7 +16,7 @@ "password": "Passord", "username": "Brukernavn" }, - "description": "Vil du konfigurere {name} ( {ip_address} )? Du trenger en lokal bruker opprettet i UniFi OS-konsollen for \u00e5 logge p\u00e5. Ubiquiti Cloud-brukere vil ikke fungere. For mer informasjon: {local_user_documentation_url}", + "description": "Vil du sette opp {name} ( {ip_address} )? Du trenger en lokal bruker opprettet i UniFi OS-konsollen for \u00e5 logge p\u00e5. Ubiquiti Cloud-brukere vil ikke fungere. For mer informasjon: {local_user_documentation_url}", "title": "UniFi Protect oppdaget" }, "reauth_confirm": { @@ -41,6 +41,15 @@ } } }, + "entity": { + "sensor": { + "license_plate": { + "state": { + "none": "Klar" + } + } + } + }, "issues": { "deprecate_smart_sensor": { "description": "Den enhetlige \u00abDetected Object\u00bb-sensoren for smarte deteksjoner er n\u00e5 avviklet. Den er erstattet med individuelle smartdeteksjonsbin\u00e6re sensorer for hver smartdeteksjonstype. Oppdater eventuelle maler eller automatiseringer tilsvarende.", diff --git a/homeassistant/components/unifiprotect/translations/pl.json b/homeassistant/components/unifiprotect/translations/pl.json index 03175f0728b..e54a4e5c61b 100644 --- a/homeassistant/components/unifiprotect/translations/pl.json +++ b/homeassistant/components/unifiprotect/translations/pl.json @@ -41,7 +41,20 @@ } } }, + "entity": { + "sensor": { + "license_plate": { + "state": { + "none": "brak" + } + } + } + }, "issues": { + "deprecate_smart_sensor": { + "description": "Ujednolicony sensor \u201eWykryty obiekt\u201d do inteligentnego wykrywania jest teraz przestarza\u0142y. Zosta\u0142 on zast\u0105piony indywidualnymi sensorami binarnymi dla ka\u017cdego typu inteligentnej detekcji. Zaktualizuj odpowiednio wszystkie szablony lub automatyzacje.", + "title": "Przestarza\u0142y inteligentny sensor wykrywania" + }, "ea_setup_failed": { "description": "U\u017cywasz wersji {version} UniFi Protect, kt\u00f3ra jest wersj\u0105 Early Access. Wyst\u0105pi\u0142 nienaprawialny b\u0142\u0105d podczas pr\u00f3by za\u0142adowania integracji. Aby kontynuowa\u0107 korzystanie z integracji, [zmie\u0144 wersj\u0119 na stabiln\u0105](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) UniFi Protect. \n\nB\u0142\u0105d: {error}", "title": "B\u0142\u0105d konfiguracji w wersji Early Access" diff --git a/homeassistant/components/unifiprotect/translations/pt-BR.json b/homeassistant/components/unifiprotect/translations/pt-BR.json index c9fc3beb2db..564ad749a01 100644 --- a/homeassistant/components/unifiprotect/translations/pt-BR.json +++ b/homeassistant/components/unifiprotect/translations/pt-BR.json @@ -41,6 +41,15 @@ } } }, + "entity": { + "sensor": { + "license_plate": { + "state": { + "none": "Limpar" + } + } + } + }, "issues": { "deprecate_smart_sensor": { "description": "O sensor unificado de \"objeto detectado\" para detec\u00e7\u00f5es inteligentes agora est\u00e1 obsoleto. Ele foi substitu\u00eddo por sensores bin\u00e1rios de detec\u00e7\u00e3o inteligente individuais para cada tipo de detec\u00e7\u00e3o inteligente. Atualize quaisquer modelos ou automa\u00e7\u00f5es de acordo.", diff --git a/homeassistant/components/unifiprotect/translations/pt.json b/homeassistant/components/unifiprotect/translations/pt.json index bd20918e494..b3a355fd416 100644 --- a/homeassistant/components/unifiprotect/translations/pt.json +++ b/homeassistant/components/unifiprotect/translations/pt.json @@ -15,7 +15,7 @@ }, "user": { "data": { - "host": "Servidor", + "host": "Endere\u00e7o", "password": "Palavra-passe", "port": "Porta", "verify_ssl": "Verificar o certificado SSL" diff --git a/homeassistant/components/unifiprotect/translations/ru.json b/homeassistant/components/unifiprotect/translations/ru.json index 51e453aebb3..1e2cde53076 100644 --- a/homeassistant/components/unifiprotect/translations/ru.json +++ b/homeassistant/components/unifiprotect/translations/ru.json @@ -41,6 +41,15 @@ } } }, + "entity": { + "sensor": { + "license_plate": { + "state": { + "none": "\u041d\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u043e" + } + } + } + }, "issues": { "deprecate_smart_sensor": { "description": "\u0421\u0435\u043d\u0441\u043e\u0440 \"Detected Object\" \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u043b\u043b\u0435\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0445 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u0439 \u0442\u0435\u043f\u0435\u0440\u044c \u0443\u0441\u0442\u0430\u0440\u0435\u043b. \u041e\u043d \u0437\u0430\u043c\u0435\u043d\u0451\u043d \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u043c\u0438 \u0431\u0438\u043d\u0430\u0440\u043d\u044b\u043c\u0438 \u0441\u0435\u043d\u0441\u043e\u0440\u0430\u043c\u0438 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0442\u0438\u043f\u0430 \u0438\u043d\u0442\u0435\u043b\u043b\u0435\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u044f. \u041e\u0442\u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0443\u0439\u0442\u0435 \u0448\u0430\u0431\u043b\u043e\u043d\u044b \u0438\u043b\u0438 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0438, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0449\u0438\u0435 \u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0438\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b.", diff --git a/homeassistant/components/unifiprotect/translations/sensor.ca.json b/homeassistant/components/unifiprotect/translations/sensor.ca.json new file mode 100644 index 00000000000..228e6951f6a --- /dev/null +++ b/homeassistant/components/unifiprotect/translations/sensor.ca.json @@ -0,0 +1,7 @@ +{ + "state": { + "unifiprotect__license_plate": { + "none": "Lliure" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/unifiprotect/translations/sensor.de.json b/homeassistant/components/unifiprotect/translations/sensor.de.json new file mode 100644 index 00000000000..9e6d2cd54c7 --- /dev/null +++ b/homeassistant/components/unifiprotect/translations/sensor.de.json @@ -0,0 +1,7 @@ +{ + "state": { + "unifiprotect__license_plate": { + "none": "Normal" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/unifiprotect/translations/sensor.el.json b/homeassistant/components/unifiprotect/translations/sensor.el.json new file mode 100644 index 00000000000..227713ee18c --- /dev/null +++ b/homeassistant/components/unifiprotect/translations/sensor.el.json @@ -0,0 +1,7 @@ +{ + "state": { + "unifiprotect__license_plate": { + "none": "\u0394\u03b5\u03bd \u03b5\u03bd\u03c4\u03bf\u03c0\u03af\u03c3\u03c4\u03b7\u03ba\u03b5" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/unifiprotect/translations/sensor.es.json b/homeassistant/components/unifiprotect/translations/sensor.es.json new file mode 100644 index 00000000000..f7242214080 --- /dev/null +++ b/homeassistant/components/unifiprotect/translations/sensor.es.json @@ -0,0 +1,7 @@ +{ + "state": { + "unifiprotect__license_plate": { + "none": "Despejado" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/unifiprotect/translations/sensor.et.json b/homeassistant/components/unifiprotect/translations/sensor.et.json new file mode 100644 index 00000000000..4cd756b5550 --- /dev/null +++ b/homeassistant/components/unifiprotect/translations/sensor.et.json @@ -0,0 +1,7 @@ +{ + "state": { + "unifiprotect__license_plate": { + "none": "Puudub" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/unifiprotect/translations/sensor.hu.json b/homeassistant/components/unifiprotect/translations/sensor.hu.json new file mode 100644 index 00000000000..9ba039dc5f1 --- /dev/null +++ b/homeassistant/components/unifiprotect/translations/sensor.hu.json @@ -0,0 +1,7 @@ +{ + "state": { + "unifiprotect__license_plate": { + "none": "Nincs" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/unifiprotect/translations/sensor.id.json b/homeassistant/components/unifiprotect/translations/sensor.id.json new file mode 100644 index 00000000000..84eca58d255 --- /dev/null +++ b/homeassistant/components/unifiprotect/translations/sensor.id.json @@ -0,0 +1,7 @@ +{ + "state": { + "unifiprotect__license_plate": { + "none": "Tidak ada" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/unifiprotect/translations/sensor.it.json b/homeassistant/components/unifiprotect/translations/sensor.it.json new file mode 100644 index 00000000000..cf4af874125 --- /dev/null +++ b/homeassistant/components/unifiprotect/translations/sensor.it.json @@ -0,0 +1,7 @@ +{ + "state": { + "unifiprotect__license_plate": { + "none": "Libero" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/unifiprotect/translations/sensor.ko.json b/homeassistant/components/unifiprotect/translations/sensor.ko.json new file mode 100644 index 00000000000..aa6efa7ce97 --- /dev/null +++ b/homeassistant/components/unifiprotect/translations/sensor.ko.json @@ -0,0 +1,7 @@ +{ + "state": { + "unifiprotect__license_plate": { + "none": "\uc774\uc0c1\uc5c6\uc74c" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/unifiprotect/translations/sensor.no.json b/homeassistant/components/unifiprotect/translations/sensor.no.json new file mode 100644 index 00000000000..393a4e2a992 --- /dev/null +++ b/homeassistant/components/unifiprotect/translations/sensor.no.json @@ -0,0 +1,7 @@ +{ + "state": { + "unifiprotect__license_plate": { + "none": "Klar" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/unifiprotect/translations/sensor.pl.json b/homeassistant/components/unifiprotect/translations/sensor.pl.json new file mode 100644 index 00000000000..5a5ebe80d9e --- /dev/null +++ b/homeassistant/components/unifiprotect/translations/sensor.pl.json @@ -0,0 +1,7 @@ +{ + "state": { + "unifiprotect__license_plate": { + "none": "brak" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/unifiprotect/translations/sensor.pt-BR.json b/homeassistant/components/unifiprotect/translations/sensor.pt-BR.json new file mode 100644 index 00000000000..9688637bc9b --- /dev/null +++ b/homeassistant/components/unifiprotect/translations/sensor.pt-BR.json @@ -0,0 +1,7 @@ +{ + "state": { + "unifiprotect__license_plate": { + "none": "Limpar" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/unifiprotect/translations/sensor.ru.json b/homeassistant/components/unifiprotect/translations/sensor.ru.json new file mode 100644 index 00000000000..526249db518 --- /dev/null +++ b/homeassistant/components/unifiprotect/translations/sensor.ru.json @@ -0,0 +1,7 @@ +{ + "state": { + "unifiprotect__license_plate": { + "none": "\u041d\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u043e" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/unifiprotect/translations/sensor.sk.json b/homeassistant/components/unifiprotect/translations/sensor.sk.json new file mode 100644 index 00000000000..99487c6e438 --- /dev/null +++ b/homeassistant/components/unifiprotect/translations/sensor.sk.json @@ -0,0 +1,7 @@ +{ + "state": { + "unifiprotect__license_plate": { + "none": "Zmaza\u0165" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/unifiprotect/translations/sensor.zh-Hant.json b/homeassistant/components/unifiprotect/translations/sensor.zh-Hant.json new file mode 100644 index 00000000000..2de36a0f477 --- /dev/null +++ b/homeassistant/components/unifiprotect/translations/sensor.zh-Hant.json @@ -0,0 +1,7 @@ +{ + "state": { + "unifiprotect__license_plate": { + "none": "\u672a\u5075\u6e2c" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/unifiprotect/translations/sk.json b/homeassistant/components/unifiprotect/translations/sk.json index e84afc395eb..2e0d22fc8d0 100644 --- a/homeassistant/components/unifiprotect/translations/sk.json +++ b/homeassistant/components/unifiprotect/translations/sk.json @@ -1,11 +1,13 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "discovery_started": "Vyh\u013ead\u00e1vanie za\u010dalo" }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "protect_version": "Minim\u00e1lna po\u017eadovan\u00e1 verzia je v1.20.0. Inovujte UniFi Protect a potom to sk\u00faste znova." }, "flow_title": "{name} ({ip_address})", "step": { @@ -13,14 +15,18 @@ "data": { "password": "Heslo", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" - } + }, + "description": "Chcete nastavi\u0165 {name} ({ip_address})? Na prihl\u00e1senie budete potrebova\u0165 lok\u00e1lneho pou\u017e\u00edvate\u013ea vytvoren\u00e9ho vo va\u0161ej konzole UniFi OS. Pou\u017e\u00edvatelia cloudu Ubiquiti nebud\u00fa fungova\u0165. \u010eal\u0161ie inform\u00e1cie: {local_user_documentation_url}", + "title": "Objaven\u00fd UniFi Protect" }, "reauth_confirm": { "data": { + "host": "IP/hostite\u013e servera UniFi Protect Server", "password": "Heslo", "port": "Port", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" - } + }, + "title": "UniFi Protect Reauth" }, "user": { "data": { @@ -29,16 +35,69 @@ "port": "Port", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno", "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" + }, + "description": "Na prihl\u00e1senie budete potrebova\u0165 lok\u00e1lneho pou\u017e\u00edvate\u013ea vytvoren\u00e9ho vo va\u0161ej konzole UniFi OS. Pou\u017e\u00edvatelia cloudu Ubiquiti nebud\u00fa fungova\u0165. \u010eal\u0161ie inform\u00e1cie: {local_user_documentation_url}", + "title": "Nastavenie UniFi Protect" + } + } + }, + "entity": { + "sensor": { + "license_plate": { + "state": { + "none": "Zmaza\u0165" } } } }, + "issues": { + "deprecate_smart_sensor": { + "description": "Unifikovan\u00fd sn\u00edma\u010d \u201eZisten\u00fd objekt\u201c pre inteligentn\u00e9 detekcie je teraz zastaran\u00fd. Bol nahraden\u00fd samostatn\u00fdmi bin\u00e1rnymi sn\u00edma\u010dmi inteligentnej detekcie pre ka\u017ed\u00fd typ inteligentnej detekcie. \n\nNi\u017e\u0161ie s\u00fa uveden\u00e9 zisten\u00e9 automatiz\u00e1cie alebo skripty, ktor\u00e9 pou\u017e\u00edvaj\u00fa jednu alebo viacero zastaran\u00fdch ent\u00edt:\n{items}\nVy\u0161\u0161ie uveden\u00fd zoznam m\u00f4\u017ee by\u0165 ne\u00fapln\u00fd a nezah\u0155\u0148a pou\u017eitie \u0161abl\u00f3n v informa\u010dn\u00fdch paneloch. Pod\u013ea toho aktualizujte v\u0161etky \u0161abl\u00f3ny, automatiz\u00e1cie alebo skripty.", + "title": "Inteligentn\u00fd detek\u010dn\u00fd senzor zastaran\u00fd" + }, + "deprecated_service_set_doorbell_message": { + "fix_flow": { + "step": { + "confirm": { + "description": "Slu\u017eba \u201eunifiprotect.set_doorbell_message\u201c je zastaran\u00e1 v prospech novej entity Doorbell Text pridanej do ka\u017ed\u00e9ho zariadenia Doorbell. Bude odstr\u00e1nen\u00e1 vo verzii 2023.3.0. Aktualizujte, aby ste mohli pou\u017e\u00edva\u0165 slu\u017ebu [`text.set_value`]({link}).", + "title": "set_doorbell_message je zastaran\u00fd" + } + } + }, + "title": "set_doorbell_message je zastaran\u00fd" + }, + "ea_setup_failed": { + "description": "Pou\u017e\u00edvate v{version} UniFi Protect, \u010do je verzia skor\u00e9ho pr\u00edstupu. Pri pokuse o na\u010d\u00edtanie integr\u00e1cie sa vyskytla neodstr\u00e1nite\u013en\u00e1 chyba. Ak chcete pokra\u010dova\u0165 v pou\u017e\u00edvan\u00ed integr\u00e1cie, [prejdite na stabiln\u00fa verziu](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) UniFi Protect. \n\n Chyba: {error}", + "title": "Chyba nastavenia pri pou\u017eit\u00ed verzie predbe\u017en\u00e9ho pr\u00edstupu" + }, + "ea_warning": { + "fix_flow": { + "step": { + "confirm": { + "description": "Naozaj chcete spusti\u0165 nepodporovan\u00e9 verzie UniFi Protect? M\u00f4\u017ee to sp\u00f4sobi\u0165 zlyhanie integr\u00e1cie dom\u00e1ceho asistenta.", + "title": "v {version} je verzia predbe\u017en\u00e9ho pr\u00edstupu" + }, + "start": { + "description": "Pou\u017e\u00edvate v{version} UniFi Protect, \u010do je verzia skor\u00e9ho pr\u00edstupu. [Verzie s pred\u010dasn\u00fdm pr\u00edstupom Home Assistant nepodporuje](https://www.home-assistant.io/integrations/unifiprotect#about-unifi-early-access) a odpor\u00fa\u010da sa, aby ste sa \u010do najsk\u00f4r vr\u00e1tili k stabiln\u00e9mu vydaniu mo\u017en\u00e9. \n\n Odoslan\u00edm tohto formul\u00e1ra ste bu\u010f [pre\u0161li na star\u0161iu verziu UniFi Protect](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect), alebo s\u00fahlas\u00edte so spusten\u00edm nepodporovanej verzie UniFi Protect.", + "title": "v {version} je verzia predbe\u017en\u00e9ho pr\u00edstupu" + } + } + }, + "title": "UniFi Protect v {version} je verzia skor\u00e9ho pr\u00edstupu" + } + }, "options": { "step": { "init": { "data": { - "all_updates": "Metriky v re\u00e1lnom \u010dase (UPOZORNENIE: V\u00fdrazne zvy\u0161uje vyu\u017eitie procesora)" - } + "all_updates": "Metriky v re\u00e1lnom \u010dase (UPOZORNENIE: V\u00fdrazne zvy\u0161uje vyu\u017eitie procesora)", + "allow_ea": "Povoli\u0165 prednostn\u00fd pr\u00edstup verzie Protect (UPOZORNENIE: Va\u0161a integr\u00e1cia bude ozna\u010den\u00e1 ako nepodporovan\u00e1)", + "disable_rtsp": "Zak\u00e1za\u0165 stream RTSP", + "max_media": "Maxim\u00e1lny po\u010det udalost\u00ed na na\u010d\u00edtanie pre prehliada\u010d m\u00e9di\u00ed (zvy\u0161uje vyu\u017eitie pam\u00e4te RAM)", + "override_connection_host": "Prep\u00edsa\u0165 hostite\u013ea pripojenia" + }, + "description": "Mo\u017enos\u0165 metr\u00edk v re\u00e1lnom \u010dase by mala by\u0165 povolen\u00e1 iba vtedy, ak ste povolili diagnostick\u00e9 senzory a chcete ich aktualizova\u0165 v re\u00e1lnom \u010dase. Ak nie s\u00fa povolen\u00e9, aktualizuj\u00fa sa iba raz za 15 min\u00fat.", + "title": "Mo\u017enosti UniFi Protect" } } } diff --git a/homeassistant/components/unifiprotect/translations/zh-Hant.json b/homeassistant/components/unifiprotect/translations/zh-Hant.json index ca8f1297d25..c7a092c3f96 100644 --- a/homeassistant/components/unifiprotect/translations/zh-Hant.json +++ b/homeassistant/components/unifiprotect/translations/zh-Hant.json @@ -41,11 +41,31 @@ } } }, + "entity": { + "sensor": { + "license_plate": { + "state": { + "none": "\u672a\u5075\u6e2c" + } + } + } + }, "issues": { "deprecate_smart_sensor": { - "description": "\u7528\u65bc\u667a\u80fd\u5075\u6e2c\u7684\u7d71\u4e00 \"\u5075\u6e2c\u7269\u4ef6\" \u611f\u6e2c\u5668\u5df2\u68c4\u7528\uff0c\u6539\u4ee5\u500b\u5225\u667a\u80fd\u5075\u6e2c\u985e\u578b\u7684\u667a\u80fd\u5075\u6e2c\u4e8c\u9032\u4f4d\u611f\u6e2c\u5668\u4f86\u53d6\u4ee3\u3002\u8acb\u66f4\u65b0\u76f8\u5c0d\u61c9\u7684\u6a23\u677f\u6216\u81ea\u52d5\u5316\u3002", + "description": "\u7528\u65bc\u667a\u80fd\u5075\u6e2c\u7684\u7d71\u4e00 \"\u5075\u6e2c\u7269\u4ef6\" \u611f\u6e2c\u5668\u5df2\u68c4\u7528\uff0c\u6539\u4ee5\u500b\u5225\u667a\u80fd\u5075\u6e2c\u985e\u578b\u7684\u667a\u80fd\u5075\u6e2c\u4e8c\u9032\u4f4d\u611f\u6e2c\u5668\u4f86\u53d6\u4ee3\u3002\n\n\u4e0b\u65b9\u70ba\u5df2\u767c\u73fe\u4f7f\u7528\u4e00\u500b\u6216\u4ee5\u4e0a\u5df2\u505c\u6b62\u4f7f\u7528\u5be6\u9ad4\u7684\u81ea\u52d5\u5316\u6216\u8173\u672c\uff1a\n{items}\n\u4e0a\u65b9\u5217\u8868\u53ef\u80fd\u4e0d\u5b8c\u6574\u3001\u4e26\u672a\u5305\u542b\u4efb\u4f55\u65bc\u4e3b\u9762\u677f\u4e2d\u6a21\u677f\u4e4b\u4f7f\u7528\u3002\u8acb\u66f4\u65b0\u76f8\u5c0d\u61c9\u7684\u6a21\u677f\u3001\u81ea\u52d5\u5316\u6216\u8173\u672c\u3002", "title": "\u667a\u80fd\u5075\u6e2c\u611f\u6e2c\u5668\u5df2\u505c\u7528" }, + "deprecated_service_set_doorbell_message": { + "fix_flow": { + "step": { + "confirm": { + "description": "`unifiprotect.set_doorbell_message` \u670d\u52d9\u5df2\u505c\u6b62\u4f7f\u7528\u3001\u4ee5\u65b0\u589e\u81f3\u6bcf\u4e00\u500b\u9580\u9234\u88dd\u7f6e\u7684\u5168\u65b0\u9580\u9234\u6587\u5b57\u5be6\u9ad4\u9032\u884c\u53d6\u4ee3\u3002\u5c07\u6703\u65bc v2023.3.0 \u7248\u9032\u884c\u79fb\u9664\u3002\u8acb\u66f4\u65b0\u4f7f\u7528 [`text.set_value` service]({link})\u3002", + "title": "set_doorbell_message \u5df2\u505c\u6b62\u4f7f\u7528" + } + } + }, + "title": "set_doorbell_message \u5df2\u505c\u6b62\u4f7f\u7528" + }, "ea_setup_failed": { "description": "\u6b63\u5728\u4f7f\u7528\u7684 UniFi Protect v{version} \u7248\u6436\u5148\u9ad4\u9a57\u7248\u3002\u5617\u8a66\u8f09\u5165\u6574\u5408\u6642\u767c\u751f\u4e0d\u53ef\u6062\u5fa9\u7684\u932f\u8aa4\u3002\u8acb[\u964d\u7d1a\u81f3\u7a69\u5b9a\u7248\u672c](https://www.home-assistant.io/integrations/unifiprotect#downgrading-unifi-protect) \u4e4b UniFi Protect \u4ee5\u7e7c\u7e8c\u4f7f\u7528\u6b64\u6574\u5408\u3002\n\n\u932f\u8aa4\uff1a{error}", "title": "\u4f7f\u7528\u6436\u5148\u9ad4\u9a57\u7248\u8a2d\u5b9a\u932f\u8aa4" diff --git a/homeassistant/components/universal/media_player.py b/homeassistant/components/universal/media_player.py index 3c6ba701f7d..4f0e17cac9b 100644 --- a/homeassistant/components/universal/media_player.py +++ b/homeassistant/components/universal/media_player.py @@ -40,6 +40,7 @@ from homeassistant.components.media_player import ( SERVICE_PLAY_MEDIA, SERVICE_SELECT_SOUND_MODE, SERVICE_SELECT_SOURCE, + MediaPlayerDeviceClass, MediaPlayerEntity, MediaPlayerEntityFeature, MediaPlayerState, @@ -279,7 +280,7 @@ class UniversalMediaPlayer(MediaPlayerEntity): ) @property - def device_class(self) -> str | None: + def device_class(self) -> MediaPlayerDeviceClass | None: """Return the class of this device.""" return self._device_class diff --git a/homeassistant/components/upb/translations/pt.json b/homeassistant/components/upb/translations/pt.json index 3bd5d8358a6..08ad7a08c4c 100644 --- a/homeassistant/components/upb/translations/pt.json +++ b/homeassistant/components/upb/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "unknown": "Erro inesperado" }, "step": { diff --git a/homeassistant/components/upb/translations/sk.json b/homeassistant/components/upb/translations/sk.json index 2174735cca1..18406a4bfb1 100644 --- a/homeassistant/components/upb/translations/sk.json +++ b/homeassistant/components/upb/translations/sk.json @@ -5,14 +5,17 @@ }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", + "invalid_upb_file": "Ch\u00fdbaj\u00faci alebo neplatn\u00fd exportn\u00fd s\u00fabor UPB UPSstart, skontrolujte n\u00e1zov a cestu k s\u00faboru.", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "user": { "data": { "address": "Adresa (pozri popis vy\u0161\u0161ie)", + "file_path": "Cesta a n\u00e1zov exportn\u00e9ho s\u00faboru UPSstart UPB.", "protocol": "Protokol" }, + "description": "Pripojte modul rozhrania Universal Powerline Bus Powerline Interface Module (UPB PIM). Re\u0165azec adresy mus\u00ed by\u0165 v tvare 'adresa[:port]' pre 'tcp'. Port je volite\u013en\u00fd a \u0161tandardne je nastaven\u00fd na 2101. Pr\u00edklad: '192.168.1.42'. Pre s\u00e9riov\u00fd protokol mus\u00ed by\u0165 adresa v tvare 'tty[:baud]'. Prenosov\u00e1 r\u00fdchlos\u0165 je volite\u013en\u00e1 a predvolen\u00e1 je 4800. Pr\u00edklad: '/dev/ttyS1'.", "title": "Pripojte sa k UPB PIM" } } diff --git a/homeassistant/components/upcloud/const.py b/homeassistant/components/upcloud/const.py index 62aeb75d3dd..763462c37f4 100644 --- a/homeassistant/components/upcloud/const.py +++ b/homeassistant/components/upcloud/const.py @@ -4,4 +4,4 @@ from datetime import timedelta DOMAIN = "upcloud" DEFAULT_SCAN_INTERVAL = timedelta(seconds=60) -CONFIG_ENTRY_UPDATE_SIGNAL_TEMPLATE = f"{DOMAIN}_config_entry_update:" "{}" +CONFIG_ENTRY_UPDATE_SIGNAL_TEMPLATE = f"{DOMAIN}_config_entry_update:{{}}" diff --git a/homeassistant/components/upcloud/translations/pt.json b/homeassistant/components/upcloud/translations/pt.json index a2f32087684..47b25129962 100644 --- a/homeassistant/components/upcloud/translations/pt.json +++ b/homeassistant/components/upcloud/translations/pt.json @@ -1,7 +1,7 @@ { "config": { "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida" }, "step": { diff --git a/homeassistant/components/update/__init__.py b/homeassistant/components/update/__init__.py index 57e20a764ba..f8d00300d81 100644 --- a/homeassistant/components/update/__init__.py +++ b/homeassistant/components/update/__init__.py @@ -174,7 +174,7 @@ async def async_clear_skipped(entity: UpdateEntity, service_call: ServiceCall) - class UpdateEntityDescription(EntityDescription): """A class that describes update entities.""" - device_class: UpdateDeviceClass | str | None = None + device_class: UpdateDeviceClass | None = None entity_category: EntityCategory | None = EntityCategory.CONFIG @@ -184,7 +184,7 @@ class UpdateEntity(RestoreEntity): entity_description: UpdateEntityDescription _attr_auto_update: bool = False _attr_installed_version: str | None = None - _attr_device_class: UpdateDeviceClass | str | None + _attr_device_class: UpdateDeviceClass | None _attr_in_progress: bool | int = False _attr_latest_version: str | None = None _attr_release_summary: str | None = None @@ -206,7 +206,7 @@ class UpdateEntity(RestoreEntity): return self._attr_installed_version @property - def device_class(self) -> UpdateDeviceClass | str | None: + def device_class(self) -> UpdateDeviceClass | None: """Return the class of this entity.""" if hasattr(self, "_attr_device_class"): return self._attr_device_class diff --git a/homeassistant/components/update/translations/sk.json b/homeassistant/components/update/translations/sk.json index 76ac84624ef..3088935af76 100644 --- a/homeassistant/components/update/translations/sk.json +++ b/homeassistant/components/update/translations/sk.json @@ -2,7 +2,8 @@ "device_automation": { "trigger_type": { "changed_states": "Dostupnos\u0165 aktualiz\u00e1cie {entity_name} sa zmenila", - "turned_off": "{entity_name} sa stalo aktu\u00e1lnym" + "turned_off": "{entity_name} sa stalo aktu\u00e1lnym", + "turned_on": "{entity_name} m\u00e1 k dispoz\u00edcii aktualiz\u00e1ciu" } }, "title": "Aktualizova\u0165" diff --git a/homeassistant/components/upnp/const.py b/homeassistant/components/upnp/const.py index 5f73b1e63c9..1f33f7cc676 100644 --- a/homeassistant/components/upnp/const.py +++ b/homeassistant/components/upnp/const.py @@ -2,7 +2,7 @@ from datetime import timedelta import logging -from homeassistant.const import TIME_SECONDS +from homeassistant.const import UnitOfTime LOGGER = logging.getLogger(__package__) @@ -18,7 +18,7 @@ PACKETS_PER_SEC_RECEIVED = "packets_per_sec_received" PACKETS_PER_SEC_SENT = "packets_per_sec_sent" TIMESTAMP = "timestamp" DATA_PACKETS = "packets" -DATA_RATE_PACKETS_PER_SECOND = f"{DATA_PACKETS}/{TIME_SECONDS}" +DATA_RATE_PACKETS_PER_SECOND = f"{DATA_PACKETS}/{UnitOfTime.SECONDS}" WAN_STATUS = "wan_status" ROUTER_IP = "ip" ROUTER_UPTIME = "uptime" diff --git a/homeassistant/components/upnp/manifest.json b/homeassistant/components/upnp/manifest.json index 214f5ce2931..ff683c883bb 100644 --- a/homeassistant/components/upnp/manifest.json +++ b/homeassistant/components/upnp/manifest.json @@ -3,7 +3,7 @@ "name": "UPnP/IGD", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/upnp", - "requirements": ["async-upnp-client==0.32.3", "getmac==0.8.2"], + "requirements": ["async-upnp-client==0.33.0", "getmac==0.8.2"], "dependencies": ["network", "ssdp"], "codeowners": ["@StevenLooman"], "ssdp": [ diff --git a/homeassistant/components/upnp/sensor.py b/homeassistant/components/upnp/sensor.py index 3d0c71fafdb..e5b09d1a398 100644 --- a/homeassistant/components/upnp/sensor.py +++ b/homeassistant/components/upnp/sensor.py @@ -4,12 +4,13 @@ from __future__ import annotations from dataclasses import dataclass from homeassistant.components.sensor import ( + SensorDeviceClass, SensorEntity, SensorEntityDescription, SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import DATA_BYTES, DATA_RATE_KIBIBYTES_PER_SECOND, TIME_SECONDS +from homeassistant.const import UnitOfDataRate, UnitOfInformation, UnitOfTime from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -43,18 +44,20 @@ class UpnpSensorEntityDescription(UpnpEntityDescription, SensorEntityDescription SENSOR_DESCRIPTIONS: tuple[UpnpSensorEntityDescription, ...] = ( UpnpSensorEntityDescription( key=BYTES_RECEIVED, - name=f"{DATA_BYTES} received", + name=f"{UnitOfInformation.BYTES} received", icon="mdi:server-network", - native_unit_of_measurement=DATA_BYTES, + device_class=SensorDeviceClass.DATA_SIZE, + native_unit_of_measurement=UnitOfInformation.BYTES, format="d", entity_registry_enabled_default=False, state_class=SensorStateClass.TOTAL_INCREASING, ), UpnpSensorEntityDescription( key=BYTES_SENT, - name=f"{DATA_BYTES} sent", + name=f"{UnitOfInformation.BYTES} sent", icon="mdi:server-network", - native_unit_of_measurement=DATA_BYTES, + device_class=SensorDeviceClass.DATA_SIZE, + native_unit_of_measurement=UnitOfInformation.BYTES, format="d", entity_registry_enabled_default=False, state_class=SensorStateClass.TOTAL_INCREASING, @@ -87,7 +90,7 @@ SENSOR_DESCRIPTIONS: tuple[UpnpSensorEntityDescription, ...] = ( key=ROUTER_UPTIME, name="Uptime", icon="mdi:server-network", - native_unit_of_measurement=TIME_SECONDS, + native_unit_of_measurement=UnitOfTime.SECONDS, entity_registry_enabled_default=False, format="d", entity_category=EntityCategory.DIAGNOSTIC, @@ -103,9 +106,10 @@ SENSOR_DESCRIPTIONS: tuple[UpnpSensorEntityDescription, ...] = ( key=BYTES_RECEIVED, value_key=KIBIBYTES_PER_SEC_RECEIVED, unique_id="KiB/sec_received", - name=f"{DATA_RATE_KIBIBYTES_PER_SECOND} received", + name=f"{UnitOfDataRate.KIBIBYTES_PER_SECOND} received", icon="mdi:server-network", - native_unit_of_measurement=DATA_RATE_KIBIBYTES_PER_SECOND, + device_class=SensorDeviceClass.DATA_RATE, + native_unit_of_measurement=UnitOfDataRate.KIBIBYTES_PER_SECOND, format=".1f", state_class=SensorStateClass.MEASUREMENT, ), @@ -113,9 +117,10 @@ SENSOR_DESCRIPTIONS: tuple[UpnpSensorEntityDescription, ...] = ( key=BYTES_SENT, value_key=KIBIBYTES_PER_SEC_SENT, unique_id="KiB/sec_sent", - name=f"{DATA_RATE_KIBIBYTES_PER_SECOND} sent", + name=f"{UnitOfDataRate.KIBIBYTES_PER_SECOND} sent", icon="mdi:server-network", - native_unit_of_measurement=DATA_RATE_KIBIBYTES_PER_SECOND, + device_class=SensorDeviceClass.DATA_RATE, + native_unit_of_measurement=UnitOfDataRate.KIBIBYTES_PER_SECOND, format=".1f", state_class=SensorStateClass.MEASUREMENT, ), diff --git a/homeassistant/components/upnp/translations/sk.json b/homeassistant/components/upnp/translations/sk.json index ae6b0093d7f..852b7dc685b 100644 --- a/homeassistant/components/upnp/translations/sk.json +++ b/homeassistant/components/upnp/translations/sk.json @@ -2,15 +2,40 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "incomplete_discovery": "Ne\u00fapln\u00e9 vyh\u013ead\u00e1vanie", "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" }, + "error": { + "few": "Pr\u00e1zdnych", + "many": "Pr\u00e1zdnych", + "one": "Pr\u00e1zdny", + "other": "Pr\u00e1zdny" + }, "flow_title": "{name}", "step": { + "init": { + "few": "Pr\u00e1zdnych", + "many": "Pr\u00e1zdnych", + "one": "Pr\u00e1zdny", + "other": "Pr\u00e1zdny" + }, + "ssdp_confirm": { + "description": "Chcete nastavi\u0165 toto zariadenie UPnP/IGD?" + }, "user": { "data": { "unique_id": "Zariadenie" } } } + }, + "options": { + "step": { + "init": { + "data": { + "scan_interval": "Interval aktualiz\u00e1cie (sekundy, minim\u00e1lne 30)" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/uptime/translations/en.json b/homeassistant/components/uptime/translations/en.json index ec70b67c0e7..b44ff570fcf 100644 --- a/homeassistant/components/uptime/translations/en.json +++ b/homeassistant/components/uptime/translations/en.json @@ -5,7 +5,7 @@ }, "step": { "user": { - "description": "Do you want to start set up?" + "description": "Do you want to start setup?" } } }, diff --git a/homeassistant/components/uptime/translations/ko.json b/homeassistant/components/uptime/translations/ko.json index 758f3336cd4..251c9777b6c 100644 --- a/homeassistant/components/uptime/translations/ko.json +++ b/homeassistant/components/uptime/translations/ko.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." + }, "step": { "user": { "description": "\uc124\uc815\uc744 \uc2dc\uc791\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" diff --git a/homeassistant/components/uptime/translations/sk.json b/homeassistant/components/uptime/translations/sk.json index 6ba11236f08..eecd59dae57 100644 --- a/homeassistant/components/uptime/translations/sk.json +++ b/homeassistant/components/uptime/translations/sk.json @@ -8,5 +8,12 @@ "description": "Chcete za\u010da\u0165 nastavova\u0165?" } } - } + }, + "issues": { + "removed_yaml": { + "description": "Konfigur\u00e1cia Uptime pomocou YAML bola odstr\u00e1nen\u00e1. \n\n Asistent dom\u00e1cnosti nepou\u017e\u00edva va\u0161u existuj\u00facu konfigur\u00e1ciu YAML. \n\n Ak chcete tento probl\u00e9m vyrie\u0161i\u0165, odstr\u00e1\u0148te konfigur\u00e1ciu YAML zo s\u00faboru configuration.yaml a re\u0161tartujte aplik\u00e1ciu Home Assistant.", + "title": "Konfigur\u00e1cia Uptime YAML bola odstr\u00e1nen\u00e1" + } + }, + "title": "Uptime" } \ No newline at end of file diff --git a/homeassistant/components/uptimerobot/__init__.py b/homeassistant/components/uptimerobot/__init__.py index 14221463c0e..00ee0889c3d 100644 --- a/homeassistant/components/uptimerobot/__init__.py +++ b/homeassistant/components/uptimerobot/__init__.py @@ -53,10 +53,9 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return unload_ok -class UptimeRobotDataUpdateCoordinator(DataUpdateCoordinator): +class UptimeRobotDataUpdateCoordinator(DataUpdateCoordinator[list[UptimeRobotMonitor]]): """Data update coordinator for UptimeRobot.""" - data: list[UptimeRobotMonitor] config_entry: ConfigEntry def __init__( @@ -77,7 +76,7 @@ class UptimeRobotDataUpdateCoordinator(DataUpdateCoordinator): self._device_registry = dev_reg self.api = api - async def _async_update_data(self) -> list[UptimeRobotMonitor] | None: + async def _async_update_data(self) -> list[UptimeRobotMonitor]: """Update data.""" try: response = await self.api.async_get_monitors() @@ -111,6 +110,5 @@ class UptimeRobotDataUpdateCoordinator(DataUpdateCoordinator): self.hass.async_create_task( self.hass.config_entries.async_reload(self._config_entry_id) ) - return None return monitors diff --git a/homeassistant/components/uptimerobot/sensor.py b/homeassistant/components/uptimerobot/sensor.py index 0e450bf24b7..ed8c42c77e7 100644 --- a/homeassistant/components/uptimerobot/sensor.py +++ b/homeassistant/components/uptimerobot/sensor.py @@ -3,7 +3,11 @@ from __future__ import annotations from typing import TypedDict -from homeassistant.components.sensor import SensorEntity, SensorEntityDescription +from homeassistant.components.sensor import ( + SensorDeviceClass, + SensorEntity, + SensorEntityDescription, +) from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory @@ -44,7 +48,9 @@ async def async_setup_entry( key=str(monitor.id), name=monitor.friendly_name, entity_category=EntityCategory.DIAGNOSTIC, - device_class="uptimerobot__monitor_status", + device_class=SensorDeviceClass.ENUM, + options=["down", "not_checked_yet", "pause", "seems_down", "up"], + translation_key="monitor_status", ), monitor=monitor, ) diff --git a/homeassistant/components/uptimerobot/strings.json b/homeassistant/components/uptimerobot/strings.json index 4cca3c159ae..8fccc3cb9e9 100644 --- a/homeassistant/components/uptimerobot/strings.json +++ b/homeassistant/components/uptimerobot/strings.json @@ -28,5 +28,18 @@ "reauth_failed_existing": "Could not update the config entry, please remove the integration and set it up again.", "unknown": "[%key:common::config_flow::error::unknown%]" } + }, + "entity": { + "sensor": { + "monitor_status": { + "state": { + "down": "Down", + "not_checked_yet": "Not checked yet", + "pause": "Pause", + "seems_down": "Seems down", + "up": "Up" + } + } + } } } diff --git a/homeassistant/components/uptimerobot/strings.sensor.json b/homeassistant/components/uptimerobot/strings.sensor.json deleted file mode 100644 index 700f7cd9f4c..00000000000 --- a/homeassistant/components/uptimerobot/strings.sensor.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "state": { - "uptimerobot__monitor_status": { - "pause": "Pause", - "not_checked_yet": "Not checked yet", - "up": "Up", - "seems_down": "Seems down", - "down": "Down" - } - } -} diff --git a/homeassistant/components/uptimerobot/translations/bg.json b/homeassistant/components/uptimerobot/translations/bg.json index d729a88dbc0..eea44357be7 100644 --- a/homeassistant/components/uptimerobot/translations/bg.json +++ b/homeassistant/components/uptimerobot/translations/bg.json @@ -23,5 +23,14 @@ } } } + }, + "entity": { + "sensor": { + "monitor_status": { + "state": { + "pause": "\u041f\u0430\u0443\u0437\u0430" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/uptimerobot/translations/ca.json b/homeassistant/components/uptimerobot/translations/ca.json index a6ed19c11f0..441f0fddc81 100644 --- a/homeassistant/components/uptimerobot/translations/ca.json +++ b/homeassistant/components/uptimerobot/translations/ca.json @@ -28,5 +28,18 @@ "description": "Has de proporcionar la clau API 'principal' d'UptimeRobot" } } + }, + "entity": { + "sensor": { + "monitor_status": { + "state": { + "down": "Caigut", + "not_checked_yet": "Encara no comprovat", + "pause": "En pausa", + "seems_down": "Sembla caigut", + "up": "Funcionant" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/uptimerobot/translations/de.json b/homeassistant/components/uptimerobot/translations/de.json index ee2af73ea20..8ec31b0e9d1 100644 --- a/homeassistant/components/uptimerobot/translations/de.json +++ b/homeassistant/components/uptimerobot/translations/de.json @@ -28,5 +28,18 @@ "description": "Du musst den Haupt-API-Schl\u00fcssel von UptimeRobot bereitstellen" } } + }, + "entity": { + "sensor": { + "monitor_status": { + "state": { + "down": "Offline", + "not_checked_yet": "Noch nicht gepr\u00fcft", + "pause": "Pause", + "seems_down": "Scheint offline zu sein", + "up": "Online" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/uptimerobot/translations/el.json b/homeassistant/components/uptimerobot/translations/el.json index 98034c40eee..e0ee9b2a39c 100644 --- a/homeassistant/components/uptimerobot/translations/el.json +++ b/homeassistant/components/uptimerobot/translations/el.json @@ -28,5 +28,18 @@ "description": "\u03a0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03c0\u03b1\u03c1\u03ad\u03c7\u03b5\u03c4\u03b5 \u03ad\u03bd\u03b1 \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API \u03bc\u03cc\u03bd\u03bf \u03b3\u03b9\u03b1 \u03b1\u03bd\u03ac\u03b3\u03bd\u03c9\u03c3\u03b7 \u03b1\u03c0\u03cc \u03c4\u03bf UptimeRobot" } } + }, + "entity": { + "sensor": { + "monitor_status": { + "state": { + "down": "\u039a\u03ac\u03c4\u03c9", + "not_checked_yet": "\u0394\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03b5\u03bb\u03b5\u03b3\u03c7\u03b8\u03b5\u03af \u03b1\u03ba\u03cc\u03bc\u03b1", + "pause": "\u03a0\u03b1\u03cd\u03c3\u03b7", + "seems_down": "\u03a6\u03b1\u03af\u03bd\u03b5\u03c4\u03b1\u03b9 \u03bd\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03ba\u03c4\u03cc\u03c2", + "up": "\u03a0\u03ac\u03bd\u03c9" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/uptimerobot/translations/en.json b/homeassistant/components/uptimerobot/translations/en.json index f4fe398a195..eca66f395c1 100644 --- a/homeassistant/components/uptimerobot/translations/en.json +++ b/homeassistant/components/uptimerobot/translations/en.json @@ -28,5 +28,18 @@ "description": "You need to supply the 'main' API key from UptimeRobot" } } + }, + "entity": { + "sensor": { + "monitor_status": { + "state": { + "down": "Down", + "not_checked_yet": "Not checked yet", + "pause": "Pause", + "seems_down": "Seems down", + "up": "Up" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/uptimerobot/translations/es.json b/homeassistant/components/uptimerobot/translations/es.json index 6bc4fe5adf6..f680c63626d 100644 --- a/homeassistant/components/uptimerobot/translations/es.json +++ b/homeassistant/components/uptimerobot/translations/es.json @@ -28,5 +28,18 @@ "description": "Debes proporcionar la clave API 'principal' de UptimeRobot" } } + }, + "entity": { + "sensor": { + "monitor_status": { + "state": { + "down": "Ca\u00eddo", + "not_checked_yet": "A\u00fan no se ha comprobado", + "pause": "Pausa", + "seems_down": "Parece ca\u00eddo", + "up": "Arriba" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/uptimerobot/translations/et.json b/homeassistant/components/uptimerobot/translations/et.json index ed2e46b7cc2..209874433da 100644 --- a/homeassistant/components/uptimerobot/translations/et.json +++ b/homeassistant/components/uptimerobot/translations/et.json @@ -28,5 +28,18 @@ "description": "Pead sisestama UptimeRoboti peamise API-v\u00f5tme" } } + }, + "entity": { + "sensor": { + "monitor_status": { + "state": { + "down": "\u00dchenduseta", + "not_checked_yet": "Pole veel kontrollitud", + "pause": "Paus", + "seems_down": "Tundub kadunud olevat", + "up": "\u00dchendatud" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/uptimerobot/translations/hu.json b/homeassistant/components/uptimerobot/translations/hu.json index 4a607c03303..41f7de1f860 100644 --- a/homeassistant/components/uptimerobot/translations/hu.json +++ b/homeassistant/components/uptimerobot/translations/hu.json @@ -28,5 +28,18 @@ "description": "Meg kell adnia a \u201ef\u0151\u201d API-kulcsot az UptimeRobott\u00f3l" } } + }, + "entity": { + "sensor": { + "monitor_status": { + "state": { + "down": "Nem el\u00e9rhet\u0151", + "not_checked_yet": "M\u00e9g nincs ellen\u0151rizve", + "pause": "Sz\u00fcnet", + "seems_down": "Nem el\u00e9rhet\u0151nek t\u0171nik", + "up": "El\u00e9rhet\u0151" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/uptimerobot/translations/id.json b/homeassistant/components/uptimerobot/translations/id.json index b92f8f74027..30343ae8f82 100644 --- a/homeassistant/components/uptimerobot/translations/id.json +++ b/homeassistant/components/uptimerobot/translations/id.json @@ -28,5 +28,18 @@ "description": "Anda perlu menyediakan kunci API 'main' dari UptimeRobot" } } + }, + "entity": { + "sensor": { + "monitor_status": { + "state": { + "down": "Mati", + "not_checked_yet": "Belum diperiksa", + "pause": "Jeda", + "seems_down": "Sepertinya mati", + "up": "Nyala" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/uptimerobot/translations/it.json b/homeassistant/components/uptimerobot/translations/it.json index 4359ab682ee..5adc51f1342 100644 --- a/homeassistant/components/uptimerobot/translations/it.json +++ b/homeassistant/components/uptimerobot/translations/it.json @@ -28,5 +28,18 @@ "description": "Devi fornire la chiave API 'principale' da UptimeRobot" } } + }, + "entity": { + "sensor": { + "monitor_status": { + "state": { + "down": "Spento", + "not_checked_yet": "Non ancora controllato", + "pause": "Pausa", + "seems_down": "Sembra spento", + "up": "Acceso" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/uptimerobot/translations/ko.json b/homeassistant/components/uptimerobot/translations/ko.json new file mode 100644 index 00000000000..990fe0aa9ee --- /dev/null +++ b/homeassistant/components/uptimerobot/translations/ko.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_api_key": "API \ud0a4\uac00 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "API \ud0a4" + }, + "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" + }, + "user": { + "data": { + "api_key": "API \ud0a4" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/uptimerobot/translations/no.json b/homeassistant/components/uptimerobot/translations/no.json index e3cbe428b64..61672642500 100644 --- a/homeassistant/components/uptimerobot/translations/no.json +++ b/homeassistant/components/uptimerobot/translations/no.json @@ -28,5 +28,18 @@ "description": "Du m\u00e5 oppgi \"hoved\" API-n\u00f8kkelen fra UptimeRobot" } } + }, + "entity": { + "sensor": { + "monitor_status": { + "state": { + "down": "Ned", + "not_checked_yet": "Ikke sjekket enn\u00e5", + "pause": "Pause", + "seems_down": "Virker nede", + "up": "Opp" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/uptimerobot/translations/pl.json b/homeassistant/components/uptimerobot/translations/pl.json index d5698192e6d..00cc07a7ce2 100644 --- a/homeassistant/components/uptimerobot/translations/pl.json +++ b/homeassistant/components/uptimerobot/translations/pl.json @@ -28,5 +28,18 @@ "description": "Musisz poda\u0107 \"g\u0142\u00f3wny\" klucz API od Uptime Robot" } } + }, + "entity": { + "sensor": { + "monitor_status": { + "state": { + "down": "offline", + "not_checked_yet": "jeszcze nie sprawdzone", + "pause": "wstrzymano", + "seems_down": "prawdopodobnie offline", + "up": "online" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/uptimerobot/translations/pt-BR.json b/homeassistant/components/uptimerobot/translations/pt-BR.json index 4e905f67b31..7db6bfe3fd9 100644 --- a/homeassistant/components/uptimerobot/translations/pt-BR.json +++ b/homeassistant/components/uptimerobot/translations/pt-BR.json @@ -28,5 +28,18 @@ "description": "Voc\u00ea precisa fornecer a chave de API 'principal' do UptimeRobot" } } + }, + "entity": { + "sensor": { + "monitor_status": { + "state": { + "down": "Baixo", + "not_checked_yet": "Ainda n\u00e3o verificado", + "pause": "Pausa", + "seems_down": "Parece estar para baixo", + "up": "Para cima" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/uptimerobot/translations/ru.json b/homeassistant/components/uptimerobot/translations/ru.json index 4497dc5a362..fb490c55f55 100644 --- a/homeassistant/components/uptimerobot/translations/ru.json +++ b/homeassistant/components/uptimerobot/translations/ru.json @@ -28,5 +28,18 @@ "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 'main' \u043a\u043b\u044e\u0447 API UptimeRobot." } } + }, + "entity": { + "sensor": { + "monitor_status": { + "state": { + "down": "\u041d\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442", + "not_checked_yet": "\u0415\u0449\u0451 \u043d\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u0435\u043d\u043e", + "pause": "\u041f\u0430\u0443\u0437\u0430", + "seems_down": "\u041f\u043e\u0445\u043e\u0436\u0435, \u0447\u0442\u043e \u043d\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442", + "up": "\u0420\u0430\u0431\u043e\u0442\u0430\u0435\u0442" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/uptimerobot/translations/sensor.sk.json b/homeassistant/components/uptimerobot/translations/sensor.sk.json index 4177567e40e..348da39ac5e 100644 --- a/homeassistant/components/uptimerobot/translations/sensor.sk.json +++ b/homeassistant/components/uptimerobot/translations/sensor.sk.json @@ -1,7 +1,11 @@ { "state": { "uptimerobot__monitor_status": { - "not_checked_yet": "Zatia\u013e neskontrolovan\u00e9" + "down": "Dole", + "not_checked_yet": "Zatia\u013e neskontrolovan\u00e9", + "pause": "Pauza", + "seems_down": "Zd\u00e1 sa, \u017ee je offline", + "up": "Hore" } } } \ No newline at end of file diff --git a/homeassistant/components/uptimerobot/translations/sk.json b/homeassistant/components/uptimerobot/translations/sk.json index e9d14e025a2..53e9f7caf96 100644 --- a/homeassistant/components/uptimerobot/translations/sk.json +++ b/homeassistant/components/uptimerobot/translations/sk.json @@ -2,6 +2,7 @@ "config": { "abort": { "already_configured": "\u00da\u010det je u\u017e nakonfigurovan\u00fd", + "reauth_failed_existing": "Nepodarilo sa aktualizova\u0165 polo\u017eku konfigur\u00e1cie, odstr\u00e1\u0148te integr\u00e1ciu a znova ju nastavte.", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, @@ -9,6 +10,7 @@ "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d", "not_main_key": "Bol zisten\u00fd nespr\u00e1vny typ k\u013e\u00fa\u010da API, pou\u017eite \u201emain\u201c k\u013e\u00fa\u010d API", + "reauth_failed_matching_account": "Zadan\u00fd k\u013e\u00fa\u010d API sa nezhoduje s ID \u00fa\u010dtu pre existuj\u00facu konfigur\u00e1ciu.", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { @@ -16,11 +18,26 @@ "data": { "api_key": "API k\u013e\u00fa\u010d" }, + "description": "Mus\u00edte doda\u0165 nov\u00fd \u201ehlavn\u00fd\u201c k\u013e\u00fa\u010d API od UptimeRobot", "title": "Znova overi\u0165 integr\u00e1ciu" }, "user": { "data": { "api_key": "API k\u013e\u00fa\u010d" + }, + "description": "Mus\u00edte doda\u0165 \u201ehlavn\u00fd\u201c k\u013e\u00fa\u010d API od UptimeRobot" + } + } + }, + "entity": { + "sensor": { + "monitor_status": { + "state": { + "down": "Offline", + "not_checked_yet": "Zatia\u013e neskontrolovan\u00e9", + "pause": "Pauza", + "seems_down": "Zd\u00e1 sa, \u017ee je offline", + "up": "Online" } } } diff --git a/homeassistant/components/uptimerobot/translations/zh-Hant.json b/homeassistant/components/uptimerobot/translations/zh-Hant.json index e8edfbf1934..2f808ebdeb2 100644 --- a/homeassistant/components/uptimerobot/translations/zh-Hant.json +++ b/homeassistant/components/uptimerobot/translations/zh-Hant.json @@ -28,5 +28,18 @@ "description": "\u5fc5\u9808\u63d0\u4f9b\u7531 UptimeRobot \u53d6\u5f97\u4e4b 'main' API \u91d1\u9470" } } + }, + "entity": { + "sensor": { + "monitor_status": { + "state": { + "down": "\u96e2\u7dda", + "not_checked_yet": "\u5c1a\u672a\u6aa2\u67e5", + "pause": "\u66ab\u505c", + "seems_down": "\u4f3c\u4e4e\u96e2\u7dda", + "up": "\u7dda\u4e0a" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/usgs_earthquakes_feed/geo_location.py b/homeassistant/components/usgs_earthquakes_feed/geo_location.py index c82705174fe..28927baf926 100644 --- a/homeassistant/components/usgs_earthquakes_feed/geo_location.py +++ b/homeassistant/components/usgs_earthquakes_feed/geo_location.py @@ -20,7 +20,7 @@ from homeassistant.const import ( CONF_RADIUS, CONF_SCAN_INTERVAL, EVENT_HOMEASSISTANT_START, - LENGTH_KILOMETERS, + UnitOfLength, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import aiohttp_client @@ -48,7 +48,7 @@ CONF_MINIMUM_MAGNITUDE = "minimum_magnitude" DEFAULT_MINIMUM_MAGNITUDE = 0.0 DEFAULT_RADIUS_IN_KM = 50.0 -DEFAULT_UNIT_OF_MEASUREMENT = LENGTH_KILOMETERS +DEFAULT_UNIT_OF_MEASUREMENT = UnitOfLength.KILOMETERS SCAN_INTERVAL = timedelta(minutes=5) diff --git a/homeassistant/components/utility_meter/__init__.py b/homeassistant/components/utility_meter/__init__.py index ea5fb4933be..c436ea757ac 100644 --- a/homeassistant/components/utility_meter/__init__.py +++ b/homeassistant/components/utility_meter/__init__.py @@ -62,10 +62,10 @@ def period_or_cron(config): def max_28_days(config): - """Check that time period does not include more then 28 days.""" + """Check that time period does not include more than 28 days.""" if config.days >= 28: raise vol.Invalid( - "Unsupported offset of more then 28 days, please use a cron pattern." + "Unsupported offset of more than 28 days, please use a cron pattern." ) return config diff --git a/homeassistant/components/utility_meter/sensor.py b/homeassistant/components/utility_meter/sensor.py index e1f5ef052e0..04909c1b7ae 100644 --- a/homeassistant/components/utility_meter/sensor.py +++ b/homeassistant/components/utility_meter/sensor.py @@ -22,10 +22,9 @@ from homeassistant.const import ( ATTR_UNIT_OF_MEASUREMENT, CONF_NAME, CONF_UNIQUE_ID, - ENERGY_KILO_WATT_HOUR, - ENERGY_WATT_HOUR, STATE_UNAVAILABLE, STATE_UNKNOWN, + UnitOfEnergy, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import entity_platform, entity_registry as er @@ -88,8 +87,8 @@ ATTR_LAST_PERIOD = "last_period" ATTR_TARIFF = "tariff" DEVICE_CLASS_MAP = { - ENERGY_WATT_HOUR: SensorDeviceClass.ENERGY, - ENERGY_KILO_WATT_HOUR: SensorDeviceClass.ENERGY, + UnitOfEnergy.WATT_HOUR: SensorDeviceClass.ENERGY, + UnitOfEnergy.KILO_WATT_HOUR: SensorDeviceClass.ENERGY, } ICON = "mdi:counter" diff --git a/homeassistant/components/utility_meter/translations/he.json b/homeassistant/components/utility_meter/translations/he.json new file mode 100644 index 00000000000..602d9ababd6 --- /dev/null +++ b/homeassistant/components/utility_meter/translations/he.json @@ -0,0 +1,35 @@ +{ + "config": { + "step": { + "user": { + "data": { + "cycle": "\u05de\u05d7\u05d6\u05d5\u05e8 \u05d0\u05d9\u05e4\u05d5\u05e1 \u05de\u05d3", + "delta_values": "\u05e2\u05e8\u05db\u05d9 \u05d3\u05dc\u05ea\u05d0", + "name": "\u05e9\u05dd", + "net_consumption": "\u05e6\u05e8\u05d9\u05db\u05d4 \u05e0\u05d8\u05d5", + "offset": "\u05d4\u05d9\u05e1\u05d8 \u05d0\u05d9\u05e4\u05d5\u05e1 \u05de\u05d3", + "source": "\u05d7\u05d9\u05d9\u05e9\u05df \u05e7\u05dc\u05d8", + "tariffs": "\u05ea\u05e2\u05e8\u05d9\u05e4\u05d9\u05dd \u05e0\u05ea\u05de\u05db\u05d9\u05dd" + }, + "data_description": { + "delta_values": "\u05dc\u05d0\u05e4\u05e9\u05e8 \u05d0\u05dd \u05e2\u05e8\u05db\u05d9 \u05d4\u05de\u05e7\u05d5\u05e8 \u05d4\u05dd \u05e2\u05e8\u05db\u05d9 \u05d3\u05dc\u05ea\u05d0 \u05de\u05d0\u05d6 \u05d4\u05e7\u05e8\u05d9\u05d0\u05d4 \u05d4\u05d0\u05d7\u05e8\u05d5\u05e0\u05d4 \u05d1\u05de\u05e7\u05d5\u05dd \u05e2\u05e8\u05db\u05d9\u05dd \u05de\u05d5\u05d7\u05dc\u05d8\u05d9\u05dd.", + "net_consumption": "\u05dc\u05d0\u05e4\u05e9\u05e8 \u05d0\u05dd \u05d4\u05de\u05e7\u05d5\u05e8 \u05d4\u05d5\u05d0 \u05de\u05d5\u05e0\u05d4 \u05e0\u05d8\u05d5, \u05db\u05dc\u05d5\u05de\u05e8 \u05d4\u05d5\u05d0 \u05d9\u05db\u05d5\u05dc \u05d2\u05dd \u05dc\u05d4\u05d2\u05d3\u05d9\u05dc \u05d5\u05d2\u05dd \u05dc\u05d4\u05e7\u05d8\u05d9\u05df.", + "offset": "\u05e7\u05d9\u05d6\u05d5\u05d6 \u05d4\u05d9\u05d5\u05dd \u05e9\u05dc \u05d0\u05d9\u05e4\u05d5\u05e1 \u05de\u05d5\u05e0\u05d4 \u05d7\u05d5\u05d3\u05e9\u05d9.", + "tariffs": "\u05e8\u05e9\u05d9\u05de\u05d4 \u05e9\u05dc \u05ea\u05e2\u05e8\u05d9\u05e4\u05d9\u05dd \u05e0\u05ea\u05de\u05db\u05d9\u05dd, \u05dc\u05d4\u05e9\u05d0\u05d9\u05e8 \u05e8\u05d9\u05e7 \u05d0\u05dd \u05d9\u05e9 \u05e6\u05d5\u05e8\u05da \u05d1\u05ea\u05e2\u05e8\u05d9\u05e3 \u05d1\u05d5\u05d3\u05d3 \u05d1\u05dc\u05d1\u05d3." + }, + "description": "\u05d9\u05e6\u05d9\u05e8\u05ea \u05d7\u05d9\u05d9\u05e9\u05df \u05d4\u05e2\u05d5\u05e7\u05d1 \u05d0\u05d7\u05e8 \u05d4\u05e6\u05e8\u05d9\u05db\u05d4 \u05e9\u05dc \u05db\u05dc\u05d9 \u05e2\u05d6\u05e8 \u05e9\u05d5\u05e0\u05d9\u05dd (\u05dc\u05d3\u05d5\u05d2\u05de\u05d4, \u05d0\u05e0\u05e8\u05d2\u05d9\u05d4, \u05d2\u05d6, \u05de\u05d9\u05dd, \u05d7\u05d9\u05de\u05d5\u05dd) \u05dc\u05d0\u05d5\u05e8\u05da \u05e4\u05e8\u05e7 \u05d6\u05de\u05df \u05de\u05d5\u05d2\u05d3\u05e8, \u05d1\u05d3\u05e8\u05da \u05db\u05dc\u05dc \u05d7\u05d5\u05d3\u05e9\u05d9. \u05d7\u05d9\u05d9\u05e9\u05df \u05de\u05d3 \u05d4\u05e9\u05d9\u05e8\u05d5\u05ea \u05ea\u05d5\u05de\u05da \u05d1\u05d0\u05d5\u05e4\u05df \u05d0\u05d5\u05e4\u05e6\u05d9\u05d5\u05e0\u05dc\u05d9 \u05d1\u05e4\u05d9\u05e6\u05d5\u05dc \u05d4\u05e6\u05e8\u05d9\u05db\u05d4 \u05dc\u05e4\u05d9 \u05ea\u05e2\u05e8\u05d9\u05e4\u05d9\u05dd, \u05d1\u05de\u05e7\u05e8\u05d4 \u05d6\u05d4 \u05e0\u05d5\u05e6\u05e8 \u05d7\u05d9\u05d9\u05e9\u05df \u05d0\u05d7\u05d3 \u05dc\u05db\u05dc \u05ea\u05e2\u05e8\u05d9\u05e3 \u05d5\u05db\u05df \u05d9\u05e9\u05d5\u05ea \u05e0\u05d1\u05d7\u05e8\u05ea \u05dc\u05d1\u05d7\u05d9\u05e8\u05ea \u05d4\u05ea\u05e2\u05e8\u05d9\u05e3 \u05d4\u05e0\u05d5\u05db\u05d7\u05d9.", + "title": "\u05d4\u05d5\u05e1\u05e4\u05ea \u05de\u05d3 \u05e9\u05d9\u05e8\u05d5\u05ea" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source": "\u05d7\u05d9\u05d9\u05e9\u05df \u05e7\u05dc\u05d8" + } + } + } + }, + "title": "\u05de\u05d3 \u05e9\u05d9\u05e8\u05d5\u05ea" +} \ No newline at end of file diff --git a/homeassistant/components/utility_meter/translations/sk.json b/homeassistant/components/utility_meter/translations/sk.json index 54c043ce6e2..da702823218 100644 --- a/homeassistant/components/utility_meter/translations/sk.json +++ b/homeassistant/components/utility_meter/translations/sk.json @@ -3,10 +3,22 @@ "step": { "user": { "data": { + "cycle": "Cyklus vynulovania mera\u010da", + "delta_values": "Rozdielov\u00e9 hodnoty", "name": "N\u00e1zov", + "net_consumption": "\u010cist\u00e1 spotreba", + "offset": "Offset vynulovania mera\u010da", "source": "Vstupn\u00fd sn\u00edma\u010d", "tariffs": "Podporovan\u00e9 tarify" - } + }, + "data_description": { + "delta_values": "Povo\u013ete, ak s\u00fa zdrojov\u00e9 hodnoty delta hodnoty od posledn\u00e9ho od\u010d\u00edtania namiesto absol\u00fatnych hodn\u00f4t.", + "net_consumption": "Povo\u013ete, ak je zdroj \u010dist\u00fdm mera\u010dom, \u010do znamen\u00e1, \u017ee sa m\u00f4\u017ee zvy\u0161ova\u0165 aj zni\u017eova\u0165.", + "offset": "Offset d\u0148a mesa\u010dn\u00e9ho vynulovania mera\u010da.", + "tariffs": "Zoznam podporovan\u00fdch tar\u00edf, ponechajte pr\u00e1zdne, ak potrebujete iba jeden tarif." + }, + "description": "Vytvorte sn\u00edma\u010d, ktor\u00fd sleduje spotrebu r\u00f4znych zariaden\u00ed (napr. energie, plynu, vody, k\u00farenia) po\u010das nastaven\u00e9ho \u010dasov\u00e9ho obdobia, zvy\u010dajne mesa\u010dne. Sn\u00edma\u010d elektromeru volite\u013ene podporuje rozdelenie spotreby pod\u013ea tar\u00edf, v tomto pr\u00edpade je vytvoren\u00fd jeden sn\u00edma\u010d pre ka\u017ed\u00fa tarifu a z\u00e1rove\u0148 aj v\u00fdber entity pre v\u00fdber aktu\u00e1lnej tarify.", + "title": "Prida\u0165 mera\u010d obslu\u017en\u00fdch slu\u017eieb" } } }, @@ -18,5 +30,6 @@ } } } - } + }, + "title": "Mera\u010d spotreby" } \ No newline at end of file diff --git a/homeassistant/components/vacuum/translations/el.json b/homeassistant/components/vacuum/translations/el.json index 7c849d93998..191c5995ee3 100644 --- a/homeassistant/components/vacuum/translations/el.json +++ b/homeassistant/components/vacuum/translations/el.json @@ -1,29 +1,29 @@ { "device_automation": { "action_type": { - "clean": "\u0391\u03c6\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf {entity_name} \u03bd\u03b1 \u03ba\u03b1\u03b8\u03b1\u03c1\u03af\u03c3\u03b5\u03b9", - "dock": "\u0391\u03c6\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf {entity_name} \u03bd\u03b1 \u03b5\u03c0\u03b9\u03c3\u03c4\u03c1\u03ad\u03c8\u03b5\u03b9 \u03c3\u03c4\u03b7 \u03b2\u03ac\u03c3\u03b7" + "clean": "\u0391\u03c6\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7 {entity_name} \u03bd\u03b1 \u03ba\u03b1\u03b8\u03b1\u03c1\u03af\u03c3\u03b5\u03b9", + "dock": "\u0391\u03c6\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7 {entity_name} \u03bd\u03b1 \u03b5\u03c0\u03b9\u03c3\u03c4\u03c1\u03ad\u03c8\u03b5\u03b9 \u03c3\u03c4\u03b7 \u03b2\u03ac\u03c3\u03b7" }, "condition_type": { - "is_cleaning": "{entity_name} \u03ba\u03b1\u03b8\u03b1\u03c1\u03af\u03b6\u03b5\u03b9", - "is_docked": "{entity_name} \u03b2\u03c1\u03af\u03c3\u03ba\u03b5\u03c4\u03b1\u03b9 \u03c3\u03c4\u03b7 \u03b2\u03ac\u03c3\u03b7" + "is_cleaning": "\u0397 {entity_name} \u03ba\u03b1\u03b8\u03b1\u03c1\u03af\u03b6\u03b5\u03b9", + "is_docked": "\u0397 {entity_name} \u03b2\u03c1\u03af\u03c3\u03ba\u03b5\u03c4\u03b1\u03b9 \u03c3\u03c4\u03b7 \u03b2\u03ac\u03c3\u03b7" }, "trigger_type": { - "cleaning": "{entity_name} \u03be\u03b5\u03ba\u03af\u03bd\u03b7\u03c3\u03b5 \u03c4\u03bf\u03bd \u03ba\u03b1\u03b8\u03b1\u03c1\u03b9\u03c3\u03bc\u03cc", - "docked": "{entity_name} \u03b3\u03cd\u03c1\u03b9\u03c3\u03b5 \u03c3\u03c4\u03b7 \u03b2\u03ac\u03c3\u03b7" + "cleaning": "\u0397 {entity_name} \u03be\u03b5\u03ba\u03af\u03bd\u03b7\u03c3\u03b5 \u03c4\u03bf \u03ba\u03b1\u03b8\u03ac\u03c1\u03b9\u03c3\u03bc\u03b1", + "docked": "\u0397 {entity_name} \u03b2\u03c1\u03af\u03c3\u03ba\u03b5\u03c4\u03b1\u03b9 \u03c3\u03c4\u03b7 \u03b2\u03ac\u03c3\u03b7" } }, "state": { "_": { "cleaning": "\u039a\u03b1\u03b8\u03b1\u03c1\u03af\u03b6\u03b5\u03b9", - "docked": "\u03a6\u03bf\u03c1\u03c4\u03af\u03b6\u03b5\u03b9", + "docked": "\u03a3\u03c4\u03b7 \u03b2\u03ac\u03c3\u03b7", "error": "\u03a3\u03c6\u03ac\u03bb\u03bc\u03b1", "idle": "\u03a3\u03b5 \u03b1\u03b4\u03c1\u03ac\u03bd\u03b5\u03b9\u03b1", "off": "\u0395\u03ba\u03c4\u03cc\u03c2 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1\u03c2", "on": "\u03a3\u03b5 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1", - "paused": "\u03a0\u03b1\u03cd\u03c3\u03b7", - "returning": "\u03a0\u03c1\u03bf\u03c2 \u03c6\u03cc\u03c1\u03c4\u03b9\u03c3\u03b7" + "paused": "\u03a3\u03b5 \u03c0\u03b1\u03cd\u03c3\u03b7", + "returning": "\u0395\u03c0\u03b9\u03c3\u03c4\u03c1\u03bf\u03c6\u03ae \u03c3\u03c4\u03b7 \u03b2\u03ac\u03c3\u03b7" } }, - "title": "\u03a1\u03bf\u03bc\u03c0\u03bf\u03c4\u03b9\u03ba\u03ae \u03c3\u03ba\u03bf\u03cd\u03c0\u03b1" + "title": "\u03a3\u03ba\u03bf\u03cd\u03c0\u03b1" } \ No newline at end of file diff --git a/homeassistant/components/vacuum/translations/en_GB.json b/homeassistant/components/vacuum/translations/en-GB.json similarity index 100% rename from homeassistant/components/vacuum/translations/en_GB.json rename to homeassistant/components/vacuum/translations/en-GB.json diff --git a/homeassistant/components/vacuum/translations/lt.json b/homeassistant/components/vacuum/translations/lt.json index 3cfb5717736..736423f1661 100644 --- a/homeassistant/components/vacuum/translations/lt.json +++ b/homeassistant/components/vacuum/translations/lt.json @@ -1,7 +1,7 @@ { "state": { "_": { - "docked": "Prikabinta" + "docked": "Priparkuotas" } } } \ No newline at end of file diff --git a/homeassistant/components/vacuum/translations/sk.json b/homeassistant/components/vacuum/translations/sk.json index a0782fb4cdc..b71ee238066 100644 --- a/homeassistant/components/vacuum/translations/sk.json +++ b/homeassistant/components/vacuum/translations/sk.json @@ -1,4 +1,18 @@ { + "device_automation": { + "action_type": { + "clean": "Nechajte {entity_name} uprata\u0165", + "dock": "Nechajte {entity_name} vr\u00e1ti\u0165 sa do doku" + }, + "condition_type": { + "is_cleaning": "{entity_name} upratuje", + "is_docked": "{entity_name} je v doku" + }, + "trigger_type": { + "cleaning": "{entity_name} za\u010dala upratova\u0165", + "docked": "{entity_name} v doku" + } + }, "state": { "_": { "cleaning": "\u010cist\u00ed", diff --git a/homeassistant/components/vallox/__init__.py b/homeassistant/components/vallox/__init__.py index f393342dfd5..dceffb47a21 100644 --- a/homeassistant/components/vallox/__init__.py +++ b/homeassistant/components/vallox/__init__.py @@ -8,8 +8,7 @@ import logging from typing import Any, NamedTuple, cast from uuid import UUID -from vallox_websocket_api import PROFILE as VALLOX_PROFILE, Vallox -from vallox_websocket_api.exceptions import ValloxApiException +from vallox_websocket_api import PROFILE as VALLOX_PROFILE, Vallox, ValloxApiException from vallox_websocket_api.vallox import ( get_model as _api_get_model, get_next_filter_change_date as _api_get_next_filter_change_date, @@ -191,7 +190,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: metric_cache = await client.fetch_metrics() profile = await client.get_profile() - except (OSError, ValloxApiException) as err: + except ValloxApiException as err: raise UpdateFailed("Error during state cache update") from err return ValloxState(metric_cache, profile) @@ -262,7 +261,7 @@ class ValloxServiceHandler: ) return True - except (OSError, ValloxApiException) as err: + except ValloxApiException as err: _LOGGER.error("Error setting fan speed for Home profile: %s", err) return False @@ -278,7 +277,7 @@ class ValloxServiceHandler: ) return True - except (OSError, ValloxApiException) as err: + except ValloxApiException as err: _LOGGER.error("Error setting fan speed for Away profile: %s", err) return False @@ -294,7 +293,7 @@ class ValloxServiceHandler: ) return True - except (OSError, ValloxApiException) as err: + except ValloxApiException as err: _LOGGER.error("Error setting fan speed for Boost profile: %s", err) return False diff --git a/homeassistant/components/vallox/config_flow.py b/homeassistant/components/vallox/config_flow.py index d30c8641d2c..b9d29b17689 100644 --- a/homeassistant/components/vallox/config_flow.py +++ b/homeassistant/components/vallox/config_flow.py @@ -4,8 +4,7 @@ from __future__ import annotations import logging from typing import Any -from vallox_websocket_api import Vallox -from vallox_websocket_api.exceptions import ValloxApiException +from vallox_websocket_api import Vallox, ValloxApiException import voluptuous as vol from homeassistant import config_entries @@ -25,11 +24,6 @@ STEP_USER_DATA_SCHEMA = vol.Schema( } ) -VALLOX_CONNECTION_EXCEPTIONS = ( - OSError, - ValloxApiException, -) - async def validate_host(hass: HomeAssistant, host: str) -> None: """Validate that the user input allows us to connect.""" @@ -61,7 +55,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): except InvalidHost: _LOGGER.error("An invalid host is configured for Vallox: %s", host) reason = "invalid_host" - except VALLOX_CONNECTION_EXCEPTIONS: + except ValloxApiException: _LOGGER.error("Cannot connect to Vallox host %s", host) reason = "cannot_connect" except Exception: # pylint: disable=broad-except @@ -98,7 +92,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): await validate_host(self.hass, host) except InvalidHost: errors[CONF_HOST] = "invalid_host" - except VALLOX_CONNECTION_EXCEPTIONS: + except ValloxApiException: errors[CONF_HOST] = "cannot_connect" except Exception: # pylint: disable=broad-except _LOGGER.exception("Unexpected exception") diff --git a/homeassistant/components/vallox/const.py b/homeassistant/components/vallox/const.py index aba10188bde..ef6115a2894 100644 --- a/homeassistant/components/vallox/const.py +++ b/homeassistant/components/vallox/const.py @@ -22,20 +22,20 @@ DEFAULT_FAN_SPEED_HOME = 50 DEFAULT_FAN_SPEED_AWAY = 25 DEFAULT_FAN_SPEED_BOOST = 65 -VALLOX_PROFILE_TO_STR_SETTABLE = { +VALLOX_PROFILE_TO_PRESET_MODE_SETTABLE = { VALLOX_PROFILE.HOME: "Home", VALLOX_PROFILE.AWAY: "Away", VALLOX_PROFILE.BOOST: "Boost", VALLOX_PROFILE.FIREPLACE: "Fireplace", } -VALLOX_PROFILE_TO_STR_REPORTABLE = { +VALLOX_PROFILE_TO_PRESET_MODE_REPORTABLE = { VALLOX_PROFILE.EXTRA: "Extra", - **VALLOX_PROFILE_TO_STR_SETTABLE, + **VALLOX_PROFILE_TO_PRESET_MODE_SETTABLE, } -STR_TO_VALLOX_PROFILE_SETTABLE = { - value: key for (key, value) in VALLOX_PROFILE_TO_STR_SETTABLE.items() +PRESET_MODE_TO_VALLOX_PROFILE_SETTABLE = { + value: key for (key, value) in VALLOX_PROFILE_TO_PRESET_MODE_SETTABLE.items() } VALLOX_CELL_STATE_TO_STR = { diff --git a/homeassistant/components/vallox/fan.py b/homeassistant/components/vallox/fan.py index be713e34e25..50c30927fde 100644 --- a/homeassistant/components/vallox/fan.py +++ b/homeassistant/components/vallox/fan.py @@ -2,11 +2,14 @@ from __future__ import annotations from collections.abc import Mapping -import logging from typing import Any, NamedTuple -from vallox_websocket_api import Vallox -from vallox_websocket_api.exceptions import ValloxApiException +from vallox_websocket_api import ( + PROFILE_TO_SET_FAN_SPEED_METRIC_MAP, + Vallox, + ValloxApiException, + ValloxInvalidInputException, +) from homeassistant.components.fan import ( FanEntity, @@ -15,6 +18,7 @@ from homeassistant.components.fan import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import StateType @@ -27,12 +31,10 @@ from .const import ( METRIC_KEY_PROFILE_FAN_SPEED_HOME, MODE_OFF, MODE_ON, - STR_TO_VALLOX_PROFILE_SETTABLE, - VALLOX_PROFILE_TO_STR_SETTABLE, + PRESET_MODE_TO_VALLOX_PROFILE_SETTABLE, + VALLOX_PROFILE_TO_PRESET_MODE_REPORTABLE, ) -_LOGGER = logging.getLogger(__name__) - class ExtraStateAttributeDetails(NamedTuple): """Extra state attribute details.""" @@ -54,7 +56,7 @@ EXTRA_STATE_ATTRIBUTES = ( ) -def _convert_fan_speed_value(value: StateType) -> int | None: +def _convert_to_int(value: StateType) -> int | None: if isinstance(value, (int, float)): return int(value) @@ -68,7 +70,6 @@ async def async_setup_entry( data = hass.data[DOMAIN][entry.entry_id] client = data["client"] - client.set_settable_address(METRIC_KEY_MODE, int) device = ValloxFanEntity( data["name"], @@ -82,8 +83,8 @@ async def async_setup_entry( class ValloxFanEntity(ValloxEntity, FanEntity): """Representation of the fan.""" - _attr_supported_features = FanEntityFeature.PRESET_MODE _attr_has_entity_name = True + _attr_supported_features = FanEntityFeature.PRESET_MODE | FanEntityFeature.SET_SPEED def __init__( self, @@ -97,12 +98,7 @@ class ValloxFanEntity(ValloxEntity, FanEntity): self._client = client self._attr_unique_id = str(self._device_uuid) - - @property - def preset_modes(self) -> list[str]: - """Return a list of available preset modes.""" - # Use the Vallox profile names for the preset names. - return list(STR_TO_VALLOX_PROFILE_SETTABLE.keys()) + self._attr_preset_modes = list(PRESET_MODE_TO_VALLOX_PROFILE_SETTABLE) @property def is_on(self) -> bool: @@ -113,7 +109,18 @@ class ValloxFanEntity(ValloxEntity, FanEntity): def preset_mode(self) -> str | None: """Return the current preset mode.""" vallox_profile = self.coordinator.data.profile - return VALLOX_PROFILE_TO_STR_SETTABLE.get(vallox_profile) + return VALLOX_PROFILE_TO_PRESET_MODE_REPORTABLE.get(vallox_profile) + + @property + def percentage(self) -> int | None: + """Return the current speed as a percentage.""" + + vallox_profile = self.coordinator.data.profile + metric_key = PROFILE_TO_SET_FAN_SPEED_METRIC_MAP.get(vallox_profile) + if not metric_key: + return None + + return _convert_to_int(self.coordinator.data.get_metric(metric_key)) @property def extra_state_attributes(self) -> Mapping[str, int | None]: @@ -121,35 +128,10 @@ class ValloxFanEntity(ValloxEntity, FanEntity): data = self.coordinator.data return { - attr.description: _convert_fan_speed_value(data.get_metric(attr.metric_key)) + attr.description: _convert_to_int(data.get_metric(attr.metric_key)) for attr in EXTRA_STATE_ATTRIBUTES } - async def _async_set_preset_mode_internal(self, preset_mode: str) -> bool: - """ - Set new preset mode. - - Returns true if the mode has been changed, false otherwise. - """ - try: - self._valid_preset_mode_or_raise(preset_mode) - - except NotValidPresetModeError as err: - _LOGGER.error(err) - return False - - if preset_mode == self.preset_mode: - return False - - try: - await self._client.set_profile(STR_TO_VALLOX_PROFILE_SETTABLE[preset_mode]) - - except (OSError, ValloxApiException) as err: - _LOGGER.error("Error setting preset: %s", err) - return False - - return True - async def async_set_preset_mode(self, preset_mode: str) -> None: """Set new preset mode.""" update_needed = await self._async_set_preset_mode_internal(preset_mode) @@ -166,22 +148,16 @@ class ValloxFanEntity(ValloxEntity, FanEntity): **kwargs: Any, ) -> None: """Turn the device on.""" - _LOGGER.debug("Turn on") - update_needed = False - if preset_mode: - update_needed = await self._async_set_preset_mode_internal(preset_mode) - if not self.is_on: - try: - await self._client.set_values({METRIC_KEY_MODE: MODE_ON}) + update_needed |= await self._async_set_power(True) - except OSError as err: - _LOGGER.error("Error turning on: %s", err) + if preset_mode: + update_needed |= await self._async_set_preset_mode_internal(preset_mode) - else: - update_needed = True + if percentage is not None: + update_needed |= await self._async_set_percentage_internal(percentage) if update_needed: # This state change affects other entities like sensors. Force an immediate update that @@ -193,12 +169,73 @@ class ValloxFanEntity(ValloxEntity, FanEntity): if not self.is_on: return - try: - await self._client.set_values({METRIC_KEY_MODE: MODE_OFF}) + update_needed = await self._async_set_power(False) - except OSError as err: - _LOGGER.error("Error turning off: %s", err) + if update_needed: + await self.coordinator.async_request_refresh() + + async def async_set_percentage(self, percentage: int) -> None: + """Set the speed of the fan, as a percentage.""" + if percentage == 0: + await self.async_turn_off() return - # Same as for turn_on method. - await self.coordinator.async_request_refresh() + update_needed = await self._async_set_percentage_internal(percentage) + + if update_needed: + await self.coordinator.async_request_refresh() + + async def _async_set_power(self, mode: bool) -> bool: + try: + await self._client.set_values( + {METRIC_KEY_MODE: MODE_ON if mode else MODE_OFF} + ) + except ValloxApiException as err: + raise HomeAssistantError("Failed to set power mode") from err + + return True + + async def _async_set_preset_mode_internal(self, preset_mode: str) -> bool: + """ + Set new preset mode. + + Returns true if the mode has been changed, false otherwise. + """ + try: + self._valid_preset_mode_or_raise(preset_mode) + + except NotValidPresetModeError as err: + raise ValueError(f"Not valid preset mode: {preset_mode}") from err + + if preset_mode == self.preset_mode: + return False + + try: + profile = PRESET_MODE_TO_VALLOX_PROFILE_SETTABLE[preset_mode] + await self._client.set_profile(profile) + self.coordinator.data.profile = profile + + except ValloxApiException as err: + raise HomeAssistantError(f"Failed to set profile: {preset_mode}") from err + + return True + + async def _async_set_percentage_internal(self, percentage: int) -> bool: + """ + Set fan speed percentage for current profile. + + Returns true if speed has been changed, false otherwise. + """ + vallox_profile = self.coordinator.data.profile + + try: + await self._client.set_fan_speed(vallox_profile, percentage) + except ValloxInvalidInputException as err: + # This can happen if current profile does not support setting the fan speed. + raise ValueError( + f"{vallox_profile} profile does not support setting the fan speed" + ) from err + except ValloxApiException as err: + raise HomeAssistantError("Failed to set fan speed") from err + + return True diff --git a/homeassistant/components/vallox/manifest.json b/homeassistant/components/vallox/manifest.json index 5c862562fc1..1e2783a5b9c 100644 --- a/homeassistant/components/vallox/manifest.json +++ b/homeassistant/components/vallox/manifest.json @@ -2,7 +2,7 @@ "domain": "vallox", "name": "Vallox", "documentation": "https://www.home-assistant.io/integrations/vallox", - "requirements": ["vallox-websocket-api==2.12.0"], + "requirements": ["vallox-websocket-api==3.0.0"], "codeowners": ["@andre-richter", "@slovdahl", "@viiru-"], "config_flow": true, "iot_class": "local_polling", diff --git a/homeassistant/components/vallox/number.py b/homeassistant/components/vallox/number.py index 5be91fe66e6..81d1557f938 100644 --- a/homeassistant/components/vallox/number.py +++ b/homeassistant/components/vallox/number.py @@ -11,7 +11,7 @@ from homeassistant.components.number import ( NumberEntityDescription, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import TEMP_CELSIUS +from homeassistant.const import UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -80,7 +80,7 @@ NUMBER_ENTITIES: tuple[ValloxNumberEntityDescription, ...] = ( name="Supply air temperature (Home)", metric_key="A_CYC_HOME_AIR_TEMP_TARGET", device_class=NumberDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, icon="mdi:thermometer", native_min_value=5.0, native_max_value=25.0, @@ -91,7 +91,7 @@ NUMBER_ENTITIES: tuple[ValloxNumberEntityDescription, ...] = ( name="Supply air temperature (Away)", metric_key="A_CYC_AWAY_AIR_TEMP_TARGET", device_class=NumberDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, icon="mdi:thermometer", native_min_value=5.0, native_max_value=25.0, @@ -102,7 +102,7 @@ NUMBER_ENTITIES: tuple[ValloxNumberEntityDescription, ...] = ( name="Supply air temperature (Boost)", metric_key="A_CYC_BOOST_AIR_TEMP_TARGET", device_class=NumberDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, icon="mdi:thermometer", native_min_value=5.0, native_max_value=25.0, diff --git a/homeassistant/components/vallox/sensor.py b/homeassistant/components/vallox/sensor.py index c349107a3f3..da3df00af50 100644 --- a/homeassistant/components/vallox/sensor.py +++ b/homeassistant/components/vallox/sensor.py @@ -15,7 +15,7 @@ from homeassistant.const import ( CONCENTRATION_PARTS_PER_MILLION, PERCENTAGE, REVOLUTIONS_PER_MINUTE, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory @@ -29,7 +29,7 @@ from .const import ( METRIC_KEY_MODE, MODE_ON, VALLOX_CELL_STATE_TO_STR, - VALLOX_PROFILE_TO_STR_REPORTABLE, + VALLOX_PROFILE_TO_PRESET_MODE_REPORTABLE, ) @@ -76,7 +76,7 @@ class ValloxProfileSensor(ValloxSensorEntity): def native_value(self) -> StateType: """Return the value reported by the sensor.""" vallox_profile = self.coordinator.data.profile - return VALLOX_PROFILE_TO_STR_REPORTABLE.get(vallox_profile) + return VALLOX_PROFILE_TO_PRESET_MODE_REPORTABLE.get(vallox_profile) # There is a quirk with respect to the fan speed reporting. The device keeps on reporting the last @@ -190,7 +190,7 @@ SENSOR_ENTITIES: tuple[ValloxSensorEntityDescription, ...] = ( metric_key="A_CYC_TEMP_EXTRACT_AIR", device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, ), ValloxSensorEntityDescription( key="exhaust_air", @@ -198,7 +198,7 @@ SENSOR_ENTITIES: tuple[ValloxSensorEntityDescription, ...] = ( metric_key="A_CYC_TEMP_EXHAUST_AIR", device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, ), ValloxSensorEntityDescription( key="outdoor_air", @@ -206,7 +206,7 @@ SENSOR_ENTITIES: tuple[ValloxSensorEntityDescription, ...] = ( metric_key="A_CYC_TEMP_OUTDOOR_AIR", device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, ), ValloxSensorEntityDescription( key="supply_air", @@ -214,7 +214,7 @@ SENSOR_ENTITIES: tuple[ValloxSensorEntityDescription, ...] = ( metric_key="A_CYC_TEMP_SUPPLY_AIR", device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, ), ValloxSensorEntityDescription( key="supply_cell_air", @@ -222,7 +222,7 @@ SENSOR_ENTITIES: tuple[ValloxSensorEntityDescription, ...] = ( metric_key="A_CYC_TEMP_SUPPLY_CELL_AIR", device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, ), ValloxSensorEntityDescription( key="optional_air", @@ -230,7 +230,7 @@ SENSOR_ENTITIES: tuple[ValloxSensorEntityDescription, ...] = ( metric_key="A_CYC_TEMP_OPTIONAL", device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, entity_registry_enabled_default=False, ), ValloxSensorEntityDescription( diff --git a/homeassistant/components/vallox/translations/ko.json b/homeassistant/components/vallox/translations/ko.json new file mode 100644 index 00000000000..4e9866ced7b --- /dev/null +++ b/homeassistant/components/vallox/translations/ko.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "\uc11c\ube44\uc2a4\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_host": "\ud638\uc2a4\ud2b8 \uc774\ub984 \ub610\ub294 IP \uc8fc\uc18c\uac00 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_host": "\ud638\uc2a4\ud2b8 \uc774\ub984 \ub610\ub294 IP \uc8fc\uc18c\uac00 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/vallox/translations/pt.json b/homeassistant/components/vallox/translations/pt.json index f5c3b1c7b61..041eed7c390 100644 --- a/homeassistant/components/vallox/translations/pt.json +++ b/homeassistant/components/vallox/translations/pt.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "invalid_host": "Nome de servidor ou endere\u00e7o IP inv\u00e1lido." + "invalid_host": "Endere\u00e7o IP ou hostname inv\u00e1lido." }, "error": { "unknown": "Erro inesperado" diff --git a/homeassistant/components/velbus/climate.py b/homeassistant/components/velbus/climate.py index 76eb3e30fa0..ccdfb3b073b 100644 --- a/homeassistant/components/velbus/climate.py +++ b/homeassistant/components/velbus/climate.py @@ -11,7 +11,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -40,7 +40,7 @@ class VelbusClimate(VelbusEntity, ClimateEntity): _attr_supported_features = ( ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE ) - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS _attr_hvac_mode = HVACMode.HEAT _attr_hvac_modes = [HVACMode.HEAT] _attr_preset_modes = list(PRESET_MODES) diff --git a/homeassistant/components/velbus/translations/de.json b/homeassistant/components/velbus/translations/de.json index c6c452953ca..c04ee733c19 100644 --- a/homeassistant/components/velbus/translations/de.json +++ b/homeassistant/components/velbus/translations/de.json @@ -10,10 +10,10 @@ "step": { "user": { "data": { - "name": "Der Name f\u00fcr diese Velbus-Verbindung", + "name": "Der Name f\u00fcr diese Velbus Verbindung", "port": "Verbindungsdetails" }, - "title": "Definieren des Velbus-Verbindungstyps" + "title": "Definieren des Velbus Verbindungstyps" } } } diff --git a/homeassistant/components/velbus/translations/pt.json b/homeassistant/components/velbus/translations/pt.json index 94b13c6bc7d..55552e3bf29 100644 --- a/homeassistant/components/velbus/translations/pt.json +++ b/homeassistant/components/velbus/translations/pt.json @@ -5,7 +5,7 @@ }, "error": { "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" } } } \ No newline at end of file diff --git a/homeassistant/components/velbus/translations/sk.json b/homeassistant/components/velbus/translations/sk.json index 6e5e2436adf..7fdb05967ee 100644 --- a/homeassistant/components/velbus/translations/sk.json +++ b/homeassistant/components/velbus/translations/sk.json @@ -10,8 +10,10 @@ "step": { "user": { "data": { + "name": "N\u00e1zov tohto spojenia velbus", "port": "Re\u0165azec pripojenia" - } + }, + "title": "Definujte typ pripojenia velbus" } } } diff --git a/homeassistant/components/venstar/__init__.py b/homeassistant/components/venstar/__init__.py index b543f6d947a..3f31c417a02 100644 --- a/homeassistant/components/venstar/__init__.py +++ b/homeassistant/components/venstar/__init__.py @@ -56,14 +56,14 @@ async def async_setup_entry(hass: HomeAssistant, config: ConfigEntry) -> bool: async def async_unload_entry(hass: HomeAssistant, config: ConfigEntry) -> bool: - """Unload the config config and platforms.""" + """Unload the config and platforms.""" unload_ok = await hass.config_entries.async_unload_platforms(config, PLATFORMS) if unload_ok: hass.data[DOMAIN].pop(config.entry_id) return unload_ok -class VenstarDataUpdateCoordinator(update_coordinator.DataUpdateCoordinator): +class VenstarDataUpdateCoordinator(update_coordinator.DataUpdateCoordinator[None]): """Class to manage fetching Venstar data.""" def __init__( diff --git a/homeassistant/components/venstar/climate.py b/homeassistant/components/venstar/climate.py index 2a921fe3731..b4d3b6c6837 100644 --- a/homeassistant/components/venstar/climate.py +++ b/homeassistant/components/venstar/climate.py @@ -28,8 +28,7 @@ from homeassistant.const import ( CONF_USERNAME, PRECISION_HALVES, STATE_ON, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv @@ -145,8 +144,8 @@ class VenstarThermostat(VenstarEntity, ClimateEntity): def temperature_unit(self) -> str: """Return the unit of measurement, as defined by the API.""" if self._client.tempunits == self._client.TEMPUNITS_F: - return TEMP_FAHRENHEIT - return TEMP_CELSIUS + return UnitOfTemperature.FAHRENHEIT + return UnitOfTemperature.CELSIUS @property def current_temperature(self): @@ -292,8 +291,10 @@ class VenstarThermostat(VenstarEntity, ClimateEntity): else: success = False _LOGGER.error( - "The thermostat is currently not in a mode " - "that supports target temperature: %s", + ( + "The thermostat is currently not in a mode " + "that supports target temperature: %s" + ), operation_mode, ) diff --git a/homeassistant/components/venstar/sensor.py b/homeassistant/components/venstar/sensor.py index 723b26d1956..0d3b66c620e 100644 --- a/homeassistant/components/venstar/sensor.py +++ b/homeassistant/components/venstar/sensor.py @@ -15,9 +15,8 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONCENTRATION_PARTS_PER_MILLION, PERCENTAGE, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, - TIME_MINUTES, + UnitOfTemperature, + UnitOfTime, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import Entity @@ -62,8 +61,8 @@ RUNTIME_ATTRIBUTES = { class VenstarSensorTypeMixin: """Mixin for sensor required keys.""" - value_fn: Callable[[Any, Any], Any] - name_fn: Callable[[Any, Any], str] + value_fn: Callable[[VenstarDataUpdateCoordinator, str], Any] + name_fn: Callable[[VenstarDataUpdateCoordinator, str], str] uom_fn: Callable[[Any], str | None] @@ -78,7 +77,7 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up Vensar device binary_sensors based on a config entry.""" - coordinator = hass.data[DOMAIN][config_entry.entry_id] + coordinator: VenstarDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id] entities: list[Entity] = [] if not (sensors := coordinator.client.get_sensor_list()): @@ -106,9 +105,9 @@ async def async_setup_entry( def temperature_unit(coordinator: VenstarDataUpdateCoordinator) -> str: """Return the correct unit for temperature.""" - unit = TEMP_CELSIUS + unit = UnitOfTemperature.CELSIUS if coordinator.client.tempunits == coordinator.client.TEMPUNITS_F: - unit = TEMP_FAHRENHEIT + unit = UnitOfTemperature.FAHRENHEIT return unit @@ -207,7 +206,7 @@ SENSOR_ENTITIES: tuple[VenstarSensorEntityDescription, ...] = ( RUNTIME_ENTITY = VenstarSensorEntityDescription( key="runtime", state_class=SensorStateClass.MEASUREMENT, - uom_fn=lambda _: TIME_MINUTES, + uom_fn=lambda _: UnitOfTime.MINUTES, value_fn=lambda coordinator, sensor_name: coordinator.runtimes[-1][sensor_name], name_fn=lambda coordinator, sensor_name: f"{coordinator.client.name} {RUNTIME_ATTRIBUTES[sensor_name]} Runtime", ) diff --git a/homeassistant/components/venstar/translations/ko.json b/homeassistant/components/venstar/translations/ko.json new file mode 100644 index 00000000000..7f1c8d53058 --- /dev/null +++ b/homeassistant/components/venstar/translations/ko.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8", + "password": "\ube44\ubc00\ubc88\ud638", + "pin": "PIN \ucf54\ub4dc", + "ssl": "SSL \uc778\uc99d\uc11c \uc0ac\uc6a9", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/venstar/translations/pt.json b/homeassistant/components/venstar/translations/pt.json index 04374af8e82..632cb64e485 100644 --- a/homeassistant/components/venstar/translations/pt.json +++ b/homeassistant/components/venstar/translations/pt.json @@ -4,13 +4,13 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "unknown": "Erro inesperado" }, "step": { "user": { "data": { - "host": "Servidor" + "host": "Endere\u00e7o" } } } diff --git a/homeassistant/components/vera/climate.py b/homeassistant/components/vera/climate.py index 924acbe6243..164da079ac1 100644 --- a/homeassistant/components/vera/climate.py +++ b/homeassistant/components/vera/climate.py @@ -14,12 +14,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - ATTR_TEMPERATURE, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, - Platform, -) +from homeassistant.const import ATTR_TEMPERATURE, Platform, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -104,9 +99,9 @@ class VeraThermostat(VeraDevice[veraApi.VeraThermostat], ClimateEntity): vera_temp_units = self.vera_device.vera_controller.temperature_units if vera_temp_units == "F": - return TEMP_FAHRENHEIT + return UnitOfTemperature.FAHRENHEIT - return TEMP_CELSIUS + return UnitOfTemperature.CELSIUS @property def current_temperature(self) -> float | None: diff --git a/homeassistant/components/vera/sensor.py b/homeassistant/components/vera/sensor.py index 9f488fad33e..b493f9aac3d 100644 --- a/homeassistant/components/vera/sensor.py +++ b/homeassistant/components/vera/sensor.py @@ -15,10 +15,9 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( LIGHT_LUX, PERCENTAGE, - POWER_WATT, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, Platform, + UnitOfPower, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -90,7 +89,7 @@ class VeraSensor(VeraDevice[veraApi.VeraSensor], SensorEntity): if self.vera_device.category == veraApi.CATEGORY_HUMIDITY_SENSOR: return PERCENTAGE if self.vera_device.category == veraApi.CATEGORY_POWER_METER: - return POWER_WATT + return UnitOfPower.WATT return None def update(self) -> None: @@ -102,9 +101,9 @@ class VeraSensor(VeraDevice[veraApi.VeraSensor], SensorEntity): vera_temp_units = self.vera_device.vera_controller.temperature_units if vera_temp_units == "F": - self._temperature_units = TEMP_FAHRENHEIT + self._temperature_units = UnitOfTemperature.FAHRENHEIT else: - self._temperature_units = TEMP_CELSIUS + self._temperature_units = UnitOfTemperature.CELSIUS elif self.vera_device.category == veraApi.CATEGORY_LIGHT_SENSOR: self.current_value = self.vera_device.light diff --git a/homeassistant/components/vera/translations/sk.json b/homeassistant/components/vera/translations/sk.json index 23f69af3665..fdb0ceeaf6a 100644 --- a/homeassistant/components/vera/translations/sk.json +++ b/homeassistant/components/vera/translations/sk.json @@ -6,6 +6,8 @@ "step": { "user": { "data": { + "exclude": "Identifik\u00e1tory zariaden\u00ed Vera, ktor\u00e9 sa maj\u00fa vyl\u00fa\u010di\u0165 z dom\u00e1ceho asistenta.", + "lights": "Vera prep\u00edna ID zariadenia, ktor\u00e9 sa m\u00e1 v aplik\u00e1cii Home Assistant pova\u017eova\u0165 za svetl\u00e1.", "vera_controller_url": "URL kontrol\u00e9ra" }, "data_description": { @@ -13,5 +15,17 @@ } } } + }, + "options": { + "step": { + "init": { + "data": { + "exclude": "ID zariaden\u00ed Vera, ktor\u00e9 sa maj\u00fa vyl\u00fa\u010di\u0165 z dom\u00e1ceho asistenta.", + "lights": "Vera prep\u00edna ID zariadenia, ktor\u00e9 sa m\u00e1 v aplik\u00e1cii Home Assistant pova\u017eova\u0165 za svetl\u00e1." + }, + "description": "Podrobnosti o volite\u013en\u00fdch parametroch n\u00e1jdete v dokument\u00e1cii vera: https://www.home-assistant.io/integrations/vera/. Pozn\u00e1mka: Ak\u00e9ko\u013evek zmeny tu bud\u00fa vy\u017eadova\u0165 re\u0161tart servera dom\u00e1ceho asistenta. Ak chcete vymaza\u0165 hodnoty, poskytnite priestor.", + "title": "Mo\u017enosti ovl\u00e1da\u010da Vera" + } + } } } \ No newline at end of file diff --git a/homeassistant/components/verisure/sensor.py b/homeassistant/components/verisure/sensor.py index 676082e4cda..bbc1c15159c 100644 --- a/homeassistant/components/verisure/sensor.py +++ b/homeassistant/components/verisure/sensor.py @@ -7,7 +7,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import PERCENTAGE, TEMP_CELSIUS +from homeassistant.const import PERCENTAGE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo, Entity from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -53,7 +53,7 @@ class VerisureThermometer( _attr_device_class = SensorDeviceClass.TEMPERATURE _attr_has_entity_name = True _attr_name = "Temperature" - _attr_native_unit_of_measurement = TEMP_CELSIUS + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS _attr_state_class = SensorStateClass.MEASUREMENT def __init__( diff --git a/homeassistant/components/verisure/translations/de.json b/homeassistant/components/verisure/translations/de.json index d306064d121..a07e1f1dd79 100644 --- a/homeassistant/components/verisure/translations/de.json +++ b/homeassistant/components/verisure/translations/de.json @@ -14,7 +14,7 @@ "data": { "giid": "Installation" }, - "description": "Home Assistant hat mehrere Verisure-Installationen in deinem My Pages-Konto gefunden. Bitte w\u00e4hle die Installation aus, die du zu Home Assistant hinzuf\u00fcgen m\u00f6chtest." + "description": "Home Assistant hat mehrere Verisure-Installationen in deinem My Pages Konto gefunden. Bitte w\u00e4hle die Installation aus, die du zu Home Assistant hinzuf\u00fcgen m\u00f6chtest." }, "mfa": { "data": { @@ -24,7 +24,7 @@ }, "reauth_confirm": { "data": { - "description": "Authentifiziere dich erneut mit deinem Verisure My Pages-Konto.", + "description": "Authentifiziere dich erneut mit deinem Verisure My Pages Konto.", "email": "E-Mail", "password": "Passwort" } @@ -37,7 +37,7 @@ }, "user": { "data": { - "description": "Melde dich mit deinen Verisure My Pages-Konto an.", + "description": "Melde dich mit deinen Verisure My Pages Konto an.", "email": "E-Mail", "password": "Passwort" } diff --git a/homeassistant/components/verisure/translations/lb.json b/homeassistant/components/verisure/translations/lb.json new file mode 100644 index 00000000000..05cc9afcbd3 --- /dev/null +++ b/homeassistant/components/verisure/translations/lb.json @@ -0,0 +1,15 @@ +{ + "options": { + "error": { + "code_format_mismatch": "De Standard-PIN-Code st\u00ebmmt net mat der n\u00e9ideger Unzuel un Zifferen iwwerteneen" + }, + "step": { + "init": { + "data": { + "lock_code_digits": "Unzuel vun Zifferen am PIN-Code fir Schl\u00e4sser", + "lock_default_code": "Standard-PIN-Code fir Schl\u00e4sser: g\u00ebtt benotzt, wa keen ugi g\u00ebtt" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/verisure/translations/pt.json b/homeassistant/components/verisure/translations/pt.json index a60db2b4ea0..0b59716996b 100644 --- a/homeassistant/components/verisure/translations/pt.json +++ b/homeassistant/components/verisure/translations/pt.json @@ -11,13 +11,13 @@ "step": { "reauth_confirm": { "data": { - "email": "Email", + "email": "", "password": "Palavra-passe" } }, "user": { "data": { - "email": "Email", + "email": "", "password": "Palavra-passe" } } diff --git a/homeassistant/components/verisure/translations/sk.json b/homeassistant/components/verisure/translations/sk.json index 33dc3546d09..dc2631c6e5e 100644 --- a/homeassistant/components/verisure/translations/sk.json +++ b/homeassistant/components/verisure/translations/sk.json @@ -5,36 +5,56 @@ "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba", + "unknown_mfa": "Po\u010das nastavovania MFA sa vyskytla nezn\u00e1ma chyba" }, "step": { "installation": { "data": { "giid": "In\u0161tal\u00e1cia" - } + }, + "description": "Home Assistant na\u0161iel vo va\u0161om \u00fa\u010dte Moje str\u00e1nky viacero in\u0161tal\u00e1ci\u00ed Verisure. Vyberte in\u0161tal\u00e1ciu, ktor\u00fa chcete prida\u0165 do Home Assistant." }, "mfa": { "data": { - "code": "Overovac\u00ed k\u00f3d" + "code": "Overovac\u00ed k\u00f3d", + "description": "Va\u0161e konto m\u00e1 povolen\u00e9 dvojf\u00e1zov\u00e9 overovanie. Zadajte overovac\u00ed k\u00f3d, ktor\u00fd v\u00e1m za\u0161le spolo\u010dnos\u0165 Verisure." } }, "reauth_confirm": { "data": { + "description": "Znova sa overte pomocou svojho \u00fa\u010dtu Verisure My Pages.", "email": "Email", "password": "Heslo" } }, "reauth_mfa": { "data": { - "code": "Overovac\u00ed k\u00f3d" + "code": "Overovac\u00ed k\u00f3d", + "description": "Va\u0161e konto m\u00e1 povolen\u00e9 dvojf\u00e1zov\u00e9 overovanie. Zadajte overovac\u00ed k\u00f3d, ktor\u00fd v\u00e1m za\u0161le spolo\u010dnos\u0165 Verisure." } }, "user": { "data": { + "description": "Prihl\u00e1ste sa pomocou svojho \u00fa\u010dtu Verisure My Pages.", "email": "Email", "password": "Heslo" } } } + }, + "options": { + "error": { + "code_format_mismatch": "Predvolen\u00fd PIN k\u00f3d nezodpoved\u00e1 po\u017eadovan\u00e9mu po\u010dtu \u010d\u00edslic" + }, + "step": { + "init": { + "data": { + "lock_code_digits": "Po\u010det \u010d\u00edslic v PIN k\u00f3de pre z\u00e1mky", + "lock_default_code": "Predvolen\u00fd PIN k\u00f3d pre z\u00e1mky, ktor\u00fd sa pou\u017e\u00edva, ak nie je zadan\u00fd" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/version/coordinator.py b/homeassistant/components/version/coordinator.py index 24e44f9f56c..a41816c0824 100644 --- a/homeassistant/components/version/coordinator.py +++ b/homeassistant/components/version/coordinator.py @@ -14,7 +14,7 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda from .const import DOMAIN, LOGGER, UPDATE_COORDINATOR_UPDATE_INTERVAL -class VersionDataUpdateCoordinator(DataUpdateCoordinator): +class VersionDataUpdateCoordinator(DataUpdateCoordinator[None]): """Data update coordinator for Version entities.""" config_entry: ConfigEntry diff --git a/homeassistant/components/version/diagnostics.py b/homeassistant/components/version/diagnostics.py index 2ba31bc8870..20b84fabce7 100644 --- a/homeassistant/components/version/diagnostics.py +++ b/homeassistant/components/version/diagnostics.py @@ -10,6 +10,7 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry as dr, entity_registry as er from .const import DOMAIN +from .coordinator import VersionDataUpdateCoordinator async def async_get_config_entry_diagnostics( @@ -17,7 +18,7 @@ async def async_get_config_entry_diagnostics( config_entry: ConfigEntry, ) -> dict[str, Any]: """Return diagnostics for a config entry.""" - coordinator = hass.data[DOMAIN][config_entry.entry_id] + coordinator: VersionDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id] device_registry = dr.async_get(hass) entity_registry = er.async_get(hass) diff --git a/homeassistant/components/version/translations/ko.json b/homeassistant/components/version/translations/ko.json new file mode 100644 index 00000000000..17dee71d640 --- /dev/null +++ b/homeassistant/components/version/translations/ko.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/version/translations/sk.json b/homeassistant/components/version/translations/sk.json index 8e621c87efb..4482d9d370f 100644 --- a/homeassistant/components/version/translations/sk.json +++ b/homeassistant/components/version/translations/sk.json @@ -8,12 +8,17 @@ "data": { "version_source": "Zdroj verzie" }, + "description": "Vyberte zdroj, z ktor\u00e9ho chcete sledova\u0165 verzie", "title": "Vyberte typ in\u0161tal\u00e1cie" }, "version_source": { "data": { - "beta": "Zahrn\u00fa\u0165 beta verzie" + "beta": "Zahrn\u00fa\u0165 beta verzie", + "board": "Ktor\u00e1 doska sa m\u00e1 sledova\u0165", + "channel": "Ktor\u00fd kan\u00e1l sa m\u00e1 sledova\u0165", + "image": "Ktor\u00fd obr\u00e1zok sa m\u00e1 sledova\u0165" }, + "description": "Konfigur\u00e1cia sledovania verzi\u00ed {version_source}", "title": "Konfigurova\u0165" } } diff --git a/homeassistant/components/vesync/light.py b/homeassistant/components/vesync/light.py index 57329c0973e..3837ab1019c 100644 --- a/homeassistant/components/vesync/light.py +++ b/homeassistant/components/vesync/light.py @@ -156,7 +156,10 @@ class VeSyncTunableWhiteLightHA(VeSyncBaseLight, LightEntity): except ValueError: # deal if any unexpected/non numeric value _LOGGER.debug( - "VeSync - received unexpected 'color_temp_pct' value from pyvesync api: %s", + ( + "VeSync - received unexpected 'color_temp_pct' value from pyvesync" + " api: %s" + ), result, ) return 0 diff --git a/homeassistant/components/vesync/sensor.py b/homeassistant/components/vesync/sensor.py index c5a2e7182a9..10fd9ef1442 100644 --- a/homeassistant/components/vesync/sensor.py +++ b/homeassistant/components/vesync/sensor.py @@ -18,10 +18,10 @@ from homeassistant.components.sensor import ( from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, - ELECTRIC_POTENTIAL_VOLT, - ENERGY_KILO_WATT_HOUR, PERCENTAGE, - POWER_WATT, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfPower, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect @@ -105,7 +105,7 @@ SENSORS: tuple[VeSyncSensorEntityDescription, ...] = ( key="power", name="current power", device_class=SensorDeviceClass.POWER, - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, state_class=SensorStateClass.MEASUREMENT, value_fn=lambda device: device.details["power"], update_fn=update_energy, @@ -115,7 +115,7 @@ SENSORS: tuple[VeSyncSensorEntityDescription, ...] = ( key="energy", name="energy use today", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, state_class=SensorStateClass.TOTAL_INCREASING, value_fn=lambda device: device.energy_today, update_fn=update_energy, @@ -125,7 +125,7 @@ SENSORS: tuple[VeSyncSensorEntityDescription, ...] = ( key="energy-weekly", name="energy use weekly", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, state_class=SensorStateClass.TOTAL_INCREASING, value_fn=lambda device: device.weekly_energy_total, update_fn=update_energy, @@ -135,7 +135,7 @@ SENSORS: tuple[VeSyncSensorEntityDescription, ...] = ( key="energy-monthly", name="energy use monthly", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, state_class=SensorStateClass.TOTAL_INCREASING, value_fn=lambda device: device.monthly_energy_total, update_fn=update_energy, @@ -145,7 +145,7 @@ SENSORS: tuple[VeSyncSensorEntityDescription, ...] = ( key="energy-yearly", name="energy use yearly", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, state_class=SensorStateClass.TOTAL_INCREASING, value_fn=lambda device: device.yearly_energy_total, update_fn=update_energy, @@ -155,7 +155,7 @@ SENSORS: tuple[VeSyncSensorEntityDescription, ...] = ( key="voltage", name="current voltage", device_class=SensorDeviceClass.VOLTAGE, - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, state_class=SensorStateClass.MEASUREMENT, value_fn=lambda device: device.details["voltage"], update_fn=update_energy, diff --git a/homeassistant/components/viaggiatreno/sensor.py b/homeassistant/components/viaggiatreno/sensor.py index bdc8909da0f..9326db64d0a 100644 --- a/homeassistant/components/viaggiatreno/sensor.py +++ b/homeassistant/components/viaggiatreno/sensor.py @@ -11,7 +11,7 @@ import async_timeout import voluptuous as vol from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity -from homeassistant.const import TIME_MINUTES +from homeassistant.const import UnitOfTime from homeassistant.core import HomeAssistant from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv @@ -187,5 +187,5 @@ class ViaggiaTrenoSensor(SensorEntity): self._unit = "" else: self._state = res.get("ritardo") - self._unit = TIME_MINUTES + self._unit = UnitOfTime.MINUTES self._icon = ICON diff --git a/homeassistant/components/vicare/climate.py b/homeassistant/components/vicare/climate.py index 9d507ab9913..9a55da0f219 100644 --- a/homeassistant/components/vicare/climate.py +++ b/homeassistant/components/vicare/climate.py @@ -6,6 +6,7 @@ import logging from typing import Any from PyViCare.PyViCareUtils import ( + PyViCareCommandError, PyViCareInvalidDataError, PyViCareNotSupportedFeatureError, PyViCareRateLimitError, @@ -27,7 +28,7 @@ from homeassistant.const import ( ATTR_TEMPERATURE, PRECISION_TENTHS, PRECISION_WHOLE, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_platform @@ -147,7 +148,7 @@ class ViCareClimate(ClimateEntity): _attr_supported_features = ( ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE ) - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS def __init__(self, name, api, circuit, device_config, heating_type): """Initialize the climate device.""" @@ -354,7 +355,10 @@ class ViCareClimate(ClimateEntity): _LOGGER.debug("Setting preset to %s / %s", preset_mode, vicare_program) if self._current_program != VICARE_PROGRAM_NORMAL: # We can't deactivate "normal" - self._circuit.deactivateProgram(self._current_program) + try: + self._circuit.deactivateProgram(self._current_program) + except PyViCareCommandError: + _LOGGER.debug("Unable to deactivate program %s", self._current_program) if vicare_program != VICARE_PROGRAM_NORMAL: # And we can't explicitly activate normal, either self._circuit.activateProgram(vicare_program) diff --git a/homeassistant/components/vicare/const.py b/homeassistant/components/vicare/const.py index b8c430acaca..c3bd3037d96 100644 --- a/homeassistant/components/vicare/const.py +++ b/homeassistant/components/vicare/const.py @@ -1,8 +1,7 @@ """Constants for the ViCare integration.""" import enum -from homeassistant.components.sensor import SensorDeviceClass -from homeassistant.const import ENERGY_KILO_WATT_HOUR, VOLUME_CUBIC_METERS, Platform +from homeassistant.const import Platform, UnitOfEnergy, UnitOfVolume DOMAIN = "vicare" @@ -26,14 +25,10 @@ DEFAULT_SCAN_INTERVAL = 60 VICARE_CUBIC_METER = "cubicMeter" VICARE_KWH = "kilowattHour" -VICARE_UNIT_TO_DEVICE_CLASS = { - VICARE_KWH: SensorDeviceClass.ENERGY, - VICARE_CUBIC_METER: SensorDeviceClass.GAS, -} VICARE_UNIT_TO_UNIT_OF_MEASUREMENT = { - VICARE_KWH: ENERGY_KILO_WATT_HOUR, - VICARE_CUBIC_METER: VOLUME_CUBIC_METERS, + VICARE_KWH: UnitOfEnergy.KILO_WATT_HOUR, + VICARE_CUBIC_METER: UnitOfVolume.CUBIC_METERS, } diff --git a/homeassistant/components/vicare/manifest.json b/homeassistant/components/vicare/manifest.json index 0294022137c..0105bc72df5 100644 --- a/homeassistant/components/vicare/manifest.json +++ b/homeassistant/components/vicare/manifest.json @@ -3,7 +3,7 @@ "name": "Viessmann ViCare", "documentation": "https://www.home-assistant.io/integrations/vicare", "codeowners": ["@oischinger"], - "requirements": ["PyViCare==2.19.0"], + "requirements": ["PyViCare==2.21.0"], "iot_class": "cloud_polling", "config_flow": true, "dhcp": [ diff --git a/homeassistant/components/vicare/sensor.py b/homeassistant/components/vicare/sensor.py index 86f2f393138..9f74df79e2b 100644 --- a/homeassistant/components/vicare/sensor.py +++ b/homeassistant/components/vicare/sensor.py @@ -22,12 +22,12 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - ENERGY_KILO_WATT_HOUR, PERCENTAGE, - POWER_WATT, - TEMP_CELSIUS, - TIME_HOURS, - VOLUME_CUBIC_METERS, + UnitOfEnergy, + UnitOfPower, + UnitOfTemperature, + UnitOfTime, + UnitOfVolume, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo @@ -37,14 +37,20 @@ from . import ViCareRequiredKeysMixin from .const import ( DOMAIN, VICARE_API, + VICARE_CUBIC_METER, VICARE_DEVICE_CONFIG, + VICARE_KWH, VICARE_NAME, - VICARE_UNIT_TO_DEVICE_CLASS, VICARE_UNIT_TO_UNIT_OF_MEASUREMENT, ) _LOGGER = logging.getLogger(__name__) +VICARE_UNIT_TO_DEVICE_CLASS = { + VICARE_KWH: SensorDeviceClass.ENERGY, + VICARE_CUBIC_METER: SensorDeviceClass.GAS, +} + @dataclass class ViCareSensorEntityDescription(SensorEntityDescription, ViCareRequiredKeysMixin): @@ -57,7 +63,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="outside_temperature", name="Outside Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, value_getter=lambda api: api.getOutsideTemperature(), device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, @@ -65,7 +71,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="return_temperature", name="Return Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, value_getter=lambda api: api.getReturnTemperature(), device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, @@ -73,7 +79,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="boiler_temperature", name="Boiler Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, value_getter=lambda api: api.getBoilerTemperature(), device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, @@ -81,7 +87,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="boiler_supply_temperature", name="Boiler Supply Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, value_getter=lambda api: api.getBoilerCommonSupplyTemperature(), device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, @@ -89,7 +95,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="primary_circuit_supply_temperature", name="Primary Circuit Supply Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, value_getter=lambda api: api.getSupplyTemperaturePrimaryCircuit(), device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, @@ -97,7 +103,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="primary_circuit_return_temperature", name="Primary Circuit Return Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, value_getter=lambda api: api.getReturnTemperaturePrimaryCircuit(), device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, @@ -105,7 +111,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="secondary_circuit_supply_temperature", name="Secondary Circuit Supply Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, value_getter=lambda api: api.getSupplyTemperatureSecondaryCircuit(), device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, @@ -113,7 +119,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="secondary_circuit_return_temperature", name="Secondary Circuit Return Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, value_getter=lambda api: api.getReturnTemperatureSecondaryCircuit(), device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, @@ -121,7 +127,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="hotwater_out_temperature", name="Hot Water Out Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, value_getter=lambda api: api.getDomesticHotWaterOutletTemperature(), device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, @@ -129,7 +135,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="hotwater_max_temperature", name="Hot Water Max Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, value_getter=lambda api: api.getDomesticHotWaterMaxTemperature(), device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, @@ -137,7 +143,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="hotwater_min_temperature", name="Hot Water Min Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, value_getter=lambda api: api.getDomesticHotWaterMinTemperature(), device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, @@ -201,7 +207,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="gas_summary_consumption_heating_currentday", name="Heating gas consumption current day", - native_unit_of_measurement=VOLUME_CUBIC_METERS, + native_unit_of_measurement=UnitOfVolume.CUBIC_METERS, value_getter=lambda api: api.getGasSummaryConsumptionHeatingCurrentDay(), unit_getter=lambda api: api.getGasSummaryConsumptionHeatingUnit(), state_class=SensorStateClass.TOTAL_INCREASING, @@ -209,7 +215,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="gas_summary_consumption_heating_currentmonth", name="Heating gas consumption current month", - native_unit_of_measurement=VOLUME_CUBIC_METERS, + native_unit_of_measurement=UnitOfVolume.CUBIC_METERS, value_getter=lambda api: api.getGasSummaryConsumptionHeatingCurrentMonth(), unit_getter=lambda api: api.getGasSummaryConsumptionHeatingUnit(), state_class=SensorStateClass.TOTAL_INCREASING, @@ -217,7 +223,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="gas_summary_consumption_heating_currentyear", name="Heating gas consumption current year", - native_unit_of_measurement=VOLUME_CUBIC_METERS, + native_unit_of_measurement=UnitOfVolume.CUBIC_METERS, value_getter=lambda api: api.getGasSummaryConsumptionHeatingCurrentYear(), unit_getter=lambda api: api.getGasSummaryConsumptionHeatingUnit(), state_class=SensorStateClass.TOTAL_INCREASING, @@ -225,7 +231,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="hotwater_gas_summary_consumption_heating_currentday", name="Hot water gas consumption current day", - native_unit_of_measurement=VOLUME_CUBIC_METERS, + native_unit_of_measurement=UnitOfVolume.CUBIC_METERS, value_getter=lambda api: api.getGasSummaryConsumptionDomesticHotWaterCurrentDay(), unit_getter=lambda api: api.getGasSummaryConsumptionDomesticHotWaterUnit(), state_class=SensorStateClass.TOTAL_INCREASING, @@ -233,7 +239,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="hotwater_gas_summary_consumption_heating_currentmonth", name="Hot water gas consumption current month", - native_unit_of_measurement=VOLUME_CUBIC_METERS, + native_unit_of_measurement=UnitOfVolume.CUBIC_METERS, value_getter=lambda api: api.getGasSummaryConsumptionDomesticHotWaterCurrentMonth(), unit_getter=lambda api: api.getGasSummaryConsumptionDomesticHotWaterUnit(), state_class=SensorStateClass.TOTAL_INCREASING, @@ -241,7 +247,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="hotwater_gas_summary_consumption_heating_currentyear", name="Hot water gas consumption current year", - native_unit_of_measurement=VOLUME_CUBIC_METERS, + native_unit_of_measurement=UnitOfVolume.CUBIC_METERS, value_getter=lambda api: api.getGasSummaryConsumptionDomesticHotWaterCurrentYear(), unit_getter=lambda api: api.getGasSummaryConsumptionDomesticHotWaterUnit(), state_class=SensorStateClass.TOTAL_INCREASING, @@ -249,7 +255,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="hotwater_gas_summary_consumption_heating_lastsevendays", name="Hot water gas consumption last seven days", - native_unit_of_measurement=VOLUME_CUBIC_METERS, + native_unit_of_measurement=UnitOfVolume.CUBIC_METERS, value_getter=lambda api: api.getGasSummaryConsumptionDomesticHotWaterLastSevenDays(), unit_getter=lambda api: api.getGasSummaryConsumptionDomesticHotWaterUnit(), state_class=SensorStateClass.TOTAL_INCREASING, @@ -257,7 +263,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="energy_summary_consumption_heating_currentday", name="Energy consumption of gas heating current day", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, value_getter=lambda api: api.getPowerSummaryConsumptionHeatingCurrentDay(), unit_getter=lambda api: api.getPowerSummaryConsumptionHeatingUnit(), state_class=SensorStateClass.TOTAL_INCREASING, @@ -265,7 +271,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="energy_summary_consumption_heating_currentmonth", name="Energy consumption of gas heating current month", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, value_getter=lambda api: api.getPowerSummaryConsumptionHeatingCurrentMonth(), unit_getter=lambda api: api.getPowerSummaryConsumptionHeatingUnit(), state_class=SensorStateClass.TOTAL_INCREASING, @@ -273,7 +279,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="energy_summary_consumption_heating_currentyear", name="Energy consumption of gas heating current year", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, value_getter=lambda api: api.getPowerSummaryConsumptionHeatingCurrentYear(), unit_getter=lambda api: api.getPowerSummaryConsumptionHeatingUnit(), state_class=SensorStateClass.TOTAL_INCREASING, @@ -281,7 +287,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="energy_summary_consumption_heating_lastsevendays", name="Energy consumption of gas heating last seven days", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, value_getter=lambda api: api.getPowerSummaryConsumptionHeatingLastSevenDays(), unit_getter=lambda api: api.getPowerSummaryConsumptionHeatingUnit(), state_class=SensorStateClass.TOTAL_INCREASING, @@ -289,7 +295,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="energy_dhw_summary_consumption_heating_currentday", name="Energy consumption of hot water gas heating current day", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, value_getter=lambda api: api.getPowerSummaryConsumptionDomesticHotWaterCurrentDay(), unit_getter=lambda api: api.getPowerSummaryConsumptionDomesticHotWaterUnit(), state_class=SensorStateClass.TOTAL_INCREASING, @@ -297,7 +303,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="energy_dhw_summary_consumption_heating_currentmonth", name="Energy consumption of hot water gas heating current month", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, value_getter=lambda api: api.getPowerSummaryConsumptionDomesticHotWaterCurrentMonth(), unit_getter=lambda api: api.getPowerSummaryConsumptionDomesticHotWaterUnit(), state_class=SensorStateClass.TOTAL_INCREASING, @@ -305,7 +311,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="energy_dhw_summary_consumption_heating_currentyear", name="Energy consumption of hot water gas heating current year", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, value_getter=lambda api: api.getPowerSummaryConsumptionDomesticHotWaterCurrentYear(), unit_getter=lambda api: api.getPowerSummaryConsumptionDomesticHotWaterUnit(), state_class=SensorStateClass.TOTAL_INCREASING, @@ -313,7 +319,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="energy_summary_dhw_consumption_heating_lastsevendays", name="Energy consumption of hot water gas heating last seven days", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, value_getter=lambda api: api.getPowerSummaryConsumptionDomesticHotWaterLastSevenDays(), unit_getter=lambda api: api.getPowerSummaryConsumptionDomesticHotWaterUnit(), state_class=SensorStateClass.TOTAL_INCREASING, @@ -321,7 +327,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="power_production_current", name="Power production current", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, value_getter=lambda api: api.getPowerProductionCurrent(), device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, @@ -329,15 +335,15 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="power_production_today", name="Energy production today", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, value_getter=lambda api: api.getPowerProductionToday(), device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), ViCareSensorEntityDescription( key="power_production_this_week", - name="Power production this week", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + name="Energy production this week", + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, value_getter=lambda api: api.getPowerProductionThisWeek(), device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, @@ -345,7 +351,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="power_production_this_month", name="Energy production this month", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, value_getter=lambda api: api.getPowerProductionThisMonth(), device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, @@ -353,7 +359,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="power_production_this_year", name="Energy production this year", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, value_getter=lambda api: api.getPowerProductionThisYear(), device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, @@ -361,7 +367,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="solar storage temperature", name="Solar Storage Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, value_getter=lambda api: api.getSolarStorageTemperature(), device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, @@ -369,7 +375,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="collector temperature", name="Solar Collector Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, value_getter=lambda api: api.getSolarCollectorTemperature(), device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, @@ -377,7 +383,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="solar power production today", name="Solar energy production today", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, value_getter=lambda api: api.getSolarPowerProductionToday(), unit_getter=lambda api: api.getSolarPowerProductionUnit(), device_class=SensorDeviceClass.ENERGY, @@ -386,7 +392,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="solar power production this week", name="Solar energy production this week", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, value_getter=lambda api: api.getSolarPowerProductionThisWeek(), unit_getter=lambda api: api.getSolarPowerProductionUnit(), device_class=SensorDeviceClass.ENERGY, @@ -395,7 +401,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="solar power production this month", name="Solar energy production this month", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, value_getter=lambda api: api.getSolarPowerProductionThisMonth(), unit_getter=lambda api: api.getSolarPowerProductionUnit(), device_class=SensorDeviceClass.ENERGY, @@ -404,7 +410,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="solar power production this year", name="Solar energy production this year", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, value_getter=lambda api: api.getSolarPowerProductionThisYear(), unit_getter=lambda api: api.getSolarPowerProductionUnit(), device_class=SensorDeviceClass.ENERGY, @@ -413,7 +419,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="power consumption today", name="Energy consumption today", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, value_getter=lambda api: api.getPowerConsumptionToday(), unit_getter=lambda api: api.getPowerConsumptionUnit(), device_class=SensorDeviceClass.ENERGY, @@ -422,7 +428,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="power consumption this week", name="Power consumption this week", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, value_getter=lambda api: api.getPowerConsumptionThisWeek(), unit_getter=lambda api: api.getPowerConsumptionUnit(), device_class=SensorDeviceClass.ENERGY, @@ -431,7 +437,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="power consumption this month", name="Energy consumption this month", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, value_getter=lambda api: api.getPowerConsumptionThisMonth(), unit_getter=lambda api: api.getPowerConsumptionUnit(), device_class=SensorDeviceClass.ENERGY, @@ -440,7 +446,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="power consumption this year", name="Energy consumption this year", - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, value_getter=lambda api: api.getPowerConsumptionThisYear(), unit_getter=lambda api: api.getPowerConsumptionUnit(), device_class=SensorDeviceClass.ENERGY, @@ -452,7 +458,7 @@ CIRCUIT_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( key="supply_temperature", name="Supply Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, value_getter=lambda api: api.getSupplyTemperature(), device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, @@ -471,7 +477,7 @@ BURNER_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( key="burner_hours", name="Burner Hours", icon="mdi:counter", - native_unit_of_measurement=TIME_HOURS, + native_unit_of_measurement=UnitOfTime.HOURS, value_getter=lambda api: api.getHours(), state_class=SensorStateClass.TOTAL_INCREASING, ), @@ -497,7 +503,7 @@ COMPRESSOR_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( key="compressor_hours", name="Compressor Hours", icon="mdi:counter", - native_unit_of_measurement=TIME_HOURS, + native_unit_of_measurement=UnitOfTime.HOURS, value_getter=lambda api: api.getHours(), state_class=SensorStateClass.TOTAL_INCREASING, ), @@ -505,7 +511,7 @@ COMPRESSOR_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( key="compressor_hours_loadclass1", name="Compressor Hours Load Class 1", icon="mdi:counter", - native_unit_of_measurement=TIME_HOURS, + native_unit_of_measurement=UnitOfTime.HOURS, value_getter=lambda api: api.getHoursLoadClass1(), state_class=SensorStateClass.TOTAL_INCREASING, ), @@ -513,7 +519,7 @@ COMPRESSOR_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( key="compressor_hours_loadclass2", name="Compressor Hours Load Class 2", icon="mdi:counter", - native_unit_of_measurement=TIME_HOURS, + native_unit_of_measurement=UnitOfTime.HOURS, value_getter=lambda api: api.getHoursLoadClass2(), state_class=SensorStateClass.TOTAL_INCREASING, ), @@ -521,7 +527,7 @@ COMPRESSOR_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( key="compressor_hours_loadclass3", name="Compressor Hours Load Class 3", icon="mdi:counter", - native_unit_of_measurement=TIME_HOURS, + native_unit_of_measurement=UnitOfTime.HOURS, value_getter=lambda api: api.getHoursLoadClass3(), state_class=SensorStateClass.TOTAL_INCREASING, ), @@ -529,7 +535,7 @@ COMPRESSOR_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( key="compressor_hours_loadclass4", name="Compressor Hours Load Class 4", icon="mdi:counter", - native_unit_of_measurement=TIME_HOURS, + native_unit_of_measurement=UnitOfTime.HOURS, value_getter=lambda api: api.getHoursLoadClass4(), state_class=SensorStateClass.TOTAL_INCREASING, ), @@ -537,7 +543,7 @@ COMPRESSOR_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( key="compressor_hours_loadclass5", name="Compressor Hours Load Class 5", icon="mdi:counter", - native_unit_of_measurement=TIME_HOURS, + native_unit_of_measurement=UnitOfTime.HOURS, value_getter=lambda api: api.getHoursLoadClass5(), state_class=SensorStateClass.TOTAL_INCREASING, ), diff --git a/homeassistant/components/vicare/translations/de.json b/homeassistant/components/vicare/translations/de.json index 1ef07bd2fc4..06e9e2dd823 100644 --- a/homeassistant/components/vicare/translations/de.json +++ b/homeassistant/components/vicare/translations/de.json @@ -16,7 +16,7 @@ "password": "Passwort", "username": "E-Mail" }, - "description": "Richte die ViCare-Integration ein. Um einen API-Schl\u00fcssel zu generieren, gehe auf https://developer.viessmann.com" + "description": "Richte die ViCare Integration ein. Um einen API-Schl\u00fcssel zu generieren, gehe auf https://developer.viessmann.com" } } } diff --git a/homeassistant/components/vicare/translations/ko.json b/homeassistant/components/vicare/translations/ko.json new file mode 100644 index 00000000000..23699dda3c3 --- /dev/null +++ b/homeassistant/components/vicare/translations/ko.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "client_id": "API \ud0a4", + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc774\uba54\uc77c" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/vicare/water_heater.py b/homeassistant/components/vicare/water_heater.py index a3bcc79a117..59ed07bdeb2 100644 --- a/homeassistant/components/vicare/water_heater.py +++ b/homeassistant/components/vicare/water_heater.py @@ -19,7 +19,7 @@ from homeassistant.const import ( ATTR_TEMPERATURE, PRECISION_TENTHS, PRECISION_WHOLE, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo @@ -36,7 +36,9 @@ from .const import ( _LOGGER = logging.getLogger(__name__) VICARE_MODE_DHW = "dhw" +VICARE_MODE_HEATING = "heating" VICARE_MODE_DHWANDHEATING = "dhwAndHeating" +VICARE_MODE_DHWANDHEATINGCOOLING = "dhwAndHeatingCooling" VICARE_MODE_FORCEDREDUCED = "forcedReduced" VICARE_MODE_FORCEDNORMAL = "forcedNormal" VICARE_MODE_OFF = "standby" @@ -50,6 +52,8 @@ OPERATION_MODE_OFF = "off" VICARE_TO_HA_HVAC_DHW = { VICARE_MODE_DHW: OPERATION_MODE_ON, VICARE_MODE_DHWANDHEATING: OPERATION_MODE_ON, + VICARE_MODE_DHWANDHEATINGCOOLING: OPERATION_MODE_ON, + VICARE_MODE_HEATING: OPERATION_MODE_OFF, VICARE_MODE_FORCEDREDUCED: OPERATION_MODE_OFF, VICARE_MODE_FORCEDNORMAL: OPERATION_MODE_ON, VICARE_MODE_OFF: OPERATION_MODE_OFF, @@ -166,7 +170,7 @@ class ViCareWater(WaterHeaterEntity): @property def temperature_unit(self): """Return the unit of measurement.""" - return TEMP_CELSIUS + return UnitOfTemperature.CELSIUS @property def current_temperature(self): diff --git a/homeassistant/components/vilfo/translations/pt.json b/homeassistant/components/vilfo/translations/pt.json index 9f7a5918551..420cc272d60 100644 --- a/homeassistant/components/vilfo/translations/pt.json +++ b/homeassistant/components/vilfo/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, diff --git a/homeassistant/components/vivotek/camera.py b/homeassistant/components/vivotek/camera.py index 46cf2586c34..a61897f996e 100644 --- a/homeassistant/components/vivotek/camera.py +++ b/homeassistant/components/vivotek/camera.py @@ -68,7 +68,9 @@ def setup_platform( digest_auth=config[CONF_AUTHENTICATION] == HTTP_DIGEST_AUTHENTICATION, sec_lvl=config[CONF_SECURITY_LEVEL], ), - "stream_source": f"rtsp://{creds}@{config[CONF_IP_ADDRESS]}:554/{config[CONF_STREAM_PATH]}", + "stream_source": ( + f"rtsp://{creds}@{config[CONF_IP_ADDRESS]}:554/{config[CONF_STREAM_PATH]}" + ), } add_entities([VivotekCam(**args)], True) diff --git a/homeassistant/components/vizio/__init__.py b/homeassistant/components/vizio/__init__.py index 38cf916a5e6..9fc40c40c2b 100644 --- a/homeassistant/components/vizio/__init__.py +++ b/homeassistant/components/vizio/__init__.py @@ -30,7 +30,8 @@ def validate_apps(config: ConfigType) -> ConfigType: and config[CONF_DEVICE_CLASS] != MediaPlayerDeviceClass.TV ): raise vol.Invalid( - f"'{CONF_APPS}' can only be used if {CONF_DEVICE_CLASS}' is '{MediaPlayerDeviceClass.TV}'" + f"'{CONF_APPS}' can only be used if {CONF_DEVICE_CLASS}' is" + f" '{MediaPlayerDeviceClass.TV}'" ) return config @@ -94,7 +95,7 @@ async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> return unload_ok -class VizioAppsDataUpdateCoordinator(DataUpdateCoordinator): +class VizioAppsDataUpdateCoordinator(DataUpdateCoordinator[list[dict[str, Any]]]): """Define an object to hold Vizio app config data.""" def __init__(self, hass: HomeAssistant) -> None: diff --git a/homeassistant/components/vizio/config_flow.py b/homeassistant/components/vizio/config_flow.py index 54e4ef53fe1..4b49d7feed5 100644 --- a/homeassistant/components/vizio/config_flow.py +++ b/homeassistant/components/vizio/config_flow.py @@ -261,9 +261,11 @@ class VizioConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): # their configuration.yaml or to proceed with config flow pairing. We # will also provide contextual message to user explaining why _LOGGER.warning( - "Couldn't complete configuration.yaml import: '%s' key is " - "missing. Either provide '%s' key in configuration.yaml or " - "finish setup by completing configuration via frontend", + ( + "Couldn't complete configuration.yaml import: '%s' key is " + "missing. Either provide '%s' key in configuration.yaml or " + "finish setup by completing configuration via frontend" + ), CONF_ACCESS_TOKEN, CONF_ACCESS_TOKEN, ) diff --git a/homeassistant/components/vizio/media_player.py b/homeassistant/components/vizio/media_player.py index ea341f1ca02..bc852b7a855 100644 --- a/homeassistant/components/vizio/media_player.py +++ b/homeassistant/components/vizio/media_player.py @@ -32,8 +32,8 @@ from homeassistant.helpers.dispatcher import ( ) from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.update_coordinator import DataUpdateCoordinator +from . import VizioAppsDataUpdateCoordinator from .const import ( CONF_ADDITIONAL_CONFIGS, CONF_APPS, @@ -136,7 +136,7 @@ class VizioDevice(MediaPlayerEntity): device: VizioAsync, name: str, device_class: MediaPlayerDeviceClass, - apps_coordinator: DataUpdateCoordinator, + apps_coordinator: VizioAppsDataUpdateCoordinator, ) -> None: """Initialize Vizio device.""" self._config_entry = config_entry @@ -264,7 +264,9 @@ class VizioDevice(MediaPlayerEntity): # Create list of available known apps from known app list after # filtering by CONF_INCLUDE/CONF_EXCLUDE - self._available_apps = self._apps_list([app["name"] for app in self._all_apps]) + self._available_apps = self._apps_list( + [app["name"] for app in self._all_apps or ()] + ) self._current_app_config = await self._device.get_current_app_config( log_api_exception=False @@ -272,7 +274,7 @@ class VizioDevice(MediaPlayerEntity): self._attr_app_name = find_app_name( self._current_app_config, - [APP_HOME, *self._all_apps, *self._additional_app_configs], + [APP_HOME, *(self._all_apps or ()), *self._additional_app_configs], ) if self._attr_app_name == NO_APP_RUNNING: diff --git a/homeassistant/components/vizio/strings.json b/homeassistant/components/vizio/strings.json index adad8406da0..7be10b80b01 100644 --- a/homeassistant/components/vizio/strings.json +++ b/homeassistant/components/vizio/strings.json @@ -35,7 +35,7 @@ "abort": { "already_configured_device": "[%key:common::config_flow::abort::already_configured_device%]", "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", - "updated_entry": "This entry has already been setup but the name, apps, and/or options defined in the configuration do not match the previously imported configuration, so the configuration entry has been updated accordingly." + "updated_entry": "This entry has already been set up but the name, apps, and/or options defined in the configuration do not match the previously imported configuration, so the configuration entry has been updated accordingly." } }, "options": { diff --git a/homeassistant/components/vizio/translations/en.json b/homeassistant/components/vizio/translations/en.json index 55176b962f2..0ba96992424 100644 --- a/homeassistant/components/vizio/translations/en.json +++ b/homeassistant/components/vizio/translations/en.json @@ -3,7 +3,7 @@ "abort": { "already_configured_device": "Device is already configured", "cannot_connect": "Failed to connect", - "updated_entry": "This entry has already been setup but the name, apps, and/or options defined in the configuration do not match the previously imported configuration, so the configuration entry has been updated accordingly." + "updated_entry": "This entry has already been set up but the name, apps, and/or options defined in the configuration do not match the previously imported configuration, so the configuration entry has been updated accordingly." }, "error": { "cannot_connect": "Failed to connect", diff --git a/homeassistant/components/vizio/translations/lb.json b/homeassistant/components/vizio/translations/lb.json index 029d75ea2cb..57c72429dee 100644 --- a/homeassistant/components/vizio/translations/lb.json +++ b/homeassistant/components/vizio/translations/lb.json @@ -13,7 +13,7 @@ "step": { "pair_tv": { "data": { - "pin": "PIN Code" + "pin": "PIN-Code" }, "description": "Um TV sollt e Code ugewisen ginn. G\u00ebff d\u00ebse Code an d'Form a fuer weider mam n\u00e4chste Schr\u00ebtt fir d'Kopplung ofzeschl\u00e9issen.", "title": "Kopplungs Prozess ofschl\u00e9issen" diff --git a/homeassistant/components/vizio/translations/no.json b/homeassistant/components/vizio/translations/no.json index de00cf0fce6..c184a7487dd 100644 --- a/homeassistant/components/vizio/translations/no.json +++ b/homeassistant/components/vizio/translations/no.json @@ -3,7 +3,7 @@ "abort": { "already_configured_device": "Enheten er allerede konfigurert", "cannot_connect": "Tilkobling mislyktes", - "updated_entry": "Dette innlegget har allerede v\u00e6rt oppsett, men navnet, apps, og/eller alternativer som er definert i konfigurasjon som ikke stemmer med det som tidligere er importert konfigurasjon, s\u00e5 konfigurasjonen innlegget har blitt oppdatert i henhold til dette." + "updated_entry": "Denne oppf\u00f8ringen er allerede satt opp, men navnet, appene og/eller alternativene som er definert i konfigurasjonen samsvarer ikke med den tidligere importerte konfigurasjonen, s\u00e5 konfigurasjonsoppf\u00f8ringen har blitt oppdatert tilsvarende." }, "error": { "cannot_connect": "Tilkobling mislyktes", diff --git a/homeassistant/components/vizio/translations/pt.json b/homeassistant/components/vizio/translations/pt.json index b8259aca07f..d8368b56b4a 100644 --- a/homeassistant/components/vizio/translations/pt.json +++ b/homeassistant/components/vizio/translations/pt.json @@ -2,10 +2,10 @@ "config": { "abort": { "already_configured_device": "O dispositivo j\u00e1 est\u00e1 configurado", - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "pair_tv": { @@ -24,7 +24,7 @@ "data": { "access_token": "Token de Acesso", "device_class": "Tipo de dispositivo", - "host": "Servidor", + "host": "Endere\u00e7o", "name": "Nome" }, "title": "Dispositivo VIZIO SmartCast" diff --git a/homeassistant/components/vizio/translations/sk.json b/homeassistant/components/vizio/translations/sk.json index b43555b8eec..4c4a7224c03 100644 --- a/homeassistant/components/vizio/translations/sk.json +++ b/homeassistant/components/vizio/translations/sk.json @@ -2,16 +2,29 @@ "config": { "abort": { "already_configured_device": "Zariadenie u\u017e je nakonfigurovan\u00e9", - "cannot_connect": "Nepodarilo sa pripoji\u0165" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "updated_entry": "T\u00e1to polo\u017eka u\u017e bola nastaven\u00e1, ale n\u00e1zov, aplik\u00e1cie a/alebo mo\u017enosti definovan\u00e9 v konfigur\u00e1cii sa nezhoduj\u00fa s predt\u00fdm importovanou konfigur\u00e1ciou, tak\u017ee polo\u017eka konfigur\u00e1cie bola zodpovedaj\u00facim sp\u00f4sobom aktualizovan\u00e1." }, "error": { - "cannot_connect": "Nepodarilo sa pripoji\u0165" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "complete_pairing_failed": "Nie je mo\u017en\u00e9 dokon\u010di\u0165 p\u00e1rovanie. Pred op\u00e4tovn\u00fdm odoslan\u00edm skontrolujte, \u010di je zadan\u00fd k\u00f3d PIN spr\u00e1vny a \u010di je telev\u00edzor st\u00e1le nap\u00e1jan\u00fd a pripojen\u00fd k sieti.", + "existing_config_entry_found": "Existuj\u00faci konfigura\u010dn\u00fd z\u00e1znam Zariadenie VIZIO SmartCast s rovnak\u00fdm s\u00e9riov\u00fdm \u010d\u00edslom u\u017e bol nakonfigurovan\u00fd. Ak chcete nakonfigurova\u0165 t\u00fato polo\u017eku, mus\u00edte odstr\u00e1ni\u0165 existuj\u00facu polo\u017eku." }, "step": { "pair_tv": { "data": { "pin": "PIN k\u00f3d" - } + }, + "description": "V\u00e1\u0161 telev\u00edzor by mal zobrazova\u0165 k\u00f3d. Zadajte tento k\u00f3d do formul\u00e1ra a potom pokra\u010dujte \u010fal\u0161\u00edm krokom na dokon\u010denie p\u00e1rovania.", + "title": "Dokon\u010dite proces p\u00e1rovania" + }, + "pairing_complete": { + "description": "V\u00e1\u0161 Zariadenie VIZIO SmartCast je teraz pripojen\u00fd k Home Assistant.", + "title": "P\u00e1rovanie dokon\u010den\u00e9" + }, + "pairing_complete_import": { + "description": "V\u00e1\u0161 Zariadenie VIZIO SmartCast je teraz pripojen\u00fd k Home Assistant. \n\n V\u00e1\u0161 Pr\u00edstupov\u00fd token je '**{access_token}**'.", + "title": "P\u00e1rovanie dokon\u010den\u00e9" }, "user": { "data": { @@ -19,7 +32,22 @@ "device_class": "Typ zariadenia", "host": "Hostite\u013e", "name": "N\u00e1zov" - } + }, + "description": "Pr\u00edstupov\u00fd token je potrebn\u00fd len pre telev\u00edzory. Ak konfigurujete telev\u00edzor a e\u0161te nem\u00e1te Pr\u00edstupov\u00fd token , nechajte ho pr\u00e1zdne, aby ste pre\u0161li procesom p\u00e1rovania.", + "title": "Zariadenie VIZIO SmartCast" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "apps_to_include_or_exclude": "Aplik\u00e1cie, ktor\u00e9 chcete zahrn\u00fa\u0165 alebo vyl\u00fa\u010di\u0165", + "include_or_exclude": "Zahrn\u00fa\u0165 alebo vyl\u00fa\u010di\u0165 aplik\u00e1cie?", + "volume_step": "Ve\u013ekos\u0165 kroku hlasitosti" + }, + "description": "Ak m\u00e1te Smart TV, m\u00f4\u017eete volite\u013ene filtrova\u0165 zoznam zdrojov v\u00fdberom aplik\u00e1ci\u00ed, ktor\u00e9 chcete zahrn\u00fa\u0165 alebo vyl\u00fa\u010di\u0165 zo zoznamu zdrojov.", + "title": "Aktualizujte mo\u017enosti Zariadenie VIZIO SmartCast" } } } diff --git a/homeassistant/components/vlc_telnet/translations/ko.json b/homeassistant/components/vlc_telnet/translations/ko.json new file mode 100644 index 00000000000..2a4f037b228 --- /dev/null +++ b/homeassistant/components/vlc_telnet/translations/ko.json @@ -0,0 +1,31 @@ +{ + "config": { + "abort": { + "already_configured": "\uc11c\ube44\uc2a4\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638" + } + }, + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8", + "name": "\uc774\ub984", + "password": "\ube44\ubc00\ubc88\ud638", + "port": "\ud3ec\ud2b8" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/vlc_telnet/translations/pt.json b/homeassistant/components/vlc_telnet/translations/pt.json index 5e8878a0a55..4e792c4c9d5 100644 --- a/homeassistant/components/vlc_telnet/translations/pt.json +++ b/homeassistant/components/vlc_telnet/translations/pt.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "reauth_successful": "Reautentica\u00e7\u00e3o bem sucedida" }, diff --git a/homeassistant/components/vlc_telnet/translations/sk.json b/homeassistant/components/vlc_telnet/translations/sk.json index 875dff682d3..97101192e15 100644 --- a/homeassistant/components/vlc_telnet/translations/sk.json +++ b/homeassistant/components/vlc_telnet/translations/sk.json @@ -4,11 +4,13 @@ "already_configured": "Slu\u017eba u\u017e je nakonfigurovan\u00e1", "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie", - "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" + "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "flow_title": "{host}", "step": { diff --git a/homeassistant/components/volkszaehler/sensor.py b/homeassistant/components/volkszaehler/sensor.py index 44d53018048..83b242d0bc0 100644 --- a/homeassistant/components/volkszaehler/sensor.py +++ b/homeassistant/components/volkszaehler/sensor.py @@ -10,6 +10,7 @@ import voluptuous as vol from homeassistant.components.sensor import ( PLATFORM_SCHEMA, + SensorDeviceClass, SensorEntity, SensorEntityDescription, ) @@ -18,8 +19,9 @@ from homeassistant.const import ( CONF_MONITORED_CONDITIONS, CONF_NAME, CONF_PORT, - ENERGY_WATT_HOUR, - POWER_WATT, + CONF_UUID, + UnitOfEnergy, + UnitOfPower, ) from homeassistant.core import HomeAssistant from homeassistant.exceptions import PlatformNotReady @@ -31,8 +33,6 @@ from homeassistant.util import Throttle _LOGGER = logging.getLogger(__name__) -CONF_UUID = "uuid" - DEFAULT_HOST = "localhost" DEFAULT_NAME = "Volkszaehler" DEFAULT_PORT = 80 @@ -43,25 +43,29 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="average", name="Average", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, + device_class=SensorDeviceClass.POWER, icon="mdi:power-off", ), SensorEntityDescription( key="consumption", name="Consumption", - native_unit_of_measurement=ENERGY_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, + device_class=SensorDeviceClass.ENERGY, icon="mdi:power-plug", ), SensorEntityDescription( key="max", name="Max", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, + device_class=SensorDeviceClass.POWER, icon="mdi:arrow-up", ), SensorEntityDescription( key="min", name="Min", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, + device_class=SensorDeviceClass.POWER, icon="mdi:arrow-down", ), ) @@ -89,11 +93,11 @@ async def async_setup_platform( ) -> None: """Set up the Volkszaehler sensors.""" - host = config[CONF_HOST] - name = config[CONF_NAME] - port = config[CONF_PORT] - uuid = config[CONF_UUID] - conditions = config[CONF_MONITORED_CONDITIONS] + host: str = config[CONF_HOST] + name: str = config[CONF_NAME] + port: int = config[CONF_PORT] + uuid: str = config[CONF_UUID] + conditions: list[str] = config[CONF_MONITORED_CONDITIONS] session = async_get_clientsession(hass) vz_api = VolkszaehlerData(Volkszaehler(session, uuid, host=host, port=port)) @@ -115,7 +119,9 @@ async def async_setup_platform( class VolkszaehlerSensor(SensorEntity): """Implementation of a Volkszaehler sensor.""" - def __init__(self, vz_api, name, description: SensorEntityDescription): + def __init__( + self, vz_api: VolkszaehlerData, name: str, description: SensorEntityDescription + ) -> None: """Initialize the Volkszaehler sensor.""" self.entity_description = description self.vz_api = vz_api @@ -140,13 +146,13 @@ class VolkszaehlerSensor(SensorEntity): class VolkszaehlerData: """The class for handling the data retrieval from the Volkszaehler API.""" - def __init__(self, api): + def __init__(self, api: Volkszaehler) -> None: """Initialize the data object.""" self.api = api self.available = True @Throttle(MIN_TIME_BETWEEN_UPDATES) - async def async_update(self): + async def async_update(self) -> None: """Get the latest data from the Volkszaehler REST API.""" try: diff --git a/homeassistant/components/volumio/translations/pt.json b/homeassistant/components/volumio/translations/pt.json index 3616141b35b..4f91b8b46f5 100644 --- a/homeassistant/components/volumio/translations/pt.json +++ b/homeassistant/components/volumio/translations/pt.json @@ -4,13 +4,13 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "unknown": "Erro inesperado" }, "step": { "user": { "data": { - "host": "Servidor", + "host": "Endere\u00e7o", "port": "Porta" } } diff --git a/homeassistant/components/volumio/translations/sk.json b/homeassistant/components/volumio/translations/sk.json index 26921034b40..58b0cebb0ff 100644 --- a/homeassistant/components/volumio/translations/sk.json +++ b/homeassistant/components/volumio/translations/sk.json @@ -5,9 +5,14 @@ "cannot_connect": "Ned\u00e1 sa pripoji\u0165 k objavenej Volumio" }, "error": { - "cannot_connect": "Nepodarilo sa pripoji\u0165" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { + "discovery_confirm": { + "description": "Chcete prida\u0165 Volumio (`{name}`) do Home Assistant?", + "title": "Objaven\u00fd Volumio" + }, "user": { "data": { "host": "Hostite\u013e", diff --git a/homeassistant/components/volvooncall/__init__.py b/homeassistant/components/volvooncall/__init__.py index 65bc6c1cfbe..b6d97dea216 100644 --- a/homeassistant/components/volvooncall/__init__.py +++ b/homeassistant/components/volvooncall/__init__.py @@ -239,7 +239,7 @@ class VolvoData: raise InvalidAuth from exc -class VolvoUpdateCoordinator(DataUpdateCoordinator): +class VolvoUpdateCoordinator(DataUpdateCoordinator[None]): """Volvo coordinator.""" def __init__(self, hass: HomeAssistant, volvo_data: VolvoData) -> None: @@ -254,14 +254,14 @@ class VolvoUpdateCoordinator(DataUpdateCoordinator): self.volvo_data = volvo_data - async def _async_update_data(self): + async def _async_update_data(self) -> None: """Fetch data from API endpoint.""" async with async_timeout.timeout(10): await self.volvo_data.update() -class VolvoEntity(CoordinatorEntity): +class VolvoEntity(CoordinatorEntity[VolvoUpdateCoordinator]): """Base class for all VOC entities.""" def __init__( diff --git a/homeassistant/components/volvooncall/config_flow.py b/homeassistant/components/volvooncall/config_flow.py index c04fe6b4c4c..c1b3ab3f66b 100644 --- a/homeassistant/components/volvooncall/config_flow.py +++ b/homeassistant/components/volvooncall/config_flow.py @@ -92,7 +92,9 @@ class VolvoOnCallConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): ): vol.In( { UNIT_SYSTEM_METRIC: "Metric", - UNIT_SYSTEM_SCANDINAVIAN_MILES: "Metric with Scandinavian Miles", + UNIT_SYSTEM_SCANDINAVIAN_MILES: ( + "Metric with Scandinavian Miles" + ), UNIT_SYSTEM_IMPERIAL: "Imperial", } ), diff --git a/homeassistant/components/volvooncall/translations/ko.json b/homeassistant/components/volvooncall/translations/ko.json new file mode 100644 index 00000000000..3cc828cc140 --- /dev/null +++ b/homeassistant/components/volvooncall/translations/ko.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/volvooncall/translations/pt.json b/homeassistant/components/volvooncall/translations/pt.json index b89c4a9f74a..3ba98035e55 100644 --- a/homeassistant/components/volvooncall/translations/pt.json +++ b/homeassistant/components/volvooncall/translations/pt.json @@ -1,10 +1,20 @@ { "config": { + "abort": { + "already_configured": "Conta j\u00e1 configurada", + "reauth_successful": "Reautentica\u00e7\u00e3o bem sucedida" + }, + "error": { + "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", + "unknown": "Erro inesperado" + }, "step": { "user": { "data": { "mutable": "Permitir Partida/Bloqueio Remoto/etc.", - "region": "Regi\u00e3o" + "password": "Palavra-passe", + "region": "Regi\u00e3o", + "username": "Nome de Utilizador" } } } diff --git a/homeassistant/components/volvooncall/translations/sk.json b/homeassistant/components/volvooncall/translations/sk.json index c249a185495..c6a7ab6583e 100644 --- a/homeassistant/components/volvooncall/translations/sk.json +++ b/homeassistant/components/volvooncall/translations/sk.json @@ -11,11 +11,19 @@ "step": { "user": { "data": { + "mutable": "Povoli\u0165 vzdialen\u00e9 spustenie / uzamknutie / at\u010f.", "password": "Heslo", "region": "Regi\u00f3n", + "unit_system": "Meracia s\u00fastava", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } } } + }, + "issues": { + "deprecated_yaml": { + "description": "Konfigur\u00e1cia platformy Volvo On Call pomocou YAML sa v bud\u00facom vydan\u00ed aplik\u00e1cie Home Assistant odstra\u0148uje. \n\n Va\u0161a existuj\u00faca konfigur\u00e1cia bola importovan\u00e1 do pou\u017e\u00edvate\u013esk\u00e9ho rozhrania automaticky. Ak chcete tento probl\u00e9m vyrie\u0161i\u0165, odstr\u00e1\u0148te konfigur\u00e1ciu YAML zo s\u00faboru configuration.yaml a re\u0161tartujte aplik\u00e1ciu Home Assistant.", + "title": "Konfigur\u00e1cia Volvo On Call YAML sa odstra\u0148uje" + } } } \ No newline at end of file diff --git a/homeassistant/components/vulcan/calendar.py b/homeassistant/components/vulcan/calendar.py index cfa962e51a3..d9182bb9905 100644 --- a/homeassistant/components/vulcan/calendar.py +++ b/homeassistant/components/vulcan/calendar.py @@ -68,9 +68,14 @@ class VulcanCalendarEntity(CalendarEntity): "identifiers": {(DOMAIN, f"calendar_{self.student_info['id']}")}, "entry_type": DeviceEntryType.SERVICE, "name": f"{self.student_info['full_name']}: Calendar", - "model": f"{self.student_info['full_name']} - {self.student_info['class']} {self.student_info['school']}", + "model": ( + f"{self.student_info['full_name']} -" + f" {self.student_info['class']} {self.student_info['school']}" + ), "manufacturer": "Uonet +", - "configuration_url": f"https://uonetplus.vulcan.net.pl/{self.student_info['symbol']}", + "configuration_url": ( + f"https://uonetplus.vulcan.net.pl/{self.student_info['symbol']}" + ), } @property diff --git a/homeassistant/components/vulcan/config_flow.py b/homeassistant/components/vulcan/config_flow.py index f1e1c13871c..6c873194124 100644 --- a/homeassistant/components/vulcan/config_flow.py +++ b/homeassistant/components/vulcan/config_flow.py @@ -218,7 +218,9 @@ class VulcanFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): await self.async_set_unique_id(str(new_students[0].pupil.id)) self._abort_if_unique_id_configured() return self.async_create_entry( - title=f"{new_students[0].pupil.first_name} {new_students[0].pupil.last_name}", + title=( + f"{new_students[0].pupil.first_name} {new_students[0].pupil.last_name}" + ), data={ "student_id": str(new_students[0].pupil.id), "keystore": keystore.as_dict, @@ -282,7 +284,9 @@ class VulcanFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): if str(student.pupil.id) == str(entry.data["student_id"]): self.hass.config_entries.async_update_entry( entry, - title=f"{student.pupil.first_name} {student.pupil.last_name}", + title=( + f"{student.pupil.first_name} {student.pupil.last_name}" + ), data={ "student_id": str(student.pupil.id), "keystore": keystore.as_dict, diff --git a/homeassistant/components/vulcan/translations/sk.json b/homeassistant/components/vulcan/translations/sk.json index 0a366f7ef1b..dfc5b23aafd 100644 --- a/homeassistant/components/vulcan/translations/sk.json +++ b/homeassistant/components/vulcan/translations/sk.json @@ -2,35 +2,53 @@ "config": { "abort": { "all_student_already_configured": "V\u0161etci \u0161tudenti u\u017e boli pridan\u00ed.", - "already_configured": "Tento \u0161tudent u\u017e bol pridan\u00fd." + "already_configured": "Tento \u0161tudent u\u017e bol pridan\u00fd.", + "no_matching_entries": "Nena\u0161li sa \u017eiadne zodpovedaj\u00face polo\u017eky, pou\u017eite in\u00e9 konto alebo odstr\u00e1\u0148te integr\u00e1ciu s neaktu\u00e1lnym \u0161tudentom.", + "reauth_successful": "\u00daspe\u0161n\u00e9 op\u00e4tovn\u00e9 overenie" }, "error": { "cannot_connect": "Chyba pripojenia \u2013 skontrolujte svoje internetov\u00e9 pripojenie", + "expired_credentials": "Platnos\u0165 poveren\u00ed vypr\u0161ala \u2013 vytvorte si nov\u00e9 na registra\u010dnej str\u00e1nke mobilnej aplik\u00e1cie Vulcan", "expired_token": "Platnos\u0165 tokenu vypr\u0161ala \u2013 vygenerujte si nov\u00fd token", "invalid_pin": "Neplatn\u00fd k\u00f3d PIN", + "invalid_symbol": "Neplatn\u00fd symbol", "invalid_token": "Neplatn\u00fd token", "unknown": "Vyskytla sa nezn\u00e1ma chyba" }, "step": { "add_next_config_entry": { + "data": { + "use_saved_credentials": "Pou\u017eite ulo\u017een\u00e9 poverenia" + }, "description": "Prida\u0165 \u010fal\u0161ieho \u0161tudenta." }, "auth": { "data": { "pin": "Pin", + "region": "Symbol", "token": "Token" - } + }, + "description": "Prihl\u00e1ste sa do svojho \u00fa\u010dtu Vulcan pomocou registra\u010dnej str\u00e1nky mobilnej aplik\u00e1cie." }, "reauth_confirm": { "data": { "pin": "Pin", + "region": "Symbol", "token": "Token" - } + }, + "description": "Prihl\u00e1ste sa do svojho \u00fa\u010dtu Vulcan pomocou registra\u010dnej str\u00e1nky mobilnej aplik\u00e1cie." }, "select_saved_credentials": { "data": { "credentials": "Prihl\u00e1si\u0165" - } + }, + "description": "Pou\u017eite ulo\u017een\u00e9 poverenia." + }, + "select_student": { + "data": { + "student_name": "Vyberte \u0161tudenta" + }, + "description": "Vyberte \u0161tudenta, \u010fal\u0161\u00edch \u0161tudentov m\u00f4\u017eete prida\u0165 op\u00e4tovn\u00fdm pridan\u00edm integr\u00e1cie." } } } diff --git a/homeassistant/components/vultr/__init__.py b/homeassistant/components/vultr/__init__.py index 4e27e77ac86..7bf3b4b07f5 100644 --- a/homeassistant/components/vultr/__init__.py +++ b/homeassistant/components/vultr/__init__.py @@ -59,7 +59,7 @@ def setup(hass: HomeAssistant, config: ConfigType) -> bool: _LOGGER.error("Failed to make update API request because: %s", ex) persistent_notification.create( hass, - "Error: {}" "".format(ex), + f"Error: {ex}", title=NOTIFICATION_TITLE, notification_id=NOTIFICATION_ID, ) diff --git a/homeassistant/components/vultr/sensor.py b/homeassistant/components/vultr/sensor.py index c2eddfc3078..3710bd959f3 100644 --- a/homeassistant/components/vultr/sensor.py +++ b/homeassistant/components/vultr/sensor.py @@ -7,10 +7,11 @@ import voluptuous as vol from homeassistant.components.sensor import ( PLATFORM_SCHEMA, + SensorDeviceClass, SensorEntity, SensorEntityDescription, ) -from homeassistant.const import CONF_MONITORED_CONDITIONS, CONF_NAME, DATA_GIGABYTES +from homeassistant.const import CONF_MONITORED_CONDITIONS, CONF_NAME, UnitOfInformation from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -30,7 +31,8 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key=ATTR_CURRENT_BANDWIDTH_USED, name="Current Bandwidth Used", - native_unit_of_measurement=DATA_GIGABYTES, + native_unit_of_measurement=UnitOfInformation.GIGABYTES, + device_class=SensorDeviceClass.DATA_SIZE, icon="mdi:chart-histogram", ), SensorEntityDescription( diff --git a/homeassistant/components/wake_on_lan/translations/ca.json b/homeassistant/components/wake_on_lan/translations/ca.json new file mode 100644 index 00000000000..5249da9309c --- /dev/null +++ b/homeassistant/components/wake_on_lan/translations/ca.json @@ -0,0 +1,8 @@ +{ + "issues": { + "moved_yaml": { + "description": "La configuraci\u00f3 de Wake on Lan mitjan\u00e7ant YAML s'ha mogut a una integraci\u00f3.\n\nLa configuraci\u00f3 YAML actual continuar\u00e0 funcionant durant les dues pr\u00f2ximes versions de Home Assistant.\n\nMigra la teva configuraci\u00f3 YAML a la nova integraci\u00f3, consulta la documentaci\u00f3 per saber com fer-ho.", + "title": "La configuraci\u00f3 YAML de Wake on Lan s'ha mogut" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/wake_on_lan/translations/ko.json b/homeassistant/components/wake_on_lan/translations/ko.json new file mode 100644 index 00000000000..affcc91955c --- /dev/null +++ b/homeassistant/components/wake_on_lan/translations/ko.json @@ -0,0 +1,8 @@ +{ + "issues": { + "moved_yaml": { + "description": "YAML\uc744 \uc0ac\uc6a9\ud55c Wake on Lan \uad6c\uc131\uc774 \ud1b5\ud569 \ud0a4\ub85c \uc774\ub3d9\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \n\n \uae30\uc874 YAML \uad6c\uc131\uc740 2\uac1c \uc774\uc0c1\uc758 \ubc84\uc804\uc5d0\uc11c \uc791\ub3d9\ud569\ub2c8\ub2e4. \n\n \uc124\uba85\uc11c\uc5d0 \ub530\ub77c YAML \uad6c\uc131\uc744 \ud1b5\ud569 \ud0a4\ub85c \ub9c8\uc774\uadf8\ub808\uc774\uc158\ud569\ub2c8\ub2e4.", + "title": "Wake on Lan YAML \uad6c\uc131\uc774 \uc774\ub3d9\ub418\uc5c8\uc2b5\ub2c8\ub2e4." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/wake_on_lan/translations/sk.json b/homeassistant/components/wake_on_lan/translations/sk.json new file mode 100644 index 00000000000..8755515f99a --- /dev/null +++ b/homeassistant/components/wake_on_lan/translations/sk.json @@ -0,0 +1,8 @@ +{ + "issues": { + "moved_yaml": { + "description": "Konfigur\u00e1cia Wake on Lan pomocou YAML bola presunut\u00e1 do integra\u010dn\u00e9ho k\u013e\u00fa\u010da. \n\n Va\u0161a existuj\u00faca konfigur\u00e1cia YAML bude fungova\u0165 pre \u010fal\u0161ie 2 verzie. \n\n Migrujte svoju konfigur\u00e1ciu YAML na integra\u010dn\u00fd k\u013e\u00fa\u010d pod\u013ea dokument\u00e1cie.", + "title": "Konfigur\u00e1cia Wake on Lan YAML bola presunut\u00e1" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/wallbox/sensor.py b/homeassistant/components/wallbox/sensor.py index 9ce76d59608..afd2b13f790 100644 --- a/homeassistant/components/wallbox/sensor.py +++ b/homeassistant/components/wallbox/sensor.py @@ -13,11 +13,11 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - ELECTRIC_CURRENT_AMPERE, - ENERGY_KILO_WATT_HOUR, - LENGTH_KILOMETERS, PERCENTAGE, - POWER_KILO_WATT, + UnitOfElectricCurrent, + UnitOfEnergy, + UnitOfLength, + UnitOfPower, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -62,7 +62,7 @@ SENSOR_TYPES: dict[str, WallboxSensorEntityDescription] = { key=CHARGER_CHARGING_POWER_KEY, name="Charging Power", precision=2, - native_unit_of_measurement=POWER_KILO_WATT, + native_unit_of_measurement=UnitOfPower.KILO_WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), @@ -70,7 +70,7 @@ SENSOR_TYPES: dict[str, WallboxSensorEntityDescription] = { key=CHARGER_MAX_AVAILABLE_POWER_KEY, name="Max Available Power", precision=0, - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, ), @@ -86,7 +86,7 @@ SENSOR_TYPES: dict[str, WallboxSensorEntityDescription] = { icon="mdi:map-marker-distance", name="Added Range", precision=0, - native_unit_of_measurement=LENGTH_KILOMETERS, + native_unit_of_measurement=UnitOfLength.KILOMETERS, device_class=SensorDeviceClass.DISTANCE, state_class=SensorStateClass.TOTAL_INCREASING, ), @@ -94,7 +94,7 @@ SENSOR_TYPES: dict[str, WallboxSensorEntityDescription] = { key=CHARGER_ADDED_ENERGY_KEY, name="Added Energy", precision=2, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), @@ -102,7 +102,7 @@ SENSOR_TYPES: dict[str, WallboxSensorEntityDescription] = { key=CHARGER_ADDED_DISCHARGED_ENERGY_KEY, name="Discharged Energy", precision=2, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), @@ -146,7 +146,7 @@ SENSOR_TYPES: dict[str, WallboxSensorEntityDescription] = { CHARGER_MAX_CHARGING_CURRENT_KEY: WallboxSensorEntityDescription( key=CHARGER_MAX_CHARGING_CURRENT_KEY, name="Max. Charging Current", - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, ), diff --git a/homeassistant/components/wallbox/translations/ko.json b/homeassistant/components/wallbox/translations/ko.json index 7c532be8890..51239739bad 100644 --- a/homeassistant/components/wallbox/translations/ko.json +++ b/homeassistant/components/wallbox/translations/ko.json @@ -1,7 +1,28 @@ { "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" + }, "error": { - "reauth_invalid": "\uc7ac\uc778\uc99d\uc5d0 \uc2e4\ud328\ud588\uc2b5\ub2c8\ub2e4. \uc2dc\ub9ac\uc5bc \ubc88\ud638\uac00 \uc6d0\ubcf8\uacfc \uc77c\uce58\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4." + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_invalid": "\uc7ac\uc778\uc99d\uc5d0 \uc2e4\ud328\ud588\uc2b5\ub2c8\ub2e4. \uc2dc\ub9ac\uc5bc \ubc88\ud638\uac00 \uc6d0\ubcf8\uacfc \uc77c\uce58\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + } + }, + "user": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/water_heater/__init__.py b/homeassistant/components/water_heater/__init__.py index c821824d279..3cd9378cfca 100644 --- a/homeassistant/components/water_heater/__init__.py +++ b/homeassistant/components/water_heater/__init__.py @@ -21,8 +21,7 @@ from homeassistant.const import ( SERVICE_TURN_ON, STATE_OFF, STATE_ON, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant, ServiceCall import homeassistant.helpers.config_validation as cv @@ -184,7 +183,7 @@ class WaterHeaterEntity(Entity): """Return the precision of the system.""" if hasattr(self, "_attr_precision"): return self._attr_precision - if self.hass.config.units.temperature_unit == TEMP_CELSIUS: + if self.hass.config.units.temperature_unit == UnitOfTemperature.CELSIUS: return PRECISION_TENTHS return PRECISION_WHOLE @@ -325,7 +324,7 @@ class WaterHeaterEntity(Entity): if hasattr(self, "_attr_min_temp"): return self._attr_min_temp return TemperatureConverter.convert( - DEFAULT_MIN_TEMP, TEMP_FAHRENHEIT, self.temperature_unit + DEFAULT_MIN_TEMP, UnitOfTemperature.FAHRENHEIT, self.temperature_unit ) @property @@ -334,7 +333,7 @@ class WaterHeaterEntity(Entity): if hasattr(self, "_attr_max_temp"): return self._attr_max_temp return TemperatureConverter.convert( - DEFAULT_MAX_TEMP, TEMP_FAHRENHEIT, self.temperature_unit + DEFAULT_MAX_TEMP, UnitOfTemperature.FAHRENHEIT, self.temperature_unit ) @property diff --git a/homeassistant/components/water_heater/translations/sk.json b/homeassistant/components/water_heater/translations/sk.json index 61841c12639..82d78faef9b 100644 --- a/homeassistant/components/water_heater/translations/sk.json +++ b/homeassistant/components/water_heater/translations/sk.json @@ -1,7 +1,19 @@ { + "device_automation": { + "action_type": { + "turn_off": "Vypn\u00fa\u0165 {entity_name}", + "turn_on": "Zapn\u00fa\u0165 {entity_name}" + } + }, "state": { "_": { - "off": "Neakt\u00edvny" + "eco": "Eco", + "electric": "Elektrick\u00e9", + "gas": "Plyn", + "heat_pump": "Tepeln\u00e9 \u010derpadlo", + "high_demand": "Vysok\u00fd dopyt", + "off": "Neakt\u00edvny", + "performance": "V\u00fdkon" } } } \ No newline at end of file diff --git a/homeassistant/components/waterfurnace/__init__.py b/homeassistant/components/waterfurnace/__init__.py index 107ae7b9d67..50031216609 100644 --- a/homeassistant/components/waterfurnace/__init__.py +++ b/homeassistant/components/waterfurnace/__init__.py @@ -96,8 +96,10 @@ class WaterFurnaceData(threading.Thread): _LOGGER.error("Failed to refresh login credentials. Thread stopped") persistent_notification.create( self.hass, - "Error:
Connection to waterfurnace website failed " - "the maximum number of times. Thread has stopped", + ( + "Error:
Connection to waterfurnace website failed " + "the maximum number of times. Thread has stopped" + ), title=NOTIFICATION_TITLE, notification_id=NOTIFICATION_ID, ) diff --git a/homeassistant/components/waterfurnace/sensor.py b/homeassistant/components/waterfurnace/sensor.py index 454e73f5f7a..9d60de29264 100644 --- a/homeassistant/components/waterfurnace/sensor.py +++ b/homeassistant/components/waterfurnace/sensor.py @@ -5,73 +5,91 @@ from homeassistant.components.sensor import ( ENTITY_ID_FORMAT, SensorDeviceClass, SensorEntity, + SensorEntityDescription, ) -from homeassistant.const import PERCENTAGE, POWER_WATT, TEMP_FAHRENHEIT +from homeassistant.const import PERCENTAGE, UnitOfPower, UnitOfTemperature from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.util import slugify -from . import DOMAIN as WF_DOMAIN, UPDATE_TOPIC - - -class WFSensorConfig: - """Water Furnace Sensor configuration.""" - - def __init__( - self, - friendly_name, - field, - icon="mdi:gauge", - unit_of_measurement=None, - device_class=None, - ): - """Initialize configuration.""" - self.device_class = device_class - self.friendly_name = friendly_name - self.field = field - self.icon = icon - self.unit_of_measurement = unit_of_measurement - +from . import DOMAIN as WF_DOMAIN, UPDATE_TOPIC, WaterFurnaceData SENSORS = [ - WFSensorConfig("Furnace Mode", "mode"), - WFSensorConfig("Total Power", "totalunitpower", "mdi:flash", POWER_WATT), - WFSensorConfig( - "Active Setpoint", - "tstatactivesetpoint", - None, - TEMP_FAHRENHEIT, - SensorDeviceClass.TEMPERATURE, + SensorEntityDescription(name="Furnace Mode", key="mode", icon="mdi:gauge"), + SensorEntityDescription( + name="Total Power", + key="totalunitpower", + native_unit_of_measurement=UnitOfPower.WATT, + device_class=SensorDeviceClass.POWER, ), - WFSensorConfig( - "Leaving Air", - "leavingairtemp", - None, - TEMP_FAHRENHEIT, - SensorDeviceClass.TEMPERATURE, + SensorEntityDescription( + name="Active Setpoint", + key="tstatactivesetpoint", + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, + device_class=SensorDeviceClass.TEMPERATURE, ), - WFSensorConfig( - "Room Temp", - "tstatroomtemp", - None, - TEMP_FAHRENHEIT, - SensorDeviceClass.TEMPERATURE, + SensorEntityDescription( + name="Leaving Air", + key="leavingairtemp", + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, + device_class=SensorDeviceClass.TEMPERATURE, ), - WFSensorConfig("Loop Temp", "enteringwatertemp", None, TEMP_FAHRENHEIT), - WFSensorConfig( - "Humidity Set Point", "tstathumidsetpoint", "mdi:water-percent", PERCENTAGE + SensorEntityDescription( + name="Room Temp", + key="tstatroomtemp", + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, + device_class=SensorDeviceClass.TEMPERATURE, ), - WFSensorConfig( - "Humidity", "tstatrelativehumidity", "mdi:water-percent", PERCENTAGE + SensorEntityDescription( + name="Loop Temp", + key="enteringwatertemp", + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, + device_class=SensorDeviceClass.TEMPERATURE, + ), + SensorEntityDescription( + name="Humidity Set Point", + key="tstathumidsetpoint", + icon="mdi:water-percent", + native_unit_of_measurement=PERCENTAGE, + ), + SensorEntityDescription( + name="Humidity", + key="tstatrelativehumidity", + icon="mdi:water-percent", + native_unit_of_measurement=PERCENTAGE, + ), + SensorEntityDescription( + name="Compressor Power", + key="compressorpower", + native_unit_of_measurement=UnitOfPower.WATT, + device_class=SensorDeviceClass.POWER, + ), + SensorEntityDescription( + name="Fan Power", + key="fanpower", + native_unit_of_measurement=UnitOfPower.WATT, + device_class=SensorDeviceClass.POWER, + ), + SensorEntityDescription( + name="Aux Power", + key="auxpower", + native_unit_of_measurement=UnitOfPower.WATT, + device_class=SensorDeviceClass.POWER, + ), + SensorEntityDescription( + name="Loop Pump Power", + key="looppumppower", + native_unit_of_measurement=UnitOfPower.WATT, + device_class=SensorDeviceClass.POWER, + ), + SensorEntityDescription( + name="Compressor Speed", key="actualcompressorspeed", icon="mdi:speedometer" + ), + SensorEntityDescription( + name="Fan Speed", key="airflowcurrentspeed", icon="mdi:fan" ), - WFSensorConfig("Compressor Power", "compressorpower", "mdi:flash", POWER_WATT), - WFSensorConfig("Fan Power", "fanpower", "mdi:flash", POWER_WATT), - WFSensorConfig("Aux Power", "auxpower", "mdi:flash", POWER_WATT), - WFSensorConfig("Loop Pump Power", "looppumppower", "mdi:flash", POWER_WATT), - WFSensorConfig("Compressor Speed", "actualcompressorspeed", "mdi:speedometer"), - WFSensorConfig("Fan Speed", "airflowcurrentspeed", "mdi:fan"), ] @@ -87,8 +105,8 @@ def setup_platform( sensors = [] client = hass.data[WF_DOMAIN] - for sconfig in SENSORS: - sensors.append(WaterFurnaceSensor(client, sconfig)) + for description in SENSORS: + sensors.append(WaterFurnaceSensor(client, description)) add_entities(sensors) @@ -98,41 +116,18 @@ class WaterFurnaceSensor(SensorEntity): _attr_should_poll = False - def __init__(self, client, config): + def __init__( + self, client: WaterFurnaceData, description: SensorEntityDescription + ) -> None: """Initialize the sensor.""" self.client = client - self._name = config.friendly_name - self._attr = config.field - self._state = None - self._icon = config.icon - self._unit_of_measurement = config.unit_of_measurement - self._attr_device_class = config.device_class + self.entity_description = description # This ensures that the sensors are isolated per waterfurnace unit self.entity_id = ENTITY_ID_FORMAT.format( - f"wf_{slugify(self.client.unit)}_{slugify(self._attr)}" + f"wf_{slugify(self.client.unit)}_{slugify(description.key)}" ) - @property - def name(self): - """Return the name of the sensor.""" - return self._name - - @property - def native_value(self): - """Return the state of the sensor.""" - return self._state - - @property - def icon(self): - """Return icon.""" - return self._icon - - @property - def native_unit_of_measurement(self): - """Return the units of measurement.""" - return self._unit_of_measurement - async def async_added_to_hass(self) -> None: """Register callbacks.""" self.async_on_remove( @@ -145,5 +140,7 @@ class WaterFurnaceSensor(SensorEntity): def async_update_callback(self): """Update state.""" if self.client.data is not None: - self._state = getattr(self.client.data, self._attr, None) + self._attr_native_value = getattr( + self.client.data, self.entity_description.key, None + ) self.async_write_ha_state() diff --git a/homeassistant/components/watttime/sensor.py b/homeassistant/components/watttime/sensor.py index 040753d97e8..c8e9a376fdc 100644 --- a/homeassistant/components/watttime/sensor.py +++ b/homeassistant/components/watttime/sensor.py @@ -10,7 +10,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_LATITUDE, ATTR_LONGITUDE, MASS_POUNDS, PERCENTAGE +from homeassistant.const import ATTR_LATITUDE, ATTR_LONGITUDE, PERCENTAGE, UnitOfMass from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import StateType @@ -37,7 +37,7 @@ REALTIME_EMISSIONS_SENSOR_DESCRIPTIONS = ( key=SENSOR_TYPE_REALTIME_EMISSIONS_MOER, name="Marginal operating emissions rate", icon="mdi:blur", - native_unit_of_measurement=f"{MASS_POUNDS} CO2/MWh", + native_unit_of_measurement=f"{UnitOfMass.POUNDS} CO2/MWh", state_class=SensorStateClass.MEASUREMENT, ), SensorEntityDescription( diff --git a/homeassistant/components/watttime/translations/ko.json b/homeassistant/components/watttime/translations/ko.json new file mode 100644 index 00000000000..87845b65975 --- /dev/null +++ b/homeassistant/components/watttime/translations/ko.json @@ -0,0 +1,37 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "coordinates": { + "data": { + "latitude": "\uc704\ub3c4", + "longitude": "\uacbd\ub3c4" + } + }, + "location": { + "data": { + "location_type": "\uc704\uce58" + } + }, + "reauth_confirm": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638" + }, + "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" + }, + "user": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/watttime/translations/lb.json b/homeassistant/components/watttime/translations/lb.json new file mode 100644 index 00000000000..0e3a8987acd --- /dev/null +++ b/homeassistant/components/watttime/translations/lb.json @@ -0,0 +1,9 @@ +{ + "config": { + "step": { + "user": { + "description": "G\u00ebff d\u00e4i Benotzernumm an d\u00e4i Passwuert an:" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/watttime/translations/sk.json b/homeassistant/components/watttime/translations/sk.json index a18c17ea0e1..4f8797e1859 100644 --- a/homeassistant/components/watttime/translations/sk.json +++ b/homeassistant/components/watttime/translations/sk.json @@ -44,7 +44,8 @@ "init": { "data": { "show_on_map": "Zobrazi\u0165 monitorovan\u00fa polohu na mape" - } + }, + "title": "Konfigur\u00e1cia WattTime" } } } diff --git a/homeassistant/components/waze_travel_time/manifest.json b/homeassistant/components/waze_travel_time/manifest.json index 24e52d3186e..05180c649d7 100644 --- a/homeassistant/components/waze_travel_time/manifest.json +++ b/homeassistant/components/waze_travel_time/manifest.json @@ -6,5 +6,5 @@ "codeowners": ["@eifinger"], "config_flow": true, "iot_class": "cloud_polling", - "loggers": ["WazeRouteCalculator"] + "loggers": ["WazeRouteCalculator", "homeassistant.helpers.location"] } diff --git a/homeassistant/components/waze_travel_time/sensor.py b/homeassistant/components/waze_travel_time/sensor.py index c8d3e308435..f69f9a019fc 100644 --- a/homeassistant/components/waze_travel_time/sensor.py +++ b/homeassistant/components/waze_travel_time/sensor.py @@ -16,9 +16,8 @@ from homeassistant.const import ( CONF_NAME, CONF_REGION, EVENT_HOMEASSISTANT_STARTED, - LENGTH_KILOMETERS, - LENGTH_MILES, - TIME_MINUTES, + UnitOfLength, + UnitOfTime, ) from homeassistant.core import CoreState, HomeAssistant from homeassistant.helpers.device_registry import DeviceEntryType @@ -75,7 +74,7 @@ class WazeTravelTime(SensorEntity): """Representation of a Waze travel time sensor.""" _attr_attribution = "Powered by Waze" - _attr_native_unit_of_measurement = TIME_MINUTES + _attr_native_unit_of_measurement = UnitOfTime.MINUTES _attr_device_class = SensorDeviceClass.DURATION _attr_state_class = SensorStateClass.MEASUREMENT _attr_device_info = DeviceInfo( @@ -210,7 +209,7 @@ class WazeTravelTimeData: if units == IMPERIAL_UNITS: # Convert to miles. self.distance = DistanceConverter.convert( - distance, LENGTH_KILOMETERS, LENGTH_MILES + distance, UnitOfLength.KILOMETERS, UnitOfLength.MILES ) else: self.distance = distance diff --git a/homeassistant/components/waze_travel_time/translations/de.json b/homeassistant/components/waze_travel_time/translations/de.json index a12552c4870..05d1ca7b93d 100644 --- a/homeassistant/components/waze_travel_time/translations/de.json +++ b/homeassistant/components/waze_travel_time/translations/de.json @@ -14,7 +14,7 @@ "origin": "Startort", "region": "Region" }, - "description": "Gib f\u00fcr Ursprung und Ziel die Adresse oder die GPS-Koordinaten des Standorts ein (GPS-Koordinaten m\u00fcssen durch ein Komma getrennt werden). Du kannst auch eine Entity-ID eingeben, die diese Informationen in ihrem Zustand bereitstellt, eine Entity-ID mit den Attributen Breitengrad und L\u00e4ngengrad oder einen Zonen-Namen." + "description": "Gib f\u00fcr Ursprung und Ziel die Adresse oder die GPS-Koordinaten des Standorts ein (GPS-Koordinaten m\u00fcssen durch ein Komma getrennt werden). Du kannst auch eine Entit\u00e4t-ID eingeben, die diese Informationen in ihrem Zustand bereitstellt, eine Entit\u00e4t-ID mit den Attributen Breitengrad und L\u00e4ngengrad oder einen Zonen-Namen." } } }, diff --git a/homeassistant/components/waze_travel_time/translations/ko.json b/homeassistant/components/waze_travel_time/translations/ko.json index 5b2dbd7ac46..2d057c695e8 100644 --- a/homeassistant/components/waze_travel_time/translations/ko.json +++ b/homeassistant/components/waze_travel_time/translations/ko.json @@ -10,6 +10,7 @@ "user": { "data": { "destination": "\ubaa9\uc801\uc9c0", + "name": "\uc774\ub984", "origin": "\ucd9c\ubc1c\uc9c0", "region": "\uc9c0\uc5ed" }, diff --git a/homeassistant/components/waze_travel_time/translations/sk.json b/homeassistant/components/waze_travel_time/translations/sk.json index 6eef223b1a1..7c072fd84bc 100644 --- a/homeassistant/components/waze_travel_time/translations/sk.json +++ b/homeassistant/components/waze_travel_time/translations/sk.json @@ -9,8 +9,12 @@ "step": { "user": { "data": { - "name": "N\u00e1zov" - } + "destination": "Cie\u013e", + "name": "N\u00e1zov", + "origin": "V\u00fdchodzie miesto", + "region": "Regi\u00f3n" + }, + "description": "Pre Origin a Destination zadajte adresu alebo GPS s\u00faradnice miesta (GPS s\u00faradnice musia by\u0165 oddelen\u00e9 \u010diarkou). M\u00f4\u017eete tie\u017e zada\u0165 ID entity, ktor\u00e1 poskytuje tieto inform\u00e1cie vo svojom stave, ID entity s atrib\u00fatmi zemepisnej \u0161\u00edrky a d\u013a\u017eky alebo popisn\u00fd n\u00e1zov z\u00f3ny." } } }, @@ -18,9 +22,18 @@ "step": { "init": { "data": { - "units": "Jednotky" - } + "avoid_ferries": "Vyhn\u00fa\u0165 sa trajektom?", + "avoid_subscription_roads": "Vyhnite sa cest\u00e1m, ktor\u00e9 potrebuj\u00fa dia\u013eni\u010dn\u00fa zn\u00e1mku / predplatn\u00e9?", + "avoid_toll_roads": "Vyhn\u00fa\u0165 sa spoplatnen\u00fdm cest\u00e1m?", + "excl_filter": "Podre\u0165azec NIE v opise vybranej trasy", + "incl_filter": "Podre\u0165azec v popise vybranej trasy", + "realtime": "Cestovanie v re\u00e1lnom \u010dase?", + "units": "Jednotky", + "vehicle_type": "Typ vozidla" + }, + "description": "Vstupy \u201epodre\u0165azca\u201c v\u00e1m umo\u017enia prin\u00fati\u0165 integr\u00e1ciu pou\u017ei\u0165 konkr\u00e9tnu trasu alebo sa vyhn\u00fa\u0165 konkr\u00e9tnej trase pri v\u00fdpo\u010dte cestovania v \u010dase." } } - } + }, + "title": "\u010cas cesty Waze" } \ No newline at end of file diff --git a/homeassistant/components/weather/__init__.py b/homeassistant/components/weather/__init__.py index 610b8f7c355..805139d134e 100644 --- a/homeassistant/components/weather/__init__.py +++ b/homeassistant/components/weather/__init__.py @@ -297,9 +297,11 @@ class WeatherEntity(Entity): "https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue" ) _LOGGER.warning( - "%s::%s is overriding deprecated methods on an instance of " - "WeatherEntity, this is not valid and will be unsupported " - "from Home Assistant 2023.1. Please %s", + ( + "%s::%s is overriding deprecated methods on an instance of " + "WeatherEntity, this is not valid and will be unsupported " + "from Home Assistant 2023.1. Please %s" + ), cls.__module__, cls.__name__, report_issue, diff --git a/homeassistant/components/weather/translations/nl.json b/homeassistant/components/weather/translations/nl.json index 2fff10215ab..fa2c81f0f8c 100644 --- a/homeassistant/components/weather/translations/nl.json +++ b/homeassistant/components/weather/translations/nl.json @@ -6,10 +6,10 @@ "exceptional": "Uitzonderlijk", "fog": "Mist", "hail": "Hagel", - "lightning": "Bliksem", - "lightning-rainy": "Bliksem, regenachtig", + "lightning": "Onweer", + "lightning-rainy": "Onweer, regenachtig", "partlycloudy": "Gedeeltelijk bewolkt", - "pouring": "Gieten", + "pouring": "Regen", "rainy": "Regenachtig", "snowy": "Sneeuwachtig", "snowy-rainy": "Sneeuw-, regenachtig", diff --git a/homeassistant/components/webostv/media_player.py b/homeassistant/components/webostv/media_player.py index dcbec24c665..339124142b1 100644 --- a/homeassistant/components/webostv/media_player.py +++ b/homeassistant/components/webostv/media_player.py @@ -100,7 +100,8 @@ def cmd( except WEBOSTV_EXCEPTIONS as exc: if self.state != MediaPlayerState.OFF: raise HomeAssistantError( - f"Error calling {func.__name__} on entity {self.entity_id}, state:{self.state}" + f"Error calling {func.__name__} on entity {self.entity_id}," + f" state:{self.state}" ) from exc _LOGGER.warning( "Error calling %s on entity %s, state:%s, error: %r", diff --git a/homeassistant/components/webostv/translations/pt.json b/homeassistant/components/webostv/translations/pt.json index ce7cbc3f548..37f4b5fedc0 100644 --- a/homeassistant/components/webostv/translations/pt.json +++ b/homeassistant/components/webostv/translations/pt.json @@ -3,7 +3,7 @@ "step": { "user": { "data": { - "host": "Servidor" + "host": "Endere\u00e7o" } } } diff --git a/homeassistant/components/websocket_api/http.py b/homeassistant/components/websocket_api/http.py index acb5bf48131..7a08d19d857 100644 --- a/homeassistant/components/websocket_api/http.py +++ b/homeassistant/components/websocket_api/http.py @@ -155,8 +155,11 @@ class WebSocketHandler: return self._logger.error( - "Client unable to keep up with pending messages. Stayed over %s for %s seconds. " - "The system's load is too high or an integration is misbehaving", + ( + "Client unable to keep up with pending messages. Stayed over %s for %s" + " seconds. The system's load is too high or an integration is" + " misbehaving" + ), PENDING_MSG_PEAK, PENDING_MSG_PEAK_TIME, ) diff --git a/homeassistant/components/wemo/sensor.py b/homeassistant/components/wemo/sensor.py index b79ce08a1e0..634d4a9e41d 100644 --- a/homeassistant/components/wemo/sensor.py +++ b/homeassistant/components/wemo/sensor.py @@ -13,7 +13,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ENERGY_KILO_WATT_HOUR, POWER_WATT +from homeassistant.const import UnitOfEnergy, UnitOfPower from homeassistant.core import HomeAssistant from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -37,7 +37,7 @@ ATTRIBUTE_SENSORS = ( name="Current Power", device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, key="current_power_watts", unique_id_suffix="currentpower", state_conversion=lambda state: round(cast(float, state), 2), @@ -46,7 +46,7 @@ ATTRIBUTE_SENSORS = ( name="Today Energy", device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, key="today_kwh", unique_id_suffix="todaymw", state_conversion=lambda state: round(cast(float, state), 2), diff --git a/homeassistant/components/wemo/translations/sk.json b/homeassistant/components/wemo/translations/sk.json index 99798036ffd..e6a3750659f 100644 --- a/homeassistant/components/wemo/translations/sk.json +++ b/homeassistant/components/wemo/translations/sk.json @@ -3,6 +3,16 @@ "abort": { "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + }, + "step": { + "confirm": { + "description": "Chcete nastavi\u0165 Wemo?" + } + } + }, + "device_automation": { + "trigger_type": { + "long_press": "Tla\u010didlo Wemo bolo stla\u010den\u00e9 na 2 sekundy" } } } \ No newline at end of file diff --git a/homeassistant/components/wemo/wemo_device.py b/homeassistant/components/wemo/wemo_device.py index 826df24a108..56ed9b8e1e6 100644 --- a/homeassistant/components/wemo/wemo_device.py +++ b/homeassistant/components/wemo/wemo_device.py @@ -30,7 +30,7 @@ from .const import DOMAIN, WEMO_SUBSCRIPTION_EVENT _LOGGER = logging.getLogger(__name__) -class DeviceCoordinator(DataUpdateCoordinator): +class DeviceCoordinator(DataUpdateCoordinator[None]): """Home Assistant wrapper for a pyWeMo device.""" def __init__(self, hass: HomeAssistant, wemo: WeMoDevice, device_id: str) -> None: diff --git a/homeassistant/components/whirlpool/climate.py b/homeassistant/components/whirlpool/climate.py index 729746a0bcf..ad4cd2ea389 100644 --- a/homeassistant/components/whirlpool/climate.py +++ b/homeassistant/components/whirlpool/climate.py @@ -22,7 +22,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import generate_entity_id from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -102,7 +102,7 @@ class AirConEntity(ClimateEntity): ) _attr_swing_modes = SUPPORTED_SWING_MODES _attr_target_temperature_step = SUPPORTED_TARGET_TEMPERATURE_STEP - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS _attr_should_poll = False def __init__(self, hass, said, name, backend_selector: BackendSelector, auth: Auth): diff --git a/homeassistant/components/whirlpool/translations/ko.json b/homeassistant/components/whirlpool/translations/ko.json new file mode 100644 index 00000000000..6758a02c625 --- /dev/null +++ b/homeassistant/components/whirlpool/translations/ko.json @@ -0,0 +1,17 @@ +{ + "config": { + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/whois/sensor.py b/homeassistant/components/whois/sensor.py index 68e77162a4f..6bf2b9d2f02 100644 --- a/homeassistant/components/whois/sensor.py +++ b/homeassistant/components/whois/sensor.py @@ -14,7 +14,7 @@ from homeassistant.components.sensor import ( SensorEntityDescription, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_DOMAIN, TIME_DAYS +from homeassistant.const import CONF_DOMAIN, UnitOfTime from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceEntryType from homeassistant.helpers.entity import DeviceInfo, EntityCategory @@ -81,7 +81,7 @@ SENSORS: tuple[WhoisSensorEntityDescription, ...] = ( key="days_until_expiration", name="Days until expiration", icon="mdi:calendar-clock", - native_unit_of_measurement=TIME_DAYS, + native_unit_of_measurement=UnitOfTime.DAYS, value_fn=_days_until_expiration, ), WhoisSensorEntityDescription( diff --git a/homeassistant/components/whois/translations/ko.json b/homeassistant/components/whois/translations/ko.json new file mode 100644 index 00000000000..e1300423811 --- /dev/null +++ b/homeassistant/components/whois/translations/ko.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "\uc11c\ube44\uc2a4\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/whois/translations/sk.json b/homeassistant/components/whois/translations/sk.json index 3ad01750f60..ecc800c5da0 100644 --- a/homeassistant/components/whois/translations/sk.json +++ b/homeassistant/components/whois/translations/sk.json @@ -4,7 +4,10 @@ "already_configured": "Service is already configured" }, "error": { - "unexpected_response": "Neo\u010dak\u00e1van\u00e1 odpove\u010f zo servera whois" + "unexpected_response": "Neo\u010dak\u00e1van\u00e1 odpove\u010f zo servera whois", + "unknown_date_format": "Nezn\u00e1my form\u00e1t d\u00e1tumu v odpovedi servera whois", + "unknown_tld": "Dan\u00e1 TLD je nezn\u00e1ma alebo nie je pre t\u00fato integr\u00e1ciu dostupn\u00e1", + "whois_command_failed": "Pr\u00edkaz Whois zlyhal: nepodarilo sa z\u00edska\u0165 inform\u00e1cie whois" }, "step": { "user": { diff --git a/homeassistant/components/wiffi/sensor.py b/homeassistant/components/wiffi/sensor.py index e4c0597d1be..b53a9140857 100644 --- a/homeassistant/components/wiffi/sensor.py +++ b/homeassistant/components/wiffi/sensor.py @@ -5,7 +5,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import DEGREE, PRESSURE_MBAR, TEMP_CELSIUS +from homeassistant.const import DEGREE, UnitOfPressure, UnitOfTemperature from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -31,8 +31,8 @@ UOM_TO_DEVICE_CLASS_MAP = { # map to convert wiffi unit of measurements to common HA uom's UOM_MAP = { WIFFI_UOM_DEGREE: DEGREE, - WIFFI_UOM_TEMP_CELSIUS: TEMP_CELSIUS, - WIFFI_UOM_MILLI_BAR: PRESSURE_MBAR, + WIFFI_UOM_TEMP_CELSIUS: UnitOfTemperature.CELSIUS, + WIFFI_UOM_MILLI_BAR: UnitOfPressure.MBAR, } diff --git a/homeassistant/components/wiffi/strings.json b/homeassistant/components/wiffi/strings.json index ebc5dd67a20..ac92a8ee8c3 100644 --- a/homeassistant/components/wiffi/strings.json +++ b/homeassistant/components/wiffi/strings.json @@ -2,7 +2,7 @@ "config": { "step": { "user": { - "title": "Setup TCP server for WIFFI devices", + "title": "Set up TCP server for WIFFI devices", "data": { "port": "[%key:common::config_flow::data::port%]" } diff --git a/homeassistant/components/wiffi/translations/de.json b/homeassistant/components/wiffi/translations/de.json index 101fed5f6a6..16d013c7398 100644 --- a/homeassistant/components/wiffi/translations/de.json +++ b/homeassistant/components/wiffi/translations/de.json @@ -10,7 +10,7 @@ "data": { "port": "Port" }, - "title": "TCP-Server f\u00fcr WIFFI-Ger\u00e4te einrichten" + "title": "TCP-Server f\u00fcr WIFFI Ger\u00e4te einrichten" } } }, diff --git a/homeassistant/components/wiffi/translations/en.json b/homeassistant/components/wiffi/translations/en.json index 6732e5102da..e24c8aa7eca 100644 --- a/homeassistant/components/wiffi/translations/en.json +++ b/homeassistant/components/wiffi/translations/en.json @@ -10,7 +10,7 @@ "data": { "port": "Port" }, - "title": "Setup TCP server for WIFFI devices" + "title": "Set up TCP server for WIFFI devices" } } }, diff --git a/homeassistant/components/wiffi/translations/sk.json b/homeassistant/components/wiffi/translations/sk.json index cf4a04ee532..26b51ac71d1 100644 --- a/homeassistant/components/wiffi/translations/sk.json +++ b/homeassistant/components/wiffi/translations/sk.json @@ -2,12 +2,23 @@ "config": { "abort": { "addr_in_use": "Port servera sa u\u017e pou\u017e\u00edva.", - "already_configured": "Port servera je u\u017e nakonfigurovan\u00fd." + "already_configured": "Port servera je u\u017e nakonfigurovan\u00fd.", + "start_server_failed": "Spustenie servera zlyhalo." }, "step": { "user": { "data": { "port": "Port" + }, + "title": "Nastavte TCP server pre zariadenia WIFFI" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "timeout": "\u010casov\u00fd limit (min\u00faty)" } } } diff --git a/homeassistant/components/wirelesstag/__init__.py b/homeassistant/components/wirelesstag/__init__.py index 86dd5f2802b..06fbfa3621e 100644 --- a/homeassistant/components/wirelesstag/__init__.py +++ b/homeassistant/components/wirelesstag/__init__.py @@ -12,9 +12,9 @@ from homeassistant.const import ( ATTR_VOLTAGE, CONF_PASSWORD, CONF_USERNAME, - ELECTRIC_POTENTIAL_VOLT, PERCENTAGE, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, + UnitOfElectricPotential, ) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv @@ -118,8 +118,7 @@ class WirelessTagPlatform: ) except Exception as ex: # pylint: disable=broad-except _LOGGER.error( - "Unable to handle tag update:\ - %s error: %s", + "Unable to handle tag update: %s error: %s", str(tag), str(ex), ) @@ -213,8 +212,14 @@ class WirelessTagBaseSensor(Entity): """Return the state attributes.""" return { ATTR_BATTERY_LEVEL: int(self._tag.battery_remaining * 100), - ATTR_VOLTAGE: f"{self._tag.battery_volts:.2f}{ELECTRIC_POTENTIAL_VOLT}", - ATTR_TAG_SIGNAL_STRENGTH: f"{self._tag.signal_strength}{SIGNAL_STRENGTH_DECIBELS_MILLIWATT}", + ATTR_VOLTAGE: ( + f"{self._tag.battery_volts:.2f}{UnitOfElectricPotential.VOLT}" + ), + ATTR_TAG_SIGNAL_STRENGTH: ( + f"{self._tag.signal_strength}{SIGNAL_STRENGTH_DECIBELS_MILLIWATT}" + ), ATTR_TAG_OUT_OF_RANGE: not self._tag.is_in_range, - ATTR_TAG_POWER_CONSUMPTION: f"{self._tag.power_consumption:.2f}{PERCENTAGE}", + ATTR_TAG_POWER_CONSUMPTION: ( + f"{self._tag.power_consumption:.2f}{PERCENTAGE}" + ), } diff --git a/homeassistant/components/withings/__init__.py b/homeassistant/components/withings/__init__.py index da2174c3822..8220bb547c9 100644 --- a/homeassistant/components/withings/__init__.py +++ b/homeassistant/components/withings/__init__.py @@ -205,7 +205,10 @@ async def async_webhook_handler( data_manager = get_data_manager_by_webhook_id(hass, webhook_id) if not data_manager: _LOGGER.error( - "Webhook id %s not handled by data manager. This is a bug and should be reported", + ( + "Webhook id %s not handled by data manager. This is a bug and should be" + " reported" + ), webhook_id, ) return json_message_response("User not found", message_code=1) diff --git a/homeassistant/components/withings/binary_sensor.py b/homeassistant/components/withings/binary_sensor.py index ff98e6e0d45..6b072030bda 100644 --- a/homeassistant/components/withings/binary_sensor.py +++ b/homeassistant/components/withings/binary_sensor.py @@ -1,16 +1,47 @@ """Sensors flow for Withings.""" from __future__ import annotations +from dataclasses import dataclass + +from withings_api.common import NotifyAppli + from homeassistant.components.binary_sensor import ( - DOMAIN as BINARY_SENSOR_DOMAIN, BinarySensorDeviceClass, BinarySensorEntity, + BinarySensorEntityDescription, ) from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .common import BaseWithingsSensor, async_create_entities +from .common import ( + BaseWithingsSensor, + UpdateType, + WithingsEntityDescription, + async_get_data_manager, +) +from .const import Measurement + + +@dataclass +class WithingsBinarySensorEntityDescription( + BinarySensorEntityDescription, WithingsEntityDescription +): + """Immutable class for describing withings binary sensor data.""" + + +BINARY_SENSORS = [ + # Webhook measurements. + WithingsBinarySensorEntityDescription( + key=Measurement.IN_BED.value, + measurement=Measurement.IN_BED, + measure_type=NotifyAppli.BED_IN, + name="In bed", + icon="mdi:bed", + update_type=UpdateType.WEBHOOK, + device_class=BinarySensorDeviceClass.OCCUPANCY, + ), +] async def async_setup_entry( @@ -19,9 +50,12 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up the sensor config entry.""" - entities = await async_create_entities( - hass, entry, WithingsHealthBinarySensor, BINARY_SENSOR_DOMAIN - ) + data_manager = await async_get_data_manager(hass, entry) + + entities = [ + WithingsHealthBinarySensor(data_manager, attribute) + for attribute in BINARY_SENSORS + ] async_add_entities(entities, True) @@ -29,7 +63,7 @@ async def async_setup_entry( class WithingsHealthBinarySensor(BaseWithingsSensor, BinarySensorEntity): """Implementation of a Withings sensor.""" - _attr_device_class = BinarySensorDeviceClass.OCCUPANCY + entity_description: WithingsBinarySensorEntityDescription @property def is_on(self) -> bool | None: diff --git a/homeassistant/components/withings/common.py b/homeassistant/components/withings/common.py index 429360b272d..c1616a062ca 100644 --- a/homeassistant/components/withings/common.py +++ b/homeassistant/components/withings/common.py @@ -6,7 +6,7 @@ from collections.abc import Callable from dataclasses import dataclass import datetime from datetime import timedelta -from enum import Enum, IntEnum +from enum import IntEnum from http import HTTPStatus import logging import re @@ -27,27 +27,19 @@ from withings_api.common import ( query_measure_groups, ) +from homeassistant.backports.enum import StrEnum from homeassistant.components import webhook from homeassistant.components.application_credentials import AuthImplementation -from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN from homeassistant.components.http import HomeAssistantView -from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - CONF_WEBHOOK_ID, - MASS_KILOGRAMS, - PERCENTAGE, - SPEED_METERS_PER_SECOND, - TIME_SECONDS, -) +from homeassistant.const import CONF_WEBHOOK_ID from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback -from homeassistant.exceptions import HomeAssistantError -from homeassistant.helpers import config_entry_oauth2_flow, entity_registry as er +from homeassistant.helpers import config_entry_oauth2_flow from homeassistant.helpers.config_entry_oauth2_flow import ( AbstractOAuth2Implementation, OAuth2Session, ) -from homeassistant.helpers.entity import Entity +from homeassistant.helpers.entity import Entity, EntityDescription from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from homeassistant.util import dt @@ -62,18 +54,8 @@ NOT_AUTHENTICATED_ERROR = re.compile( ) DATA_UPDATED_SIGNAL = "withings_entity_state_updated" -MeasurementData = dict[Measurement, Any] - -class NotAuthenticatedError(HomeAssistantError): - """Raise when not authenticated with the service.""" - - -class ServiceError(HomeAssistantError): - """Raise when the service has an error.""" - - -class UpdateType(Enum): +class UpdateType(StrEnum): """Data update type.""" POLL = "poll" @@ -81,25 +63,17 @@ class UpdateType(Enum): @dataclass -class WithingsAttribute: - """Immutable class for describing withings sensor data.""" +class WithingsEntityDescriptionMixin: + """Mixin for describing withings data.""" measurement: Measurement - measute_type: Enum - friendly_name: str - unit_of_measurement: str - icon: str | None - platform: str - enabled_by_default: bool + measure_type: NotifyAppli | GetSleepSummaryField | MeasureType update_type: UpdateType @dataclass -class WithingsData: - """Represents value and meta-data from the withings service.""" - - attribute: WithingsAttribute - value: Any +class WithingsEntityDescription(EntityDescription, WithingsEntityDescriptionMixin): + """Immutable class for describing withings data.""" @dataclass @@ -111,365 +85,44 @@ class WebhookConfig: enabled: bool -@dataclass -class StateData: - """State data held by data manager for retrieval by entities.""" - - unique_id: str - state: Any - - -WITHINGS_ATTRIBUTES = [ - WithingsAttribute( - Measurement.WEIGHT_KG, - MeasureType.WEIGHT, - "Weight", - MASS_KILOGRAMS, - "mdi:weight-kilogram", - SENSOR_DOMAIN, - True, - UpdateType.POLL, - ), - WithingsAttribute( - Measurement.FAT_MASS_KG, - MeasureType.FAT_MASS_WEIGHT, - "Fat Mass", - MASS_KILOGRAMS, - "mdi:weight-kilogram", - SENSOR_DOMAIN, - True, - UpdateType.POLL, - ), - WithingsAttribute( - Measurement.FAT_FREE_MASS_KG, - MeasureType.FAT_FREE_MASS, - "Fat Free Mass", - MASS_KILOGRAMS, - "mdi:weight-kilogram", - SENSOR_DOMAIN, - True, - UpdateType.POLL, - ), - WithingsAttribute( - Measurement.MUSCLE_MASS_KG, - MeasureType.MUSCLE_MASS, - "Muscle Mass", - MASS_KILOGRAMS, - "mdi:weight-kilogram", - SENSOR_DOMAIN, - True, - UpdateType.POLL, - ), - WithingsAttribute( - Measurement.BONE_MASS_KG, - MeasureType.BONE_MASS, - "Bone Mass", - MASS_KILOGRAMS, - "mdi:weight-kilogram", - SENSOR_DOMAIN, - True, - UpdateType.POLL, - ), - WithingsAttribute( - Measurement.HEIGHT_M, - MeasureType.HEIGHT, - "Height", - const.UOM_LENGTH_M, - "mdi:ruler", - SENSOR_DOMAIN, - False, - UpdateType.POLL, - ), - WithingsAttribute( - Measurement.TEMP_C, - MeasureType.TEMPERATURE, - "Temperature", - const.UOM_TEMP_C, - "mdi:thermometer", - SENSOR_DOMAIN, - True, - UpdateType.POLL, - ), - WithingsAttribute( - Measurement.BODY_TEMP_C, - MeasureType.BODY_TEMPERATURE, - "Body Temperature", - const.UOM_TEMP_C, - "mdi:thermometer", - SENSOR_DOMAIN, - True, - UpdateType.POLL, - ), - WithingsAttribute( - Measurement.SKIN_TEMP_C, - MeasureType.SKIN_TEMPERATURE, - "Skin Temperature", - const.UOM_TEMP_C, - "mdi:thermometer", - SENSOR_DOMAIN, - True, - UpdateType.POLL, - ), - WithingsAttribute( - Measurement.FAT_RATIO_PCT, - MeasureType.FAT_RATIO, - "Fat Ratio", - PERCENTAGE, - None, - SENSOR_DOMAIN, - True, - UpdateType.POLL, - ), - WithingsAttribute( - Measurement.DIASTOLIC_MMHG, - MeasureType.DIASTOLIC_BLOOD_PRESSURE, - "Diastolic Blood Pressure", - const.UOM_MMHG, - None, - SENSOR_DOMAIN, - True, - UpdateType.POLL, - ), - WithingsAttribute( - Measurement.SYSTOLIC_MMGH, - MeasureType.SYSTOLIC_BLOOD_PRESSURE, - "Systolic Blood Pressure", - const.UOM_MMHG, - None, - SENSOR_DOMAIN, - True, - UpdateType.POLL, - ), - WithingsAttribute( - Measurement.HEART_PULSE_BPM, - MeasureType.HEART_RATE, - "Heart Pulse", - const.UOM_BEATS_PER_MINUTE, - "mdi:heart-pulse", - SENSOR_DOMAIN, - True, - UpdateType.POLL, - ), - WithingsAttribute( - Measurement.SPO2_PCT, - MeasureType.SP02, - "SP02", - PERCENTAGE, - None, - SENSOR_DOMAIN, - True, - UpdateType.POLL, - ), - WithingsAttribute( - Measurement.HYDRATION, - MeasureType.HYDRATION, - "Hydration", - MASS_KILOGRAMS, - "mdi:water", - SENSOR_DOMAIN, - False, - UpdateType.POLL, - ), - WithingsAttribute( - Measurement.PWV, - MeasureType.PULSE_WAVE_VELOCITY, - "Pulse Wave Velocity", - SPEED_METERS_PER_SECOND, - None, - SENSOR_DOMAIN, - True, - UpdateType.POLL, - ), - WithingsAttribute( - Measurement.SLEEP_BREATHING_DISTURBANCES_INTENSITY, - GetSleepSummaryField.BREATHING_DISTURBANCES_INTENSITY, - "Breathing disturbances intensity", - "", - "", - SENSOR_DOMAIN, - False, - UpdateType.POLL, - ), - WithingsAttribute( - Measurement.SLEEP_DEEP_DURATION_SECONDS, - GetSleepSummaryField.DEEP_SLEEP_DURATION, - "Deep sleep", - TIME_SECONDS, - "mdi:sleep", - SENSOR_DOMAIN, - False, - UpdateType.POLL, - ), - WithingsAttribute( - Measurement.SLEEP_TOSLEEP_DURATION_SECONDS, - GetSleepSummaryField.DURATION_TO_SLEEP, - "Time to sleep", - TIME_SECONDS, - "mdi:sleep", - SENSOR_DOMAIN, - False, - UpdateType.POLL, - ), - WithingsAttribute( - Measurement.SLEEP_TOWAKEUP_DURATION_SECONDS, - GetSleepSummaryField.DURATION_TO_WAKEUP, - "Time to wakeup", - TIME_SECONDS, - "mdi:sleep-off", - SENSOR_DOMAIN, - False, - UpdateType.POLL, - ), - WithingsAttribute( - Measurement.SLEEP_HEART_RATE_AVERAGE, - GetSleepSummaryField.HR_AVERAGE, - "Average heart rate", - const.UOM_BEATS_PER_MINUTE, - "mdi:heart-pulse", - SENSOR_DOMAIN, - False, - UpdateType.POLL, - ), - WithingsAttribute( - Measurement.SLEEP_HEART_RATE_MAX, - GetSleepSummaryField.HR_MAX, - "Maximum heart rate", - const.UOM_BEATS_PER_MINUTE, - "mdi:heart-pulse", - SENSOR_DOMAIN, - False, - UpdateType.POLL, - ), - WithingsAttribute( - Measurement.SLEEP_HEART_RATE_MIN, - GetSleepSummaryField.HR_MIN, - "Minimum heart rate", - const.UOM_BEATS_PER_MINUTE, - "mdi:heart-pulse", - SENSOR_DOMAIN, - False, - UpdateType.POLL, - ), - WithingsAttribute( - Measurement.SLEEP_LIGHT_DURATION_SECONDS, - GetSleepSummaryField.LIGHT_SLEEP_DURATION, - "Light sleep", - TIME_SECONDS, - "mdi:sleep", - SENSOR_DOMAIN, - False, - UpdateType.POLL, - ), - WithingsAttribute( - Measurement.SLEEP_REM_DURATION_SECONDS, - GetSleepSummaryField.REM_SLEEP_DURATION, - "REM sleep", - TIME_SECONDS, - "mdi:sleep", - SENSOR_DOMAIN, - False, - UpdateType.POLL, - ), - WithingsAttribute( - Measurement.SLEEP_RESPIRATORY_RATE_AVERAGE, - GetSleepSummaryField.RR_AVERAGE, - "Average respiratory rate", - const.UOM_BREATHS_PER_MINUTE, - None, - SENSOR_DOMAIN, - False, - UpdateType.POLL, - ), - WithingsAttribute( - Measurement.SLEEP_RESPIRATORY_RATE_MAX, - GetSleepSummaryField.RR_MAX, - "Maximum respiratory rate", - const.UOM_BREATHS_PER_MINUTE, - None, - SENSOR_DOMAIN, - False, - UpdateType.POLL, - ), - WithingsAttribute( - Measurement.SLEEP_RESPIRATORY_RATE_MIN, - GetSleepSummaryField.RR_MIN, - "Minimum respiratory rate", - const.UOM_BREATHS_PER_MINUTE, - None, - SENSOR_DOMAIN, - False, - UpdateType.POLL, - ), - WithingsAttribute( - Measurement.SLEEP_SCORE, - GetSleepSummaryField.SLEEP_SCORE, - "Sleep score", - const.SCORE_POINTS, - "mdi:medal", - SENSOR_DOMAIN, - False, - UpdateType.POLL, - ), - WithingsAttribute( - Measurement.SLEEP_SNORING, - GetSleepSummaryField.SNORING, - "Snoring", - "", - None, - SENSOR_DOMAIN, - False, - UpdateType.POLL, - ), - WithingsAttribute( - Measurement.SLEEP_SNORING_EPISODE_COUNT, - GetSleepSummaryField.SNORING_EPISODE_COUNT, - "Snoring episode count", - "", - None, - SENSOR_DOMAIN, - False, - UpdateType.POLL, - ), - WithingsAttribute( - Measurement.SLEEP_WAKEUP_COUNT, - GetSleepSummaryField.WAKEUP_COUNT, - "Wakeup count", - const.UOM_FREQUENCY, - "mdi:sleep-off", - SENSOR_DOMAIN, - False, - UpdateType.POLL, - ), - WithingsAttribute( - Measurement.SLEEP_WAKEUP_DURATION_SECONDS, - GetSleepSummaryField.WAKEUP_DURATION, - "Wakeup time", - TIME_SECONDS, - "mdi:sleep-off", - SENSOR_DOMAIN, - False, - UpdateType.POLL, - ), - # Webhook measurements. - WithingsAttribute( - Measurement.IN_BED, - NotifyAppli.BED_IN, - "In bed", - "", - "mdi:bed", - BINARY_SENSOR_DOMAIN, - True, - UpdateType.WEBHOOK, - ), -] - -WITHINGS_MEASUREMENTS_MAP: dict[Measurement, WithingsAttribute] = { - attr.measurement: attr for attr in WITHINGS_ATTRIBUTES -} - WITHINGS_MEASURE_TYPE_MAP: dict[ - NotifyAppli | GetSleepSummaryField | MeasureType, WithingsAttribute -] = {attr.measute_type: attr for attr in WITHINGS_ATTRIBUTES} + NotifyAppli | GetSleepSummaryField | MeasureType, Measurement +] = { + MeasureType.WEIGHT: Measurement.WEIGHT_KG, + MeasureType.FAT_MASS_WEIGHT: Measurement.FAT_MASS_KG, + MeasureType.FAT_FREE_MASS: Measurement.FAT_FREE_MASS_KG, + MeasureType.MUSCLE_MASS: Measurement.MUSCLE_MASS_KG, + MeasureType.BONE_MASS: Measurement.BONE_MASS_KG, + MeasureType.HEIGHT: Measurement.HEIGHT_M, + MeasureType.TEMPERATURE: Measurement.TEMP_C, + MeasureType.BODY_TEMPERATURE: Measurement.BODY_TEMP_C, + MeasureType.SKIN_TEMPERATURE: Measurement.SKIN_TEMP_C, + MeasureType.FAT_RATIO: Measurement.FAT_RATIO_PCT, + MeasureType.DIASTOLIC_BLOOD_PRESSURE: Measurement.DIASTOLIC_MMHG, + MeasureType.SYSTOLIC_BLOOD_PRESSURE: Measurement.SYSTOLIC_MMGH, + MeasureType.HEART_RATE: Measurement.HEART_PULSE_BPM, + MeasureType.SP02: Measurement.SPO2_PCT, + MeasureType.HYDRATION: Measurement.HYDRATION, + MeasureType.PULSE_WAVE_VELOCITY: Measurement.PWV, + GetSleepSummaryField.BREATHING_DISTURBANCES_INTENSITY: Measurement.SLEEP_BREATHING_DISTURBANCES_INTENSITY, + GetSleepSummaryField.DEEP_SLEEP_DURATION: Measurement.SLEEP_DEEP_DURATION_SECONDS, + GetSleepSummaryField.DURATION_TO_SLEEP: Measurement.SLEEP_TOSLEEP_DURATION_SECONDS, + GetSleepSummaryField.DURATION_TO_WAKEUP: Measurement.SLEEP_TOWAKEUP_DURATION_SECONDS, + GetSleepSummaryField.HR_AVERAGE: Measurement.SLEEP_HEART_RATE_AVERAGE, + GetSleepSummaryField.HR_MAX: Measurement.SLEEP_HEART_RATE_MAX, + GetSleepSummaryField.HR_MIN: Measurement.SLEEP_HEART_RATE_MIN, + GetSleepSummaryField.LIGHT_SLEEP_DURATION: Measurement.SLEEP_LIGHT_DURATION_SECONDS, + GetSleepSummaryField.REM_SLEEP_DURATION: Measurement.SLEEP_REM_DURATION_SECONDS, + GetSleepSummaryField.RR_AVERAGE: Measurement.SLEEP_RESPIRATORY_RATE_AVERAGE, + GetSleepSummaryField.RR_MAX: Measurement.SLEEP_RESPIRATORY_RATE_MAX, + GetSleepSummaryField.RR_MIN: Measurement.SLEEP_RESPIRATORY_RATE_MIN, + GetSleepSummaryField.SLEEP_SCORE: Measurement.SLEEP_SCORE, + GetSleepSummaryField.SNORING: Measurement.SLEEP_SNORING, + GetSleepSummaryField.SNORING_EPISODE_COUNT: Measurement.SLEEP_SNORING_EPISODE_COUNT, + GetSleepSummaryField.WAKEUP_COUNT: Measurement.SLEEP_WAKEUP_COUNT, + GetSleepSummaryField.WAKEUP_DURATION: Measurement.SLEEP_WAKEUP_DURATION_SECONDS, + NotifyAppli.BED_IN: Measurement.IN_BED, +} class ConfigEntryWithingsApi(AbstractWithingsApi): @@ -528,7 +181,7 @@ class WebhookUpdateCoordinator: self._hass = hass self._user_id = user_id self._listeners: list[CALLBACK_TYPE] = [] - self.data: MeasurementData = {} + self.data: dict[Measurement, Any] = {} def async_add_listener(self, listener: CALLBACK_TYPE) -> Callable[[], None]: """Add a listener.""" @@ -743,14 +396,14 @@ class DataManager: raise exception - async def _async_get_all_data(self) -> dict[MeasureType, Any] | None: + async def _async_get_all_data(self) -> dict[Measurement, Any] | None: _LOGGER.info("Updating all withings data") return { **await self.async_get_measures(), **await self.async_get_sleep_summary(), } - async def async_get_measures(self) -> dict[MeasureType, Any]: + async def async_get_measures(self) -> dict[Measurement, Any]: """Get the measures data.""" _LOGGER.debug("Updating withings measures") now = dt.utcnow() @@ -770,7 +423,7 @@ class DataManager: ) return { - WITHINGS_MEASURE_TYPE_MAP[measure.type].measurement: round( + WITHINGS_MEASURE_TYPE_MAP[measure.type]: round( float(measure.value * pow(10, measure.unit)), 2 ) for group in groups @@ -778,7 +431,7 @@ class DataManager: if measure.type in WITHINGS_MEASURE_TYPE_MAP } - async def async_get_sleep_summary(self) -> dict[MeasureType, Any]: + async def async_get_sleep_summary(self) -> dict[Measurement, Any]: """Get the sleep summary data.""" _LOGGER.debug("Updating withing sleep summary") now = dt.utcnow() @@ -862,7 +515,7 @@ class DataManager: set_value(GetSleepSummaryField.WAKEUP_DURATION, average) return { - WITHINGS_MEASURE_TYPE_MAP[field].measurement: round(value, 4) + WITHINGS_MEASURE_TYPE_MAP[field]: round(value, 4) if value is not None else None for field, value in values.items() @@ -884,78 +537,47 @@ class DataManager: ) -def get_attribute_unique_id(attribute: WithingsAttribute, user_id: int) -> str: +def get_attribute_unique_id( + description: WithingsEntityDescription, user_id: int +) -> str: """Get a entity unique id for a user's attribute.""" - return f"withings_{user_id}_{attribute.measurement.value}" - - -async def async_get_entity_id( - hass: HomeAssistant, attribute: WithingsAttribute, user_id: int -) -> str | None: - """Get an entity id for a user's attribute.""" - entity_registry = er.async_get(hass) - unique_id = get_attribute_unique_id(attribute, user_id) - - entity_id = entity_registry.async_get_entity_id( - attribute.platform, const.DOMAIN, unique_id - ) - - if entity_id is None: - _LOGGER.error("Cannot find entity id for unique_id: %s", unique_id) - return None - - return entity_id + return f"withings_{user_id}_{description.measurement.value}" class BaseWithingsSensor(Entity): """Base class for withings sensors.""" _attr_should_poll = False + entity_description: WithingsEntityDescription - def __init__(self, data_manager: DataManager, attribute: WithingsAttribute) -> None: + def __init__( + self, data_manager: DataManager, description: WithingsEntityDescription + ) -> None: """Initialize the Withings sensor.""" self._data_manager = data_manager - self._attribute = attribute - self._profile = self._data_manager.profile - self._user_id = self._data_manager.user_id - self._name = f"Withings {self._attribute.measurement.value} {self._profile}" - self._unique_id = get_attribute_unique_id(self._attribute, self._user_id) + self.entity_description = description + self._attr_name = ( + f"Withings {description.measurement.value} {data_manager.profile}" + ) + self._attr_unique_id = get_attribute_unique_id( + description, data_manager.user_id + ) self._state_data: Any | None = None - @property - def name(self) -> str: - """Return the name of the sensor.""" - return self._name - @property def available(self) -> bool: """Return True if entity is available.""" - if self._attribute.update_type == UpdateType.POLL: + if self.entity_description.update_type == UpdateType.POLL: return self._data_manager.poll_data_update_coordinator.last_update_success - if self._attribute.update_type == UpdateType.WEBHOOK: + if self.entity_description.update_type == UpdateType.WEBHOOK: return self._data_manager.webhook_config.enabled and ( - self._attribute.measurement + self.entity_description.measurement in self._data_manager.webhook_update_coordinator.data ) return True - @property - def unique_id(self) -> str: - """Return a unique, Home Assistant friendly identifier for this entity.""" - return self._unique_id - - @property - def icon(self) -> str | None: - """Icon to use in the frontend, if any.""" - return self._attribute.icon - - @property - def entity_registry_enabled_default(self) -> bool: - """Return if the entity should be enabled when first added to the entity registry.""" - return self._attribute.enabled_by_default - @callback def _on_poll_data_updated(self) -> None: self._update_state_data( @@ -968,14 +590,14 @@ class BaseWithingsSensor(Entity): self._data_manager.webhook_update_coordinator.data or {} ) - def _update_state_data(self, data: MeasurementData) -> None: + def _update_state_data(self, data: dict[Measurement, Any]) -> None: """Update the state data.""" - self._state_data = data.get(self._attribute.measurement) + self._state_data = data.get(self.entity_description.measurement) self.async_write_ha_state() async def async_added_to_hass(self) -> None: """Register update dispatcher.""" - if self._attribute.update_type == UpdateType.POLL: + if self.entity_description.update_type == UpdateType.POLL: self.async_on_remove( self._data_manager.poll_data_update_coordinator.async_add_listener( self._on_poll_data_updated @@ -983,7 +605,7 @@ class BaseWithingsSensor(Entity): ) self._on_poll_data_updated() - elif self._attribute.update_type == UpdateType.WEBHOOK: + elif self.entity_description.update_type == UpdateType.WEBHOOK: self.async_on_remove( self._data_manager.webhook_update_coordinator.async_add_listener( self._on_webhook_data_updated @@ -1057,28 +679,6 @@ def async_remove_data_manager(hass: HomeAssistant, config_entry: ConfigEntry) -> del hass.data[const.DOMAIN][config_entry.entry_id][const.DATA_MANAGER] -async def async_create_entities( - hass: HomeAssistant, - entry: ConfigEntry, - create_func: Callable[[DataManager, WithingsAttribute], Entity], - platform: str, -) -> list[Entity]: - """Create withings entities from config entry.""" - data_manager = await async_get_data_manager(hass, entry) - - return [ - create_func(data_manager, attribute) - for attribute in get_platform_attributes(platform) - ] - - -def get_platform_attributes(platform: str) -> tuple[WithingsAttribute, ...]: - """Get withings attributes used for a specific platform.""" - return tuple( - attribute for attribute in WITHINGS_ATTRIBUTES if attribute.platform == platform - ) - - class WithingsLocalOAuth2Implementation(AuthImplementation): """Oauth2 implementation that only uses the external url.""" diff --git a/homeassistant/components/withings/const.py b/homeassistant/components/withings/const.py index a5a99c193ab..20ad91f16cf 100644 --- a/homeassistant/components/withings/const.py +++ b/homeassistant/components/withings/const.py @@ -1,7 +1,6 @@ """Constants used by the Withings component.""" -from enum import Enum - from homeassistant import const +from homeassistant.backports.enum import StrEnum CONF_PROFILES = "profiles" CONF_USE_WEBHOOK = "use_webhook" @@ -15,7 +14,7 @@ PROFILE = "profile" PUSH_HANDLER = "push_handler" -class Measurement(Enum): +class Measurement(StrEnum): """Measurement supported by the withings integration.""" BODY_TEMP_C = "body_temperature_c" @@ -59,5 +58,3 @@ UOM_BEATS_PER_MINUTE = "bpm" UOM_BREATHS_PER_MINUTE = f"br/{const.TIME_MINUTES}" UOM_FREQUENCY = "times" UOM_MMHG = "mmhg" -UOM_LENGTH_M = const.LENGTH_METERS -UOM_TEMP_C = const.TEMP_CELSIUS diff --git a/homeassistant/components/withings/sensor.py b/homeassistant/components/withings/sensor.py index fbeefb2c514..c2cdd89a17f 100644 --- a/homeassistant/components/withings/sensor.py +++ b/homeassistant/components/withings/sensor.py @@ -1,16 +1,396 @@ """Sensors flow for Withings.""" from __future__ import annotations +from dataclasses import dataclass + +from withings_api.common import GetSleepSummaryField, MeasureType + from homeassistant.components.sensor import ( - DOMAIN as SENSOR_DOMAIN, + SensorDeviceClass, SensorEntity, + SensorEntityDescription, SensorStateClass, ) from homeassistant.config_entries import ConfigEntry +from homeassistant.const import ( + PERCENTAGE, + UnitOfLength, + UnitOfMass, + UnitOfSpeed, + UnitOfTemperature, + UnitOfTime, +) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .common import BaseWithingsSensor, async_create_entities +from .common import ( + BaseWithingsSensor, + UpdateType, + WithingsEntityDescription, + async_get_data_manager, +) +from .const import ( + SCORE_POINTS, + UOM_BEATS_PER_MINUTE, + UOM_BREATHS_PER_MINUTE, + UOM_FREQUENCY, + UOM_MMHG, + Measurement, +) + + +@dataclass +class WithingsSensorEntityDescription( + SensorEntityDescription, WithingsEntityDescription +): + """Immutable class for describing withings binary sensor data.""" + + +SENSORS = [ + WithingsSensorEntityDescription( + key=Measurement.WEIGHT_KG.value, + measurement=Measurement.WEIGHT_KG, + measure_type=MeasureType.WEIGHT, + name="Weight", + native_unit_of_measurement=UnitOfMass.KILOGRAMS, + device_class=SensorDeviceClass.WEIGHT, + state_class=SensorStateClass.MEASUREMENT, + update_type=UpdateType.POLL, + ), + WithingsSensorEntityDescription( + key=Measurement.FAT_MASS_KG.value, + measurement=Measurement.FAT_MASS_KG, + measure_type=MeasureType.FAT_MASS_WEIGHT, + name="Fat Mass", + native_unit_of_measurement=UnitOfMass.KILOGRAMS, + device_class=SensorDeviceClass.WEIGHT, + state_class=SensorStateClass.MEASUREMENT, + update_type=UpdateType.POLL, + ), + WithingsSensorEntityDescription( + key=Measurement.FAT_FREE_MASS_KG.value, + measurement=Measurement.FAT_FREE_MASS_KG, + measure_type=MeasureType.FAT_FREE_MASS, + name="Fat Free Mass", + native_unit_of_measurement=UnitOfMass.KILOGRAMS, + device_class=SensorDeviceClass.WEIGHT, + state_class=SensorStateClass.MEASUREMENT, + update_type=UpdateType.POLL, + ), + WithingsSensorEntityDescription( + key=Measurement.MUSCLE_MASS_KG.value, + measurement=Measurement.MUSCLE_MASS_KG, + measure_type=MeasureType.MUSCLE_MASS, + name="Muscle Mass", + native_unit_of_measurement=UnitOfMass.KILOGRAMS, + device_class=SensorDeviceClass.WEIGHT, + state_class=SensorStateClass.MEASUREMENT, + update_type=UpdateType.POLL, + ), + WithingsSensorEntityDescription( + key=Measurement.BONE_MASS_KG.value, + measurement=Measurement.BONE_MASS_KG, + measure_type=MeasureType.BONE_MASS, + name="Bone Mass", + native_unit_of_measurement=UnitOfMass.KILOGRAMS, + device_class=SensorDeviceClass.WEIGHT, + state_class=SensorStateClass.MEASUREMENT, + update_type=UpdateType.POLL, + ), + WithingsSensorEntityDescription( + key=Measurement.HEIGHT_M.value, + measurement=Measurement.HEIGHT_M, + measure_type=MeasureType.HEIGHT, + name="Height", + native_unit_of_measurement=UnitOfLength.METERS, + device_class=SensorDeviceClass.DISTANCE, + state_class=SensorStateClass.MEASUREMENT, + entity_registry_enabled_default=False, + update_type=UpdateType.POLL, + ), + WithingsSensorEntityDescription( + key=Measurement.TEMP_C.value, + measurement=Measurement.TEMP_C, + measure_type=MeasureType.TEMPERATURE, + name="Temperature", + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + update_type=UpdateType.POLL, + ), + WithingsSensorEntityDescription( + key=Measurement.BODY_TEMP_C.value, + measurement=Measurement.BODY_TEMP_C, + measure_type=MeasureType.BODY_TEMPERATURE, + name="Body Temperature", + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + update_type=UpdateType.POLL, + ), + WithingsSensorEntityDescription( + key=Measurement.SKIN_TEMP_C.value, + measurement=Measurement.SKIN_TEMP_C, + measure_type=MeasureType.SKIN_TEMPERATURE, + name="Skin Temperature", + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + update_type=UpdateType.POLL, + ), + WithingsSensorEntityDescription( + key=Measurement.FAT_RATIO_PCT.value, + measurement=Measurement.FAT_RATIO_PCT, + measure_type=MeasureType.FAT_RATIO, + name="Fat Ratio", + native_unit_of_measurement=PERCENTAGE, + state_class=SensorStateClass.MEASUREMENT, + update_type=UpdateType.POLL, + ), + WithingsSensorEntityDescription( + key=Measurement.DIASTOLIC_MMHG.value, + measurement=Measurement.DIASTOLIC_MMHG, + measure_type=MeasureType.DIASTOLIC_BLOOD_PRESSURE, + name="Diastolic Blood Pressure", + native_unit_of_measurement=UOM_MMHG, + state_class=SensorStateClass.MEASUREMENT, + update_type=UpdateType.POLL, + ), + WithingsSensorEntityDescription( + key=Measurement.SYSTOLIC_MMGH.value, + measurement=Measurement.SYSTOLIC_MMGH, + measure_type=MeasureType.SYSTOLIC_BLOOD_PRESSURE, + name="Systolic Blood Pressure", + native_unit_of_measurement=UOM_MMHG, + state_class=SensorStateClass.MEASUREMENT, + update_type=UpdateType.POLL, + ), + WithingsSensorEntityDescription( + key=Measurement.HEART_PULSE_BPM.value, + measurement=Measurement.HEART_PULSE_BPM, + measure_type=MeasureType.HEART_RATE, + name="Heart Pulse", + native_unit_of_measurement=UOM_BEATS_PER_MINUTE, + icon="mdi:heart-pulse", + state_class=SensorStateClass.MEASUREMENT, + update_type=UpdateType.POLL, + ), + WithingsSensorEntityDescription( + key=Measurement.SPO2_PCT.value, + measurement=Measurement.SPO2_PCT, + measure_type=MeasureType.SP02, + name="SP02", + native_unit_of_measurement=PERCENTAGE, + state_class=SensorStateClass.MEASUREMENT, + update_type=UpdateType.POLL, + ), + WithingsSensorEntityDescription( + key=Measurement.HYDRATION.value, + measurement=Measurement.HYDRATION, + measure_type=MeasureType.HYDRATION, + name="Hydration", + native_unit_of_measurement=UnitOfMass.KILOGRAMS, + device_class=SensorDeviceClass.WEIGHT, + icon="mdi:water", + state_class=SensorStateClass.MEASUREMENT, + entity_registry_enabled_default=False, + update_type=UpdateType.POLL, + ), + WithingsSensorEntityDescription( + key=Measurement.PWV.value, + measurement=Measurement.PWV, + measure_type=MeasureType.PULSE_WAVE_VELOCITY, + name="Pulse Wave Velocity", + native_unit_of_measurement=UnitOfSpeed.METERS_PER_SECOND, + device_class=SensorDeviceClass.SPEED, + state_class=SensorStateClass.MEASUREMENT, + update_type=UpdateType.POLL, + ), + WithingsSensorEntityDescription( + key=Measurement.SLEEP_BREATHING_DISTURBANCES_INTENSITY.value, + measurement=Measurement.SLEEP_BREATHING_DISTURBANCES_INTENSITY, + measure_type=GetSleepSummaryField.BREATHING_DISTURBANCES_INTENSITY, + name="Breathing disturbances intensity", + state_class=SensorStateClass.MEASUREMENT, + entity_registry_enabled_default=False, + update_type=UpdateType.POLL, + ), + WithingsSensorEntityDescription( + key=Measurement.SLEEP_DEEP_DURATION_SECONDS.value, + measurement=Measurement.SLEEP_DEEP_DURATION_SECONDS, + measure_type=GetSleepSummaryField.DEEP_SLEEP_DURATION, + name="Deep sleep", + native_unit_of_measurement=UnitOfTime.SECONDS, + icon="mdi:sleep", + device_class=SensorDeviceClass.DURATION, + state_class=SensorStateClass.MEASUREMENT, + entity_registry_enabled_default=False, + update_type=UpdateType.POLL, + ), + WithingsSensorEntityDescription( + key=Measurement.SLEEP_TOSLEEP_DURATION_SECONDS.value, + measurement=Measurement.SLEEP_TOSLEEP_DURATION_SECONDS, + measure_type=GetSleepSummaryField.DURATION_TO_SLEEP, + name="Time to sleep", + native_unit_of_measurement=UnitOfTime.SECONDS, + icon="mdi:sleep", + device_class=SensorDeviceClass.DURATION, + state_class=SensorStateClass.MEASUREMENT, + entity_registry_enabled_default=False, + update_type=UpdateType.POLL, + ), + WithingsSensorEntityDescription( + key=Measurement.SLEEP_TOWAKEUP_DURATION_SECONDS.value, + measurement=Measurement.SLEEP_TOWAKEUP_DURATION_SECONDS, + measure_type=GetSleepSummaryField.DURATION_TO_WAKEUP, + name="Time to wakeup", + native_unit_of_measurement=UnitOfTime.SECONDS, + icon="mdi:sleep-off", + device_class=SensorDeviceClass.DURATION, + state_class=SensorStateClass.MEASUREMENT, + entity_registry_enabled_default=False, + update_type=UpdateType.POLL, + ), + WithingsSensorEntityDescription( + key=Measurement.SLEEP_HEART_RATE_AVERAGE.value, + measurement=Measurement.SLEEP_HEART_RATE_AVERAGE, + measure_type=GetSleepSummaryField.HR_AVERAGE, + name="Average heart rate", + native_unit_of_measurement=UOM_BEATS_PER_MINUTE, + icon="mdi:heart-pulse", + state_class=SensorStateClass.MEASUREMENT, + entity_registry_enabled_default=False, + update_type=UpdateType.POLL, + ), + WithingsSensorEntityDescription( + key=Measurement.SLEEP_HEART_RATE_MAX.value, + measurement=Measurement.SLEEP_HEART_RATE_MAX, + measure_type=GetSleepSummaryField.HR_MAX, + name="Maximum heart rate", + native_unit_of_measurement=UOM_BEATS_PER_MINUTE, + icon="mdi:heart-pulse", + state_class=SensorStateClass.MEASUREMENT, + entity_registry_enabled_default=False, + update_type=UpdateType.POLL, + ), + WithingsSensorEntityDescription( + key=Measurement.SLEEP_HEART_RATE_MIN.value, + measurement=Measurement.SLEEP_HEART_RATE_MIN, + measure_type=GetSleepSummaryField.HR_MIN, + name="Minimum heart rate", + native_unit_of_measurement=UOM_BEATS_PER_MINUTE, + icon="mdi:heart-pulse", + state_class=SensorStateClass.MEASUREMENT, + entity_registry_enabled_default=False, + update_type=UpdateType.POLL, + ), + WithingsSensorEntityDescription( + key=Measurement.SLEEP_LIGHT_DURATION_SECONDS.value, + measurement=Measurement.SLEEP_LIGHT_DURATION_SECONDS, + measure_type=GetSleepSummaryField.LIGHT_SLEEP_DURATION, + name="Light sleep", + native_unit_of_measurement=UnitOfTime.SECONDS, + icon="mdi:sleep", + device_class=SensorDeviceClass.DURATION, + state_class=SensorStateClass.MEASUREMENT, + entity_registry_enabled_default=False, + update_type=UpdateType.POLL, + ), + WithingsSensorEntityDescription( + key=Measurement.SLEEP_REM_DURATION_SECONDS.value, + measurement=Measurement.SLEEP_REM_DURATION_SECONDS, + measure_type=GetSleepSummaryField.REM_SLEEP_DURATION, + name="REM sleep", + native_unit_of_measurement=UnitOfTime.SECONDS, + icon="mdi:sleep", + device_class=SensorDeviceClass.DURATION, + state_class=SensorStateClass.MEASUREMENT, + entity_registry_enabled_default=False, + update_type=UpdateType.POLL, + ), + WithingsSensorEntityDescription( + key=Measurement.SLEEP_RESPIRATORY_RATE_AVERAGE.value, + measurement=Measurement.SLEEP_RESPIRATORY_RATE_AVERAGE, + measure_type=GetSleepSummaryField.RR_AVERAGE, + name="Average respiratory rate", + native_unit_of_measurement=UOM_BREATHS_PER_MINUTE, + state_class=SensorStateClass.MEASUREMENT, + entity_registry_enabled_default=False, + update_type=UpdateType.POLL, + ), + WithingsSensorEntityDescription( + key=Measurement.SLEEP_RESPIRATORY_RATE_MAX.value, + measurement=Measurement.SLEEP_RESPIRATORY_RATE_MAX, + measure_type=GetSleepSummaryField.RR_MAX, + name="Maximum respiratory rate", + native_unit_of_measurement=UOM_BREATHS_PER_MINUTE, + state_class=SensorStateClass.MEASUREMENT, + entity_registry_enabled_default=False, + update_type=UpdateType.POLL, + ), + WithingsSensorEntityDescription( + key=Measurement.SLEEP_RESPIRATORY_RATE_MIN.value, + measurement=Measurement.SLEEP_RESPIRATORY_RATE_MIN, + measure_type=GetSleepSummaryField.RR_MIN, + name="Minimum respiratory rate", + native_unit_of_measurement=UOM_BREATHS_PER_MINUTE, + state_class=SensorStateClass.MEASUREMENT, + entity_registry_enabled_default=False, + update_type=UpdateType.POLL, + ), + WithingsSensorEntityDescription( + key=Measurement.SLEEP_SCORE.value, + measurement=Measurement.SLEEP_SCORE, + measure_type=GetSleepSummaryField.SLEEP_SCORE, + name="Sleep score", + native_unit_of_measurement=SCORE_POINTS, + icon="mdi:medal", + state_class=SensorStateClass.MEASUREMENT, + entity_registry_enabled_default=False, + update_type=UpdateType.POLL, + ), + WithingsSensorEntityDescription( + key=Measurement.SLEEP_SNORING.value, + measurement=Measurement.SLEEP_SNORING, + measure_type=GetSleepSummaryField.SNORING, + name="Snoring", + state_class=SensorStateClass.MEASUREMENT, + entity_registry_enabled_default=False, + update_type=UpdateType.POLL, + ), + WithingsSensorEntityDescription( + key=Measurement.SLEEP_SNORING_EPISODE_COUNT.value, + measurement=Measurement.SLEEP_SNORING_EPISODE_COUNT, + measure_type=GetSleepSummaryField.SNORING_EPISODE_COUNT, + name="Snoring episode count", + state_class=SensorStateClass.MEASUREMENT, + entity_registry_enabled_default=False, + update_type=UpdateType.POLL, + ), + WithingsSensorEntityDescription( + key=Measurement.SLEEP_WAKEUP_COUNT.value, + measurement=Measurement.SLEEP_WAKEUP_COUNT, + measure_type=GetSleepSummaryField.WAKEUP_COUNT, + name="Wakeup count", + native_unit_of_measurement=UOM_FREQUENCY, + icon="mdi:sleep-off", + state_class=SensorStateClass.MEASUREMENT, + entity_registry_enabled_default=False, + update_type=UpdateType.POLL, + ), + WithingsSensorEntityDescription( + key=Measurement.SLEEP_WAKEUP_DURATION_SECONDS.value, + measurement=Measurement.SLEEP_WAKEUP_DURATION_SECONDS, + measure_type=GetSleepSummaryField.WAKEUP_DURATION, + name="Wakeup time", + native_unit_of_measurement=UnitOfTime.SECONDS, + icon="mdi:sleep-off", + device_class=SensorDeviceClass.DURATION, + state_class=SensorStateClass.MEASUREMENT, + entity_registry_enabled_default=False, + update_type=UpdateType.POLL, + ), +] async def async_setup_entry( @@ -19,12 +399,9 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up the sensor config entry.""" - entities = await async_create_entities( - hass, - entry, - WithingsHealthSensor, - SENSOR_DOMAIN, - ) + data_manager = await async_get_data_manager(hass, entry) + + entities = [WithingsHealthSensor(data_manager, attribute) for attribute in SENSORS] async_add_entities(entities, True) @@ -32,17 +409,9 @@ async def async_setup_entry( class WithingsHealthSensor(BaseWithingsSensor, SensorEntity): """Implementation of a Withings sensor.""" + entity_description: WithingsSensorEntityDescription + @property def native_value(self) -> None | str | int | float: """Return the state of the entity.""" return self._state_data - - @property - def native_unit_of_measurement(self) -> str: - """Return the unit of measurement of this entity, if any.""" - return self._attribute.unit_of_measurement - - @property - def state_class(self) -> str: - """Return the state_class of this entity, if any.""" - return SensorStateClass.MEASUREMENT diff --git a/homeassistant/components/withings/translations/en_GB.json b/homeassistant/components/withings/translations/en-GB.json similarity index 100% rename from homeassistant/components/withings/translations/en_GB.json rename to homeassistant/components/withings/translations/en-GB.json diff --git a/homeassistant/components/withings/translations/ko.json b/homeassistant/components/withings/translations/ko.json index 80eb0648e07..9da0002e71f 100644 --- a/homeassistant/components/withings/translations/ko.json +++ b/homeassistant/components/withings/translations/ko.json @@ -23,6 +23,9 @@ }, "description": "\uace0\uc720\ud55c \ud504\ub85c\ud544 \uc774\ub984\uc744 \uc785\ub825\ud574\uc8fc\uc138\uc694. \uc77c\ubc18\uc801\uc73c\ub85c \uc774\uc804 \ub2e8\uacc4\uc5d0\uc11c \uc120\ud0dd\ud55c \ud504\ub85c\ud544\uc758 \uc774\ub984\uc785\ub2c8\ub2e4.", "title": "\uc0ac\uc6a9\uc790 \ud504\ub85c\ud544." + }, + "reauth_confirm": { + "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" } } } diff --git a/homeassistant/components/withings/translations/pt.json b/homeassistant/components/withings/translations/pt.json index 204a4fd2b69..3a6d2ebb57a 100644 --- a/homeassistant/components/withings/translations/pt.json +++ b/homeassistant/components/withings/translations/pt.json @@ -1,9 +1,9 @@ { "config": { "abort": { - "authorize_url_timeout": "Tempo excedido a gerar um URL de autoriza\u00e7\u00e3o", + "authorize_url_timeout": "Excedido o tempo limite para gerar um URL de autoriza\u00e7\u00e3o", "missing_configuration": "O componente n\u00e3o est\u00e1 configurado. Por favor, siga a documenta\u00e7\u00e3o.", - "no_url_available": "Nenhum URL dispon\u00edvel. Para obter informa\u00e7\u00f5es sobre esse erro, [verifique a sec\u00e7\u00e3o de ajuda]({docs_url})" + "no_url_available": "Nenhum URL est\u00e1 dispon\u00edvel. Para obter informa\u00e7\u00f5es sobre esse erro, [consulte a sec\u00e7\u00e3o de ajuda]({docs_url})" }, "error": { "already_configured": "Conta j\u00e1 configurada" diff --git a/homeassistant/components/withings/translations/sk.json b/homeassistant/components/withings/translations/sk.json index 218c7d4bec7..c964e8a23fb 100644 --- a/homeassistant/components/withings/translations/sk.json +++ b/homeassistant/components/withings/translations/sk.json @@ -20,9 +20,12 @@ "profile": { "data": { "profile": "N\u00e1zov profilu" - } + }, + "description": "Zadajte jedine\u010dn\u00fd n\u00e1zov profilu pre tieto \u00fadaje. Zvy\u010dajne ide o n\u00e1zov profilu, ktor\u00fd ste vybrali v predch\u00e1dzaj\u00facom kroku.", + "title": "U\u017e\u00edvate\u013esk\u00fd profil." }, "reauth_confirm": { + "description": "Ak chcete na\u010falej dost\u00e1va\u0165 \u00fadaje Withings, profil \u201e{profile}\u201c sa mus\u00ed znova overi\u0165.", "title": "Znova overi\u0165 integr\u00e1ciu" } } diff --git a/homeassistant/components/wiz/sensor.py b/homeassistant/components/wiz/sensor.py index d2042d6ea9c..ea4e57dc18b 100644 --- a/homeassistant/components/wiz/sensor.py +++ b/homeassistant/components/wiz/sensor.py @@ -8,7 +8,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import POWER_WATT, SIGNAL_STRENGTH_DECIBELS_MILLIWATT +from homeassistant.const import SIGNAL_STRENGTH_DECIBELS_MILLIWATT, UnitOfPower from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -36,7 +36,7 @@ POWER_SENSORS: tuple[SensorEntityDescription, ...] = ( name="Current power", state_class=SensorStateClass.MEASUREMENT, device_class=SensorDeviceClass.POWER, - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, ), ) diff --git a/homeassistant/components/wiz/strings.json b/homeassistant/components/wiz/strings.json index 3640b17195c..64537aacaf2 100644 --- a/homeassistant/components/wiz/strings.json +++ b/homeassistant/components/wiz/strings.json @@ -9,7 +9,7 @@ "description": "If you leave the IP Address empty, discovery will be used to find devices." }, "discovery_confirm": { - "description": "Do you want to setup {name} ({host})?" + "description": "Do you want to set up {name} ({host})?" }, "pick_device": { "data": { diff --git a/homeassistant/components/wiz/translations/cs.json b/homeassistant/components/wiz/translations/cs.json index aa21c1d63ea..75975bb6a71 100644 --- a/homeassistant/components/wiz/translations/cs.json +++ b/homeassistant/components/wiz/translations/cs.json @@ -7,7 +7,7 @@ }, "error": { "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", - "no_ip": "Neplatn\u00e1 adresa IP.", + "no_ip": "Neplatn\u00e1 IP adresa.", "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" }, "step": { diff --git a/homeassistant/components/wiz/translations/en.json b/homeassistant/components/wiz/translations/en.json index 97bb7fc25ba..005f30ac9b8 100644 --- a/homeassistant/components/wiz/translations/en.json +++ b/homeassistant/components/wiz/translations/en.json @@ -15,7 +15,7 @@ "flow_title": "{name} ({host})", "step": { "discovery_confirm": { - "description": "Do you want to setup {name} ({host})?" + "description": "Do you want to set up {name} ({host})?" }, "pick_device": { "data": { diff --git a/homeassistant/components/wiz/translations/ko.json b/homeassistant/components/wiz/translations/ko.json index 9c08b2d78b0..80d9a074c84 100644 --- a/homeassistant/components/wiz/translations/ko.json +++ b/homeassistant/components/wiz/translations/ko.json @@ -1,7 +1,19 @@ { "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, "step": { "user": { + "data": { + "host": "IP \uc8fc\uc18c" + }, "description": "IP \uc8fc\uc18c\ub97c \ube44\uc6cc\ub450\uba74 \uc7a5\uce58\ub97c \uc790\ub3d9\uc73c\ub85c \ucc3e\uc2b5\ub2c8\ub2e4." } } diff --git a/homeassistant/components/wiz/translations/no.json b/homeassistant/components/wiz/translations/no.json index 031b184ae84..335b63217b2 100644 --- a/homeassistant/components/wiz/translations/no.json +++ b/homeassistant/components/wiz/translations/no.json @@ -15,7 +15,7 @@ "flow_title": "{name} ({host})", "step": { "discovery_confirm": { - "description": "Vil du konfigurere {name} ({host})?" + "description": "Vil du sette opp {name} ( {host} )?" }, "pick_device": { "data": { diff --git a/homeassistant/components/wiz/translations/pt.json b/homeassistant/components/wiz/translations/pt.json index b686fee56b5..d2c3a52eedb 100644 --- a/homeassistant/components/wiz/translations/pt.json +++ b/homeassistant/components/wiz/translations/pt.json @@ -1,10 +1,10 @@ { "config": { "abort": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "unknown": "Erro inesperado" } } diff --git a/homeassistant/components/wiz/translations/sk.json b/homeassistant/components/wiz/translations/sk.json index d5a802b858e..17ec2bf46eb 100644 --- a/homeassistant/components/wiz/translations/sk.json +++ b/homeassistant/components/wiz/translations/sk.json @@ -6,8 +6,10 @@ "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia" }, "error": { + "bulb_time_out": "Ned\u00e1 sa pripoji\u0165 k \u017eiarovke. Mo\u017eno je \u017eiarovka offline alebo bola zadan\u00e1 nespr\u00e1vna adresa IP. Zapnite svetlo a sk\u00faste to znova!", "cannot_connect": "Nepodarilo sa pripoji\u0165", "no_ip": "Neplatn\u00e1 adresa IP.", + "no_wiz_light": "\u017diarovka sa ned\u00e1 pripoji\u0165 cez integr\u00e1ciu platformy WiZ.", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "flow_title": "{name} ({host})", @@ -23,7 +25,8 @@ "user": { "data": { "host": "IP adresa" - } + }, + "description": "Ak nech\u00e1te IP adresu pr\u00e1zdnu, na n\u00e1jdenie zariaden\u00ed sa pou\u017eije vyh\u013ead\u00e1vanie." } } } diff --git a/homeassistant/components/wled/__init__.py b/homeassistant/components/wled/__init__.py index 809afdfb3c7..fe6ac5a1fc1 100644 --- a/homeassistant/components/wled/__init__.py +++ b/homeassistant/components/wled/__init__.py @@ -27,8 +27,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: if coordinator.data.info.leds.cct: LOGGER.error( - "WLED device '%s' has a CCT channel, which is not supported by " - "this integration", + ( + "WLED device '%s' has a CCT channel, which is not supported by " + "this integration" + ), entry.title, ) return False diff --git a/homeassistant/components/wled/const.py b/homeassistant/components/wled/const.py index e323b5ab87b..40f831772bc 100644 --- a/homeassistant/components/wled/const.py +++ b/homeassistant/components/wled/const.py @@ -1,7 +1,6 @@ """Constants for the WLED integration.""" from datetime import timedelta import logging -from typing import Final # Integration domain DOMAIN = "wled" @@ -24,6 +23,3 @@ ATTR_SOFTWARE_VERSION = "sw_version" ATTR_SPEED = "speed" ATTR_TARGET_BRIGHTNESS = "target_brightness" ATTR_UDP_PORT = "udp_port" - -# Device classes -DEVICE_CLASS_WLED_LIVE_OVERRIDE: Final = "wled__live_override" diff --git a/homeassistant/components/wled/manifest.json b/homeassistant/components/wled/manifest.json index 3566349a853..0e1e364b5ce 100644 --- a/homeassistant/components/wled/manifest.json +++ b/homeassistant/components/wled/manifest.json @@ -3,7 +3,7 @@ "name": "WLED", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/wled", - "requirements": ["wled==0.14.1"], + "requirements": ["wled==0.15.0"], "zeroconf": ["_wled._tcp.local."], "codeowners": ["@frenck"], "quality_scale": "platinum", diff --git a/homeassistant/components/wled/select.py b/homeassistant/components/wled/select.py index badde5515d4..8f6356e9e00 100644 --- a/homeassistant/components/wled/select.py +++ b/homeassistant/components/wled/select.py @@ -11,7 +11,7 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .const import DEVICE_CLASS_WLED_LIVE_OVERRIDE, DOMAIN +from .const import DOMAIN from .coordinator import WLEDDataUpdateCoordinator from .helpers import wled_exception_handler from .models import WLEDEntity @@ -48,10 +48,10 @@ async def async_setup_entry( class WLEDLiveOverrideSelect(WLEDEntity, SelectEntity): """Defined a WLED Live Override select.""" - _attr_device_class = DEVICE_CLASS_WLED_LIVE_OVERRIDE _attr_entity_category = EntityCategory.CONFIG _attr_icon = "mdi:theater" _attr_name = "Live override" + _attr_translation_key = "live_override" def __init__(self, coordinator: WLEDDataUpdateCoordinator) -> None: """Initialize WLED .""" diff --git a/homeassistant/components/wled/sensor.py b/homeassistant/components/wled/sensor.py index 1b5e81ec469..c478fd98d30 100644 --- a/homeassistant/components/wled/sensor.py +++ b/homeassistant/components/wled/sensor.py @@ -15,10 +15,10 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - DATA_BYTES, - ELECTRIC_CURRENT_MILLIAMPERE, PERCENTAGE, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, + UnitOfElectricCurrent, + UnitOfInformation, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory @@ -51,7 +51,7 @@ SENSORS: tuple[WLEDSensorEntityDescription, ...] = ( WLEDSensorEntityDescription( key="estimated_current", name="Estimated current", - native_unit_of_measurement=ELECTRIC_CURRENT_MILLIAMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.MILLIAMPERE, device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, @@ -67,7 +67,7 @@ SENSORS: tuple[WLEDSensorEntityDescription, ...] = ( WLEDSensorEntityDescription( key="info_leds_max_power", name="Max current", - native_unit_of_measurement=ELECTRIC_CURRENT_MILLIAMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.MILLIAMPERE, entity_category=EntityCategory.DIAGNOSTIC, device_class=SensorDeviceClass.CURRENT, value_fn=lambda device: device.info.leds.max_power, @@ -85,8 +85,9 @@ SENSORS: tuple[WLEDSensorEntityDescription, ...] = ( key="free_heap", name="Free memory", icon="mdi:memory", - native_unit_of_measurement=DATA_BYTES, + native_unit_of_measurement=UnitOfInformation.BYTES, state_class=SensorStateClass.MEASUREMENT, + device_class=SensorDeviceClass.DATA_SIZE, entity_category=EntityCategory.DIAGNOSTIC, entity_registry_enabled_default=False, value_fn=lambda device: device.info.free_heap, diff --git a/homeassistant/components/wled/strings.json b/homeassistant/components/wled/strings.json index ac1eb4046dd..eed62ab0499 100644 --- a/homeassistant/components/wled/strings.json +++ b/homeassistant/components/wled/strings.json @@ -30,5 +30,16 @@ } } } + }, + "entity": { + "select": { + "live_override": { + "state": { + "0": "[%key:common::state::off%]", + "1": "[%key:common::state::on%]", + "2": "Until device restarts" + } + } + } } } diff --git a/homeassistant/components/wled/strings.select.json b/homeassistant/components/wled/strings.select.json deleted file mode 100644 index 9f678e380b4..00000000000 --- a/homeassistant/components/wled/strings.select.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "state": { - "wled__live_override": { - "0": "[%key:common::state::off%]", - "1": "[%key:common::state::on%]", - "2": "Until device restarts" - } - } -} diff --git a/homeassistant/components/wled/translations/ca.json b/homeassistant/components/wled/translations/ca.json index 743bf324b94..246c82261d1 100644 --- a/homeassistant/components/wled/translations/ca.json +++ b/homeassistant/components/wled/translations/ca.json @@ -22,6 +22,17 @@ } } }, + "entity": { + "select": { + "live_override": { + "state": { + "0": "OFF", + "1": "ON", + "2": "Fins que el dispositiu es reinici\u00ef" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/wled/translations/de.json b/homeassistant/components/wled/translations/de.json index 76cc923f3d4..9aedadcf06c 100644 --- a/homeassistant/components/wled/translations/de.json +++ b/homeassistant/components/wled/translations/de.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "Ger\u00e4t ist bereits konfiguriert", "cannot_connect": "Verbindung fehlgeschlagen", - "cct_unsupported": "Dieses WLED-Ger\u00e4t verwendet CCT-Kan\u00e4le, die von dieser Integration nicht unterst\u00fctzt werden." + "cct_unsupported": "Dieses WLED-Ger\u00e4t verwendet CCT Kan\u00e4le, die von dieser Integration nicht unterst\u00fctzt werden." }, "error": { "cannot_connect": "Verbindung fehlgeschlagen" @@ -22,6 +22,17 @@ } } }, + "entity": { + "select": { + "live_override": { + "state": { + "0": "Aus", + "1": "An", + "2": "Bis zum Neustart des Ger\u00e4ts" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/wled/translations/el.json b/homeassistant/components/wled/translations/el.json index d6c3dbbc837..a8f78c1b94a 100644 --- a/homeassistant/components/wled/translations/el.json +++ b/homeassistant/components/wled/translations/el.json @@ -22,6 +22,17 @@ } } }, + "entity": { + "select": { + "live_override": { + "state": { + "0": "\u0391\u03bd\u03b5\u03bd\u03b5\u03c1\u03b3\u03cc", + "1": "\u0395\u03bd\u03b5\u03c1\u03b3\u03cc", + "2": "\u039c\u03ad\u03c7\u03c1\u03b9 \u03bd\u03b1 \u03b3\u03af\u03bd\u03b5\u03b9 \u03b5\u03c0\u03b1\u03bd\u03b5\u03ba\u03ba\u03af\u03bd\u03b7\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/wled/translations/en.json b/homeassistant/components/wled/translations/en.json index 4cc3e12bf2a..4ff8dbc566d 100644 --- a/homeassistant/components/wled/translations/en.json +++ b/homeassistant/components/wled/translations/en.json @@ -22,6 +22,17 @@ } } }, + "entity": { + "select": { + "live_override": { + "state": { + "0": "Off", + "1": "On", + "2": "Until device restarts" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/wled/translations/es.json b/homeassistant/components/wled/translations/es.json index 1b8d0eeeb00..4e42069d879 100644 --- a/homeassistant/components/wled/translations/es.json +++ b/homeassistant/components/wled/translations/es.json @@ -22,6 +22,17 @@ } } }, + "entity": { + "select": { + "live_override": { + "state": { + "0": "Apagado", + "1": "Encendido", + "2": "Hasta que el dispositivo se reinicie" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/wled/translations/et.json b/homeassistant/components/wled/translations/et.json index d45fdad2dde..3d8ace126e5 100644 --- a/homeassistant/components/wled/translations/et.json +++ b/homeassistant/components/wled/translations/et.json @@ -22,6 +22,17 @@ } } }, + "entity": { + "select": { + "live_override": { + "state": { + "0": "V\u00e4ljas", + "1": "Sees", + "2": "Kuni seade taask\u00e4ivitub" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/wled/translations/he.json b/homeassistant/components/wled/translations/he.json index 1cd249b4daa..7d442985101 100644 --- a/homeassistant/components/wled/translations/he.json +++ b/homeassistant/components/wled/translations/he.json @@ -15,5 +15,16 @@ } } } + }, + "entity": { + "select": { + "live_override": { + "state": { + "0": "\u05db\u05d1\u05d5\u05d9", + "1": "\u05de\u05d5\u05e4\u05e2\u05dc", + "2": "\u05e2\u05d3 \u05dc\u05d4\u05e4\u05e2\u05dc\u05d4 \u05de\u05d7\u05d3\u05e9 \u05e9\u05dc \u05d4\u05de\u05db\u05e9\u05d9\u05e8" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/wled/translations/hu.json b/homeassistant/components/wled/translations/hu.json index 88dc16a9a55..a4354e6b541 100644 --- a/homeassistant/components/wled/translations/hu.json +++ b/homeassistant/components/wled/translations/hu.json @@ -22,6 +22,17 @@ } } }, + "entity": { + "select": { + "live_override": { + "state": { + "0": "Ki", + "1": "Be", + "2": "Am\u00edg az eszk\u00f6z \u00fajraindul" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/wled/translations/id.json b/homeassistant/components/wled/translations/id.json index f52d88f5401..9cfd5bc6cb2 100644 --- a/homeassistant/components/wled/translations/id.json +++ b/homeassistant/components/wled/translations/id.json @@ -22,6 +22,17 @@ } } }, + "entity": { + "select": { + "live_override": { + "state": { + "0": "Mati", + "1": "Nyala", + "2": "Hingga perangkat dimulai ulang" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/wled/translations/it.json b/homeassistant/components/wled/translations/it.json index 2025bf2f336..90c457743d5 100644 --- a/homeassistant/components/wled/translations/it.json +++ b/homeassistant/components/wled/translations/it.json @@ -22,6 +22,17 @@ } } }, + "entity": { + "select": { + "live_override": { + "state": { + "0": "Spento", + "1": "Acceso", + "2": "Fino al riavvio del dispositivo" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/wled/translations/nl.json b/homeassistant/components/wled/translations/nl.json index f732d538489..10fe78b7626 100644 --- a/homeassistant/components/wled/translations/nl.json +++ b/homeassistant/components/wled/translations/nl.json @@ -22,6 +22,16 @@ } } }, + "entity": { + "select": { + "live_override": { + "state": { + "0": "Uit", + "1": "Aan" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/wled/translations/no.json b/homeassistant/components/wled/translations/no.json index f161d24b41e..755a7e398a1 100644 --- a/homeassistant/components/wled/translations/no.json +++ b/homeassistant/components/wled/translations/no.json @@ -22,6 +22,17 @@ } } }, + "entity": { + "select": { + "live_override": { + "state": { + "0": "Av", + "1": "P\u00e5", + "2": "Inntil enheten starter p\u00e5 nytt" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/wled/translations/pl.json b/homeassistant/components/wled/translations/pl.json index b24f44f2055..ed6cebc4a86 100644 --- a/homeassistant/components/wled/translations/pl.json +++ b/homeassistant/components/wled/translations/pl.json @@ -22,6 +22,17 @@ } } }, + "entity": { + "select": { + "live_override": { + "state": { + "0": "wy\u0142\u0105czone", + "1": "w\u0142\u0105czone", + "2": "do czasu ponownego uruchomienia urz\u0105dzenia" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/wled/translations/pt-BR.json b/homeassistant/components/wled/translations/pt-BR.json index c922f86d776..9d66ea525d6 100644 --- a/homeassistant/components/wled/translations/pt-BR.json +++ b/homeassistant/components/wled/translations/pt-BR.json @@ -22,6 +22,17 @@ } } }, + "entity": { + "select": { + "live_override": { + "state": { + "0": "Desligado", + "1": "Ligado", + "2": "At\u00e9 o dispositivo reiniciar" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/wled/translations/pt.json b/homeassistant/components/wled/translations/pt.json index cc9f0af829a..11dccc1e649 100644 --- a/homeassistant/components/wled/translations/pt.json +++ b/homeassistant/components/wled/translations/pt.json @@ -2,10 +2,10 @@ "config": { "abort": { "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "user": { diff --git a/homeassistant/components/wled/translations/ru.json b/homeassistant/components/wled/translations/ru.json index fd7a4bf6d23..96b37768f02 100644 --- a/homeassistant/components/wled/translations/ru.json +++ b/homeassistant/components/wled/translations/ru.json @@ -22,6 +22,17 @@ } } }, + "entity": { + "select": { + "live_override": { + "state": { + "0": "\u0412\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u043e", + "1": "\u0412\u043a\u043b\u044e\u0447\u0435\u043d\u043e", + "2": "\u0414\u043e \u043f\u0435\u0440\u0435\u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/wled/translations/select.ko.json b/homeassistant/components/wled/translations/select.ko.json new file mode 100644 index 00000000000..adee483f477 --- /dev/null +++ b/homeassistant/components/wled/translations/select.ko.json @@ -0,0 +1,8 @@ +{ + "state": { + "wled__live_override": { + "0": "\uaebc\uc9d0", + "1": "\ucf1c\uc9d0" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/wled/translations/sk.json b/homeassistant/components/wled/translations/sk.json index 449f0ae40e4..5ace5cffd7e 100644 --- a/homeassistant/components/wled/translations/sk.json +++ b/homeassistant/components/wled/translations/sk.json @@ -2,7 +2,8 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", - "cannot_connect": "Nepodarilo sa pripoji\u0165" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "cct_unsupported": "Toto zariadenie WLED pou\u017e\u00edva CCT kan\u00e1ly, ktor\u00e9 t\u00e1to integr\u00e1cia nepodporuje" }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165" @@ -14,6 +15,30 @@ "host": "Hostite\u013e" }, "description": "Nastavte si WLED na integr\u00e1ciu s Home Assistant." + }, + "zeroconf_confirm": { + "description": "Chcete prida\u0165 WLED s n\u00e1zvom `{name}` do Home Assistant?", + "title": "Objaven\u00e9 zariadenie WLED" + } + } + }, + "entity": { + "select": { + "live_override": { + "state": { + "0": "Neakt\u00edvny", + "1": "Akt\u00edvny", + "2": "K\u00fdm sa zariadenie nere\u0161tartuje" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "keep_master_light": "Udr\u017eujte hlavn\u00e9 svetlo aj s 1 LED segmentom." + } } } } diff --git a/homeassistant/components/wled/translations/zh-Hant.json b/homeassistant/components/wled/translations/zh-Hant.json index 6125c3a2cb5..f50d476e01f 100644 --- a/homeassistant/components/wled/translations/zh-Hant.json +++ b/homeassistant/components/wled/translations/zh-Hant.json @@ -22,6 +22,17 @@ } } }, + "entity": { + "select": { + "live_override": { + "state": { + "0": "\u95dc\u9589", + "1": "\u958b\u555f", + "2": "\u76f4\u5230\u88dd\u7f6e\u91cd\u555f" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/wolflink/sensor.py b/homeassistant/components/wolflink/sensor.py index a39b03fbd9f..3da12b65d5f 100644 --- a/homeassistant/components/wolflink/sensor.py +++ b/homeassistant/components/wolflink/sensor.py @@ -13,7 +13,7 @@ from wolf_smartset.models import ( from homeassistant.components.sensor import SensorDeviceClass, SensorEntity from homeassistant.config_entries import ConfigEntry -from homeassistant.const import PRESSURE_BAR, TEMP_CELSIUS, TIME_HOURS +from homeassistant.const import UnitOfPressure, UnitOfTemperature, UnitOfTime from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import CoordinatorEntity @@ -100,7 +100,7 @@ class WolfLinkHours(WolfLinkSensor): @property def native_unit_of_measurement(self): """Return the unit the value is expressed in.""" - return TIME_HOURS + return UnitOfTime.HOURS class WolfLinkTemperature(WolfLinkSensor): @@ -114,7 +114,7 @@ class WolfLinkTemperature(WolfLinkSensor): @property def native_unit_of_measurement(self): """Return the unit the value is expressed in.""" - return TEMP_CELSIUS + return UnitOfTemperature.CELSIUS class WolfLinkPressure(WolfLinkSensor): @@ -128,7 +128,7 @@ class WolfLinkPressure(WolfLinkSensor): @property def native_unit_of_measurement(self): """Return the unit the value is expressed in.""" - return PRESSURE_BAR + return UnitOfPressure.BAR class WolfLinkPercentage(WolfLinkSensor): @@ -143,10 +143,7 @@ class WolfLinkPercentage(WolfLinkSensor): class WolfLinkState(WolfLinkSensor): """Class for entities which has defined list of state.""" - @property - def device_class(self): - """Return the device class.""" - return "wolflink__state" + _attr_translation_key = "state" @property def native_value(self): diff --git a/homeassistant/components/wolflink/strings.json b/homeassistant/components/wolflink/strings.json index 4a98f93318f..c8db962215f 100644 --- a/homeassistant/components/wolflink/strings.json +++ b/homeassistant/components/wolflink/strings.json @@ -23,5 +23,94 @@ "title": "Select WOLF device" } } + }, + "entity": { + "sensor": { + "state": { + "state": { + "ein": "Enabled", + "deaktiviert": "Inactive", + "aus": "Disabled", + "standby": "Standby", + "auto": "Auto", + "permanent": "Permanent", + "initialisierung": "Initialization", + "antilegionellenfunktion": "Anti-legionella Function", + "fernschalter_ein": "Remote control enabled", + "1_x_warmwasser": "1 x DHW", + "bereit_keine_ladung": "Ready, not loading", + "solarbetrieb": "Solar mode", + "reduzierter_betrieb": "Limited mode", + "smart_home": "SmartHome", + "smart_grid": "SmartGrid", + "ruhekontakt": "Rest contact", + "vorspulen": "Entry rinsing", + "zunden": "Ignition", + "stabilisierung": "Stabilization", + "ventilprufung": "Valve test", + "nachspulen": "Post-flush", + "softstart": "Soft start", + "taktsperre": "Anti-cycle", + "betrieb_ohne_brenner": "Working without burner", + "abgasklappe": "Flue gas damper", + "storung": "Fault", + "gradienten_uberwachung": "Gradient monitoring", + "gasdruck": "Gas pressure", + "spreizung_hoch": "dT too wide", + "spreizung_kf": "Spread KF", + "test": "Test", + "start": "Start", + "frost_heizkreis": "Heating circuit frost", + "frost_warmwasser": "DHW frost", + "schornsteinfeger": "Emissions test", + "kombibetrieb": "Combi mode", + "parallelbetrieb": "Parallel mode", + "warmwasserbetrieb": "DHW mode", + "warmwassernachlauf": "DHW run-on", + "heizbetrieb": "Heating mode", + "nachlauf_heizkreispumpe": "Heating circuit pump run-on", + "frostschutz": "Frost protection", + "kaskadenbetrieb": "Cascade operation", + "glt_betrieb": "BMS mode", + "kalibration": "Calibration", + "kalibration_heizbetrieb": "Heating mode calibration", + "kalibration_warmwasserbetrieb": "DHW calibration", + "kalibration_kombibetrieb": "Combi mode calibration", + "warmwasser_schnellstart": "DHW quick start", + "externe_deaktivierung": "External deactivation", + "heizung": "Heating", + "warmwasser": "DHW", + "kombigerat": "Combi boiler", + "kombigerat_mit_solareinbindung": "Combi boiler with solar integration", + "heizgerat_mit_speicher": "Boiler with cylinder", + "nur_heizgerat": "Boiler only", + "aktiviert": "Activated", + "sparen": "Economy", + "estrichtrocknung": "Screed drying", + "telefonfernschalter": "Telephone remote switch", + "partymodus": "Party mode", + "urlaubsmodus": "Holiday mode", + "automatik_ein": "Automatic ON", + "automatik_aus": "Automatic OFF", + "permanentbetrieb": "Permanent mode", + "sparbetrieb": "Economy mode", + "auto_on_cool": "AutoOnCool", + "auto_off_cool": "AutoOffCool", + "perm_cooling": "PermCooling", + "absenkbetrieb": "Setback mode", + "eco": "Eco", + "absenkstop": "Setback stop", + "at_abschaltung": "OT shutdown", + "rt_abschaltung": "RT shutdown", + "at_frostschutz": "OT frost protection", + "rt_frostschutz": "RT frost protection", + "dhw_prior": "DHWPrior", + "cooling": "Cooling", + "tpw": "TPW", + "warmwasservorrang": "DHW priority", + "mindest_kombizeit": "Minimum combi time" + } + } + } } } diff --git a/homeassistant/components/wolflink/strings.sensor.json b/homeassistant/components/wolflink/strings.sensor.json deleted file mode 100644 index c197edb5ea8..00000000000 --- a/homeassistant/components/wolflink/strings.sensor.json +++ /dev/null @@ -1,87 +0,0 @@ -{ - "state": { - "wolflink__state": { - "ein": "Enabled", - "deaktiviert": "Inactive", - "aus": "Disabled", - "standby": "Standby", - "auto": "Auto", - "permanent": "Permanent", - "initialisierung": "Initialization", - "antilegionellenfunktion": "Anti-legionella Function", - "fernschalter_ein": "Remote control enabled", - "1_x_warmwasser": "1 x DHW", - "bereit_keine_ladung": "Ready, not loading", - "solarbetrieb": "Solar mode", - "reduzierter_betrieb": "Limited mode", - "smart_home": "SmartHome", - "smart_grid": "SmartGrid", - "ruhekontakt": "Rest contact", - "vorspulen": "Entry rinsing", - "zunden": "Ignition", - "stabilisierung": "Stabilization", - "ventilprufung": "Valve test", - "nachspulen": "Post-flush", - "softstart": "Soft start", - "taktsperre": "Anti-cycle", - "betrieb_ohne_brenner": "Working without burner", - "abgasklappe": "Flue gas damper", - "storung": "Fault", - "gradienten_uberwachung": "Gradient monitoring", - "gasdruck": "Gas pressure", - "spreizung_hoch": "dT too wide", - "spreizung_kf": "Spread KF", - "test": "Test", - "start": "Start", - "frost_heizkreis": "Heating circuit frost", - "frost_warmwasser": "DHW frost", - "schornsteinfeger": "Emissions test", - "kombibetrieb": "Combi mode", - "parallelbetrieb": "Parallel mode", - "warmwasserbetrieb": "DHW mode", - "warmwassernachlauf": "DHW run-on", - "heizbetrieb": "Heating mode", - "nachlauf_heizkreispumpe": "Heating circuit pump run-on", - "frostschutz": "Frost protection", - "kaskadenbetrieb": "Cascade operation", - "glt_betrieb": "BMS mode", - "kalibration": "Calibration", - "kalibration_heizbetrieb": "Heating mode calibration", - "kalibration_warmwasserbetrieb": "DHW calibration", - "kalibration_kombibetrieb": "Combi mode calibration", - "warmwasser_schnellstart": "DHW quick start", - "externe_deaktivierung": "External deactivation", - "heizung": "Heating", - "warmwasser": "DHW", - "kombigerat": "Combi boiler", - "kombigerat_mit_solareinbindung": "Combi boiler with solar integration", - "heizgerat_mit_speicher": "Boiler with cylinder", - "nur_heizgerat": "Boiler only", - "aktiviert": "Activated", - "sparen": "Economy", - "estrichtrocknung": "Screed drying", - "telefonfernschalter": "Telephone remote switch", - "partymodus": "Party mode", - "urlaubsmodus": "Holiday mode", - "automatik_ein": "Automatic ON", - "automatik_aus": "Automatic OFF", - "permanentbetrieb": "Permanent mode", - "sparbetrieb": "Economy mode", - "auto_on_cool": "AutoOnCool", - "auto_off_cool": "AutoOffCool", - "perm_cooling": "PermCooling", - "absenkbetrieb": "Setback mode", - "eco": "Eco", - "absenkstop": "Setback stop", - "at_abschaltung": "OT shutdown", - "rt_abschaltung": "RT shutdown", - "at_frostschutz": "OT frost protection", - "rt_frostschutz": "RT frost protection", - "dhw_prior": "DHWPrior", - "cooling": "Cooling", - "tpw": "TPW", - "warmwasservorrang": "DHW priority", - "mindest_kombizeit": "Minimum combi time" - } - } -} diff --git a/homeassistant/components/wolflink/translations/bg.json b/homeassistant/components/wolflink/translations/bg.json index 3caea097ed7..84521f7891f 100644 --- a/homeassistant/components/wolflink/translations/bg.json +++ b/homeassistant/components/wolflink/translations/bg.json @@ -21,5 +21,20 @@ } } } + }, + "entity": { + "sensor": { + "state": { + "state": { + "1_x_warmwasser": "1 x DHW", + "cooling": "\u041e\u0445\u043b\u0430\u0436\u0434\u0430\u043d\u0435", + "initialisierung": "\u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0430\u043d\u0435", + "kalibration": "\u041a\u0430\u043b\u0438\u0431\u0440\u0438\u0440\u0430\u043d\u0435", + "test": "\u0422\u0435\u0441\u0442", + "tpw": "TPW", + "warmwasser": "DHW" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/wolflink/translations/ca.json b/homeassistant/components/wolflink/translations/ca.json index 80f8a793309..ad43f0489f1 100644 --- a/homeassistant/components/wolflink/translations/ca.json +++ b/homeassistant/components/wolflink/translations/ca.json @@ -23,5 +23,94 @@ "title": "Connexi\u00f3 WOLF SmartSet" } } + }, + "entity": { + "sensor": { + "state": { + "state": { + "1_x_warmwasser": "1 x DHW", + "abgasklappe": "Amortidor de gasos", + "absenkbetrieb": "Mode de retroc\u00e9s", + "absenkstop": "Retroc\u00e9s aturat", + "aktiviert": "Activat", + "antilegionellenfunktion": "Funci\u00f3 anti-legionel\u00b7la", + "at_abschaltung": "Aturada OT", + "at_frostschutz": "Protecci\u00f3 contra gelades OT", + "aus": "Desactivat", + "auto": "Autom\u00e0tic", + "auto_off_cool": "AutoOffCool", + "auto_on_cool": "AutoOnCool", + "automatik_aus": "Apagada autom\u00e0tica", + "automatik_ein": "Engegada autom\u00e0tica", + "bereit_keine_ladung": "Llest, no carregant-se", + "betrieb_ohne_brenner": "Funcionant sense cremador", + "cooling": "Refredant", + "deaktiviert": "Inactiu", + "dhw_prior": "DHWPrior", + "eco": "Eco", + "ein": "Activat", + "estrichtrocknung": "Assecant superf\u00edcie", + "externe_deaktivierung": "Desactivaci\u00f3 externa", + "fernschalter_ein": "Control remot activat", + "frost_heizkreis": "Congelaci\u00f3 del circuit escalfador", + "frost_warmwasser": "Congelaci\u00f3 DHW", + "frostschutz": "Protecci\u00f3 contra gelades", + "gasdruck": "Pressi\u00f3 del gas", + "glt_betrieb": "Mode BMS", + "gradienten_uberwachung": "Monitoritzaci\u00f3 de gradient", + "heizbetrieb": "Mode calefacci\u00f3", + "heizgerat_mit_speicher": "Caldera amb cilindre", + "heizung": "Escalfant", + "initialisierung": "Inicialitzaci\u00f3", + "kalibration": "Calibraci\u00f3", + "kalibration_heizbetrieb": "Calibraci\u00f3 de mode calefacci\u00f3", + "kalibration_kombibetrieb": "Calibraci\u00f3 de mode combi", + "kalibration_warmwasserbetrieb": "Calibraci\u00f3 DHW", + "kaskadenbetrieb": "Funcionament en cascada", + "kombibetrieb": "Mode combi", + "kombigerat": "Caldera combi", + "kombigerat_mit_solareinbindung": "Caldera combi amb integraci\u00f3 solar", + "mindest_kombizeit": "Temps combi m\u00ednim", + "nachlauf_heizkreispumpe": "Bomba del circuit escalfador en marxa", + "nachspulen": "Post-desc\u00e0rrega", + "nur_heizgerat": "Nom\u00e9s caldera", + "parallelbetrieb": "Mode paral\u00b7lel", + "partymodus": "Mode festa", + "perm_cooling": "PermCooling", + "permanent": "Permanent", + "permanentbetrieb": "Mode permanent", + "reduzierter_betrieb": "Mode limitat", + "rt_abschaltung": "Aturada RT", + "rt_frostschutz": "Protecci\u00f3 contra gelades RT", + "ruhekontakt": "Contacte de rep\u00f2s", + "schornsteinfeger": "Prova d'emissions", + "smart_grid": "SmartGrid", + "smart_home": "SmartHome", + "softstart": "Inici suau", + "solarbetrieb": "Mode solar", + "sparbetrieb": "Mode econ\u00f2mic", + "sparen": "Economia", + "spreizung_hoch": "dT massa ample", + "spreizung_kf": "'Spread' KF", + "stabilisierung": "Estabilitzaci\u00f3", + "standby": "En espera", + "start": "Inici", + "storung": "Error", + "taktsperre": "Anti-cicle", + "telefonfernschalter": "Interruptor remot de tel\u00e8fon", + "test": "Prova", + "tpw": "TPW", + "urlaubsmodus": "Mode vacances", + "ventilprufung": "Test de v\u00e0lvula", + "vorspulen": "Esbandint entrada", + "warmwasser": "DHW", + "warmwasser_schnellstart": "Inici r\u00e0pid del DHW", + "warmwasserbetrieb": "Mode del DHW", + "warmwassernachlauf": "DHW en marxa", + "warmwasservorrang": "Prioritat del DHW", + "zunden": "Ignici\u00f3" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/wolflink/translations/de.json b/homeassistant/components/wolflink/translations/de.json index aba055e8646..bb94488e8b9 100644 --- a/homeassistant/components/wolflink/translations/de.json +++ b/homeassistant/components/wolflink/translations/de.json @@ -20,7 +20,96 @@ "password": "Passwort", "username": "Benutzername" }, - "title": "WOLF SmartSet-Verbindung" + "title": "WOLF SmartSet Verbindung" + } + } + }, + "entity": { + "sensor": { + "state": { + "state": { + "1_x_warmwasser": "1 x Warmwasser", + "abgasklappe": "Abgasklappe", + "absenkbetrieb": "Absenkbetrieb", + "absenkstop": "Absenkstopp", + "aktiviert": "Aktiviert", + "antilegionellenfunktion": "Anti-Legionellen-Funktion", + "at_abschaltung": "AT Abschaltung", + "at_frostschutz": "AT Frostschutz", + "aus": "Deaktiviert", + "auto": "Automatisch", + "auto_off_cool": "AutoOffCool", + "auto_on_cool": "AutoOnCool", + "automatik_aus": "Automatik AUS", + "automatik_ein": "Automatik EIN", + "bereit_keine_ladung": "Bereit, keine Ladung", + "betrieb_ohne_brenner": "Betrieb ohne Brenner", + "cooling": "K\u00fchlen", + "deaktiviert": "Deaktiviert", + "dhw_prior": "DHWPrior", + "eco": "Eco", + "ein": "Ein", + "estrichtrocknung": "Estrichtrocknung", + "externe_deaktivierung": "Externe Deaktivierung", + "fernschalter_ein": "Fernsteuerung aktiviert", + "frost_heizkreis": "Frost Heizkreis", + "frost_warmwasser": "Warmwasser Frost", + "frostschutz": "Frostschutz", + "gasdruck": "Gasdruck", + "glt_betrieb": "BMS-Modus", + "gradienten_uberwachung": "Gradienten\u00fcberwachung", + "heizbetrieb": "Heizbetrieb", + "heizgerat_mit_speicher": "Heizger\u00e4t mit Speicher", + "heizung": "Heizen", + "initialisierung": "Initialisierung", + "kalibration": "Kalibrierung", + "kalibration_heizbetrieb": "Kalibrierung Heizbetrieb", + "kalibration_kombibetrieb": "Kalibrierung Kombibetrieb", + "kalibration_warmwasserbetrieb": "Kalibrierung Warmwasserbetrieb", + "kaskadenbetrieb": "Kaskadenbetrieb", + "kombibetrieb": "Kombibetrieb", + "kombigerat": "Kombiger\u00e4t", + "kombigerat_mit_solareinbindung": "Kombiger\u00e4t mit Solareinbindung", + "mindest_kombizeit": "Minimale Kombizeit", + "nachlauf_heizkreispumpe": "Nachlauf Heizkreispumpe", + "nachspulen": "Nachsp\u00fclung", + "nur_heizgerat": "Nur Heizger\u00e4t", + "parallelbetrieb": "Parallelbetrieb", + "partymodus": "Party-Modus", + "perm_cooling": "PermCooling", + "permanent": "Permanent", + "permanentbetrieb": "Permanentbetrieb", + "reduzierter_betrieb": "Reduzierter Betrieb", + "rt_abschaltung": "RT Abschaltung", + "rt_frostschutz": "RT Frostschutz", + "ruhekontakt": "Ruhekontakt", + "schornsteinfeger": "Emissionspr\u00fcfung", + "smart_grid": "SmartGrid", + "smart_home": "SmartHome", + "softstart": "Soft Start", + "solarbetrieb": "Solarmodus", + "sparbetrieb": "Sparmodus", + "sparen": "Sparen", + "spreizung_hoch": "Spreizung zu hoch", + "spreizung_kf": "Spreizung KF", + "stabilisierung": "Stabilisierung", + "standby": "Bereitschaft", + "start": "Start", + "storung": "Fehler", + "taktsperre": "Taktsperre", + "telefonfernschalter": "Telefonfernschalter", + "test": "Test", + "tpw": "TPW", + "urlaubsmodus": "Urlaubsmodus", + "ventilprufung": "Ventilpr\u00fcfung", + "vorspulen": "Vorsp\u00fclen", + "warmwasser": "Warmwasser", + "warmwasser_schnellstart": "Warmwasser Schnellstart", + "warmwasserbetrieb": "Warmwasserbetrieb", + "warmwassernachlauf": "Warmwassernachlauf", + "warmwasservorrang": "Warmwasserpriorit\u00e4t", + "zunden": "Z\u00fcnden" + } } } } diff --git a/homeassistant/components/wolflink/translations/el.json b/homeassistant/components/wolflink/translations/el.json index cd3c573a4dd..ab8a489b2a1 100644 --- a/homeassistant/components/wolflink/translations/el.json +++ b/homeassistant/components/wolflink/translations/el.json @@ -23,5 +23,94 @@ "title": "\u03a3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 WOLF SmartSet" } } + }, + "entity": { + "sensor": { + "state": { + "state": { + "1_x_warmwasser": "1 x DHW", + "abgasklappe": "\u0391\u03c0\u03bf\u03c3\u03b2\u03b5\u03c3\u03c4\u03ae\u03c1\u03b1\u03c2 \u03ba\u03b1\u03c5\u03c3\u03b1\u03b5\u03c1\u03af\u03c9\u03bd", + "absenkbetrieb": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03bf\u03c0\u03b9\u03c3\u03b8\u03bf\u03b4\u03c1\u03cc\u03bc\u03b7\u03c3\u03b7\u03c2", + "absenkstop": "\u0394\u03b9\u03b1\u03ba\u03bf\u03c0\u03ae \u03bf\u03c0\u03b9\u03c3\u03b8\u03bf\u03b4\u03c1\u03cc\u03bc\u03b7\u03c3\u03b7\u03c2", + "aktiviert": "\u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03bf", + "antilegionellenfunktion": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03c2 \u03bb\u03b5\u03b3\u03b9\u03bf\u03bd\u03ad\u03bb\u03bb\u03b1\u03c2", + "at_abschaltung": "\u0394\u03b9\u03b1\u03ba\u03bf\u03c0\u03ae \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1\u03c2 OT", + "at_frostschutz": "\u03a0\u03c1\u03bf\u03c3\u03c4\u03b1\u03c3\u03af\u03b1 \u03b1\u03c0\u03cc \u03c0\u03b1\u03b3\u03b5\u03c4\u03cc OT", + "aus": "\u0391\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03bf", + "auto": "\u0391\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03bf", + "auto_off_cool": "AutoOffCool", + "auto_on_cool": "AutoOnCool", + "automatik_aus": "\u0391\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03bf OFF", + "automatik_ein": "\u0391\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03bf ON", + "bereit_keine_ladung": "\u0388\u03c4\u03bf\u03b9\u03bc\u03bf, \u03b4\u03b5\u03bd \u03c6\u03bf\u03c1\u03c4\u03ce\u03bd\u03b5\u03b9", + "betrieb_ohne_brenner": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03c7\u03c9\u03c1\u03af\u03c2 \u03ba\u03b1\u03c5\u03c3\u03c4\u03ae\u03c1\u03b1", + "cooling": "\u03a8\u03cd\u03be\u03b7", + "deaktiviert": "\u0391\u03b4\u03c1\u03b1\u03bd\u03ad\u03c2", + "dhw_prior": "DHWPrior", + "eco": "Eco", + "ein": "\u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03bf", + "estrichtrocknung": "\u03a3\u03c4\u03ad\u03b3\u03bd\u03c9\u03bc\u03b1 \u03b5\u03c0\u03af\u03c3\u03c4\u03c1\u03c9\u03c3\u03b7\u03c2", + "externe_deaktivierung": "\u0395\u03be\u03c9\u03c4\u03b5\u03c1\u03b9\u03ba\u03ae \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7", + "fernschalter_ein": "\u0391\u03c0\u03bf\u03bc\u03b1\u03ba\u03c1\u03c5\u03c3\u03bc\u03ad\u03bd\u03b7 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u0395\u03bd\u03b5\u03c1\u03b3\u03ae", + "frost_heizkreis": "\u03a0\u03b1\u03b3\u03b5\u03c4\u03cc\u03c2 \u03c3\u03c4\u03bf \u03ba\u03cd\u03ba\u03bb\u03c9\u03bc\u03b1 \u03b8\u03ad\u03c1\u03bc\u03b1\u03bd\u03c3\u03b7\u03c2", + "frost_warmwasser": "\u03a0\u03b1\u03b3\u03b5\u03c4\u03cc\u03c2 DHW", + "frostschutz": "\u03a0\u03c1\u03bf\u03c3\u03c4\u03b1\u03c3\u03af\u03b1 \u03b1\u03c0\u03cc \u03c0\u03b1\u03b3\u03b5\u03c4\u03cc", + "gasdruck": "\u03a0\u03af\u03b5\u03c3\u03b7 \u03b1\u03b5\u03c1\u03af\u03bf\u03c5", + "glt_betrieb": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 BMS", + "gradienten_uberwachung": "\u03a0\u03b1\u03c1\u03b1\u03ba\u03bf\u03bb\u03bf\u03cd\u03b8\u03b7\u03c3\u03b7 \u03ba\u03bb\u03af\u03c3\u03b7\u03c2", + "heizbetrieb": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03b8\u03ad\u03c1\u03bc\u03b1\u03bd\u03c3\u03b7\u03c2", + "heizgerat_mit_speicher": "\u039b\u03ad\u03b2\u03b7\u03c4\u03b1\u03c2 \u03bc\u03b5 \u03ba\u03cd\u03bb\u03b9\u03bd\u03b4\u03c1\u03bf", + "heizung": "\u0398\u03ad\u03c1\u03bc\u03b1\u03bd\u03c3\u03b7", + "initialisierung": "\u0391\u03c1\u03c7\u03b9\u03ba\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7", + "kalibration": "\u0392\u03b1\u03b8\u03bc\u03bf\u03bd\u03cc\u03bc\u03b7\u03c3\u03b7", + "kalibration_heizbetrieb": "\u0392\u03b1\u03b8\u03bc\u03bf\u03bd\u03cc\u03bc\u03b7\u03c3\u03b7 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1\u03c2 \u03b8\u03ad\u03c1\u03bc\u03b1\u03bd\u03c3\u03b7\u03c2", + "kalibration_kombibetrieb": "\u0392\u03b1\u03b8\u03bc\u03bf\u03bd\u03cc\u03bc\u03b7\u03c3\u03b7 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1\u03c2 Combi", + "kalibration_warmwasserbetrieb": "\u0392\u03b1\u03b8\u03bc\u03bf\u03bd\u03cc\u03bc\u03b7\u03c3\u03b7 DHW", + "kaskadenbetrieb": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03ba\u03b1\u03c4\u03b1\u03c1\u03c1\u03ac\u03ba\u03c4\u03b7", + "kombibetrieb": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 Combi", + "kombigerat": "\u039c\u03c0\u03cc\u03b9\u03bb\u03b5\u03c1 Combi", + "kombigerat_mit_solareinbindung": "\u039c\u03c0\u03cc\u03b9\u03bb\u03b5\u03c1 Combi \u03bc\u03b5 \u03b7\u03bb\u03b9\u03b1\u03ba\u03ae \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7", + "mindest_kombizeit": "\u0395\u03bb\u03ac\u03c7\u03b9\u03c3\u03c4\u03bf\u03c2 \u03c7\u03c1\u03cc\u03bd\u03bf\u03c2 \u03c3\u03c5\u03bd\u03b4\u03c5\u03b1\u03c3\u03bc\u03bf\u03cd", + "nachlauf_heizkreispumpe": "\u0391\u03bd\u03c4\u03bb\u03af\u03b1 \u03ba\u03c5\u03ba\u03bb\u03ce\u03bc\u03b1\u03c4\u03bf\u03c2 \u03b8\u03ad\u03c1\u03bc\u03b1\u03bd\u03c3\u03b7\u03c2 \u03c3\u03b5 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1", + "nachspulen": "\u039c\u03b5\u03c4\u03ac \u03c4\u03bf \u03be\u03ad\u03c0\u03bb\u03c5\u03bc\u03b1", + "nur_heizgerat": "\u039c\u03cc\u03bd\u03bf \u03bb\u03ad\u03b2\u03b7\u03c4\u03b1\u03c2", + "parallelbetrieb": "\u03a0\u03b1\u03c1\u03ac\u03bb\u03bb\u03b7\u03bb\u03b7 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1", + "partymodus": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03c0\u03ac\u03c1\u03c4\u03b9", + "perm_cooling": "PermCooling", + "permanent": "\u039c\u03cc\u03bd\u03b9\u03bc\u03bf", + "permanentbetrieb": "\u039c\u03cc\u03bd\u03b9\u03bc\u03b7 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1", + "reduzierter_betrieb": "\u03a0\u03b5\u03c1\u03b9\u03bf\u03c1\u03b9\u03c3\u03bc\u03ad\u03bd\u03b7 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1", + "rt_abschaltung": "\u03a4\u03b5\u03c1\u03bc\u03b1\u03c4\u03b9\u03c3\u03bc\u03cc\u03c2 RT", + "rt_frostschutz": "RT \u03c0\u03c1\u03bf\u03c3\u03c4\u03b1\u03c3\u03af\u03b1 \u03b1\u03c0\u03cc \u03c0\u03b1\u03b3\u03b5\u03c4\u03cc", + "ruhekontakt": "\u0395\u03c0\u03b1\u03c6\u03ae \u03b1\u03bd\u03ac\u03c0\u03b1\u03c5\u03c3\u03b7\u03c2", + "schornsteinfeger": "\u0394\u03bf\u03ba\u03b9\u03bc\u03ae \u03b5\u03ba\u03c0\u03bf\u03bc\u03c0\u03ce\u03bd", + "smart_grid": "SmartGrid", + "smart_home": "SmartHome", + "softstart": "\u0389\u03c0\u03b9\u03b1 \u03b5\u03ba\u03ba\u03af\u03bd\u03b7\u03c3\u03b7", + "solarbetrieb": "\u0397\u03bb\u03b9\u03b1\u03ba\u03ae \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1", + "sparbetrieb": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03bf\u03b9\u03ba\u03bf\u03bd\u03bf\u03bc\u03af\u03b1\u03c2", + "sparen": "\u039f\u03b9\u03ba\u03bf\u03bd\u03bf\u03bc\u03af\u03b1", + "spreizung_hoch": "dT \u03c0\u03bf\u03bb\u03cd \u03c6\u03b1\u03c1\u03b4\u03cd", + "spreizung_kf": "\u0394\u03b9\u03ac\u03b4\u03bf\u03c3\u03b7 KF", + "stabilisierung": "\u03a3\u03c4\u03b1\u03b8\u03b5\u03c1\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7", + "standby": "\u0391\u03bd\u03b1\u03bc\u03bf\u03bd\u03ae", + "start": "\u0388\u03bd\u03b1\u03c1\u03be\u03b7", + "storung": "\u0392\u03bb\u03ac\u03b2\u03b7", + "taktsperre": "\u0391\u03bd\u03c4\u03b9-\u03ba\u03cd\u03ba\u03bb\u03bf\u03c2", + "telefonfernschalter": "\u03a4\u03b7\u03bb\u03b5\u03c6\u03c9\u03bd\u03b9\u03ba\u03cc\u03c2 \u03b1\u03c0\u03bf\u03bc\u03b1\u03ba\u03c1\u03c5\u03c3\u03bc\u03ad\u03bd\u03bf\u03c2 \u03b4\u03b9\u03b1\u03ba\u03cc\u03c0\u03c4\u03b7\u03c2", + "test": "\u0394\u03bf\u03ba\u03b9\u03bc\u03ae", + "tpw": "TPW", + "urlaubsmodus": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03b4\u03b9\u03b1\u03ba\u03bf\u03c0\u03ce\u03bd", + "ventilprufung": "\u0394\u03bf\u03ba\u03b9\u03bc\u03ae \u03b2\u03b1\u03bb\u03b2\u03af\u03b4\u03b1\u03c2", + "vorspulen": "\u039e\u03ad\u03b2\u03b3\u03b1\u03bb\u03bc\u03b1 \u03b5\u03b9\u03c3\u03cc\u03b4\u03bf\u03c5", + "warmwasser": "DHW", + "warmwasser_schnellstart": "\u0393\u03c1\u03ae\u03b3\u03bf\u03c1\u03b7 \u03b5\u03ba\u03ba\u03af\u03bd\u03b7\u03c3\u03b7 DHW", + "warmwasserbetrieb": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 DHW", + "warmwassernachlauf": "\u0395\u03ba\u03c4\u03ad\u03bb\u03b5\u03c3\u03b7 DHW", + "warmwasservorrang": "\u03a0\u03c1\u03bf\u03c4\u03b5\u03c1\u03b1\u03b9\u03cc\u03c4\u03b7\u03c4\u03b1 DHW", + "zunden": "\u0391\u03bd\u03ac\u03c6\u03bb\u03b5\u03be\u03b7" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/wolflink/translations/en.json b/homeassistant/components/wolflink/translations/en.json index 18148bea38a..f3d69ff6ec3 100644 --- a/homeassistant/components/wolflink/translations/en.json +++ b/homeassistant/components/wolflink/translations/en.json @@ -23,5 +23,94 @@ "title": "WOLF SmartSet connection" } } + }, + "entity": { + "sensor": { + "state": { + "state": { + "1_x_warmwasser": "1 x DHW", + "abgasklappe": "Flue gas damper", + "absenkbetrieb": "Setback mode", + "absenkstop": "Setback stop", + "aktiviert": "Activated", + "antilegionellenfunktion": "Anti-legionella Function", + "at_abschaltung": "OT shutdown", + "at_frostschutz": "OT frost protection", + "aus": "Disabled", + "auto": "Auto", + "auto_off_cool": "AutoOffCool", + "auto_on_cool": "AutoOnCool", + "automatik_aus": "Automatic OFF", + "automatik_ein": "Automatic ON", + "bereit_keine_ladung": "Ready, not loading", + "betrieb_ohne_brenner": "Working without burner", + "cooling": "Cooling", + "deaktiviert": "Inactive", + "dhw_prior": "DHWPrior", + "eco": "Eco", + "ein": "Enabled", + "estrichtrocknung": "Screed drying", + "externe_deaktivierung": "External deactivation", + "fernschalter_ein": "Remote control enabled", + "frost_heizkreis": "Heating circuit frost", + "frost_warmwasser": "DHW frost", + "frostschutz": "Frost protection", + "gasdruck": "Gas pressure", + "glt_betrieb": "BMS mode", + "gradienten_uberwachung": "Gradient monitoring", + "heizbetrieb": "Heating mode", + "heizgerat_mit_speicher": "Boiler with cylinder", + "heizung": "Heating", + "initialisierung": "Initialization", + "kalibration": "Calibration", + "kalibration_heizbetrieb": "Heating mode calibration", + "kalibration_kombibetrieb": "Combi mode calibration", + "kalibration_warmwasserbetrieb": "DHW calibration", + "kaskadenbetrieb": "Cascade operation", + "kombibetrieb": "Combi mode", + "kombigerat": "Combi boiler", + "kombigerat_mit_solareinbindung": "Combi boiler with solar integration", + "mindest_kombizeit": "Minimum combi time", + "nachlauf_heizkreispumpe": "Heating circuit pump run-on", + "nachspulen": "Post-flush", + "nur_heizgerat": "Boiler only", + "parallelbetrieb": "Parallel mode", + "partymodus": "Party mode", + "perm_cooling": "PermCooling", + "permanent": "Permanent", + "permanentbetrieb": "Permanent mode", + "reduzierter_betrieb": "Limited mode", + "rt_abschaltung": "RT shutdown", + "rt_frostschutz": "RT frost protection", + "ruhekontakt": "Rest contact", + "schornsteinfeger": "Emissions test", + "smart_grid": "SmartGrid", + "smart_home": "SmartHome", + "softstart": "Soft start", + "solarbetrieb": "Solar mode", + "sparbetrieb": "Economy mode", + "sparen": "Economy", + "spreizung_hoch": "dT too wide", + "spreizung_kf": "Spread KF", + "stabilisierung": "Stabilization", + "standby": "Standby", + "start": "Start", + "storung": "Fault", + "taktsperre": "Anti-cycle", + "telefonfernschalter": "Telephone remote switch", + "test": "Test", + "tpw": "TPW", + "urlaubsmodus": "Holiday mode", + "ventilprufung": "Valve test", + "vorspulen": "Entry rinsing", + "warmwasser": "DHW", + "warmwasser_schnellstart": "DHW quick start", + "warmwasserbetrieb": "DHW mode", + "warmwassernachlauf": "DHW run-on", + "warmwasservorrang": "DHW priority", + "zunden": "Ignition" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/wolflink/translations/es.json b/homeassistant/components/wolflink/translations/es.json index a98fe86a983..43b9d3bac83 100644 --- a/homeassistant/components/wolflink/translations/es.json +++ b/homeassistant/components/wolflink/translations/es.json @@ -23,5 +23,94 @@ "title": "Conexi\u00f3n WOLF SmartSet" } } + }, + "entity": { + "sensor": { + "state": { + "state": { + "1_x_warmwasser": "1 x DHW", + "abgasklappe": "Compuerta de gases de combusti\u00f3n", + "absenkbetrieb": "Modo de recuperaci\u00f3n", + "absenkstop": "Parada de recuperaci\u00f3n", + "aktiviert": "Activado", + "antilegionellenfunktion": "Funci\u00f3n anti legionela", + "at_abschaltung": "Apagado OT", + "at_frostschutz": "Protecci\u00f3n contra heladas OT", + "aus": "Deshabilitado", + "auto": "Autom\u00e1tico", + "auto_off_cool": "AutoOffCool", + "auto_on_cool": "AutoOnCool", + "automatik_aus": "Apagado autom\u00e1tico", + "automatik_ein": "Encendido autom\u00e1tico", + "bereit_keine_ladung": "Listo, no est\u00e1 cargando", + "betrieb_ohne_brenner": "Trabajar sin quemador", + "cooling": "Refrigeraci\u00f3n", + "deaktiviert": "Inactivo", + "dhw_prior": "DHWPrior", + "eco": "Eco", + "ein": "Habilitado", + "estrichtrocknung": "Secado en regla", + "externe_deaktivierung": "Desactivaci\u00f3n externa", + "fernschalter_ein": "Control remoto habilitado", + "frost_heizkreis": "Escarcha del circuito de calefacci\u00f3n", + "frost_warmwasser": "Heladas de DHW", + "frostschutz": "Protecci\u00f3n contra las heladas", + "gasdruck": "Presi\u00f3n del gas", + "glt_betrieb": "Modo BMS", + "gradienten_uberwachung": "Supervisi\u00f3n de gradiente", + "heizbetrieb": "Modo de calefacci\u00f3n", + "heizgerat_mit_speicher": "Caldera con cilindro", + "heizung": "Calefacci\u00f3n", + "initialisierung": "Inicializaci\u00f3n", + "kalibration": "Calibraci\u00f3n", + "kalibration_heizbetrieb": "Calibraci\u00f3n del modo de calefacci\u00f3n", + "kalibration_kombibetrieb": "Calibraci\u00f3n en modo mixto", + "kalibration_warmwasserbetrieb": "Calibraci\u00f3n DHW", + "kaskadenbetrieb": "Operaci\u00f3n en cascada", + "kombibetrieb": "Modo mixto", + "kombigerat": "Caldera mixta", + "kombigerat_mit_solareinbindung": "Caldera mixta con integraci\u00f3n solar", + "mindest_kombizeit": "Tiempo combinado m\u00ednimo", + "nachlauf_heizkreispumpe": "Bomba de circuito de calefacci\u00f3n en ejecuci\u00f3n", + "nachspulen": "Post-lavado", + "nur_heizgerat": "S\u00f3lo la caldera", + "parallelbetrieb": "Modo paralelo", + "partymodus": "Modo fiesta", + "perm_cooling": "Enfriamiento permanente", + "permanent": "Modo permanente", + "permanentbetrieb": "Modo permanente", + "reduzierter_betrieb": "Modo limitado", + "rt_abschaltung": "RT apagado", + "rt_frostschutz": "RT protecci\u00f3n contra heladas", + "ruhekontakt": "Contacto de reposo", + "schornsteinfeger": "Prueba de emisiones", + "smart_grid": "SmartGrid", + "smart_home": "SmartHome", + "softstart": "Arranque suave", + "solarbetrieb": "Modo solar", + "sparbetrieb": "Modo econ\u00f3mico", + "sparen": "Econom\u00eda", + "spreizung_hoch": "dT demasiado ancho", + "spreizung_kf": "Difundir el KF", + "stabilisierung": "Estabilizaci\u00f3n", + "standby": "En espera", + "start": "Arranque", + "storung": "Fallo", + "taktsperre": "Anti-ciclo", + "telefonfernschalter": "Interruptor remoto del tel\u00e9fono", + "test": "Prueba", + "tpw": "TPW", + "urlaubsmodus": "Modo vacaciones", + "ventilprufung": "Prueba de la v\u00e1lvula", + "vorspulen": "Enjuague de entrada", + "warmwasser": "DHW", + "warmwasser_schnellstart": "Inicio r\u00e1pido de DHW", + "warmwasserbetrieb": "Modo DHW", + "warmwassernachlauf": "DHW en ejecuci\u00f3n", + "warmwasservorrang": "Prioridad de DHW", + "zunden": "Encendido" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/wolflink/translations/et.json b/homeassistant/components/wolflink/translations/et.json index 1c7a7a09b3b..f072d5b22c8 100644 --- a/homeassistant/components/wolflink/translations/et.json +++ b/homeassistant/components/wolflink/translations/et.json @@ -23,5 +23,94 @@ "title": "WOLF SmartSeti \u00fchendus" } } + }, + "entity": { + "sensor": { + "state": { + "state": { + "1_x_warmwasser": "1 x soe vesi", + "abgasklappe": "Suitsugaasi siiber", + "absenkbetrieb": "Tagasil\u00f6\u00f6gi re\u017eiim", + "absenkstop": "Tagasil\u00f6\u00f6gi peatamine", + "aktiviert": "Aktiveeritud", + "antilegionellenfunktion": "Legionella vastane funktsioon", + "at_abschaltung": "OT sulgumine", + "at_frostschutz": "OT k\u00fclmakaitse", + "aus": "Keelatud", + "auto": "Automaatne", + "auto_off_cool": "AutoOffCool", + "auto_on_cool": "AutoOnCool", + "automatik_aus": "Automaatne V\u00c4LJAS", + "automatik_ein": "Automaatne SEES", + "bereit_keine_ladung": "Valmis, ei laadita", + "betrieb_ohne_brenner": "T\u00f6\u00f6tamine ilma p\u00f5letita", + "cooling": "Jahutamine", + "deaktiviert": "Passiivne", + "dhw_prior": "DHWPrior", + "eco": "\u00d6ko", + "ein": "Lubatud", + "estrichtrocknung": "Tasanduskihi kuivatamine", + "externe_deaktivierung": "V\u00e4line deaktiveerimine", + "fernschalter_ein": "Kaugjuhtimine on lubatud", + "frost_heizkreis": "K\u00fcttekontuuri k\u00fclmakaitse", + "frost_warmwasser": "DHW k\u00fclmakaitse", + "frostschutz": "K\u00fclmumiskaitse", + "gasdruck": "Gaasi r\u00f5hk", + "glt_betrieb": "BMS-re\u017eiim", + "gradienten_uberwachung": "Muutuste j\u00e4lgimine", + "heizbetrieb": "K\u00fcttere\u017eiim", + "heizgerat_mit_speicher": "Salvestiga katel", + "heizung": "K\u00fcte", + "initialisierung": "L\u00e4htestamine", + "kalibration": "Kalibreerimine", + "kalibration_heizbetrieb": "K\u00fcttere\u017eiimi kalibreerimine", + "kalibration_kombibetrieb": "Kombire\u017eiimi kalibreerimine", + "kalibration_warmwasserbetrieb": "DHW kalibreerimine", + "kaskadenbetrieb": "J\u00e4rjestikkune toimimine", + "kombibetrieb": "Kombire\u017eiim", + "kombigerat": "Kombineeritud k\u00fcttega katel", + "kombigerat_mit_solareinbindung": "P\u00e4ikeseintegratsiooniga katel", + "mindest_kombizeit": "Minimaalne liitaeg", + "nachlauf_heizkreispumpe": "K\u00fcttekontuuri pumba j\u00e4relt\u00f6\u00f6", + "nachspulen": "J\u00e4relloputus", + "nur_heizgerat": "Ainult katel", + "parallelbetrieb": "Paralleelre\u017eiim", + "partymodus": "Peore\u017eiim", + "perm_cooling": "P\u00fcsijahutus", + "permanent": "Alaline", + "permanentbetrieb": "P\u00fcsire\u017eiim", + "reduzierter_betrieb": "Piiratud re\u017eiim", + "rt_abschaltung": "RT sulgumine", + "rt_frostschutz": "RT k\u00fclmakaitse", + "ruhekontakt": "Puhkekontakt", + "schornsteinfeger": "Saastetest", + "smart_grid": "SmartGrid", + "smart_home": "SmartHome", + "softstart": "Pehme k\u00e4ivitus", + "solarbetrieb": "P\u00e4ikesek\u00fctte re\u017eiim", + "sparbetrieb": "S\u00e4\u00e4sture\u017eiim", + "sparen": "S\u00e4\u00e4sture\u017eiim", + "spreizung_hoch": "temperatuurivahemik liiga suur", + "spreizung_kf": "KF-i hajutamine", + "stabilisierung": "Stabiliseerumine", + "standby": "Ootel", + "start": "K\u00e4ivitus", + "storung": "Viga", + "taktsperre": "Ts\u00fcklivastane", + "telefonfernschalter": "Telefoni kaugl\u00fclitus", + "test": "Kontroll", + "tpw": "TPW", + "urlaubsmodus": "Puhkusere\u017eiim", + "ventilprufung": "Ventiili test", + "vorspulen": "Eelloputus", + "warmwasser": "DHW", + "warmwasser_schnellstart": "DHW kiirk\u00e4ivitus", + "warmwasserbetrieb": "DHW re\u017eiim", + "warmwassernachlauf": "DHW run-on", + "warmwasservorrang": "DHW prioriteet", + "zunden": "S\u00fc\u00fcde" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/wolflink/translations/hu.json b/homeassistant/components/wolflink/translations/hu.json index 79d03d91034..cc3428e2592 100644 --- a/homeassistant/components/wolflink/translations/hu.json +++ b/homeassistant/components/wolflink/translations/hu.json @@ -23,5 +23,94 @@ "title": "WOLF SmartSet kapcsolat" } } + }, + "entity": { + "sensor": { + "state": { + "state": { + "1_x_warmwasser": "1 x HMV", + "abgasklappe": "F\u00fcstg\u00e1zcsillap\u00edt\u00f3", + "absenkbetrieb": "Visszaes\u00e9s m\u00f3d", + "absenkstop": "Visszaes\u00e9s meg\u00e1ll\u00edt\u00e1sa", + "aktiviert": "Aktiv\u00e1lva", + "antilegionellenfunktion": "Anti-legionella funkci\u00f3", + "at_abschaltung": "OT le\u00e1ll\u00edt\u00e1s", + "at_frostschutz": "OT fagyv\u00e9delem", + "aus": "Letiltva", + "auto": "Automatikus", + "auto_off_cool": "AutomataKiH\u0171t\u00e9s", + "auto_on_cool": "AutomataBeH\u0171t\u00e9s", + "automatik_aus": "Automatikus kikapcsol\u00e1s", + "automatik_ein": "Automatikus bekapcsol\u00e1s", + "bereit_keine_ladung": "K\u00e9sz, nincs bet\u00f6ltve", + "betrieb_ohne_brenner": "Munka \u00e9g\u0151 n\u00e9lk\u00fcl", + "cooling": "H\u0171t\u00e9s", + "deaktiviert": "Inakt\u00edv", + "dhw_prior": "HMV Priorit\u00e1s", + "eco": "Takar\u00e9kos", + "ein": "Enged\u00e9lyezve", + "estrichtrocknung": "Padl\u00f3sz\u00e1r\u00edt\u00e1si", + "externe_deaktivierung": "K\u00fcls\u0151 deaktiv\u00e1l\u00e1s", + "fernschalter_ein": "T\u00e1vvez\u00e9rl\u00e9s enged\u00e9lyezve", + "frost_heizkreis": "F\u0171t\u0151k\u00f6r fagy\u00e1s", + "frost_warmwasser": "HMV fagy\u00e1s", + "frostschutz": "Fagyv\u00e9delem", + "gasdruck": "G\u00e1znyom\u00e1s", + "glt_betrieb": "BMS m\u00f3d", + "gradienten_uberwachung": "\u00c1tmenet monitoroz\u00e1s", + "heizbetrieb": "F\u0171t\u00e9si m\u00f3d", + "heizgerat_mit_speicher": "Kaz\u00e1n hengerrel", + "heizung": "F\u0171t\u00e9s", + "initialisierung": "Inicializ\u00e1l\u00e1s", + "kalibration": "Kalibr\u00e1ci\u00f3", + "kalibration_heizbetrieb": "F\u0171t\u00e9si m\u00f3d kalibr\u00e1l\u00e1sa", + "kalibration_kombibetrieb": "Kombin\u00e1lt m\u00f3d kalibr\u00e1l\u00e1sa", + "kalibration_warmwasserbetrieb": "HMV kalibr\u00e1l\u00e1s", + "kaskadenbetrieb": "Kaszk\u00e1d m\u0171k\u00f6d\u00e9s", + "kombibetrieb": "Kombin\u00e1lt m\u00f3d", + "kombigerat": "Kombin\u00e1lt kaz\u00e1n", + "kombigerat_mit_solareinbindung": "Kombin\u00e1lt kaz\u00e1n napkollektor integr\u00e1ci\u00f3val", + "mindest_kombizeit": "Minim\u00e1lis kombin\u00e1lt id\u0151", + "nachlauf_heizkreispumpe": "F\u0171t\u0151k\u00f6r szivatty\u00fa bekapcsol\u00e1sa", + "nachspulen": "Ut\u00f3\u00f6bl\u00edt\u00e9s", + "nur_heizgerat": "Csak kaz\u00e1n", + "parallelbetrieb": "P\u00e1rhuzamos \u00fczemm\u00f3d", + "partymodus": "Party m\u00f3d", + "perm_cooling": "\u00c1lland\u00f3H\u0171t\u00e9s", + "permanent": "\u00c1lland\u00f3", + "permanentbetrieb": "\u00c1lland\u00f3 \u00fczemm\u00f3d", + "reduzierter_betrieb": "Korl\u00e1tozott m\u00f3d", + "rt_abschaltung": "RT le\u00e1ll\u00edt\u00e1s", + "rt_frostschutz": "RT fagyv\u00e9delem", + "ruhekontakt": "Pihen\u0151 kapcsolat", + "schornsteinfeger": "Emisszi\u00f3s vizsg\u00e1lat", + "smart_grid": "SmartGrid", + "smart_home": "OkosOtthon", + "softstart": "L\u00e1gy ind\u00edt\u00e1s", + "solarbetrieb": "Napenergia \u00fczemm\u00f3d", + "sparbetrieb": "Taker\u00e9kos m\u00f3d", + "sparen": "Takar\u00e9kos", + "spreizung_hoch": "dT t\u00fal sz\u00e9les", + "spreizung_kf": "Spread KF", + "stabilisierung": "Stabiliz\u00e1l\u00e1s", + "standby": "K\u00e9szenl\u00e9tben", + "start": "Indul\u00e1s", + "storung": "Hiba", + "taktsperre": "Anti-ciklus", + "telefonfernschalter": "Telefonos t\u00e1vkapcsol\u00f3", + "test": "Teszt", + "tpw": "TPW", + "urlaubsmodus": "Vak\u00e1ci\u00f3 \u00fczemm\u00f3d", + "ventilprufung": "Szelep teszt", + "vorspulen": "Bel\u00e9p\u0151 \u00f6bl\u00edt\u00e9s", + "warmwasser": "HMV", + "warmwasser_schnellstart": "HMV gyorsind\u00edt\u00e1s", + "warmwasserbetrieb": "HMV \u00fczemm\u00f3d", + "warmwassernachlauf": "HMV felfut\u00e1s", + "warmwasservorrang": "HMV priorit\u00e1s", + "zunden": "Gy\u00fajt\u00e1s" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/wolflink/translations/id.json b/homeassistant/components/wolflink/translations/id.json index 64d692efa7d..c76c36011c6 100644 --- a/homeassistant/components/wolflink/translations/id.json +++ b/homeassistant/components/wolflink/translations/id.json @@ -23,5 +23,94 @@ "title": "Koneksi WOLF SmartSet" } } + }, + "entity": { + "sensor": { + "state": { + "state": { + "1_x_warmwasser": "1 x Air Panas", + "abgasklappe": "Katup saluran buang gas", + "absenkbetrieb": "Mode rendah", + "absenkstop": "Mode rendah berhenti", + "aktiviert": "Diaktifkan", + "antilegionellenfunktion": "Fungsi Anti-legionella", + "at_abschaltung": "Mati suhu luar", + "at_frostschutz": "Perlindungan embun beku suhu luar", + "aus": "Dinonaktifkan", + "auto": "Otomatis", + "auto_off_cool": "Otomatis mati dingin", + "auto_on_cool": "Otomatis nyala dingin", + "automatik_aus": "Otomatis mati", + "automatik_ein": "Otomatis nyala", + "bereit_keine_ladung": "Siap, tidak memuat", + "betrieb_ohne_brenner": "Bekerja tanpa pembakar", + "cooling": "Mendinginkan", + "deaktiviert": "Tidak aktif", + "dhw_prior": "Air panas sebelumnya", + "eco": "Eco", + "ein": "Diaktifkan", + "estrichtrocknung": "Pengeringan lapisan lantai", + "externe_deaktivierung": "Penonaktifan eksternal", + "fernschalter_ein": "Kontrol jarak jauh diaktifkan", + "frost_heizkreis": "Perlindungan beku sirkuit pemanasan", + "frost_warmwasser": "Perlindungan beku air panas", + "frostschutz": "Perlindungan beku", + "gasdruck": "Tekanan gas", + "glt_betrieb": "Mode BMS", + "gradienten_uberwachung": "Pemantauan gradien", + "heizbetrieb": "Mode pemanasan", + "heizgerat_mit_speicher": "Ketel dengan silinder", + "heizung": "Memanaskan", + "initialisierung": "Inisialisasi", + "kalibration": "Kalibrasi", + "kalibration_heizbetrieb": "Kalibrasi mode pemanasan", + "kalibration_kombibetrieb": "Kalibrasi mode kombi", + "kalibration_warmwasserbetrieb": "Kalibrasi air panas domestik", + "kaskadenbetrieb": "Operasi bertingkat", + "kombibetrieb": "Mode kombi", + "kombigerat": "Ketel kombinasi", + "kombigerat_mit_solareinbindung": "Ketel kombinasi dengan integrasi tenaga surya", + "mindest_kombizeit": "Waktu kombi minimum", + "nachlauf_heizkreispumpe": "Pompa sisa dari sirkuit pemanasan", + "nachspulen": "Putar ulang", + "nur_heizgerat": "Hanya boiler", + "parallelbetrieb": "Mode paralel", + "partymodus": "Mode pesta", + "perm_cooling": "Pendinginan permanen", + "permanent": "Permanen", + "permanentbetrieb": "Mode permanen", + "reduzierter_betrieb": "Mode terbatas", + "rt_abschaltung": "Mati suhu kamar", + "rt_frostschutz": "Perlindungan embun beku suhu kamar", + "ruhekontakt": "Kontak saat istirahat", + "schornsteinfeger": "Uji emisi", + "smart_grid": "SmartGrid", + "smart_home": "SmartHome", + "softstart": "Mulai rendah", + "solarbetrieb": "Mode surya", + "sparbetrieb": "Mode ekonomis", + "sparen": "Ekonomis", + "spreizung_hoch": "Perbedaan suhu sensor terlalu tinggi", + "spreizung_kf": "Sebaran sensor suhu ketel", + "stabilisierung": "Stabilisasi", + "standby": "Siaga", + "start": "Mulai", + "storung": "Kesalahan", + "taktsperre": "Anti-siklus", + "telefonfernschalter": "Saklar jarak jauh per telepon", + "test": "Pengujian", + "tpw": "Pemantau titik embun", + "urlaubsmodus": "Mode liburan", + "ventilprufung": "Uji katup", + "vorspulen": "Putar awal", + "warmwasser": "Air Panas Domestik", + "warmwasser_schnellstart": "Mulai cepat Air Panas Domestik", + "warmwasserbetrieb": "Mode Air Panas Domestik", + "warmwassernachlauf": "Air Panas Domestik sisa", + "warmwasservorrang": "Prioritas Air Panas Domestik", + "zunden": "Pengapian" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/wolflink/translations/it.json b/homeassistant/components/wolflink/translations/it.json index 7d41f47cd2d..f83b6be6545 100644 --- a/homeassistant/components/wolflink/translations/it.json +++ b/homeassistant/components/wolflink/translations/it.json @@ -23,5 +23,94 @@ "title": "Connessione WOLF SmartSet" } } + }, + "entity": { + "sensor": { + "state": { + "state": { + "1_x_warmwasser": "1 x acqua calda sanitaria", + "abgasklappe": "Serranda gas di scarico", + "absenkbetrieb": "Modalit\u00e0 di arretramento", + "absenkstop": "Arresto di arretramento", + "aktiviert": "Attivato", + "antilegionellenfunktion": "Funzione anti-legionella", + "at_abschaltung": "Spegnimento OT", + "at_frostschutz": "Protezione antigelo OT", + "aus": "Disabilitato", + "auto": "Automatico", + "auto_off_cool": "Raffreddamento automatico disattivo", + "auto_on_cool": "Raffreddamento automatico attivo", + "automatik_aus": "Spegnimento automatico", + "automatik_ein": "Accensione automatica", + "bereit_keine_ladung": "Pronto, non in fase di caricamento", + "betrieb_ohne_brenner": "Funzionante senza bruciatore", + "cooling": "Raffreddamento", + "deaktiviert": "Inattivo", + "dhw_prior": "Acqua calda sanitario prioritaria", + "eco": "Eco", + "ein": "Abilitato", + "estrichtrocknung": "Asciugatura del massetto", + "externe_deaktivierung": "Disattivazione esterna", + "fernschalter_ein": "Controllo remoto abilitato", + "frost_heizkreis": "Gelo del circuito di riscaldamento", + "frost_warmwasser": "Gelo dell'acqua calda sanitaria", + "frostschutz": "Protezione antigelo", + "gasdruck": "Pressione del gas", + "glt_betrieb": "Modalit\u00e0 BMS", + "gradienten_uberwachung": "Monitoraggio del gradiente", + "heizbetrieb": "Modalit\u00e0 di riscaldamento", + "heizgerat_mit_speicher": "Caldaia con cilindro", + "heizung": "Riscaldamento", + "initialisierung": "Inizializzazione", + "kalibration": "Calibrazione", + "kalibration_heizbetrieb": "Calibrazione della modalit\u00e0 di riscaldamento", + "kalibration_kombibetrieb": "Calibrazione modalit\u00e0 combinata", + "kalibration_warmwasserbetrieb": "Calibrazione acqua calda sanitaria", + "kaskadenbetrieb": "Funzionamento in cascata", + "kombibetrieb": "Modalit\u00e0 combinata", + "kombigerat": "Caldaia combinata", + "kombigerat_mit_solareinbindung": "Caldaia combinata con integrazione solare", + "mindest_kombizeit": "Tempo combinato minimo", + "nachlauf_heizkreispumpe": "Pompa del circuito di riscaldamento in funzione", + "nachspulen": "Post-risciacquo", + "nur_heizgerat": "Solo caldaia", + "parallelbetrieb": "Modalit\u00e0 parallela", + "partymodus": "Modalit\u00e0 festa", + "perm_cooling": "Raffreddamento permanente", + "permanent": "Permanente", + "permanentbetrieb": "Modalit\u00e0 permanente", + "reduzierter_betrieb": "Modalit\u00e0 limitata", + "rt_abschaltung": "Arresto RT", + "rt_frostschutz": "Protezione antigelo RT", + "ruhekontakt": "Contatto di riposo", + "schornsteinfeger": "Test sulle emissioni", + "smart_grid": "Rete intelligente", + "smart_home": "Casa intelligente", + "softstart": "Avvio graduale", + "solarbetrieb": "Modalit\u00e0 solare", + "sparbetrieb": "Modalit\u00e0 economica", + "sparen": "Economia", + "spreizung_hoch": "dT troppo ampio", + "spreizung_kf": "Diffusione KF", + "stabilisierung": "Stabilizzazione", + "standby": "Pausa", + "start": "Inizio", + "storung": "Guasto", + "taktsperre": "Anti-ciclo", + "telefonfernschalter": "Interruttore telefonico a distanza", + "test": "Test", + "tpw": "TPW", + "urlaubsmodus": "Modalit\u00e0 vacanza", + "ventilprufung": "Test della valvola", + "vorspulen": "Risciacquo dell'ingresso", + "warmwasser": "ACQUA CALDA SANITARIA", + "warmwasser_schnellstart": "Avvio rapido dell'acqua calda sanitaria", + "warmwasserbetrieb": "Modalit\u00e0 acqua calda sanitaria", + "warmwassernachlauf": "Accensione dell'acqua calda sanitaria", + "warmwasservorrang": "Priorit\u00e0 acqua calda sanitaria", + "zunden": "Accensione" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/wolflink/translations/nl.json b/homeassistant/components/wolflink/translations/nl.json index 069ed928962..b2b792070f8 100644 --- a/homeassistant/components/wolflink/translations/nl.json +++ b/homeassistant/components/wolflink/translations/nl.json @@ -23,5 +23,17 @@ "title": "WOLF SmartSet-verbinding" } } + }, + "entity": { + "sensor": { + "state": { + "state": { + "aktiviert": "Geactiveerd", + "aus": "Uitgeschakeld", + "deaktiviert": "Inactief", + "ein": "Ingeschakeld" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/wolflink/translations/no.json b/homeassistant/components/wolflink/translations/no.json index 82cf873bd78..d0aa421d03c 100644 --- a/homeassistant/components/wolflink/translations/no.json +++ b/homeassistant/components/wolflink/translations/no.json @@ -23,5 +23,94 @@ "title": "WOLF SmartSet-tilkobling" } } + }, + "entity": { + "sensor": { + "state": { + "state": { + "1_x_warmwasser": "1 x DHW", + "abgasklappe": "Spjeld for r\u00f8ykgass", + "absenkbetrieb": "Tilbakeslagsmodus", + "absenkstop": "Tilbakeslagsstopp", + "aktiviert": "Aktivert", + "antilegionellenfunktion": "Anti-legionella funksjon", + "at_abschaltung": "OT-avstenging", + "at_frostschutz": "OT frostsikring", + "aus": "Uf\u00f8r", + "auto": "Auto", + "auto_off_cool": "AutoOffCool", + "auto_on_cool": "AutoOnCool", + "automatik_aus": "Automatisk AV", + "automatik_ein": "Automatisk P\u00c5", + "bereit_keine_ladung": "Klar, laster ikke", + "betrieb_ohne_brenner": "Arbeide uten brenner", + "cooling": "Kj\u00f8ling", + "deaktiviert": "Inaktiv", + "dhw_prior": "DHWPrior", + "eco": "\u00d8ko", + "ein": "Aktivert", + "estrichtrocknung": "Avrettingst\u00f8rking", + "externe_deaktivierung": "Ekstern deaktivering", + "fernschalter_ein": "Fjernkontroll aktivert", + "frost_heizkreis": "Varmekrets frost", + "frost_warmwasser": "DHW frost", + "frostschutz": "Frostbeskyttelse", + "gasdruck": "Gasstrykk", + "glt_betrieb": "BMS-modus", + "gradienten_uberwachung": "Gradientoverv\u00e5king", + "heizbetrieb": "Oppvarmingsmodus", + "heizgerat_mit_speicher": "Kjele med sylinder", + "heizung": "Oppvarming", + "initialisierung": "Initialisering", + "kalibration": "Kalibrering", + "kalibration_heizbetrieb": "Kalibrering av varmemodus", + "kalibration_kombibetrieb": "Kombimodus kalibrering", + "kalibration_warmwasserbetrieb": "DHW-kalibrering", + "kaskadenbetrieb": "Kaskade operasjon", + "kombibetrieb": "Kombimodus", + "kombigerat": "Kombikjele", + "kombigerat_mit_solareinbindung": "Kombikjele med solintegrasjon", + "mindest_kombizeit": "Minimum kombitid", + "nachlauf_heizkreispumpe": "P\u00e5kj\u00f8ring av varmekretspumpe", + "nachspulen": "Etterspyling", + "nur_heizgerat": "Kun kjele", + "parallelbetrieb": "Parallell modus", + "partymodus": "Festmodus", + "perm_cooling": "PermCooling", + "permanent": "Permanent", + "permanentbetrieb": "Permanent modus", + "reduzierter_betrieb": "Begrenset modus", + "rt_abschaltung": "RT-avstenging", + "rt_frostschutz": "RT frostbeskyttelse", + "ruhekontakt": "Hvilekontakt", + "schornsteinfeger": "Utslippstest", + "smart_grid": "SmartGrid", + "smart_home": "SmartHome", + "softstart": "Myk start", + "solarbetrieb": "Solar modus", + "sparbetrieb": "\u00d8konomimodus", + "sparen": "\u00d8konomi", + "spreizung_hoch": "dT for bred", + "spreizung_kf": "Spre KF", + "stabilisierung": "Stabilisering", + "standby": "Ventemodus", + "start": "Start", + "storung": "Feil", + "taktsperre": "Anti-syklus", + "telefonfernschalter": "Telefon fjernbryter", + "test": "Test", + "tpw": "TPW", + "urlaubsmodus": "Feriemodus", + "ventilprufung": "Ventiltest", + "vorspulen": "Inngangsskylling", + "warmwasser": "DHW", + "warmwasser_schnellstart": "DHW hurtigstart", + "warmwasserbetrieb": "DHW-modus", + "warmwassernachlauf": "DHW run-on", + "warmwasservorrang": "DHW-prioritet", + "zunden": "Tenning" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/wolflink/translations/pl.json b/homeassistant/components/wolflink/translations/pl.json index 1b84e8b9e3a..77dd7fb564b 100644 --- a/homeassistant/components/wolflink/translations/pl.json +++ b/homeassistant/components/wolflink/translations/pl.json @@ -23,5 +23,94 @@ "title": "Po\u0142\u0105czenie WOLF SmartSet" } } + }, + "entity": { + "sensor": { + "state": { + "state": { + "1_x_warmwasser": "1 x CWU", + "abgasklappe": "przepustnica spalin", + "absenkbetrieb": "wych\u0142adzanie", + "absenkstop": "zatrzymanie wych\u0142adzania", + "aktiviert": "w\u0142\u0105czone", + "antilegionellenfunktion": "funkcja antylegionella", + "at_abschaltung": "granica wy\u0142\u0105czenia temperatura zewn\u0119trzna", + "at_frostschutz": "ochrona antyzamro\u017ceniowa AT", + "aus": "wy\u0142\u0105czony", + "auto": "auto", + "auto_off_cool": "AutoOffCool", + "auto_on_cool": "AutoOnCool", + "automatik_aus": "automatyczne wy\u0142\u0105czanie", + "automatik_ein": "automatyczne w\u0142\u0105czenie", + "bereit_keine_ladung": "gotowe, nie \u0142aduj\u0119", + "betrieb_ohne_brenner": "praca bez palnika", + "cooling": "ch\u0142odzenie", + "deaktiviert": "nieaktywne", + "dhw_prior": "priorytet CWU", + "eco": "Eco", + "ein": "w\u0142\u0105czony", + "estrichtrocknung": "suszenie jastrychu", + "externe_deaktivierung": "zewn\u0119trzna dezaktywacja", + "fernschalter_ein": "zdalne sterowanie w\u0142\u0105czone", + "frost_heizkreis": "mr\u00f3z w obwodzie grzewczym", + "frost_warmwasser": "mr\u00f3z na CWU", + "frostschutz": "zabezpieczenie przed zamarzaniem", + "gasdruck": "ci\u015bnienie gazu", + "glt_betrieb": "tryb BMS", + "gradienten_uberwachung": "monitorowanie gradientu", + "heizbetrieb": "tryb ogrzewania", + "heizgerat_mit_speicher": "urz\u0105dzenie grzewcze z zasobnikiem", + "heizung": "grzanie", + "initialisierung": "inicjalizacja", + "kalibration": "kalibracja", + "kalibration_heizbetrieb": "kalibracja trybu ogrzewania", + "kalibration_kombibetrieb": "kalibracja trybu kombi", + "kalibration_warmwasserbetrieb": "kalibracja CWU", + "kaskadenbetrieb": "tryb kaskadowy", + "kombibetrieb": "tryb kombi", + "kombigerat": "urz\u0105dzenie kombi", + "kombigerat_mit_solareinbindung": "urz\u0105dzenie kombi zintegrowane z solarem", + "mindest_kombizeit": "minimalny czas kombi", + "nachlauf_heizkreispumpe": "wybieg pompy obiegu grzewczego", + "nachspulen": "przedmuchiwanie", + "nur_heizgerat": "tylko urz\u0105dzenie grzewcze", + "parallelbetrieb": "tryb r\u00f3wnoleg\u0142y", + "partymodus": "tryb imprezowy", + "perm_cooling": "PermCooling", + "permanent": "sta\u0142y", + "permanentbetrieb": "tryb pracy ci\u0105g\u0142ej", + "reduzierter_betrieb": "tryb ograniczony", + "rt_abschaltung": "wy\u0142\u0105czenie RT", + "rt_frostschutz": "ochrona antyzamro\u017ceniowa RT", + "ruhekontakt": "zestyk spoczynkowy", + "schornsteinfeger": "badanie emisji", + "smart_grid": "SmartGrid", + "smart_home": "SmartHome", + "softstart": "\u0142agodny rozruch", + "solarbetrieb": "tryb solarny", + "sparbetrieb": "tryb oszcz\u0119dzania", + "sparen": "oszcz\u0119dzanie", + "spreizung_hoch": "zbyt du\u017cy zakres", + "spreizung_kf": "zakres KF", + "stabilisierung": "stabilizacja", + "standby": "tryb czuwania", + "start": "uruchomienie", + "storung": "usterka", + "taktsperre": "blokada taktu", + "telefonfernschalter": "zdalny prze\u0142\u0105cznik w telefonie", + "test": "test", + "tpw": "TPW", + "urlaubsmodus": "tryb wakacyjny", + "ventilprufung": "kontrola zaworu", + "vorspulen": "p\u0142ukanie wst\u0119pne", + "warmwasser": "CWU", + "warmwasser_schnellstart": "szybki start CWU", + "warmwasserbetrieb": "tryb CWU", + "warmwassernachlauf": "wybieg CWU", + "warmwasservorrang": "priorytet CWU", + "zunden": "zap\u0142on" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/wolflink/translations/pt-BR.json b/homeassistant/components/wolflink/translations/pt-BR.json index 617a2398ae8..bc1b55fa74b 100644 --- a/homeassistant/components/wolflink/translations/pt-BR.json +++ b/homeassistant/components/wolflink/translations/pt-BR.json @@ -23,5 +23,94 @@ "title": "Conex\u00e3o WOLF SmartSet" } } + }, + "entity": { + "sensor": { + "state": { + "state": { + "1_x_warmwasser": "1 x AQS", + "abgasklappe": "Amortecedor de g\u00e1s de combust\u00e3o", + "absenkbetrieb": "Modo de recuo", + "absenkstop": "Parada de recuo", + "aktiviert": "Ativado", + "antilegionellenfunktion": "Fun\u00e7\u00e3o anti-legionela", + "at_abschaltung": "Desligamento OT", + "at_frostschutz": "Prote\u00e7\u00e3o contra congelamento OT", + "aus": "Desabilitado", + "auto": "Autom\u00e1tico", + "auto_off_cool": "Resfriamento Autom\u00e1tico Desligado", + "auto_on_cool": "Resfriamento Autom\u00e1tico Ligado", + "automatik_aus": "Autom\u00e1tico DESLIGADO", + "automatik_ein": "Autom\u00e1tico LIGADO", + "bereit_keine_ladung": "Pronto, n\u00e3o carregando", + "betrieb_ohne_brenner": "Trabalhando sem queimador", + "cooling": "Resfriamento", + "deaktiviert": "Inativo", + "dhw_prior": "AQS Anterior", + "eco": "Eco", + "ein": "Habilitado", + "estrichtrocknung": "Secagem de betonilha", + "externe_deaktivierung": "Desativa\u00e7\u00e3o externa", + "fernschalter_ein": "Controle remoto ativado", + "frost_heizkreis": "Geada no circuito de aquecimento", + "frost_warmwasser": "Gelo AQS", + "frostschutz": "Prote\u00e7\u00e3o contra congelamento", + "gasdruck": "Press\u00e3o do g\u00e1s", + "glt_betrieb": "Modo BMS", + "gradienten_uberwachung": "Monitoramento de gradiente", + "heizbetrieb": "Modo de aquecimento", + "heizgerat_mit_speicher": "Caldeira com cilindro", + "heizung": "Aquecimento", + "initialisierung": "Inicializa\u00e7\u00e3o", + "kalibration": "Calibra\u00e7\u00e3o", + "kalibration_heizbetrieb": "Calibra\u00e7\u00e3o do modo de aquecimento", + "kalibration_kombibetrieb": "Calibra\u00e7\u00e3o do modo combinado", + "kalibration_warmwasserbetrieb": "Calibra\u00e7\u00e3o AQS", + "kaskadenbetrieb": "Opera\u00e7\u00e3o em cascata", + "kombibetrieb": "Modo combinado", + "kombigerat": "Caldeira combinada", + "kombigerat_mit_solareinbindung": "Caldeira combinada com integra\u00e7\u00e3o solar", + "mindest_kombizeit": "Tempo m\u00ednimo combinado", + "nachlauf_heizkreispumpe": "Circuito de aquecimento da bomba em funcionamento", + "nachspulen": "P\u00f3s-lavagem", + "nur_heizgerat": "Apenas caldeira", + "parallelbetrieb": "Modo paralelo", + "partymodus": "Modo de festa", + "perm_cooling": "PermCooling", + "permanent": "Permanente", + "permanentbetrieb": "Modo permanente", + "reduzierter_betrieb": "Modo limitado", + "rt_abschaltung": "Desligamento RT", + "rt_frostschutz": "Prote\u00e7\u00e3o contra congelamento RT", + "ruhekontakt": "Contato de descanso", + "schornsteinfeger": "Teste de emiss\u00f5es", + "smart_grid": "SmartGrid", + "smart_home": "SmartHome", + "softstart": "In\u00edcio suave", + "solarbetrieb": "Modo solar", + "sparbetrieb": "Modo econ\u00f4mico", + "sparen": "Economia", + "spreizung_hoch": "dT muito largo", + "spreizung_kf": "Espalhar KF", + "stabilisierung": "Estabiliza\u00e7\u00e3o", + "standby": "Em espera", + "start": "Come\u00e7ar", + "storung": "Falha", + "taktsperre": "Anti-ciclo", + "telefonfernschalter": "Interruptor remoto do telefone", + "test": "Teste", + "tpw": "TPW", + "urlaubsmodus": "Modo de f\u00e9rias", + "ventilprufung": "Teste de v\u00e1lvula", + "vorspulen": "Enx\u00e1gue de entrada", + "warmwasser": "AQS", + "warmwasser_schnellstart": "In\u00edcio r\u00e1pido de AQS", + "warmwasserbetrieb": "Modo AQS", + "warmwassernachlauf": "Execu\u00e7\u00e3o do AQS", + "warmwasservorrang": "Prioridade de AQS", + "zunden": "Igni\u00e7\u00e3o" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/wolflink/translations/pt.json b/homeassistant/components/wolflink/translations/pt.json index 308f60400aa..85971ccfcf2 100644 --- a/homeassistant/components/wolflink/translations/pt.json +++ b/homeassistant/components/wolflink/translations/pt.json @@ -4,7 +4,7 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, diff --git a/homeassistant/components/wolflink/translations/ru.json b/homeassistant/components/wolflink/translations/ru.json index 8a430c9a63d..b89d6877a1b 100644 --- a/homeassistant/components/wolflink/translations/ru.json +++ b/homeassistant/components/wolflink/translations/ru.json @@ -23,5 +23,94 @@ "title": "WOLF SmartSet" } } + }, + "entity": { + "sensor": { + "state": { + "state": { + "1_x_warmwasser": "1 \u0445 \u0413\u0412\u0421", + "abgasklappe": "\u0417\u0430\u0441\u043b\u043e\u043d\u043a\u0430 \u0434\u044b\u043c\u043e\u0432\u044b\u0445 \u0433\u0430\u0437\u043e\u0432", + "absenkbetrieb": "\u0420\u0435\u0436\u0438\u043c \u0430\u0432\u0430\u0440\u0438\u0438", + "absenkstop": "\u0410\u0432\u0430\u0440\u0438\u0439\u043d\u0430\u044f \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430", + "aktiviert": "\u0410\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u043d\u043e", + "antilegionellenfunktion": "\u0424\u0443\u043d\u043a\u0446\u0438\u044f \u0430\u043d\u0442\u0438-\u043b\u0435\u0433\u0438\u043e\u043d\u0435\u043b\u043b\u044b", + "at_abschaltung": "\u041e\u0422 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435", + "at_frostschutz": "\u041e\u0422 \u0437\u0430\u0449\u0438\u0442\u0430 \u043e\u0442 \u0437\u0430\u043c\u0435\u0440\u0437\u0430\u043d\u0438\u044f", + "aus": "\u041e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u043e", + "auto": "\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438", + "auto_off_cool": "AutoOffCool", + "auto_on_cool": "AutoOnCool", + "automatik_aus": "\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u0432\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435", + "automatik_ein": "\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435", + "bereit_keine_ladung": "\u0413\u043e\u0442\u043e\u0432, \u043d\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442\u0441\u044f", + "betrieb_ohne_brenner": "\u0420\u0430\u0431\u043e\u0442\u0430 \u0431\u0435\u0437 \u0433\u043e\u0440\u0435\u043b\u043a\u0438", + "cooling": "\u041e\u0445\u043b\u0430\u0436\u0434\u0435\u043d\u0438\u0435", + "deaktiviert": "\u041d\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u043e", + "dhw_prior": "DHWPrior", + "eco": "\u042d\u043a\u043e", + "ein": "\u0412\u043a\u043b\u044e\u0447\u0435\u043d\u043e", + "estrichtrocknung": "\u0421\u0443\u0448\u043a\u0430", + "externe_deaktivierung": "\u0412\u043d\u0435\u0448\u043d\u044f\u044f \u0434\u0435\u0430\u043a\u0442\u0438\u0432\u0430\u0446\u0438\u044f", + "fernschalter_ein": "\u0414\u0438\u0441\u0442\u0430\u043d\u0446\u0438\u043e\u043d\u043d\u043e\u0435 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u043e", + "frost_heizkreis": "\u0417\u0430\u043c\u0435\u0440\u0437\u0430\u043d\u0438\u0435 \u043a\u043e\u043d\u0442\u0443\u0440\u0430 \u043e\u0442\u043e\u043f\u043b\u0435\u043d\u0438\u044f", + "frost_warmwasser": "\u0417\u0430\u043c\u0435\u0440\u0437\u0430\u043d\u0438\u0435 \u0413\u0412\u0421", + "frostschutz": "\u0417\u0430\u0449\u0438\u0442\u0430 \u043e\u0442 \u0437\u0430\u043c\u0435\u0440\u0437\u0430\u043d\u0438\u044f", + "gasdruck": "\u0414\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0433\u0430\u0437\u0430", + "glt_betrieb": "\u0420\u0435\u0436\u0438\u043c BMS", + "gradienten_uberwachung": "\u0413\u0440\u0430\u0434\u0438\u0435\u043d\u0442\u043d\u044b\u0439 \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433", + "heizbetrieb": "\u0420\u0435\u0436\u0438\u043c \u043e\u0442\u043e\u043f\u043b\u0435\u043d\u0438\u044f", + "heizgerat_mit_speicher": "\u041a\u043e\u0442\u0435\u043b \u0441 \u0446\u0438\u043b\u0438\u043d\u0434\u0440\u043e\u043c", + "heizung": "\u041e\u0431\u043e\u0433\u0440\u0435\u0432", + "initialisierung": "\u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f", + "kalibration": "\u041a\u0430\u043b\u0438\u0431\u0440\u043e\u0432\u043a\u0430", + "kalibration_heizbetrieb": "\u041a\u0430\u043b\u0438\u0431\u0440\u043e\u0432\u043a\u0430 \u0440\u0435\u0436\u0438\u043c\u0430 \u043e\u0442\u043e\u043f\u043b\u0435\u043d\u0438\u044f", + "kalibration_kombibetrieb": "\u041a\u0430\u043b\u0438\u0431\u0440\u043e\u0432\u043a\u0430 \u0432 \u043a\u043e\u043c\u0431\u0438\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u043c \u0440\u0435\u0436\u0438\u043c\u0435", + "kalibration_warmwasserbetrieb": "\u041a\u0430\u043b\u0438\u0431\u0440\u043e\u0432\u043a\u0430 \u0413\u0412\u0421", + "kaskadenbetrieb": "\u041a\u0430\u0441\u043a\u0430\u0434\u043d\u0430\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f", + "kombibetrieb": "\u041a\u043e\u043c\u0431\u0438\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c", + "kombigerat": "\u0414\u0432\u0443\u0445\u043a\u043e\u043d\u0442\u0443\u0440\u043d\u044b\u0439 \u043a\u043e\u0442\u0435\u043b", + "kombigerat_mit_solareinbindung": "\u0414\u0432\u0443\u0445\u043a\u043e\u043d\u0442\u0443\u0440\u043d\u044b\u0439 \u043a\u043e\u0442\u0435\u043b \u0441 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0435\u0439 \u0441\u043e\u043b\u043d\u0435\u0447\u043d\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b", + "mindest_kombizeit": "\u041c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u043a\u043e\u043c\u0431\u0438\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u0432\u0440\u0435\u043c\u044f", + "nachlauf_heizkreispumpe": "\u0420\u0430\u0431\u043e\u0442\u0430 \u043d\u0430\u0441\u043e\u0441\u0430 \u043a\u043e\u043d\u0442\u0443\u0440\u0430 \u043e\u0442\u043e\u043f\u043b\u0435\u043d\u0438\u044f", + "nachspulen": "\u041f\u043e\u0441\u0442-\u043f\u0440\u043e\u043c\u044b\u0432\u043a\u0430", + "nur_heizgerat": "\u0422\u043e\u043b\u044c\u043a\u043e \u0431\u043e\u0439\u043b\u0435\u0440", + "parallelbetrieb": "\u041f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c", + "partymodus": "\u0420\u0435\u0436\u0438\u043c \u0432\u0435\u0447\u0435\u0440\u0438\u043d\u043a\u0438", + "perm_cooling": "\u041f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e\u0435 \u043e\u0445\u043b\u0430\u0436\u0434\u0435\u043d\u0438\u0435", + "permanent": "\u041f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e", + "permanentbetrieb": "\u041f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c", + "reduzierter_betrieb": "\u041e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c", + "rt_abschaltung": "RT \u0432\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435", + "rt_frostschutz": "RT \u0437\u0430\u0449\u0438\u0442\u0430 \u043e\u0442 \u0437\u0430\u043c\u0435\u0440\u0437\u0430\u043d\u0438\u044f", + "ruhekontakt": "\u041e\u0441\u0442\u0430\u043b\u044c\u043d\u043e\u0439 \u043a\u043e\u043d\u0442\u0430\u043a\u0442", + "schornsteinfeger": "\u0422\u0435\u0441\u0442 \u043d\u0430 \u0432\u044b\u0431\u0440\u043e\u0441\u044b", + "smart_grid": "\u0423\u043c\u043d\u0430\u044f \u0441\u0435\u0442\u044c \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u0441\u043d\u0430\u0431\u0436\u0435\u043d\u0438\u044f", + "smart_home": "SmartHome", + "softstart": "\u041c\u044f\u0433\u043a\u0438\u0439 \u0437\u0430\u043f\u0443\u0441\u043a", + "solarbetrieb": "\u0421\u043e\u043b\u043d\u0435\u0447\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c", + "sparbetrieb": "\u0420\u0435\u0436\u0438\u043c \u044d\u043a\u043e\u043d\u043e\u043c\u0438\u0438", + "sparen": "\u042d\u043a\u043e\u043d\u043e\u043c\u0438\u044f", + "spreizung_hoch": "dT too wide", + "spreizung_kf": "Spread KF", + "stabilisierung": "\u0421\u0442\u0430\u0431\u0438\u043b\u0438\u0437\u0430\u0446\u0438\u044f", + "standby": "\u041e\u0436\u0438\u0434\u0430\u043d\u0438\u0435", + "start": "\u0417\u0430\u043f\u0443\u0441\u043a", + "storung": "\u041e\u0448\u0438\u0431\u043a\u0430", + "taktsperre": "\u0410\u043d\u0442\u0438-\u0446\u0438\u043a\u043b", + "telefonfernschalter": "\u0414\u0438\u0441\u0442\u0430\u043d\u0446\u0438\u043e\u043d\u043d\u043e\u0435 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0441 \u0442\u0435\u043b\u0435\u0444\u043e\u043d\u0430", + "test": "\u0422\u0435\u0441\u0442", + "tpw": "TPW", + "urlaubsmodus": "\u0420\u0435\u0436\u0438\u043c \"\u0432\u044b\u0445\u043e\u0434\u043d\u044b\u0435\"", + "ventilprufung": "\u0422\u0435\u0441\u0442 \u043a\u043b\u0430\u043f\u0430\u043d\u0430", + "vorspulen": "\u041f\u0440\u043e\u043c\u044b\u0432\u043a\u0430 \u0432\u0445\u043e\u0434\u0430", + "warmwasser": "\u0413\u0412\u0421", + "warmwasser_schnellstart": "\u0411\u044b\u0441\u0442\u0440\u044b\u0439 \u0437\u0430\u043f\u0443\u0441\u043a \u0413\u0412\u0421", + "warmwasserbetrieb": "\u0420\u0435\u0436\u0438\u043c \u0413\u0412\u0421", + "warmwassernachlauf": "\u0417\u0430\u043f\u0443\u0441\u043a \u0413\u0412\u0421", + "warmwasservorrang": "\u041f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442 \u0413\u0412\u0421", + "zunden": "\u0417\u0430\u0436\u0438\u0433\u0430\u043d\u0438\u0435" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/wolflink/translations/sensor.sk.json b/homeassistant/components/wolflink/translations/sensor.sk.json index 0ad86ee29b6..f57fb72c3d6 100644 --- a/homeassistant/components/wolflink/translations/sensor.sk.json +++ b/homeassistant/components/wolflink/translations/sensor.sk.json @@ -1,20 +1,87 @@ { "state": { "wolflink__state": { + "1_x_warmwasser": "1 x T\u00daV", + "abgasklappe": "Klapka odvodu spal\u00edn", + "absenkbetrieb": "Re\u017eim spiato\u010dky", + "absenkstop": "Zastavenie spiato\u010dky", + "aktiviert": "Aktivovan\u00e9", + "antilegionellenfunktion": "Anti-legionella funkcia", + "at_abschaltung": "hrani\u010dn\u00e1 hodnota vypnutia vonkaj\u0161ej teploty", + "at_frostschutz": "Protimrazov\u00e1 ochrana AT", "aus": "Zak\u00e1zan\u00e9", + "auto": "Auto", + "auto_off_cool": "AutoOffCool", + "auto_on_cool": "AutoOnCool", + "automatik_aus": "Automatick\u00e9 vypnutie", + "automatik_ein": "Automatick\u00e9 zapnutie", "bereit_keine_ladung": "Pripraven\u00e9, nena\u010d\u00edtava sa", + "betrieb_ohne_brenner": "Pr\u00e1ca bez hor\u00e1ka", "cooling": "Chladenie", + "deaktiviert": "Neakt\u00edvne", + "dhw_prior": "T\u00daV Prior", "eco": "Eco", "ein": "Povolen\u00e9", + "estrichtrocknung": "Su\u0161enie poteru", "externe_deaktivierung": "Extern\u00e1 deaktiv\u00e1cia", "fernschalter_ein": "Dia\u013ekov\u00e9 ovl\u00e1danie je povolen\u00e9", + "frost_heizkreis": "N\u00e1mraza vykurovacieho okruhu", + "frost_warmwasser": "T\u00daV zamrznutie", "frostschutz": "Ochrana pred mrazom", "gasdruck": "Tlak plynu", + "glt_betrieb": "Re\u017eim BMS", + "gradienten_uberwachung": "Monitorovanie gradientu", + "heizbetrieb": "Re\u017eim vykurovania", + "heizgerat_mit_speicher": "Ohrieva\u010d so z\u00e1sobn\u00edkom", "heizung": "Vykurovanie", "initialisierung": "Inicializ\u00e1cia", "kalibration": "Kalibr\u00e1cia", + "kalibration_heizbetrieb": "Kalibr\u00e1cia re\u017eimu vykurovania", + "kalibration_kombibetrieb": "Kalibr\u00e1cia kombinovan\u00e9ho re\u017eimu", + "kalibration_warmwasserbetrieb": "T\u00daV kalibr\u00e1cia", + "kaskadenbetrieb": "Kask\u00e1dov\u00e1 prev\u00e1dzka", + "kombibetrieb": "Kombinovan\u00fd re\u017eim", + "kombigerat": "Kombinovan\u00fd kotol", + "kombigerat_mit_solareinbindung": "Kombinovan\u00fd kotol so sol\u00e1rnou integr\u00e1ciou", + "mindest_kombizeit": "Minim\u00e1lny kombinovan\u00fd \u010das", + "nachlauf_heizkreispumpe": "\u010cerpadlo vykurovacieho okruhu s predstihom", + "nachspulen": "Oplachovanie", "nur_heizgerat": "Iba kotol", - "urlaubsmodus": "Dovolenkov\u00fd re\u017eim" + "parallelbetrieb": "Paraleln\u00fd re\u017eim", + "partymodus": "P\u00e1rty re\u017eim", + "perm_cooling": "Trval\u00e9 chladenie", + "permanent": "Trval\u00fd", + "permanentbetrieb": "Trval\u00fd re\u017eim", + "reduzierter_betrieb": "Obmedzen\u00fd re\u017eim", + "rt_abschaltung": "Vypnutie RT", + "rt_frostschutz": "Protimrazov\u00e1 ochrana RT", + "ruhekontakt": "Rozp\u00ednac\u00ed kontakt", + "schornsteinfeger": "Emisn\u00e1 sk\u00fa\u0161ka", + "smart_grid": "SmartGrid", + "smart_home": "SmartHome", + "softstart": "M\u00e4kk\u00fd \u0161tart", + "solarbetrieb": "Sol\u00e1rny re\u017eim", + "sparbetrieb": "\u00dasporn\u00fd re\u017eim", + "sparen": "\u00dasporn\u00fd", + "spreizung_hoch": "Pr\u00edli\u0161 vysok\u00e9 rozp\u00e4tie", + "spreizung_kf": "Rozsah KF", + "stabilisierung": "Stabiliz\u00e1cia", + "standby": "Pohotovostn\u00fd re\u017eim", + "start": "\u0160tart", + "storung": "Chyba", + "taktsperre": "Z\u00e1mok hod\u00edn", + "telefonfernschalter": "Telef\u00f3nny dia\u013ekov\u00fd sp\u00edna\u010d", + "test": "Test", + "tpw": "TPW", + "urlaubsmodus": "Dovolenkov\u00fd re\u017eim", + "ventilprufung": "Test ventilu", + "vorspulen": "Predopl\u00e1chnutie", + "warmwasser": "T\u00daV", + "warmwasser_schnellstart": "T\u00daV r\u00fdchly \u0161tart", + "warmwasserbetrieb": "T\u00daV re\u017eim", + "warmwassernachlauf": "T\u00daV n\u00e1beh", + "warmwasservorrang": "T\u00daV priorita", + "zunden": "Zapa\u013eovanie" } } } \ No newline at end of file diff --git a/homeassistant/components/wolflink/translations/sk.json b/homeassistant/components/wolflink/translations/sk.json index f1d6b3feeb3..795815071f7 100644 --- a/homeassistant/components/wolflink/translations/sk.json +++ b/homeassistant/components/wolflink/translations/sk.json @@ -12,12 +12,103 @@ "device": { "data": { "device_name": "Zariadenie" - } + }, + "title": "Vyberte zariadenie WOLF" }, "user": { "data": { "password": "Heslo", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" + }, + "title": "Pripojenie WOLF SmartSet" + } + } + }, + "entity": { + "sensor": { + "state": { + "state": { + "1_x_warmwasser": "1 x T\u00daV", + "abgasklappe": "Klapka odvodu spal\u00edn", + "absenkbetrieb": "Re\u017eim spiato\u010dky", + "absenkstop": "Zastavenie spiato\u010dky", + "aktiviert": "Aktivovan\u00e9", + "antilegionellenfunktion": "Anti-legionella funkcia", + "at_abschaltung": "hrani\u010dn\u00e1 hodnota vypnutia vonkaj\u0161ej teploty", + "at_frostschutz": "Protimrazov\u00e1 ochrana AT", + "aus": "Zak\u00e1zan\u00e9", + "auto": "Auto", + "auto_off_cool": "AutoOffCool", + "auto_on_cool": "AutoOnCool", + "automatik_aus": "Automatick\u00e9 VYP", + "automatik_ein": "Automatick\u00e9 ZAP", + "bereit_keine_ladung": "Pripraven\u00e9, nena\u010d\u00edtava sa", + "betrieb_ohne_brenner": "Pr\u00e1ca bez hor\u00e1ka", + "cooling": "Chladenie", + "deaktiviert": "Neakt\u00edvne", + "dhw_prior": "T\u00daV Prior", + "eco": "Eco", + "ein": "Povolen\u00e9", + "estrichtrocknung": "Su\u0161enie poteru", + "externe_deaktivierung": "Extern\u00e1 deaktiv\u00e1cia", + "fernschalter_ein": "Dia\u013ekov\u00e9 ovl\u00e1danie je povolen\u00e9", + "frost_heizkreis": "N\u00e1mraza vykurovacieho okruhu", + "frost_warmwasser": "T\u00daV zamrznutie", + "frostschutz": "Ochrana proti mrazu", + "gasdruck": "Tlak plynu", + "glt_betrieb": "Re\u017eim BMS", + "gradienten_uberwachung": "Monitorovanie gradientu", + "heizbetrieb": "Re\u017eim vykurovania", + "heizgerat_mit_speicher": "Ohrieva\u010d so z\u00e1sobn\u00edkom", + "heizung": "Vykurovanie", + "initialisierung": "Inicializ\u00e1cia", + "kalibration": "Kalibr\u00e1cia", + "kalibration_heizbetrieb": "Kalibr\u00e1cia re\u017eimu vykurovania", + "kalibration_kombibetrieb": "Kalibr\u00e1cia kombinovan\u00e9ho re\u017eimu", + "kalibration_warmwasserbetrieb": "T\u00daV kalibr\u00e1cia", + "kaskadenbetrieb": "Kask\u00e1dov\u00e1 prev\u00e1dzka", + "kombibetrieb": "Kombinovan\u00fd re\u017eim", + "kombigerat": "Kombinovan\u00fd kotol", + "kombigerat_mit_solareinbindung": "Kombinovan\u00fd kotol so sol\u00e1rnou integr\u00e1ciou", + "mindest_kombizeit": "Minim\u00e1lny kombinovan\u00fd \u010das", + "nachlauf_heizkreispumpe": "Zapnutie \u010derpadla vykurovacieho okruhu", + "nachspulen": "Oplachovanie", + "nur_heizgerat": "Iba kotol", + "parallelbetrieb": "Paraleln\u00fd re\u017eim", + "partymodus": "P\u00e1rty re\u017eim", + "perm_cooling": "Trval\u00e9 chladenie", + "permanent": "Trval\u00fd", + "permanentbetrieb": "Trval\u00fd re\u017eim", + "reduzierter_betrieb": "Obmedzen\u00fd re\u017eim", + "rt_abschaltung": "Vypnutie RT", + "rt_frostschutz": "Protimrazov\u00e1 ochrana RT", + "ruhekontakt": "Rozp\u00ednac\u00ed kontakt", + "schornsteinfeger": "Emisn\u00e1 sk\u00fa\u0161ka", + "smart_grid": "SmartGrid", + "smart_home": "SmartHome", + "softstart": "Soft Start", + "solarbetrieb": "Sol\u00e1rny re\u017eim", + "sparbetrieb": "\u00dasporn\u00fd re\u017eim", + "sparen": "\u00dasporn\u00fd", + "spreizung_hoch": "Pr\u00edli\u0161 vysok\u00e9 rozp\u00e4tie", + "spreizung_kf": "Rozsah KF", + "stabilisierung": "Stabiliz\u00e1cia", + "standby": "Pohotovostn\u00fd re\u017eim", + "start": "\u0160tart", + "storung": "Porucha", + "taktsperre": "Z\u00e1mok hod\u00edn", + "telefonfernschalter": "Telef\u00f3nny dia\u013ekov\u00fd sp\u00edna\u010d", + "test": "Test", + "tpw": "TPW", + "urlaubsmodus": "Dovolenkov\u00fd re\u017eim", + "ventilprufung": "Test ventilu", + "vorspulen": "Predopl\u00e1chnutie", + "warmwasser": "T\u00daV", + "warmwasser_schnellstart": "T\u00daV r\u00fdchly \u0161tart", + "warmwasserbetrieb": "T\u00daV re\u017eim", + "warmwassernachlauf": "T\u00daV n\u00e1beh", + "warmwasservorrang": "T\u00daV priorita", + "zunden": "Zapa\u013eovanie" } } } diff --git a/homeassistant/components/wolflink/translations/zh-Hant.json b/homeassistant/components/wolflink/translations/zh-Hant.json index d7f78e49992..610676f349e 100644 --- a/homeassistant/components/wolflink/translations/zh-Hant.json +++ b/homeassistant/components/wolflink/translations/zh-Hant.json @@ -23,5 +23,94 @@ "title": "WOLF SmartSet connection" } } + }, + "entity": { + "sensor": { + "state": { + "state": { + "1_x_warmwasser": "1 x DHW", + "abgasklappe": "\u71c3\u6c23\u8abf\u7bc0\u95a5", + "absenkbetrieb": "\u5012\u9000\u505c\u6b62", + "absenkstop": "\u5012\u9000\u505c\u6b62", + "aktiviert": "\u5df2\u555f\u52d5", + "antilegionellenfunktion": "\u6297\u83cc\u529f\u80fd", + "at_abschaltung": "OT \u95dc\u6a5f", + "at_frostschutz": "OT \u9632\u51cd", + "aus": "\u5df2\u95dc\u9589", + "auto": "\u81ea\u52d5", + "auto_off_cool": "\u81ea\u52d5\u95dc\u9589\u51b7\u537b", + "auto_on_cool": "\u81ea\u52d5\u958b\u555f\u51b7\u537b", + "automatik_aus": "\u81ea\u52d5\u95dc\u9589", + "automatik_ein": "\u81ea\u52d5\u958b\u555f", + "bereit_keine_ladung": "\u5c31\u7dd2\uff0c\u672a\u8f09\u5165", + "betrieb_ohne_brenner": "\u7121\u71c3\u71d2\u904b\u4f5c", + "cooling": "\u51b7\u6c23", + "deaktiviert": "\u672a\u555f\u52d5", + "dhw_prior": "DHWPrior", + "eco": "\u7bc0\u80fd", + "ein": "\u5df2\u555f\u7528", + "estrichtrocknung": "\u71d9\u677f\u4e7e\u71e5", + "externe_deaktivierung": "\u5916\u90e8\u505c\u7528", + "fernschalter_ein": "\u9060\u7aef\u63a7\u5236\u5df2\u958b\u555f", + "frost_heizkreis": "\u52a0\u71b1\u8ff4\u8def\u7d50\u971c", + "frost_warmwasser": "DHW \u971c", + "frostschutz": "\u9632\u51cd", + "gasdruck": "\u6c23\u58d3", + "glt_betrieb": "BMS \u6a21\u5f0f", + "gradienten_uberwachung": "\u50be\u659c\u76e3\u63a7", + "heizbetrieb": "\u6696\u6c23\u6a21\u5f0f", + "heizgerat_mit_speicher": "\u6c7d\u7f38\u934b\u7210", + "heizung": "\u6696\u6c23", + "initialisierung": "\u521d\u59cb\u5316", + "kalibration": "\u6821\u6b63", + "kalibration_heizbetrieb": "\u6696\u6c23\u6a21\u5f0f\u6821\u6b63", + "kalibration_kombibetrieb": "\u6df7\u5408\u6a21\u5f0f\u6821\u6b63", + "kalibration_warmwasserbetrieb": "DHW \u6821\u6b63", + "kaskadenbetrieb": "\u4e32\u9023\u64cd\u4f5c", + "kombibetrieb": "\u6df7\u5408\u6a21\u5f0f", + "kombigerat": "\u6df7\u5408\u934b\u7210", + "kombigerat_mit_solareinbindung": "\u6574\u5408\u592a\u967d\u80fd\u6df7\u5408\u934b\u7210", + "mindest_kombizeit": "\u6700\u4f4e\u6df7\u5408\u6642\u9593", + "nachlauf_heizkreispumpe": "\u8ff4\u8def\u5e6b\u6d66\u904b\u884c\u6696\u6c23", + "nachspulen": "\u6c96\u6d17\u5f8c", + "nur_heizgerat": "\u50c5\u934b\u7210", + "parallelbetrieb": "\u4e26\u884c\u6a21\u5f0f", + "partymodus": "\u6d3e\u5c0d\u6a21\u5f0f", + "perm_cooling": "PermCooling", + "permanent": "\u56fa\u5b9a", + "permanentbetrieb": "\u6c38\u4e45\u6a21\u5f0f", + "reduzierter_betrieb": "\u9650\u5236\u6a21\u5f0f", + "rt_abschaltung": "RT \u95dc\u6a5f", + "rt_frostschutz": "RT \u9632\u51cd", + "ruhekontakt": "Rest contact", + "schornsteinfeger": "\u6392\u653e\u6e2c\u8a66", + "smart_grid": "SmartGrid", + "smart_home": "SmartHome", + "softstart": "\u8edf\u958b\u6a5f", + "solarbetrieb": "\u592a\u967d\u80fd\u6a21\u5f0f", + "sparbetrieb": "\u7bc0\u80fd\u6a21\u5f0f", + "sparen": "\u7bc0\u80fd", + "spreizung_hoch": "DT \u904e\u5bec", + "spreizung_kf": "Spread KF", + "stabilisierung": "\u7a69\u5b9a", + "standby": "\u5f85\u547d", + "start": "\u958b\u59cb", + "storung": "\u6545\u969c", + "taktsperre": "\u53cd\u5faa\u74b0", + "telefonfernschalter": "\u96fb\u8a71\u9060\u7aef\u958b\u95dc", + "test": "\u6e2c\u8a66", + "tpw": "TPW", + "urlaubsmodus": "\u5047\u65e5\u6a21\u5f0f", + "ventilprufung": "\u95a5\u9580\u6e2c\u8a66", + "vorspulen": "Entry rinsing", + "warmwasser": "DHW", + "warmwasser_schnellstart": "DHW \u5feb\u901f\u555f\u52d5", + "warmwasserbetrieb": "DHW \u6a21\u5f0f", + "warmwassernachlauf": "DHW \u904b\u884c", + "warmwasservorrang": "DHW \u512a\u5148", + "zunden": "\u9ede\u706b" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/ws66i/translations/pt.json b/homeassistant/components/ws66i/translations/pt.json index 3b5850222d9..439e6f27fc1 100644 --- a/homeassistant/components/ws66i/translations/pt.json +++ b/homeassistant/components/ws66i/translations/pt.json @@ -1,7 +1,7 @@ { "config": { "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" } } } \ No newline at end of file diff --git a/homeassistant/components/wsdot/sensor.py b/homeassistant/components/wsdot/sensor.py index b8c362e56a5..b36f598624d 100644 --- a/homeassistant/components/wsdot/sensor.py +++ b/homeassistant/components/wsdot/sensor.py @@ -10,13 +10,7 @@ import requests import voluptuous as vol from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity -from homeassistant.const import ( - ATTR_NAME, - CONF_API_KEY, - CONF_ID, - CONF_NAME, - TIME_MINUTES, -) +from homeassistant.const import ATTR_NAME, CONF_API_KEY, CONF_ID, CONF_NAME, UnitOfTime from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -106,7 +100,7 @@ class WashingtonStateTravelTimeSensor(WashingtonStateTransportSensor): """Travel time sensor from WSDOT.""" _attr_attribution = ATTRIBUTION - _attr_native_unit_of_measurement = TIME_MINUTES + _attr_native_unit_of_measurement = UnitOfTime.MINUTES def __init__(self, name, access_code, travel_time_id): """Construct a travel time sensor.""" diff --git a/homeassistant/components/xbox/__init__.py b/homeassistant/components/xbox/__init__.py index 443eee7a334..4263059f0fa 100644 --- a/homeassistant/components/xbox/__init__.py +++ b/homeassistant/components/xbox/__init__.py @@ -174,7 +174,7 @@ class XboxData: presence: dict[str, PresenceData] -class XboxUpdateCoordinator(DataUpdateCoordinator): +class XboxUpdateCoordinator(DataUpdateCoordinator[XboxData]): """Store Xbox Console Status.""" def __init__( @@ -190,7 +190,7 @@ class XboxUpdateCoordinator(DataUpdateCoordinator): name=DOMAIN, update_interval=timedelta(seconds=10), ) - self.data: XboxData = XboxData({}, {}) + self.data = XboxData({}, {}) self.client: XboxLiveClient = client self.consoles: SmartglassConsoleList = consoles diff --git a/homeassistant/components/xbox/translations/en_GB.json b/homeassistant/components/xbox/translations/en-GB.json similarity index 100% rename from homeassistant/components/xbox/translations/en_GB.json rename to homeassistant/components/xbox/translations/en-GB.json diff --git a/homeassistant/components/xbox/translations/pt.json b/homeassistant/components/xbox/translations/pt.json index 38f070ab3db..606f0c81aa5 100644 --- a/homeassistant/components/xbox/translations/pt.json +++ b/homeassistant/components/xbox/translations/pt.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "authorize_url_timeout": "Tempo excedido a gerar um URL de autoriza\u00e7\u00e3o", + "authorize_url_timeout": "Excedido o tempo limite para gerar um URL de autoriza\u00e7\u00e3o", "missing_configuration": "O componente n\u00e3o est\u00e1 configurado. Por favor, siga a documenta\u00e7\u00e3o.", "single_instance_allowed": "J\u00e1 configurado. Apenas uma \u00fanica configura\u00e7\u00e3o \u00e9 poss\u00edvel." }, diff --git a/homeassistant/components/xbox/translations/sk.json b/homeassistant/components/xbox/translations/sk.json index 732253dabb4..ae78ba79fec 100644 --- a/homeassistant/components/xbox/translations/sk.json +++ b/homeassistant/components/xbox/translations/sk.json @@ -13,5 +13,11 @@ "title": "Vyberte met\u00f3du overenia" } } + }, + "issues": { + "deprecated_yaml": { + "description": "Konfigur\u00e1cia konzoly Xbox v s\u00fabore configuration.yaml sa v aplik\u00e1cii Home Assistant 2022.9 odstra\u0148uje. \n\n Va\u0161e existuj\u00face poverenia aplik\u00e1cie OAuth a nastavenia pr\u00edstupu sa importovali do pou\u017e\u00edvate\u013esk\u00e9ho rozhrania automaticky. Ak chcete tento probl\u00e9m vyrie\u0161i\u0165, odstr\u00e1\u0148te konfigur\u00e1ciu YAML zo s\u00faboru configuration.yaml a re\u0161tartujte aplik\u00e1ciu Home Assistant.", + "title": "Konfigur\u00e1cia Xbox YAML sa odstra\u0148uje" + } } } \ No newline at end of file diff --git a/homeassistant/components/xbox_live/sensor.py b/homeassistant/components/xbox_live/sensor.py index f75dbe6ba4e..d95031a646e 100644 --- a/homeassistant/components/xbox_live/sensor.py +++ b/homeassistant/components/xbox_live/sensor.py @@ -57,8 +57,10 @@ def setup_platform( response = api.api_get("profile") if not response.ok: _LOGGER.error( - "Can't setup X API connection. Check your account or " - "api key on xapi.us. Code: %s Description: %s ", + ( + "Can't setup X API connection. Check your account or " + "api key on xapi.us. Code: %s Description: %s " + ), response.status_code, response.reason, ) diff --git a/homeassistant/components/xbox_live/translations/ca.json b/homeassistant/components/xbox_live/translations/ca.json new file mode 100644 index 00000000000..adca372177a --- /dev/null +++ b/homeassistant/components/xbox_live/translations/ca.json @@ -0,0 +1,8 @@ +{ + "issues": { + "pending_removal": { + "description": "La integraci\u00f3 Xbox Live est\u00e0 pendent d'eliminar-se de Home Assistant i ja no estar\u00e0 disponible a partir de Home Assistant 2023.2. \n\nLa integraci\u00f3 s'est\u00e0 eliminant, perqu\u00e8 nom\u00e9s \u00e9s \u00fatil per dispositius heretats Xbox 360 i l'API actual necessita una subscripci\u00f3 de pagament. Les consoles m\u00e9s noves s\u00f3n compatibles amb la integraci\u00f3 Xbox de forma gratu\u00efta.\n\nElimina la configuraci\u00f3 YAML d'Xbox Live del fitxer configuration.yaml i reinicia Home Assistant per arreglar aquest error.", + "title": "La integraci\u00f3 Xbox Live est\u00e0 sent eliminada" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/xbox_live/translations/de.json b/homeassistant/components/xbox_live/translations/de.json new file mode 100644 index 00000000000..6eec6080268 --- /dev/null +++ b/homeassistant/components/xbox_live/translations/de.json @@ -0,0 +1,8 @@ +{ + "issues": { + "pending_removal": { + "description": "Die Xbox Live-Integration wurde aus dem Home Assistant entfernt und wird ab Home Assistant 2023.2 nicht mehr verf\u00fcgbar sein.\n\nDie Integration wird entfernt, weil sie nur f\u00fcr das \u00e4ltere Ger\u00e4t Xbox 360 n\u00fctzlich ist und die vorgelagerte API jetzt ein kostenpflichtiges Abonnement erfordert. Neuere Konsolen werden von der Xbox-Integration kostenlos unterst\u00fctzt.\n\nEntferne die Xbox Live YAML-Konfiguration aus deiner configuration.yaml-Datei und starte Home Assistant neu, um dieses Problem zu beheben.", + "title": "Die Xbox Live-Integration wird entfernt" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/xbox_live/translations/el.json b/homeassistant/components/xbox_live/translations/el.json new file mode 100644 index 00000000000..b429d53f252 --- /dev/null +++ b/homeassistant/components/xbox_live/translations/el.json @@ -0,0 +1,8 @@ +{ + "issues": { + "pending_removal": { + "description": "\u0397 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 Xbox Live \u03b5\u03ba\u03ba\u03c1\u03b5\u03bc\u03b5\u03af \u03ba\u03b1\u03c4\u03ac\u03c1\u03b3\u03b7\u03c3\u03b7 \u03b1\u03c0\u03cc \u03c4\u03bf Home Assistant \u03ba\u03b1\u03b9 \u03b4\u03b5\u03bd \u03b8\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u03c0\u03bb\u03ad\u03bf\u03bd \u03b4\u03b9\u03b1\u03b8\u03ad\u03c3\u03b9\u03bc\u03b7 \u03b1\u03c0\u03cc \u03c4\u03bf Home Assistant 2023.2. \n\n \u0397 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b5\u03af\u03c4\u03b1\u03b9, \u03b5\u03c0\u03b5\u03b9\u03b4\u03ae \u03b5\u03af\u03bd\u03b1\u03b9 \u03c7\u03c1\u03ae\u03c3\u03b9\u03bc\u03b7 \u03bc\u03cc\u03bd\u03bf \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03c0\u03b1\u03bb\u03b1\u03b9\u03bf\u03cd \u03c4\u03cd\u03c0\u03bf\u03c5 Xbox 360 \u03ba\u03b1\u03b9 \u03c4\u03bf upstream API \u03b1\u03c0\u03b1\u03b9\u03c4\u03b5\u03af \u03c0\u03bb\u03ad\u03bf\u03bd \u03c3\u03c5\u03bd\u03b4\u03c1\u03bf\u03bc\u03ae \u03b5\u03c0\u03af \u03c0\u03bb\u03b7\u03c1\u03c9\u03bc\u03ae. \u039f\u03b9 \u03bd\u03b5\u03cc\u03c4\u03b5\u03c1\u03b5\u03c2 \u03ba\u03bf\u03bd\u03c3\u03cc\u03bb\u03b5\u03c2 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03bf\u03bd\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03b7\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 Xbox \u03b4\u03c9\u03c1\u03b5\u03ac\u03bd. \n\n \u039a\u03b1\u03c4\u03b1\u03c1\u03b3\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 Xbox Live YAML \u03b1\u03c0\u03cc \u03c4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf configuration.yaml \u03ba\u03b1\u03b9 \u03b5\u03c0\u03b1\u03bd\u03b5\u03ba\u03ba\u03b9\u03bd\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf Home Assistant \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c0\u03c1\u03cc\u03b2\u03bb\u03b7\u03bc\u03b1.", + "title": "\u0397 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 Xbox Live \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03b5\u03af\u03c4\u03b1\u03b9" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/xbox_live/translations/es.json b/homeassistant/components/xbox_live/translations/es.json new file mode 100644 index 00000000000..809ac84766b --- /dev/null +++ b/homeassistant/components/xbox_live/translations/es.json @@ -0,0 +1,8 @@ +{ + "issues": { + "pending_removal": { + "description": "La integraci\u00f3n Xbox Live est\u00e1 pendiente de eliminaci\u00f3n de Home Assistant y ya no estar\u00e1 disponible a partir de Home Assistant 2023.2. \n\nLa integraci\u00f3n se eliminar\u00e1 porque solo es \u00fatil para el dispositivo heredado Xbox 360 y la API ahora requiere una suscripci\u00f3n de pago. Las consolas m\u00e1s nuevas son compatibles con la integraci\u00f3n Xbox de forma gratuita. \n\nElimina la configuraci\u00f3n YAML de Xbox Live de tu archivo configuration.yaml y reinicia Home Assistant para solucionar este problema.", + "title": "Se va a eliminar la integraci\u00f3n Xbox Live" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/xbox_live/translations/et.json b/homeassistant/components/xbox_live/translations/et.json new file mode 100644 index 00000000000..ba0726437e7 --- /dev/null +++ b/homeassistant/components/xbox_live/translations/et.json @@ -0,0 +1,8 @@ +{ + "issues": { + "pending_removal": { + "description": "Xbox Live'i integratsioon ootab Home Assistantist eemaldamist ja pole alates versioonist Home Assistant 2023.2 enam saadaval. \n\n Integratsioon eemaldatakse, kuna see on kasulik ainult p\u00e4randseadme Xbox 360 jaoks ja \u00fclesvoolu API n\u00f5uab n\u00fc\u00fcd tasulist tellimust. Uuemaid konsoole toetab Xboxi integratsioon tasuta. \n\n Selle probleemi lahendamiseks eemaldage Xbox Live'i YAML-i konfiguratsioon failist configuration.yaml ja taask\u00e4ivitage Home Assistant.", + "title": "Xbox Live'i integratsioon eemaldatakse" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/xbox_live/translations/hu.json b/homeassistant/components/xbox_live/translations/hu.json new file mode 100644 index 00000000000..cb30d903fea --- /dev/null +++ b/homeassistant/components/xbox_live/translations/hu.json @@ -0,0 +1,8 @@ +{ + "issues": { + "pending_removal": { + "description": "Az Xbox Live integr\u00e1ci\u00f3 elt\u00e1vol\u00edt\u00e1sra v\u00e1r a Home Assistantb\u00f3l, \u00e9s a Home Assistant 2023.2-t\u0151l m\u00e1r nem lesz el\u00e9rhet\u0151.\n\nAz integr\u00e1ci\u00f3 elt\u00e1vol\u00edt\u00e1sra ker\u00fcl, mivel csak a r\u00e9gi Xbox 360 eszk\u00f6zzel haszn\u00e1lhat\u00f3, \u00e9s az upstream API m\u00e1r fizet\u0151s el\u0151fizet\u00e9st ig\u00e9nyel. Az \u00fajabb konzolokat az Xbox integr\u00e1ci\u00f3 ingyenesen t\u00e1mogatja.\n\nA hiba kijav\u00edt\u00e1s\u00e1hoz t\u00e1vol\u00edtsa el az Xbox Live YAML konfigur\u00e1ci\u00f3t a configuration.yaml f\u00e1jlb\u00f3l, \u00e9s ind\u00edtsa \u00fajra a Home Assistantot.", + "title": "Az Xbox Live integr\u00e1ci\u00f3 elt\u00e1vol\u00edt\u00e1sra ker\u00fcl" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/xbox_live/translations/id.json b/homeassistant/components/xbox_live/translations/id.json new file mode 100644 index 00000000000..e9e3f02b802 --- /dev/null +++ b/homeassistant/components/xbox_live/translations/id.json @@ -0,0 +1,8 @@ +{ + "issues": { + "pending_removal": { + "description": "Integrasi Xbox Live sedang menunggu penghapusan dari Home Assistant dan tidak akan lagi tersedia pada Home Assistant 2023.2.\n\nIntegrasi ini dalam proses penghapusan, karena hanya berguna untuk perangkat Xbox 360 usang dan API di tingkat atas membutuhkan langganan berbayar. Konsol yang lebih baru didukung dengan integrasi Xbox yang gratis.\n\nHapus konfigurasi Xbox Live YAML dari file configuration.yaml Anda dan mulai ulang Home Assistant untuk memperbaiki masalah ini.", + "title": "Integrasi Xbox Live dalam proses penghapusan" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/xbox_live/translations/it.json b/homeassistant/components/xbox_live/translations/it.json new file mode 100644 index 00000000000..e4283ad1e03 --- /dev/null +++ b/homeassistant/components/xbox_live/translations/it.json @@ -0,0 +1,8 @@ +{ + "issues": { + "pending_removal": { + "description": "L'integrazione Xbox Live \u00e8 in attesa di rimozione da Home Assistant e non sar\u00e0 pi\u00f9 disponibile a partire da Home Assistant 2023.2. \n\nL'integrazione \u00e8 stata rimossa, perch\u00e9 \u00e8 utile solo per il vecchio dispositivo Xbox 360 e l'API upstream ora richiede un abbonamento a pagamento. Le console pi\u00f9 recenti sono supportate dall'integrazione Xbox gratuitamente. \n\nRimuovi la configurazione YAML di Xbox Live dal file configuration.yaml e riavvia Home Assistant per risolvere il problema.", + "title": "L'integrazione Xbox Live \u00e8 stata rimossa" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/xbox_live/translations/nl.json b/homeassistant/components/xbox_live/translations/nl.json new file mode 100644 index 00000000000..a622a391abb --- /dev/null +++ b/homeassistant/components/xbox_live/translations/nl.json @@ -0,0 +1,7 @@ +{ + "issues": { + "pending_removal": { + "title": "De Xbox Live-integratie wordt verwijderd" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/xbox_live/translations/no.json b/homeassistant/components/xbox_live/translations/no.json new file mode 100644 index 00000000000..c0be0d27e31 --- /dev/null +++ b/homeassistant/components/xbox_live/translations/no.json @@ -0,0 +1,8 @@ +{ + "issues": { + "pending_removal": { + "description": "Xbox Live-integrasjonen venter p\u00e5 fjerning fra Home Assistant og vil ikke lenger v\u00e6re tilgjengelig fra og med Home Assistant 2023.2. \n\n Integrasjonen blir fjernet, fordi den kun er nyttig for den eldre enheten Xbox 360 og oppstr\u00f8ms API krever n\u00e5 et betalt abonnement. Nyere konsoller st\u00f8ttes av Xbox-integrasjonen gratis. \n\n Fjern Xbox Live YAML-konfigurasjonen fra configuration.yaml-filen og start Home Assistant p\u00e5 nytt for \u00e5 fikse dette problemet.", + "title": "Xbox Live-integrasjonen blir fjernet" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/xbox_live/translations/pl.json b/homeassistant/components/xbox_live/translations/pl.json new file mode 100644 index 00000000000..b182538813b --- /dev/null +++ b/homeassistant/components/xbox_live/translations/pl.json @@ -0,0 +1,8 @@ +{ + "issues": { + "pending_removal": { + "description": "Integracja Xbox Live oczekuje na usuni\u0119cie z Home Assistant i nie b\u0119dzie ju\u017c dost\u0119pna od Home Assistant 2023.2. \n\nIntegracja jest usuwana, poniewa\u017c jest u\u017cyteczna tylko dla starszego urz\u0105dzenia Xbox 360, a nadrz\u0119dny interfejs API wymaga teraz p\u0142atnej subskrypcji. Nowsze konsole s\u0105 obs\u0142ugiwane przez integracj\u0119 Xbox za darmo. \n\nUsu\u0144 konfiguracj\u0119 YAML z pliku configuration.yaml i uruchom ponownie Home Assistanta, aby rozwi\u0105za\u0107 ten problem.", + "title": "Integracja Xbox Live zostanie usuni\u0119ta" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/xbox_live/translations/pt-BR.json b/homeassistant/components/xbox_live/translations/pt-BR.json new file mode 100644 index 00000000000..c61b75c11ab --- /dev/null +++ b/homeassistant/components/xbox_live/translations/pt-BR.json @@ -0,0 +1,8 @@ +{ + "issues": { + "pending_removal": { + "description": "A integra\u00e7\u00e3o do Xbox Live est\u00e1 pendente de remo\u00e7\u00e3o do Home Assistant e n\u00e3o estar\u00e1 mais dispon\u00edvel a partir do Home Assistant 2023.2. \n\n A integra\u00e7\u00e3o est\u00e1 sendo removida porque s\u00f3 \u00e9 \u00fatil para o dispositivo herdado Xbox 360 e a API upstream agora requer uma assinatura paga. Os consoles mais novos s\u00e3o suportados pela integra\u00e7\u00e3o do Xbox gratuitamente. \n\n Remova a configura\u00e7\u00e3o YAML do Xbox Live do arquivo configuration.yaml e reinicie o Home Assistant para corrigir esse problema.", + "title": "A integra\u00e7\u00e3o do Xbox Live est\u00e1 sendo removida" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/xbox_live/translations/ru.json b/homeassistant/components/xbox_live/translations/ru.json new file mode 100644 index 00000000000..00f7e4ac276 --- /dev/null +++ b/homeassistant/components/xbox_live/translations/ru.json @@ -0,0 +1,8 @@ +{ + "issues": { + "pending_removal": { + "description": "\u0418\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0441 Xbox Live \u043e\u0436\u0438\u0434\u0430\u0435\u0442 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0438\u0437 Home Assistant \u0438 \u0431\u043e\u043b\u044c\u0448\u0435 \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430 \u0441 Home Assistant 2023.2. \n\n\u0418\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043b\u0435\u043d\u0430, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043e\u043d\u0430 \u043f\u043e\u043b\u0435\u0437\u043d\u0430 \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0435\u0433\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 Xbox 360, \u0430 \u0434\u043b\u044f \u043d\u043e\u0432\u043e\u0433\u043e API \u0442\u0435\u043f\u0435\u0440\u044c \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u043b\u0430\u0442\u043d\u0430\u044f \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0430. \u0411\u043e\u043b\u0435\u0435 \u043d\u043e\u0432\u044b\u0435 \u043a\u043e\u043d\u0441\u043e\u043b\u0438 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044e\u0442\u0441\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0435\u0439 Xbox \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e. \n\n\u0423\u0434\u0430\u043b\u0438\u0442\u0435 YAML-\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e Xbox Live \u0438\u0437 \u0444\u0430\u0439\u043b\u0430 configuration.yaml \u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 Home Assistant, \u0447\u0442\u043e\u0431\u044b \u0443\u0441\u0442\u0440\u0430\u043d\u0438\u0442\u044c \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443.", + "title": "\u0418\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f Xbox Live \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043b\u0435\u043d\u0430" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/xbox_live/translations/sk.json b/homeassistant/components/xbox_live/translations/sk.json new file mode 100644 index 00000000000..f3b112b6633 --- /dev/null +++ b/homeassistant/components/xbox_live/translations/sk.json @@ -0,0 +1,8 @@ +{ + "issues": { + "pending_removal": { + "description": "Integr\u00e1cia Xbox Live \u010dak\u00e1 na odstr\u00e1nenie z Home Assistant a od Home Assistant 2023.2 u\u017e nebude k dispoz\u00edcii. \n\n Integr\u00e1cia sa odstra\u0148uje, preto\u017ee je u\u017eito\u010dn\u00e1 iba pre star\u0161ie zariadenie Xbox 360 a upstream API teraz vy\u017eaduje platen\u00e9 predplatn\u00e9. Nov\u0161ie konzoly s\u00fa podporovan\u00e9 integr\u00e1ciou Xbox zadarmo. \n\n Odstr\u00e1\u0148te konfigur\u00e1ciu Xbox Live YAML zo s\u00faboru configuration.yaml a re\u0161tartujte Home Assistant, aby ste tento probl\u00e9m vyrie\u0161ili.", + "title": "Integr\u00e1cia slu\u017eby Xbox Live sa odstra\u0148uje" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/xbox_live/translations/zh-Hant.json b/homeassistant/components/xbox_live/translations/zh-Hant.json new file mode 100644 index 00000000000..c8ba901a7f5 --- /dev/null +++ b/homeassistant/components/xbox_live/translations/zh-Hant.json @@ -0,0 +1,8 @@ +{ + "issues": { + "pending_removal": { + "description": "Xbox Live \u6574\u5408\u5373\u5c07\u7531 Home Assistant \u4e2d\u79fb\u9664\u3001\u4e26\u65bc Home Assistant 2023.2 \u7248\u5f8c\u7121\u6cd5\u518d\u4f7f\u7528\u3002\n\n\u7531\u65bc\u50c5\u5c0d\u820a\u6b3e Xbox 360 \u6709\u7528\u3001\u4e0a\u50b3 API \u5fc5\u9808\u70ba\u4ed8\u8cbb\u8a02\u95b1\u6a21\u5f0f\uff0c\u6574\u5408\u5373\u5c07\u79fb\u9664\u3002\u65b0\u7248\u672c Xbox \u652f\u63f4\u6574\u5408\u5247\u70ba\u514d\u8cbb\u65b9\u5f0f\u3002\n\n\u7531 configuration.yaml \u6a94\u6848\u4e2d\u79fb\u9664 Xbox LiveYAML \u8a2d\u5b9a\u4e26\u91cd\u555f Home Assistant to \u4ee5\u4fee\u6b63\u6b64\u554f\u984c\u3002", + "title": "Xbox Live \u6574\u5408\u5373\u5c07\u79fb\u9664" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/xiaomi_aqara/__init__.py b/homeassistant/components/xiaomi_aqara/__init__.py index aba9da05611..be6eba6793e 100644 --- a/homeassistant/components/xiaomi_aqara/__init__.py +++ b/homeassistant/components/xiaomi_aqara/__init__.py @@ -105,8 +105,10 @@ def setup(hass: HomeAssistant, config: ConfigType) -> bool: gateway.write_to_hub(gateway.sid, join_permission="yes") persistent_notification.async_create( hass, - "Join permission enabled for 30 seconds! " - "Please press the pairing button of the new device once.", + ( + "Join permission enabled for 30 seconds! " + "Please press the pairing button of the new device once." + ), title="Xiaomi Aqara Gateway", ) diff --git a/homeassistant/components/xiaomi_aqara/config_flow.py b/homeassistant/components/xiaomi_aqara/config_flow.py index cc86fda85f5..773e63d92bf 100644 --- a/homeassistant/components/xiaomi_aqara/config_flow.py +++ b/homeassistant/components/xiaomi_aqara/config_flow.py @@ -162,7 +162,10 @@ class XiaomiAqaraFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): name.startswith(ZEROCONF_GATEWAY) or name.startswith(ZEROCONF_ACPARTNER) ): _LOGGER.debug( - "Xiaomi device '%s' discovered with host %s, not identified as xiaomi aqara gateway", + ( + "Xiaomi device '%s' discovered with host %s, not identified as" + " xiaomi aqara gateway" + ), name, self.host, ) diff --git a/homeassistant/components/xiaomi_aqara/light.py b/homeassistant/components/xiaomi_aqara/light.py index 173ffe564fc..822d4121c68 100644 --- a/homeassistant/components/xiaomi_aqara/light.py +++ b/homeassistant/components/xiaomi_aqara/light.py @@ -70,8 +70,7 @@ class XiaomiGatewayLight(XiaomiDevice, LightEntity): rgbhexstr = f"{value:x}" if len(rgbhexstr) > 8: _LOGGER.error( - "Light RGB data error." - " Can't be more than 8 characters. Received: %s", + "Light RGB data error. Can't be more than 8 characters. Received: %s", rgbhexstr, ) return False diff --git a/homeassistant/components/xiaomi_aqara/sensor.py b/homeassistant/components/xiaomi_aqara/sensor.py index 5deed77d775..e114adaa8c4 100644 --- a/homeassistant/components/xiaomi_aqara/sensor.py +++ b/homeassistant/components/xiaomi_aqara/sensor.py @@ -14,9 +14,9 @@ from homeassistant.const import ( ATTR_BATTERY_LEVEL, LIGHT_LUX, PERCENTAGE, - POWER_WATT, - PRESSURE_HPA, - TEMP_CELSIUS, + UnitOfPower, + UnitOfPressure, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -29,7 +29,7 @@ _LOGGER = logging.getLogger(__name__) SENSOR_TYPES: dict[str, SensorEntityDescription] = { "temperature": SensorEntityDescription( key="temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), @@ -42,7 +42,6 @@ SENSOR_TYPES: dict[str, SensorEntityDescription] = { "illumination": SensorEntityDescription( key="illumination", native_unit_of_measurement="lm", - device_class=SensorDeviceClass.ILLUMINANCE, state_class=SensorStateClass.MEASUREMENT, ), "lux": SensorEntityDescription( @@ -53,7 +52,7 @@ SENSOR_TYPES: dict[str, SensorEntityDescription] = { ), "pressure": SensorEntityDescription( key="pressure", - native_unit_of_measurement=PRESSURE_HPA, + native_unit_of_measurement=UnitOfPressure.HPA, device_class=SensorDeviceClass.PRESSURE, state_class=SensorStateClass.MEASUREMENT, ), @@ -65,7 +64,7 @@ SENSOR_TYPES: dict[str, SensorEntityDescription] = { ), "load_power": SensorEntityDescription( key="load_power", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, ), diff --git a/homeassistant/components/xiaomi_aqara/translations/sk.json b/homeassistant/components/xiaomi_aqara/translations/sk.json index f6fbe6ed65c..6c4aced12c5 100644 --- a/homeassistant/components/xiaomi_aqara/translations/sk.json +++ b/homeassistant/components/xiaomi_aqara/translations/sk.json @@ -2,9 +2,11 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", - "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" + "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", + "not_xiaomi_aqara": "Nie je to Xiaomi Aqara Gateway, objaven\u00e9 zariadenie sa nezhoduje so zn\u00e1mymi br\u00e1nami" }, "error": { + "discovery_error": "Nepodarilo sa objavi\u0165 br\u00e1nu Xiaomi Aqara, sk\u00faste ako rozhranie pou\u017ei\u0165 IP adresu zariadenia s HomeAssistant", "invalid_host": "Neplatn\u00fd n\u00e1zov hostite\u013ea alebo IP adresa , pozrite si https://www.home-assistant.io/integrations/xiaomi_aqara/#connection-problem", "invalid_interface": "Neplatn\u00e9 sie\u0165ov\u00e9 rozhranie", "invalid_key": "Neplatn\u00fd k\u013e\u00fa\u010d br\u00e1ny", @@ -15,16 +17,24 @@ "select": { "data": { "select_ip": "IP adresa" - } + }, + "description": "Vyberte br\u00e1nu Xiaomi Aqara, ktor\u00fa chcete pripoji\u0165" }, "settings": { + "data": { + "key": "K\u013e\u00fa\u010d va\u0161ej br\u00e1ny", + "name": "N\u00e1zov br\u00e1ny" + }, + "description": "K\u013e\u00fa\u010d (heslo) je mo\u017en\u00e9 z\u00edska\u0165 pomocou tohto n\u00e1vodu: https://www.domoticz.com/wiki/Xiaomi_Gateway_(Aqara)#Adding_the_Xiaomi_Gateway_to_Domoticz. Ak k\u013e\u00fa\u010d nie je poskytnut\u00fd, bud\u00fa pr\u00edstupn\u00e9 iba sn\u00edma\u010de", "title": "Volite\u013en\u00e9 nastavenia" }, "user": { "data": { "host": "IP adresa (volite\u013en\u00e1)", + "interface": "Sie\u0165ov\u00e9 rozhranie, ktor\u00e9 sa m\u00e1 pou\u017ei\u0165", "mac": "Adresa Mac (volite\u013en\u00e9)" - } + }, + "description": "Ak s\u00fa adresy IP a MAC ponechan\u00e9 pr\u00e1zdne, pou\u017eije sa automatick\u00e9 zis\u0165ovanie" } } } diff --git a/homeassistant/components/xiaomi_ble/__init__.py b/homeassistant/components/xiaomi_ble/__init__.py index 201e4f14582..8ed18c045d4 100644 --- a/homeassistant/components/xiaomi_ble/__init__.py +++ b/homeassistant/components/xiaomi_ble/__init__.py @@ -12,7 +12,7 @@ from homeassistant.components.bluetooth import ( BluetoothServiceInfoBleak, async_ble_device_from_address, ) -from homeassistant.components.bluetooth.active_update_coordinator import ( +from homeassistant.components.bluetooth.active_update_processor import ( ActiveBluetoothProcessorCoordinator, ) from homeassistant.config_entries import ConfigEntry diff --git a/homeassistant/components/xiaomi_ble/sensor.py b/homeassistant/components/xiaomi_ble/sensor.py index 831b5d0910b..334099cdbb6 100644 --- a/homeassistant/components/xiaomi_ble/sensor.py +++ b/homeassistant/components/xiaomi_ble/sensor.py @@ -21,12 +21,12 @@ from homeassistant.components.sensor import ( from homeassistant.const import ( CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER, CONDUCTIVITY, - ELECTRIC_POTENTIAL_VOLT, LIGHT_LUX, PERCENTAGE, - PRESSURE_MBAR, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, - TEMP_CELSIUS, + UnitOfElectricPotential, + UnitOfPressure, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory @@ -79,7 +79,7 @@ SENSOR_DESCRIPTIONS = { (DeviceClass.PRESSURE, Units.PRESSURE_MBAR): SensorEntityDescription( key=f"{DeviceClass.PRESSURE}_{Units.PRESSURE_MBAR}", device_class=SensorDeviceClass.PRESSURE, - native_unit_of_measurement=PRESSURE_MBAR, + native_unit_of_measurement=UnitOfPressure.MBAR, state_class=SensorStateClass.MEASUREMENT, ), ( @@ -96,13 +96,13 @@ SENSOR_DESCRIPTIONS = { (DeviceClass.TEMPERATURE, Units.TEMP_CELSIUS): SensorEntityDescription( key=f"{DeviceClass.TEMPERATURE}_{Units.TEMP_CELSIUS}", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, ), (DeviceClass.VOLTAGE, Units.ELECTRIC_POTENTIAL_VOLT): SensorEntityDescription( key=f"{DeviceClass.VOLTAGE}_{Units.ELECTRIC_POTENTIAL_VOLT}", device_class=SensorDeviceClass.VOLTAGE, - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, ), diff --git a/homeassistant/components/xiaomi_ble/translations/en.json b/homeassistant/components/xiaomi_ble/translations/en.json index 2cb77dd2c07..a66ee1d2f00 100644 --- a/homeassistant/components/xiaomi_ble/translations/en.json +++ b/homeassistant/components/xiaomi_ble/translations/en.json @@ -14,7 +14,7 @@ "flow_title": "{name}", "step": { "bluetooth_confirm": { - "description": "Do you want to setup {name}?" + "description": "Do you want to set up {name}?" }, "confirm_slow": { "description": "There hasn't been a broadcast from this device in the last minute so we aren't sure if this device uses encryption or not. This may be because the device uses a slow broadcast interval. Confirm to add this device anyway, then the next time a broadcast is received you will be prompted to enter its bindkey if it's needed." @@ -35,7 +35,7 @@ "data": { "address": "Device" }, - "description": "Choose a device to setup" + "description": "Choose a device to set up" } } } diff --git a/homeassistant/components/xiaomi_ble/translations/it.json b/homeassistant/components/xiaomi_ble/translations/it.json index 7d6837d35ee..54ae2bf9b7a 100644 --- a/homeassistant/components/xiaomi_ble/translations/it.json +++ b/homeassistant/components/xiaomi_ble/translations/it.json @@ -35,7 +35,7 @@ "data": { "address": "Dispositivo" }, - "description": "Seleziona un dispositivo da configurare" + "description": "Scegli un dispositivo da configurare" } } } diff --git a/homeassistant/components/xiaomi_ble/translations/ko.json b/homeassistant/components/xiaomi_ble/translations/ko.json new file mode 100644 index 00000000000..0cfe3268874 --- /dev/null +++ b/homeassistant/components/xiaomi_ble/translations/ko.json @@ -0,0 +1,10 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", + "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/xiaomi_ble/translations/no.json b/homeassistant/components/xiaomi_ble/translations/no.json index 3c8670c3c94..c157e1aa6d4 100644 --- a/homeassistant/components/xiaomi_ble/translations/no.json +++ b/homeassistant/components/xiaomi_ble/translations/no.json @@ -14,7 +14,7 @@ "flow_title": "{name}", "step": { "bluetooth_confirm": { - "description": "Vil du konfigurere {name}?" + "description": "Vil du sette opp {name} ?" }, "confirm_slow": { "description": "Det har ikke v\u00e6rt en sending fra denne enheten det siste minuttet, s\u00e5 vi er ikke sikre p\u00e5 om denne enheten bruker kryptering eller ikke. Dette kan skyldes at enheten bruker et tregt kringkastingsintervall. Bekreft \u00e5 legge til denne enheten uansett, s\u00e5 neste gang en kringkasting mottas, vil du bli bedt om \u00e5 angi bindn\u00f8kkelen hvis det er n\u00f8dvendig." diff --git a/homeassistant/components/xiaomi_ble/translations/pt.json b/homeassistant/components/xiaomi_ble/translations/pt.json index e7b9aaefbed..cd049bbd237 100644 --- a/homeassistant/components/xiaomi_ble/translations/pt.json +++ b/homeassistant/components/xiaomi_ble/translations/pt.json @@ -1,5 +1,11 @@ { "config": { + "abort": { + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", + "already_in_progress": "O processo de configura\u00e7\u00e3o j\u00e1 est\u00e1 a decorrer", + "no_devices_found": "Nenhum dispositivo encontrado na rede", + "reauth_successful": "Reautentica\u00e7\u00e3o bem sucedida" + }, "error": { "decryption_failed": "A chave de liga\u00e7\u00e3o fornecida n\u00e3o funcionou, os dados do sensor n\u00e3o puderam ser descriptografados. Por favor verifique e tente novamente.", "expected_24_characters": "Espera-se uma chave de liga\u00e7\u00e3o hexadecimal de 24 caracteres.", diff --git a/homeassistant/components/xiaomi_ble/translations/sk.json b/homeassistant/components/xiaomi_ble/translations/sk.json index 431454357f8..21684d82ec8 100644 --- a/homeassistant/components/xiaomi_ble/translations/sk.json +++ b/homeassistant/components/xiaomi_ble/translations/sk.json @@ -6,11 +6,31 @@ "no_devices_found": "V sieti sa nena\u0161li \u017eiadne zariadenia", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, + "error": { + "decryption_failed": "Poskytnut\u00fd bindkey k\u013e\u00fa\u010d nefungoval, \u00fadaje sn\u00edma\u010da sa nepodarilo de\u0161ifrova\u0165. Skontrolujte to a sk\u00faste to znova.", + "expected_24_characters": "O\u010dak\u00e1van\u00fd 24-znakov\u00fd hexadecim\u00e1lny bindkey.", + "expected_32_characters": "O\u010dak\u00e1van\u00fd 32-znakov\u00fd hexadecim\u00e1lny bindkey." + }, "flow_title": "{name}", "step": { "bluetooth_confirm": { "description": "Chcete nastavi\u0165 {name}?" }, + "confirm_slow": { + "description": "V poslednej min\u00fate sa z tohto zariadenia nevysielalo, tak\u017ee si nie sme ist\u00ed, \u010di toto zariadenie pou\u017e\u00edva \u0161ifrovanie alebo nie. M\u00f4\u017ee to by\u0165 sp\u00f4soben\u00e9 t\u00fdm, \u017ee zariadenie pou\u017e\u00edva pomal\u00fd interval vysielania. Napriek tomu potvr\u010fte, \u017ee chcete toto zariadenie prida\u0165, potom pri \u010fal\u0161om prijat\u00ed vysielania budete vyzvan\u00ed, aby ste zadali jeho k\u013e\u00fa\u010d v\u00e4zby, ak je to potrebn\u00e9." + }, + "get_encryption_key_4_5": { + "data": { + "bindkey": "Bindkey" + }, + "description": "D\u00e1ta sn\u00edma\u010da vysielan\u00e9 sn\u00edma\u010dom s\u00fa \u0161ifrovan\u00e9. Aby sme ho de\u0161ifrovali, potrebujeme 32-znakov\u00fd hexadecim\u00e1lny bindkey k\u013e\u00fa\u010d." + }, + "get_encryption_key_legacy": { + "data": { + "bindkey": "Bindkey" + }, + "description": "D\u00e1ta sn\u00edma\u010da vysielan\u00e9 sn\u00edma\u010dom s\u00fa \u0161ifrovan\u00e9. Aby sme ho de\u0161ifrovali, potrebujeme 24-znakov\u00fd hexadecim\u00e1lny bindkey k\u013e\u00fa\u010d." + }, "user": { "data": { "address": "Zaradenie" diff --git a/homeassistant/components/xiaomi_ble/translations/zh-Hant.json b/homeassistant/components/xiaomi_ble/translations/zh-Hant.json index 6faa7422e56..4b259be5ede 100644 --- a/homeassistant/components/xiaomi_ble/translations/zh-Hant.json +++ b/homeassistant/components/xiaomi_ble/translations/zh-Hant.json @@ -7,7 +7,7 @@ "reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f" }, "error": { - "decryption_failed": "\u6240\u63d0\u4f9b\u7684\u7d81\u5b9a\u78bc\u7121\u6cd5\u4f7f\u7528\u3001\u50b3\u611f\u5668\u8cc7\u6599\u7121\u6cd5\u89e3\u5bc6\u3002\u8acb\u4fee\u6b63\u5f8c\u3001\u518d\u8a66\u4e00\u6b21\u3002", + "decryption_failed": "\u6240\u63d0\u4f9b\u7684\u7d81\u5b9a\u78bc\u7121\u6cd5\u4f7f\u7528\u3001\u611f\u6e2c\u5668\u8cc7\u6599\u7121\u6cd5\u89e3\u5bc6\u3002\u8acb\u4fee\u6b63\u5f8c\u3001\u518d\u8a66\u4e00\u6b21\u3002", "expected_24_characters": "\u9700\u8981 24 \u500b\u5b57\u5143\u4e4b\u5341\u516d\u9032\u4f4d\u7d81\u5b9a\u78bc\u3002", "expected_32_characters": "\u9700\u8981 32 \u500b\u5b57\u5143\u4e4b\u5341\u516d\u9032\u4f4d\u7d81\u5b9a\u78bc\u3002" }, @@ -23,13 +23,13 @@ "data": { "bindkey": "\u7d81\u5b9a\u78bc" }, - "description": "\u7531\u50b3\u611f\u5668\u6240\u5ee3\u64ad\u4e4b\u8cc7\u6599\u70ba\u52a0\u5bc6\u8cc7\u6599\u3002\u82e5\u8981\u89e3\u78bc\u3001\u9700\u8981 32 \u500b\u5b57\u5143\u4e4b\u7d81\u5b9a\u78bc\u3002" + "description": "\u7531\u611f\u6e2c\u5668\u6240\u5ee3\u64ad\u4e4b\u8cc7\u6599\u70ba\u52a0\u5bc6\u8cc7\u6599\u3002\u82e5\u8981\u89e3\u78bc\u3001\u9700\u8981 32 \u500b\u5b57\u5143\u4e4b\u7d81\u5b9a\u78bc\u3002" }, "get_encryption_key_legacy": { "data": { "bindkey": "\u7d81\u5b9a\u78bc" }, - "description": "\u7531\u50b3\u611f\u5668\u6240\u5ee3\u64ad\u4e4b\u8cc7\u6599\u70ba\u52a0\u5bc6\u8cc7\u6599\u3002\u82e5\u8981\u89e3\u78bc\u3001\u9700\u8981 24 \u500b\u5b57\u5143\u4e4b\u7d81\u5b9a\u78bc\u3002" + "description": "\u7531\u611f\u6e2c\u5668\u6240\u5ee3\u64ad\u4e4b\u8cc7\u6599\u70ba\u52a0\u5bc6\u8cc7\u6599\u3002\u82e5\u8981\u89e3\u78bc\u3001\u9700\u8981 24 \u500b\u5b57\u5143\u4e4b\u7d81\u5b9a\u78bc\u3002" }, "user": { "data": { diff --git a/homeassistant/components/xiaomi_miio/__init__.py b/homeassistant/components/xiaomi_miio/__init__.py index d3a407d529e..6e8d0831445 100644 --- a/homeassistant/components/xiaomi_miio/__init__.py +++ b/homeassistant/components/xiaomi_miio/__init__.py @@ -153,9 +153,11 @@ def get_platforms(config_entry): if model.startswith(air_monitor_model): return AIR_MONITOR_PLATFORMS _LOGGER.error( - "Unsupported device found! Please create an issue at " - "https://github.com/syssi/xiaomi_airpurifier/issues " - "and provide the following data: %s", + ( + "Unsupported device found! Please create an issue at " + "https://github.com/syssi/xiaomi_airpurifier/issues " + "and provide the following data: %s" + ), model, ) return [] @@ -336,9 +338,11 @@ async def async_create_miio_device_and_coordinator( device = Fan(host, token, model=model) else: _LOGGER.error( - "Unsupported device found! Please create an issue at " - "https://github.com/syssi/xiaomi_airpurifier/issues " - "and provide the following data: %s", + ( + "Unsupported device found! Please create an issue at " + "https://github.com/syssi/xiaomi_airpurifier/issues " + "and provide the following data: %s" + ), model, ) return diff --git a/homeassistant/components/xiaomi_miio/gateway.py b/homeassistant/components/xiaomi_miio/gateway.py index 6d0984a9aeb..655a04a4340 100644 --- a/homeassistant/components/xiaomi_miio/gateway.py +++ b/homeassistant/components/xiaomi_miio/gateway.py @@ -92,8 +92,10 @@ class ConnectXiaomiGateway: self._gateway_device.discover_devices() except DeviceException as error: _LOGGER.info( - "DeviceException during getting subdevices of xiaomi gateway" - " with host %s, trying cloud to obtain subdevices: %s", + ( + "DeviceException during getting subdevices of xiaomi gateway" + " with host %s, trying cloud to obtain subdevices: %s" + ), self._host, error, ) @@ -114,8 +116,10 @@ class ConnectXiaomiGateway: miio_cloud = MiCloud(self._cloud_username, self._cloud_password) if not miio_cloud.login(): raise SetupException( - "Failed to login to Xiaomi Miio Cloud during setup of Xiaomi" - f" gateway with host {self._host}", + ( + "Failed to login to Xiaomi Miio Cloud during setup of" + f" Xiaomi gateway with host {self._host}" + ), ) devices_raw = miio_cloud.get_devices(self._cloud_country) self._gateway_device.get_devices_from_dict(devices_raw) @@ -125,7 +129,8 @@ class ConnectXiaomiGateway: ) from error except DeviceException as error: raise SetupException( - f"DeviceException during setup of xiaomi gateway with host {self._host}" + "DeviceException during setup of xiaomi gateway with host" + f" {self._host}" ) from error diff --git a/homeassistant/components/xiaomi_miio/light.py b/homeassistant/components/xiaomi_miio/light.py index 49554311b91..d691c44a38a 100644 --- a/homeassistant/components/xiaomi_miio/light.py +++ b/homeassistant/components/xiaomi_miio/light.py @@ -198,9 +198,11 @@ async def async_setup_entry( hass.data[DATA_KEY][host] = entity else: _LOGGER.error( - "Unsupported device found! Please create an issue at " - "https://github.com/syssi/philipslight/issues " - "and provide the following data: %s", + ( + "Unsupported device found! Please create an issue at " + "https://github.com/syssi/philipslight/issues " + "and provide the following data: %s" + ), model, ) return @@ -445,8 +447,10 @@ class XiaomiPhilipsBulb(XiaomiPhilipsGenericLight): if ATTR_BRIGHTNESS in kwargs and ATTR_COLOR_TEMP in kwargs: _LOGGER.debug( - "Setting brightness and color temperature: " - "%s %s%%, %s mireds, %s%% cct", + ( + "Setting brightness and color temperature: " + "%s %s%%, %s mireds, %s%% cct" + ), brightness, percent_brightness, color_temp, @@ -843,8 +847,10 @@ class XiaomiPhilipsMoonlightLamp(XiaomiPhilipsBulb): elif ATTR_BRIGHTNESS in kwargs and ATTR_COLOR_TEMP in kwargs: _LOGGER.debug( - "Setting brightness and color temperature: " - "%s %s%%, %s mireds, %s%% cct", + ( + "Setting brightness and color temperature: " + "%s %s%%, %s mireds, %s%% cct" + ), brightness, percent_brightness, color_temp, diff --git a/homeassistant/components/xiaomi_miio/number.py b/homeassistant/components/xiaomi_miio/number.py index 869379530fc..1961448a138 100644 --- a/homeassistant/components/xiaomi_miio/number.py +++ b/homeassistant/components/xiaomi_miio/number.py @@ -12,7 +12,7 @@ from homeassistant.components.number import ( NumberEntityDescription, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_MODEL, DEGREE, REVOLUTIONS_PER_MINUTE, TIME_MINUTES +from homeassistant.const import CONF_MODEL, DEGREE, REVOLUTIONS_PER_MINUTE, UnitOfTime from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import entity_registry as er from homeassistant.helpers.entity import EntityCategory @@ -193,7 +193,7 @@ NUMBER_TYPES = { key=ATTR_DELAY_OFF_COUNTDOWN, name="Delay off countdown", icon="mdi:fan-off", - native_unit_of_measurement=TIME_MINUTES, + native_unit_of_measurement=UnitOfTime.MINUTES, native_min_value=0, native_max_value=480, native_step=1, diff --git a/homeassistant/components/xiaomi_miio/select.py b/homeassistant/components/xiaomi_miio/select.py index 87cafe3c58a..4d3c8bf0c85 100644 --- a/homeassistant/components/xiaomi_miio/select.py +++ b/homeassistant/components/xiaomi_miio/select.py @@ -156,7 +156,7 @@ SELECTOR_TYPES = ( set_method="set_display_orientation", set_method_error_message="Setting the display orientation failed.", icon="mdi:tablet", - device_class="xiaomi_miio__display_orientation", + translation_key="display_orientation", options=["forward", "left", "right"], entity_category=EntityCategory.CONFIG, ), @@ -167,7 +167,7 @@ SELECTOR_TYPES = ( set_method="set_led_brightness", set_method_error_message="Setting the led brightness failed.", icon="mdi:brightness-6", - device_class="xiaomi_miio__led_brightness", + translation_key="led_brightness", options=["bright", "dim", "off"], entity_category=EntityCategory.CONFIG, ), @@ -178,7 +178,7 @@ SELECTOR_TYPES = ( set_method="set_ptc_level", set_method_error_message="Setting the ptc level failed.", icon="mdi:fire-circle", - device_class="xiaomi_miio__ptc_level", + translation_key="ptc_level", options=["low", "medium", "high"], entity_category=EntityCategory.CONFIG, ), diff --git a/homeassistant/components/xiaomi_miio/sensor.py b/homeassistant/components/xiaomi_miio/sensor.py index a83f3ef528d..13344e5044a 100644 --- a/homeassistant/components/xiaomi_miio/sensor.py +++ b/homeassistant/components/xiaomi_miio/sensor.py @@ -33,14 +33,12 @@ from homeassistant.const import ( CONF_TOKEN, LIGHT_LUX, PERCENTAGE, - POWER_WATT, - PRESSURE_HPA, REVOLUTIONS_PER_MINUTE, - TEMP_CELSIUS, - TIME_DAYS, - TIME_HOURS, - TIME_SECONDS, - VOLUME_CUBIC_METERS, + UnitOfPower, + UnitOfPressure, + UnitOfTemperature, + UnitOfTime, + UnitOfVolume, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import EntityCategory @@ -162,7 +160,7 @@ SENSOR_TYPES = { ATTR_TEMPERATURE: XiaomiMiioSensorDescription( key=ATTR_TEMPERATURE, name="Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, ), @@ -176,14 +174,14 @@ SENSOR_TYPES = { ATTR_PRESSURE: XiaomiMiioSensorDescription( key=ATTR_PRESSURE, name="Pressure", - native_unit_of_measurement=PRESSURE_HPA, - device_class=SensorDeviceClass.PRESSURE, + native_unit_of_measurement=UnitOfPressure.HPA, + device_class=SensorDeviceClass.ATMOSPHERIC_PRESSURE, state_class=SensorStateClass.MEASUREMENT, ), ATTR_LOAD_POWER: XiaomiMiioSensorDescription( key=ATTR_LOAD_POWER, name="Load power", - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, ), ATTR_WATER_LEVEL: XiaomiMiioSensorDescription( @@ -237,7 +235,7 @@ SENSOR_TYPES = { ATTR_USE_TIME: XiaomiMiioSensorDescription( key=ATTR_USE_TIME, name="Use time", - native_unit_of_measurement=TIME_SECONDS, + native_unit_of_measurement=UnitOfTime.SECONDS, icon="mdi:progress-clock", device_class=SensorDeviceClass.DURATION, state_class=SensorStateClass.TOTAL_INCREASING, @@ -248,7 +246,6 @@ SENSOR_TYPES = { key=ATTR_ILLUMINANCE, name="Illuminance", native_unit_of_measurement=UNIT_LUMEN, - device_class=SensorDeviceClass.ILLUMINANCE, state_class=SensorStateClass.MEASUREMENT, ), ATTR_ILLUMINANCE_LUX: XiaomiMiioSensorDescription( @@ -304,7 +301,7 @@ SENSOR_TYPES = { ATTR_FILTER_USE: XiaomiMiioSensorDescription( key=ATTR_FILTER_HOURS_USED, name="Filter use", - native_unit_of_measurement=TIME_HOURS, + native_unit_of_measurement=UnitOfTime.HOURS, icon="mdi:clock-outline", device_class=SensorDeviceClass.DURATION, state_class=SensorStateClass.MEASUREMENT, @@ -313,7 +310,7 @@ SENSOR_TYPES = { ATTR_FILTER_LEFT_TIME: XiaomiMiioSensorDescription( key=ATTR_FILTER_LEFT_TIME, name="Filter time left", - native_unit_of_measurement=TIME_DAYS, + native_unit_of_measurement=UnitOfTime.DAYS, icon="mdi:clock-outline", device_class=SensorDeviceClass.DURATION, state_class=SensorStateClass.MEASUREMENT, @@ -331,7 +328,7 @@ SENSOR_TYPES = { ATTR_DUST_FILTER_LIFE_REMAINING_DAYS: XiaomiMiioSensorDescription( key=ATTR_DUST_FILTER_LIFE_REMAINING_DAYS, name="Dust filter life remaining days", - native_unit_of_measurement=TIME_DAYS, + native_unit_of_measurement=UnitOfTime.DAYS, icon="mdi:clock-outline", device_class=SensorDeviceClass.DURATION, state_class=SensorStateClass.MEASUREMENT, @@ -349,7 +346,7 @@ SENSOR_TYPES = { ATTR_UPPER_FILTER_LIFE_REMAINING_DAYS: XiaomiMiioSensorDescription( key=ATTR_UPPER_FILTER_LIFE_REMAINING_DAYS, name="Upper filter life remaining days", - native_unit_of_measurement=TIME_DAYS, + native_unit_of_measurement=UnitOfTime.DAYS, icon="mdi:clock-outline", device_class=SensorDeviceClass.DURATION, state_class=SensorStateClass.MEASUREMENT, @@ -365,8 +362,8 @@ SENSOR_TYPES = { ATTR_PURIFY_VOLUME: XiaomiMiioSensorDescription( key=ATTR_PURIFY_VOLUME, name="Purify volume", - native_unit_of_measurement=VOLUME_CUBIC_METERS, - device_class=SensorDeviceClass.GAS, + native_unit_of_measurement=UnitOfVolume.CUBIC_METERS, + device_class=SensorDeviceClass.VOLUME, state_class=SensorStateClass.TOTAL_INCREASING, entity_registry_enabled_default=False, entity_category=EntityCategory.DIAGNOSTIC, @@ -608,7 +605,7 @@ VACUUM_SENSORS = { entity_category=EntityCategory.DIAGNOSTIC, ), f"last_clean_{ATTR_LAST_CLEAN_TIME}": XiaomiMiioSensorDescription( - native_unit_of_measurement=TIME_SECONDS, + native_unit_of_measurement=UnitOfTime.SECONDS, icon="mdi:timer-sand", device_class=SensorDeviceClass.DURATION, key=ATTR_LAST_CLEAN_TIME, @@ -625,7 +622,7 @@ VACUUM_SENSORS = { entity_category=EntityCategory.DIAGNOSTIC, ), f"current_{ATTR_STATUS_CLEAN_TIME}": XiaomiMiioSensorDescription( - native_unit_of_measurement=TIME_SECONDS, + native_unit_of_measurement=UnitOfTime.SECONDS, icon="mdi:timer-sand", device_class=SensorDeviceClass.DURATION, key=ATTR_STATUS_CLEAN_TIME, @@ -642,7 +639,7 @@ VACUUM_SENSORS = { name="Current clean area", ), f"clean_history_{ATTR_CLEAN_HISTORY_TOTAL_DURATION}": XiaomiMiioSensorDescription( - native_unit_of_measurement=TIME_SECONDS, + native_unit_of_measurement=UnitOfTime.SECONDS, device_class=SensorDeviceClass.DURATION, icon="mdi:timer-sand", key=ATTR_CLEAN_HISTORY_TOTAL_DURATION, @@ -681,7 +678,7 @@ VACUUM_SENSORS = { entity_category=EntityCategory.DIAGNOSTIC, ), f"consumable_{ATTR_CONSUMABLE_STATUS_MAIN_BRUSH_LEFT}": XiaomiMiioSensorDescription( - native_unit_of_measurement=TIME_SECONDS, + native_unit_of_measurement=UnitOfTime.SECONDS, icon="mdi:brush", device_class=SensorDeviceClass.DURATION, key=ATTR_CONSUMABLE_STATUS_MAIN_BRUSH_LEFT, @@ -690,7 +687,7 @@ VACUUM_SENSORS = { entity_category=EntityCategory.DIAGNOSTIC, ), f"consumable_{ATTR_CONSUMABLE_STATUS_SIDE_BRUSH_LEFT}": XiaomiMiioSensorDescription( - native_unit_of_measurement=TIME_SECONDS, + native_unit_of_measurement=UnitOfTime.SECONDS, icon="mdi:brush", device_class=SensorDeviceClass.DURATION, key=ATTR_CONSUMABLE_STATUS_SIDE_BRUSH_LEFT, @@ -699,7 +696,7 @@ VACUUM_SENSORS = { entity_category=EntityCategory.DIAGNOSTIC, ), f"consumable_{ATTR_CONSUMABLE_STATUS_FILTER_LEFT}": XiaomiMiioSensorDescription( - native_unit_of_measurement=TIME_SECONDS, + native_unit_of_measurement=UnitOfTime.SECONDS, icon="mdi:air-filter", device_class=SensorDeviceClass.DURATION, key=ATTR_CONSUMABLE_STATUS_FILTER_LEFT, @@ -708,7 +705,7 @@ VACUUM_SENSORS = { entity_category=EntityCategory.DIAGNOSTIC, ), f"consumable_{ATTR_CONSUMABLE_STATUS_SENSOR_DIRTY_LEFT}": XiaomiMiioSensorDescription( - native_unit_of_measurement=TIME_SECONDS, + native_unit_of_measurement=UnitOfTime.SECONDS, icon="mdi:eye-outline", device_class=SensorDeviceClass.DURATION, key=ATTR_CONSUMABLE_STATUS_SENSOR_DIRTY_LEFT, diff --git a/homeassistant/components/xiaomi_miio/strings.json b/homeassistant/components/xiaomi_miio/strings.json index ea9e1712697..bf722c3a1e5 100644 --- a/homeassistant/components/xiaomi_miio/strings.json +++ b/homeassistant/components/xiaomi_miio/strings.json @@ -4,14 +4,14 @@ "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]", "already_configured": "[%key:common::config_flow::abort::already_configured_device%]", "already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]", - "incomplete_info": "Incomplete information to setup device, no host or token supplied.", + "incomplete_info": "Incomplete information to set up device, no host or token supplied.", "not_xiaomi_miio": "Device is not (yet) supported by Xiaomi Miio.", "unknown": "[%key:common::config_flow::error::unknown%]" }, "error": { "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", "wrong_token": "Checksum error, wrong token", - "unknown_device": "The device model is not known, not able to setup the device using config flow.", + "unknown_device": "The device model is not known, not able to set up the device using config flow.", "cloud_no_devices": "No devices found in this Xiaomi Miio cloud account.", "cloud_credentials_incomplete": "Cloud credentials incomplete, please fill in username, password and country", "cloud_login_error": "Could not login to Xiaomi Miio Cloud, check the credentials." @@ -35,7 +35,7 @@ "data": { "select_device": "Miio device" }, - "description": "Select the Xiaomi Miio device to setup." + "description": "Select the Xiaomi Miio device to set up." }, "manual": { "data": { @@ -62,5 +62,30 @@ } } } + }, + "entity": { + "select": { + "led_brightness": { + "state": { + "bright": "Bright", + "dim": "Dim", + "off": "Off" + } + }, + "display_orientation": { + "state": { + "forward": "Forward", + "left": "Left", + "right": "Right" + } + }, + "ptc_level": { + "state": { + "low": "Low", + "medium": "Medium", + "high": "High" + } + } + } } } diff --git a/homeassistant/components/xiaomi_miio/strings.select.json b/homeassistant/components/xiaomi_miio/strings.select.json deleted file mode 100644 index 38ee8b1aa07..00000000000 --- a/homeassistant/components/xiaomi_miio/strings.select.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "state": { - "xiaomi_miio__led_brightness": { - "bright": "Bright", - "dim": "Dim", - "off": "Off" - }, - "xiaomi_miio__display_orientation": { - "forward": "Forward", - "left": "Left", - "right": "Right" - }, - "xiaomi_miio__ptc_level": { - "low": "Low", - "medium": "Medium", - "high": "High" - } - } -} diff --git a/homeassistant/components/xiaomi_miio/switch.py b/homeassistant/components/xiaomi_miio/switch.py index f2b7b071923..98340087401 100644 --- a/homeassistant/components/xiaomi_miio/switch.py +++ b/homeassistant/components/xiaomi_miio/switch.py @@ -470,9 +470,11 @@ async def async_setup_other_entry(hass, config_entry, async_add_entities): hass.data[DATA_KEY][host] = device else: _LOGGER.error( - "Unsupported device found! Please create an issue at " - "https://github.com/rytilahti/python-miio/issues " - "and provide the following data: %s", + ( + "Unsupported device found! Please create an issue at " + "https://github.com/rytilahti/python-miio/issues " + "and provide the following data: %s" + ), model, ) diff --git a/homeassistant/components/xiaomi_miio/translations/ca.json b/homeassistant/components/xiaomi_miio/translations/ca.json index ff1297080c3..e6f71c33d45 100644 --- a/homeassistant/components/xiaomi_miio/translations/ca.json +++ b/homeassistant/components/xiaomi_miio/translations/ca.json @@ -51,6 +51,31 @@ } } }, + "entity": { + "select": { + "display_orientation": { + "state": { + "forward": "Endavant", + "left": "Esquerra", + "right": "Dreta" + } + }, + "led_brightness": { + "state": { + "bright": "Llumin\u00f3s", + "dim": "Atenuat", + "off": "OFF" + } + }, + "ptc_level": { + "state": { + "high": "Alt", + "low": "Baix", + "medium": "Mitj\u00e0" + } + } + } + }, "options": { "error": { "cloud_credentials_incomplete": "Credencials del n\u00favol incompletes, introdueix el nom d'usuari, la contrasenya i el pa\u00eds" diff --git a/homeassistant/components/xiaomi_miio/translations/de.json b/homeassistant/components/xiaomi_miio/translations/de.json index aa5ad47dd4c..4f30c9b256a 100644 --- a/homeassistant/components/xiaomi_miio/translations/de.json +++ b/homeassistant/components/xiaomi_miio/translations/de.json @@ -37,7 +37,7 @@ "host": "IP-Adresse", "token": "API-Token" }, - "description": "Du ben\u00f6tigst den 32 Zeichen langen API-Token, siehe https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token f\u00fcr Anweisungen. Bitte beachte, dass sich dieser API-Token von dem Schl\u00fcssel unterscheidet, der von der Xiaomi Aqara-Integration verwendet wird." + "description": "Du ben\u00f6tigst den 32 Zeichen langen API-Token, siehe https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token f\u00fcr Anweisungen. Bitte beachte, dass sich dieser API-Token von dem Schl\u00fcssel unterscheidet, der von der Xiaomi Aqara Integration verwendet wird." }, "reauth_confirm": { "description": "Die Xiaomi Miio-Integration muss dein Konto neu authentifizieren, um die Token zu aktualisieren oder fehlende Cloud-Anmeldedaten hinzuzuf\u00fcgen.", @@ -51,6 +51,31 @@ } } }, + "entity": { + "select": { + "display_orientation": { + "state": { + "forward": "Vorw\u00e4rts", + "left": "Links", + "right": "Rechts" + } + }, + "led_brightness": { + "state": { + "bright": "Hell", + "dim": "Abgeblendet", + "off": "Aus" + } + }, + "ptc_level": { + "state": { + "high": "Hoch", + "low": "Niedrig", + "medium": "Mittel" + } + } + } + }, "options": { "error": { "cloud_credentials_incomplete": "Cloud-Anmeldeinformationen unvollst\u00e4ndig, bitte Benutzernamen, Passwort und Land eingeben" diff --git a/homeassistant/components/xiaomi_miio/translations/el.json b/homeassistant/components/xiaomi_miio/translations/el.json index 779d71d14ff..da3ddb4013c 100644 --- a/homeassistant/components/xiaomi_miio/translations/el.json +++ b/homeassistant/components/xiaomi_miio/translations/el.json @@ -51,6 +51,31 @@ } } }, + "entity": { + "select": { + "display_orientation": { + "state": { + "forward": "\u0395\u03bc\u03c0\u03c1\u03cc\u03c2", + "left": "\u0391\u03c1\u03b9\u03c3\u03c4\u03b5\u03c1\u03ac", + "right": "\u0394\u03b5\u03be\u03b9\u03ac" + } + }, + "led_brightness": { + "state": { + "bright": "\u03a6\u03c9\u03c4\u03b5\u03b9\u03bd\u03cc", + "dim": "\u0391\u03bc\u03c5\u03b4\u03c1\u03cc", + "off": "\u0391\u03bd\u03b5\u03bd\u03b5\u03c1\u03b3\u03cc" + } + }, + "ptc_level": { + "state": { + "high": "\u03a5\u03c8\u03b7\u03bb\u03cc", + "low": "\u03a7\u03b1\u03bc\u03b7\u03bb\u03cc", + "medium": "\u039c\u03b5\u03c3\u03b1\u03af\u03bf" + } + } + } + }, "options": { "error": { "cloud_credentials_incomplete": "\u03a4\u03b1 \u03b4\u03b9\u03b1\u03c0\u03b9\u03c3\u03c4\u03b5\u03c5\u03c4\u03ae\u03c1\u03b9\u03b1 cloud \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bb\u03bb\u03b9\u03c0\u03ae, \u03c3\u03c5\u03bc\u03c0\u03bb\u03b7\u03c1\u03ce\u03c3\u03c4\u03b5 \u03c4\u03bf \u03cc\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7, \u03c4\u03bf\u03bd \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2 \u03ba\u03b1\u03b9 \u03c4\u03b7 \u03c7\u03ce\u03c1\u03b1" diff --git a/homeassistant/components/xiaomi_miio/translations/en.json b/homeassistant/components/xiaomi_miio/translations/en.json index d24509e0e25..e54df0b0a99 100644 --- a/homeassistant/components/xiaomi_miio/translations/en.json +++ b/homeassistant/components/xiaomi_miio/translations/en.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "Device is already configured", "already_in_progress": "Configuration flow is already in progress", - "incomplete_info": "Incomplete information to setup device, no host or token supplied.", + "incomplete_info": "Incomplete information to set up device, no host or token supplied.", "not_xiaomi_miio": "Device is not (yet) supported by Xiaomi Miio.", "reauth_successful": "Re-authentication was successful", "unknown": "Unexpected error" @@ -13,7 +13,7 @@ "cloud_credentials_incomplete": "Cloud credentials incomplete, please fill in username, password and country", "cloud_login_error": "Could not login to Xiaomi Miio Cloud, check the credentials.", "cloud_no_devices": "No devices found in this Xiaomi Miio cloud account.", - "unknown_device": "The device model is not known, not able to setup the device using config flow.", + "unknown_device": "The device model is not known, not able to set up the device using config flow.", "wrong_token": "Checksum error, wrong token" }, "flow_title": "{name}", @@ -47,7 +47,32 @@ "data": { "select_device": "Miio device" }, - "description": "Select the Xiaomi Miio device to setup." + "description": "Select the Xiaomi Miio device to set up." + } + } + }, + "entity": { + "select": { + "display_orientation": { + "state": { + "forward": "Forward", + "left": "Left", + "right": "Right" + } + }, + "led_brightness": { + "state": { + "bright": "Bright", + "dim": "Dim", + "off": "Off" + } + }, + "ptc_level": { + "state": { + "high": "High", + "low": "Low", + "medium": "Medium" + } } } }, diff --git a/homeassistant/components/xiaomi_miio/translations/es.json b/homeassistant/components/xiaomi_miio/translations/es.json index 3173e888df6..8e8ba28d21d 100644 --- a/homeassistant/components/xiaomi_miio/translations/es.json +++ b/homeassistant/components/xiaomi_miio/translations/es.json @@ -47,7 +47,32 @@ "data": { "select_device": "Dispositivo Miio" }, - "description": "Selecciona el dispositivo Xiaomi Miio para configurarlo." + "description": "Selecciona el dispositivo Xiaomi Miio para configurar." + } + } + }, + "entity": { + "select": { + "display_orientation": { + "state": { + "forward": "Adelante", + "left": "Izquierda", + "right": "Derecha" + } + }, + "led_brightness": { + "state": { + "bright": "Brillo", + "dim": "Atenuar", + "off": "Apagado" + } + }, + "ptc_level": { + "state": { + "high": "Alto", + "low": "Bajo", + "medium": "Medio" + } } } }, diff --git a/homeassistant/components/xiaomi_miio/translations/et.json b/homeassistant/components/xiaomi_miio/translations/et.json index bfb9de5077e..54e3f505473 100644 --- a/homeassistant/components/xiaomi_miio/translations/et.json +++ b/homeassistant/components/xiaomi_miio/translations/et.json @@ -51,6 +51,31 @@ } } }, + "entity": { + "select": { + "display_orientation": { + "state": { + "forward": "Edaspidi", + "left": "Vasakule", + "right": "Paremale" + } + }, + "led_brightness": { + "state": { + "bright": "Hele", + "dim": "Tuhm", + "off": "V\u00e4ljas" + } + }, + "ptc_level": { + "state": { + "high": "K\u00f5rge", + "low": "Madal", + "medium": "Keskmine" + } + } + } + }, "options": { "error": { "cloud_credentials_incomplete": "Pilve mandaat on poolik, palun t\u00e4ida kasutajanimi, salas\u00f5na ja riik" diff --git a/homeassistant/components/xiaomi_miio/translations/he.json b/homeassistant/components/xiaomi_miio/translations/he.json index 07cdec38236..835cba4c125 100644 --- a/homeassistant/components/xiaomi_miio/translations/he.json +++ b/homeassistant/components/xiaomi_miio/translations/he.json @@ -47,7 +47,32 @@ "data": { "select_device": "\u05d4\u05ea\u05e7\u05df \u05de\u05d9\u05d5" }, - "description": "\u05d1\u05d7\u05e8 \u05d0\u05ea \u05d4\u05ea\u05e7\u05df \u05e9\u05d9\u05d5\u05d0\u05de\u05d9 \u05de\u05d9\u05d5 \u05dc\u05d4\u05ea\u05e7\u05e0\u05d4." + "description": "\u05d1\u05d7\u05d9\u05e8\u05ea \u05d4\u05ea\u05e7\u05df \u05e9\u05d9\u05d5\u05d0\u05de\u05d9 \u05de\u05d9\u05d5 \u05dc\u05d4\u05ea\u05e7\u05e0\u05d4." + } + } + }, + "entity": { + "select": { + "display_orientation": { + "state": { + "forward": "\u05e7\u05d3\u05d9\u05de\u05d4", + "left": "\u05e9\u05de\u05d0\u05dc", + "right": "\u05d9\u05de\u05d9\u05df" + } + }, + "led_brightness": { + "state": { + "bright": "\u05d1\u05d4\u05d9\u05e8", + "dim": "\u05de\u05e2\u05d5\u05de\u05e2\u05dd", + "off": "\u05db\u05d1\u05d5\u05d9" + } + }, + "ptc_level": { + "state": { + "high": "\u05d2\u05d1\u05d5\u05d4", + "low": "\u05e0\u05de\u05d5\u05da", + "medium": "\u05d1\u05d9\u05e0\u05d5\u05e0\u05d9" + } } } }, diff --git a/homeassistant/components/xiaomi_miio/translations/hu.json b/homeassistant/components/xiaomi_miio/translations/hu.json index 874483fffb3..b57417dd665 100644 --- a/homeassistant/components/xiaomi_miio/translations/hu.json +++ b/homeassistant/components/xiaomi_miio/translations/hu.json @@ -51,6 +51,31 @@ } } }, + "entity": { + "select": { + "display_orientation": { + "state": { + "forward": "El\u0151re", + "left": "Bal", + "right": "Jobb" + } + }, + "led_brightness": { + "state": { + "bright": "F\u00e9nyes", + "dim": "Halv\u00e1ny", + "off": "Ki" + } + }, + "ptc_level": { + "state": { + "high": "Magas", + "low": "Alacsony", + "medium": "K\u00f6zepes" + } + } + } + }, "options": { "error": { "cloud_credentials_incomplete": "A felh\u0151alap\u00fa hiteles\u00edt\u0151 adatok hi\u00e1nyosak, k\u00e9rem, adja meg a felhaszn\u00e1l\u00f3nevet, a jelsz\u00f3t \u00e9s az orsz\u00e1got" diff --git a/homeassistant/components/xiaomi_miio/translations/id.json b/homeassistant/components/xiaomi_miio/translations/id.json index f3dbc9877dd..bcbc66dd27f 100644 --- a/homeassistant/components/xiaomi_miio/translations/id.json +++ b/homeassistant/components/xiaomi_miio/translations/id.json @@ -51,6 +51,31 @@ } } }, + "entity": { + "select": { + "display_orientation": { + "state": { + "forward": "Maju", + "left": "Kiri", + "right": "Kanan" + } + }, + "led_brightness": { + "state": { + "bright": "Terang", + "dim": "Redup", + "off": "Mati" + } + }, + "ptc_level": { + "state": { + "high": "Tinggi", + "low": "Rendah", + "medium": "Sedang" + } + } + } + }, "options": { "error": { "cloud_credentials_incomplete": "Kredensial cloud tidak lengkap, isi nama pengguna, kata sandi, dan negara" diff --git a/homeassistant/components/xiaomi_miio/translations/it.json b/homeassistant/components/xiaomi_miio/translations/it.json index 239620dfdc6..eca534edeaf 100644 --- a/homeassistant/components/xiaomi_miio/translations/it.json +++ b/homeassistant/components/xiaomi_miio/translations/it.json @@ -51,6 +51,31 @@ } } }, + "entity": { + "select": { + "display_orientation": { + "state": { + "forward": "Avanti", + "left": "Sinistra", + "right": "Destra" + } + }, + "led_brightness": { + "state": { + "bright": "Luminoso", + "dim": "Scuro", + "off": "Spento" + } + }, + "ptc_level": { + "state": { + "high": "Alto", + "low": "Basso", + "medium": "Medio" + } + } + } + }, "options": { "error": { "cloud_credentials_incomplete": "Credenziali cloud incomplete, inserisci nome utente, password e paese" diff --git a/homeassistant/components/xiaomi_miio/translations/ko.json b/homeassistant/components/xiaomi_miio/translations/ko.json index 2c188fa670b..2e259efad80 100644 --- a/homeassistant/components/xiaomi_miio/translations/ko.json +++ b/homeassistant/components/xiaomi_miio/translations/ko.json @@ -2,12 +2,26 @@ "config": { "abort": { "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", - "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4" + "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" }, "error": { "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", "unknown_device": "\uae30\uae30\uc758 \ubaa8\ub378\uc744 \uc54c \uc218 \uc5c6\uc2b5\ub2c8\ub2e4. \uad6c\uc131 \ud750\ub984\uc5d0\uc11c \uae30\uae30\ub97c \uc124\uc815\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4." }, - "flow_title": "Xiaomi Miio: {name}" + "flow_title": "Xiaomi Miio: {name}", + "step": { + "manual": { + "data": { + "host": "IP \uc8fc\uc18c", + "token": "API \ud1a0\ud070" + }, + "description": "32\uc790\ub9ac API \ud1a0\ud070 \uac00 \ud544\uc694\ud569\ub2c8\ub2e4. \uc790\uc138\ud55c \ub0b4\uc6a9\uc740 https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token\uc744 \ucc38\uc870\ud558\uc2ed\uc2dc\uc624. \uc774 API \ud1a0\ud070 \uc740 Xiaomi Aqara \ud1b5\ud569\uad6c\uc131\uc694\uc18c\uc5d0\uc11c \uc0ac\uc6a9\ud558\ub294 \ud0a4\uc640 \ub2e4\ub985\ub2c8\ub2e4." + }, + "reauth_confirm": { + "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" + } + } } } \ No newline at end of file diff --git a/homeassistant/components/xiaomi_miio/translations/lb.json b/homeassistant/components/xiaomi_miio/translations/lb.json index 9a747e8e315..9be213c2206 100644 --- a/homeassistant/components/xiaomi_miio/translations/lb.json +++ b/homeassistant/components/xiaomi_miio/translations/lb.json @@ -7,6 +7,13 @@ "error": { "cannot_connect": "Feeler beim verbannen" }, - "flow_title": "Xiaomi Miio: {name}" + "flow_title": "Xiaomi Miio: {name}", + "step": { + "cloud": { + "data": { + "cloud_username": "Cloud-Benotzernumm" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/xiaomi_miio/translations/nl.json b/homeassistant/components/xiaomi_miio/translations/nl.json index 07671e1802e..290412f048e 100644 --- a/homeassistant/components/xiaomi_miio/translations/nl.json +++ b/homeassistant/components/xiaomi_miio/translations/nl.json @@ -51,6 +51,16 @@ } } }, + "entity": { + "select": { + "display_orientation": { + "state": { + "left": "Links", + "right": "Rechts" + } + } + } + }, "options": { "error": { "cloud_credentials_incomplete": "Cloud-inloggegevens onvolledig, vul gebruikersnaam, wachtwoord en land in" diff --git a/homeassistant/components/xiaomi_miio/translations/no.json b/homeassistant/components/xiaomi_miio/translations/no.json index 8f06bee0223..182831d658b 100644 --- a/homeassistant/components/xiaomi_miio/translations/no.json +++ b/homeassistant/components/xiaomi_miio/translations/no.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "Enheten er allerede konfigurert", "already_in_progress": "Konfigurasjonsflyten p\u00e5g\u00e5r allerede", - "incomplete_info": "Ufullstendig informasjon til installasjonsenheten, ingen vert eller token leveres.", + "incomplete_info": "Ufullstendig informasjon for \u00e5 konfigurere enheten, ingen vert eller token levert.", "not_xiaomi_miio": "Enheten st\u00f8ttes (enn\u00e5) ikke av Xiaomi Miio.", "reauth_successful": "Re-autentisering var vellykket", "unknown": "Uventet feil" @@ -13,7 +13,7 @@ "cloud_credentials_incomplete": "Utskriftsinformasjon for skyen er fullstendig. Fyll ut brukernavn, passord og land", "cloud_login_error": "Kunne ikke logge inn p\u00e5 Xiaomi Miio Cloud, sjekk legitimasjonen.", "cloud_no_devices": "Ingen enheter funnet i denne Xiaomi Miio-skykontoen.", - "unknown_device": "Enhetsmodellen er ikke kjent, kan ikke konfigurere enheten ved hjelp av konfigurasjonsflyt.", + "unknown_device": "Enhetsmodellen er ikke kjent, kan ikke sette opp enheten ved hjelp av konfigurasjonsflyt.", "wrong_token": "Kontrollsumfeil, feil token" }, "flow_title": "{name}", @@ -47,7 +47,32 @@ "data": { "select_device": "Miio-enhet" }, - "description": "Velg Xiaomi Miio-enheten du vil installere." + "description": "Velg Xiaomi Miio-enheten du vil konfigurere." + } + } + }, + "entity": { + "select": { + "display_orientation": { + "state": { + "forward": "Framover", + "left": "Venstre", + "right": "H\u00f8yre" + } + }, + "led_brightness": { + "state": { + "bright": "Lys", + "dim": "Dim", + "off": "Av" + } + }, + "ptc_level": { + "state": { + "high": "H\u00f8y", + "low": "Lav", + "medium": "Medium" + } } } }, diff --git a/homeassistant/components/xiaomi_miio/translations/pl.json b/homeassistant/components/xiaomi_miio/translations/pl.json index 72b031bd606..f66993e403f 100644 --- a/homeassistant/components/xiaomi_miio/translations/pl.json +++ b/homeassistant/components/xiaomi_miio/translations/pl.json @@ -51,6 +51,31 @@ } } }, + "entity": { + "select": { + "display_orientation": { + "state": { + "forward": "do przodu", + "left": "w lewo", + "right": "w prawo" + } + }, + "led_brightness": { + "state": { + "bright": "jasne", + "dim": "ciemne", + "off": "wy\u0142\u0105czone" + } + }, + "ptc_level": { + "state": { + "high": "wysoki", + "low": "niski", + "medium": "\u015bredni" + } + } + } + }, "options": { "error": { "cloud_credentials_incomplete": "Dane logowania do chmury niekompletne, prosz\u0119 poda\u0107 nazw\u0119 u\u017cytkownika, has\u0142o i kraj" diff --git a/homeassistant/components/xiaomi_miio/translations/pt-BR.json b/homeassistant/components/xiaomi_miio/translations/pt-BR.json index f12c3637b7e..bbec1722e08 100644 --- a/homeassistant/components/xiaomi_miio/translations/pt-BR.json +++ b/homeassistant/components/xiaomi_miio/translations/pt-BR.json @@ -51,6 +51,31 @@ } } }, + "entity": { + "select": { + "display_orientation": { + "state": { + "forward": "Avan\u00e7ar", + "left": "Esquerda", + "right": "Direita" + } + }, + "led_brightness": { + "state": { + "bright": "Brilhante", + "dim": "Ofuscar", + "off": "Desligado" + } + }, + "ptc_level": { + "state": { + "high": "Alto", + "low": "Baixo", + "medium": "M\u00e9dio" + } + } + } + }, "options": { "error": { "cloud_credentials_incomplete": "Credenciais da cloud incompletas, preencha o nome de usu\u00e1rio, a senha e o pa\u00eds" diff --git a/homeassistant/components/xiaomi_miio/translations/pt.json b/homeassistant/components/xiaomi_miio/translations/pt.json index 41cb2e55e91..14ac1f1deb4 100644 --- a/homeassistant/components/xiaomi_miio/translations/pt.json +++ b/homeassistant/components/xiaomi_miio/translations/pt.json @@ -1,10 +1,11 @@ { "config": { "abort": { - "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", + "unknown": "Erro inesperado" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "manual": { diff --git a/homeassistant/components/xiaomi_miio/translations/ru.json b/homeassistant/components/xiaomi_miio/translations/ru.json index 256cf75bbd2..085317cabe1 100644 --- a/homeassistant/components/xiaomi_miio/translations/ru.json +++ b/homeassistant/components/xiaomi_miio/translations/ru.json @@ -51,6 +51,31 @@ } } }, + "entity": { + "select": { + "display_orientation": { + "state": { + "forward": "\u0412\u043f\u0435\u0440\u0435\u0434", + "left": "\u041d\u0430\u043b\u0435\u0432\u043e", + "right": "\u041d\u0430\u043f\u0440\u0430\u0432\u043e" + } + }, + "led_brightness": { + "state": { + "bright": "\u042f\u0440\u043a\u043e", + "dim": "\u0422\u0443\u0441\u043a\u043b\u043e", + "off": "\u0412\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u043e" + } + }, + "ptc_level": { + "state": { + "high": "\u0412\u044b\u0441\u043e\u043a\u0438\u0439", + "low": "\u041d\u0438\u0437\u043a\u0438\u0439", + "medium": "\u0421\u0440\u0435\u0434\u043d\u0438\u0439" + } + } + } + }, "options": { "error": { "cloud_credentials_incomplete": "\u0423\u0447\u0435\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 \u043e\u0431\u043b\u0430\u043a\u0435 \u043d\u0435\u043f\u043e\u043b\u043d\u044b\u0435. \u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u043f\u0430\u0440\u043e\u043b\u044c \u0438 \u0441\u0442\u0440\u0430\u043d\u0443." diff --git a/homeassistant/components/xiaomi_miio/translations/select.hu.json b/homeassistant/components/xiaomi_miio/translations/select.hu.json index 7c74e7a4730..6b647d3e652 100644 --- a/homeassistant/components/xiaomi_miio/translations/select.hu.json +++ b/homeassistant/components/xiaomi_miio/translations/select.hu.json @@ -7,7 +7,7 @@ }, "xiaomi_miio__led_brightness": { "bright": "F\u00e9nyes", - "dim": "Hom\u00e1lyos", + "dim": "Halv\u00e1ny", "off": "Ki" }, "xiaomi_miio__ptc_level": { diff --git a/homeassistant/components/xiaomi_miio/translations/select.sk.json b/homeassistant/components/xiaomi_miio/translations/select.sk.json index e2021b4e247..a558a0b9186 100644 --- a/homeassistant/components/xiaomi_miio/translations/select.sk.json +++ b/homeassistant/components/xiaomi_miio/translations/select.sk.json @@ -1,6 +1,7 @@ { "state": { "xiaomi_miio__display_orientation": { + "forward": "Dopredu", "left": "V\u013eavo", "right": "Vpravo" }, @@ -8,6 +9,11 @@ "bright": "Jas", "dim": "Dim", "off": "Vypnut\u00e9" + }, + "xiaomi_miio__ptc_level": { + "high": "Vysok\u00e9", + "low": "N\u00edzke", + "medium": "Stredn\u00e9" } } } \ No newline at end of file diff --git a/homeassistant/components/xiaomi_miio/translations/sk.json b/homeassistant/components/xiaomi_miio/translations/sk.json index 6c9d42328fa..fcfb9f3555d 100644 --- a/homeassistant/components/xiaomi_miio/translations/sk.json +++ b/homeassistant/components/xiaomi_miio/translations/sk.json @@ -10,6 +10,9 @@ }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", + "cloud_credentials_incomplete": "Poverenia cloudu nie s\u00fa \u00fapln\u00e9, vypl\u0148te pou\u017e\u00edvate\u013esk\u00e9 meno, heslo a krajinu", + "cloud_login_error": "Nepodarilo sa prihl\u00e1si\u0165 do Xiaomi Miio Cloud, skontrolujte prihlasovacie \u00fadaje.", + "cloud_no_devices": "V tomto cloudovom \u00fa\u010dte Xiaomi Miio sa nena\u0161li \u017eiadne zariadenia.", "unknown_device": "Model zariadenia nie je zn\u00e1my, nie je mo\u017en\u00e9 nastavi\u0165 zariadenie pomocou konfigura\u010dn\u00e9ho toku.", "wrong_token": "Chyba kontroln\u00e9ho s\u00fa\u010dtu, nespr\u00e1vny token" }, @@ -21,7 +24,8 @@ "cloud_password": "Heslo do cloudu", "cloud_username": "Cloudov\u00e9 pou\u017e\u00edvate\u013esk\u00e9 meno", "manual": "Konfigurova\u0165 manu\u00e1lne (neodpor\u00fa\u010da sa)" - } + }, + "description": "Prihl\u00e1ste sa do cloudu Xiaomi Miio, pozrite si https://www.openhab.org/addons/bindings/miio/#country-servers, kde n\u00e1jdete cloudov\u00fd server, ktor\u00fd chcete pou\u017ei\u0165." }, "connect": { "data": { @@ -32,9 +36,11 @@ "data": { "host": "IP adresa", "token": "API token" - } + }, + "description": "Budete potrebova\u0165 32 znakov API token , pokyny n\u00e1jdete na https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token. Upozor\u0148ujeme, \u017ee tento API token sa l\u00ed\u0161i od k\u013e\u00fa\u010da pou\u017e\u00edvan\u00e9ho integr\u00e1ciou Xiaomi Aqara." }, "reauth_confirm": { + "description": "Integr\u00e1cia Xiaomi Miio mus\u00ed znova overi\u0165 v\u00e1\u0161 \u00fa\u010det, aby sa aktualizovali tokeny alebo pridali ch\u00fdbaj\u00face cloudov\u00e9 poverenia.", "title": "Znova overi\u0165 integr\u00e1ciu" }, "select": { @@ -45,7 +51,35 @@ } } }, + "entity": { + "select": { + "display_orientation": { + "state": { + "forward": "Dopredu", + "left": "V\u013eavo", + "right": "Vpravo" + } + }, + "led_brightness": { + "state": { + "bright": "Jas", + "dim": "Dim", + "off": "Vypnut\u00e9" + } + }, + "ptc_level": { + "state": { + "high": "Vysok\u00e9", + "low": "N\u00edzke", + "medium": "Stredn\u00e9" + } + } + } + }, "options": { + "error": { + "cloud_credentials_incomplete": "Poverenia cloudu nie s\u00fa \u00fapln\u00e9, vypl\u0148te pou\u017e\u00edvate\u013esk\u00e9 meno, heslo a krajinu" + }, "step": { "init": { "data": { diff --git a/homeassistant/components/xiaomi_miio/translations/zh-Hant.json b/homeassistant/components/xiaomi_miio/translations/zh-Hant.json index 9c3158ac2e9..b92b25aa002 100644 --- a/homeassistant/components/xiaomi_miio/translations/zh-Hant.json +++ b/homeassistant/components/xiaomi_miio/translations/zh-Hant.json @@ -51,6 +51,31 @@ } } }, + "entity": { + "select": { + "display_orientation": { + "state": { + "forward": "\u524d\u9032", + "left": "\u5de6", + "right": "\u53f3" + } + }, + "led_brightness": { + "state": { + "bright": "\u4eae\u5149", + "dim": "\u5fae\u5149", + "off": "\u95dc\u9589" + } + }, + "ptc_level": { + "state": { + "high": "\u9ad8", + "low": "\u4f4e", + "medium": "\u4e2d" + } + } + } + }, "options": { "error": { "cloud_credentials_incomplete": "\u96f2\u7aef\u6191\u8b49\u672a\u5b8c\u6210\uff0c\u8acb\u586b\u5beb\u4f7f\u7528\u8005\u540d\u7a31\u3001\u5bc6\u78bc\u8207\u570b\u5bb6" diff --git a/homeassistant/components/xmpp/manifest.json b/homeassistant/components/xmpp/manifest.json index 5fc8d6e50a2..4f11b83a55d 100644 --- a/homeassistant/components/xmpp/manifest.json +++ b/homeassistant/components/xmpp/manifest.json @@ -2,7 +2,7 @@ "domain": "xmpp", "name": "Jabber (XMPP)", "documentation": "https://www.home-assistant.io/integrations/xmpp", - "requirements": ["slixmpp==1.8.2"], + "requirements": ["slixmpp==1.8.3"], "codeowners": ["@fabaff", "@flowolf"], "iot_class": "cloud_push", "loggers": ["pyasn1", "slixmpp"] diff --git a/homeassistant/components/yale_smart_alarm/alarm_control_panel.py b/homeassistant/components/yale_smart_alarm/alarm_control_panel.py index 8577a2a179f..799949a462a 100644 --- a/homeassistant/components/yale_smart_alarm/alarm_control_panel.py +++ b/homeassistant/components/yale_smart_alarm/alarm_control_panel.py @@ -81,7 +81,8 @@ class YaleAlarmDevice(YaleAlarmEntity, AlarmControlPanelEntity): ) except YALE_ALL_ERRORS as error: raise HomeAssistantError( - f"Could not set alarm for {self.coordinator.entry.data[CONF_NAME]}: {error}" + f"Could not set alarm for {self.coordinator.entry.data[CONF_NAME]}:" + f" {error}" ) from error if alarm_state: diff --git a/homeassistant/components/yale_smart_alarm/translations/ko.json b/homeassistant/components/yale_smart_alarm/translations/ko.json index b213fdda32c..f92a419cf14 100644 --- a/homeassistant/components/yale_smart_alarm/translations/ko.json +++ b/homeassistant/components/yale_smart_alarm/translations/ko.json @@ -1,4 +1,30 @@ { + "config": { + "abort": { + "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "step": { + "reauth_confirm": { + "data": { + "name": "\uc774\ub984", + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + } + }, + "user": { + "data": { + "name": "\uc774\ub984", + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + } + } + } + }, "options": { "step": { "init": { diff --git a/homeassistant/components/yale_smart_alarm/translations/lb.json b/homeassistant/components/yale_smart_alarm/translations/lb.json new file mode 100644 index 00000000000..684b68e8f3a --- /dev/null +++ b/homeassistant/components/yale_smart_alarm/translations/lb.json @@ -0,0 +1,11 @@ +{ + "options": { + "step": { + "init": { + "data": { + "lock_code_digits": "Anzuel vun Zifferen am PIN-Code fir Schl\u00e4sser" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/yale_smart_alarm/translations/sk.json b/homeassistant/components/yale_smart_alarm/translations/sk.json index e86404bb51d..7c7b0b35ee1 100644 --- a/homeassistant/components/yale_smart_alarm/translations/sk.json +++ b/homeassistant/components/yale_smart_alarm/translations/sk.json @@ -34,6 +34,7 @@ "step": { "init": { "data": { + "code": "Predvolen\u00fd k\u00f3d pre z\u00e1mky, ktor\u00fd sa pou\u017eije, ak nie je zadan\u00fd", "lock_code_digits": "Po\u010det \u010d\u00edslic v PIN k\u00f3de pre z\u00e1mky" } } diff --git a/homeassistant/components/yalexs_ble/config_flow.py b/homeassistant/components/yalexs_ble/config_flow.py index 7845e8aa5e8..e6008526341 100644 --- a/homeassistant/components/yalexs_ble/config_flow.py +++ b/homeassistant/components/yalexs_ble/config_flow.py @@ -233,7 +233,9 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): { vol.Required(CONF_ADDRESS): vol.In( { - service_info.address: f"{service_info.name} ({service_info.address})" + service_info.address: ( + f"{service_info.name} ({service_info.address})" + ) for service_info in self._discovered_devices.values() } ), diff --git a/homeassistant/components/yalexs_ble/manifest.json b/homeassistant/components/yalexs_ble/manifest.json index ce86f7f7ea1..82d8f0eb228 100644 --- a/homeassistant/components/yalexs_ble/manifest.json +++ b/homeassistant/components/yalexs_ble/manifest.json @@ -3,7 +3,7 @@ "name": "Yale Access Bluetooth", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/yalexs_ble", - "requirements": ["yalexs-ble==1.10.2"], + "requirements": ["yalexs-ble==1.12.5"], "dependencies": ["bluetooth"], "codeowners": ["@bdraco"], "bluetooth": [ diff --git a/homeassistant/components/yalexs_ble/sensor.py b/homeassistant/components/yalexs_ble/sensor.py index 19fee1924e6..ce9113d3f46 100644 --- a/homeassistant/components/yalexs_ble/sensor.py +++ b/homeassistant/components/yalexs_ble/sensor.py @@ -1,11 +1,23 @@ """Support for yalexs ble sensors.""" from __future__ import annotations +from collections.abc import Callable +from dataclasses import dataclass + from yalexs_ble import ConnectionInfo, LockInfo, LockState from homeassistant import config_entries -from homeassistant.components.sensor import SensorDeviceClass, SensorEntity -from homeassistant.const import SIGNAL_STRENGTH_DECIBELS_MILLIWATT +from homeassistant.components.sensor import ( + SensorDeviceClass, + SensorEntity, + SensorEntityDescription, + SensorStateClass, +) +from homeassistant.const import ( + PERCENTAGE, + SIGNAL_STRENGTH_DECIBELS_MILLIWATT, + UnitOfElectricPotential, +) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -15,6 +27,60 @@ from .entity import YALEXSBLEEntity from .models import YaleXSBLEData +@dataclass +class YaleXSBLERequiredKeysMixin: + """Mixin for required keys.""" + + value_fn: Callable[[LockState, LockInfo, ConnectionInfo], int | float | None] + + +@dataclass +class YaleXSBLESensorEntityDescription( + SensorEntityDescription, YaleXSBLERequiredKeysMixin +): + """Describes Yale Access Bluetooth sensor entity.""" + + +SENSORS: tuple[YaleXSBLESensorEntityDescription, ...] = ( + YaleXSBLESensorEntityDescription( + key="", # No key for the original RSSI sensor unique id + name="Signal strength", + device_class=SensorDeviceClass.SIGNAL_STRENGTH, + entity_category=EntityCategory.DIAGNOSTIC, + state_class=SensorStateClass.MEASUREMENT, + has_entity_name=True, + native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS_MILLIWATT, + entity_registry_enabled_default=False, + value_fn=lambda state, info, connection: connection.rssi, + ), + YaleXSBLESensorEntityDescription( + key="battery_level", + name="Battery level", + device_class=SensorDeviceClass.BATTERY, + entity_category=EntityCategory.DIAGNOSTIC, + state_class=SensorStateClass.MEASUREMENT, + has_entity_name=True, + native_unit_of_measurement=PERCENTAGE, + value_fn=lambda state, info, connection: state.battery.percentage + if state.battery + else None, + ), + YaleXSBLESensorEntityDescription( + key="battery_voltage", + name="Battery Voltage", + device_class=SensorDeviceClass.VOLTAGE, + entity_category=EntityCategory.DIAGNOSTIC, + state_class=SensorStateClass.MEASUREMENT, + has_entity_name=True, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + entity_registry_enabled_default=False, + value_fn=lambda state, info, connection: state.battery.voltage + if state.battery + else None, + ), +) + + async def async_setup_entry( hass: HomeAssistant, entry: config_entries.ConfigEntry, @@ -22,23 +88,30 @@ async def async_setup_entry( ) -> None: """Set up YALE XS Bluetooth sensors.""" data: YaleXSBLEData = hass.data[DOMAIN][entry.entry_id] - async_add_entities([YaleXSBLERSSISensor(data)]) + async_add_entities(YaleXSBLESensor(description, data) for description in SENSORS) -class YaleXSBLERSSISensor(YALEXSBLEEntity, SensorEntity): - """Yale XS Bluetooth RSSI sensor.""" +class YaleXSBLESensor(YALEXSBLEEntity, SensorEntity): + """Yale XS Bluetooth sensor.""" - _attr_device_class = SensorDeviceClass.SIGNAL_STRENGTH - _attr_entity_category = EntityCategory.DIAGNOSTIC - _attr_entity_registry_enabled_default = False - _attr_has_entity_name = True - _attr_name = "Signal strength" - _attr_native_unit_of_measurement = SIGNAL_STRENGTH_DECIBELS_MILLIWATT + entity_description: YaleXSBLESensorEntityDescription + + def __init__( + self, + description: YaleXSBLESensorEntityDescription, + data: YaleXSBLEData, + ) -> None: + """Initialize the sensor.""" + self.entity_description = description + super().__init__(data) + self._attr_unique_id = f"{data.lock.address}{description.key}" @callback def _async_update_state( self, new_state: LockState, lock_info: LockInfo, connection_info: ConnectionInfo ) -> None: """Update the state.""" - self._attr_native_value = connection_info.rssi + self._attr_native_value = self.entity_description.value_fn( + new_state, lock_info, connection_info + ) super()._async_update_state(new_state, lock_info, connection_info) diff --git a/homeassistant/components/yalexs_ble/strings.json b/homeassistant/components/yalexs_ble/strings.json index df5ac713b07..6a60982f29c 100644 --- a/homeassistant/components/yalexs_ble/strings.json +++ b/homeassistant/components/yalexs_ble/strings.json @@ -11,7 +11,7 @@ } }, "integration_discovery_confirm": { - "description": "Do you want to setup {name} over Bluetooth with address {address}?" + "description": "Do you want to set up {name} over Bluetooth with address {address}?" } }, "error": { diff --git a/homeassistant/components/yalexs_ble/translations/de.json b/homeassistant/components/yalexs_ble/translations/de.json index 3fffdc25cf3..5876d6d632c 100644 --- a/homeassistant/components/yalexs_ble/translations/de.json +++ b/homeassistant/components/yalexs_ble/translations/de.json @@ -9,7 +9,7 @@ "error": { "cannot_connect": "Verbindung fehlgeschlagen", "invalid_auth": "Ung\u00fcltige Authentifizierung", - "invalid_key_format": "Der Offline-Schl\u00fcssel muss eine 32-Byte-Hex-Zeichenfolge sein.", + "invalid_key_format": "Der Offline-Schl\u00fcssel muss eine 32 Byte Hex-Zeichenfolge sein.", "invalid_key_index": "Der Offline-Schl\u00fcssel-Slot muss eine ganze Zahl zwischen 0 und 255 sein.", "unknown": "Unerwarteter Fehler" }, @@ -21,7 +21,7 @@ "user": { "data": { "address": "Bluetooth-Adresse", - "key": "Offline-Schl\u00fcssel (32-Byte-Hex-Zeichenfolge)", + "key": "Offline-Schl\u00fcssel (32 Byte Hex-Zeichenfolge)", "slot": "Offline-Schl\u00fcssel-Slot (Ganzzahl zwischen 0 und 255)" }, "description": "Lies in der Dokumentation nach, wie du den Offline-Schl\u00fcssel finden kannst." diff --git a/homeassistant/components/yalexs_ble/translations/en.json b/homeassistant/components/yalexs_ble/translations/en.json index f8ebc0737ac..9b00cd42ef4 100644 --- a/homeassistant/components/yalexs_ble/translations/en.json +++ b/homeassistant/components/yalexs_ble/translations/en.json @@ -16,7 +16,7 @@ "flow_title": "{name}", "step": { "integration_discovery_confirm": { - "description": "Do you want to setup {name} over Bluetooth with address {address}?" + "description": "Do you want to set up {name} over Bluetooth with address {address}?" }, "user": { "data": { diff --git a/homeassistant/components/yalexs_ble/translations/ko.json b/homeassistant/components/yalexs_ble/translations/ko.json new file mode 100644 index 00000000000..67285fd4ac4 --- /dev/null +++ b/homeassistant/components/yalexs_ble/translations/ko.json @@ -0,0 +1,14 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", + "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/yalexs_ble/translations/no.json b/homeassistant/components/yalexs_ble/translations/no.json index 99a1b78849d..3d9aaa0948f 100644 --- a/homeassistant/components/yalexs_ble/translations/no.json +++ b/homeassistant/components/yalexs_ble/translations/no.json @@ -16,7 +16,7 @@ "flow_title": "{name}", "step": { "integration_discovery_confirm": { - "description": "Vil du konfigurere {name} over Bluetooth med adressen {address} ?" + "description": "Vil du sette opp {name} over Bluetooth med adressen {address} ?" }, "user": { "data": { diff --git a/homeassistant/components/yalexs_ble/translations/pl.json b/homeassistant/components/yalexs_ble/translations/pl.json index 6017fb86ffb..5bf8282604a 100644 --- a/homeassistant/components/yalexs_ble/translations/pl.json +++ b/homeassistant/components/yalexs_ble/translations/pl.json @@ -16,7 +16,7 @@ "flow_title": "{name}", "step": { "integration_discovery_confirm": { - "description": "Czy chcesz skonfigurowa\u0107 {name} przez Bluetooth z adresem {address}?" + "description": "Czy chcesz skonfigurowa\u0107 {name} przez Bluetooth o adresie {address}?" }, "user": { "data": { diff --git a/homeassistant/components/yalexs_ble/translations/pt.json b/homeassistant/components/yalexs_ble/translations/pt.json index 3cbc65bda52..5408a9ff3c1 100644 --- a/homeassistant/components/yalexs_ble/translations/pt.json +++ b/homeassistant/components/yalexs_ble/translations/pt.json @@ -1,8 +1,16 @@ { "config": { + "abort": { + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", + "already_in_progress": "O processo de configura\u00e7\u00e3o j\u00e1 est\u00e1 a decorrer", + "no_devices_found": "Nenhum dispositivo encontrado na rede" + }, "error": { + "cannot_connect": "A liga\u00e7\u00e3o falhou", + "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "invalid_key_format": "A chave offline deve ser uma string hexadecimal de 32 bytes.", - "invalid_key_index": "O slot de chave offline deve ser um n\u00famero inteiro entre 0 e 255." + "invalid_key_index": "O slot de chave offline deve ser um n\u00famero inteiro entre 0 e 255.", + "unknown": "Erro inesperado" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/yalexs_ble/translations/sk.json b/homeassistant/components/yalexs_ble/translations/sk.json index 09746a52f33..f781c9012fe 100644 --- a/homeassistant/components/yalexs_ble/translations/sk.json +++ b/homeassistant/components/yalexs_ble/translations/sk.json @@ -10,6 +10,7 @@ "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_auth": "Neplatn\u00e9 overenie", "invalid_key_format": "Offline k\u013e\u00fa\u010d mus\u00ed by\u0165 32-bajtov\u00fd hexadecim\u00e1lny re\u0165azec.", + "invalid_key_index": "Offline k\u013e\u00fa\u010dov\u00fd slot mus\u00ed by\u0165 cel\u00e9 \u010d\u00edslo od 0 do 255.", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "flow_title": "{name}", @@ -20,8 +21,10 @@ "user": { "data": { "address": "Adresa Bluetooth", - "key": "Offline k\u013e\u00fa\u010d (32-bajtov\u00fd hexadecim\u00e1lny re\u0165azec)" - } + "key": "Offline k\u013e\u00fa\u010d (32-bajtov\u00fd hexadecim\u00e1lny re\u0165azec)", + "slot": "Offline Key Slot (Cel\u00e9 \u010d\u00edslo od 0 do 255)" + }, + "description": "V dokument\u00e1cii n\u00e1jdete inform\u00e1cie o tom, ako n\u00e1js\u0165 offline k\u013e\u00fa\u010d." } } } diff --git a/homeassistant/components/yamaha_musiccast/const.py b/homeassistant/components/yamaha_musiccast/const.py index 79ee3b8e95d..49234ac38ee 100644 --- a/homeassistant/components/yamaha_musiccast/const.py +++ b/homeassistant/components/yamaha_musiccast/const.py @@ -45,13 +45,13 @@ ENTITY_CATEGORY_MAPPING = { EntityType.DIAGNOSTIC: EntityCategory.DIAGNOSTIC, } -DEVICE_CLASS_MAPPING = { - "DIMMER": "yamaha_musiccast__dimmer", - "zone_SLEEP": "yamaha_musiccast__zone_sleep", - "zone_TONE_CONTROL_mode": "yamaha_musiccast__zone_tone_control_mode", - "zone_SURR_DECODER_TYPE": "yamaha_musiccast__zone_surr_decoder_type", - "zone_EQUALIZER_mode": "yamaha_musiccast__zone_equalizer_mode", - "zone_LINK_AUDIO_QUALITY": "yamaha_musiccast__zone_link_audio_quality", - "zone_LINK_CONTROL": "yamaha_musiccast__zone_link_control", - "zone_LINK_AUDIO_DELAY": "yamaha_musiccast__zone_link_audio_delay", +TRANSLATION_KEY_MAPPING = { + "DIMMER": "dimmer", + "zone_SLEEP": "zone_sleep", + "zone_TONE_CONTROL_mode": "zone_tone_control_mode", + "zone_SURR_DECODER_TYPE": "zone_surr_decoder_type", + "zone_EQUALIZER_mode": "zone_equalizer_mode", + "zone_LINK_AUDIO_QUALITY": "zone_link_audio_quality", + "zone_LINK_CONTROL": "zone_link_control", + "zone_LINK_AUDIO_DELAY": "zone_link_audio_delay", } diff --git a/homeassistant/components/yamaha_musiccast/media_player.py b/homeassistant/components/yamaha_musiccast/media_player.py index 0a6203eddf2..5c4b2ae5ae5 100644 --- a/homeassistant/components/yamaha_musiccast/media_player.py +++ b/homeassistant/components/yamaha_musiccast/media_player.py @@ -463,7 +463,8 @@ class MusicCastMediaPlayer(MusicCastDeviceEntity, MediaPlayerEntity): await self.coordinator.musiccast.tuner_previous_station() else: raise HomeAssistantError( - "Service previous track is not supported for non NetUSB or Tuner sources." + "Service previous track is not supported for non NetUSB or Tuner" + " sources." ) async def async_media_next_track(self) -> None: @@ -653,7 +654,7 @@ class MusicCastMediaPlayer(MusicCastDeviceEntity, MediaPlayerEntity): @property def musiccast_zone_entity(self) -> MusicCastMediaPlayer: - """Return the the entity of the zone, which is using MusicCast at the moment, if there is one, self else. + """Return the entity of the zone, which is using MusicCast at the moment, if there is one, self else. It is possible that multiple zones use MusicCast as client at the same time. In this case the first one is returned. @@ -720,7 +721,10 @@ class MusicCastMediaPlayer(MusicCastDeviceEntity, MediaPlayerEntity): network_join = await client.async_client_join(group, self) except MusicCastGroupException: _LOGGER.warning( - "%s is struggling to update its group data. Will retry perform the update", + ( + "%s is struggling to update its group data. Will retry" + " perform the update" + ), client.entity_id, ) network_join = await client.async_client_join(group, self) @@ -786,8 +790,10 @@ class MusicCastMediaPlayer(MusicCastDeviceEntity, MediaPlayerEntity): if self.musiccast_zone_entity.is_server: # If one of the zones of the device is a server, we need to unjoin first. _LOGGER.debug( - "%s is a server of a group and has to stop distribution " - "to use MusicCast for %s", + ( + "%s is a server of a group and has to stop distribution " + "to use MusicCast for %s" + ), self.musiccast_zone_entity.entity_id, self.entity_id, ) diff --git a/homeassistant/components/yamaha_musiccast/select.py b/homeassistant/components/yamaha_musiccast/select.py index 475d32311ed..a8ca6162c91 100644 --- a/homeassistant/components/yamaha_musiccast/select.py +++ b/homeassistant/components/yamaha_musiccast/select.py @@ -9,7 +9,7 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from . import DOMAIN, MusicCastCapabilityEntity, MusicCastDataUpdateCoordinator -from .const import DEVICE_CLASS_MAPPING +from .const import TRANSLATION_KEY_MAPPING async def async_setup_entry( @@ -47,9 +47,9 @@ class SelectableCapapility(MusicCastCapabilityEntity, SelectEntity): await self.capability.set(value) @property - def device_class(self) -> str | None: - """Return the device class, to identify the entity for translations.""" - return DEVICE_CLASS_MAPPING.get(self.capability.id) + def translation_key(self) -> str | None: + """Return the translation key to translate the entity's states.""" + return TRANSLATION_KEY_MAPPING.get(self.capability.id) @property def options(self) -> list[str]: diff --git a/homeassistant/components/yamaha_musiccast/strings.json b/homeassistant/components/yamaha_musiccast/strings.json index 145c8a2849e..0b3a51989b8 100644 --- a/homeassistant/components/yamaha_musiccast/strings.json +++ b/homeassistant/components/yamaha_musiccast/strings.json @@ -19,5 +19,73 @@ "error": { "no_musiccast_device": "This device seems to be no MusicCast Device." } + }, + "entity": { + "select": { + "dimmer": { + "state": { + "auto": "Auto" + } + }, + "zone_sleep": { + "state": { + "off": "Off", + "30 min": "30 Minutes", + "60 min": "60 Minutes", + "90 min": "90 Minutes", + "120 min": "120 Minutes" + } + }, + "zone_tone_control_mode": { + "state": { + "manual": "Manual", + "auto": "Auto", + "bypass": "Bypass" + } + }, + "zone_surr_decoder_type": { + "state": { + "toggle": "Toggle", + "auto": "Auto", + "dolby_pl": "Dolby ProLogic", + "dolby_pl2x_movie": "Dolby ProLogic 2x Movie", + "dolby_pl2x_music": "Dolby ProLogic 2x Music", + "dolby_pl2x_game": "Dolby ProLogic 2x Game", + "dolby_surround": "Dolby Surround", + "dts_neural_x": "DTS Neural:X", + "dts_neo6_cinema": "DTS Neo:6 Cinema", + "dts_neo6_music": "DTS Neo:6 Music" + } + }, + "zone_equalizer_mode": { + "state": { + "manual": "Manual", + "auto": "Auto", + "bypass": "Bypass" + } + }, + "zone_link_audio_quality": { + "state": { + "compressed": "Compressed", + "uncompressed": "Uncompressed" + } + }, + "zone_link_control": { + "state": { + "standard": "Standard", + "speed": "Speed", + "stability": "Stability" + } + }, + "zone_link_audio_delay": { + "state": { + "audio_sync_on": "Audio Synchronization On", + "audio_sync_off": "Audio Synchronization Off", + "balanced": "Balanced", + "lip_sync": "Lip Synchronization", + "audio_sync": "Audio Synchronization" + } + } + } } } diff --git a/homeassistant/components/yamaha_musiccast/strings.select.json b/homeassistant/components/yamaha_musiccast/strings.select.json deleted file mode 100644 index a59eef6070d..00000000000 --- a/homeassistant/components/yamaha_musiccast/strings.select.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "state": { - "yamaha_musiccast__dimmer": { - "auto": "Auto" - }, - "yamaha_musiccast__zone_sleep": { - "off": "Off", - "30 min": "30 Minutes", - "60 min": "60 Minutes", - "90 min": "90 Minutes", - "120 min": "120 Minutes" - }, - "yamaha_musiccast__zone_tone_control_mode": { - "manual": "Manual", - "auto": "Auto", - "bypass": "Bypass" - }, - "yamaha_musiccast__zone_surr_decoder_type": { - "toggle": "Toggle", - "auto": "Auto", - "dolby_pl": "Dolby ProLogic", - "dolby_pl2x_movie": "Dolby ProLogic 2x Movie", - "dolby_pl2x_music": "Dolby ProLogic 2x Music", - "dolby_pl2x_game": "Dolby ProLogic 2x Game", - "dolby_surround": "Dolby Surround", - "dts_neural_x": "DTS Neural:X", - "dts_neo6_cinema": "DTS Neo:6 Cinema", - "dts_neo6_music": "DTS Neo:6 Music" - }, - "yamaha_musiccast__zone_equalizer_mode": { - "manual": "Manual", - "auto": "Auto", - "bypass": "Bypass" - }, - "yamaha_musiccast__zone_link_audio_quality": { - "compressed": "Compressed", - "uncompressed": "Uncompressed" - }, - "yamaha_musiccast__zone_link_control": { - "standard": "Standard", - "speed": "Speed", - "stability": "Stability" - }, - "yamaha_musiccast__zone_link_audio_delay": { - "audio_sync_on": "Audio Synchronization On", - "audio_sync_off": "Audio Synchronization Off", - "balanced": "Balanced", - "lip_sync": "Lip Synchronization", - "audio_sync": "Audio Synchronization" - } - } -} diff --git a/homeassistant/components/yamaha_musiccast/translations/bg.json b/homeassistant/components/yamaha_musiccast/translations/bg.json index eec876d2be6..ecc1ff25eae 100644 --- a/homeassistant/components/yamaha_musiccast/translations/bg.json +++ b/homeassistant/components/yamaha_musiccast/translations/bg.json @@ -13,5 +13,29 @@ } } } + }, + "entity": { + "select": { + "zone_link_control": { + "state": { + "speed": "\u0421\u043a\u043e\u0440\u043e\u0441\u0442" + } + }, + "zone_sleep": { + "state": { + "120 min": "120 \u043c\u0438\u043d\u0443\u0442\u0438", + "30 min": "30 \u043c\u0438\u043d\u0443\u0442\u0438", + "60 min": "60 \u043c\u0438\u043d\u0443\u0442\u0438", + "90 min": "90 \u043c\u0438\u043d\u0443\u0442\u0438", + "off": "\u0418\u0437\u043a\u043b." + } + }, + "zone_surr_decoder_type": { + "state": { + "dolby_pl": "Dolby ProLogic", + "dolby_surround": "Dolby Surround" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/yamaha_musiccast/translations/ca.json b/homeassistant/components/yamaha_musiccast/translations/ca.json index e1ff37eeb4f..977ff6f1759 100644 --- a/homeassistant/components/yamaha_musiccast/translations/ca.json +++ b/homeassistant/components/yamaha_musiccast/translations/ca.json @@ -19,5 +19,73 @@ "description": "Configura la integraci\u00f3 de MusicCast amb Home Assistant." } } + }, + "entity": { + "select": { + "dimmer": { + "state": { + "auto": "Autom\u00e0tic" + } + }, + "zone_equalizer_mode": { + "state": { + "auto": "Autom\u00e0tic", + "bypass": "Pont", + "manual": "Manual" + } + }, + "zone_link_audio_delay": { + "state": { + "audio_sync": "Sincronitzaci\u00f3 d'\u00e0udio", + "audio_sync_off": "Sincronitzaci\u00f3 d'\u00e0udio OFF", + "audio_sync_on": "Sincronitzaci\u00f3 d'\u00e0udio ON", + "balanced": "Equilibrat", + "lip_sync": "Sincronitzaci\u00f3 Lip" + } + }, + "zone_link_audio_quality": { + "state": { + "compressed": "Amb compressi\u00f3", + "uncompressed": "Sense compressi\u00f3" + } + }, + "zone_link_control": { + "state": { + "speed": "Velocitat", + "stability": "Estabilitat", + "standard": "Est\u00e0ndard" + } + }, + "zone_sleep": { + "state": { + "120 min": "120 minuts", + "30 min": "30 minuts", + "60 min": "60 minuts", + "90 min": "90 minuts", + "off": "OFF" + } + }, + "zone_surr_decoder_type": { + "state": { + "auto": "Autom\u00e0tic", + "dolby_pl": "Dolby ProLogic", + "dolby_pl2x_game": "Dolby ProLogic 2x Joc", + "dolby_pl2x_movie": "Dolby ProLogic 2x Pel\u00b7l\u00edcula", + "dolby_pl2x_music": "Dolby ProLogic 2x M\u00fasica", + "dolby_surround": "Dolby Surround", + "dts_neo6_cinema": "DTS Neo:6 Cinema", + "dts_neo6_music": "DTS Neo:6 M\u00fasica", + "dts_neural_x": "DTS Neural:X", + "toggle": "Commuta" + } + }, + "zone_tone_control_mode": { + "state": { + "auto": "Autom\u00e0tic", + "bypass": "Pont", + "manual": "Manual" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/yamaha_musiccast/translations/de.json b/homeassistant/components/yamaha_musiccast/translations/de.json index 526e480602a..9c68fc00e1f 100644 --- a/homeassistant/components/yamaha_musiccast/translations/de.json +++ b/homeassistant/components/yamaha_musiccast/translations/de.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Ger\u00e4t ist bereits konfiguriert", - "yxc_control_url_missing": "Die Steuer-URL ist in der ssdp-Beschreibung nicht angegeben." + "yxc_control_url_missing": "Die Steuer-URL ist in der ssdp Beschreibung nicht angegeben." }, "error": { "no_musiccast_device": "Dieses Ger\u00e4t scheint kein MusicCast-Ger\u00e4t zu sein." @@ -19,5 +19,73 @@ "description": "Einrichten von MusicCast zur Integration mit Home Assistant." } } + }, + "entity": { + "select": { + "dimmer": { + "state": { + "auto": "Automatisch" + } + }, + "zone_equalizer_mode": { + "state": { + "auto": "Automatisch", + "bypass": "Bypass", + "manual": "Manuell" + } + }, + "zone_link_audio_delay": { + "state": { + "audio_sync": "Audio-Synchronisation", + "audio_sync_off": "Audio-Synchronisation Aus", + "audio_sync_on": "Audio-Synchronisation Ein", + "balanced": "Ausgeglichen", + "lip_sync": "Lippensynchronisation" + } + }, + "zone_link_audio_quality": { + "state": { + "compressed": "Komprimiert", + "uncompressed": "Unkomprimiert" + } + }, + "zone_link_control": { + "state": { + "speed": "Geschwindigkeit", + "stability": "Stabilit\u00e4t", + "standard": "Standard" + } + }, + "zone_sleep": { + "state": { + "120 min": "120 Minuten", + "30 min": "30 Minuten", + "60 min": "60 Minuten", + "90 min": "90 Minuten", + "off": "Aus" + } + }, + "zone_surr_decoder_type": { + "state": { + "auto": "Automatisch", + "dolby_pl": "Dolby ProLogic", + "dolby_pl2x_game": "Dolby ProLogic 2x Game", + "dolby_pl2x_movie": "Dolby ProLogic 2x Movie", + "dolby_pl2x_music": "Dolby ProLogic 2x Music", + "dolby_surround": "Dolby Surround", + "dts_neo6_cinema": "DTS Neo:6 Cinema", + "dts_neo6_music": "DTS Neo:6 Music", + "dts_neural_x": "DTS Neural:X", + "toggle": "Umschalten" + } + }, + "zone_tone_control_mode": { + "state": { + "auto": "Automatisch", + "bypass": "Bypass", + "manual": "Manuell" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/yamaha_musiccast/translations/el.json b/homeassistant/components/yamaha_musiccast/translations/el.json index b164109231e..21a602c2505 100644 --- a/homeassistant/components/yamaha_musiccast/translations/el.json +++ b/homeassistant/components/yamaha_musiccast/translations/el.json @@ -19,5 +19,73 @@ "description": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03c4\u03b5 \u03c4\u03bf MusicCast \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b5\u03bd\u03c3\u03c9\u03bc\u03b1\u03c4\u03c9\u03b8\u03b5\u03af \u03bc\u03b5 \u03c4\u03bf Home Assistant." } } + }, + "entity": { + "select": { + "dimmer": { + "state": { + "auto": "\u0391\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03bf" + } + }, + "zone_equalizer_mode": { + "state": { + "auto": "\u0391\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03bf", + "bypass": "\u03a0\u03b1\u03c1\u03ac\u03ba\u03b1\u03bc\u03c8\u03b7", + "manual": "\u03a7\u03b5\u03b9\u03c1\u03bf\u03ba\u03af\u03bd\u03b7\u03c4\u03bf" + } + }, + "zone_link_audio_delay": { + "state": { + "audio_sync": "\u03a3\u03c5\u03b3\u03c7\u03c1\u03bf\u03bd\u03b9\u03c3\u03bc\u03cc\u03c2 \u03ae\u03c7\u03bf\u03c5", + "audio_sync_off": "\u0391\u03bd\u03b5\u03bd\u03b5\u03c1\u03b3\u03cc\u03c2 \u03c3\u03c5\u03b3\u03c7\u03c1\u03bf\u03bd\u03b9\u03c3\u03bc\u03cc\u03c2 \u03ae\u03c7\u03bf\u03c5", + "audio_sync_on": "\u0395\u03bd\u03b5\u03c1\u03b3\u03cc\u03c2 \u03c3\u03c5\u03b3\u03c7\u03c1\u03bf\u03bd\u03b9\u03c3\u03bc\u03cc\u03c2 \u03ae\u03c7\u03bf\u03c5", + "balanced": "\u0399\u03c3\u03bf\u03c1\u03c1\u03bf\u03c0\u03b7\u03bc\u03ad\u03bd\u03bf", + "lip_sync": "\u03a3\u03c5\u03b3\u03c7\u03c1\u03bf\u03bd\u03b9\u03c3\u03bc\u03cc\u03c2 \u03c7\u03b5\u03b9\u03bb\u03b9\u03ce\u03bd" + } + }, + "zone_link_audio_quality": { + "state": { + "compressed": "\u03a3\u03c5\u03bc\u03c0\u03b9\u03b5\u03c3\u03bc\u03ad\u03bd\u03bf", + "uncompressed": "\u039c\u03b7 \u03c3\u03c5\u03bc\u03c0\u03b9\u03b5\u03c3\u03bc\u03ad\u03bd\u03bf" + } + }, + "zone_link_control": { + "state": { + "speed": "\u03a4\u03b1\u03c7\u03cd\u03c4\u03b7\u03c4\u03b1", + "stability": "\u03a3\u03c4\u03b1\u03b8\u03b5\u03c1\u03cc\u03c4\u03b7\u03c4\u03b1", + "standard": "\u03a4\u03c5\u03c0\u03b9\u03ba\u03cc" + } + }, + "zone_sleep": { + "state": { + "120 min": "120 \u03bb\u03b5\u03c0\u03c4\u03ac", + "30 min": "30 \u03bb\u03b5\u03c0\u03c4\u03ac", + "60 min": "60 \u03bb\u03b5\u03c0\u03c4\u03ac", + "90 min": "90 \u03bb\u03b5\u03c0\u03c4\u03ac", + "off": "\u0391\u03bd\u03b5\u03bd\u03b5\u03c1\u03b3\u03cc" + } + }, + "zone_surr_decoder_type": { + "state": { + "auto": "\u0391\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03bf", + "dolby_pl": "Dolby ProLogic", + "dolby_pl2x_game": "\u03a0\u03b1\u03b9\u03c7\u03bd\u03af\u03b4\u03b9 Dolby ProLogic 2x", + "dolby_pl2x_movie": "\u03a4\u03b1\u03b9\u03bd\u03af\u03b1 Dolby ProLogic 2x", + "dolby_pl2x_music": "\u039c\u03bf\u03c5\u03c3\u03b9\u03ba\u03ae Dolby ProLogic 2x", + "dolby_surround": "Dolby Surround", + "dts_neo6_cinema": "DTS Neo:6 \u039a\u03b9\u03bd\u03b7\u03bc\u03b1\u03c4\u03bf\u03b3\u03c1\u03ac\u03c6\u03bf\u03c2", + "dts_neo6_music": "DTS Neo:6 \u039c\u03bf\u03c5\u03c3\u03b9\u03ba\u03ae", + "dts_neural_x": "DTS Neural:X", + "toggle": "\u0395\u03bd\u03b1\u03bb\u03bb\u03b1\u03b3\u03ae" + } + }, + "zone_tone_control_mode": { + "state": { + "auto": "\u0391\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03bf", + "bypass": "\u03a0\u03b1\u03c1\u03ac\u03ba\u03b1\u03bc\u03c8\u03b7", + "manual": "\u03a7\u03b5\u03b9\u03c1\u03bf\u03ba\u03af\u03bd\u03b7\u03c4\u03bf" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/yamaha_musiccast/translations/en.json b/homeassistant/components/yamaha_musiccast/translations/en.json index 4c7f3b45f0b..f15b986ff8d 100644 --- a/homeassistant/components/yamaha_musiccast/translations/en.json +++ b/homeassistant/components/yamaha_musiccast/translations/en.json @@ -10,7 +10,7 @@ "flow_title": "MusicCast: {name}", "step": { "confirm": { - "description": "Do you want to start set up?" + "description": "Do you want to start setup?" }, "user": { "data": { @@ -19,5 +19,73 @@ "description": "Set up MusicCast to integrate with Home Assistant." } } + }, + "entity": { + "select": { + "dimmer": { + "state": { + "auto": "Auto" + } + }, + "zone_equalizer_mode": { + "state": { + "auto": "Auto", + "bypass": "Bypass", + "manual": "Manual" + } + }, + "zone_link_audio_delay": { + "state": { + "audio_sync": "Audio Synchronization", + "audio_sync_off": "Audio Synchronization Off", + "audio_sync_on": "Audio Synchronization On", + "balanced": "Balanced", + "lip_sync": "Lip Synchronization" + } + }, + "zone_link_audio_quality": { + "state": { + "compressed": "Compressed", + "uncompressed": "Uncompressed" + } + }, + "zone_link_control": { + "state": { + "speed": "Speed", + "stability": "Stability", + "standard": "Standard" + } + }, + "zone_sleep": { + "state": { + "120 min": "120 Minutes", + "30 min": "30 Minutes", + "60 min": "60 Minutes", + "90 min": "90 Minutes", + "off": "Off" + } + }, + "zone_surr_decoder_type": { + "state": { + "auto": "Auto", + "dolby_pl": "Dolby ProLogic", + "dolby_pl2x_game": "Dolby ProLogic 2x Game", + "dolby_pl2x_movie": "Dolby ProLogic 2x Movie", + "dolby_pl2x_music": "Dolby ProLogic 2x Music", + "dolby_surround": "Dolby Surround", + "dts_neo6_cinema": "DTS Neo:6 Cinema", + "dts_neo6_music": "DTS Neo:6 Music", + "dts_neural_x": "DTS Neural:X", + "toggle": "Toggle" + } + }, + "zone_tone_control_mode": { + "state": { + "auto": "Auto", + "bypass": "Bypass", + "manual": "Manual" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/yamaha_musiccast/translations/es.json b/homeassistant/components/yamaha_musiccast/translations/es.json index e5c24d474b8..2c9649e8a2e 100644 --- a/homeassistant/components/yamaha_musiccast/translations/es.json +++ b/homeassistant/components/yamaha_musiccast/translations/es.json @@ -19,5 +19,73 @@ "description": "Configura MusicCast para integrarse con Home Assistant." } } + }, + "entity": { + "select": { + "dimmer": { + "state": { + "auto": "Autom\u00e1tico" + } + }, + "zone_equalizer_mode": { + "state": { + "auto": "Autom\u00e1tico", + "bypass": "Puentear", + "manual": "Manual" + } + }, + "zone_link_audio_delay": { + "state": { + "audio_sync": "Sincronizaci\u00f3n de audio", + "audio_sync_off": "Sincronizaci\u00f3n de audio desactivada", + "audio_sync_on": "Sincronizaci\u00f3n de audio activada", + "balanced": "Equilibrado", + "lip_sync": "Sincronizaci\u00f3n de labios" + } + }, + "zone_link_audio_quality": { + "state": { + "compressed": "Comprimido", + "uncompressed": "Sin comprimir" + } + }, + "zone_link_control": { + "state": { + "speed": "Velocidad", + "stability": "Estabilidad", + "standard": "Est\u00e1ndar" + } + }, + "zone_sleep": { + "state": { + "120 min": "120 minutos", + "30 min": "30 minutos", + "60 min": "60 minutos", + "90 min": "90 minutos", + "off": "Apagado" + } + }, + "zone_surr_decoder_type": { + "state": { + "auto": "Autom\u00e1tico", + "dolby_pl": "Dolby ProLogic", + "dolby_pl2x_game": "Dolby ProLogic 2x Game", + "dolby_pl2x_movie": "Dolby ProLogic 2x Movie", + "dolby_pl2x_music": "Dolby ProLogic 2x Music", + "dolby_surround": "Dolby Surround", + "dts_neo6_cinema": "DTS Neo:6 Cinema", + "dts_neo6_music": "DTS Neo:6 Music", + "dts_neural_x": "DTS Neural:X", + "toggle": "Alternar" + } + }, + "zone_tone_control_mode": { + "state": { + "auto": "Autom\u00e1tico", + "bypass": "Puentear", + "manual": "Manual" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/yamaha_musiccast/translations/et.json b/homeassistant/components/yamaha_musiccast/translations/et.json index 8283298f11b..561cf8acc64 100644 --- a/homeassistant/components/yamaha_musiccast/translations/et.json +++ b/homeassistant/components/yamaha_musiccast/translations/et.json @@ -19,5 +19,73 @@ "description": "Seadista MusicCast'i sidumine Home Assistantiga." } } + }, + "entity": { + "select": { + "dimmer": { + "state": { + "auto": "Automaatne" + } + }, + "zone_equalizer_mode": { + "state": { + "auto": "Automaatne", + "bypass": "M\u00f6\u00f6daviik", + "manual": "K\u00e4sitsi" + } + }, + "zone_link_audio_delay": { + "state": { + "audio_sync": "Heli s\u00fcnkroonimine", + "audio_sync_off": "Heli s\u00fcnkroonimine v\u00e4ljas", + "audio_sync_on": "Heli s\u00fcnkroonimine sees", + "balanced": "Tasakaalustatud", + "lip_sync": "Huulte s\u00fcnkroonimine" + } + }, + "zone_link_audio_quality": { + "state": { + "compressed": "Tihendatud", + "uncompressed": "Tihendamata" + } + }, + "zone_link_control": { + "state": { + "speed": "Kiirus", + "stability": "Stabiilne", + "standard": "Tavaline" + } + }, + "zone_sleep": { + "state": { + "120 min": "120 minutit", + "30 min": "30 minutit", + "60 min": "60 minutit", + "90 min": "90 minutit", + "off": "V\u00e4ljas" + } + }, + "zone_surr_decoder_type": { + "state": { + "auto": "Automaatne", + "dolby_pl": "Dolby ProLogic", + "dolby_pl2x_game": "Dolby ProLogic 2x Game", + "dolby_pl2x_movie": "Dolby ProLogic 2x Movie", + "dolby_pl2x_music": "Dolby ProLogic 2x Music", + "dolby_surround": "Dolby Surround", + "dts_neo6_cinema": "DTS Neo:6 Cinema", + "dts_neo6_music": "DTS Neo:6 Music", + "dts_neural_x": "DTS Neural:X", + "toggle": "Muuda olekut" + } + }, + "zone_tone_control_mode": { + "state": { + "auto": "Automaatne", + "bypass": "M\u00f6\u00f6daviik", + "manual": "K\u00e4sitsi" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/yamaha_musiccast/translations/hu.json b/homeassistant/components/yamaha_musiccast/translations/hu.json index fc2672f5839..ad078116f84 100644 --- a/homeassistant/components/yamaha_musiccast/translations/hu.json +++ b/homeassistant/components/yamaha_musiccast/translations/hu.json @@ -19,5 +19,73 @@ "description": "\u00c1ll\u00edtsa be a MusicCast-ot a Homeassistanttal val\u00f3 integr\u00e1ci\u00f3hoz." } } + }, + "entity": { + "select": { + "dimmer": { + "state": { + "auto": "Automatikus" + } + }, + "zone_equalizer_mode": { + "state": { + "auto": "Automatikus", + "bypass": "Kihagy\u00e1s", + "manual": "Manu\u00e1lis" + } + }, + "zone_link_audio_delay": { + "state": { + "audio_sync": "Audi\u00f3 szinkroniz\u00e1l\u00e1s", + "audio_sync_off": "Audi\u00f3 szinkroniz\u00e1l\u00e1s kikapcsolva", + "audio_sync_on": "Audi\u00f3 szinkroniz\u00e1l\u00e1s bekapcsolva", + "balanced": "Kiegyens\u00falyozott", + "lip_sync": "K\u00e9p-hang k\u00e9sleltet\u00e9s" + } + }, + "zone_link_audio_quality": { + "state": { + "compressed": "T\u00f6m\u00f6r\u00edtve", + "uncompressed": "T\u00f6m\u00f6r\u00edtetlen" + } + }, + "zone_link_control": { + "state": { + "speed": "Sebess\u00e9g", + "stability": "Stabilit\u00e1s", + "standard": "Standard" + } + }, + "zone_sleep": { + "state": { + "120 min": "120 perc", + "30 min": "30 perc", + "60 min": "60 perc", + "90 min": "90 perc", + "off": "Ki" + } + }, + "zone_surr_decoder_type": { + "state": { + "auto": "Automatikus", + "dolby_pl": "Dolby ProLogic", + "dolby_pl2x_game": "Dolby ProLogic 2x J\u00e1t\u00e9k", + "dolby_pl2x_movie": "Dolby ProLogic 2x Film", + "dolby_pl2x_music": "Dolby ProLogic 2x Zene", + "dolby_surround": "Dolby Surround", + "dts_neo6_cinema": "DTS Neo:6 Cinema", + "dts_neo6_music": "DTS Neo:6 Music", + "dts_neural_x": "DTS Neural:X", + "toggle": "Kapcsol\u00e1s" + } + }, + "zone_tone_control_mode": { + "state": { + "auto": "Automatikus", + "bypass": "Kihagy\u00e1s", + "manual": "Manu\u00e1lis" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/yamaha_musiccast/translations/id.json b/homeassistant/components/yamaha_musiccast/translations/id.json index 9f1a68abdfd..1fb8c36d91e 100644 --- a/homeassistant/components/yamaha_musiccast/translations/id.json +++ b/homeassistant/components/yamaha_musiccast/translations/id.json @@ -19,5 +19,73 @@ "description": "Siapkan MusicCast untuk diintegrasikan dengan Home Assistant." } } + }, + "entity": { + "select": { + "dimmer": { + "state": { + "auto": "Otomatis" + } + }, + "zone_equalizer_mode": { + "state": { + "auto": "Otomatis", + "bypass": "Pintas", + "manual": "Manual" + } + }, + "zone_link_audio_delay": { + "state": { + "audio_sync": "Sinkronisasi Audio", + "audio_sync_off": "Sinkronisasi Audio Mati", + "audio_sync_on": "Sinkronisasi Audio Nyala", + "balanced": "Seimbang", + "lip_sync": "Sinkronisasi Bibir" + } + }, + "zone_link_audio_quality": { + "state": { + "compressed": "Terkompresi", + "uncompressed": "Tidak terkompresi" + } + }, + "zone_link_control": { + "state": { + "speed": "Kecepatan", + "stability": "Stabilitas", + "standard": "Standar" + } + }, + "zone_sleep": { + "state": { + "120 min": "120 Menit", + "30 min": "30 Menit", + "60 min": "60 Menit", + "90 min": "90 Menit", + "off": "Mati" + } + }, + "zone_surr_decoder_type": { + "state": { + "auto": "Otomatis", + "dolby_pl": "Dolby ProLogic", + "dolby_pl2x_game": "Dolby ProLogic 2x Game", + "dolby_pl2x_movie": "Dolby ProLogic 2x Movie", + "dolby_pl2x_music": "Dolby ProLogic 2x Music", + "dolby_surround": "Dolby Surround", + "dts_neo6_cinema": "DTS Neo:6 Cinema", + "dts_neo6_music": "DTS Neo:6 Music", + "dts_neural_x": "DTS Neural:X", + "toggle": "Alihkan" + } + }, + "zone_tone_control_mode": { + "state": { + "auto": "Otomatis", + "bypass": "Pintas", + "manual": "Manual" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/yamaha_musiccast/translations/it.json b/homeassistant/components/yamaha_musiccast/translations/it.json index 09a9e698ab5..095a98379f0 100644 --- a/homeassistant/components/yamaha_musiccast/translations/it.json +++ b/homeassistant/components/yamaha_musiccast/translations/it.json @@ -10,7 +10,7 @@ "flow_title": "MusicCast: {name}", "step": { "confirm": { - "description": "Vuoi iniziare la configurazione?" + "description": "Vuoi avviare la configurazione?" }, "user": { "data": { @@ -19,5 +19,73 @@ "description": "Configura MusicCast per l'integrazione con Home Assistant." } } + }, + "entity": { + "select": { + "dimmer": { + "state": { + "auto": "Automatico" + } + }, + "zone_equalizer_mode": { + "state": { + "auto": "Automatico", + "bypass": "Escluso", + "manual": "Manuale" + } + }, + "zone_link_audio_delay": { + "state": { + "audio_sync": "Sincronizzazione audio", + "audio_sync_off": "Sincronizzazione audio disattivata", + "audio_sync_on": "Sincronizzazione audio attivata", + "balanced": "Bilanciato", + "lip_sync": "Sincronizzazione labiale" + } + }, + "zone_link_audio_quality": { + "state": { + "compressed": "Compresso", + "uncompressed": "Non compresso" + } + }, + "zone_link_control": { + "state": { + "speed": "Velocit\u00e0", + "stability": "Stabilit\u00e0", + "standard": "Standard" + } + }, + "zone_sleep": { + "state": { + "120 min": "120 minuti", + "30 min": "30 minuti", + "60 min": "60 minuti", + "90 min": "90 minuti", + "off": "Spento" + } + }, + "zone_surr_decoder_type": { + "state": { + "auto": "Automatico", + "dolby_pl": "Dolby ProLogic", + "dolby_pl2x_game": "Dolby ProLogic 2x Gioco", + "dolby_pl2x_movie": "Dolby ProLogic 2x Cinema", + "dolby_pl2x_music": "Dolby ProLogic 2x Musica", + "dolby_surround": "Dolby Surround", + "dts_neo6_cinema": "DTS Neo:6 Cinema", + "dts_neo6_music": "DTS Neo:6 Musica", + "dts_neural_x": "DTS Neural:X", + "toggle": "Alterna" + } + }, + "zone_tone_control_mode": { + "state": { + "auto": "Automatico", + "bypass": "Escluso", + "manual": "Manuale" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/yamaha_musiccast/translations/ko.json b/homeassistant/components/yamaha_musiccast/translations/ko.json index 20ad990e862..e5e22476c21 100644 --- a/homeassistant/components/yamaha_musiccast/translations/ko.json +++ b/homeassistant/components/yamaha_musiccast/translations/ko.json @@ -1,8 +1,16 @@ { "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, "step": { "confirm": { "description": "\uc124\uc815\uc744 \uc2dc\uc791\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" + }, + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8" + } } } } diff --git a/homeassistant/components/yamaha_musiccast/translations/nl.json b/homeassistant/components/yamaha_musiccast/translations/nl.json index e1e31149c06..0cafd20a62e 100644 --- a/homeassistant/components/yamaha_musiccast/translations/nl.json +++ b/homeassistant/components/yamaha_musiccast/translations/nl.json @@ -19,5 +19,39 @@ "description": "Stel MusicCast in om te integreren met Home Assistant." } } + }, + "entity": { + "select": { + "zone_equalizer_mode": { + "state": { + "manual": "Handmatig" + } + }, + "zone_link_control": { + "state": { + "speed": "Snelheid" + } + }, + "zone_sleep": { + "state": { + "120 min": "120 minuten", + "30 min": "30 minuten", + "60 min": "60 minuten", + "90 min": "90 minuten", + "off": "Uit" + } + }, + "zone_surr_decoder_type": { + "state": { + "dolby_pl": "Dolby ProLogic", + "dolby_surround": "Dolby Surround" + } + }, + "zone_tone_control_mode": { + "state": { + "manual": "Handmatig" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/yamaha_musiccast/translations/no.json b/homeassistant/components/yamaha_musiccast/translations/no.json index 5381cdc5d67..b7ff15aa85f 100644 --- a/homeassistant/components/yamaha_musiccast/translations/no.json +++ b/homeassistant/components/yamaha_musiccast/translations/no.json @@ -19,5 +19,73 @@ "description": "Sett opp MusicCast for \u00e5 integrere med Home Assistant." } } + }, + "entity": { + "select": { + "dimmer": { + "state": { + "auto": "Auto" + } + }, + "zone_equalizer_mode": { + "state": { + "auto": "Auto", + "bypass": "Bypass", + "manual": "H\u00e5ndbok" + } + }, + "zone_link_audio_delay": { + "state": { + "audio_sync": "Lydsynkronisering", + "audio_sync_off": "Lydsynkronisering av", + "audio_sync_on": "Lydsynkronisering p\u00e5", + "balanced": "Balansert", + "lip_sync": "Leppesynkronisering" + } + }, + "zone_link_audio_quality": { + "state": { + "compressed": "Komprimert", + "uncompressed": "Ukomprimert" + } + }, + "zone_link_control": { + "state": { + "speed": "Hastighet", + "stability": "Stabilitet", + "standard": "Standard" + } + }, + "zone_sleep": { + "state": { + "120 min": "120 minutter", + "30 min": "30 minutter", + "60 min": "60 minutter", + "90 min": "90 minutter", + "off": "Av" + } + }, + "zone_surr_decoder_type": { + "state": { + "auto": "Auto", + "dolby_pl": "Dolby ProLogic", + "dolby_pl2x_game": "Dolby ProLogic 2x-spill", + "dolby_pl2x_movie": "Dolby ProLogic 2x film", + "dolby_pl2x_music": "Dolby ProLogic 2x musikk", + "dolby_surround": "Dolby Surround", + "dts_neo6_cinema": "DTS Neo:6 kino", + "dts_neo6_music": "DTS Neo:6 musikk", + "dts_neural_x": "DTS Neural:X", + "toggle": "Veksle" + } + }, + "zone_tone_control_mode": { + "state": { + "auto": "Auto", + "bypass": "Bypass", + "manual": "Manuell" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/yamaha_musiccast/translations/pl.json b/homeassistant/components/yamaha_musiccast/translations/pl.json index d22126557d0..07c6f67f91c 100644 --- a/homeassistant/components/yamaha_musiccast/translations/pl.json +++ b/homeassistant/components/yamaha_musiccast/translations/pl.json @@ -19,5 +19,73 @@ "description": "Skonfiguruj MusicCast, aby zintegrowa\u0107 go z Home Assistantem." } } + }, + "entity": { + "select": { + "dimmer": { + "state": { + "auto": "auto" + } + }, + "zone_equalizer_mode": { + "state": { + "auto": "auto", + "bypass": "pomijanie", + "manual": "r\u0119czny" + } + }, + "zone_link_audio_delay": { + "state": { + "audio_sync": "synchronizacja d\u017awi\u0119ku", + "audio_sync_off": "synchronizacja d\u017awi\u0119ku wy\u0142\u0105czona", + "audio_sync_on": "synchronizacja d\u017awi\u0119ku w\u0142\u0105czona", + "balanced": "zr\u00f3wnowa\u017cone", + "lip_sync": "synchronizacja ust" + } + }, + "zone_link_audio_quality": { + "state": { + "compressed": "skompresowana", + "uncompressed": "nieskompresowana" + } + }, + "zone_link_control": { + "state": { + "speed": "pr\u0119dko\u015b\u0107", + "stability": "stabilno\u015b\u0107", + "standard": "normalny" + } + }, + "zone_sleep": { + "state": { + "120 min": "120 minut", + "30 min": "30 minut", + "60 min": "60 minut", + "90 min": "90 minut", + "off": "wy\u0142\u0105czone" + } + }, + "zone_surr_decoder_type": { + "state": { + "auto": "auto", + "dolby_pl": "Dolby ProLogic", + "dolby_pl2x_game": "Dolby ProLogic 2x (Gra)", + "dolby_pl2x_movie": "Dolby ProLogic 2x (Film)", + "dolby_pl2x_music": "Dolby ProLogic 2x (Muzyka)", + "dolby_surround": "Dolby Surround", + "dts_neo6_cinema": "DTS Neo:6 (Kino)", + "dts_neo6_music": "DTS Neo:6 (Muzyka)", + "dts_neural_x": "DTS Neural:X", + "toggle": "prze\u0142\u0105czanie" + } + }, + "zone_tone_control_mode": { + "state": { + "auto": "auto", + "bypass": "pomijanie", + "manual": "r\u0119czny" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/yamaha_musiccast/translations/pt-BR.json b/homeassistant/components/yamaha_musiccast/translations/pt-BR.json index eee52e2182b..615fc15c426 100644 --- a/homeassistant/components/yamaha_musiccast/translations/pt-BR.json +++ b/homeassistant/components/yamaha_musiccast/translations/pt-BR.json @@ -19,5 +19,73 @@ "description": "Configure o MusicCast para integrar com o Home Assistant." } } + }, + "entity": { + "select": { + "dimmer": { + "state": { + "auto": "Auto" + } + }, + "zone_equalizer_mode": { + "state": { + "auto": "Auto", + "bypass": "Desviar", + "manual": "Manual" + } + }, + "zone_link_audio_delay": { + "state": { + "audio_sync": "Sincroniza\u00e7\u00e3o de \u00e1udio", + "audio_sync_off": "Sincroniza\u00e7\u00e3o de \u00e1udio desligada", + "audio_sync_on": "Sincroniza\u00e7\u00e3o de \u00e1udio ativada", + "balanced": "Equilibrado", + "lip_sync": "Sincroniza\u00e7\u00e3o labial" + } + }, + "zone_link_audio_quality": { + "state": { + "compressed": "Comprimido", + "uncompressed": "Descomprimido" + } + }, + "zone_link_control": { + "state": { + "speed": "Velocidade", + "stability": "Estabilidade", + "standard": "Padr\u00e3o" + } + }, + "zone_sleep": { + "state": { + "120 min": "120 minutos", + "30 min": "30 minutos", + "60 min": "60 minutos", + "90 min": "90 minutos", + "off": "Desligado" + } + }, + "zone_surr_decoder_type": { + "state": { + "auto": "Auto", + "dolby_pl": "Dolby ProLogic", + "dolby_pl2x_game": "Dolby ProLogic 2x Jogo", + "dolby_pl2x_movie": "Dolby ProLogic 2x Filme", + "dolby_pl2x_music": "Dolby ProLogic 2x M\u00fasica", + "dolby_surround": "Dolby Surround", + "dts_neo6_cinema": "DTS Neo:6 Cinema", + "dts_neo6_music": "DTS Neo:6 M\u00fasica", + "dts_neural_x": "DTS Neural:X", + "toggle": "Alternar" + } + }, + "zone_tone_control_mode": { + "state": { + "auto": "Auto", + "bypass": "Desviar", + "manual": "Manual" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/yamaha_musiccast/translations/ru.json b/homeassistant/components/yamaha_musiccast/translations/ru.json index a09b1b4cf3c..d55bb5e6a96 100644 --- a/homeassistant/components/yamaha_musiccast/translations/ru.json +++ b/homeassistant/components/yamaha_musiccast/translations/ru.json @@ -19,5 +19,73 @@ "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 Home Assistant \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 MusicCast." } } + }, + "entity": { + "select": { + "dimmer": { + "state": { + "auto": "\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438" + } + }, + "zone_equalizer_mode": { + "state": { + "auto": "\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438", + "bypass": "\u0411\u0430\u0439\u043f\u0430\u0441", + "manual": "\u0420\u0443\u0447\u043d\u043e\u0439 \u0440\u0435\u0436\u0438\u043c" + } + }, + "zone_link_audio_delay": { + "state": { + "audio_sync": "\u0421\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u044f \u0437\u0432\u0443\u043a\u0430", + "audio_sync_off": "\u0421\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u044f \u0437\u0432\u0443\u043a\u0430 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0430", + "audio_sync_on": "\u0421\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u044f \u0437\u0432\u0443\u043a\u0430 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0430", + "balanced": "\u0421\u0431\u0430\u043b\u0430\u043d\u0441\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439", + "lip_sync": "\u0421\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u044f \u0433\u0443\u0431" + } + }, + "zone_link_audio_quality": { + "state": { + "compressed": "\u0421\u0436\u0430\u0442\u044b\u0439", + "uncompressed": "\u041d\u0435\u0441\u0436\u0430\u0442\u044b\u0439" + } + }, + "zone_link_control": { + "state": { + "speed": "\u0421\u043a\u043e\u0440\u043e\u0441\u0442\u044c", + "stability": "\u0421\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u043e\u0441\u0442\u044c", + "standard": "\u0421\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439" + } + }, + "zone_sleep": { + "state": { + "120 min": "120 \u043c\u0438\u043d\u0443\u0442", + "30 min": "30 \u043c\u0438\u043d\u0443\u0442", + "60 min": "60 \u043c\u0438\u043d\u0443\u0442", + "90 min": "90 \u043c\u0438\u043d\u0443\u0442", + "off": "\u0412\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u043e" + } + }, + "zone_surr_decoder_type": { + "state": { + "auto": "\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438", + "dolby_pl": "Dolby ProLogic", + "dolby_pl2x_game": "Dolby ProLogic 2x \u0418\u0433\u0440\u0430", + "dolby_pl2x_movie": "Dolby ProLogic 2x \u0424\u0438\u043b\u044c\u043c", + "dolby_pl2x_music": "Dolby ProLogic 2x \u041c\u0443\u0437\u044b\u043a\u0430", + "dolby_surround": "Dolby Surround", + "dts_neo6_cinema": "DTS Neo:6 \u041a\u0438\u043d\u043e\u0442\u0435\u0430\u0442\u0440", + "dts_neo6_music": "DTS Neo:6 \u041c\u0443\u0437\u044b\u043a\u0430", + "dts_neural_x": "DTS Neural:X", + "toggle": "\u041f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0438\u0442\u044c" + } + }, + "zone_tone_control_mode": { + "state": { + "auto": "\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438", + "bypass": "\u0411\u0430\u0439\u043f\u0430\u0441", + "manual": "\u0420\u0443\u0447\u043d\u043e\u0439 \u0440\u0435\u0436\u0438\u043c" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/yamaha_musiccast/translations/select.it.json b/homeassistant/components/yamaha_musiccast/translations/select.it.json index 0640173398f..b1311ac9ef1 100644 --- a/homeassistant/components/yamaha_musiccast/translations/select.it.json +++ b/homeassistant/components/yamaha_musiccast/translations/select.it.json @@ -5,7 +5,7 @@ }, "yamaha_musiccast__zone_equalizer_mode": { "auto": "Automatico", - "bypass": "Bypass", + "bypass": "Escluso", "manual": "Manuale" }, "yamaha_musiccast__zone_link_audio_delay": { @@ -45,7 +45,7 @@ }, "yamaha_musiccast__zone_tone_control_mode": { "auto": "Automatico", - "bypass": "Bypass", + "bypass": "Escluso", "manual": "Manuale" } } diff --git a/homeassistant/components/yamaha_musiccast/translations/select.sk.json b/homeassistant/components/yamaha_musiccast/translations/select.sk.json index 1e344784685..ff857c58c95 100644 --- a/homeassistant/components/yamaha_musiccast/translations/select.sk.json +++ b/homeassistant/components/yamaha_musiccast/translations/select.sk.json @@ -1,14 +1,52 @@ { "state": { + "yamaha_musiccast__dimmer": { + "auto": "Auto" + }, + "yamaha_musiccast__zone_equalizer_mode": { + "auto": "Auto", + "bypass": "Bypass", + "manual": "Manu\u00e1lne" + }, + "yamaha_musiccast__zone_link_audio_delay": { + "audio_sync": "Synchroniz\u00e1cia zvuku", + "audio_sync_off": "Synchroniz\u00e1cia zvuku je vypnut\u00e1", + "audio_sync_on": "Synchroniz\u00e1cia zvuku je zapnut\u00e1", + "balanced": "Vyv\u00e1\u017een\u00fd", + "lip_sync": "Synchroniz\u00e1cia pier" + }, "yamaha_musiccast__zone_link_audio_quality": { "compressed": "Komprimovan\u00e9", "uncompressed": "Nekomprimovan\u00e9" }, + "yamaha_musiccast__zone_link_control": { + "speed": "R\u00fdchlos\u0165", + "stability": "Stabilita", + "standard": "\u0160tandard" + }, "yamaha_musiccast__zone_sleep": { "120 min": "120 min\u00fat", "30 min": "30 min\u00fat", "60 min": "60 min\u00fat", - "90 min": "90 min\u00fat" + "90 min": "90 min\u00fat", + "off": "Vypnut\u00e9" + }, + "yamaha_musiccast__zone_surr_decoder_type": { + "auto": "Auto", + "dolby_pl": "Dolby ProLogic", + "dolby_pl2x_game": "Dolby ProLogic 2x Game", + "dolby_pl2x_movie": "Dolby ProLogic 2x Movie", + "dolby_pl2x_music": "Dolby ProLogic 2x Music", + "dolby_surround": "Dolby Surround", + "dts_neo6_cinema": "DTS Neo:6 Kino", + "dts_neo6_music": "DTS Neo:6 Hudba", + "dts_neural_x": "DTS Neural:X", + "toggle": "Prepn\u00fa\u0165" + }, + "yamaha_musiccast__zone_tone_control_mode": { + "auto": "Auto", + "bypass": "Bypass", + "manual": "Ru\u010dne" } } } \ No newline at end of file diff --git a/homeassistant/components/yamaha_musiccast/translations/sk.json b/homeassistant/components/yamaha_musiccast/translations/sk.json index 94a606fc1fe..96f9dd46b27 100644 --- a/homeassistant/components/yamaha_musiccast/translations/sk.json +++ b/homeassistant/components/yamaha_musiccast/translations/sk.json @@ -4,6 +4,10 @@ "already_configured": "Zariadenie je u\u017e nakonfigurovan\u00e9", "yxc_control_url_missing": "Riadiaca adresa URL nie je uveden\u00e1 v popise ssdp." }, + "error": { + "no_musiccast_device": "Zd\u00e1 sa, \u017ee toto zariadenie nie je zariadenie MusicCast." + }, + "flow_title": "MusicCast: {name}", "step": { "confirm": { "description": "Chcete za\u010da\u0165 nastavova\u0165?" @@ -11,6 +15,75 @@ "user": { "data": { "host": "Hostite\u013e" + }, + "description": "Nastavenie aplik\u00e1cie MusicCast na integr\u00e1ciu s aplik\u00e1ciou Home Assistant." + } + } + }, + "entity": { + "select": { + "dimmer": { + "state": { + "auto": "Auto" + } + }, + "zone_equalizer_mode": { + "state": { + "auto": "Auto", + "bypass": "Bypass", + "manual": "Manu\u00e1lne" + } + }, + "zone_link_audio_delay": { + "state": { + "audio_sync": "Synchroniz\u00e1cia zvuku", + "audio_sync_off": "Synchroniz\u00e1cia zvuku je vypnut\u00e1", + "audio_sync_on": "Synchroniz\u00e1cia zvuku je zapnut\u00e1", + "balanced": "Vyv\u00e1\u017een\u00e9", + "lip_sync": "Synchroniz\u00e1cia pier" + } + }, + "zone_link_audio_quality": { + "state": { + "compressed": "Komprimovan\u00e9", + "uncompressed": "Nekomprimovan\u00e9" + } + }, + "zone_link_control": { + "state": { + "speed": "R\u00fdchlos\u0165", + "stability": "Stabilita", + "standard": "\u0160tandard" + } + }, + "zone_sleep": { + "state": { + "120 min": "120 min\u00fat", + "30 min": "30 min\u00fat", + "60 min": "60 min\u00fat", + "90 min": "90 min\u00fat", + "off": "Vypnut\u00e9" + } + }, + "zone_surr_decoder_type": { + "state": { + "auto": "Auto", + "dolby_pl": "Dolby ProLogic", + "dolby_pl2x_game": "Dolby ProLogic 2x Game", + "dolby_pl2x_movie": "Dolby ProLogic 2x Movie", + "dolby_pl2x_music": "Dolby ProLogic 2x Music", + "dolby_surround": "Dolby Surround", + "dts_neo6_cinema": "DTS Neo:6 Kino", + "dts_neo6_music": "DTS Neo:6 Hudba", + "dts_neural_x": "DTS Neural:X", + "toggle": "Prepn\u00fa\u0165" + } + }, + "zone_tone_control_mode": { + "state": { + "auto": "Auto", + "bypass": "Bypass", + "manual": "Manu\u00e1lne" } } } diff --git a/homeassistant/components/yamaha_musiccast/translations/zh-Hant.json b/homeassistant/components/yamaha_musiccast/translations/zh-Hant.json index 45664e0b815..4931da48893 100644 --- a/homeassistant/components/yamaha_musiccast/translations/zh-Hant.json +++ b/homeassistant/components/yamaha_musiccast/translations/zh-Hant.json @@ -19,5 +19,73 @@ "description": "\u8a2d\u5b9a MusicCast \u4ee5\u6574\u5408\u81f3 Home Assistant\u3002" } } + }, + "entity": { + "select": { + "dimmer": { + "state": { + "auto": "\u81ea\u52d5" + } + }, + "zone_equalizer_mode": { + "state": { + "auto": "\u81ea\u52d5", + "bypass": "\u5ffd\u7565", + "manual": "\u624b\u52d5" + } + }, + "zone_link_audio_delay": { + "state": { + "audio_sync": "\u97f3\u6548\u540c\u6b65", + "audio_sync_off": "\u97f3\u6548\u540c\u6b65\u95dc\u9589", + "audio_sync_on": "\u97f3\u6548\u540c\u6b65\u958b\u555f", + "balanced": "\u5e73\u8861", + "lip_sync": "\u5507\u5f62\u540c\u6b65" + } + }, + "zone_link_audio_quality": { + "state": { + "compressed": "\u58d3\u7e2e", + "uncompressed": "\u672a\u58d3\u7e2e" + } + }, + "zone_link_control": { + "state": { + "speed": "\u6548\u80fd", + "stability": "\u7a69\u5b9a", + "standard": "\u6a19\u6e96" + } + }, + "zone_sleep": { + "state": { + "120 min": "120 \u5206\u9418", + "30 min": "30 \u5206\u9418", + "60 min": "60 \u5206\u9418", + "90 min": "90 \u5206\u9418", + "off": "\u95dc\u9589" + } + }, + "zone_surr_decoder_type": { + "state": { + "auto": "\u81ea\u52d5", + "dolby_pl": "Dolby ProLogic", + "dolby_pl2x_game": "Dolby ProLogic 2x \u904a\u6232\u6a21\u5f0f", + "dolby_pl2x_movie": "Dolby ProLogic 2x \u96fb\u5f71\u6a21\u5f0f", + "dolby_pl2x_music": "Dolby ProLogic 2x \u97f3\u6a02\u6a21\u5f0f", + "dolby_surround": "Dolby \u74b0\u7e5e\u97f3\u6548", + "dts_neo6_cinema": "DTS Neo:6 \u5287\u5834\u6a21\u5f0f", + "dts_neo6_music": "DTS Neo:6 \u97f3\u6a02\u6a21\u5f0f", + "dts_neural_x": "DTS Neural:X", + "toggle": "\u958b\u95dc" + } + }, + "zone_tone_control_mode": { + "state": { + "auto": "\u81ea\u52d5", + "bypass": "\u5ffd\u7565", + "manual": "\u624b\u52d5" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/yandex_transport/sensor.py b/homeassistant/components/yandex_transport/sensor.py index b7a846bbc67..fe27ddcdbbe 100644 --- a/homeassistant/components/yandex_transport/sensor.py +++ b/homeassistant/components/yandex_transport/sensor.py @@ -96,7 +96,10 @@ class DiscoverYandexTransport(SensorEntity): data = yandex_reply["data"] except KeyError as key_error: _LOGGER.warning( - "Exception KeyError was captured, missing key is %s. Yandex returned: %s", + ( + "Exception KeyError was captured, missing key is %s. Yandex" + " returned: %s" + ), key_error, yandex_reply, ) diff --git a/homeassistant/components/yeelight/manifest.json b/homeassistant/components/yeelight/manifest.json index f3d9d8b24ca..c72a9401c52 100644 --- a/homeassistant/components/yeelight/manifest.json +++ b/homeassistant/components/yeelight/manifest.json @@ -2,7 +2,7 @@ "domain": "yeelight", "name": "Yeelight", "documentation": "https://www.home-assistant.io/integrations/yeelight", - "requirements": ["yeelight==0.7.10", "async-upnp-client==0.32.3"], + "requirements": ["yeelight==0.7.10", "async-upnp-client==0.33.0"], "codeowners": ["@zewelor", "@shenxn", "@starkillerOG", "@alexyao2015"], "config_flow": true, "dependencies": ["network"], diff --git a/homeassistant/components/yeelight/scanner.py b/homeassistant/components/yeelight/scanner.py index 7a0d409b434..612edc29791 100644 --- a/homeassistant/components/yeelight/scanner.py +++ b/homeassistant/components/yeelight/scanner.py @@ -2,7 +2,7 @@ from __future__ import annotations import asyncio -from collections.abc import Awaitable, Callable, ValuesView +from collections.abc import Callable, ValuesView import contextlib from datetime import datetime from ipaddress import IPv4Address @@ -64,10 +64,11 @@ class YeelightScanner: for idx, source_ip in enumerate(await self._async_build_source_set()): self._connected_events.append(asyncio.Event()) - def _wrap_async_connected_idx(idx) -> Callable[[], Awaitable[None]]: + def _wrap_async_connected_idx(idx) -> Callable[[], None]: """Create a function to capture the idx cell variable.""" - async def _async_connected() -> None: + @callback + def _async_connected() -> None: self._connected_events[idx].set() return _async_connected @@ -75,11 +76,11 @@ class YeelightScanner: source = (str(source_ip), 0) self._listeners.append( SsdpSearchListener( - async_callback=self._async_process_entry, + callback=self._async_process_entry, search_target=SSDP_ST, target=SSDP_TARGET, source=source, - async_connect_callback=_wrap_async_connected_idx(idx), + connect_callback=_wrap_async_connected_idx(idx), ) ) @@ -180,7 +181,8 @@ class YeelightScanner: # of another discovery async_call_later(self._hass, 1, _async_start_flow) - async def _async_process_entry(self, headers: CaseInsensitiveDict) -> None: + @callback + def _async_process_entry(self, headers: CaseInsensitiveDict) -> None: """Process a discovery.""" _LOGGER.debug("Discovered via SSDP: %s", headers) unique_id = headers["id"] diff --git a/homeassistant/components/yeelight/strings.json b/homeassistant/components/yeelight/strings.json index 2614b7b7899..0ecbd134b6a 100644 --- a/homeassistant/components/yeelight/strings.json +++ b/homeassistant/components/yeelight/strings.json @@ -14,7 +14,7 @@ } }, "discovery_confirm": { - "description": "Do you want to setup {model} ({host})?" + "description": "Do you want to set up {model} ({host})?" } }, "error": { diff --git a/homeassistant/components/yeelight/translations/en.json b/homeassistant/components/yeelight/translations/en.json index b49e3d7658e..f0092de9d13 100644 --- a/homeassistant/components/yeelight/translations/en.json +++ b/homeassistant/components/yeelight/translations/en.json @@ -10,7 +10,7 @@ "flow_title": "{model} {id} ({host})", "step": { "discovery_confirm": { - "description": "Do you want to setup {model} ({host})?" + "description": "Do you want to set up {model} ({host})?" }, "pick_device": { "data": { diff --git a/homeassistant/components/yeelight/translations/pt.json b/homeassistant/components/yeelight/translations/pt.json index 923a9a3f4d1..59e650eb0d6 100644 --- a/homeassistant/components/yeelight/translations/pt.json +++ b/homeassistant/components/yeelight/translations/pt.json @@ -5,7 +5,7 @@ "no_devices_found": "Nenhum dispositivo encontrado na rede" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "step": { "discovery_confirm": { @@ -18,7 +18,7 @@ }, "user": { "data": { - "host": "Servidor" + "host": "Endere\u00e7o" }, "description": "Se voc\u00ea deixar o modelo vazio, ele ser\u00e1 detectado automaticamente." } diff --git a/homeassistant/components/yeelight/translations/sk.json b/homeassistant/components/yeelight/translations/sk.json index 62861427cf7..57296018bc2 100644 --- a/homeassistant/components/yeelight/translations/sk.json +++ b/homeassistant/components/yeelight/translations/sk.json @@ -20,7 +20,8 @@ "user": { "data": { "host": "Hostite\u013e" - } + }, + "description": "Ak nech\u00e1te hostite\u013ea pr\u00e1zdneho, na vyh\u013ead\u00e1vanie zariaden\u00ed sa pou\u017eije zis\u0165ovanie." } } }, @@ -28,7 +29,11 @@ "step": { "init": { "data": { - "model": "Model" + "model": "Model", + "nightlight_switch": "Pou\u017eite prep\u00edna\u010d no\u010dn\u00e9ho osvetlenia", + "save_on_change": "Ulo\u017ei\u0165 stav pri zmene", + "transition": "\u010cas prechodu (v ms)", + "use_music_mode": "Povoli\u0165 hudobn\u00fd re\u017eim" } } } diff --git a/homeassistant/components/yolink/climate.py b/homeassistant/components/yolink/climate.py index d79c7d0fa15..a151e30062d 100644 --- a/homeassistant/components/yolink/climate.py +++ b/homeassistant/components/yolink/climate.py @@ -16,7 +16,7 @@ from homeassistant.components.climate import ( HVACMode, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import TEMP_CELSIUS +from homeassistant.const import UnitOfTemperature from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -67,7 +67,7 @@ class YoLinkClimateEntity(YoLinkEntity, ClimateEntity): super().__init__(config_entry, coordinator) self._attr_unique_id = f"{coordinator.device.device_id}_climate" self._attr_name = f"{coordinator.device.device_name} (Thermostat)" - self._attr_temperature_unit = TEMP_CELSIUS + self._attr_temperature_unit = UnitOfTemperature.CELSIUS self._attr_fan_modes = [FAN_ON, FAN_AUTO] self._attr_min_temp = -10 self._attr_max_temp = 50 diff --git a/homeassistant/components/yolink/translations/pt.json b/homeassistant/components/yolink/translations/pt.json index 1ffa7cb5245..23f213daf95 100644 --- a/homeassistant/components/yolink/translations/pt.json +++ b/homeassistant/components/yolink/translations/pt.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Conta j\u00e1 configurada", - "authorize_url_timeout": "Tempo excedido a gerar um URL de autoriza\u00e7\u00e3o" + "authorize_url_timeout": "Excedido o tempo limite para gerar um URL de autoriza\u00e7\u00e3o" }, "step": { "pick_implementation": { diff --git a/homeassistant/components/yolink/translations/sk.json b/homeassistant/components/yolink/translations/sk.json index d502e9f3f7b..50700251a1a 100644 --- a/homeassistant/components/yolink/translations/sk.json +++ b/homeassistant/components/yolink/translations/sk.json @@ -17,6 +17,7 @@ "title": "Vyberte met\u00f3du overenia" }, "reauth_confirm": { + "description": "Integr\u00e1cia yolink mus\u00ed znova overi\u0165 v\u00e1\u0161 \u00fa\u010det", "title": "Znova overi\u0165 integr\u00e1ciu" } } diff --git a/homeassistant/components/youless/sensor.py b/homeassistant/components/youless/sensor.py index 53ffb223939..ae8b0e1691b 100644 --- a/homeassistant/components/youless/sensor.py +++ b/homeassistant/components/youless/sensor.py @@ -9,12 +9,7 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - CONF_DEVICE, - ENERGY_KILO_WATT_HOUR, - POWER_WATT, - VOLUME_CUBIC_METERS, -) +from homeassistant.const import CONF_DEVICE, UnitOfEnergy, UnitOfPower, UnitOfVolume from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -98,7 +93,7 @@ class YoulessBaseSensor(CoordinatorEntity, SensorEntity): class GasSensor(YoulessBaseSensor): """The Youless gas sensor.""" - _attr_native_unit_of_measurement = VOLUME_CUBIC_METERS + _attr_native_unit_of_measurement = UnitOfVolume.CUBIC_METERS _attr_device_class = SensorDeviceClass.GAS _attr_state_class = SensorStateClass.TOTAL_INCREASING @@ -117,7 +112,7 @@ class GasSensor(YoulessBaseSensor): class CurrentPowerSensor(YoulessBaseSensor): """The current power usage sensor.""" - _attr_native_unit_of_measurement = POWER_WATT + _attr_native_unit_of_measurement = UnitOfPower.WATT _attr_device_class = SensorDeviceClass.POWER _attr_state_class = SensorStateClass.MEASUREMENT @@ -136,7 +131,7 @@ class CurrentPowerSensor(YoulessBaseSensor): class DeliveryMeterSensor(YoulessBaseSensor): """The Youless delivery meter value sensor.""" - _attr_native_unit_of_measurement = ENERGY_KILO_WATT_HOUR + _attr_native_unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR _attr_device_class = SensorDeviceClass.ENERGY _attr_state_class = SensorStateClass.TOTAL_INCREASING @@ -162,7 +157,7 @@ class DeliveryMeterSensor(YoulessBaseSensor): class EnergyMeterSensor(YoulessBaseSensor): """The Youless low meter value sensor.""" - _attr_native_unit_of_measurement = ENERGY_KILO_WATT_HOUR + _attr_native_unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR _attr_device_class = SensorDeviceClass.ENERGY _attr_state_class = SensorStateClass.TOTAL_INCREASING @@ -194,7 +189,7 @@ class EnergyMeterSensor(YoulessBaseSensor): class ExtraMeterSensor(YoulessBaseSensor): """The Youless extra meter value sensor (s0).""" - _attr_native_unit_of_measurement = ENERGY_KILO_WATT_HOUR + _attr_native_unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR _attr_device_class = SensorDeviceClass.ENERGY _attr_state_class = SensorStateClass.TOTAL_INCREASING @@ -220,7 +215,7 @@ class ExtraMeterSensor(YoulessBaseSensor): class ExtraMeterPowerSensor(YoulessBaseSensor): """The Youless extra meter power value sensor (s0).""" - _attr_native_unit_of_measurement = POWER_WATT + _attr_native_unit_of_measurement = UnitOfPower.WATT _attr_device_class = SensorDeviceClass.POWER _attr_state_class = SensorStateClass.MEASUREMENT diff --git a/homeassistant/components/youless/translations/ko.json b/homeassistant/components/youless/translations/ko.json new file mode 100644 index 00000000000..15666044f5b --- /dev/null +++ b/homeassistant/components/youless/translations/ko.json @@ -0,0 +1,15 @@ +{ + "config": { + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8", + "name": "\uc774\ub984" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/youless/translations/pt.json b/homeassistant/components/youless/translations/pt.json index 3b5850222d9..439e6f27fc1 100644 --- a/homeassistant/components/youless/translations/pt.json +++ b/homeassistant/components/youless/translations/pt.json @@ -1,7 +1,7 @@ { "config": { "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" } } } \ No newline at end of file diff --git a/homeassistant/components/zamg/__init__.py b/homeassistant/components/zamg/__init__.py index 67fe7521f95..0e57bef64ff 100644 --- a/homeassistant/components/zamg/__init__.py +++ b/homeassistant/components/zamg/__init__.py @@ -3,16 +3,19 @@ from __future__ import annotations from homeassistant.config_entries import ConfigEntry from homeassistant.const import Platform -from homeassistant.core import HomeAssistant +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers import entity_registry as er -from .const import CONF_STATION_ID, DOMAIN +from .const import CONF_STATION_ID, DOMAIN, LOGGER from .coordinator import ZamgDataUpdateCoordinator -PLATFORMS = (Platform.WEATHER, Platform.SENSOR) +PLATFORMS = (Platform.SENSOR, Platform.WEATHER) async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Zamg from config entry.""" + await _async_migrate_entries(hass, entry) + coordinator = ZamgDataUpdateCoordinator(hass, entry=entry) station_id = entry.data[CONF_STATION_ID] coordinator.zamg.set_default_station(station_id) @@ -31,3 +34,45 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): hass.data[DOMAIN].pop(entry.entry_id) return unload_ok + + +async def _async_migrate_entries( + hass: HomeAssistant, config_entry: ConfigEntry +) -> bool: + """Migrate old entry.""" + entity_registry = er.async_get(hass) + + @callback + def update_unique_id(entry: er.RegistryEntry) -> dict[str, str] | None: + """Convert the unique_id from 'name_stationid' to 'station_id'. + + Example: 'WIEN/HOHE WARTE_11035' --> '11035'. + """ + if ( + entry.domain == Platform.WEATHER + and entry.unique_id != config_entry.data[CONF_STATION_ID] + ): + new_unique_id = config_entry.data[CONF_STATION_ID] + LOGGER.debug( + "Migrating entity '%s' unique_id from '%s' to '%s'", + entry.entity_id, + entry.unique_id, + new_unique_id, + ) + if existing_entity_id := entity_registry.async_get_entity_id( + entry.domain, entry.platform, new_unique_id + ): + LOGGER.debug( + "Cannot migrate to unique_id '%s', already exists for '%s'", + new_unique_id, + existing_entity_id, + ) + return None + return { + "new_unique_id": new_unique_id, + } + return None + + await er.async_migrate_entries(hass, config_entry.entry_id, update_unique_id) + + return True diff --git a/homeassistant/components/zamg/config_flow.py b/homeassistant/components/zamg/config_flow.py index 43434b1e8bd..86584fd7f1c 100644 --- a/homeassistant/components/zamg/config_flow.py +++ b/homeassistant/components/zamg/config_flow.py @@ -5,9 +5,10 @@ from typing import Any import voluptuous as vol from zamg import ZamgData +from zamg.exceptions import ZamgApiError, ZamgNoDataError, ZamgStationNotFoundError from homeassistant import config_entries -from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME +from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue @@ -26,28 +27,28 @@ class ZamgConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): self, user_input: dict[str, Any] | None = None ) -> FlowResult: """Handle a flow initiated by the user.""" - errors: dict[str, Any] = {} - if self._client is None: self._client = ZamgData() self._client.session = async_get_clientsession(self.hass) if user_input is None: - closest_station_id = await self._client.closest_station( - self.hass.config.latitude, - self.hass.config.longitude, - ) - LOGGER.debug("config_flow: closest station = %s", str(closest_station_id)) - stations = await self._client.zamg_stations() + try: + stations = await self._client.zamg_stations() + closest_station_id = await self._client.closest_station( + self.hass.config.latitude, + self.hass.config.longitude, + ) + except (ZamgApiError, ZamgNoDataError) as err: + LOGGER.error("Config_flow: Received error from ZAMG: %s", err) + return self.async_abort(reason="cannot_connect") + LOGGER.debug("config_flow: closest station = %s", closest_station_id) user_input = {} schema = vol.Schema( { - vol.Required( - CONF_STATION_ID, default=int(closest_station_id) - ): vol.In( + vol.Required(CONF_STATION_ID, default=closest_station_id): vol.In( { - int(station): f"{stations[station][2]} ({station})" + station: f"{stations[station][2]} ({station})" for station in stations } ) @@ -55,7 +56,7 @@ class ZamgConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): ) return self.async_show_form(step_id="user", data_schema=schema) - station_id = str(user_input[CONF_STATION_ID]) + station_id = user_input[CONF_STATION_ID] # Check if already configured await self.async_set_unique_id(station_id) @@ -64,22 +65,18 @@ class ZamgConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): try: self._client.set_default_station(station_id) await self._client.update() - except (ValueError, TypeError) as err: + except (ZamgApiError, ZamgNoDataError) as err: LOGGER.error("Config_flow: Received error from ZAMG: %s", err) - errors["base"] = "cannot_connect" - return self.async_abort( - reason="cannot_connect", description_placeholders=errors - ) + return self.async_abort(reason="cannot_connect") return self.async_create_entry( - title=user_input.get(CONF_NAME) or self._client.get_station_name, + title=self._client.get_station_name, data={CONF_STATION_ID: station_id}, ) async def async_step_import(self, config: dict[str, Any]) -> FlowResult: """Handle ZAMG configuration import.""" - station_id = str(config.get(CONF_STATION_ID)) - station_name = config.get(CONF_NAME) + station_id = config.get(CONF_STATION_ID) # create issue every time after restart # parameter is_persistent seems not working async_create_issue( @@ -92,45 +89,40 @@ class ZamgConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): translation_key="deprecated_yaml", ) - for entry in self.hass.config_entries.async_entries(DOMAIN): - if station_id in entry.data[CONF_STATION_ID]: - return self.async_abort( - reason="already_configured", - ) - if self._client is None: self._client = ZamgData() self._client.session = async_get_clientsession(self.hass) - if station_id not in await self._client.zamg_stations(): - LOGGER.warning( - "Configured station_id %s could not be found at zamg, adding the nearest weather station instead", + try: + if station_id not in await self._client.zamg_stations(): + LOGGER.warning( + ( + "Configured station_id %s could not be found at zamg, trying to" + " add nearest weather station instead" + ), + station_id, + ) + latitude = config.get(CONF_LATITUDE) or self.hass.config.latitude + longitude = config.get(CONF_LONGITUDE) or self.hass.config.longitude + station_id = await self._client.closest_station(latitude, longitude) + + # Check if already configured + await self.async_set_unique_id(station_id) + self._abort_if_unique_id_configured() + + LOGGER.debug( + "importing zamg station from configuration.yaml: station_id = %s", station_id, ) - latitude = config.get(CONF_LATITUDE) or self.hass.config.latitude - longitude = config.get(CONF_LONGITUDE) or self.hass.config.longitude - station_id = await self._client.closest_station(latitude, longitude) - - if not station_name: - await self._client.zamg_stations() - self._client.set_default_station(station_id) - station_name = self._client.get_station_name - - for entry in self.hass.config_entries.async_entries(DOMAIN): - if station_id in entry.data[CONF_STATION_ID]: - return self.async_abort( - reason="already_configured", - ) - - LOGGER.debug( - "importing zamg station from configuration.yaml: station_id = %s, name = %s", - station_id, - station_name, - ) + except (ZamgApiError) as err: + LOGGER.error("Config_flow import: Received error from ZAMG: %s", err) + return self.async_abort(reason="cannot_connect") + except (ZamgStationNotFoundError) as err: + LOGGER.error("Config_flow import: Received error from ZAMG: %s", err) + return self.async_abort(reason="station_not_found") return await self.async_step_user( user_input={ - CONF_STATION_ID: int(station_id), - CONF_NAME: station_name, + CONF_STATION_ID: station_id, } ) diff --git a/homeassistant/components/zamg/coordinator.py b/homeassistant/components/zamg/coordinator.py index 69113e8e23f..4b25f4f7fbc 100644 --- a/homeassistant/components/zamg/coordinator.py +++ b/homeassistant/components/zamg/coordinator.py @@ -2,6 +2,7 @@ from __future__ import annotations from zamg import ZamgData as ZamgDevice +from zamg.exceptions import ZamgError, ZamgNoDataError from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant @@ -16,6 +17,7 @@ class ZamgDataUpdateCoordinator(DataUpdateCoordinator[ZamgDevice]): config_entry: ConfigEntry data: dict = {} + api_fields: list[str] | None = None def __init__( self, @@ -36,9 +38,13 @@ class ZamgDataUpdateCoordinator(DataUpdateCoordinator[ZamgDevice]): async def _async_update_data(self) -> ZamgDevice: """Fetch data from ZAMG api.""" try: - await self.zamg.zamg_stations() + if self.api_fields: + self.zamg.set_parameters(self.api_fields) + self.zamg.request_timeout = 60.0 device = await self.zamg.update() - except ValueError as error: + except ZamgNoDataError as error: + raise UpdateFailed("No response from API") from error + except (ZamgError) as error: raise UpdateFailed(f"Invalid response from API: {error}") from error self.data = device self.data["last_update"] = self.zamg.last_update diff --git a/homeassistant/components/zamg/manifest.json b/homeassistant/components/zamg/manifest.json index a6383ce8584..c920ca328f5 100644 --- a/homeassistant/components/zamg/manifest.json +++ b/homeassistant/components/zamg/manifest.json @@ -2,7 +2,7 @@ "domain": "zamg", "name": "Zentralanstalt f\u00fcr Meteorologie und Geodynamik (ZAMG)", "documentation": "https://www.home-assistant.io/integrations/zamg", - "requirements": ["zamg==0.1.1"], + "requirements": ["zamg==0.2.2"], "codeowners": ["@killer0071234"], "config_flow": true, "iot_class": "cloud_polling" diff --git a/homeassistant/components/zamg/sensor.py b/homeassistant/components/zamg/sensor.py index e40f42abf0b..6b3f8c10700 100644 --- a/homeassistant/components/zamg/sensor.py +++ b/homeassistant/components/zamg/sensor.py @@ -15,19 +15,17 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry from homeassistant.const import ( - ATTR_ATTRIBUTION, CONF_LATITUDE, CONF_LONGITUDE, CONF_MONITORED_CONDITIONS, CONF_NAME, DEGREE, - LENGTH_CENTIMETERS, - LENGTH_MILLIMETERS, PERCENTAGE, - PRESSURE_HPA, - SPEED_METERS_PER_SECOND, - TEMP_CELSIUS, - TIME_SECONDS, + UnitOfPrecipitationDepth, + UnitOfPressure, + UnitOfSpeed, + UnitOfTemperature, + UnitOfTime, ) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv @@ -46,6 +44,7 @@ from .const import ( DOMAIN, MANUFACTURER_URL, ) +from .coordinator import ZamgDataUpdateCoordinator _DType = Union[type[int], type[float], type[str]] @@ -67,7 +66,7 @@ SENSOR_TYPES: tuple[ZamgSensorEntityDescription, ...] = ( ZamgSensorEntityDescription( key="pressure", name="Pressure", - native_unit_of_measurement=PRESSURE_HPA, + native_unit_of_measurement=UnitOfPressure.HPA, device_class=SensorDeviceClass.PRESSURE, state_class=SensorStateClass.MEASUREMENT, para_name="P", @@ -76,7 +75,7 @@ SENSOR_TYPES: tuple[ZamgSensorEntityDescription, ...] = ( ZamgSensorEntityDescription( key="pressure_sealevel", name="Pressure at Sea Level", - native_unit_of_measurement=PRESSURE_HPA, + native_unit_of_measurement=UnitOfPressure.HPA, device_class=SensorDeviceClass.PRESSURE, state_class=SensorStateClass.MEASUREMENT, para_name="PRED", @@ -94,7 +93,8 @@ SENSOR_TYPES: tuple[ZamgSensorEntityDescription, ...] = ( ZamgSensorEntityDescription( key="wind_speed", name="Wind Speed", - native_unit_of_measurement=SPEED_METERS_PER_SECOND, + native_unit_of_measurement=UnitOfSpeed.METERS_PER_SECOND, + device_class=SensorDeviceClass.WIND_SPEED, state_class=SensorStateClass.MEASUREMENT, para_name="FFAM", dtype=float, @@ -110,7 +110,8 @@ SENSOR_TYPES: tuple[ZamgSensorEntityDescription, ...] = ( ZamgSensorEntityDescription( key="wind_max_speed", name="Top Wind Speed", - native_unit_of_measurement=SPEED_METERS_PER_SECOND, + native_unit_of_measurement=UnitOfSpeed.METERS_PER_SECOND, + device_class=SensorDeviceClass.WIND_SPEED, state_class=SensorStateClass.MEASUREMENT, para_name="FFX", dtype=float, @@ -126,7 +127,7 @@ SENSOR_TYPES: tuple[ZamgSensorEntityDescription, ...] = ( ZamgSensorEntityDescription( key="sun_last_10min", name="Sun Last 10 Minutes", - native_unit_of_measurement=TIME_SECONDS, + native_unit_of_measurement=UnitOfTime.SECONDS, state_class=SensorStateClass.MEASUREMENT, para_name="SO", dtype=int, @@ -134,7 +135,7 @@ SENSOR_TYPES: tuple[ZamgSensorEntityDescription, ...] = ( ZamgSensorEntityDescription( key="temperature", name="Temperature", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, para_name="TL", @@ -143,7 +144,7 @@ SENSOR_TYPES: tuple[ZamgSensorEntityDescription, ...] = ( ZamgSensorEntityDescription( key="temperature_average", name="Temperature Average", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, para_name="TLAM", @@ -152,7 +153,8 @@ SENSOR_TYPES: tuple[ZamgSensorEntityDescription, ...] = ( ZamgSensorEntityDescription( key="precipitation", name="Precipitation", - native_unit_of_measurement=LENGTH_MILLIMETERS, + native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS, + device_class=SensorDeviceClass.PRECIPITATION, state_class=SensorStateClass.MEASUREMENT, para_name="RR", dtype=float, @@ -160,7 +162,8 @@ SENSOR_TYPES: tuple[ZamgSensorEntityDescription, ...] = ( ZamgSensorEntityDescription( key="snow", name="Snow", - native_unit_of_measurement=LENGTH_CENTIMETERS, + native_unit_of_measurement=UnitOfPrecipitationDepth.CENTIMETERS, + device_class=SensorDeviceClass.PRECIPITATION, state_class=SensorStateClass.MEASUREMENT, para_name="SCHNEE", dtype=float, @@ -168,7 +171,7 @@ SENSOR_TYPES: tuple[ZamgSensorEntityDescription, ...] = ( ZamgSensorEntityDescription( key="dewpoint", name="Dew Point", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, para_name="TP", @@ -177,7 +180,7 @@ SENSOR_TYPES: tuple[ZamgSensorEntityDescription, ...] = ( ZamgSensorEntityDescription( key="dewpoint_average", name="Dew Point Average", - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, para_name="TPAM", @@ -187,9 +190,7 @@ SENSOR_TYPES: tuple[ZamgSensorEntityDescription, ...] = ( SENSOR_KEYS: list[str] = [desc.key for desc in SENSOR_TYPES] -API_FIELDS: dict[str, tuple[str, _DType]] = { - desc.para_name: (desc.key, desc.dtype) for desc in SENSOR_TYPES -} +API_FIELDS: list[str] = [desc.para_name for desc in SENSOR_TYPES] PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend( { @@ -216,10 +217,12 @@ async def async_setup_platform( ) -> None: """Set up the ZAMG sensor platform.""" # trigger import flow - await hass.config_entries.flow.async_init( - DOMAIN, - context={"source": SOURCE_IMPORT}, - data=config, + hass.async_create_task( + hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_IMPORT}, + data=config, + ) ) @@ -242,8 +245,12 @@ class ZamgSensor(CoordinatorEntity, SensorEntity): entity_description: ZamgSensorEntityDescription def __init__( - self, coordinator, name, station_id, description: ZamgSensorEntityDescription - ): + self, + coordinator: ZamgDataUpdateCoordinator, + name: str, + station_id: str, + description: ZamgSensorEntityDescription, + ) -> None: """Initialize the sensor.""" super().__init__(coordinator) self.entity_description = description @@ -257,21 +264,24 @@ class ZamgSensor(CoordinatorEntity, SensorEntity): configuration_url=MANUFACTURER_URL, name=coordinator.name, ) + coordinator.api_fields = API_FIELDS @property def native_value(self) -> StateType: """Return the state of the sensor.""" - return self.coordinator.data[self.station_id].get( - self.entity_description.para_name - )["data"] + try: + return self.coordinator.data[self.station_id][ + self.entity_description.para_name + ]["data"] + except (KeyError): + return None @property def extra_state_attributes(self) -> Mapping[str, str]: """Return the state attributes.""" - update_time = self.coordinator.data.get("last_update", "") + if (update_time := self.coordinator.data["last_update"]) is not None: + update_time = update_time.isoformat() return { - ATTR_ATTRIBUTION: ATTRIBUTION, - ATTR_STATION: self.coordinator.data.get("Name"), - CONF_STATION_ID: self.station_id, - ATTR_UPDATED: update_time.isoformat(), + ATTR_STATION: self.coordinator.data["Name"], + ATTR_UPDATED: update_time, } diff --git a/homeassistant/components/zamg/strings.json b/homeassistant/components/zamg/strings.json index 74b3c7c9fa2..6305f68efd9 100644 --- a/homeassistant/components/zamg/strings.json +++ b/homeassistant/components/zamg/strings.json @@ -10,11 +10,13 @@ } }, "error": { - "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]" + "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", + "station_not_found": "Station ID not found at zamg" }, "abort": { "already_configured": "[%key:common::config_flow::abort::already_configured_device%]", - "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]" + "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", + "station_not_found": "Station ID not found at zamg" } }, "issues": { diff --git a/homeassistant/components/zamg/translations/ca.json b/homeassistant/components/zamg/translations/ca.json index cc116086966..a43c5d215da 100644 --- a/homeassistant/components/zamg/translations/ca.json +++ b/homeassistant/components/zamg/translations/ca.json @@ -2,10 +2,12 @@ "config": { "abort": { "already_configured": "El dispositiu ja est\u00e0 configurat", - "cannot_connect": "Ha fallat la connexi\u00f3" + "cannot_connect": "Ha fallat la connexi\u00f3", + "station_not_found": "No s'ha trobat l'ID de l'estaci\u00f3 a zamg" }, "error": { - "cannot_connect": "Ha fallat la connexi\u00f3" + "cannot_connect": "Ha fallat la connexi\u00f3", + "station_not_found": "No s'ha trobat l'ID de l'estaci\u00f3 a zamg" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/zamg/translations/de.json b/homeassistant/components/zamg/translations/de.json index 9aeb4483a81..8ec2acfe302 100644 --- a/homeassistant/components/zamg/translations/de.json +++ b/homeassistant/components/zamg/translations/de.json @@ -2,10 +2,12 @@ "config": { "abort": { "already_configured": "Ger\u00e4t ist bereits konfiguriert", - "cannot_connect": "Verbindung fehlgeschlagen" + "cannot_connect": "Verbindung fehlgeschlagen", + "station_not_found": "Stations-ID bei zamg nicht gefunden" }, "error": { - "cannot_connect": "Verbindung fehlgeschlagen" + "cannot_connect": "Verbindung fehlgeschlagen", + "station_not_found": "Stations-ID bei zamg nicht gefunden" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/zamg/translations/el.json b/homeassistant/components/zamg/translations/el.json index 9ed237562b4..4e12b74b7cb 100644 --- a/homeassistant/components/zamg/translations/el.json +++ b/homeassistant/components/zamg/translations/el.json @@ -2,10 +2,12 @@ "config": { "abort": { "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", - "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2" + "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", + "station_not_found": "\u03a4\u03bf \u03b1\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c3\u03c4\u03b1\u03b8\u03bc\u03bf\u03cd \u03b4\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b5 \u03c3\u03c4\u03bf zamg" }, "error": { - "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2" + "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", + "station_not_found": "\u03a4\u03bf \u03b1\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c3\u03c4\u03b1\u03b8\u03bc\u03bf\u03cd \u03b4\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b5 \u03c3\u03c4\u03bf zamg" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/zamg/translations/en.json b/homeassistant/components/zamg/translations/en.json index 6931f9f96f5..dcebb308ba1 100644 --- a/homeassistant/components/zamg/translations/en.json +++ b/homeassistant/components/zamg/translations/en.json @@ -2,10 +2,12 @@ "config": { "abort": { "already_configured": "Device is already configured", - "cannot_connect": "Failed to connect" + "cannot_connect": "Failed to connect", + "station_not_found": "Station ID not found at zamg" }, "error": { - "cannot_connect": "Failed to connect" + "cannot_connect": "Failed to connect", + "station_not_found": "Station ID not found at zamg" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/zamg/translations/es.json b/homeassistant/components/zamg/translations/es.json index dbd8b9af040..432ce413e77 100644 --- a/homeassistant/components/zamg/translations/es.json +++ b/homeassistant/components/zamg/translations/es.json @@ -2,10 +2,12 @@ "config": { "abort": { "already_configured": "El dispositivo ya est\u00e1 configurado", - "cannot_connect": "No se pudo conectar" + "cannot_connect": "No se pudo conectar", + "station_not_found": "ID de estaci\u00f3n no encontrado en zamg" }, "error": { - "cannot_connect": "No se pudo conectar" + "cannot_connect": "No se pudo conectar", + "station_not_found": "ID de estaci\u00f3n no encontrado en zamg" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/zamg/translations/et.json b/homeassistant/components/zamg/translations/et.json index b3e0006f63b..5fc445a0544 100644 --- a/homeassistant/components/zamg/translations/et.json +++ b/homeassistant/components/zamg/translations/et.json @@ -2,10 +2,12 @@ "config": { "abort": { "already_configured": "Seade on juba h\u00e4\u00e4lestatud", - "cannot_connect": "\u00dchendamine nurjus" + "cannot_connect": "\u00dchendamine nurjus", + "station_not_found": "Jaama ID-d zamg-st ei leitud" }, "error": { - "cannot_connect": "\u00dchendamine nurjus" + "cannot_connect": "\u00dchendamine nurjus", + "station_not_found": "Jaama ID-d zamg-st ei leitud" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/zamg/translations/hu.json b/homeassistant/components/zamg/translations/hu.json index 339739ea266..935ed18e010 100644 --- a/homeassistant/components/zamg/translations/hu.json +++ b/homeassistant/components/zamg/translations/hu.json @@ -2,10 +2,12 @@ "config": { "abort": { "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", - "cannot_connect": "Sikertelen csatlakoz\u00e1s" + "cannot_connect": "Sikertelen csatlakoz\u00e1s", + "station_not_found": "Az \u00e1llom\u00e1sazonos\u00edt\u00f3 nem tal\u00e1lhat\u00f3 a zamg-n\u00e1l" }, "error": { - "cannot_connect": "Sikertelen csatlakoz\u00e1s" + "cannot_connect": "Sikertelen csatlakoz\u00e1s", + "station_not_found": "Az \u00e1llom\u00e1sazonos\u00edt\u00f3 nem tal\u00e1lhat\u00f3 a zamg-n\u00e1l" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/zamg/translations/id.json b/homeassistant/components/zamg/translations/id.json index 4330ae8c3de..f7180260b83 100644 --- a/homeassistant/components/zamg/translations/id.json +++ b/homeassistant/components/zamg/translations/id.json @@ -2,10 +2,12 @@ "config": { "abort": { "already_configured": "Perangkat sudah dikonfigurasi", - "cannot_connect": "Gagal terhubung" + "cannot_connect": "Gagal terhubung", + "station_not_found": "ID stasiun tidak ditemukan di zamg" }, "error": { - "cannot_connect": "Gagal terhubung" + "cannot_connect": "Gagal terhubung", + "station_not_found": "ID stasiun tidak ditemukan di zamg" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/zamg/translations/it.json b/homeassistant/components/zamg/translations/it.json index 8ec3f4a93bc..713ac101d08 100644 --- a/homeassistant/components/zamg/translations/it.json +++ b/homeassistant/components/zamg/translations/it.json @@ -2,10 +2,12 @@ "config": { "abort": { "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato", - "cannot_connect": "Impossibile connettersi" + "cannot_connect": "Impossibile connettersi", + "station_not_found": "ID stazione non trovato su zamg" }, "error": { - "cannot_connect": "Impossibile connettersi" + "cannot_connect": "Impossibile connettersi", + "station_not_found": "ID stazione non trovato su zamg" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/zamg/translations/ko.json b/homeassistant/components/zamg/translations/ko.json new file mode 100644 index 00000000000..e17552c26f8 --- /dev/null +++ b/homeassistant/components/zamg/translations/ko.json @@ -0,0 +1,11 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zamg/translations/no.json b/homeassistant/components/zamg/translations/no.json index 265fff5c70d..fdfd1ff509d 100644 --- a/homeassistant/components/zamg/translations/no.json +++ b/homeassistant/components/zamg/translations/no.json @@ -2,10 +2,12 @@ "config": { "abort": { "already_configured": "Enheten er allerede konfigurert", - "cannot_connect": "Tilkobling mislyktes" + "cannot_connect": "Tilkobling mislyktes", + "station_not_found": "Finner ikke stasjons-ID p\u00e5 zamg" }, "error": { - "cannot_connect": "Tilkobling mislyktes" + "cannot_connect": "Tilkobling mislyktes", + "station_not_found": "Finner ikke stasjons-ID p\u00e5 zamg" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/zamg/translations/pl.json b/homeassistant/components/zamg/translations/pl.json index 8837a14ecd6..36453bb40a4 100644 --- a/homeassistant/components/zamg/translations/pl.json +++ b/homeassistant/components/zamg/translations/pl.json @@ -2,10 +2,12 @@ "config": { "abort": { "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane", - "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia" + "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", + "station_not_found": "Nie znaleziono identyfikatora stacji w zamg" }, "error": { - "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia" + "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", + "station_not_found": "Nie znaleziono identyfikatora stacji w zamg" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/zamg/translations/pt-BR.json b/homeassistant/components/zamg/translations/pt-BR.json index e35fbd0e49e..f09f396ea08 100644 --- a/homeassistant/components/zamg/translations/pt-BR.json +++ b/homeassistant/components/zamg/translations/pt-BR.json @@ -2,10 +2,12 @@ "config": { "abort": { "already_configured": "Dispositivo j\u00e1 est\u00e1 configurado", - "cannot_connect": "Falha ao conectar" + "cannot_connect": "Falha ao conectar", + "station_not_found": "ID da esta\u00e7\u00e3o n\u00e3o encontrado no zamg" }, "error": { - "cannot_connect": "Falha ao conectar" + "cannot_connect": "Falha ao conectar", + "station_not_found": "ID da esta\u00e7\u00e3o n\u00e3o encontrado no zamg" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/zamg/translations/pt.json b/homeassistant/components/zamg/translations/pt.json new file mode 100644 index 00000000000..9742be327c7 --- /dev/null +++ b/homeassistant/components/zamg/translations/pt.json @@ -0,0 +1,11 @@ +{ + "config": { + "abort": { + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", + "cannot_connect": "A liga\u00e7\u00e3o falhou" + }, + "error": { + "cannot_connect": "A liga\u00e7\u00e3o falhou" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zamg/translations/ru.json b/homeassistant/components/zamg/translations/ru.json index 59439e4789b..07a6d9dfe7a 100644 --- a/homeassistant/components/zamg/translations/ru.json +++ b/homeassistant/components/zamg/translations/ru.json @@ -2,10 +2,12 @@ "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.", - "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f." + "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", + "station_not_found": "\u0418\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u0441\u0442\u0430\u043d\u0446\u0438\u0438 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d." }, "error": { - "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f." + "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", + "station_not_found": "\u0418\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u0441\u0442\u0430\u043d\u0446\u0438\u0438 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d." }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/zamg/translations/sk.json b/homeassistant/components/zamg/translations/sk.json index dddfc872188..75f8551bb55 100644 --- a/homeassistant/components/zamg/translations/sk.json +++ b/homeassistant/components/zamg/translations/sk.json @@ -2,18 +2,27 @@ "config": { "abort": { "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", - "cannot_connect": "Nepodarilo sa pripoji\u0165" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "station_not_found": "ID stanice sa nena\u0161lo na zamg" }, "error": { - "cannot_connect": "Nepodarilo sa pripoji\u0165" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "station_not_found": "ID stanice sa nena\u0161lo na zamg" }, "flow_title": "{name}", "step": { "user": { "data": { "station_id": "ID stanice (predvolen\u00e9 nastavenie na najbli\u017e\u0161iu stanicu)" - } + }, + "description": "Nastavte ZAMG na integr\u00e1ciu s Home Assistant." } } + }, + "issues": { + "deprecated_yaml": { + "description": "Konfigur\u00e1cia ZAMG pomocou YAML sa odstra\u0148uje. \n\n Va\u0161a existuj\u00faca konfigur\u00e1cia YAML bola importovan\u00e1 do pou\u017e\u00edvate\u013esk\u00e9ho rozhrania automaticky. \n\n Odstr\u00e1\u0148te konfigur\u00e1ciu ZAMG YAML zo s\u00faboru configuration.yaml a re\u0161tartujte Home Assistant, aby ste tento probl\u00e9m vyrie\u0161ili.", + "title": "Konfigur\u00e1cia ZAMG YAML sa odstra\u0148uje" + } } } \ No newline at end of file diff --git a/homeassistant/components/zamg/translations/zh-Hant.json b/homeassistant/components/zamg/translations/zh-Hant.json index 3e8bccdb4f5..ee28e894c89 100644 --- a/homeassistant/components/zamg/translations/zh-Hant.json +++ b/homeassistant/components/zamg/translations/zh-Hant.json @@ -2,10 +2,12 @@ "config": { "abort": { "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", - "cannot_connect": "\u9023\u7dda\u5931\u6557" + "cannot_connect": "\u9023\u7dda\u5931\u6557", + "station_not_found": "\u65bc zamg \u627e\u4e0d\u5230\u7ad9\u9ede ID" }, "error": { - "cannot_connect": "\u9023\u7dda\u5931\u6557" + "cannot_connect": "\u9023\u7dda\u5931\u6557", + "station_not_found": "\u65bc zamg \u627e\u4e0d\u5230\u7ad9\u9ede ID" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/zamg/weather.py b/homeassistant/components/zamg/weather.py index 39c9d77f071..c57574d97ec 100644 --- a/homeassistant/components/zamg/weather.py +++ b/homeassistant/components/zamg/weather.py @@ -47,10 +47,12 @@ async def async_setup_platform( ) -> None: """Set up the ZAMG weather platform.""" # trigger import flow - await hass.config_entries.flow.async_init( - DOMAIN, - context={"source": SOURCE_IMPORT}, - data=config, + hass.async_create_task( + hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_IMPORT}, + data=config, + ) ) @@ -67,12 +69,14 @@ async def async_setup_entry( class ZamgWeather(CoordinatorEntity, WeatherEntity): """Representation of a weather condition.""" + _attr_attribution = ATTRIBUTION + def __init__( - self, coordinator: ZamgDataUpdateCoordinator, name, station_id + self, coordinator: ZamgDataUpdateCoordinator, name: str, station_id: str ) -> None: """Initialise the platform with a data instance and station name.""" super().__init__(coordinator) - self._attr_unique_id = f"{name}_{station_id}" + self._attr_unique_id = station_id self._attr_name = f"ZAMG {name}" self.station_id = f"{station_id}" self._attr_device_info = DeviceInfo( @@ -93,47 +97,42 @@ class ZamgWeather(CoordinatorEntity, WeatherEntity): """Return the current condition.""" return None - @property - def attribution(self) -> str | None: - """Return the attribution.""" - return ATTRIBUTION - @property def native_temperature(self) -> float | None: """Return the platform temperature.""" try: - return float(self.coordinator.data[self.station_id].get("TL")["data"]) - except (TypeError, ValueError): + return float(self.coordinator.data[self.station_id]["TL"]["data"]) + except (KeyError, ValueError): return None @property def native_pressure(self) -> float | None: """Return the pressure.""" try: - return float(self.coordinator.data[self.station_id].get("P")["data"]) - except (TypeError, ValueError): + return float(self.coordinator.data[self.station_id]["P"]["data"]) + except (KeyError, ValueError): return None @property def humidity(self) -> float | None: """Return the humidity.""" try: - return float(self.coordinator.data[self.station_id].get("RFAM")["data"]) - except (TypeError, ValueError): + return float(self.coordinator.data[self.station_id]["RFAM"]["data"]) + except (KeyError, ValueError): return None @property def native_wind_speed(self) -> float | None: """Return the wind speed.""" try: - return float(self.coordinator.data[self.station_id].get("FF")["data"]) - except (TypeError, ValueError): + return float(self.coordinator.data[self.station_id]["FFAM"]["data"]) + except (KeyError, ValueError): return None @property def wind_bearing(self) -> float | str | None: """Return the wind bearing.""" try: - return self.coordinator.data[self.station_id].get("DD")["data"] - except (TypeError, ValueError): + return self.coordinator.data[self.station_id]["DD"]["data"] + except (KeyError, ValueError): return None diff --git a/homeassistant/components/zeroconf/__init__.py b/homeassistant/components/zeroconf/__init__.py index 82a9604a08c..a3865f5e168 100644 --- a/homeassistant/components/zeroconf/__init__.py +++ b/homeassistant/components/zeroconf/__init__.py @@ -579,7 +579,10 @@ def _suppress_invalid_properties(properties: dict) -> None: if len(prop_value.encode("utf-8")) > MAX_PROPERTY_VALUE_LEN: _LOGGER.error( - "The property '%s' was suppressed because it is longer than the maximum length of %d bytes: %s", + ( + "The property '%s' was suppressed because it is longer than the" + " maximum length of %d bytes: %s" + ), prop, MAX_PROPERTY_VALUE_LEN, prop_value, @@ -593,7 +596,10 @@ def _truncate_location_name_to_valid(location_name: str) -> str: return location_name _LOGGER.warning( - "The location name was truncated because it is longer than the maximum length of %d bytes: %s", + ( + "The location name was truncated because it is longer than the maximum" + " length of %d bytes: %s" + ), MAX_NAME_LEN, location_name, ) diff --git a/homeassistant/components/zeroconf/manifest.json b/homeassistant/components/zeroconf/manifest.json index 382cf42b54f..cd00e0b05ea 100644 --- a/homeassistant/components/zeroconf/manifest.json +++ b/homeassistant/components/zeroconf/manifest.json @@ -2,7 +2,7 @@ "domain": "zeroconf", "name": "Zero-configuration networking (zeroconf)", "documentation": "https://www.home-assistant.io/integrations/zeroconf", - "requirements": ["zeroconf==0.39.4"], + "requirements": ["zeroconf==0.47.1"], "dependencies": ["network", "api"], "codeowners": ["@bdraco"], "quality_scale": "internal", diff --git a/homeassistant/components/zeroconf/usage.py b/homeassistant/components/zeroconf/usage.py index 47798be3def..7cedb11a418 100644 --- a/homeassistant/components/zeroconf/usage.py +++ b/homeassistant/components/zeroconf/usage.py @@ -14,7 +14,11 @@ def install_multiple_zeroconf_catcher(hass_zc: HaZeroconf) -> None: def new_zeroconf_new(self: zeroconf.Zeroconf, *k: Any, **kw: Any) -> HaZeroconf: report( - "attempted to create another Zeroconf instance. Please use the shared Zeroconf via await homeassistant.components.zeroconf.async_get_instance(hass)", + ( + "attempted to create another Zeroconf instance. Please use the shared" + " Zeroconf via await" + " homeassistant.components.zeroconf.async_get_instance(hass)" + ), exclude_integrations={"zeroconf"}, error_if_core=False, ) diff --git a/homeassistant/components/zerproc/translations/en.json b/homeassistant/components/zerproc/translations/en.json index f05becffed3..1f858b1dfb5 100644 --- a/homeassistant/components/zerproc/translations/en.json +++ b/homeassistant/components/zerproc/translations/en.json @@ -6,7 +6,7 @@ }, "step": { "confirm": { - "description": "Do you want to start set up?" + "description": "Do you want to start setup?" } } } diff --git a/homeassistant/components/zerproc/translations/it.json b/homeassistant/components/zerproc/translations/it.json index 0278fe07bfe..278f85c5cff 100644 --- a/homeassistant/components/zerproc/translations/it.json +++ b/homeassistant/components/zerproc/translations/it.json @@ -6,7 +6,7 @@ }, "step": { "confirm": { - "description": "Vuoi iniziare la configurazione?" + "description": "Vuoi avviare la configurazione?" } } } diff --git a/homeassistant/components/zerproc/translations/pt.json b/homeassistant/components/zerproc/translations/pt.json index e25888655a9..02b15520b4f 100644 --- a/homeassistant/components/zerproc/translations/pt.json +++ b/homeassistant/components/zerproc/translations/pt.json @@ -6,7 +6,7 @@ }, "step": { "confirm": { - "description": "Quer dar inicio \u00e0 configura\u00e7\u00e3o?" + "description": "Quer dar in\u00edcio \u00e0 configura\u00e7\u00e3o?" } } } diff --git a/homeassistant/components/zha/api.py b/homeassistant/components/zha/api.py index eb5fc2e4343..59f2bfa331b 100644 --- a/homeassistant/components/zha/api.py +++ b/homeassistant/components/zha/api.py @@ -821,7 +821,10 @@ async def websocket_read_zigbee_cluster_attributes( [attribute], allow_cache=False, only_cache=False, manufacturer=manufacturer ) _LOGGER.debug( - "Read attribute for: %s: [%s] %s: [%s] %s: [%s] %s: [%s] %s: [%s] %s: [%s] %s: [%s],", + ( + "Read attribute for: %s: [%s] %s: [%s] %s: [%s] %s: [%s] %s: [%s] %s: [%s]" + " %s: [%s]," + ), ATTR_CLUSTER_ID, cluster_id, ATTR_CLUSTER_TYPE, @@ -1286,7 +1289,10 @@ def async_load_api(hass: HomeAssistant) -> None: manufacturer=manufacturer, ) _LOGGER.debug( - "Set attribute for: %s: [%s] %s: [%s] %s: [%s] %s: [%s] %s: [%s] %s: [%s] %s: [%s]", + ( + "Set attribute for: %s: [%s] %s: [%s] %s: [%s] %s: [%s] %s: [%s] %s:" + " [%s] %s: [%s]" + ), ATTR_CLUSTER_ID, cluster_id, ATTR_CLUSTER_TYPE, @@ -1338,7 +1344,10 @@ def async_load_api(hass: HomeAssistant) -> None: manufacturer=manufacturer, ) _LOGGER.debug( - "Issued command for: %s: [%s] %s: [%s] %s: [%s] %s: [%s] %s: [%s] %s: [%s] %s: [%s] %s: [%s]", + ( + "Issued command for: %s: [%s] %s: [%s] %s: [%s] %s: [%s] %s: [%s]" + " %s: [%s] %s: [%s] %s: [%s]" + ), ATTR_CLUSTER_ID, cluster_id, ATTR_CLUSTER_TYPE, diff --git a/homeassistant/components/zha/climate.py b/homeassistant/components/zha/climate.py index 4de07bf0d74..05b0528b330 100644 --- a/homeassistant/components/zha/climate.py +++ b/homeassistant/components/zha/climate.py @@ -33,8 +33,8 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( ATTR_TEMPERATURE, PRECISION_TENTHS, - TEMP_CELSIUS, Platform, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect @@ -139,7 +139,7 @@ class Thermostat(ZhaEntity, ClimateEntity): DEFAULT_MIN_TEMP = 7 _attr_precision = PRECISION_TENTHS - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS def __init__(self, unique_id, zha_device, channels, **kwargs): """Initialize ZHA Thermostat instance.""" diff --git a/homeassistant/components/zha/core/channels/lighting.py b/homeassistant/components/zha/core/channels/lighting.py index ffbbc32a7a5..a1f7c6df7e1 100644 --- a/homeassistant/components/zha/core/channels/lighting.py +++ b/homeassistant/components/zha/core/channels/lighting.py @@ -101,7 +101,10 @@ class ColorChannel(ZigbeeChannel): min_mireds = self.cluster.get("color_temp_physical_min", self.MIN_MIREDS) if min_mireds == 0: self.warning( - "[Min mireds is 0, setting to %s] Please open an issue on the quirks repo to have this device corrected", + ( + "[Min mireds is 0, setting to %s] Please open an issue on the" + " quirks repo to have this device corrected" + ), self.MIN_MIREDS, ) min_mireds = self.MIN_MIREDS @@ -113,7 +116,10 @@ class ColorChannel(ZigbeeChannel): max_mireds = self.cluster.get("color_temp_physical_max", self.MAX_MIREDS) if max_mireds == 0: self.warning( - "[Max mireds is 0, setting to %s] Please open an issue on the quirks repo to have this device corrected", + ( + "[Max mireds is 0, setting to %s] Please open an issue on the" + " quirks repo to have this device corrected" + ), self.MAX_MIREDS, ) max_mireds = self.MAX_MIREDS diff --git a/homeassistant/components/zha/core/device.py b/homeassistant/components/zha/core/device.py index dfed1ce0ebe..c0959cb40c0 100644 --- a/homeassistant/components/zha/core/device.py +++ b/homeassistant/components/zha/core/device.py @@ -397,7 +397,10 @@ class ZHADevice(LogMixin): or not self._channels.pools ): self.debug( - "last_seen is %s seconds ago and ping attempts have been exhausted, marking the device unavailable", + ( + "last_seen is %s seconds ago and ping attempts have been exhausted," + " marking the device unavailable" + ), difference, ) self.update_available(False) @@ -422,7 +425,10 @@ class ZHADevice(LogMixin): def update_available(self, available: bool) -> None: """Update device availability and signal entities.""" self.debug( - "Update device availability - device available: %s - new availability: %s - changed: %s", + ( + "Update device availability - device available: %s - new availability:" + " %s - changed: %s" + ), self.available, available, self.available ^ available, @@ -432,7 +438,8 @@ class ZHADevice(LogMixin): if availability_changed and available: # reinit channels then signal entities self.debug( - "Device availability changed and device became available, reinitializing channels" + "Device availability changed and device became available," + " reinitializing channels" ) self.hass.async_create_task(self._async_became_available()) return @@ -562,8 +569,10 @@ class ZHADevice(LogMixin): else: names.append( { - ATTR_NAME: f"unknown {endpoint.device_type} device_type " - f"of 0x{(endpoint.profile_id or 0xFFFF):04x} profile id" + ATTR_NAME: ( + f"unknown {endpoint.device_type} device_type " + f"of 0x{(endpoint.profile_id or 0xFFFF):04x} profile id" + ) } ) device_info[ATTR_ENDPOINT_NAMES] = names @@ -696,7 +705,8 @@ class ZHADevice(LogMixin): ) except KeyError as exc: raise ValueError( - f"Cluster {cluster_id} not found on endpoint {endpoint_id} while issuing command {command} with args {args}" + f"Cluster {cluster_id} not found on endpoint {endpoint_id} while" + f" issuing command {command} with args {args}" ) from exc commands: dict[int, ZCLCommandDef] = ( cluster.server_commands @@ -705,7 +715,10 @@ class ZHADevice(LogMixin): ) if args is not None: self.warning( - "args [%s] are deprecated and should be passed with the params key. The parameter names are: %s", + ( + "args [%s] are deprecated and should be passed with the params key." + " The parameter names are: %s" + ), args, [field.name for field in commands[command].schema.fields], ) @@ -790,7 +803,10 @@ class ZHADevice(LogMixin): await self._zigpy_device.endpoints[endpoint_id].remove_from_group(group_id) except (zigpy.exceptions.ZigbeeException, asyncio.TimeoutError) as ex: self.debug( - "Failed to remove endpoint: %s for device '%s' from group: 0x%04x ex: %s", + ( + "Failed to remove endpoint: %s for device '%s' from group: 0x%04x" + " ex: %s" + ), endpoint_id, self._zigpy_device.ieee, group_id, diff --git a/homeassistant/components/zha/core/gateway.py b/homeassistant/components/zha/core/gateway.py index 17adc5fc848..6a02a21781d 100644 --- a/homeassistant/components/zha/core/gateway.py +++ b/homeassistant/components/zha/core/gateway.py @@ -209,7 +209,10 @@ class ZHAGateway: zha_device.available = delta < zha_device.consider_unavailable_time delta_msg = f"{str(timedelta(seconds=delta))} ago" _LOGGER.debug( - "[%s](%s) restored as '%s', last seen: %s, consider_unavailable_time: %s seconds", + ( + "[%s](%s) restored as '%s', last seen: %s," + " consider_unavailable_time: %s seconds" + ), zha_device.nwk, zha_device.name, "available" if zha_device.available else "unavailable", @@ -649,7 +652,10 @@ class ZHAGateway: tasks = [] for member in members: _LOGGER.debug( - "Adding member with IEEE: %s and endpoint ID: %s to group: %s:0x%04x", + ( + "Adding member with IEEE: %s and endpoint ID: %s to group:" + " %s:0x%04x" + ), member.ieee, member.endpoint_id, name, diff --git a/homeassistant/components/zha/core/group.py b/homeassistant/components/zha/core/group.py index 7f1c9f09998..0c86a83c8b3 100644 --- a/homeassistant/components/zha/core/group.py +++ b/homeassistant/components/zha/core/group.py @@ -103,7 +103,10 @@ class ZHAGroupMember(LogMixin): ].remove_from_group(self._zha_group.group_id) except (zigpy.exceptions.ZigbeeException, asyncio.TimeoutError) as ex: self.debug( - "Failed to remove endpoint: %s for device '%s' from group: 0x%04x ex: %s", + ( + "Failed to remove endpoint: %s for device '%s' from group: 0x%04x" + " ex: %s" + ), self._endpoint_id, self._zha_device.ieee, self._zha_group.group_id, diff --git a/homeassistant/components/zha/core/helpers.py b/homeassistant/components/zha/core/helpers.py index 2bc7d53fd79..38a4c8e3149 100644 --- a/homeassistant/components/zha/core/helpers.py +++ b/homeassistant/components/zha/core/helpers.py @@ -349,10 +349,7 @@ def retryable_req( if delay: delay = uniform(delay * 0.75, delay * 1.25) channel.debug( - ( - "%s: retryable request #%d failed: %s. " - "Retrying in %ss" - ), + "%s: retryable request #%d failed: %s. Retrying in %ss", func.__name__, try_count, ex, diff --git a/homeassistant/components/zha/device_action.py b/homeassistant/components/zha/device_action.py index 01a08bc2f32..9867bc5cfbb 100644 --- a/homeassistant/components/zha/device_action.py +++ b/homeassistant/components/zha/device_action.py @@ -210,12 +210,14 @@ async def _execute_channel_command_based_action( if action_channel is None: raise InvalidDeviceAutomationConfig( - f"Unable to execute channel action - channel: {channel_name} action: {action_type}" + f"Unable to execute channel action - channel: {channel_name} action:" + f" {action_type}" ) if not hasattr(action_channel, action_type): raise InvalidDeviceAutomationConfig( - f"Unable to execute channel action - channel: {channel_name} action: {action_type}" + f"Unable to execute channel action - channel: {channel_name} action:" + f" {action_type}" ) await getattr(action_channel, action_type)(**config) diff --git a/homeassistant/components/zha/fan.py b/homeassistant/components/zha/fan.py index 6c1d2bf2bee..d4b31a69890 100644 --- a/homeassistant/components/zha/fan.py +++ b/homeassistant/components/zha/fan.py @@ -113,7 +113,8 @@ class BaseFan(FanEntity): """Set the preset mode for the fan.""" if preset_mode not in self.preset_modes: raise NotValidPresetModeError( - f"The preset_mode {preset_mode} is not a valid preset_mode: {self.preset_modes}" + f"The preset_mode {preset_mode} is not a valid preset_mode:" + f" {self.preset_modes}" ) await self._async_set_fan_mode(NAME_TO_PRESET_MODE[preset_mode]) @@ -288,7 +289,8 @@ class IkeaFan(BaseFan, ZhaEntity): """Set the preset mode for the fan.""" if preset_mode not in self.preset_modes: raise NotValidPresetModeError( - f"The preset_mode {preset_mode} is not a valid preset_mode: {self.preset_modes}" + f"The preset_mode {preset_mode} is not a valid preset_mode:" + f" {self.preset_modes}" ) await self._async_set_fan_mode(IKEA_NAME_TO_PRESET_MODE[preset_mode]) diff --git a/homeassistant/components/zha/manifest.json b/homeassistant/components/zha/manifest.json index ef5dbb91346..7f8568f1ab3 100644 --- a/homeassistant/components/zha/manifest.json +++ b/homeassistant/components/zha/manifest.json @@ -7,7 +7,7 @@ "bellows==0.34.5", "pyserial==3.5", "pyserial-asyncio==0.6", - "zha-quirks==0.0.89", + "zha-quirks==0.0.90", "zigpy-deconz==0.19.2", "zigpy==0.52.3", "zigpy-xbee==0.16.2", @@ -95,6 +95,10 @@ { "type": "_zigstar_gw._tcp.local.", "name": "*zigstar*" + }, + { + "type": "_slzb-06._tcp.local.", + "name": "slzb-06*" } ], "dependencies": ["file_upload"], diff --git a/homeassistant/components/zha/sensor.py b/homeassistant/components/zha/sensor.py index 003f5771b93..a66f3aa1fe8 100644 --- a/homeassistant/components/zha/sensor.py +++ b/homeassistant/components/zha/sensor.py @@ -18,27 +18,21 @@ from homeassistant.const import ( CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, CONCENTRATION_PARTS_PER_BILLION, CONCENTRATION_PARTS_PER_MILLION, - ELECTRIC_CURRENT_AMPERE, - ELECTRIC_POTENTIAL_VOLT, - ENERGY_KILO_WATT_HOUR, - FREQUENCY_HERTZ, LIGHT_LUX, PERCENTAGE, - POWER_VOLT_AMPERE, - POWER_WATT, - PRESSURE_HPA, - TEMP_CELSIUS, - TIME_HOURS, - TIME_MINUTES, - TIME_SECONDS, - VOLUME_CUBIC_FEET, - VOLUME_CUBIC_METERS, - VOLUME_FLOW_RATE_CUBIC_FEET_PER_MINUTE, - VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR, - VOLUME_GALLONS, - VOLUME_LITERS, Platform, + UnitOfApparentPower, + UnitOfElectricCurrent, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfFrequency, UnitOfMass, + UnitOfPower, + UnitOfPressure, + UnitOfTemperature, + UnitOfTime, + UnitOfVolume, + UnitOfVolumeFlowRate, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect @@ -129,7 +123,6 @@ class Sensor(ZhaEntity, SensorEntity): _decimals: int = 1 _divisor: int = 1 _multiplier: int | float = 1 - _unit: str | None = None def __init__( self, @@ -167,11 +160,6 @@ class Sensor(ZhaEntity, SensorEntity): self._channel, SIGNAL_ATTR_UPDATED, self.async_set_state ) - @property - def native_unit_of_measurement(self) -> str | None: - """Return the unit of measurement of this entity.""" - return self._unit - @property def native_value(self) -> StateType: """Return the state of the entity.""" @@ -221,7 +209,7 @@ class Battery(Sensor): _attr_state_class: SensorStateClass = SensorStateClass.MEASUREMENT _attr_entity_category = EntityCategory.DIAGNOSTIC _attr_name: str = "Battery" - _unit = PERCENTAGE + _attr_native_unit_of_measurement = PERCENTAGE @classmethod def create_entity( @@ -273,7 +261,7 @@ class ElectricalMeasurement(Sensor): _attr_should_poll = True # BaseZhaEntity defaults to False _attr_state_class: SensorStateClass = SensorStateClass.MEASUREMENT _attr_name: str = "Active power" - _unit = POWER_WATT + _attr_native_unit_of_measurement: str = UnitOfPower.WATT _div_mul_prefix = "ac_power" @property @@ -315,7 +303,7 @@ class ElectricalMeasurementApparentPower( _attr_device_class: SensorDeviceClass = SensorDeviceClass.APPARENT_POWER _attr_should_poll = False # Poll indirectly by ElectricalMeasurementSensor _attr_name: str = "Apparent power" - _unit = POWER_VOLT_AMPERE + _attr_native_unit_of_measurement = UnitOfApparentPower.VOLT_AMPERE _div_mul_prefix = "ac_power" @@ -327,7 +315,7 @@ class ElectricalMeasurementRMSCurrent(ElectricalMeasurement, id_suffix="rms_curr _attr_device_class: SensorDeviceClass = SensorDeviceClass.CURRENT _attr_should_poll = False # Poll indirectly by ElectricalMeasurementSensor _attr_name: str = "RMS current" - _unit = ELECTRIC_CURRENT_AMPERE + _attr_native_unit_of_measurement = UnitOfElectricCurrent.AMPERE _div_mul_prefix = "ac_current" @@ -336,10 +324,10 @@ class ElectricalMeasurementRMSVoltage(ElectricalMeasurement, id_suffix="rms_volt """RMS Voltage measurement.""" SENSOR_ATTR = "rms_voltage" - _attr_device_class: SensorDeviceClass = SensorDeviceClass.CURRENT + _attr_device_class: SensorDeviceClass = SensorDeviceClass.VOLTAGE _attr_should_poll = False # Poll indirectly by ElectricalMeasurementSensor _attr_name: str = "RMS voltage" - _unit = ELECTRIC_POTENTIAL_VOLT + _attr_native_unit_of_measurement = UnitOfElectricPotential.VOLT _div_mul_prefix = "ac_voltage" @@ -351,7 +339,7 @@ class ElectricalMeasurementFrequency(ElectricalMeasurement, id_suffix="ac_freque _attr_device_class: SensorDeviceClass = SensorDeviceClass.FREQUENCY _attr_should_poll = False # Poll indirectly by ElectricalMeasurementSensor _attr_name: str = "AC frequency" - _unit = FREQUENCY_HERTZ + _attr_native_unit_of_measurement = UnitOfFrequency.HERTZ _div_mul_prefix = "ac_frequency" @@ -363,7 +351,7 @@ class ElectricalMeasurementPowerFactor(ElectricalMeasurement, id_suffix="power_f _attr_device_class: SensorDeviceClass = SensorDeviceClass.POWER_FACTOR _attr_should_poll = False # Poll indirectly by ElectricalMeasurementSensor _attr_name: str = "Power factor" - _unit = PERCENTAGE + _attr_native_unit_of_measurement = PERCENTAGE @MULTI_MATCH( @@ -378,7 +366,7 @@ class Humidity(Sensor): _attr_state_class: SensorStateClass = SensorStateClass.MEASUREMENT _attr_name: str = "Humidity" _divisor = 100 - _unit = PERCENTAGE + _attr_native_unit_of_measurement = PERCENTAGE @MULTI_MATCH(channel_names=CHANNEL_SOIL_MOISTURE) @@ -390,7 +378,7 @@ class SoilMoisture(Sensor): _attr_state_class: SensorStateClass = SensorStateClass.MEASUREMENT _attr_name: str = "Soil moisture" _divisor = 100 - _unit = PERCENTAGE + _attr_native_unit_of_measurement = PERCENTAGE @MULTI_MATCH(channel_names=CHANNEL_LEAF_WETNESS) @@ -402,7 +390,7 @@ class LeafWetness(Sensor): _attr_state_class: SensorStateClass = SensorStateClass.MEASUREMENT _attr_name: str = "Leaf wetness" _divisor = 100 - _unit = PERCENTAGE + _attr_native_unit_of_measurement = PERCENTAGE @MULTI_MATCH(channel_names=CHANNEL_ILLUMINANCE) @@ -413,11 +401,11 @@ class Illuminance(Sensor): _attr_device_class: SensorDeviceClass = SensorDeviceClass.ILLUMINANCE _attr_state_class: SensorStateClass = SensorStateClass.MEASUREMENT _attr_name: str = "Illuminance" - _unit = LIGHT_LUX + _attr_native_unit_of_measurement = LIGHT_LUX - def formatter(self, value: int) -> float: + def formatter(self, value: int) -> int: """Convert illumination data.""" - return round(pow(10, ((value - 1) / 10000)), 1) + return round(pow(10, ((value - 1) / 10000))) @MULTI_MATCH( @@ -433,19 +421,19 @@ class SmartEnergyMetering(Sensor): _attr_name: str = "Instantaneous demand" unit_of_measure_map = { - 0x00: POWER_WATT, - 0x01: VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR, - 0x02: VOLUME_FLOW_RATE_CUBIC_FEET_PER_MINUTE, - 0x03: f"100 {VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR}", - 0x04: f"US {VOLUME_GALLONS}/{TIME_HOURS}", - 0x05: f"IMP {VOLUME_GALLONS}/{TIME_HOURS}", - 0x06: f"BTU/{TIME_HOURS}", - 0x07: f"l/{TIME_HOURS}", - 0x08: "kPa", # gauge - 0x09: "kPa", # absolute - 0x0A: f"1000 {VOLUME_GALLONS}/{TIME_HOURS}", + 0x00: UnitOfPower.WATT, + 0x01: UnitOfVolumeFlowRate.CUBIC_METERS_PER_HOUR, + 0x02: UnitOfVolumeFlowRate.CUBIC_FEET_PER_MINUTE, + 0x03: f"100 {UnitOfVolumeFlowRate.CUBIC_METERS_PER_HOUR}", + 0x04: f"US {UnitOfVolume.GALLONS}/{UnitOfTime.HOURS}", + 0x05: f"IMP {UnitOfVolume.GALLONS}/{UnitOfTime.HOURS}", + 0x06: UnitOfPower.BTU_PER_HOUR, + 0x07: f"l/{UnitOfTime.HOURS}", + 0x08: UnitOfPressure.KPA, # gauge + 0x09: UnitOfPressure.KPA, # absolute + 0x0A: f"1000 {UnitOfVolume.GALLONS}/{UnitOfTime.HOURS}", 0x0B: "unitless", - 0x0C: f"MJ/{TIME_SECONDS}", + 0x0C: f"MJ/{UnitOfTime.SECONDS}", } def formatter(self, value: int) -> int | float: @@ -481,17 +469,17 @@ class SmartEnergySummation(SmartEnergyMetering, id_suffix="summation_delivered") _attr_name: str = "Summation delivered" unit_of_measure_map = { - 0x00: ENERGY_KILO_WATT_HOUR, - 0x01: VOLUME_CUBIC_METERS, - 0x02: VOLUME_CUBIC_FEET, - 0x03: f"100 {VOLUME_CUBIC_FEET}", - 0x04: f"US {VOLUME_GALLONS}", - 0x05: f"IMP {VOLUME_GALLONS}", + 0x00: UnitOfEnergy.KILO_WATT_HOUR, + 0x01: UnitOfVolume.CUBIC_METERS, + 0x02: UnitOfVolume.CUBIC_FEET, + 0x03: f"100 {UnitOfVolume.CUBIC_FEET}", + 0x04: f"US {UnitOfVolume.GALLONS}", + 0x05: f"IMP {UnitOfVolume.GALLONS}", 0x06: "BTU", - 0x07: VOLUME_LITERS, - 0x08: "kPa", # gauge - 0x09: "kPa", # absolute - 0x0A: f"1000 {VOLUME_CUBIC_FEET}", + 0x07: UnitOfVolume.LITERS, + 0x08: UnitOfPressure.KPA, # gauge + 0x09: UnitOfPressure.KPA, # absolute + 0x0A: f"1000 {UnitOfVolume.CUBIC_FEET}", 0x0B: "unitless", 0x0C: "MJ", } @@ -531,7 +519,7 @@ class Pressure(Sensor): _attr_state_class: SensorStateClass = SensorStateClass.MEASUREMENT _attr_name: str = "Pressure" _decimals = 0 - _unit = PRESSURE_HPA + _attr_native_unit_of_measurement = UnitOfPressure.HPA @MULTI_MATCH(channel_names=CHANNEL_TEMPERATURE) @@ -543,7 +531,7 @@ class Temperature(Sensor): _attr_state_class: SensorStateClass = SensorStateClass.MEASUREMENT _attr_name: str = "Temperature" _divisor = 100 - _unit = TEMP_CELSIUS + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS @MULTI_MATCH(channel_names=CHANNEL_DEVICE_TEMPERATURE) @@ -555,7 +543,7 @@ class DeviceTemperature(Sensor): _attr_state_class: SensorStateClass = SensorStateClass.MEASUREMENT _attr_name: str = "Device temperature" _divisor = 100 - _unit = TEMP_CELSIUS + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS _attr_entity_category = EntityCategory.DIAGNOSTIC @@ -569,7 +557,7 @@ class CarbonDioxideConcentration(Sensor): _attr_name: str = "Carbon dioxide concentration" _decimals = 0 _multiplier = 1e6 - _unit = CONCENTRATION_PARTS_PER_MILLION + _attr_native_unit_of_measurement = CONCENTRATION_PARTS_PER_MILLION @MULTI_MATCH(channel_names="carbon_monoxide_concentration") @@ -582,7 +570,7 @@ class CarbonMonoxideConcentration(Sensor): _attr_name: str = "Carbon monoxide concentration" _decimals = 0 _multiplier = 1e6 - _unit = CONCENTRATION_PARTS_PER_MILLION + _attr_native_unit_of_measurement = CONCENTRATION_PARTS_PER_MILLION @MULTI_MATCH(generic_ids="channel_0x042e", stop_on_match_group="voc_level") @@ -596,7 +584,7 @@ class VOCLevel(Sensor): _attr_name: str = "VOC level" _decimals = 0 _multiplier = 1e6 - _unit = CONCENTRATION_MICROGRAMS_PER_CUBIC_METER + _attr_native_unit_of_measurement = CONCENTRATION_MICROGRAMS_PER_CUBIC_METER @MULTI_MATCH( @@ -613,7 +601,7 @@ class PPBVOCLevel(Sensor): _attr_name: str = "VOC level" _decimals = 0 _multiplier = 1 - _unit = CONCENTRATION_PARTS_PER_BILLION + _attr_native_unit_of_measurement = CONCENTRATION_PARTS_PER_BILLION @MULTI_MATCH(channel_names="pm25") @@ -625,7 +613,7 @@ class PM25(Sensor): _attr_name: str = "Particulate matter" _decimals = 0 _multiplier = 1 - _unit = CONCENTRATION_MICROGRAMS_PER_CUBIC_METER + _attr_native_unit_of_measurement = CONCENTRATION_MICROGRAMS_PER_CUBIC_METER @MULTI_MATCH(channel_names="formaldehyde_concentration") @@ -637,7 +625,7 @@ class FormaldehydeConcentration(Sensor): _attr_name: str = "Formaldehyde concentration" _decimals = 0 _multiplier = 1e6 - _unit = CONCENTRATION_PARTS_PER_MILLION + _attr_native_unit_of_measurement = CONCENTRATION_PARTS_PER_MILLION @MULTI_MATCH(channel_names=CHANNEL_THERMOSTAT, stop_on_match_group=CHANNEL_THERMOSTAT) @@ -817,7 +805,7 @@ class TimeLeft(Sensor, id_suffix="time_left"): _attr_device_class: SensorDeviceClass = SensorDeviceClass.DURATION _attr_icon = "mdi:timer" _attr_name: str = "Time left" - _unit = TIME_MINUTES + _attr_native_unit_of_measurement = UnitOfTime.MINUTES @MULTI_MATCH(channel_names="ikea_airpurifier") @@ -828,7 +816,7 @@ class IkeaDeviceRunTime(Sensor, id_suffix="device_run_time"): _attr_device_class: SensorDeviceClass = SensorDeviceClass.DURATION _attr_icon = "mdi:timer" _attr_name: str = "Device run time" - _unit = TIME_MINUTES + _attr_native_unit_of_measurement = UnitOfTime.MINUTES @MULTI_MATCH(channel_names="ikea_airpurifier") @@ -839,7 +827,7 @@ class IkeaFilterRunTime(Sensor, id_suffix="filter_run_time"): _attr_device_class: SensorDeviceClass = SensorDeviceClass.DURATION _attr_icon = "mdi:timer" _attr_name: str = "Filter run time" - _unit = TIME_MINUTES + _attr_native_unit_of_measurement = UnitOfTime.MINUTES class AqaraFeedingSource(types.enum8): @@ -887,6 +875,6 @@ class AqaraPetFeederWeightDispensed(Sensor, id_suffix="weight_dispensed"): SENSOR_ATTR = "weight_dispensed" _attr_name: str = "Weight dispensed today" - _unit = UnitOfMass.GRAMS + _attr_native_unit_of_measurement = UnitOfMass.GRAMS _attr_state_class: SensorStateClass = SensorStateClass.TOTAL_INCREASING _attr_icon: str = "mdi:weight-gram" diff --git a/homeassistant/components/zha/strings.json b/homeassistant/components/zha/strings.json index 240f3c4ee83..057c0adb007 100644 --- a/homeassistant/components/zha/strings.json +++ b/homeassistant/components/zha/strings.json @@ -8,10 +8,10 @@ "description": "Select the serial port for your Zigbee radio" }, "confirm": { - "description": "Do you want to setup {name}?" + "description": "Do you want to set up {name}?" }, "confirm_hardware": { - "description": "Do you want to setup {name}?" + "description": "Do you want to set up {name}?" }, "manual_pick_radio_type": { "data": { "radio_type": "Radio Type" }, diff --git a/homeassistant/components/zha/translations/en.json b/homeassistant/components/zha/translations/en.json index bb62ccca64a..049cdce4c4c 100644 --- a/homeassistant/components/zha/translations/en.json +++ b/homeassistant/components/zha/translations/en.json @@ -36,10 +36,10 @@ "title": "Select a Serial Port" }, "confirm": { - "description": "Do you want to setup {name}?" + "description": "Do you want to set up {name}?" }, "confirm_hardware": { - "description": "Do you want to setup {name}?" + "description": "Do you want to set up {name}?" }, "manual_pick_radio_type": { "data": { diff --git a/homeassistant/components/zha/translations/ja.json b/homeassistant/components/zha/translations/ja.json index 030c38106c5..ba8da34fd73 100644 --- a/homeassistant/components/zha/translations/ja.json +++ b/homeassistant/components/zha/translations/ja.json @@ -32,7 +32,7 @@ "data": { "path": "\u30b7\u30ea\u30a2\u30eb \u30c7\u30d0\u30a4\u30b9\u306e\u30d1\u30b9" }, - "description": "Zigbee\u30e9\u30b8\u30aa\u306e\u30b7\u30ea\u30a2\u30eb\u30dd\u30fc\u30c8\u3092\u9078\u629e\u3057\u307e\u3059", + "description": "Zigbee\u7121\u7dda\u306e\u30b7\u30ea\u30a2\u30eb\u30dd\u30fc\u30c8\u3092\u9078\u629e\u3057\u307e\u3059", "title": "\u30b7\u30ea\u30a2\u30eb\u30dd\u30fc\u30c8\u306e\u9078\u629e" }, "confirm": { @@ -45,7 +45,7 @@ "data": { "radio_type": "\u7121\u7dda\u30bf\u30a4\u30d7" }, - "description": "Zigbee\u7121\u7dda\u6a5f\u306e\u30bf\u30a4\u30d7\u3092\u9078\u629e", + "description": "Zigbee\u7121\u7dda\u306e\u30bf\u30a4\u30d7\u3092\u9078\u629e", "title": "\u7121\u7dda\u30bf\u30a4\u30d7" }, "manual_port_config": { @@ -68,7 +68,7 @@ "data": { "uploaded_backup_file": "\u30d5\u30a1\u30a4\u30eb\u3092\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u3059\u308b" }, - "description": "\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u3055\u308c\u305f\u30d0\u30c3\u30af\u30a2\u30c3\u30d7 JSON \u30d5\u30a1\u30a4\u30eb\u304b\u3089\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u8a2d\u5b9a\u3092\u5fa9\u5143\u3057\u307e\u3059\u3002 **Network Settings** \u304b\u3089\u5225\u306e ZHA \u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u304b\u3089\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u3059\u308b\u304b\u3001Zigbee2MQTT `coordinator_backup.json` \u30d5\u30a1\u30a4\u30eb\u3092\u4f7f\u7528\u3067\u304d\u307e\u3059\u3002", + "description": "\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u3055\u308c\u305f\u30d0\u30c3\u30af\u30a2\u30c3\u30d7JSON\u30d5\u30a1\u30a4\u30eb\u304b\u3089\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u8a2d\u5b9a\u3092\u5fa9\u5143\u3057\u307e\u3059\u3002**Network Settings**\u304b\u3089\u5225\u306eZHA\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3092\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u3059\u308b\u304b\u3001Zigbee2MQTT`coordinator_backup.json`\u30d5\u30a1\u30a4\u30eb\u3092\u4f7f\u7528\u3067\u304d\u307e\u3059\u3002", "title": "\u624b\u52d5\u30d0\u30c3\u30af\u30a2\u30c3\u30d7\u3092\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u3059\u308b" } } @@ -93,6 +93,8 @@ }, "device_automation": { "action_type": { + "issue_all_led_effect": "\u3059\u3079\u3066\u306eLED\u306b\u5f71\u97ff\u3092\u4e0e\u3048\u308b", + "issue_individual_led_effect": "\u500b\u3005\u306eLED\u306b\u5f71\u97ff\u3092\u4e0e\u3048\u308b", "squawk": "\u30b9\u30b3\u30fc\u30af(Squawk)", "warn": "Warn" }, @@ -180,18 +182,26 @@ "data": { "path": "\u30b7\u30ea\u30a2\u30eb \u30c7\u30d0\u30a4\u30b9\u306e\u30d1\u30b9" }, - "description": "Zigbee\u30e9\u30b8\u30aa\u306e\u30b7\u30ea\u30a2\u30eb\u30dd\u30fc\u30c8\u3092\u9078\u629e\u3057\u307e\u3059", + "description": "Zigbee\u7121\u7dda\u306e\u30b7\u30ea\u30a2\u30eb\u30dd\u30fc\u30c8\u3092\u9078\u629e\u3057\u307e\u3059", "title": "\u30b7\u30ea\u30a2\u30eb\u30dd\u30fc\u30c8\u306e\u9078\u629e" }, "init": { "description": "ZHA\u3092\u505c\u6b62\u3057\u307e\u3059\u3002\u7d9a\u3051\u307e\u3059\u304b\uff1f", "title": "ZHA\u306e\u518d\u8a2d\u5b9a" }, + "instruct_unplug": { + "description": "\u53e4\u3044\u7121\u7dda\u304c\u30ea\u30bb\u30c3\u30c8\u3055\u308c\u307e\u3057\u305f\u3002 \u30cf\u30fc\u30c9\u30a6\u30a7\u30a2\u304c\u4e0d\u8981\u306b\u306a\u3063\u305f\u5834\u5408\u306f\u3001\u3053\u308c\u3067\u30d7\u30e9\u30b0\u3092\u629c\u304f\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002", + "title": "\u53e4\u3044\u7121\u7dda\u306e\u30d7\u30e9\u30b0\u3092\u629c\u304f" + }, + "intent_migrate": { + "description": "\u53e4\u3044\u7121\u7dda\u6a5f\u306f\u5de5\u5834\u51fa\u8377\u6642\u306b\u30ea\u30bb\u30c3\u30c8\u3055\u308c\u307e\u3059\u3002HUSBZB-1\u306e\u3088\u3046\u306aZ-Wave\u3068Zigbee\u3092\u7d44\u307f\u5408\u308f\u305b\u305f\u30a2\u30c0\u30d7\u30bf\u30fc\u3092\u4f7f\u7528\u3057\u3066\u3044\u308b\u5834\u5408\u306f\u3001Zigbee\u90e8\u5206\u306e\u307f\u30ea\u30bb\u30c3\u30c8\u3055\u308c\u307e\u3059\u3002\n\n\u3053\u306e\u307e\u307e\u7d9a\u3051\u307e\u3059\u304b\uff1f", + "title": "\u65b0\u3057\u3044\u7121\u7dda\u306b\u79fb\u884c\u3059\u308b" + }, "manual_pick_radio_type": { "data": { "radio_type": "\u7121\u7dda\u30bf\u30a4\u30d7" }, - "description": "Zigbee\u7121\u7dda\u6a5f\u306e\u30bf\u30a4\u30d7\u3092\u9078\u629e", + "description": "Zigbee\u7121\u7dda\u306e\u30bf\u30a4\u30d7\u3092\u9078\u629e", "title": "\u7121\u7dda\u30bf\u30a4\u30d7" }, "manual_port_config": { @@ -210,11 +220,19 @@ "description": "\u30d0\u30c3\u30af\u30a2\u30c3\u30d7\u306b\u306f\u3001\u7121\u7dda\u3068\u306f\u7570\u306a\u308bIEEE\u30a2\u30c9\u30ec\u30b9\u304c\u3042\u308a\u307e\u3059\u3002\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u304c\u6b63\u3057\u304f\u6a5f\u80fd\u3059\u308b\u306b\u306f\u3001\u7121\u7dda\u306eIEEE\u30a2\u30c9\u30ec\u30b9\u3082\u5909\u66f4\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002 \n\n\u3053\u308c\u306f\u6052\u4e45\u7684\u306a\u64cd\u4f5c\u3067\u3059\u3002", "title": "\u7121\u7dda\u306eIEEE\u30a2\u30c9\u30ec\u30b9\u306e\u4e0a\u66f8\u304d" }, + "prompt_migrate_or_reconfigure": { + "description": "\u65b0\u3057\u3044\u7121\u7dda\u306b\u79fb\u884c\u3057\u307e\u3059\u304b\u3001\u305d\u308c\u3068\u3082\u73fe\u5728\u306e\u7121\u7dda\u3092\u518d\u8a2d\u5b9a\u3057\u307e\u3059\u304b\uff1f", + "menu_options": { + "intent_migrate": "\u65b0\u3057\u3044\u7121\u7dda\u306b\u79fb\u884c\u3059\u308b", + "intent_reconfigure": "\u73fe\u5728\u306e\u7121\u7dda\u3092\u518d\u8a2d\u5b9a\u3059\u308b" + }, + "title": "\u79fb\u884c\u307e\u305f\u306f\u518d\u69cb\u6210" + }, "upload_manual_backup": { "data": { "uploaded_backup_file": "\u30d5\u30a1\u30a4\u30eb\u3092\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u3059\u308b" }, - "description": "\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u3055\u308c\u305f\u30d0\u30c3\u30af\u30a2\u30c3\u30d7 JSON \u30d5\u30a1\u30a4\u30eb\u304b\u3089\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u8a2d\u5b9a\u3092\u5fa9\u5143\u3057\u307e\u3059\u3002 **Network Settings** \u304b\u3089\u5225\u306e ZHA \u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u304b\u3089\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u3059\u308b\u304b\u3001Zigbee2MQTT `coordinator_backup.json` \u30d5\u30a1\u30a4\u30eb\u3092\u4f7f\u7528\u3067\u304d\u307e\u3059\u3002", + "description": "\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u3055\u308c\u305f\u30d0\u30c3\u30af\u30a2\u30c3\u30d7JSON\u30d5\u30a1\u30a4\u30eb\u304b\u3089\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u8a2d\u5b9a\u3092\u5fa9\u5143\u3057\u307e\u3059\u3002**Network Settings**\u304b\u3089\u5225\u306eZHA\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3092\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u3059\u308b\u304b\u3001Zigbee2MQTT`coordinator_backup.json`\u30d5\u30a1\u30a4\u30eb\u3092\u4f7f\u7528\u3067\u304d\u307e\u3059\u3002", "title": "\u624b\u52d5\u30d0\u30c3\u30af\u30a2\u30c3\u30d7\u3092\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u3059\u308b" } } diff --git a/homeassistant/components/zha/translations/nl.json b/homeassistant/components/zha/translations/nl.json index f47f71da21c..a01d56c7b8f 100644 --- a/homeassistant/components/zha/translations/nl.json +++ b/homeassistant/components/zha/translations/nl.json @@ -12,16 +12,27 @@ "flow_title": "{name}", "step": { "choose_automatic_backup": { + "data": { + "choose_automatic_backup": "Kies een automatische back-up" + }, + "description": "Herstel je netwerkinstellingen van een automatische back-up", "title": "Automatische back-up herstellen" }, "choose_formation_strategy": { + "description": "Kies de netwerkinstellingen voor je toegangspunt.", "menu_options": { "choose_automatic_backup": "Een automatische back-up herstellen", + "form_new_network": "Wis netwerkinstellingen en vorm een nieuw netwerk", + "reuse_settings": "Behoud de netwerkinstellingen van het toegangspunt", "upload_manual_backup": "Upload een handmatige back-up" - } + }, + "title": "Netwerkformatie" }, "choose_serial_port": { - "description": "Selecteer de seri\u00eble poort voor je Zigbee-radio", + "data": { + "path": "Serieel apparaat pad" + }, + "description": "Selecteer de seri\u00eble poort voor je Zigbee-toegangspunt", "title": "Selecteer een seri\u00eble poort" }, "confirm": { @@ -30,10 +41,28 @@ "confirm_hardware": { "description": "Wilt u {name} instellen?" }, + "manual_pick_radio_type": { + "data": { + "radio_type": "Toegangspunt type" + }, + "description": "Selecteer je Zigbee toegangspunt type", + "title": "Toegangspunt type" + }, "manual_port_config": { "data": { - "baudrate": "poortsnelheid" - } + "baudrate": "poortsnelheid", + "flow_control": "data flow controle", + "path": "Serieel apparaat pad" + }, + "description": "Geef de instellingen voor de seri\u00eble poort", + "title": "Seri\u00eblepoortinstellingen" + }, + "maybe_confirm_ezsp_restore": { + "data": { + "overwrite_coordinator_ieee": "Definitieve vervanging van IEEE toegangspunt adres" + }, + "description": "Je back-up heeft een ander IEEE adres dan je toegangspunt. Om je netwerk goed te laten functioneren, moet je ook het IEEE adres van je toegangspunt wijzigen.\n\nDit is een definitieve operatie.", + "title": "Overschrijven IEEE adres van het toegangspunt" }, "upload_manual_backup": { "data": { @@ -52,15 +81,20 @@ "title": "Alarmbedieningspaneelopties" }, "zha_options": { + "always_prefer_xy_color_mode": "Altijd XY kleurmodus prefereren", "consider_unavailable_battery": "Beschouw apparaten met batterijvoeding als onbeschikbaar na (seconden)", "consider_unavailable_mains": "Beschouw apparaten op netvoeding als onbeschikbaar na (seconden)", "default_light_transition": "Standaard licht transitietijd (seconden)", "enable_identify_on_join": "Schakel het identificatie-effect in wanneer apparaten in het netwerk komen", + "enhanced_light_transition": "Inschakelen geavanceerde light kleur/temperatuur transitie vanaf uitgeschakelde toestand", + "light_transitioning_flag": "Inschakelen geavanceerde helderheid schuifregelaar gedurende de lichtovergang", "title": "Globale opties" } }, "device_automation": { "action_type": { + "issue_all_led_effect": "Effect toepassen voor alle LED's", + "issue_individual_led_effect": "Effect toepassen voor individuele LED", "squawk": "Schreeuw", "warn": "Waarschuwen" }, @@ -118,6 +152,7 @@ "options": { "abort": { "not_zha_device": "Dit apparaat is niet een zha-apparaat.", + "single_instance_allowed": "Al geconfigureerd. Maar \u00e9\u00e9n configuratie mogelijk.", "usb_probe_failed": "Kon het USB apparaat niet onderzoeken" }, "error": { @@ -127,26 +162,71 @@ "flow_title": "{name}", "step": { "choose_automatic_backup": { + "data": { + "choose_automatic_backup": "Kies een automatische back-up" + }, + "description": "Herstel je netwerkinstellingen van een automatische back-up", "title": "Automatische back-up herstellen" }, "choose_formation_strategy": { + "description": "Kies de netwerkinstellingen voor je toegangspunt.", "menu_options": { "choose_automatic_backup": "Een automatische back-up herstellen", + "form_new_network": "Wis netwerkinstellingen en vorm een nieuw netwerk", + "reuse_settings": "Behoud de netwerkinstellingen van het toegangspunt", "upload_manual_backup": "Upload een handmatige back-up" - } + }, + "title": "Netwerkformatie" }, "choose_serial_port": { - "description": "Selecteer de seri\u00eble poort voor je Zigbee-radio", + "data": { + "path": "Serieel apparaat pad" + }, + "description": "Selecteer de seri\u00eble poort voor je Zigbee-toegangspunt", "title": "Selecteer een seri\u00eble poort" }, "init": { - "description": "ZHA wordt gestopt. Wilt u doorgaan?", + "description": "ZHA wordt gestopt. Wil je doorgaan?", "title": "ZHA opnieuw configureren" }, + "instruct_unplug": { + "description": "Je oude toegangspunt is gereset. Als de hardware niet langer nodig is kun je die nu loskoppelen.", + "title": "Loskoppellen van je oude toegangspunt" + }, + "intent_migrate": { + "description": "Je oude toegangspunt wordt teruggezet naar fabrieksinstellingen. Als je een gecombineerde Z-Wave en Zigbee adapter zoals de HUSBZB-1 gebruikt, dan zal dit alleen het Zigbee deel terugzetten.\n\nWil je doorgaan?", + "title": "Migreer naar een nieuw toegangspunt" + }, + "manual_pick_radio_type": { + "data": { + "radio_type": "Toegangspunt type" + }, + "description": "Selecteer je Zigbee toegangspunt type", + "title": "Toegangspunt type" + }, "manual_port_config": { "data": { - "baudrate": "poortsnelheid" - } + "baudrate": "poortsnelheid", + "flow_control": "data flow controle", + "path": "Serieel apparaat pad" + }, + "description": "Geef de instellingen voor de seri\u00eble poort", + "title": "Seri\u00eblepoortinstellingen" + }, + "maybe_confirm_ezsp_restore": { + "data": { + "overwrite_coordinator_ieee": "Definitieve vervanging van IEEE toegangspunt adres" + }, + "description": "Je back-up heeft een ander IEEE adres dan je toegangspunt. Om je netwerk goed te laten functioneren, moet je ook het IEEE adres van je toegangspunt wijzigen.\n\nDit is een definitieve operatie.", + "title": "Overschrijven IEEE adres van het toegangspunt" + }, + "prompt_migrate_or_reconfigure": { + "description": "Wil je migreren naar een nieuw toegangspunt of het bestaande toegangspunt herconfigureren?", + "menu_options": { + "intent_migrate": "Migreren naar een nieuw toegangspunt", + "intent_reconfigure": "Herconfigureren van het bestaande toegangspunt" + }, + "title": "Migreren of herconfigureren" }, "upload_manual_backup": { "data": { diff --git a/homeassistant/components/zha/translations/no.json b/homeassistant/components/zha/translations/no.json index 1434d243522..0d55fc2be2a 100644 --- a/homeassistant/components/zha/translations/no.json +++ b/homeassistant/components/zha/translations/no.json @@ -36,10 +36,10 @@ "title": "Velg en seriell port" }, "confirm": { - "description": "Vil du konfigurere {name}?" + "description": "Vil du sette opp {name} ?" }, "confirm_hardware": { - "description": "Vil du konfigurere {name} ?" + "description": "Vil du sette opp {name} ?" }, "manual_pick_radio_type": { "data": { diff --git a/homeassistant/components/zha/translations/sk.json b/homeassistant/components/zha/translations/sk.json index 3e8e32d1035..5d29db03b34 100644 --- a/homeassistant/components/zha/translations/sk.json +++ b/homeassistant/components/zha/translations/sk.json @@ -1,7 +1,9 @@ { "config": { "abort": { - "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + "not_zha_device": "Toto zariadenie nie je zariadenie zha", + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia.", + "usb_probe_failed": "Nepodarilo sa presk\u00fama\u0165 zariadenie USB" }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", @@ -17,20 +19,85 @@ "title": "Obnovenie automatickej z\u00e1lohy" }, "choose_formation_strategy": { + "description": "Vyberte sie\u0165ov\u00e9 nastavenia pre va\u0161e r\u00e1dio.", "menu_options": { "choose_automatic_backup": "Obnovenie automatickej z\u00e1lohy", + "form_new_network": "Vyma\u017ete nastavenia siete a vytvorte nov\u00fa sie\u0165", + "reuse_settings": "Ponechajte nastavenia siete r\u00e1dia", "upload_manual_backup": "Nahratie manu\u00e1lnej z\u00e1lohy" - } + }, + "title": "Tvorba siete" + }, + "choose_serial_port": { + "data": { + "path": "Cesta s\u00e9riov\u00e9ho zariadenia" + }, + "description": "Vyberte s\u00e9riov\u00fd port pre va\u0161e r\u00e1dio Zigbee", + "title": "Vyberte s\u00e9riov\u00fd port" }, "confirm": { "description": "Chcete nastavi\u0165 {name}?" }, "confirm_hardware": { "description": "Chcete nastavi\u0165 {name}?" + }, + "manual_pick_radio_type": { + "data": { + "radio_type": "Typ r\u00e1dia" + }, + "description": "Vyberte si typ r\u00e1dia Zigbee", + "title": "Typ r\u00e1dia" + }, + "manual_port_config": { + "data": { + "baudrate": "r\u00fdchlos\u0165 portu", + "flow_control": "riadenie toku d\u00e1t", + "path": "Cesta s\u00e9riov\u00e9ho zariadenia" + }, + "description": "Zadajte nastavenia s\u00e9riov\u00e9ho portu", + "title": "Nastavenia s\u00e9riov\u00e9ho portu" + }, + "maybe_confirm_ezsp_restore": { + "data": { + "overwrite_coordinator_ieee": "Natrvalo nahra\u010fte r\u00e1diov\u00fa adresu IEEE" + }, + "description": "Va\u0161a z\u00e1loha m\u00e1 in\u00fa adresu IEEE ako va\u0161e r\u00e1dio. Aby va\u0161a sie\u0165 fungovala spr\u00e1vne, mala by sa zmeni\u0165 aj adresa IEEE v\u00e1\u0161ho r\u00e1dia. \n\n Ide o trval\u00fa oper\u00e1ciu.", + "title": "Prep\u00edsa\u0165 adresu IEEE r\u00e1dia" + }, + "upload_manual_backup": { + "data": { + "uploaded_backup_file": "Nahrajte s\u00fabor" + }, + "description": "Obnovte nastavenia siete z nahran\u00e9ho z\u00e1lo\u017en\u00e9ho s\u00faboru JSON. M\u00f4\u017eete si ho stiahnu\u0165 z inej in\u0161tal\u00e1cie ZHA z **Nastavenia siete** alebo pou\u017ei\u0165 s\u00fabor Zigbee2MQTT `coordinator_backup.json`.", + "title": "Nahratie manu\u00e1lnej z\u00e1lohy" } } }, + "config_panel": { + "zha_alarm_options": { + "alarm_arm_requires_code": "K\u00f3d po\u017eadovan\u00fd pre \u010dinnosti str\u00e1\u017eenia", + "alarm_failed_tries": "Po\u010det po sebe nasleduj\u00facich ne\u00faspe\u0161n\u00fdch zadan\u00ed k\u00f3du na spustenie alarmu", + "alarm_master_code": "Hlavn\u00fd k\u00f3d pre poplachov\u00e9 ovl\u00e1dacie panely", + "title": "Mo\u017enosti ovl\u00e1dacieho panela alarmov" + }, + "zha_options": { + "always_prefer_xy_color_mode": "V\u017edy uprednost\u0148ujete farebn\u00fd re\u017eim XY", + "consider_unavailable_battery": "Zariadenia nap\u00e1jan\u00e9 z bat\u00e9rie pova\u017eujte za nedostupn\u00e9 po (sekund\u00e1ch)", + "consider_unavailable_mains": "Zariadenia nap\u00e1jan\u00e9 zo siete pova\u017eujte za nedostupn\u00e9 po (sekund\u00e1ch)", + "default_light_transition": "Predvolen\u00fd \u010das prechodu svetla (sekundy)", + "enable_identify_on_join": "Povoli\u0165 efekt identifik\u00e1cie, ke\u010f sa zariadenia prip\u00e1jaj\u00fa k sieti", + "enhanced_light_transition": "Povoli\u0165 vylep\u0161en\u00fd prechod farby svetla/teploty z vypnut\u00e9ho stavu", + "light_transitioning_flag": "Povolenie vylep\u0161en\u00e9ho posuvn\u00edka jasu po\u010das prechodu svetla", + "title": "Glob\u00e1lne mo\u017enosti" + } + }, "device_automation": { + "action_type": { + "issue_all_led_effect": "V\u00fdstupn\u00fd efekt pre v\u0161etky LED", + "issue_individual_led_effect": "V\u00fdstupn\u00fd efekt pre jednu LED", + "squawk": "\u0160kr\u00edpanie", + "warn": "Upozornenie" + }, "trigger_subtype": { "both_buttons": "Obe tla\u010didl\u00e1", "button_1": "Prv\u00e9 tla\u010didlo", @@ -38,15 +105,55 @@ "button_3": "Tretie tla\u010didlo", "button_4": "\u0160tvrt\u00e9 tla\u010didlo", "button_5": "Piate tla\u010didlo", - "button_6": "\u0160ieste tla\u010didlo" + "button_6": "\u0160ieste tla\u010didlo", + "close": "Zavrie\u0165", + "dim_down": "Dim dole", + "dim_up": "Dim hore", + "face_1": "Aktivovan\u00e9 tv\u00e1rou 1", + "face_2": "Aktivovan\u00e9 tv\u00e1rou 2", + "face_3": "Aktivovan\u00e9 tv\u00e1rou 3", + "face_4": "Aktivovan\u00e9 tv\u00e1rou 4", + "face_5": "Aktivovan\u00e9 tv\u00e1rou 5", + "face_6": "Aktivovan\u00e9 tv\u00e1rou 6", + "face_any": "Aktiv\u00e1cia s \u013eubovo\u013en\u00fdm/fixn\u00fdm povrchom (povrchmi)", + "left": "V\u013eavo", + "open": "Otvori\u0165", + "right": "Vpravo", + "turn_off": "Vypn\u00fa\u0165", + "turn_on": "Zapn\u00fa\u0165" }, "trigger_type": { - "device_offline": "Zariadenie je offline" + "device_dropped": "Zariadenie spadlo", + "device_flipped": "Zariadenie sa obr\u00e1tilo \u201e{subtype}\u201c", + "device_knocked": "Zariadenie zaklopalo \u201e{subtype}\u201c", + "device_offline": "Zariadenie je offline", + "device_rotated": "Zariadenie oto\u010den\u00e9 \u201e{subtype}\u201c", + "device_shaken": "Zariadenie sa zatriaslo", + "device_slid": "Zariadenie posunut\u00e9 \u201e{subtype}\u201c", + "device_tilted": "Zariadenie je naklonen\u00e9", + "remote_button_alt_double_press": "dvojit\u00e9 kliknutie na tla\u010didlo \u201e{subtype}\u201c (Alternat\u00edvny re\u017eim)", + "remote_button_alt_long_press": "Trvalo stla\u010den\u00e9 tla\u010didlo \u201e{subtype}\u201c (Alternat\u00edvny re\u017eim)", + "remote_button_alt_long_release": "Tla\u010didlo \"{subtype}\" uvo\u013enen\u00e9 po dlhom stla\u010den\u00ed (alternat\u00edvny re\u017eim)", + "remote_button_alt_quadruple_press": "\u0161tvorit\u00e9 kliknutie na tla\u010didlo \u201e{subtype}\u201c (Alternat\u00edvny re\u017eim)", + "remote_button_alt_quintuple_press": "p\u00e4\u0165n\u00e1sobn\u00e9 kliknutie na tla\u010didlo \u201e{subtype}\u201c (Alternat\u00edvny re\u017eim)", + "remote_button_alt_short_press": "Tla\u010didlo \"{subtype}\" stla\u010den\u00e9 (alternat\u00edvny re\u017eim)", + "remote_button_alt_short_release": "Uvolnen\u00e9 tla\u010didlo \"{subtype}\" (alternat\u00edvny re\u017eim)", + "remote_button_alt_triple_press": "trojit\u00e9 kliknutie na tla\u010didlo \u201e{subtype}\u201c (Alternat\u00edvny re\u017eim)", + "remote_button_double_press": "dvojklik na tla\u010didlo \u201e{subtype}\u201c", + "remote_button_long_press": "Trvalo stla\u010den\u00e9 tla\u010didlo \"{subtype}\"", + "remote_button_long_release": "Tla\u010didlo \"{subtype}\" uvo\u013enen\u00e9 po dlhom stla\u010den\u00ed", + "remote_button_quadruple_press": "Tla\u010didlo \"{subtype}\" kliknut\u00e9 \u0161tyrikr\u00e1t", + "remote_button_quintuple_press": "Tla\u010didlo \"{subtype}\" kliknut\u00e9 p\u00e4\u0165kr\u00e1t", + "remote_button_short_press": "Stla\u010den\u00e9 tla\u010didlo \"{subtype}\"", + "remote_button_short_release": "Tla\u010didlo \"{subtype}\" bolo uvo\u013enen\u00e9", + "remote_button_triple_press": "Trojklik na tla\u010didlo \"{subtype}\"" } }, "options": { "abort": { - "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia." + "not_zha_device": "Toto zariadenie nie je zariadenie zha", + "single_instance_allowed": "U\u017e je nakonfigurovan\u00fd. Mo\u017en\u00e1 len jedna konfigur\u00e1cia.", + "usb_probe_failed": "Nepodarilo sa presk\u00fama\u0165 zariadenie USB" }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", @@ -62,13 +169,71 @@ "title": "Obnovenie automatickej z\u00e1lohy" }, "choose_formation_strategy": { + "description": "Vyberte sie\u0165ov\u00e9 nastavenia pre va\u0161e r\u00e1dio.", "menu_options": { "choose_automatic_backup": "Obnovenie automatickej z\u00e1lohy", + "form_new_network": "Vyma\u017ete nastavenia siete a vytvorte nov\u00fa sie\u0165", + "reuse_settings": "Ponechajte nastavenia siete r\u00e1dia", "upload_manual_backup": "Nahratie manu\u00e1lnej z\u00e1lohy" - } + }, + "title": "Tvorba siete" + }, + "choose_serial_port": { + "data": { + "path": "Cesta s\u00e9riov\u00e9ho zariadenia" + }, + "description": "Vyberte s\u00e9riov\u00fd port pre va\u0161e r\u00e1dio Zigbee", + "title": "Vyberte s\u00e9riov\u00fd port" + }, + "init": { + "description": "Doplnok ZHA bude zastaven\u00fd. Prajete si pokra\u010dova\u0165?", + "title": "Prekonfigurova\u0165 ZHA" + }, + "instruct_unplug": { + "description": "Va\u0161e star\u00e9 r\u00e1dio bolo resetovan\u00e9. Ak u\u017e hardv\u00e9r nepotrebujete, m\u00f4\u017eete ho odpoji\u0165.", + "title": "Odpojte star\u00e9 r\u00e1dio" + }, + "intent_migrate": { + "description": "Va\u0161e star\u00e9 r\u00e1dio bude obnoven\u00e9 na v\u00fdrobn\u00e9 nastavenia. Ak pou\u017e\u00edvate kombinovan\u00fd adapt\u00e9r Z-Wave a Zigbee, ako je HUSBZB-1, resetuje sa iba \u010das\u0165 Zigbee. \n\n Chcete pokra\u010dova\u0165?", + "title": "Prejdite na nov\u00e9 r\u00e1dio" + }, + "manual_pick_radio_type": { + "data": { + "radio_type": "Typ r\u00e1dia" + }, + "description": "Vyberte si typ r\u00e1dia Zigbee", + "title": "Typ r\u00e1dia" + }, + "manual_port_config": { + "data": { + "baudrate": "r\u00fdchlos\u0165 portu", + "flow_control": "riadenie toku d\u00e1t", + "path": "Cesta s\u00e9riov\u00e9ho zariadenia" + }, + "description": "Zadajte nastavenia s\u00e9riov\u00e9ho portu", + "title": "Nastavenia s\u00e9riov\u00e9ho portu" + }, + "maybe_confirm_ezsp_restore": { + "data": { + "overwrite_coordinator_ieee": "Natrvalo nahra\u010fte r\u00e1diov\u00fa adresu IEEE" + }, + "description": "Va\u0161a z\u00e1loha m\u00e1 in\u00fa adresu IEEE ako va\u0161e r\u00e1dio. Aby va\u0161a sie\u0165 fungovala spr\u00e1vne, mala by sa zmeni\u0165 aj adresa IEEE v\u00e1\u0161ho r\u00e1dia. \n\n Ide o trval\u00fa oper\u00e1ciu.", + "title": "Prep\u00edsa\u0165 adresu IEEE r\u00e1dia" }, "prompt_migrate_or_reconfigure": { + "description": "Prech\u00e1dzate na nov\u00e9 r\u00e1dio alebo men\u00edte konfigur\u00e1ciu aktu\u00e1lneho r\u00e1dia?", + "menu_options": { + "intent_migrate": "Prejdite na nov\u00e9 r\u00e1dio", + "intent_reconfigure": "Prekonfigurujte aktu\u00e1lne r\u00e1dio" + }, "title": "Migr\u00e1cia alebo zmena konfigur\u00e1cie" + }, + "upload_manual_backup": { + "data": { + "uploaded_backup_file": "Nahrajte s\u00fabor" + }, + "description": "Obnovte nastavenia siete z nahran\u00e9ho z\u00e1lo\u017en\u00e9ho s\u00faboru JSON. M\u00f4\u017eete si ho stiahnu\u0165 z inej in\u0161tal\u00e1cie ZHA z **Nastavenia siete** alebo pou\u017ei\u0165 s\u00fabor Zigbee2MQTT `coordinator_backup.json`.", + "title": "Nahratie manu\u00e1lnej z\u00e1lohy" } } } diff --git a/homeassistant/components/zhong_hong/climate.py b/homeassistant/components/zhong_hong/climate.py index 265d4d15506..1364dbe107a 100644 --- a/homeassistant/components/zhong_hong/climate.py +++ b/homeassistant/components/zhong_hong/climate.py @@ -20,7 +20,7 @@ from homeassistant.const import ( CONF_HOST, CONF_PORT, EVENT_HOMEASSISTANT_STOP, - TEMP_CELSIUS, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv @@ -130,7 +130,7 @@ class ZhongHongClimate(ClimateEntity): _attr_supported_features = ( ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.FAN_MODE ) - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS def __init__(self, hub, addr_out, addr_in): """Set up the ZhongHong climate devices.""" diff --git a/homeassistant/components/zodiac/sensor.py b/homeassistant/components/zodiac/sensor.py index b337dda1db0..f63c844701f 100644 --- a/homeassistant/components/zodiac/sensor.py +++ b/homeassistant/components/zodiac/sensor.py @@ -1,7 +1,7 @@ """Support for tracking the zodiac sign.""" from __future__ import annotations -from homeassistant.components.sensor import SensorEntity +from homeassistant.components.sensor import SensorDeviceClass, SensorEntity from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType @@ -175,52 +175,34 @@ async def async_setup_platform( class ZodiacSensor(SensorEntity): """Representation of a Zodiac sensor.""" - def __init__(self) -> None: - """Initialize the zodiac sensor.""" - self._attrs: dict[str, str] = {} - self._state: str = "" - - @property - def unique_id(self) -> str: - """Return a unique ID.""" - return DOMAIN - - @property - def name(self) -> str: - """Return the name of the entity.""" - return "Zodiac" - - @property - def device_class(self) -> str: - """Return the device class of the entity.""" - return "zodiac__sign" - - @property - def native_value(self) -> str: - """Return the state of the device.""" - return self._state - - @property - def icon(self) -> str | None: - """Icon to use in the frontend.""" - return ZODIAC_ICONS.get(self._state) - - @property - def extra_state_attributes(self) -> dict[str, str]: - """Return the state attributes.""" - return self._attrs + _attr_name = "Zodiac" + _attr_device_class = SensorDeviceClass.ENUM + _attr_options = [ + SIGN_AQUARIUS, + SIGN_ARIES, + SIGN_CANCER, + SIGN_CAPRICORN, + SIGN_GEMINI, + SIGN_LEO, + SIGN_LIBRA, + SIGN_PISCES, + SIGN_SAGITTARIUS, + SIGN_SCORPIO, + SIGN_TAURUS, + SIGN_VIRGO, + ] + _attr_translation_key = "sign" + _attr_unique_id = DOMAIN async def async_update(self) -> None: """Get the time and updates the state.""" today = as_local(utcnow()).date() - month = int(today.month) - day = int(today.day) - for sign in ZODIAC_BY_DATE: - if (month == sign[0][1] and day >= sign[0][0]) or ( - month == sign[1][1] and day <= sign[1][0] + if (today.month == sign[0][1] and today.day >= sign[0][0]) or ( + today.month == sign[1][1] and today.day <= sign[1][0] ): - self._state = sign[2] - self._attrs = sign[3] + self._attr_native_value = sign[2] + self._attr_icon = ZODIAC_ICONS.get(sign[2]) + self._attr_extra_state_attributes = sign[3] break diff --git a/homeassistant/components/zodiac/strings.json b/homeassistant/components/zodiac/strings.json new file mode 100644 index 00000000000..cbae6ead433 --- /dev/null +++ b/homeassistant/components/zodiac/strings.json @@ -0,0 +1,22 @@ +{ + "entity": { + "sensor": { + "sign": { + "state": { + "aquarius": "Aquarius", + "aries": "Aries", + "cancer": "Cancer", + "capricorn": "Capricorn", + "gemini": "Gemini", + "leo": "Leo", + "libra": "Libra", + "pisces": "Pisces", + "sagittarius": "Sagittarius", + "scorpio": "Scorpio", + "taurus": "Taurus", + "virgo": "Virgo" + } + } + } + } +} diff --git a/homeassistant/components/zodiac/strings.sensor.json b/homeassistant/components/zodiac/strings.sensor.json deleted file mode 100644 index fec38fe2147..00000000000 --- a/homeassistant/components/zodiac/strings.sensor.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "state": { - "zodiac__sign": { - "aries": "Aries", - "taurus": "Taurus", - "gemini": "Gemini", - "cancer": "Cancer", - "leo": "Leo", - "virgo": "Virgo", - "libra": "Libra", - "scorpio": "Scorpio", - "sagittarius": "Sagittarius", - "capricorn": "Capricorn", - "aquarius": "Aquarius", - "pisces": "Pisces" - } - } -} diff --git a/homeassistant/components/zodiac/translations/bg.json b/homeassistant/components/zodiac/translations/bg.json new file mode 100644 index 00000000000..2992bffd970 --- /dev/null +++ b/homeassistant/components/zodiac/translations/bg.json @@ -0,0 +1,22 @@ +{ + "entity": { + "sensor": { + "sign": { + "state": { + "aquarius": "\u0412\u043e\u0434\u043e\u043b\u0435\u0439", + "aries": "\u041e\u0432\u0435\u043d", + "cancer": "\u0420\u0430\u043a", + "capricorn": "\u041a\u043e\u0437\u0438\u0440\u043e\u0433", + "gemini": "\u0411\u043b\u0438\u0437\u043d\u0430\u0446\u0438", + "leo": "\u041b\u044a\u0432", + "libra": "\u0412\u0435\u0437\u043d\u0438", + "pisces": "\u0420\u0438\u0431\u0438", + "sagittarius": "\u0421\u0442\u0440\u0435\u043b\u0435\u0446", + "scorpio": "\u0421\u043a\u043e\u0440\u043f\u0438\u043e\u043d", + "taurus": "\u0422\u0435\u043b\u0435\u0446", + "virgo": "\u0414\u0435\u0432\u0430" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zodiac/translations/ca.json b/homeassistant/components/zodiac/translations/ca.json new file mode 100644 index 00000000000..221ee641511 --- /dev/null +++ b/homeassistant/components/zodiac/translations/ca.json @@ -0,0 +1,22 @@ +{ + "entity": { + "sensor": { + "sign": { + "state": { + "aquarius": "Aquari", + "aries": "\u00c0ries", + "cancer": "C\u00e0ncer", + "capricorn": "Capricorn", + "gemini": "Bessons", + "leo": "Lle\u00f3", + "libra": "Balan\u00e7a", + "pisces": "Peixos", + "sagittarius": "Sagitari", + "scorpio": "Escorp\u00ed", + "taurus": "Taure", + "virgo": "Verge" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zodiac/translations/de.json b/homeassistant/components/zodiac/translations/de.json new file mode 100644 index 00000000000..e78524ab34e --- /dev/null +++ b/homeassistant/components/zodiac/translations/de.json @@ -0,0 +1,22 @@ +{ + "entity": { + "sensor": { + "sign": { + "state": { + "aquarius": "Wassermann", + "aries": "Widder", + "cancer": "Krebs", + "capricorn": "Steinbock", + "gemini": "Zwillinge", + "leo": "L\u00f6we", + "libra": "Waage", + "pisces": "Fische", + "sagittarius": "Sch\u00fctze", + "scorpio": "Skorpion", + "taurus": "Stier", + "virgo": "Jungfrau" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zodiac/translations/el.json b/homeassistant/components/zodiac/translations/el.json new file mode 100644 index 00000000000..7a2c724364e --- /dev/null +++ b/homeassistant/components/zodiac/translations/el.json @@ -0,0 +1,22 @@ +{ + "entity": { + "sensor": { + "sign": { + "state": { + "aquarius": "\u03a5\u03b4\u03c1\u03bf\u03c7\u03cc\u03bf\u03c2", + "aries": "\u039a\u03c1\u03b9\u03cc\u03c2", + "cancer": "\u039a\u03b1\u03c1\u03ba\u03af\u03bd\u03bf\u03c2", + "capricorn": "\u0391\u03b9\u03b3\u03cc\u03ba\u03b5\u03c1\u03c9\u03c2", + "gemini": "\u0394\u03af\u03b4\u03c5\u03bc\u03bf\u03c2", + "leo": "\u039b\u03ad\u03c9\u03bd", + "libra": "\u0396\u03c5\u03b3\u03cc\u03c2", + "pisces": "\u0399\u03c7\u03b8\u03cd\u03c2", + "sagittarius": "\u03a4\u03bf\u03be\u03cc\u03c4\u03b7\u03c2", + "scorpio": "\u03a3\u03ba\u03bf\u03c1\u03c0\u03b9\u03cc\u03c2", + "taurus": "\u03a4\u03b1\u03cd\u03c1\u03bf\u03c2", + "virgo": "\u03a0\u03b1\u03c1\u03b8\u03ad\u03bd\u03bf\u03c2" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zodiac/translations/en.json b/homeassistant/components/zodiac/translations/en.json new file mode 100644 index 00000000000..c25206cc0aa --- /dev/null +++ b/homeassistant/components/zodiac/translations/en.json @@ -0,0 +1,22 @@ +{ + "entity": { + "sensor": { + "sign": { + "state": { + "aquarius": "Aquarius", + "aries": "Aries", + "cancer": "Cancer", + "capricorn": "Capricorn", + "gemini": "Gemini", + "leo": "Leo", + "libra": "Libra", + "pisces": "Pisces", + "sagittarius": "Sagittarius", + "scorpio": "Scorpio", + "taurus": "Taurus", + "virgo": "Virgo" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zodiac/translations/es.json b/homeassistant/components/zodiac/translations/es.json new file mode 100644 index 00000000000..d1609a3169c --- /dev/null +++ b/homeassistant/components/zodiac/translations/es.json @@ -0,0 +1,22 @@ +{ + "entity": { + "sensor": { + "sign": { + "state": { + "aquarius": "Acuario", + "aries": "Aries", + "cancer": "C\u00e1ncer", + "capricorn": "Capricornio", + "gemini": "G\u00e9minis", + "leo": "Leo", + "libra": "Libra", + "pisces": "Piscis", + "sagittarius": "Sagitario", + "scorpio": "Escorpio", + "taurus": "Tauro", + "virgo": "Virgo" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zodiac/translations/et.json b/homeassistant/components/zodiac/translations/et.json new file mode 100644 index 00000000000..45e78b19e20 --- /dev/null +++ b/homeassistant/components/zodiac/translations/et.json @@ -0,0 +1,22 @@ +{ + "entity": { + "sensor": { + "sign": { + "state": { + "aquarius": "Veevalaja", + "aries": "J\u00e4\u00e4r", + "cancer": "V\u00e4hk", + "capricorn": "Kaljukits", + "gemini": "Kaksikud", + "leo": "L\u00f5vi", + "libra": "Kaalud", + "pisces": "Kalad", + "sagittarius": "Ambur", + "scorpio": "Skorpion", + "taurus": "S\u00f5nn", + "virgo": "Neitsi" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zodiac/translations/hu.json b/homeassistant/components/zodiac/translations/hu.json new file mode 100644 index 00000000000..3b69a59798e --- /dev/null +++ b/homeassistant/components/zodiac/translations/hu.json @@ -0,0 +1,22 @@ +{ + "entity": { + "sensor": { + "sign": { + "state": { + "aquarius": "V\u00edz\u00f6nt\u0151", + "aries": "Kos", + "cancer": "R\u00e1k", + "capricorn": "Bak", + "gemini": "Ikrek", + "leo": "Oroszl\u00e1n", + "libra": "M\u00e9rleg", + "pisces": "Halak", + "sagittarius": "Nyilas", + "scorpio": "Skorpi\u00f3", + "taurus": "Bika", + "virgo": "Sz\u0171z" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zodiac/translations/id.json b/homeassistant/components/zodiac/translations/id.json new file mode 100644 index 00000000000..c25206cc0aa --- /dev/null +++ b/homeassistant/components/zodiac/translations/id.json @@ -0,0 +1,22 @@ +{ + "entity": { + "sensor": { + "sign": { + "state": { + "aquarius": "Aquarius", + "aries": "Aries", + "cancer": "Cancer", + "capricorn": "Capricorn", + "gemini": "Gemini", + "leo": "Leo", + "libra": "Libra", + "pisces": "Pisces", + "sagittarius": "Sagittarius", + "scorpio": "Scorpio", + "taurus": "Taurus", + "virgo": "Virgo" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zodiac/translations/it.json b/homeassistant/components/zodiac/translations/it.json new file mode 100644 index 00000000000..96469f13088 --- /dev/null +++ b/homeassistant/components/zodiac/translations/it.json @@ -0,0 +1,22 @@ +{ + "entity": { + "sensor": { + "sign": { + "state": { + "aquarius": "Acquario", + "aries": "Ariete", + "cancer": "Cancro", + "capricorn": "Capricorno", + "gemini": "Gemelli", + "leo": "Leone", + "libra": "Bilancia", + "pisces": "Pesci", + "sagittarius": "Sagittario", + "scorpio": "Scorpione", + "taurus": "Toro", + "virgo": "Vergine" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zodiac/translations/no.json b/homeassistant/components/zodiac/translations/no.json new file mode 100644 index 00000000000..0659f066c7a --- /dev/null +++ b/homeassistant/components/zodiac/translations/no.json @@ -0,0 +1,22 @@ +{ + "entity": { + "sensor": { + "sign": { + "state": { + "aquarius": "Vannmannen", + "aries": "V\u00e6ren", + "cancer": "Kreft", + "capricorn": "Steinbukken", + "gemini": "Tvillingene", + "leo": "L\u00f8ven", + "libra": "Vekten", + "pisces": "Fiskene", + "sagittarius": "Skytten", + "scorpio": "Skorpionen", + "taurus": "Tyren", + "virgo": "Jomfruen" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zodiac/translations/pl.json b/homeassistant/components/zodiac/translations/pl.json new file mode 100644 index 00000000000..76db0e669b4 --- /dev/null +++ b/homeassistant/components/zodiac/translations/pl.json @@ -0,0 +1,22 @@ +{ + "entity": { + "sensor": { + "sign": { + "state": { + "aquarius": "wodnik", + "aries": "baran", + "cancer": "rak", + "capricorn": "kozioro\u017cec", + "gemini": "bli\u017ani\u0119ta", + "leo": "lew", + "libra": "waga", + "pisces": "ryby", + "sagittarius": "strzelec", + "scorpio": "skorpion", + "taurus": "byk", + "virgo": "panna" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zodiac/translations/pt-BR.json b/homeassistant/components/zodiac/translations/pt-BR.json new file mode 100644 index 00000000000..2bd771e85fc --- /dev/null +++ b/homeassistant/components/zodiac/translations/pt-BR.json @@ -0,0 +1,22 @@ +{ + "entity": { + "sensor": { + "sign": { + "state": { + "aquarius": "Aqu\u00e1rio", + "aries": "\u00c1ries", + "cancer": "C\u00e2ncer", + "capricorn": "Capric\u00f3rnio", + "gemini": "G\u00eameos", + "leo": "Le\u00e3o", + "libra": "Libra", + "pisces": "Peixes", + "sagittarius": "Sagit\u00e1rio", + "scorpio": "Escorpi\u00e3o", + "taurus": "Touro", + "virgo": "Virgem" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zodiac/translations/ru.json b/homeassistant/components/zodiac/translations/ru.json new file mode 100644 index 00000000000..afcac3a8c0d --- /dev/null +++ b/homeassistant/components/zodiac/translations/ru.json @@ -0,0 +1,22 @@ +{ + "entity": { + "sensor": { + "sign": { + "state": { + "aquarius": "\u0412\u043e\u0434\u043e\u043b\u0435\u0439", + "aries": "\u041e\u0432\u0435\u043d", + "cancer": "\u0420\u0430\u043a", + "capricorn": "\u041a\u043e\u0437\u0435\u0440\u043e\u0433", + "gemini": "\u0411\u043b\u0438\u0437\u043d\u0435\u0446\u044b", + "leo": "\u041b\u0435\u0432", + "libra": "\u0412\u0435\u0441\u044b", + "pisces": "\u0420\u044b\u0431\u044b", + "sagittarius": "\u0421\u0442\u0440\u0435\u043b\u0435\u0446", + "scorpio": "\u0421\u043a\u043e\u0440\u043f\u0438\u043e\u043d", + "taurus": "\u0422\u0435\u043b\u0435\u0446", + "virgo": "\u0414\u0435\u0432\u0430" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zodiac/translations/sk.json b/homeassistant/components/zodiac/translations/sk.json new file mode 100644 index 00000000000..2ef939e90d7 --- /dev/null +++ b/homeassistant/components/zodiac/translations/sk.json @@ -0,0 +1,22 @@ +{ + "entity": { + "sensor": { + "sign": { + "state": { + "aquarius": "Vodn\u00e1r", + "aries": "Baran", + "cancer": "Rak", + "capricorn": "Kozoro\u017eec", + "gemini": "Bl\u00ed\u017eenci", + "leo": "Lev", + "libra": "V\u00e1hy", + "pisces": "Ryby", + "sagittarius": "Strelec", + "scorpio": "\u0160korpi\u00f3n", + "taurus": "B\u00fdk", + "virgo": "Panna" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zodiac/translations/zh-Hant.json b/homeassistant/components/zodiac/translations/zh-Hant.json new file mode 100644 index 00000000000..6df4f9c16aa --- /dev/null +++ b/homeassistant/components/zodiac/translations/zh-Hant.json @@ -0,0 +1,22 @@ +{ + "entity": { + "sensor": { + "sign": { + "state": { + "aquarius": "\u6c34\u74f6\u5ea7", + "aries": "\u7261\u7f8a\u5ea7", + "cancer": "\u5de8\u87f9\u5ea7", + "capricorn": "\u6469\u7faf\u5ea7", + "gemini": "\u96d9\u5b50\u5ea7", + "leo": "\u7345\u5b50\u5ea7", + "libra": "\u5929\u79e4\u5ea7", + "pisces": "\u96d9\u9b5a\u5ea7", + "sagittarius": "\u5c04\u624b\u5ea7", + "scorpio": "\u5929\u880d\u5ea7", + "taurus": "\u91d1\u725b\u5ea7", + "virgo": "\u8655\u5973\u5ea7" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zone/translations/lb.json b/homeassistant/components/zone/translations/lb.json index 10b65bcca30..f5d2fe8fa8d 100644 --- a/homeassistant/components/zone/translations/lb.json +++ b/homeassistant/components/zone/translations/lb.json @@ -6,16 +6,16 @@ "step": { "init": { "data": { - "icon": "Ikone", + "icon": "Ikon", "latitude": "Breedegrad", "longitude": "L\u00e4ngegrad", "name": "Numm", "passive": "Passif", "radius": "Radius" }, - "title": "D\u00e9fin\u00e9iert Zone Parameter" + "title": "D\u00e9fin\u00e9ier d'Zoneparameter" } }, - "title": "Zone" + "title": "Zon" } } \ No newline at end of file diff --git a/homeassistant/components/zone/translations/sk.json b/homeassistant/components/zone/translations/sk.json index d1d6c76ca43..329b5423340 100644 --- a/homeassistant/components/zone/translations/sk.json +++ b/homeassistant/components/zone/translations/sk.json @@ -9,9 +9,13 @@ "icon": "Ikona", "latitude": "Zemepisn\u00e1 \u0161\u00edrka", "longitude": "Zemepisn\u00e1 d\u013a\u017eka", - "name": "N\u00e1zov" - } + "name": "N\u00e1zov", + "passive": "Pas\u00edvny", + "radius": "Polomer" + }, + "title": "Definujte parametre z\u00f3ny" } - } + }, + "title": "Z\u00f3na" } } \ No newline at end of file diff --git a/homeassistant/components/zone/trigger.py b/homeassistant/components/zone/trigger.py index 9fdfc9dcc90..54a2784467d 100644 --- a/homeassistant/components/zone/trigger.py +++ b/homeassistant/components/zone/trigger.py @@ -94,7 +94,10 @@ async def async_attach_trigger( if not (zone_state := hass.states.get(zone_entity_id)): _LOGGER.warning( - "Automation '%s' is referencing non-existing zone '%s' in a zone trigger", + ( + "Automation '%s' is referencing non-existing zone '%s' in a zone" + " trigger" + ), trigger_info["name"], zone_entity_id, ) diff --git a/homeassistant/components/zoneminder/translations/de.json b/homeassistant/components/zoneminder/translations/de.json index a0bf38b8def..c412024a282 100644 --- a/homeassistant/components/zoneminder/translations/de.json +++ b/homeassistant/components/zoneminder/translations/de.json @@ -19,10 +19,10 @@ "step": { "user": { "data": { - "host": "Host und Port (z. B. 10.10.0.4:8010)", + "host": "Host und Port (z.B. 10.10.0.4:8010)", "password": "Passwort", - "path": "ZM-Pfad", - "path_zms": "ZMS-Pfad", + "path": "ZM Pfad", + "path_zms": "ZMS Pfad", "ssl": "Verwendet ein SSL-Zertifikat", "username": "Benutzername", "verify_ssl": "SSL-Zertifikat \u00fcberpr\u00fcfen" diff --git a/homeassistant/components/zoneminder/translations/pt.json b/homeassistant/components/zoneminder/translations/pt.json index b85c7c2e7a7..bc71704474d 100644 --- a/homeassistant/components/zoneminder/translations/pt.json +++ b/homeassistant/components/zoneminder/translations/pt.json @@ -2,11 +2,11 @@ "config": { "abort": { "auth_fail": "O nome de utilizador ou palavra-passe est\u00e1 incorrecto.", - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", + "cannot_connect": "A liga\u00e7\u00e3o falhou", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida" }, "step": { diff --git a/homeassistant/components/zoneminder/translations/sk.json b/homeassistant/components/zoneminder/translations/sk.json index 0a34af4a435..84ee4749c00 100644 --- a/homeassistant/components/zoneminder/translations/sk.json +++ b/homeassistant/components/zoneminder/translations/sk.json @@ -6,20 +6,28 @@ "connection_error": "Nepodarilo sa pripoji\u0165 k serveru ZoneMinder.", "invalid_auth": "Neplatn\u00e9 overenie" }, + "create_entry": { + "default": "Pridan\u00fd server ZoneMinder." + }, "error": { "auth_fail": "U\u017eivate\u013esk\u00e9 meno alebo heslo je nespr\u00e1vne.", "cannot_connect": "Nepodarilo sa pripoji\u0165", + "connection_error": "Pripojenie k serveru ZoneMinder sa nepodarilo.", "invalid_auth": "Neplatn\u00e9 overenie" }, + "flow_title": "ZoneMinder", "step": { "user": { "data": { "host": "Hostite\u013e a port (napr. 10.10.0.4:8010)", "password": "Heslo", + "path": "ZM Path", + "path_zms": "ZMS Path", "ssl": "Pou\u017e\u00edva SSL certifik\u00e1t", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno", "verify_ssl": "Overi\u0165 SSL certifik\u00e1t" - } + }, + "title": "Pridajte server ZoneMinder." } } } diff --git a/homeassistant/components/zwave_js/__init__.py b/homeassistant/components/zwave_js/__init__.py index 2d6e7350899..25ca742a611 100644 --- a/homeassistant/components/zwave_js/__init__.py +++ b/homeassistant/components/zwave_js/__init__.py @@ -377,7 +377,11 @@ class ControllerEvents: async_dispatcher_send( self.hass, - f"{DOMAIN}_{get_valueless_base_unique_id(self.driver_events.driver, node)}_remove_entity", + ( + f"{DOMAIN}_" + f"{get_valueless_base_unique_id(self.driver_events.driver, node)}_" + "remove_entity" + ), ) else: self.remove_device(device) diff --git a/homeassistant/components/zwave_js/button.py b/homeassistant/components/zwave_js/button.py index c759471670b..3fcd0d4a5b9 100644 --- a/homeassistant/components/zwave_js/button.py +++ b/homeassistant/components/zwave_js/button.py @@ -63,8 +63,8 @@ class ZWaveNodePingButton(ButtonEntity): async def async_poll_value(self, _: bool) -> None: """Poll a value.""" LOGGER.error( - "There is no value to refresh for this entity so the zwave_js.refresh_value " - "service won't work for it" + "There is no value to refresh for this entity so the zwave_js.refresh_value" + " service won't work for it" ) async def async_added_to_hass(self) -> None: diff --git a/homeassistant/components/zwave_js/config_flow.py b/homeassistant/components/zwave_js/config_flow.py index 11fd3da0e75..e6411e3b879 100644 --- a/homeassistant/components/zwave_js/config_flow.py +++ b/homeassistant/components/zwave_js/config_flow.py @@ -1,7 +1,7 @@ """Config flow for Z-Wave JS integration.""" from __future__ import annotations -from abc import abstractmethod +from abc import ABC, abstractmethod import asyncio import logging from typing import Any @@ -157,7 +157,7 @@ async def async_get_usb_ports(hass: HomeAssistant) -> dict[str, str]: return await hass.async_add_executor_job(get_usb_ports) -class BaseZwaveJSFlow(FlowHandler): +class BaseZwaveJSFlow(FlowHandler, ABC): """Represent the base config flow for Z-Wave JS.""" def __init__(self) -> None: diff --git a/homeassistant/components/zwave_js/discovery_data_template.py b/homeassistant/components/zwave_js/discovery_data_template.py index 0ee7c3d758e..7d9f20b111e 100644 --- a/homeassistant/components/zwave_js/discovery_data_template.py +++ b/homeassistant/components/zwave_js/discovery_data_template.py @@ -34,6 +34,7 @@ from zwave_js_server.const.command_class.multilevel_sensor import ( PRESSURE_SENSORS, SIGNAL_STRENGTH_SENSORS, TEMPERATURE_SENSORS, + UNIT_A_WEIGHTED_DECIBELS, UNIT_AMPERE as SENSOR_UNIT_AMPERE, UNIT_BTU_H, UNIT_CELSIUS, @@ -52,6 +53,7 @@ from zwave_js_server.const.command_class.multilevel_sensor import ( UNIT_INCHES_PER_HOUR, UNIT_KILOGRAM, UNIT_KILOHERTZ, + UNIT_KILOPASCAL, UNIT_LITER, UNIT_LUX, UNIT_M_S, @@ -69,6 +71,7 @@ from zwave_js_server.const.command_class.multilevel_sensor import ( UNIT_RSSI, UNIT_SECOND, UNIT_SYSTOLIC, + UNIT_UV_INDEX, UNIT_VOLT as SENSOR_UNIT_VOLT, UNIT_WATT as SENSOR_UNIT_WATT, UNIT_WATT_PER_SQUARE_METER, @@ -92,28 +95,25 @@ from homeassistant.const import ( CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, CONCENTRATION_PARTS_PER_MILLION, DEGREE, - ELECTRIC_CURRENT_AMPERE, - ELECTRIC_CURRENT_MILLIAMPERE, - ELECTRIC_POTENTIAL_MILLIVOLT, - ELECTRIC_POTENTIAL_VOLT, - FREQUENCY_HERTZ, - FREQUENCY_KILOHERTZ, - IRRADIATION_WATTS_PER_SQUARE_METER, LIGHT_LUX, PERCENTAGE, - SIGNAL_STRENGTH_DECIBELS, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, - TIME_SECONDS, - VOLUME_FLOW_RATE_CUBIC_FEET_PER_MINUTE, - VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR, + UV_INDEX, + UnitOfElectricCurrent, + UnitOfElectricPotential, UnitOfEnergy, + UnitOfFrequency, + UnitOfIrradiance, UnitOfLength, UnitOfMass, UnitOfPower, UnitOfPressure, + UnitOfSoundPressure, UnitOfSpeed, UnitOfTemperature, + UnitOfTime, UnitOfVolume, + UnitOfVolumeFlowRate, UnitOfVolumetricFlux, ) @@ -138,7 +138,7 @@ from .const import ( ) from .helpers import ZwaveValueID -METER_DEVICE_CLASS_MAP: dict[str, set[MeterScaleType]] = { +METER_DEVICE_CLASS_MAP: dict[str, list[MeterScaleType]] = { ENTITY_DESC_KEY_CURRENT: CURRENT_METER_TYPES, ENTITY_DESC_KEY_VOLTAGE: VOLTAGE_METER_TYPES, ENTITY_DESC_KEY_ENERGY_TOTAL_INCREASING: ENERGY_TOTAL_INCREASING_METER_TYPES, @@ -146,7 +146,7 @@ METER_DEVICE_CLASS_MAP: dict[str, set[MeterScaleType]] = { ENTITY_DESC_KEY_POWER_FACTOR: POWER_FACTOR_METER_TYPES, } -MULTILEVEL_SENSOR_DEVICE_CLASS_MAP: dict[str, set[MultilevelSensorType]] = { +MULTILEVEL_SENSOR_DEVICE_CLASS_MAP: dict[str, list[MultilevelSensorType]] = { ENTITY_DESC_KEY_CO: CO_SENSORS, ENTITY_DESC_KEY_CO2: CO2_SENSORS, ENTITY_DESC_KEY_CURRENT: CURRENT_SENSORS, @@ -160,56 +160,59 @@ MULTILEVEL_SENSOR_DEVICE_CLASS_MAP: dict[str, set[MultilevelSensorType]] = { ENTITY_DESC_KEY_VOLTAGE: VOLTAGE_SENSORS, } -METER_UNIT_MAP: dict[str, set[MeterScaleType]] = { - ELECTRIC_CURRENT_AMPERE: METER_UNIT_AMPERE, +METER_UNIT_MAP: dict[str, list[MeterScaleType]] = { + UnitOfElectricCurrent.AMPERE: METER_UNIT_AMPERE, UnitOfVolume.CUBIC_FEET: UNIT_CUBIC_FEET, UnitOfVolume.CUBIC_METERS: METER_UNIT_CUBIC_METER, UnitOfVolume.GALLONS: UNIT_US_GALLON, UnitOfEnergy.KILO_WATT_HOUR: UNIT_KILOWATT_HOUR, - ELECTRIC_POTENTIAL_VOLT: METER_UNIT_VOLT, + UnitOfElectricPotential.VOLT: METER_UNIT_VOLT, UnitOfPower.WATT: METER_UNIT_WATT, } -MULTILEVEL_SENSOR_UNIT_MAP: dict[str, set[MultilevelSensorScaleType]] = { - ELECTRIC_CURRENT_AMPERE: SENSOR_UNIT_AMPERE, +MULTILEVEL_SENSOR_UNIT_MAP: dict[str, list[MultilevelSensorScaleType]] = { + UnitOfElectricCurrent.AMPERE: SENSOR_UNIT_AMPERE, UnitOfPower.BTU_PER_HOUR: UNIT_BTU_H, UnitOfTemperature.CELSIUS: UNIT_CELSIUS, UnitOfLength.CENTIMETERS: UNIT_CENTIMETER, - VOLUME_FLOW_RATE_CUBIC_FEET_PER_MINUTE: UNIT_CUBIC_FEET_PER_MINUTE, + UnitOfVolumeFlowRate.CUBIC_FEET_PER_MINUTE: UNIT_CUBIC_FEET_PER_MINUTE, UnitOfVolume.CUBIC_METERS: SENSOR_UNIT_CUBIC_METER, - VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR: UNIT_CUBIC_METER_PER_HOUR, - SIGNAL_STRENGTH_DECIBELS: UNIT_DECIBEL, + UnitOfVolumeFlowRate.CUBIC_METERS_PER_HOUR: UNIT_CUBIC_METER_PER_HOUR, + UnitOfSoundPressure.DECIBEL: UNIT_DECIBEL, + UnitOfSoundPressure.WEIGHTED_DECIBEL_A: UNIT_A_WEIGHTED_DECIBELS, DEGREE: UNIT_DEGREES, - CONCENTRATION_MICROGRAMS_PER_CUBIC_METER: { + CONCENTRATION_MICROGRAMS_PER_CUBIC_METER: [ *UNIT_DENSITY, *UNIT_MICROGRAM_PER_CUBIC_METER, - }, + ], UnitOfTemperature.FAHRENHEIT: UNIT_FAHRENHEIT, UnitOfLength.FEET: UNIT_FEET, UnitOfVolume.GALLONS: UNIT_GALLONS, - FREQUENCY_HERTZ: UNIT_HERTZ, + UnitOfFrequency.HERTZ: UNIT_HERTZ, UnitOfPressure.INHG: UNIT_INCHES_OF_MERCURY, + UnitOfPressure.KPA: UNIT_KILOPASCAL, UnitOfVolumetricFlux.INCHES_PER_HOUR: UNIT_INCHES_PER_HOUR, UnitOfMass.KILOGRAMS: UNIT_KILOGRAM, - FREQUENCY_KILOHERTZ: UNIT_KILOHERTZ, + UnitOfFrequency.KILOHERTZ: UNIT_KILOHERTZ, UnitOfVolume.LITERS: UNIT_LITER, LIGHT_LUX: UNIT_LUX, UnitOfLength.METERS: UNIT_METER, - ELECTRIC_CURRENT_MILLIAMPERE: UNIT_MILLIAMPERE, + UnitOfElectricCurrent.MILLIAMPERE: UNIT_MILLIAMPERE, UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR: UNIT_MILLIMETER_HOUR, - ELECTRIC_POTENTIAL_MILLIVOLT: UNIT_MILLIVOLT, + UnitOfElectricPotential.MILLIVOLT: UNIT_MILLIVOLT, UnitOfSpeed.MILES_PER_HOUR: UNIT_MPH, UnitOfSpeed.METERS_PER_SECOND: UNIT_M_S, CONCENTRATION_PARTS_PER_MILLION: UNIT_PARTS_MILLION, - PERCENTAGE: {*UNIT_PERCENTAGE_VALUE, *UNIT_RSSI}, + PERCENTAGE: [*UNIT_PERCENTAGE_VALUE, *UNIT_RSSI], UnitOfMass.POUNDS: UNIT_POUNDS, UnitOfPressure.PSI: UNIT_POUND_PER_SQUARE_INCH, SIGNAL_STRENGTH_DECIBELS_MILLIWATT: UNIT_POWER_LEVEL, - TIME_SECONDS: UNIT_SECOND, + UnitOfTime.SECONDS: UNIT_SECOND, UnitOfPressure.MMHG: UNIT_SYSTOLIC, - ELECTRIC_POTENTIAL_VOLT: SENSOR_UNIT_VOLT, + UnitOfElectricPotential.VOLT: SENSOR_UNIT_VOLT, UnitOfPower.WATT: SENSOR_UNIT_WATT, - IRRADIATION_WATTS_PER_SQUARE_METER: UNIT_WATT_PER_SQUARE_METER, + UnitOfIrradiance.WATTS_PER_SQUARE_METER: UNIT_WATT_PER_SQUARE_METER, + UV_INDEX: UNIT_UV_INDEX, } _LOGGER = logging.getLogger(__name__) @@ -323,9 +326,9 @@ class NumericSensorDataTemplate(BaseDiscoverySchemaDataTemplate): enum_value: MultilevelSensorType | MultilevelSensorScaleType | MeterScaleType, set_map: Mapping[ str, - set[MultilevelSensorType] - | set[MultilevelSensorScaleType] - | set[MeterScaleType], + list[MultilevelSensorType] + | list[MultilevelSensorScaleType] + | list[MeterScaleType], ], ) -> str | None: """Find a key in a set map that matches a given enum value.""" diff --git a/homeassistant/components/zwave_js/fan.py b/homeassistant/components/zwave_js/fan.py index bf4942d76cc..093f117c6a0 100644 --- a/homeassistant/components/zwave_js/fan.py +++ b/homeassistant/components/zwave_js/fan.py @@ -181,7 +181,8 @@ class ValueMappingZwaveFan(ZwaveFan): return raise NotValidPresetModeError( - f"The preset_mode {preset_mode} is not a valid preset_mode: {self.preset_modes}" + f"The preset_mode {preset_mode} is not a valid preset_mode:" + f" {self.preset_modes}" ) @property diff --git a/homeassistant/components/zwave_js/manifest.json b/homeassistant/components/zwave_js/manifest.json index 4dc86fed92c..9b313582eb5 100644 --- a/homeassistant/components/zwave_js/manifest.json +++ b/homeassistant/components/zwave_js/manifest.json @@ -3,7 +3,7 @@ "name": "Z-Wave", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/zwave_js", - "requirements": ["pyserial==3.5", "zwave-js-server-python==0.43.1"], + "requirements": ["pyserial==3.5", "zwave-js-server-python==0.44.0"], "codeowners": ["@home-assistant/z-wave"], "dependencies": ["usb", "http", "websocket_api"], "iot_class": "local_push", diff --git a/homeassistant/components/zwave_js/number.py b/homeassistant/components/zwave_js/number.py index 7f7f5d65bb8..a6a219fa7c6 100644 --- a/homeassistant/components/zwave_js/number.py +++ b/homeassistant/components/zwave_js/number.py @@ -71,30 +71,26 @@ class ZwaveNumberEntity(ZWaveBaseEntity, NumberEntity): @property def native_min_value(self) -> float: """Return the minimum value.""" - if self.info.primary_value.metadata.min is None: - return 0 - return float(self.info.primary_value.metadata.min) + min_ = self.info.primary_value.metadata.min + return float(0 if min_ is None else min_) @property def native_max_value(self) -> float: """Return the maximum value.""" - if self.info.primary_value.metadata.max is None: - return 255 - return float(self.info.primary_value.metadata.max) + max_ = self.info.primary_value.metadata.max + return float(255 if max_ is None else max_) @property def native_value(self) -> float | None: """Return the entity value.""" - if self.info.primary_value.value is None: - return None - return float(self.info.primary_value.value) + value = self.info.primary_value.value + return None if value is None else float(value) @property def native_unit_of_measurement(self) -> str | None: """Return the unit of measurement of this entity, if any.""" - if self.info.primary_value.metadata.unit is None: - return None - return str(self.info.primary_value.metadata.unit) + unit = self.info.primary_value.metadata.unit + return None if unit is None else str(unit) async def async_set_native_value(self, value: float) -> None: """Set new value.""" diff --git a/homeassistant/components/zwave_js/sensor.py b/homeassistant/components/zwave_js/sensor.py index 4592a6518b8..0177d694ed4 100644 --- a/homeassistant/components/zwave_js/sensor.py +++ b/homeassistant/components/zwave_js/sensor.py @@ -24,6 +24,18 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry +from homeassistant.const import ( + CONCENTRATION_PARTS_PER_MILLION, + LIGHT_LUX, + PERCENTAGE, + SIGNAL_STRENGTH_DECIBELS_MILLIWATT, + UnitOfElectricCurrent, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfPower, + UnitOfPressure, + UnitOfTemperature, +) from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_platform @@ -76,98 +88,207 @@ STATUS_ICON: dict[NodeStatus, str] = { } -ENTITY_DESCRIPTION_KEY_MAP: dict[str, SensorEntityDescription] = { - ENTITY_DESC_KEY_BATTERY: SensorEntityDescription( +# These descriptions should include device class. +ENTITY_DESCRIPTION_KEY_DEVICE_CLASS_MAP: dict[ + tuple[str, str], SensorEntityDescription +] = { + (ENTITY_DESC_KEY_BATTERY, PERCENTAGE): SensorEntityDescription( ENTITY_DESC_KEY_BATTERY, device_class=SensorDeviceClass.BATTERY, entity_category=EntityCategory.DIAGNOSTIC, state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=PERCENTAGE, ), - ENTITY_DESC_KEY_CURRENT: SensorEntityDescription( + (ENTITY_DESC_KEY_CURRENT, UnitOfElectricCurrent.AMPERE): SensorEntityDescription( ENTITY_DESC_KEY_CURRENT, device_class=SensorDeviceClass.CURRENT, state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, ), - ENTITY_DESC_KEY_VOLTAGE: SensorEntityDescription( + (ENTITY_DESC_KEY_VOLTAGE, UnitOfElectricPotential.VOLT): SensorEntityDescription( ENTITY_DESC_KEY_VOLTAGE, device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, ), - ENTITY_DESC_KEY_ENERGY_MEASUREMENT: SensorEntityDescription( - ENTITY_DESC_KEY_ENERGY_MEASUREMENT, - device_class=SensorDeviceClass.ENERGY, + ( + ENTITY_DESC_KEY_VOLTAGE, + UnitOfElectricPotential.MILLIVOLT, + ): SensorEntityDescription( + ENTITY_DESC_KEY_VOLTAGE, + device_class=SensorDeviceClass.VOLTAGE, state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UnitOfElectricPotential.MILLIVOLT, ), - ENTITY_DESC_KEY_ENERGY_TOTAL_INCREASING: SensorEntityDescription( + ( + ENTITY_DESC_KEY_ENERGY_TOTAL_INCREASING, + UnitOfEnergy.KILO_WATT_HOUR, + ): SensorEntityDescription( ENTITY_DESC_KEY_ENERGY_TOTAL_INCREASING, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, ), - ENTITY_DESC_KEY_POWER: SensorEntityDescription( + (ENTITY_DESC_KEY_POWER, UnitOfPower.WATT): SensorEntityDescription( ENTITY_DESC_KEY_POWER, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UnitOfPower.WATT, ), - ENTITY_DESC_KEY_POWER_FACTOR: SensorEntityDescription( + (ENTITY_DESC_KEY_POWER_FACTOR, PERCENTAGE): SensorEntityDescription( ENTITY_DESC_KEY_POWER_FACTOR, device_class=SensorDeviceClass.POWER_FACTOR, state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=PERCENTAGE, ), - ENTITY_DESC_KEY_CO: SensorEntityDescription( + (ENTITY_DESC_KEY_CO, CONCENTRATION_PARTS_PER_MILLION): SensorEntityDescription( ENTITY_DESC_KEY_CO, device_class=SensorDeviceClass.CO, state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION, ), - ENTITY_DESC_KEY_CO2: SensorEntityDescription( + (ENTITY_DESC_KEY_CO2, CONCENTRATION_PARTS_PER_MILLION): SensorEntityDescription( ENTITY_DESC_KEY_CO2, device_class=SensorDeviceClass.CO2, state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION, ), - ENTITY_DESC_KEY_HUMIDITY: SensorEntityDescription( + (ENTITY_DESC_KEY_HUMIDITY, PERCENTAGE): SensorEntityDescription( ENTITY_DESC_KEY_HUMIDITY, device_class=SensorDeviceClass.HUMIDITY, state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=PERCENTAGE, ), - ENTITY_DESC_KEY_ILLUMINANCE: SensorEntityDescription( + (ENTITY_DESC_KEY_ILLUMINANCE, LIGHT_LUX): SensorEntityDescription( ENTITY_DESC_KEY_ILLUMINANCE, device_class=SensorDeviceClass.ILLUMINANCE, state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=LIGHT_LUX, ), - ENTITY_DESC_KEY_PRESSURE: SensorEntityDescription( + (ENTITY_DESC_KEY_PRESSURE, UnitOfPressure.KPA): SensorEntityDescription( ENTITY_DESC_KEY_PRESSURE, device_class=SensorDeviceClass.PRESSURE, state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UnitOfPressure.KPA, ), - ENTITY_DESC_KEY_SIGNAL_STRENGTH: SensorEntityDescription( + (ENTITY_DESC_KEY_PRESSURE, UnitOfPressure.PSI): SensorEntityDescription( + ENTITY_DESC_KEY_PRESSURE, + device_class=SensorDeviceClass.PRESSURE, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UnitOfPressure.PSI, + ), + (ENTITY_DESC_KEY_PRESSURE, UnitOfPressure.INHG): SensorEntityDescription( + ENTITY_DESC_KEY_PRESSURE, + device_class=SensorDeviceClass.PRESSURE, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UnitOfPressure.INHG, + ), + (ENTITY_DESC_KEY_PRESSURE, UnitOfPressure.MMHG): SensorEntityDescription( + ENTITY_DESC_KEY_PRESSURE, + device_class=SensorDeviceClass.PRESSURE, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UnitOfPressure.MMHG, + ), + ( + ENTITY_DESC_KEY_SIGNAL_STRENGTH, + SIGNAL_STRENGTH_DECIBELS_MILLIWATT, + ): SensorEntityDescription( ENTITY_DESC_KEY_SIGNAL_STRENGTH, device_class=SensorDeviceClass.SIGNAL_STRENGTH, entity_category=EntityCategory.DIAGNOSTIC, entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS_MILLIWATT, ), - ENTITY_DESC_KEY_TEMPERATURE: SensorEntityDescription( + (ENTITY_DESC_KEY_TEMPERATURE, UnitOfTemperature.CELSIUS): SensorEntityDescription( ENTITY_DESC_KEY_TEMPERATURE, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, ), - ENTITY_DESC_KEY_TARGET_TEMPERATURE: SensorEntityDescription( + ( + ENTITY_DESC_KEY_TEMPERATURE, + UnitOfTemperature.FAHRENHEIT, + ): SensorEntityDescription( + ENTITY_DESC_KEY_TEMPERATURE, + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, + ), + ( + ENTITY_DESC_KEY_TARGET_TEMPERATURE, + UnitOfTemperature.CELSIUS, + ): SensorEntityDescription( ENTITY_DESC_KEY_TARGET_TEMPERATURE, device_class=SensorDeviceClass.TEMPERATURE, - state_class=None, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + ), + ( + ENTITY_DESC_KEY_TARGET_TEMPERATURE, + UnitOfTemperature.FAHRENHEIT, + ): SensorEntityDescription( + ENTITY_DESC_KEY_TARGET_TEMPERATURE, + device_class=SensorDeviceClass.TEMPERATURE, + native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT, + ), +} + +# These descriptions are without device class. +ENTITY_DESCRIPTION_KEY_MAP = { + ENTITY_DESC_KEY_CO: SensorEntityDescription( + ENTITY_DESC_KEY_CO, + state_class=SensorStateClass.MEASUREMENT, + ), + ENTITY_DESC_KEY_ENERGY_MEASUREMENT: SensorEntityDescription( + ENTITY_DESC_KEY_ENERGY_MEASUREMENT, + state_class=SensorStateClass.MEASUREMENT, + ), + ENTITY_DESC_KEY_HUMIDITY: SensorEntityDescription( + ENTITY_DESC_KEY_HUMIDITY, + state_class=SensorStateClass.MEASUREMENT, + ), + ENTITY_DESC_KEY_ILLUMINANCE: SensorEntityDescription( + ENTITY_DESC_KEY_ILLUMINANCE, + state_class=SensorStateClass.MEASUREMENT, + ), + ENTITY_DESC_KEY_POWER_FACTOR: SensorEntityDescription( + ENTITY_DESC_KEY_POWER_FACTOR, + state_class=SensorStateClass.MEASUREMENT, + ), + ENTITY_DESC_KEY_SIGNAL_STRENGTH: SensorEntityDescription( + ENTITY_DESC_KEY_SIGNAL_STRENGTH, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + state_class=SensorStateClass.MEASUREMENT, ), ENTITY_DESC_KEY_MEASUREMENT: SensorEntityDescription( ENTITY_DESC_KEY_MEASUREMENT, - device_class=None, state_class=SensorStateClass.MEASUREMENT, ), ENTITY_DESC_KEY_TOTAL_INCREASING: SensorEntityDescription( ENTITY_DESC_KEY_TOTAL_INCREASING, - device_class=None, state_class=SensorStateClass.TOTAL_INCREASING, ), } +def get_entity_description( + data: NumericSensorDataTemplateData, +) -> SensorEntityDescription: + """Return the entity description for the given data.""" + data_description_key = data.entity_description_key or "" + data_unit = data.unit_of_measurement or "" + return ENTITY_DESCRIPTION_KEY_DEVICE_CLASS_MAP.get( + (data_description_key, data_unit), + ENTITY_DESCRIPTION_KEY_MAP.get( + data_description_key, + SensorEntityDescription( + "base_sensor", native_unit_of_measurement=data.unit_of_measurement + ), + ), + ) + + async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, @@ -187,9 +308,8 @@ async def async_setup_entry( data: NumericSensorDataTemplateData = info.platform_data else: data = NumericSensorDataTemplateData() - entity_description = ENTITY_DESCRIPTION_KEY_MAP.get( - data.entity_description_key or "", SensorEntityDescription("base_sensor") - ) + + entity_description = get_entity_description(data) if info.platform_hint == "string_sensor": entities.append( @@ -308,11 +428,9 @@ class ZWaveNumericSensor(ZwaveSensorBase): @callback def on_value_update(self) -> None: """Handle scale changes for this value on value updated event.""" - self._attr_native_unit_of_measurement = ( - NumericSensorDataTemplate() - .resolve_data(self.info.primary_value) - .unit_of_measurement - ) + data = NumericSensorDataTemplate().resolve_data(self.info.primary_value) + self.entity_description = get_entity_description(data) + self._attr_native_unit_of_measurement = data.unit_of_measurement @property def native_value(self) -> float: @@ -324,6 +442,8 @@ class ZWaveNumericSensor(ZwaveSensorBase): @property def native_unit_of_measurement(self) -> str | None: """Return unit of measurement the value is expressed in.""" + if self.entity_description.native_unit_of_measurement is not None: + return self.entity_description.native_unit_of_measurement if self._attr_native_unit_of_measurement is not None: return self._attr_native_unit_of_measurement if self.info.primary_value.metadata.unit is None: @@ -490,8 +610,8 @@ class ZWaveNodeStatusSensor(SensorEntity): async def async_poll_value(self, _: bool) -> None: """Poll a value.""" LOGGER.error( - "There is no value to refresh for this entity so the zwave_js.refresh_value " - "service won't work for it" + "There is no value to refresh for this entity so the zwave_js.refresh_value" + " service won't work for it" ) @callback diff --git a/homeassistant/components/zwave_js/strings.json b/homeassistant/components/zwave_js/strings.json index 7446edb0c5d..0bcb209a760 100644 --- a/homeassistant/components/zwave_js/strings.json +++ b/homeassistant/components/zwave_js/strings.json @@ -8,7 +8,7 @@ } }, "usb_confirm": { - "description": "Do you want to setup {name} with the Z-Wave JS add-on?" + "description": "Do you want to set up {name} with the Z-Wave JS add-on?" }, "on_supervisor": { "title": "Select connection method", diff --git a/homeassistant/components/zwave_js/translations/de.json b/homeassistant/components/zwave_js/translations/de.json index 3e1616c77c7..306cdc4e0a4 100644 --- a/homeassistant/components/zwave_js/translations/de.json +++ b/homeassistant/components/zwave_js/translations/de.json @@ -107,7 +107,7 @@ "addon_start_failed": "Der Start des Z-Wave JS-Add-ons ist fehlgeschlagen.", "already_configured": "Ger\u00e4t ist bereits konfiguriert", "cannot_connect": "Verbindung fehlgeschlagen", - "different_device": "Das angeschlossene USB-Ger\u00e4t ist nicht dasselbe, das zuvor f\u00fcr diesen Config-Eintrag konfiguriert wurde. Bitte erstelle stattdessen einen neuen Konfigurationseintrag f\u00fcr das neue Ger\u00e4t." + "different_device": "Das angeschlossene USB-Ger\u00e4t ist nicht dasselbe, das zuvor f\u00fcr diesen Konfigurationseintrag konfiguriert wurde. Bitte erstelle stattdessen einen neuen Konfigurationseintrag f\u00fcr das neue Ger\u00e4t." }, "error": { "cannot_connect": "Verbindung fehlgeschlagen", diff --git a/homeassistant/components/zwave_js/translations/en.json b/homeassistant/components/zwave_js/translations/en.json index 3d288c3ebae..d614845d963 100644 --- a/homeassistant/components/zwave_js/translations/en.json +++ b/homeassistant/components/zwave_js/translations/en.json @@ -58,7 +58,7 @@ "title": "The Z-Wave JS add-on is starting." }, "usb_confirm": { - "description": "Do you want to setup {name} with the Z-Wave JS add-on?" + "description": "Do you want to set up {name} with the Z-Wave JS add-on?" }, "zeroconf_confirm": { "description": "Do you want to add the Z-Wave JS Server with home ID {home_id} found at {url} to Home Assistant?", diff --git a/homeassistant/components/zwave_js/translations/it.json b/homeassistant/components/zwave_js/translations/it.json index d4377134977..65fbbbed576 100644 --- a/homeassistant/components/zwave_js/translations/it.json +++ b/homeassistant/components/zwave_js/translations/it.json @@ -58,7 +58,7 @@ "title": "Il componente aggiuntivo Z-Wave JS si sta avviando." }, "usb_confirm": { - "description": "Vuoi configurare {name} con il componente aggiuntivo JS Z-Wave?" + "description": "Vuoi configurare {name} con il componente aggiuntivo Z-Wave JS?" }, "zeroconf_confirm": { "description": "Vuoi aggiungere il server Z-Wave JS con l'ID casa {home_id} trovato in {url} a Home Assistant?", diff --git a/homeassistant/components/zwave_js/translations/ko.json b/homeassistant/components/zwave_js/translations/ko.json index 0d00d27fea2..54a5c9e4e9f 100644 --- a/homeassistant/components/zwave_js/translations/ko.json +++ b/homeassistant/components/zwave_js/translations/ko.json @@ -64,5 +64,27 @@ "trigger_type": { "event.value_notification.scene_activation": "{subtype} \uc5d0\uc11c \uc7a5\uba74 \ud65c\uc131\ud654" } + }, + "options": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "configure_addon": { + "data": { + "usb_path": "USB \uc7a5\uce58 \uacbd\ub85c" + } + }, + "manual": { + "data": { + "url": "URL \uc8fc\uc18c" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/zwave_js/translations/nl.json b/homeassistant/components/zwave_js/translations/nl.json index f57791b2333..f54a5390ec9 100644 --- a/homeassistant/components/zwave_js/translations/nl.json +++ b/homeassistant/components/zwave_js/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "addon_get_discovery_info_failed": "Ophalen van ontdekkingsinformatie voor Z-Wave JS-add-on is mislukt.", + "addon_get_discovery_info_failed": "Ophalen van detectie informatie voor Z-Wave JS-add-on is mislukt.", "addon_info_failed": "Ophalen van Z-Wave JS add-on-info is mislukt.", "addon_install_failed": "Kan de Z-Wave JS add-on niet installeren.", "addon_set_config_failed": "Instellen van de Z-Wave JS configuratie is mislukt.", @@ -10,7 +10,8 @@ "already_in_progress": "De configuratie is momenteel al bezig", "cannot_connect": "Kan geen verbinding maken", "discovery_requires_supervisor": "Ontdekking vereist de Supervisor.", - "not_zwave_device": "Het ontdekte apparaat is niet een Z-Wave apparaat." + "not_zwave_device": "Het ontdekte apparaat is niet een Z-Wave apparaat.", + "not_zwave_js_addon": "De ontdekte add-on is niet de offici\u00eble Z-Wave JS add-on." }, "error": { "addon_start_failed": "Het is niet gelukt om de Z-Wave JS add-on te starten. Controleer de configuratie.", @@ -61,7 +62,7 @@ }, "zeroconf_confirm": { "description": "Wilt u de Z-Wave JS Server met home ID {home_id} gevonden op {url} toevoegen aan Home Assistant?", - "title": "Ontdekt Z-Wave JS Server" + "title": "Gevonden Z-Wave JS Server" } } }, @@ -93,12 +94,13 @@ }, "issues": { "invalid_server_version": { + "description": "De versie van Z-Wave JS Server die wordt uitgevoerd is te oud voor deze versie van Home Assistant. Werk de Z-Wave JS Server bij naar de laatste versie om probleem te verhelpen.", "title": "Nieuwere versie van Z-Wave JS-server vereist" } }, "options": { "abort": { - "addon_get_discovery_info_failed": "Ophalen van ontdekkingsinformatie voor Z-Wave JS-add-on is mislukt.", + "addon_get_discovery_info_failed": "Ophalen van detectie informatie voor Z-Wave JS-add-on is mislukt.", "addon_info_failed": "Ophalen van Z-Wave JS add-on-info is mislukt.", "addon_install_failed": "Kan de Z-Wave JS add-on niet installeren.", "addon_set_config_failed": "Instellen van de Z-Wave JS configuratie is mislukt.", diff --git a/homeassistant/components/zwave_js/translations/no.json b/homeassistant/components/zwave_js/translations/no.json index 7c3cec3f6f9..924e1269518 100644 --- a/homeassistant/components/zwave_js/translations/no.json +++ b/homeassistant/components/zwave_js/translations/no.json @@ -58,7 +58,7 @@ "title": "Z-Wave JS-tillegget starter" }, "usb_confirm": { - "description": "Vil du konfigurere {name} med Z-Wave JS-tillegget?" + "description": "Vil du sette opp {name} med Z-Wave JS-tillegget?" }, "zeroconf_confirm": { "description": "Vil du legge til Z-Wave JS Server med hjemme-ID {home_id} funnet p\u00e5 {url} til Home Assistant?", diff --git a/homeassistant/components/zwave_js/translations/pt.json b/homeassistant/components/zwave_js/translations/pt.json index dc59810f31e..e82fb475f28 100644 --- a/homeassistant/components/zwave_js/translations/pt.json +++ b/homeassistant/components/zwave_js/translations/pt.json @@ -1,10 +1,10 @@ { "config": { "abort": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" }, "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" + "cannot_connect": "A liga\u00e7\u00e3o falhou" } }, "options": { diff --git a/homeassistant/components/zwave_js/translations/sk.json b/homeassistant/components/zwave_js/translations/sk.json index f6eb2028a39..e8f1d0291e7 100644 --- a/homeassistant/components/zwave_js/translations/sk.json +++ b/homeassistant/components/zwave_js/translations/sk.json @@ -1,22 +1,46 @@ { "config": { "abort": { + "addon_get_discovery_info_failed": "Nepodarilo sa z\u00edska\u0165 inform\u00e1cie o zis\u0165ovan\u00ed doplnku Z-Wave JS.", + "addon_info_failed": "Nepodarilo sa z\u00edska\u0165 inform\u00e1cie o doplnku Z-Wave JS.", + "addon_install_failed": "Nepodarilo sa nain\u0161talova\u0165 doplnok Z-Wave JS.", + "addon_set_config_failed": "Nepodarilo sa nastavi\u0165 konfigur\u00e1ciu Z-Wave JS.", + "addon_start_failed": "Nepodarilo sa spusti\u0165 doplnok Z-Wave JS.", "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha", - "cannot_connect": "Nepodarilo sa pripoji\u0165" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "discovery_requires_supervisor": "Objavenie si vy\u017eaduje superv\u00edzora.", + "not_zwave_device": "Objaven\u00e9 zariadenie nie je zariadenie Z-Wave.", + "not_zwave_js_addon": "Doplnok Discovered nie je ofici\u00e1lnym doplnkom Z-Wave JS." }, "error": { + "addon_start_failed": "Nepodarilo sa spusti\u0165 doplnok Z-Wave JS. Skontrolujte konfigur\u00e1ciu.", "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_ws_url": "Neplatn\u00e1 adresa URL webov\u00e9ho soketu", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "flow_title": "{name}", + "progress": { + "install_addon": "Po\u010dkajte, k\u00fdm sa dokon\u010d\u00ed in\u0161tal\u00e1cia doplnku Z-Wave JS. M\u00f4\u017ee to trva\u0165 nieko\u013eko min\u00fat.", + "start_addon": "Po\u010dkajte, k\u00fdm sa dokon\u010d\u00ed spustenie doplnku Z-Wave JS. M\u00f4\u017ee to trva\u0165 nieko\u013eko sek\u00fand." + }, "step": { "configure_addon": { "data": { + "s0_legacy_key": "K\u013e\u00fa\u010d S0 (Legacy)", + "s2_access_control_key": "S2 K\u013e\u00fa\u010d na riadenie pr\u00edstupu", + "s2_authenticated_key": "S2 overen\u00fd k\u013e\u00fa\u010d", + "s2_unauthenticated_key": "S2 Neoveren\u00fd k\u013e\u00fa\u010d", "usb_path": "Cesta k zariadeniu USB" }, - "description": "Ak tieto polia zostan\u00fa pr\u00e1zdne, doplnok vygeneruje bezpe\u010dnostn\u00e9 k\u013e\u00fa\u010de." + "description": "Ak tieto polia zostan\u00fa pr\u00e1zdne, doplnok vygeneruje bezpe\u010dnostn\u00e9 k\u013e\u00fa\u010de.", + "title": "Zadajte konfigur\u00e1ciu doplnku Z-Wave JS" + }, + "hassio_confirm": { + "title": "Nastavte integr\u00e1ciu Z-Wave JS pomocou doplnku Z-Wave JS" + }, + "install_addon": { + "title": "In\u0161tal\u00e1cia doplnku Z-Wave JS sa za\u010dala" }, "manual": { "data": { @@ -24,36 +48,92 @@ } }, "on_supervisor": { + "data": { + "use_addon": "Pou\u017eite doplnok Z-Wave JS Supervisor" + }, + "description": "Chcete pou\u017ei\u0165 doplnok Z-Wave JS Supervisor?", "title": "Vyberte sp\u00f4sob pripojenia" + }, + "start_addon": { + "title": "Sp\u00fa\u0161\u0165a sa doplnok Z-Wave JS." + }, + "usb_confirm": { + "description": "Chcete nastavi\u0165 {name} pomocou doplnku Z-Wave JS?" + }, + "zeroconf_confirm": { + "description": "Chcete prida\u0165 Z-Wawe JS Server s ID {home_id} nach\u00e1dzaj\u00faci sa na adrese {url} do Home Assistant?", + "title": "Objaven\u00fd server Z-Wave JS" } } }, "device_automation": { "action_type": { - "ping": "Ping zariadenia" + "clear_lock_usercode": "Vymaza\u0165 pou\u017e\u00edvate\u013esk\u00fd k\u00f3d na {entity_name}", + "ping": "Ping zariadenia", + "refresh_value": "Obnovi\u0165 hodnoty pre {entity_name}", + "reset_meter": "Vynulova\u0165 mera\u010de na {subtype}", + "set_config_parameter": "Nastavi\u0165 hodnotu konfigura\u010dn\u00e9ho parametra {subtype}", + "set_lock_usercode": "Nastavi\u0165 pou\u017e\u00edvate\u013esk\u00fd k\u00f3d na {entity_name}", + "set_value": "Nastavte hodnotu hodnoty Z-Wave" + }, + "condition_type": { + "config_parameter": "Hodnota konfigura\u010dn\u00e9ho parametra {subtype}", + "node_status": "Stav uzla", + "value": "Aktu\u00e1lna hodnota Z-Wave hodnoty" }, "trigger_type": { + "event.notification.entry_control": "Odosla\u0165 ozn\u00e1menie o riaden\u00ed vstupu", "event.notification.notification": "Odosla\u0165 ozn\u00e1menie", - "event.value_notification.scene_activation": "Aktiv\u00e1cia sc\u00e9ny na {subtype}" + "event.value_notification.basic": "Z\u00e1kladn\u00e1 ud\u00e1los\u0165 CC na {subtype}", + "event.value_notification.central_scene": "Akcia centr\u00e1lnej sc\u00e9ny na {subtype}", + "event.value_notification.scene_activation": "Aktiv\u00e1cia sc\u00e9ny na {subtype}", + "state.node_status": "Stav uzlov zmenen\u00fd", + "zwave_js.value_updated.config_parameter": "Zmena hodnoty konfigura\u010dn\u00e9ho parametra {subtype}", + "zwave_js.value_updated.value": "Zmena hodnoty na Z-Wave JS Value" + } + }, + "issues": { + "invalid_server_version": { + "description": "Verzia Z-Wave JS Server, ktor\u00fa moment\u00e1lne pou\u017e\u00edvate, je pre t\u00fato verziu Home Assistant pr\u00edli\u0161 star\u00e1. Ak chcete tento probl\u00e9m vyrie\u0161i\u0165, aktualizujte server Z-Wave JS na najnov\u0161iu verziu.", + "title": "Potrebn\u00e1 je nov\u0161ia verzia Z-Wave JS Server" } }, "options": { "abort": { + "addon_get_discovery_info_failed": "Nepodarilo sa z\u00edska\u0165 inform\u00e1cie o zis\u0165ovan\u00ed doplnku Z-Wave JS.", + "addon_info_failed": "Nepodarilo sa z\u00edska\u0165 inform\u00e1cie o doplnku Z-Wave JS.", + "addon_install_failed": "Nepodarilo sa nain\u0161talova\u0165 doplnok Z-Wave JS.", + "addon_set_config_failed": "Nepodarilo sa nastavi\u0165 konfigur\u00e1ciu Z-Wave JS.", + "addon_start_failed": "Nepodarilo sa spusti\u0165 doplnok Z-Wave JS.", "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", - "cannot_connect": "Nepodarilo sa pripoji\u0165" + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "different_device": "Pripojen\u00e9 USB zariadenie nie je rovnak\u00e9 ako predt\u00fdm nakonfigurovan\u00e9 pre t\u00fato konfigura\u010dn\u00fa polo\u017eku. Namiesto toho vytvorte nov\u00fd konfigura\u010dn\u00fd z\u00e1znam pre nov\u00e9 zariadenie." }, "error": { "cannot_connect": "Nepodarilo sa pripoji\u0165", "invalid_ws_url": "Neplatn\u00e1 adresa URL webov\u00e9ho soketu", "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, + "progress": { + "install_addon": "Po\u010dkajte, k\u00fdm sa dokon\u010d\u00ed in\u0161tal\u00e1cia doplnku Z-Wave JS. M\u00f4\u017ee to trva\u0165 nieko\u013eko min\u00fat.", + "start_addon": "Po\u010dkajte, k\u00fdm sa dokon\u010d\u00ed spustenie doplnku Z-Wave JS. M\u00f4\u017ee to trva\u0165 nieko\u013eko sek\u00fand." + }, "step": { "configure_addon": { "data": { + "emulate_hardware": "Emulova\u0165 hardv\u00e9r", "log_level": "\u00darove\u0148 denn\u00edka", + "s0_legacy_key": "K\u013e\u00fa\u010d S0 (Legacy)", + "s2_access_control_key": "S2 K\u013e\u00fa\u010d na riadenie pr\u00edstupu", + "s2_authenticated_key": "S2 overen\u00fd k\u013e\u00fa\u010d", + "s2_unauthenticated_key": "S2 Neoveren\u00fd k\u013e\u00fa\u010d", "usb_path": "Cesta k zariadeniu USB" }, - "description": "Ak tieto polia zostan\u00fa pr\u00e1zdne, doplnok vygeneruje bezpe\u010dnostn\u00e9 k\u013e\u00fa\u010de." + "description": "Ak tieto polia zostan\u00fa pr\u00e1zdne, doplnok vygeneruje bezpe\u010dnostn\u00e9 k\u013e\u00fa\u010de.", + "title": "Zadajte konfigur\u00e1ciu doplnku Z-Wave JS" + }, + "install_addon": { + "title": "In\u0161tal\u00e1cia doplnku Z-Wave JS sa za\u010dala" }, "manual": { "data": { @@ -61,7 +141,14 @@ } }, "on_supervisor": { + "data": { + "use_addon": "Pou\u017eite doplnok Z-Wave JS Supervisor" + }, + "description": "Chcete pou\u017ei\u0165 doplnok Z-Wave JS Supervisor?", "title": "Vyberte sp\u00f4sob pripojenia" + }, + "start_addon": { + "title": "Sp\u00fa\u0161\u0165a sa doplnok Z-Wave JS." } } } diff --git a/homeassistant/components/zwave_js/translations/sv.json b/homeassistant/components/zwave_js/translations/sv.json index 448069933d9..812a59a42be 100644 --- a/homeassistant/components/zwave_js/translations/sv.json +++ b/homeassistant/components/zwave_js/translations/sv.json @@ -9,7 +9,7 @@ "already_configured": "Enheten \u00e4r redan konfigurerad", "already_in_progress": "Konfigurationsfl\u00f6det p\u00e5g\u00e5r redan", "cannot_connect": "Det gick inte att ansluta.", - "discovery_requires_supervisor": "Uppt\u00e4ckt kr\u00e4ver \u00f6vervakaren.", + "discovery_requires_supervisor": "Uppt\u00e4ckt kr\u00e4ver Supervisor.", "not_zwave_device": "Uppt\u00e4ckt enhet \u00e4r inte en Z-Wave-enhet." }, "error": { diff --git a/homeassistant/components/zwave_js/update.py b/homeassistant/components/zwave_js/update.py index 0c458d6e1a8..8432e70af35 100644 --- a/homeassistant/components/zwave_js/update.py +++ b/homeassistant/components/zwave_js/update.py @@ -246,8 +246,8 @@ class ZWaveNodeFirmwareUpdate(UpdateEntity): async def async_poll_value(self, _: bool) -> None: """Poll a value.""" LOGGER.error( - "There is no value to refresh for this entity so the zwave_js.refresh_value " - "service won't work for it" + "There is no value to refresh for this entity so the zwave_js.refresh_value" + " service won't work for it" ) async def async_added_to_hass(self) -> None: diff --git a/homeassistant/components/zwave_me/sensor.py b/homeassistant/components/zwave_me/sensor.py index ec115d60675..89048f4fec9 100644 --- a/homeassistant/components/zwave_me/sensor.py +++ b/homeassistant/components/zwave_me/sensor.py @@ -14,14 +14,14 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - ELECTRIC_CURRENT_AMPERE, - ELECTRIC_POTENTIAL_VOLT, - ENERGY_KILO_WATT_HOUR, LIGHT_LUX, PERCENTAGE, - POWER_WATT, - PRESSURE_KPA, - TEMP_CELSIUS, + UnitOfElectricCurrent, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfPower, + UnitOfPressure, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect @@ -42,7 +42,7 @@ SENSORS_MAP: dict[str, ZWaveMeSensorEntityDescription] = { "barometer": ZWaveMeSensorEntityDescription( key="barometer", device_class=SensorDeviceClass.PRESSURE, - native_unit_of_measurement=PRESSURE_KPA, + native_unit_of_measurement=UnitOfPressure.KPA, state_class=SensorStateClass.MEASUREMENT, ), "co": ZWaveMeSensorEntityDescription( @@ -72,13 +72,13 @@ SENSORS_MAP: dict[str, ZWaveMeSensorEntityDescription] = { "meterElectric_ampere": ZWaveMeSensorEntityDescription( key="meterElectric_ampere", device_class=SensorDeviceClass.CURRENT, - native_unit_of_measurement=ELECTRIC_CURRENT_AMPERE, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, state_class=SensorStateClass.MEASUREMENT, ), "meterElectric_kilowatt_hour": ZWaveMeSensorEntityDescription( key="meterElectric_kilowatt_hour", device_class=SensorDeviceClass.ENERGY, - native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, state_class=SensorStateClass.TOTAL_INCREASING, ), "meterElectric_power_factor": ZWaveMeSensorEntityDescription( @@ -91,19 +91,19 @@ SENSORS_MAP: dict[str, ZWaveMeSensorEntityDescription] = { "meterElectric_voltage": ZWaveMeSensorEntityDescription( key="meterElectric_voltage", device_class=SensorDeviceClass.VOLTAGE, - native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT, + native_unit_of_measurement=UnitOfElectricPotential.VOLT, state_class=SensorStateClass.MEASUREMENT, ), "meterElectric_watt": ZWaveMeSensorEntityDescription( key="meterElectric_watt", device_class=SensorDeviceClass.POWER, - native_unit_of_measurement=POWER_WATT, + native_unit_of_measurement=UnitOfPower.WATT, state_class=SensorStateClass.MEASUREMENT, ), "temperature": ZWaveMeSensorEntityDescription( key="temperature", device_class=SensorDeviceClass.TEMPERATURE, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, state_class=SensorStateClass.MEASUREMENT, ), "generic": ZWaveMeSensorEntityDescription( diff --git a/homeassistant/components/zwave_me/translations/ko.json b/homeassistant/components/zwave_me/translations/ko.json new file mode 100644 index 00000000000..edc26b5cda7 --- /dev/null +++ b/homeassistant/components/zwave_me/translations/ko.json @@ -0,0 +1,15 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "token": "API \ud1a0\ud070", + "url": "URL \uc8fc\uc18c" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zwave_me/translations/sk.json b/homeassistant/components/zwave_me/translations/sk.json index badd53ca8a7..d9142cb6bce 100644 --- a/homeassistant/components/zwave_me/translations/sk.json +++ b/homeassistant/components/zwave_me/translations/sk.json @@ -1,14 +1,19 @@ { "config": { "abort": { - "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" + "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9", + "no_valid_uuid_set": "Nie je nastaven\u00e9 \u017eiadne platn\u00e9 UUID" + }, + "error": { + "no_valid_uuid_set": "Nie je nastaven\u00e9 \u017eiadne platn\u00e9 UUID" }, "step": { "user": { "data": { "token": "API token", "url": "URL" - } + }, + "description": "Zadajte IP adresu s portom a pr\u00edstupov\u00fdm tokenom servera Z-Way. Ak chcete z\u00edska\u0165 token, prejdite do pou\u017e\u00edvate\u013esk\u00e9ho rozhrania Z-Way Smart Home UI > Menu > Nastavenia > Pou\u017e\u00edvatelia > Spr\u00e1vca > API token. \n\n Pr\u00edklad pripojenia k Z-Way v lok\u00e1lnej sieti:\n Webov\u00e1 adresa: {local_url}\n Token: {local_token} \n\n Pr\u00edklad pripojenia k Z-Way cez vzdialen\u00fd pr\u00edstup find.z-wave.me:\n Webov\u00e1 adresa: {find_url}\n Token: {find_token} \n\n Pr\u00edklad pripojenia k Z-Way so statickou verejnou IP adresou:\n Webov\u00e1 adresa: {remote_url}\n Token: {local_token} \n\n Pri prip\u00e1jan\u00ed cez find.z-wave.me mus\u00edte pou\u017ei\u0165 token s glob\u00e1lnym rozsahom (na tento \u00fa\u010del sa prihl\u00e1ste do Z-Way cez find.z-wave.me)." } } } diff --git a/homeassistant/config.py b/homeassistant/config.py index e203c45f795..a4205461b30 100644 --- a/homeassistant/config.py +++ b/homeassistant/config.py @@ -277,8 +277,10 @@ CORE_CONFIG_SCHEMA = vol.All( { CONF_TYPE: vol.NotIn( ["insecure_example"], - "The insecure_example auth provider" - " is for testing only.", + ( + "The insecure_example auth provider" + " is for testing only." + ), ) } ) @@ -900,7 +902,10 @@ async def async_process_component_config( # noqa: C901 continue except Exception: # pylint: disable=broad-except _LOGGER.exception( - "Unknown error validating %s platform config with %s component platform schema", + ( + "Unknown error validating %s platform config with %s component" + " platform schema" + ), p_name, domain, ) @@ -940,7 +945,10 @@ async def async_process_component_config( # noqa: C901 continue except Exception: # pylint: disable=broad-except _LOGGER.exception( - "Unknown error validating config for %s platform for %s component with PLATFORM_SCHEMA", + ( + "Unknown error validating config for %s platform for %s" + " component with PLATFORM_SCHEMA" + ), p_name, domain, ) diff --git a/homeassistant/config_entries.py b/homeassistant/config_entries.py index 4830ec602a7..f2e2be97e42 100644 --- a/homeassistant/config_entries.py +++ b/homeassistant/config_entries.py @@ -9,6 +9,7 @@ from copy import deepcopy from enum import Enum import functools import logging +from random import randint from types import MappingProxyType, MethodType from typing import TYPE_CHECKING, Any, Optional, TypeVar, cast import weakref @@ -27,7 +28,11 @@ from .exceptions import ( ) from .helpers import device_registry, entity_registry, storage from .helpers.dispatcher import async_dispatcher_send -from .helpers.event import async_call_later +from .helpers.event import ( + RANDOM_MICROSECOND_MAX, + RANDOM_MICROSECOND_MIN, + async_call_later, +) from .helpers.frame import report from .helpers.typing import UNDEFINED, ConfigType, DiscoveryInfoType, UndefinedType from .setup import DATA_SETUP_DONE, async_process_deps_reqs, async_setup_component @@ -278,9 +283,11 @@ class ConfigEntry: disabled_by, ConfigEntryDisabler ): report( # type: ignore[unreachable] - "uses str for config entry disabled_by. This is deprecated and will " - "stop working in Home Assistant 2022.3, it should be updated to use " - "ConfigEntryDisabler instead", + ( + "uses str for config entry disabled_by. This is deprecated and will" + " stop working in Home Assistant 2022.3, it should be updated to" + " use ConfigEntryDisabler instead" + ), error_if_core=False, ) disabled_by = ConfigEntryDisabler(disabled_by) @@ -353,7 +360,10 @@ class ConfigEntry: integration.get_platform("config_flow") except ImportError as err: _LOGGER.error( - "Error importing platform config_flow from integration %s to set up %s configuration entry: %s", + ( + "Error importing platform config_flow from integration %s to" + " set up %s configuration entry: %s" + ), integration.domain, self.domain, err, @@ -404,20 +414,28 @@ class ConfigEntry: result = False except ConfigEntryNotReady as ex: self.async_set_state(hass, ConfigEntryState.SETUP_RETRY, str(ex) or None) - wait_time = 2 ** min(tries, 4) * 5 + wait_time = 2 ** min(tries, 4) * 5 + ( + randint(RANDOM_MICROSECOND_MIN, RANDOM_MICROSECOND_MAX) / 1000000 + ) tries += 1 message = str(ex) ready_message = f"ready yet: {message}" if message else "ready yet" if tries == 1: _LOGGER.warning( - "Config entry '%s' for %s integration not %s; Retrying in background", + ( + "Config entry '%s' for %s integration not %s; Retrying in" + " background" + ), self.title, self.domain, ready_message, ) else: _LOGGER.debug( - "Config entry '%s' for %s integration not %s; Retrying in %d seconds", + ( + "Config entry '%s' for %s integration not %s; Retrying in %d" + " seconds" + ), self.title, self.domain, ready_message, @@ -813,7 +831,7 @@ class ConfigEntriesFlowManager(data_entry_flow.FlowManager): return result async def async_create_flow( - self, handler_key: Any, *, context: dict | None = None, data: Any = None + self, handler_key: str, *, context: dict | None = None, data: Any = None ) -> ConfigFlow: """Create a flow for specified handler. @@ -1065,8 +1083,9 @@ class ConfigEntries: if entry.state is not ConfigEntryState.NOT_LOADED: raise OperationNotAllowed( - f"The config entry {entry.title} ({entry.domain}) with entry_id {entry.entry_id}" - f" cannot be setup because is already loaded in the {entry.state} state" + f"The config entry {entry.title} ({entry.domain}) with entry_id" + f" {entry.entry_id} cannot be setup because is already loaded in the" + f" {entry.state} state" ) # Setup Component if not set up yet @@ -1090,8 +1109,9 @@ class ConfigEntries: if not entry.state.recoverable: raise OperationNotAllowed( - f"The config entry {entry.title} ({entry.domain}) with entry_id " - f"{entry.entry_id} cannot be unloaded because it is not in a recoverable state ({entry.state})" + f"The config entry {entry.title} ({entry.domain}) with entry_id" + f" {entry.entry_id} cannot be unloaded because it is not in a" + f" recoverable state ({entry.state})" ) return await entry.async_unload(self.hass) @@ -1126,9 +1146,11 @@ class ConfigEntries: disabled_by, ConfigEntryDisabler ): report( # type: ignore[unreachable] - "uses str for config entry disabled_by. This is deprecated and will " - "stop working in Home Assistant 2022.3, it should be updated to use " - "ConfigEntryDisabler instead", + ( + "uses str for config entry disabled_by. This is deprecated and will" + " stop working in Home Assistant 2022.3, it should be updated to" + " use ConfigEntryDisabler instead" + ), error_if_core=False, ) disabled_by = ConfigEntryDisabler(disabled_by) @@ -1225,8 +1247,10 @@ class ConfigEntries: ) -> None: """Forward the setup of an entry to platforms.""" report( - "called async_setup_platforms instead of awaiting async_forward_entry_setups; " - "this will fail in version 2022.12", + ( + "called async_setup_platforms instead of awaiting" + " async_forward_entry_setups; this will fail in version 2022.12" + ), # Raise this to warning once all core integrations have been migrated level=logging.DEBUG, error_if_core=False, @@ -1620,7 +1644,7 @@ class ConfigFlow(data_entry_flow.FlowHandler): return await self._async_step_discovery_without_unique_id() @callback - def async_create_entry( + def async_create_entry( # type: ignore[override] self, *, title: str, @@ -1647,7 +1671,7 @@ class OptionsFlowManager(data_entry_flow.FlowManager): async def async_create_flow( self, - handler_key: Any, + handler_key: str, *, context: dict[str, Any] | None = None, data: dict[str, Any] | None = None, @@ -1777,7 +1801,10 @@ class EntityRegistryDisabledHandler: self.changed = set() _LOGGER.info( - "Reloading configuration entries because disabled_by changed in entity registry: %s", + ( + "Reloading configuration entries because disabled_by changed in entity" + " registry: %s" + ), ", ".join(to_reload), ) diff --git a/homeassistant/const.py b/homeassistant/const.py index a2d2a23bf6e..a606656f579 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -6,9 +6,9 @@ from typing import Final from .backports.enum import StrEnum APPLICATION_NAME: Final = "HomeAssistant" -MAJOR_VERSION: Final = 2022 -MINOR_VERSION: Final = 12 -PATCH_VERSION: Final = "9" +MAJOR_VERSION: Final = 2023 +MINOR_VERSION: Final = 1 +PATCH_VERSION: Final = "0" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0) @@ -256,6 +256,7 @@ CONF_UNIT_SYSTEM: Final = "unit_system" CONF_UNTIL: Final = "until" CONF_URL: Final = "url" CONF_USERNAME: Final = "username" +CONF_UUID: Final = "uuid" CONF_VALUE_TEMPLATE: Final = "value_template" CONF_VARIABLES: Final = "variables" CONF_VERIFY_SSL: Final = "verify_ssl" @@ -477,9 +478,17 @@ ATTR_TEMPERATURE: Final = "temperature" # Persons attribute ATTR_PERSONS: Final = "persons" + # #### UNITS OF MEASUREMENT #### # Apparent power units +class UnitOfApparentPower(StrEnum): + """Apparent power units.""" + + VOLT_AMPERE = "VA" + + POWER_VOLT_AMPERE: Final = "VA" +"""Deprecated: please use UnitOfApparentPower.VOLT_AMPERE.""" # Power units @@ -519,13 +528,33 @@ ENERGY_MEGA_WATT_HOUR: Final = "MWh" ENERGY_WATT_HOUR: Final = "Wh" """Deprecated: please use UnitOfEnergy.WATT_HOUR.""" + # Electric_current units +class UnitOfElectricCurrent(StrEnum): + """Electric current units.""" + + MILLIAMPERE = "mA" + AMPERE = "A" + + ELECTRIC_CURRENT_MILLIAMPERE: Final = "mA" +"""Deprecated: please use UnitOfElectricCurrent.MILLIAMPERE.""" ELECTRIC_CURRENT_AMPERE: Final = "A" +"""Deprecated: please use UnitOfElectricCurrent.AMPERE.""" + # Electric_potential units +class UnitOfElectricPotential(StrEnum): + """Electric potential units.""" + + MILLIVOLT = "mV" + VOLT = "V" + + ELECTRIC_POTENTIAL_MILLIVOLT: Final = "mV" +"""Deprecated: please use UnitOfElectricPotential.MILLIVOLT.""" ELECTRIC_POTENTIAL_VOLT: Final = "V" +"""Deprecated: please use UnitOfElectricPotential.VOLT.""" # Degree units DEGREE: Final = "°" @@ -552,16 +581,40 @@ TEMP_FAHRENHEIT: Final = "°F" TEMP_KELVIN: Final = "K" """Deprecated: please use UnitOfTemperature.KELVIN""" + # Time units +class UnitOfTime(StrEnum): + """Time units.""" + + MICROSECONDS = "μs" + MILLISECONDS = "ms" + SECONDS = "s" + MINUTES = "min" + HOURS = "h" + DAYS = "d" + WEEKS = "w" + MONTHS = "m" + YEARS = "y" + + TIME_MICROSECONDS: Final = "μs" +"""Deprecated: please use UnitOfTime.MICROSECONDS.""" TIME_MILLISECONDS: Final = "ms" +"""Deprecated: please use UnitOfTime.MILLISECONDS.""" TIME_SECONDS: Final = "s" +"""Deprecated: please use UnitOfTime.SECONDS.""" TIME_MINUTES: Final = "min" +"""Deprecated: please use UnitOfTime.MINUTES.""" TIME_HOURS: Final = "h" +"""Deprecated: please use UnitOfTime.HOURS.""" TIME_DAYS: Final = "d" +"""Deprecated: please use UnitOfTime.DAYS.""" TIME_WEEKS: Final = "w" +"""Deprecated: please use UnitOfTime.WEEKS.""" TIME_MONTHS: Final = "m" +"""Deprecated: please use UnitOfTime.MONTHS.""" TIME_YEARS: Final = "y" +"""Deprecated: please use UnitOfTime.YEARS.""" # Length units @@ -595,11 +648,25 @@ LENGTH_YARD: Final = "yd" LENGTH_MILES: Final = "mi" """Deprecated: please use UnitOfLength.MILES.""" + # Frequency units +class UnitOfFrequency(StrEnum): + """Frequency units.""" + + HERTZ = "Hz" + KILOHERTZ = "kHz" + MEGAHERTZ = "MHz" + GIGAHERTZ = "GHz" + + FREQUENCY_HERTZ: Final = "Hz" +"""Deprecated: please use UnitOfFrequency.HERTZ""" FREQUENCY_KILOHERTZ: Final = "kHz" +"""Deprecated: please use UnitOfFrequency.KILOHERTZ""" FREQUENCY_MEGAHERTZ: Final = "MHz" +"""Deprecated: please use UnitOfFrequency.MEGAHERTZ""" FREQUENCY_GIGAHERTZ: Final = "GHz" +"""Deprecated: please use UnitOfFrequency.GIGAHERTZ""" # Pressure units @@ -636,9 +703,19 @@ PRESSURE_INHG: Final = "inHg" PRESSURE_PSI: Final = "psi" """Deprecated: please use UnitOfPressure.PSI""" + # Sound pressure units +class UnitOfSoundPressure(StrEnum): + """Sound pressure units.""" + + DECIBEL = "dB" + WEIGHTED_DECIBEL_A = "dBA" + + SOUND_PRESSURE_DB: Final = "dB" +"""Deprecated: please use UnitOfSoundPressure.DECIBEL""" SOUND_PRESSURE_WEIGHTED_DBA: Final = "dBa" +"""Deprecated: please use UnitOfSoundPressure.WEIGHTED_DECIBEL_A""" # Volume units @@ -646,6 +723,7 @@ class UnitOfVolume(StrEnum): """Volume units.""" CUBIC_FEET = "ft³" + CENTUM_CUBIC_FEET = "CCF" CUBIC_METERS = "m³" LITERS = "L" MILLILITERS = "mL" @@ -673,9 +751,19 @@ VOLUME_GALLONS: Final = "gal" VOLUME_FLUID_OUNCE: Final = "fl. oz." """Deprecated: please use UnitOfVolume.FLUID_OUNCES""" + # Volume Flow Rate units +class UnitOfVolumeFlowRate(StrEnum): + """Volume flow rate units.""" + + CUBIC_METERS_PER_HOUR = "m³/h" + CUBIC_FEET_PER_MINUTE = "ft³/m" + + VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR: Final = "m³/h" +"""Deprecated: please use UnitOfVolumeFlowRate.CUBIC_METERS_PER_HOUR""" VOLUME_FLOW_RATE_CUBIC_FEET_PER_MINUTE: Final = "ft³/m" +"""Deprecated: please use UnitOfVolumeFlowRate.CUBIC_FEET_PER_MINUTE""" # Area units AREA_SQUARE_METERS: Final = "m²" @@ -691,6 +779,7 @@ class UnitOfMass(StrEnum): MICROGRAMS = "µg" OUNCES = "oz" POUNDS = "lb" + STONES = "st" MASS_GRAMS: Final = "g" @@ -721,9 +810,20 @@ PERCENTAGE: Final = "%" # Rotational speed units REVOLUTIONS_PER_MINUTE: Final = "rpm" + +# Irradiance units +class UnitOfIrradiance(StrEnum): + """Irradiance units.""" + + WATTS_PER_SQUARE_METER = "W/m²" + BTUS_PER_HOUR_SQUARE_FOOT = "BTU/(h⋅ft²)" + + # Irradiation units IRRADIATION_WATTS_PER_SQUARE_METER: Final = "W/m²" +"""Deprecated: please use UnitOfIrradiance.WATTS_PER_SQUARE_METER""" IRRADIATION_BTUS_PER_HOUR_SQUARE_FOOT: Final = "BTU/(h×ft²)" +"""Deprecated: please use UnitOfIrradiance.BTUS_PER_HOUR_SQUARE_FOOT""" class UnitOfVolumetricFlux(StrEnum): @@ -734,16 +834,16 @@ class UnitOfVolumetricFlux(StrEnum): """ INCHES_PER_DAY = "in/d" - """Derived from in³/(in².d)""" + """Derived from in³/(in²⋅d)""" INCHES_PER_HOUR = "in/h" - """Derived from in³/(in².h)""" + """Derived from in³/(in²⋅h)""" MILLIMETERS_PER_DAY = "mm/d" - """Derived from mm³/(mm².d)""" + """Derived from mm³/(mm²⋅d)""" MILLIMETERS_PER_HOUR = "mm/h" - """Derived from mm³/(mm².h)""" + """Derived from mm³/(mm²⋅h)""" class UnitOfPrecipitationDepth(StrEnum): @@ -759,6 +859,9 @@ class UnitOfPrecipitationDepth(StrEnum): MILLIMETERS = "mm" """Derived from mm³/mm²""" + CENTIMETERS = "cm" + """Derived from cm³/cm²""" + # Precipitation units PRECIPITATION_INCHES: Final = "in" @@ -815,41 +918,117 @@ SPEED_INCHES_PER_HOUR: Final = "in/h" SIGNAL_STRENGTH_DECIBELS: Final = "dB" SIGNAL_STRENGTH_DECIBELS_MILLIWATT: Final = "dBm" + # Data units +class UnitOfInformation(StrEnum): + """Information units.""" + + BITS = "bit" + KILOBITS = "kbit" + MEGABITS = "Mbit" + GIGABITS = "Gbit" + BYTES = "B" + KILOBYTES = "kB" + MEGABYTES = "MB" + GIGABYTES = "GB" + TERABYTES = "TB" + PETABYTES = "PB" + EXABYTES = "EB" + ZETTABYTES = "ZB" + YOTTABYTES = "YB" + KIBIBYTES = "KiB" + MEBIBYTES = "MiB" + GIBIBYTES = "GiB" + TEBIBYTES = "TiB" + PEBIBYTES = "PiB" + EXBIBYTES = "EiB" + ZEBIBYTES = "ZiB" + YOBIBYTES = "YiB" + + DATA_BITS: Final = "bit" +"""Deprecated: please use UnitOfInformation.BITS""" DATA_KILOBITS: Final = "kbit" +"""Deprecated: please use UnitOfInformation.KILOBITS""" DATA_MEGABITS: Final = "Mbit" +"""Deprecated: please use UnitOfInformation.MEGABITS""" DATA_GIGABITS: Final = "Gbit" +"""Deprecated: please use UnitOfInformation.GIGABITS""" DATA_BYTES: Final = "B" +"""Deprecated: please use UnitOfInformation.BYTES""" DATA_KILOBYTES: Final = "kB" +"""Deprecated: please use UnitOfInformation.KILOBYTES""" DATA_MEGABYTES: Final = "MB" +"""Deprecated: please use UnitOfInformation.MEGABYTES""" DATA_GIGABYTES: Final = "GB" +"""Deprecated: please use UnitOfInformation.GIGABYTES""" DATA_TERABYTES: Final = "TB" +"""Deprecated: please use UnitOfInformation.TERABYTES""" DATA_PETABYTES: Final = "PB" +"""Deprecated: please use UnitOfInformation.PETABYTES""" DATA_EXABYTES: Final = "EB" +"""Deprecated: please use UnitOfInformation.EXABYTES""" DATA_ZETTABYTES: Final = "ZB" +"""Deprecated: please use UnitOfInformation.ZETTABYTES""" DATA_YOTTABYTES: Final = "YB" +"""Deprecated: please use UnitOfInformation.YOTTABYTES""" DATA_KIBIBYTES: Final = "KiB" +"""Deprecated: please use UnitOfInformation.KIBIBYTES""" DATA_MEBIBYTES: Final = "MiB" +"""Deprecated: please use UnitOfInformation.MEBIBYTES""" DATA_GIBIBYTES: Final = "GiB" +"""Deprecated: please use UnitOfInformation.GIBIBYTES""" DATA_TEBIBYTES: Final = "TiB" +"""Deprecated: please use UnitOfInformation.TEBIBYTES""" DATA_PEBIBYTES: Final = "PiB" +"""Deprecated: please use UnitOfInformation.PEBIBYTES""" DATA_EXBIBYTES: Final = "EiB" +"""Deprecated: please use UnitOfInformation.EXBIBYTES""" DATA_ZEBIBYTES: Final = "ZiB" +"""Deprecated: please use UnitOfInformation.ZEBIBYTES""" DATA_YOBIBYTES: Final = "YiB" +"""Deprecated: please use UnitOfInformation.YOBIBYTES""" + # Data_rate units +class UnitOfDataRate(StrEnum): + """Data rate units.""" + + BITS_PER_SECOND = "bit/s" + KILOBITS_PER_SECOND = "kbit/s" + MEGABITS_PER_SECOND = "Mbit/s" + GIGABITS_PER_SECOND = "Gbit/s" + BYTES_PER_SECOND = "B/s" + KILOBYTES_PER_SECOND = "kB/s" + MEGABYTES_PER_SECOND = "MB/s" + GIGABYTES_PER_SECOND = "GB/s" + KIBIBYTES_PER_SECOND = "KiB/s" + MEBIBYTES_PER_SECOND = "MiB/s" + GIBIBYTES_PER_SECOND = "GiB/s" + + DATA_RATE_BITS_PER_SECOND: Final = "bit/s" +"""Deprecated: please use UnitOfDataRate.BITS_PER_SECOND""" DATA_RATE_KILOBITS_PER_SECOND: Final = "kbit/s" +"""Deprecated: please use UnitOfDataRate.KILOBITS_PER_SECOND""" DATA_RATE_MEGABITS_PER_SECOND: Final = "Mbit/s" +"""Deprecated: please use UnitOfDataRate.MEGABITS_PER_SECOND""" DATA_RATE_GIGABITS_PER_SECOND: Final = "Gbit/s" +"""Deprecated: please use UnitOfDataRate.GIGABITS_PER_SECOND""" DATA_RATE_BYTES_PER_SECOND: Final = "B/s" +"""Deprecated: please use UnitOfDataRate.BYTES_PER_SECOND""" DATA_RATE_KILOBYTES_PER_SECOND: Final = "kB/s" +"""Deprecated: please use UnitOfDataRate.KILOBYTES_PER_SECOND""" DATA_RATE_MEGABYTES_PER_SECOND: Final = "MB/s" +"""Deprecated: please use UnitOfDataRate.MEGABYTES_PER_SECOND""" DATA_RATE_GIGABYTES_PER_SECOND: Final = "GB/s" +"""Deprecated: please use UnitOfDataRate.GIGABYTES_PER_SECOND""" DATA_RATE_KIBIBYTES_PER_SECOND: Final = "KiB/s" +"""Deprecated: please use UnitOfDataRate.KIBIBYTES_PER_SECOND""" DATA_RATE_MEBIBYTES_PER_SECOND: Final = "MiB/s" +"""Deprecated: please use UnitOfDataRate.MEBIBYTES_PER_SECOND""" DATA_RATE_GIBIBYTES_PER_SECOND: Final = "GiB/s" +"""Deprecated: please use UnitOfDataRate.GIBIBYTES_PER_SECOND""" # #### SERVICES #### diff --git a/homeassistant/core.py b/homeassistant/core.py index 82ee5216f4f..fee2e482e91 100644 --- a/homeassistant/core.py +++ b/homeassistant/core.py @@ -357,9 +357,12 @@ class HomeAssistant: await self.async_block_till_done() except asyncio.TimeoutError: _LOGGER.warning( - "Something is blocking Home Assistant from wrapping up the " - "start up phase. We're going to continue anyway. Please " - "report the following info at https://github.com/home-assistant/core/issues: %s", + ( + "Something is blocking Home Assistant from wrapping up the start up" + " phase. We're going to continue anyway. Please report the" + " following info at" + " https://github.com/home-assistant/core/issues: %s" + ), ", ".join(self.config.components), ) @@ -705,7 +708,8 @@ class HomeAssistant: await self.async_block_till_done() except asyncio.TimeoutError: _LOGGER.warning( - "Timed out waiting for shutdown stage 1 to complete, the shutdown will continue" + "Timed out waiting for shutdown stage 1 to complete, the shutdown will" + " continue" ) # stage 2 @@ -716,7 +720,8 @@ class HomeAssistant: await self.async_block_till_done() except asyncio.TimeoutError: _LOGGER.warning( - "Timed out waiting for shutdown stage 2 to complete, the shutdown will continue" + "Timed out waiting for shutdown stage 2 to complete, the shutdown will" + " continue" ) # stage 3 @@ -735,7 +740,8 @@ class HomeAssistant: await self.async_block_till_done() except asyncio.TimeoutError: _LOGGER.warning( - "Timed out waiting for shutdown stage 3 to complete, the shutdown will continue" + "Timed out waiting for shutdown stage 3 to complete, the shutdown will" + " continue" ) self.exit_code = exit_code @@ -825,7 +831,10 @@ class Event: def __repr__(self) -> str: """Return the representation.""" if self.data: - return f"" + return ( + f"" + ) return f"" @@ -1419,7 +1428,8 @@ class StateMachine: entity_id = entity_id.lower() if entity_id in self._states or entity_id in self._reservations: raise HomeAssistantError( - "async_reserve must not be called once the state is in the state machine." + "async_reserve must not be called once the state is in the state" + " machine." ) self._reservations.add(entity_id) diff --git a/homeassistant/data_entry_flow.py b/homeassistant/data_entry_flow.py index 866f1a5db2f..a98c616ffbc 100644 --- a/homeassistant/data_entry_flow.py +++ b/homeassistant/data_entry_flow.py @@ -84,27 +84,27 @@ class AbortFlow(FlowError): class FlowResult(TypedDict, total=False): """Typed result dict.""" - version: int - type: FlowResultType + context: dict[str, Any] + data_schema: vol.Schema | None + data: Mapping[str, Any] + description_placeholders: Mapping[str, str | None] | None + description: str | None + errors: dict[str, str] | None + extra: str flow_id: str handler: str - title: str - data: Mapping[str, Any] - step_id: str - data_schema: vol.Schema | None - extra: str - required: bool - errors: dict[str, str] | None - description: str | None - description_placeholders: Mapping[str, str | None] | None - progress_action: str - url: str - reason: str - context: dict[str, Any] - result: Any last_step: bool | None - options: Mapping[str, Any] menu_options: list[str] | dict[str, str] + options: Mapping[str, Any] + progress_action: str + reason: str + required: bool + result: Any + step_id: str + title: str + type: FlowResultType + url: str + version: int @callback @@ -112,16 +112,19 @@ def _async_flow_handler_to_flow_result( flows: Iterable[FlowHandler], include_uninitialized: bool ) -> list[FlowResult]: """Convert a list of FlowHandler to a partial FlowResult that can be serialized.""" - return [ - FlowResult( + results = [] + for flow in flows: + if not include_uninitialized and flow.cur_step is None: + continue + result = FlowResult( flow_id=flow.flow_id, handler=flow.handler, context=flow.context, - step_id=flow.cur_step["step_id"] if flow.cur_step else None, ) - for flow in flows - if include_uninitialized or flow.cur_step is not None - ] + if flow.cur_step: + result["step_id"] = flow.cur_step["step_id"] + results.append(result) + return results class FlowManager(abc.ABC): @@ -148,7 +151,7 @@ class FlowManager(abc.ABC): @abc.abstractmethod async def async_create_flow( self, - handler_key: Any, + handler_key: str, *, context: dict[str, Any] | None = None, data: dict[str, Any] | None = None, @@ -269,8 +272,10 @@ class FlowManager(abc.ABC): cur_step = flow.cur_step assert cur_step is not None - if cur_step.get("data_schema") is not None and user_input is not None: - user_input = cur_step["data_schema"](user_input) + if ( + data_schema := cur_step.get("data_schema") + ) is not None and user_input is not None: + user_input = data_schema(user_input) # Handle a menu navigation choice if cur_step["type"] == FlowResultType.MENU and user_input: @@ -303,7 +308,8 @@ class FlowManager(abc.ABC): FlowResultType.SHOW_PROGRESS_DONE, ): raise ValueError( - "Show progress can only transition to show progress or show progress done." + "Show progress can only transition to show progress or show" + " progress done." ) # If the result has changed from last result, fire event to update @@ -348,7 +354,7 @@ class FlowManager(abc.ABC): async def _async_handle_step( self, - flow: Any, + flow: FlowHandler, step_id: str, user_input: dict | BaseServiceInfo | None, step_done: asyncio.Future | None = None, @@ -381,8 +387,10 @@ class FlowManager(abc.ABC): if not isinstance(result["type"], FlowResultType): result["type"] = FlowResultType(result["type"]) # type: ignore[unreachable] report( - "does not use FlowResultType enum for data entry flow result type. " - "This is deprecated and will stop working in Home Assistant 2022.9", + ( + "does not use FlowResultType enum for data entry flow result type. " + "This is deprecated and will stop working in Home Assistant 2022.9" + ), error_if_core=False, ) @@ -415,7 +423,7 @@ class FlowHandler: """Handle the configuration flow of a component.""" # Set by flow manager - cur_step: dict[str, Any] | None = None + cur_step: FlowResult | None = None # While not purely typed, it makes typehinting more useful for us # and removes the need for constant None checks or asserts. @@ -498,23 +506,25 @@ class FlowHandler: def async_create_entry( self, *, - title: str, + title: str | None = None, data: Mapping[str, Any], description: str | None = None, description_placeholders: Mapping[str, str] | None = None, ) -> FlowResult: """Finish config flow and create a config entry.""" - return FlowResult( + flow_result = FlowResult( version=self.VERSION, type=FlowResultType.CREATE_ENTRY, flow_id=self.flow_id, handler=self.handler, - title=title, data=data, description=description, description_placeholders=description_placeholders, context=self.context, ) + if title is not None: + flow_result["title"] = title + return flow_result @callback def async_abort( diff --git a/homeassistant/exceptions.py b/homeassistant/exceptions.py index 77ac2938cf2..953df61e840 100644 --- a/homeassistant/exceptions.py +++ b/homeassistant/exceptions.py @@ -2,10 +2,9 @@ from __future__ import annotations from collections.abc import Generator, Sequence +from dataclasses import dataclass from typing import TYPE_CHECKING -import attr - if TYPE_CHECKING: from .core import Context @@ -33,12 +32,11 @@ class TemplateError(HomeAssistantError): super().__init__(f"{exception.__class__.__name__}: {exception}") -@attr.s +@dataclass class ConditionError(HomeAssistantError): """Error during condition evaluation.""" - # The type of the failed condition, such as 'and' or 'numeric_state' - type: str = attr.ib() + type: str @staticmethod def _indent(indent: int, message: str) -> str: @@ -54,28 +52,28 @@ class ConditionError(HomeAssistantError): return "\n".join(list(self.output(indent=0))) -@attr.s +@dataclass class ConditionErrorMessage(ConditionError): """Condition error message.""" # A message describing this error - message: str = attr.ib() + message: str def output(self, indent: int) -> Generator[str, None, None]: """Yield an indented representation.""" yield self._indent(indent, f"In '{self.type}' condition: {self.message}") -@attr.s +@dataclass class ConditionErrorIndex(ConditionError): """Condition error with index.""" # The zero-based index of the failed condition, for conditions with multiple parts - index: int = attr.ib() + index: int # The total number of parts in this condition, including non-failed parts - total: int = attr.ib() + total: int # The error that this error wraps - error: ConditionError = attr.ib() + error: ConditionError def output(self, indent: int) -> Generator[str, None, None]: """Yield an indented representation.""" @@ -89,12 +87,12 @@ class ConditionErrorIndex(ConditionError): yield from self.error.output(indent + 1) -@attr.s +@dataclass class ConditionErrorContainer(ConditionError): """Condition error with subconditions.""" # List of ConditionErrors that this error wraps - errors: Sequence[ConditionError] = attr.ib() + errors: Sequence[ConditionError] def output(self, indent: int) -> Generator[str, None, None]: """Yield an indented representation.""" diff --git a/homeassistant/generated/application_credentials.py b/homeassistant/generated/application_credentials.py index 31e73418c5e..87813c20189 100644 --- a/homeassistant/generated/application_credentials.py +++ b/homeassistant/generated/application_credentials.py @@ -6,6 +6,7 @@ To update, run python3 -m script.hassfest APPLICATION_CREDENTIALS = [ "geocaching", "google", + "google_assistant_sdk", "google_sheets", "home_connect", "lametric", diff --git a/homeassistant/generated/bluetooth.py b/homeassistant/generated/bluetooth.py index 42bea46086e..dc50434f63f 100644 --- a/homeassistant/generated/bluetooth.py +++ b/homeassistant/generated/bluetooth.py @@ -265,10 +265,12 @@ BLUETOOTH: list[dict[str, bool | str | int | list[int]]] = [ "service_data_uuid": "0000fdcd-0000-1000-8000-00805f9b34fb", }, { + "connectable": False, "domain": "ruuvitag_ble", "manufacturer_id": 1177, }, { + "connectable": False, "domain": "ruuvitag_ble", "local_name": "Ruuvi *", }, @@ -332,6 +334,21 @@ BLUETOOTH: list[dict[str, bool | str | int | list[int]]] = [ "domain": "switchbot", "service_uuid": "cba20d00-224d-11e6-9fb8-0002a5d5c51b", }, + { + "connectable": False, + "domain": "switchbot", + "manufacturer_id": 2409, + }, + { + "connectable": True, + "domain": "switchbot", + "manufacturer_id": 89, + }, + { + "connectable": True, + "domain": "switchbot", + "manufacturer_id": 741, + }, { "connectable": False, "domain": "thermobeacon", @@ -368,6 +385,15 @@ BLUETOOTH: list[dict[str, bool | str | int | list[int]]] = [ "manufacturer_id": 24, "service_uuid": "0000fff0-0000-1000-8000-00805f9b34fb", }, + { + "connectable": False, + "domain": "thermobeacon", + "manufacturer_data_start": [ + 0, + ], + "manufacturer_id": 27, + "service_uuid": "0000fff0-0000-1000-8000-00805f9b34fb", + }, { "connectable": False, "domain": "thermobeacon", diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py index 5875c9021f6..980f7f1897e 100644 --- a/homeassistant/generated/config_flows.py +++ b/homeassistant/generated/config_flows.py @@ -30,6 +30,7 @@ FLOWS = { "airthings_ble", "airtouch4", "airvisual", + "airvisual_pro", "airzone", "aladdin_connect", "alarmdecoder", @@ -154,6 +155,7 @@ FLOWS = { "gogogate2", "goodwe", "google", + "google_assistant_sdk", "google_sheets", "google_travel_time", "govee_ble", @@ -318,6 +320,7 @@ FLOWS = { "prusalink", "ps4", "pure_energie", + "purpleair", "pushbullet", "pushover", "pvoutput", @@ -333,6 +336,7 @@ FLOWS = { "rdw", "recollect_waste", "renault", + "reolink", "rfxtrx", "rhasspy", "ridwell", diff --git a/homeassistant/generated/dhcp.py b/homeassistant/generated/dhcp.py index dd156b29f22..f04fb56e32a 100644 --- a/homeassistant/generated/dhcp.py +++ b/homeassistant/generated/dhcp.py @@ -10,6 +10,11 @@ DHCP: list[dict[str, str | bool]] = [ "domain": "airzone", "macaddress": "E84F25*", }, + { + "domain": "august", + "hostname": "yale-connect-plus", + "macaddress": "00177A*", + }, { "domain": "august", "hostname": "connect", diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index 02068ecafa5..40d6edc0d49 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -120,9 +120,20 @@ }, "airvisual": { "name": "AirVisual", - "integration_type": "device", - "config_flow": true, - "iot_class": "cloud_polling" + "integrations": { + "airvisual": { + "integration_type": "service", + "config_flow": true, + "iot_class": "cloud_polling", + "name": "AirVisual Cloud" + }, + "airvisual_pro": { + "integration_type": "device", + "config_flow": true, + "iot_class": "local_polling", + "name": "AirVisual Pro" + } + } }, "airzone": { "name": "Airzone", @@ -143,7 +154,6 @@ "iot_class": "local_push" }, "alert": { - "name": "Alert", "integration_type": "hub", "config_flow": false, "iot_class": "local_push" @@ -232,7 +242,7 @@ }, "androidtv": { "name": "Android TV", - "integration_type": "hub", + "integration_type": "device", "config_flow": true, "iot_class": "local_polling" }, @@ -993,12 +1003,6 @@ } } }, - "deutsche_bahn": { - "name": "Deutsche Bahn", - "integration_type": "hub", - "config_flow": false, - "iot_class": "cloud_polling" - }, "device_sun_light_trigger": { "name": "Presence-based Lights", "integration_type": "hub", @@ -1957,6 +1961,12 @@ "iot_class": "cloud_push", "name": "Google Assistant" }, + "google_assistant_sdk": { + "integration_type": "service", + "config_flow": true, + "iot_class": "cloud_polling", + "name": "Google Assistant SDK" + }, "google_cloud": { "integration_type": "hub", "config_flow": false, @@ -3663,7 +3673,7 @@ }, "nut": { "name": "Network UPS Tools (NUT)", - "integration_type": "hub", + "integration_type": "device", "config_flow": true, "iot_class": "local_polling" }, @@ -4165,6 +4175,12 @@ "config_flow": true, "iot_class": "local_polling" }, + "purpleair": { + "name": "PurpleAir", + "integration_type": "hub", + "config_flow": true, + "iot_class": "cloud_polling" + }, "push": { "name": "Push", "integration_type": "hub", @@ -4394,6 +4410,12 @@ "config_flow": true, "iot_class": "cloud_polling" }, + "reolink": { + "name": "Reolink IP NVR/camera", + "integration_type": "hub", + "config_flow": true, + "iot_class": "local_polling" + }, "repetier": { "name": "Repetier-Server", "integration_type": "hub", @@ -6391,6 +6413,7 @@ } }, "translated_name": [ + "alert", "aurora", "cert_expiry", "cpuspeed", diff --git a/homeassistant/generated/ssdp.py b/homeassistant/generated/ssdp.py index 210c0c832a2..6599136b747 100644 --- a/homeassistant/generated/ssdp.py +++ b/homeassistant/generated/ssdp.py @@ -146,6 +146,14 @@ SSDP = { "deviceType": "urn:schemas-upnp-org:device:InternetGatewayDevice:1", "manufacturer": "Huawei", }, + { + "deviceType": "urn:schemas-upnp-org:device:InternetGatewayDevice:1", + "manufacturer": "Huawei Technologies Co., Ltd.", + }, + { + "deviceType": "urn:schemas-upnp-org:device:InternetGatewayDevice:1", + "manufacturer": "SOYEA TECHNOLOGY CO., LTD.", + }, ], "hue": [ { diff --git a/homeassistant/generated/zeroconf.py b/homeassistant/generated/zeroconf.py index 0d505bd2409..e7c8bdaf1df 100644 --- a/homeassistant/generated/zeroconf.py +++ b/homeassistant/generated/zeroconf.py @@ -152,6 +152,11 @@ ZEROCONF = { }, }, ], + "_bbxsrv._tcp.local.": [ + { + "domain": "blebox", + }, + ], "_bond._tcp.local.": [ { "domain": "bond", @@ -426,6 +431,12 @@ ZEROCONF = { "domain": "apple_tv", }, ], + "_slzb-06._tcp.local.": [ + { + "domain": "zha", + "name": "slzb-06*", + }, + ], "_sonos._tcp.local.": [ { "domain": "sonos", diff --git a/homeassistant/helpers/area_registry.py b/homeassistant/helpers/area_registry.py index aeb52e8faed..10ed9fdd65d 100644 --- a/homeassistant/helpers/area_registry.py +++ b/homeassistant/helpers/area_registry.py @@ -3,7 +3,7 @@ from __future__ import annotations from collections import OrderedDict from collections.abc import Container, Iterable, MutableMapping -from typing import Optional, cast +from typing import Any, cast import attr @@ -19,7 +19,8 @@ from .typing import UNDEFINED, UndefinedType DATA_REGISTRY = "area_registry" EVENT_AREA_REGISTRY_UPDATED = "area_registry_updated" STORAGE_KEY = "core.area_registry" -STORAGE_VERSION = 1 +STORAGE_VERSION_MAJOR = 1 +STORAGE_VERSION_MINOR = 3 SAVE_DELAY = 10 @@ -29,8 +30,11 @@ class AreaEntry: name: str = attr.ib() normalized_name: str = attr.ib() - picture: str | None = attr.ib(default=None) + aliases: set[str] = attr.ib( + converter=attr.converters.default_if_none(factory=set) # type: ignore[misc] + ) id: str | None = attr.ib(default=None) + picture: str | None = attr.ib(default=None) def generate_id(self, existing_ids: Container[str]) -> None: """Initialize ID.""" @@ -42,6 +46,33 @@ class AreaEntry: object.__setattr__(self, "id", suggestion) +class AreaRegistryStore(Store[dict[str, list[dict[str, Any]]]]): + """Store area registry data.""" + + async def _async_migrate_func( + self, + old_major_version: int, + old_minor_version: int, + old_data: dict[str, list[dict[str, Any]]], + ) -> dict[str, Any]: + """Migrate to the new version.""" + if old_major_version < 2: + if old_minor_version < 2: + # Version 1.2 implements migration and freezes the available keys + for area in old_data["areas"]: + # Populate keys which were introduced before version 1.2 + area.setdefault("picture", None) + + if old_minor_version < 3: + # Version 1.3 adds aliases + for area in old_data["areas"]: + area["aliases"] = [] + + if old_major_version > 1: + raise NotImplementedError + return old_data + + class AreaRegistry: """Class to hold a registry of areas.""" @@ -49,8 +80,12 @@ class AreaRegistry: """Initialize the area registry.""" self.hass = hass self.areas: MutableMapping[str, AreaEntry] = {} - self._store = Store[dict[str, list[dict[str, Optional[str]]]]]( - hass, STORAGE_VERSION, STORAGE_KEY, atomic_writes=True + self._store = AreaRegistryStore( + hass, + STORAGE_VERSION_MAJOR, + STORAGE_KEY, + atomic_writes=True, + minor_version=STORAGE_VERSION_MINOR, ) self._normalized_name_area_idx: dict[str, str] = {} @@ -80,14 +115,22 @@ class AreaRegistry: return self.async_create(name) @callback - def async_create(self, name: str, picture: str | None = None) -> AreaEntry: + def async_create( + self, + name: str, + *, + aliases: set[str] | None = None, + picture: str | None = None, + ) -> AreaEntry: """Create a new area.""" normalized_name = normalize_area_name(name) if self.async_get_area_by_name(name): raise ValueError(f"The name {name} ({normalized_name}) is already in use") - area = AreaEntry(name=name, normalized_name=normalized_name, picture=picture) + area = AreaEntry( + aliases=aliases, name=name, normalized_name=normalized_name, picture=picture + ) area.generate_id(self.areas) assert area.id is not None self.areas[area.id] = area @@ -120,11 +163,15 @@ class AreaRegistry: def async_update( self, area_id: str, + *, + aliases: set[str] | UndefinedType = UNDEFINED, name: str | UndefinedType = UNDEFINED, picture: str | None | UndefinedType = UNDEFINED, ) -> AreaEntry: """Update name of area.""" - updated = self._async_update(area_id, name=name, picture=picture) + updated = self._async_update( + area_id, aliases=aliases, name=name, picture=picture + ) self.hass.bus.async_fire( EVENT_AREA_REGISTRY_UPDATED, {"action": "update", "area_id": area_id} ) @@ -134,16 +181,22 @@ class AreaRegistry: def _async_update( self, area_id: str, + *, + aliases: set[str] | UndefinedType = UNDEFINED, name: str | UndefinedType = UNDEFINED, picture: str | None | UndefinedType = UNDEFINED, ) -> AreaEntry: """Update name of area.""" old = self.areas[area_id] - changes = {} + new_values = {} - if picture is not UNDEFINED: - changes["picture"] = picture + for attr_name, value in ( + ("aliases", aliases), + ("picture", picture), + ): + if value is not UNDEFINED and value != getattr(old, attr_name): + new_values[attr_name] = value normalized_name = None @@ -157,13 +210,13 @@ class AreaRegistry: f"The name {name} ({normalized_name}) is already in use" ) - changes["name"] = name - changes["normalized_name"] = normalized_name + new_values["name"] = name + new_values["normalized_name"] = normalized_name - if not changes: + if not new_values: return old - new = self.areas[area_id] = attr.evolve(old, **changes) + new = self.areas[area_id] = attr.evolve(old, **new_values) if normalized_name is not None: self._normalized_name_area_idx[ normalized_name @@ -183,11 +236,11 @@ class AreaRegistry: assert area["name"] is not None and area["id"] is not None normalized_name = normalize_area_name(area["name"]) areas[area["id"]] = AreaEntry( - name=area["name"], + aliases=set(area["aliases"]), id=area["id"], - # New in 2021.11 - picture=area.get("picture"), + name=area["name"], normalized_name=normalized_name, + picture=area["picture"], ) self._normalized_name_area_idx[normalized_name] = area["id"] @@ -199,12 +252,17 @@ class AreaRegistry: self._store.async_delay_save(self._data_to_save, SAVE_DELAY) @callback - def _data_to_save(self) -> dict[str, list[dict[str, str | None]]]: + def _data_to_save(self) -> dict[str, list[dict[str, Any]]]: """Return data of area registry to store in a file.""" data = {} data["areas"] = [ - {"name": entry.name, "id": entry.id, "picture": entry.picture} + { + "aliases": list(entry.aliases), + "name": entry.name, + "id": entry.id, + "picture": entry.picture, + } for entry in self.areas.values() ] @@ -231,7 +289,8 @@ async def async_get_registry(hass: HomeAssistant) -> AreaRegistry: This is deprecated and will be removed in the future. Use async_get instead. """ report( - "uses deprecated `async_get_registry` to access area registry, use async_get instead" + "uses deprecated `async_get_registry` to access area registry, use async_get" + " instead" ) return async_get(hass) diff --git a/homeassistant/helpers/collection.py b/homeassistant/helpers/collection.py index e3c31ca0e33..55be6dc7d27 100644 --- a/homeassistant/helpers/collection.py +++ b/homeassistant/helpers/collection.py @@ -211,7 +211,7 @@ class YamlCollection(ObservableCollection): await self.notify_changes(change_sets) -class StorageCollection(ObservableCollection): +class StorageCollection(ObservableCollection, ABC): """Offer a CRUD interface on top of JSON storage.""" def __init__( diff --git a/homeassistant/helpers/condition.py b/homeassistant/helpers/condition.py index 387d2ad09b0..feb7a2a17ae 100644 --- a/homeassistant/helpers/condition.py +++ b/homeassistant/helpers/condition.py @@ -385,7 +385,10 @@ def async_numeric_state( # noqa: C901 except (ValueError, TypeError) as ex: raise ConditionErrorMessage( "numeric_state", - f"the 'below' entity {below} state '{below_entity.state}' cannot be processed as a number", + ( + f"the 'below' entity {below} state '{below_entity.state}'" + " cannot be processed as a number" + ), ) from ex elif fvalue >= below: condition_trace_set_result(False, state=fvalue, wanted_state_below=below) @@ -413,7 +416,10 @@ def async_numeric_state( # noqa: C901 except (ValueError, TypeError) as ex: raise ConditionErrorMessage( "numeric_state", - f"the 'above' entity {above} state '{above_entity.state}' cannot be processed as a number", + ( + f"the 'above' entity {above} state '{above_entity.state}'" + " cannot be processed as a number" + ), ) from ex elif fvalue <= above: condition_trace_set_result(False, state=fvalue, wanted_state_above=above) @@ -889,7 +895,10 @@ def zone_from_config(config: ConfigType) -> ConditionCheckerType: errors.append( ConditionErrorMessage( "zone", - f"error matching {entity_id} with {zone_entity_id}: {ex.message}", + ( + f"error matching {entity_id} with {zone_entity_id}:" + f" {ex.message}" + ), ) ) diff --git a/homeassistant/helpers/config_entry_oauth2_flow.py b/homeassistant/helpers/config_entry_oauth2_flow.py index 0dc3415f7a9..4b135ae6a2f 100644 --- a/homeassistant/helpers/config_entry_oauth2_flow.py +++ b/homeassistant/helpers/config_entry_oauth2_flow.py @@ -217,7 +217,8 @@ class AbstractOAuth2FlowHandler(config_entries.ConfigFlow, metaclass=ABCMeta): """Instantiate config flow.""" if self.DOMAIN == "": raise TypeError( - f"Can't instantiate class {self.__class__.__name__} without DOMAIN being set" + f"Can't instantiate class {self.__class__.__name__} without DOMAIN" + " being set" ) self.external_data: Any = None @@ -290,7 +291,9 @@ class AbstractOAuth2FlowHandler(config_entries.ConfigFlow, metaclass=ABCMeta): return self.async_abort( reason="no_url_available", description_placeholders={ - "docs_url": "https://www.home-assistant.io/more-info/no-url-available" + "docs_url": ( + "https://www.home-assistant.io/more-info/no-url-available" + ) }, ) diff --git a/homeassistant/helpers/config_validation.py b/homeassistant/helpers/config_validation.py index fc71a586aee..ce2d0740d66 100644 --- a/homeassistant/helpers/config_validation.py +++ b/homeassistant/helpers/config_validation.py @@ -82,9 +82,8 @@ from homeassistant.const import ( ENTITY_MATCH_NONE, SUN_EVENT_SUNRISE, SUN_EVENT_SUNSET, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, WEEKDAYS, + UnitOfTemperature, ) from homeassistant.core import split_entity_id, valid_entity_id from homeassistant.exceptions import TemplateError @@ -579,13 +578,13 @@ def string_with_no_html(value: Any) -> str: return str(value) -def temperature_unit(value: Any) -> str: +def temperature_unit(value: Any) -> UnitOfTemperature: """Validate and transform temperature unit.""" value = str(value).upper() if value == "C": - return TEMP_CELSIUS + return UnitOfTemperature.CELSIUS if value == "F": - return TEMP_FAHRENHEIT + return UnitOfTemperature.FAHRENHEIT raise vol.Invalid("invalid temperature unit (expected C or F)") diff --git a/homeassistant/helpers/deprecation.py b/homeassistant/helpers/deprecation.py index 8d961d7008b..a132536b53f 100644 --- a/homeassistant/helpers/deprecation.py +++ b/homeassistant/helpers/deprecation.py @@ -40,8 +40,10 @@ def deprecated_substitute( if not warnings.get(module_name): logger = logging.getLogger(module_name) logger.warning( - "'%s' is deprecated. Please rename '%s' to " - "'%s' in '%s' to ensure future support.", + ( + "'%s' is deprecated. Please rename '%s' to " + "'%s' in '%s' to ensure future support." + ), substitute_name, substitute_name, func.__name__, @@ -79,8 +81,10 @@ def get_deprecated( logger = logging.getLogger(module_name) logger.warning( - "'%s' is deprecated. Please rename '%s' to '%s' in your " - "configuration file.", + ( + "'%s' is deprecated. Please rename '%s' to '%s' in your " + "configuration file." + ), old_name, old_name, new_name, @@ -133,7 +137,10 @@ def _print_deprecation_warning(obj: Any, replacement: str, description: str) -> _, integration, path = get_integration_frame() if path == "custom_components/": logger.warning( - "%s was called from %s, this is a deprecated %s. Use %s instead, please report this to the maintainer of %s", + ( + "%s was called from %s, this is a deprecated %s. Use %s instead," + " please report this to the maintainer of %s" + ), obj.__name__, integration, description, diff --git a/homeassistant/helpers/device_registry.py b/homeassistant/helpers/device_registry.py index 908db74d40d..a7c1ebdb434 100644 --- a/homeassistant/helpers/device_registry.py +++ b/homeassistant/helpers/device_registry.py @@ -146,40 +146,34 @@ class DeviceRegistryStore(storage.Store[dict[str, list[dict[str, Any]]]]): """Store entity registry data.""" async def _async_migrate_func( - self, old_major_version: int, old_minor_version: int, old_data: dict[str, Any] + self, + old_major_version: int, + old_minor_version: int, + old_data: dict[str, list[dict[str, Any]]], ) -> dict[str, Any]: """Migrate to the new version.""" if old_major_version < 2: if old_minor_version < 2: - # From version 1.1 + # Version 1.2 implements migration and freezes the available keys, + # populate keys which were introduced before version 1.2 for device in old_data["devices"]: - # Introduced in 0.110 + device.setdefault("area_id", None) + device.setdefault("configuration_url", None) + device.setdefault("disabled_by", None) try: - device["entry_type"] = DeviceEntryType(device.get("entry_type")) + device["entry_type"] = DeviceEntryType(device.get("entry_type")) # type: ignore[arg-type] except ValueError: device["entry_type"] = None - - # Introduced in 0.79 - # renamed in 0.95 - device["via_device_id"] = device.get("via_device_id") or device.get( - "hub_device_id" - ) - # Introduced in 0.87 - device["area_id"] = device.get("area_id") - device["name_by_user"] = device.get("name_by_user") - # Introduced in 0.119 - device["disabled_by"] = device.get("disabled_by") - # Introduced in 2021.11 - device["configuration_url"] = device.get("configuration_url") - # Introduced in 0.111 - old_data["deleted_devices"] = old_data.get("deleted_devices", []) + device.setdefault("name_by_user", None) + # via_device_id was originally introduced as hub_device_id + device.setdefault("via_device_id", device.get("hub_device_id")) + old_data.setdefault("deleted_devices", []) for device in old_data["deleted_devices"]: - # Introduced in 2021.2 - device["orphaned_timestamp"] = device.get("orphaned_timestamp") + device.setdefault("orphaned_timestamp", None) if old_minor_version < 3: - # Introduced in 2022.2 + # Version 1.3 adds hw_version for device in old_data["devices"]: - device["hw_version"] = device.get("hw_version") + device["hw_version"] = None if old_major_version > 1: raise NotImplementedError @@ -347,9 +341,11 @@ class DeviceRegistry: if isinstance(entry_type, str) and not isinstance(entry_type, DeviceEntryType): report( # type: ignore[unreachable] - "uses str for device registry entry_type. This is deprecated and will " - "stop working in Home Assistant 2022.3, it should be updated to use " - "DeviceEntryType instead", + ( + "uses str for device registry entry_type. This is deprecated and" + " will stop working in Home Assistant 2022.3, it should be updated" + " to use DeviceEntryType instead" + ), error_if_core=False, ) entry_type = DeviceEntryType(entry_type) @@ -418,9 +414,11 @@ class DeviceRegistry: disabled_by, DeviceEntryDisabler ): report( # type: ignore[unreachable] - "uses str for device registry disabled_by. This is deprecated and will " - "stop working in Home Assistant 2022.3, it should be updated to use " - "DeviceEntryDisabler instead", + ( + "uses str for device registry disabled_by. This is deprecated and" + " will stop working in Home Assistant 2022.3, it should be updated" + " to use DeviceEntryDisabler instead" + ), error_if_core=False, ) disabled_by = DeviceEntryDisabler(disabled_by) @@ -696,7 +694,8 @@ async def async_get_registry(hass: HomeAssistant) -> DeviceRegistry: This is deprecated and will be removed in the future. Use async_get instead. """ report( - "uses deprecated `async_get_registry` to access device registry, use async_get instead" + "uses deprecated `async_get_registry` to access device registry, use async_get" + " instead" ) return async_get(hass) diff --git a/homeassistant/helpers/entity.py b/homeassistant/helpers/entity.py index 255b0c2d834..745f9b0ba53 100644 --- a/homeassistant/helpers/entity.py +++ b/homeassistant/helpers/entity.py @@ -223,6 +223,7 @@ class EntityDescription: icon: str | None = None has_entity_name: bool = False name: str | None = None + translation_key: str | None = None unit_of_measurement: str | None = None @@ -290,6 +291,7 @@ class Entity(ABC): _attr_should_poll: bool = True _attr_state: StateType = STATE_UNKNOWN _attr_supported_features: int | None = None + _attr_translation_key: str | None _attr_unique_id: str | None = None _attr_unit_of_measurement: str | None @@ -486,6 +488,15 @@ class Entity(ABC): return self.entity_description.entity_category return None + @property + def translation_key(self) -> str | None: + """Return the translation key to translate the entity's states.""" + if hasattr(self, "_attr_translation_key"): + return self._attr_translation_key + if hasattr(self, "entity_description"): + return self.entity_description.translation_key + return None + # DO NOT OVERWRITE # These properties and methods are either managed by Home Assistant or they # are used to perform a very specific function. Overwriting these may @@ -568,7 +579,10 @@ class Entity(ABC): self._disabled_reported = True assert self.platform is not None _LOGGER.warning( - "Entity %s is incorrectly being triggered for updates while it is disabled. This is a bug in the %s integration", + ( + "Entity %s is incorrectly being triggered for updates while it" + " is disabled. This is a bug in the %s integration" + ), self.entity_id, self.platform.platform_name, ) @@ -756,7 +770,8 @@ class Entity(ABC): """Start adding an entity to a platform.""" if self._platform_state == EntityPlatformState.ADDED: raise HomeAssistantError( - f"Entity {self.entity_id} cannot be added a second time to an entity platform" + f"Entity {self.entity_id} cannot be added a second time to an entity" + " platform" ) self.hass = hass diff --git a/homeassistant/helpers/entity_platform.py b/homeassistant/helpers/entity_platform.py index 9b8e1985930..622fa4c1751 100644 --- a/homeassistant/helpers/entity_platform.py +++ b/homeassistant/helpers/entity_platform.py @@ -145,7 +145,12 @@ class EntityPlatform: def __repr__(self) -> str: """Represent an EntityPlatform.""" - return f"" + return ( + "" + ) @callback def _get_parallel_updates_semaphore( @@ -191,7 +196,10 @@ class EntityPlatform: platform, "setup_platform" ): self.logger.error( - "The %s platform for the %s integration does not support platform setup. Please remove it from your config.", + ( + "The %s platform for the %s integration does not support platform" + " setup. Please remove it from your config." + ), self.platform_name, self.domain, ) @@ -327,8 +335,10 @@ class EntityPlatform: return False except asyncio.TimeoutError: logger.error( - "Setup of platform %s is taking longer than %s seconds." - " Startup will proceed without waiting any longer.", + ( + "Setup of platform %s is taking longer than %s seconds." + " Startup will proceed without waiting any longer." + ), self.platform_name, SLOW_SETUP_MAX_WAIT, ) @@ -514,9 +524,15 @@ class EntityPlatform: f"Platform {self.platform_name} does not generate unique IDs. " ) if entity.entity_id: - msg += f"ID {entity.unique_id} is already used by {registered_entity_id} - ignoring {entity.entity_id}" + msg += ( + f"ID {entity.unique_id} is already used by" + f" {registered_entity_id} - ignoring {entity.entity_id}" + ) else: - msg += f"ID {entity.unique_id} already exists - ignoring {registered_entity_id}" + msg += ( + f"ID {entity.unique_id} already exists - ignoring" + f" {registered_entity_id}" + ) self.logger.error(msg) entity.add_to_platform_abort() return @@ -616,6 +632,7 @@ class EntityPlatform: original_name=entity.name, suggested_object_id=suggested_object_id, supported_features=entity.supported_features, + translation_key=entity.translation_key, unit_of_measurement=entity.unit_of_measurement, ) diff --git a/homeassistant/helpers/entity_registry.py b/homeassistant/helpers/entity_registry.py index 77a0b5a0400..da4a8aae6e1 100644 --- a/homeassistant/helpers/entity_registry.py +++ b/homeassistant/helpers/entity_registry.py @@ -61,7 +61,7 @@ SAVE_DELAY = 10 _LOGGER = logging.getLogger(__name__) STORAGE_VERSION_MAJOR = 1 -STORAGE_VERSION_MINOR = 8 +STORAGE_VERSION_MINOR = 10 STORAGE_KEY = "core.entity_registry" # Attributes relevant to describing entity @@ -104,6 +104,7 @@ class RegistryEntry: entity_id: str = attr.ib() unique_id: str = attr.ib() platform: str = attr.ib() + aliases: set[str] = attr.ib(factory=set) area_id: str | None = attr.ib(default=None) capabilities: Mapping[str, Any] | None = attr.ib(default=None) config_entry_id: str | None = attr.ib(default=None) @@ -125,6 +126,7 @@ class RegistryEntry: original_icon: str | None = attr.ib(default=None) original_name: str | None = attr.ib(default=None) supported_features: int = attr.ib(default=0) + translation_key: str | None = attr.ib(default=None) unit_of_measurement: str | None = attr.ib(default=None) @domain.default @@ -171,32 +173,34 @@ class RegistryEntry: hass.states.async_set(self.entity_id, STATE_UNAVAILABLE, attrs) -class EntityRegistryStore(storage.Store): +class EntityRegistryStore(storage.Store[dict[str, list[dict[str, Any]]]]): """Store entity registry data.""" async def _async_migrate_func( - self, old_major_version: int, old_minor_version: int, old_data: dict + self, + old_major_version: int, + old_minor_version: int, + old_data: dict[str, list[dict[str, Any]]], ) -> dict: """Migrate to the new version.""" data = old_data if old_major_version == 1 and old_minor_version < 2: - # From version 1.1 + # Version 1.2 implements migration and freezes the available keys for entity in data["entities"]: - # Populate all keys - entity["area_id"] = entity.get("area_id") - entity["capabilities"] = entity.get("capabilities") or {} - entity["config_entry_id"] = entity.get("config_entry_id") - entity["device_class"] = entity.get("device_class") - entity["device_id"] = entity.get("device_id") - entity["disabled_by"] = entity.get("disabled_by") - entity["entity_category"] = entity.get("entity_category") - entity["icon"] = entity.get("icon") - entity["name"] = entity.get("name") - entity["original_icon"] = entity.get("original_icon") - entity["original_name"] = entity.get("original_name") - entity["platform"] = entity["platform"] - entity["supported_features"] = entity.get("supported_features", 0) - entity["unit_of_measurement"] = entity.get("unit_of_measurement") + # Populate keys which were introduced before version 1.2 + entity.setdefault("area_id", None) + entity.setdefault("capabilities", {}) + entity.setdefault("config_entry_id", None) + entity.setdefault("device_class", None) + entity.setdefault("device_id", None) + entity.setdefault("disabled_by", None) + entity.setdefault("entity_category", None) + entity.setdefault("icon", None) + entity.setdefault("name", None) + entity.setdefault("original_icon", None) + entity.setdefault("original_name", None) + entity.setdefault("supported_features", 0) + entity.setdefault("unit_of_measurement", None) if old_major_version == 1 and old_minor_version < 3: # Version 1.3 adds original_device_class @@ -234,6 +238,16 @@ class EntityRegistryStore(storage.Store): continue entity["device_class"] = None + if old_major_version == 1 and old_minor_version < 9: + # Version 1.9 adds translation_key + for entity in data["entities"]: + entity["translation_key"] = None + + if old_major_version == 1 and old_minor_version < 10: + # Version 1.10 adds aliases + for entity in data["entities"]: + entity["aliases"] = [] + if old_major_version > 1: raise NotImplementedError return data @@ -412,6 +426,7 @@ class EntityRegistry: original_icon: str | None | UndefinedType = UNDEFINED, original_name: str | None | UndefinedType = UNDEFINED, supported_features: int | None | UndefinedType = UNDEFINED, + translation_key: str | None | UndefinedType = UNDEFINED, unit_of_measurement: str | None | UndefinedType = UNDEFINED, ) -> RegistryEntry: """Get entity. Create if it doesn't exist.""" @@ -437,6 +452,7 @@ class EntityRegistry: original_icon=original_icon, original_name=original_name, supported_features=supported_features, + translation_key=translation_key, unit_of_measurement=unit_of_measurement, ) @@ -487,6 +503,7 @@ class EntityRegistry: original_name=none_if_undefined(original_name), platform=platform, supported_features=none_if_undefined(supported_features) or 0, + translation_key=none_if_undefined(translation_key), unique_id=unique_id, unit_of_measurement=none_if_undefined(unit_of_measurement), ) @@ -579,6 +596,7 @@ class EntityRegistry: self, entity_id: str, *, + aliases: set[str] | UndefinedType = UNDEFINED, area_id: str | None | UndefinedType = UNDEFINED, capabilities: Mapping[str, Any] | None | UndefinedType = UNDEFINED, config_entry_id: str | None | UndefinedType = UNDEFINED, @@ -592,13 +610,14 @@ class EntityRegistry: name: str | None | UndefinedType = UNDEFINED, new_entity_id: str | UndefinedType = UNDEFINED, new_unique_id: str | UndefinedType = UNDEFINED, + options: EntityOptionsType | UndefinedType = UNDEFINED, original_device_class: str | None | UndefinedType = UNDEFINED, original_icon: str | None | UndefinedType = UNDEFINED, original_name: str | None | UndefinedType = UNDEFINED, - supported_features: int | UndefinedType = UNDEFINED, - unit_of_measurement: str | None | UndefinedType = UNDEFINED, platform: str | None | UndefinedType = UNDEFINED, - options: EntityOptionsType | UndefinedType = UNDEFINED, + supported_features: int | UndefinedType = UNDEFINED, + translation_key: str | None | UndefinedType = UNDEFINED, + unit_of_measurement: str | None | UndefinedType = UNDEFINED, ) -> RegistryEntry: """Private facing update properties method.""" old = self.entities[entity_id] @@ -629,6 +648,7 @@ class EntityRegistry: raise ValueError("entity_category must be a valid EntityCategory instance") for attr_name, value in ( + ("aliases", aliases), ("area_id", area_id), ("capabilities", capabilities), ("config_entry_id", config_entry_id), @@ -640,13 +660,14 @@ class EntityRegistry: ("icon", icon), ("has_entity_name", has_entity_name), ("name", name), + ("options", options), ("original_device_class", original_device_class), ("original_icon", original_icon), ("original_name", original_name), - ("supported_features", supported_features), - ("unit_of_measurement", unit_of_measurement), ("platform", platform), - ("options", options), + ("supported_features", supported_features), + ("translation_key", translation_key), + ("unit_of_measurement", unit_of_measurement), ): if value is not UNDEFINED and value != getattr(old, attr_name): new_values[attr_name] = value @@ -703,6 +724,7 @@ class EntityRegistry: self, entity_id: str, *, + aliases: set[str] | UndefinedType = UNDEFINED, area_id: str | None | UndefinedType = UNDEFINED, capabilities: Mapping[str, Any] | None | UndefinedType = UNDEFINED, config_entry_id: str | None | UndefinedType = UNDEFINED, @@ -720,11 +742,13 @@ class EntityRegistry: original_icon: str | None | UndefinedType = UNDEFINED, original_name: str | None | UndefinedType = UNDEFINED, supported_features: int | UndefinedType = UNDEFINED, + translation_key: str | None | UndefinedType = UNDEFINED, unit_of_measurement: str | None | UndefinedType = UNDEFINED, ) -> RegistryEntry: """Update properties of an entity.""" return self._async_update_entity( entity_id, + aliases=aliases, area_id=area_id, capabilities=capabilities, config_entry_id=config_entry_id, @@ -742,6 +766,7 @@ class EntityRegistry: original_icon=original_icon, original_name=original_name, supported_features=supported_features, + translation_key=translation_key, unit_of_measurement=unit_of_measurement, ) @@ -806,6 +831,7 @@ class EntityRegistry: entity["entity_category"] = None entities[entity["entity_id"]] = RegistryEntry( + aliases=set(entity["aliases"]), area_id=entity["area_id"], capabilities=entity["capabilities"], config_entry_id=entity["config_entry_id"], @@ -831,6 +857,7 @@ class EntityRegistry: original_name=entity["original_name"], platform=entity["platform"], supported_features=entity["supported_features"], + translation_key=entity["translation_key"], unique_id=entity["unique_id"], unit_of_measurement=entity["unit_of_measurement"], ) @@ -849,6 +876,7 @@ class EntityRegistry: data["entities"] = [ { + "aliases": list(entry.aliases), "area_id": entry.area_id, "capabilities": entry.capabilities, "config_entry_id": entry.config_entry_id, @@ -868,6 +896,7 @@ class EntityRegistry: "original_name": entry.original_name, "platform": entry.platform, "supported_features": entry.supported_features, + "translation_key": entry.translation_key, "unique_id": entry.unique_id, "unit_of_measurement": entry.unit_of_measurement, } @@ -914,7 +943,8 @@ async def async_get_registry(hass: HomeAssistant) -> EntityRegistry: This is deprecated and will be removed in the future. Use async_get instead. """ report( - "uses deprecated `async_get_registry` to access entity registry, use async_get instead" + "uses deprecated `async_get_registry` to access entity registry, use async_get" + " instead" ) return async_get(hass) diff --git a/homeassistant/helpers/event.py b/homeassistant/helpers/event.py index b553b855be1..b42bf6c6913 100644 --- a/homeassistant/helpers/event.py +++ b/homeassistant/helpers/event.py @@ -729,7 +729,7 @@ def async_track_template( ], variables: TemplateVarsType | None = None, ) -> CALLBACK_TYPE: - """Add a listener that fires when a a template evaluates to 'true'. + """Add a listener that fires when a template evaluates to 'true'. Listen for the result of the template becoming true, or a true-like string result, such as 'On', 'Open', or 'Yes'. If the template results @@ -884,7 +884,10 @@ class TrackTemplateResultInfo: ) self._update_time_listeners() _LOGGER.debug( - "Template group %s listens for %s, first render blocker by super template: %s", + ( + "Template group %s listens for %s, first render blocker by super" + " template: %s" + ), self._track_templates, self.listeners, block_render, @@ -1106,7 +1109,10 @@ class TrackTemplateResultInfo: ) ) _LOGGER.debug( - "Template group %s listens for %s, re-render blocker by super template: %s", + ( + "Template group %s listens for %s, re-render blocker by super" + " template: %s" + ), self._track_templates, self.listeners, block_updates, diff --git a/homeassistant/helpers/frame.py b/homeassistant/helpers/frame.py index ca5cf759d8e..988db411a6b 100644 --- a/homeassistant/helpers/frame.py +++ b/homeassistant/helpers/frame.py @@ -102,8 +102,10 @@ def report_integration( _LOGGER.log( level, - "Detected integration that %s. " - "Please report issue%s for %s using this method at %s, line %s: %s", + ( + "Detected integration that %s. " + "Please report issue%s for %s using this method at %s, line %s: %s" + ), what, extra, integration, diff --git a/homeassistant/helpers/intent.py b/homeassistant/helpers/intent.py index 7b0b1033016..100d64c8fb6 100644 --- a/homeassistant/helpers/intent.py +++ b/homeassistant/helpers/intent.py @@ -2,6 +2,9 @@ from __future__ import annotations from collections.abc import Callable, Iterable +import dataclasses +from dataclasses import dataclass +from enum import Enum import logging import re from typing import Any, TypeVar @@ -56,6 +59,7 @@ async def async_handle( slots: _SlotsType | None = None, text_input: str | None = None, context: Context | None = None, + language: str | None = None, ) -> IntentResponse: """Handle an intent.""" handler: IntentHandler = hass.data.get(DATA_KEY, {}).get(intent_type) @@ -66,7 +70,12 @@ async def async_handle( if context is None: context = Context() - intent = Intent(hass, platform, intent_type, slots or {}, text_input, context) + if language is None: + language = hass.config.language + + intent = Intent( + hass, platform, intent_type, slots or {}, text_input, context, language + ) try: _LOGGER.info("Triggering intent handler %s", handler) @@ -212,13 +221,41 @@ class ServiceIntentHandler(IntentHandler): response = intent_obj.create_response() response.async_set_speech(self.speech.format(state.name)) + response.async_set_results( + success_results=[ + IntentResponseTarget( + type=IntentResponseTargetType.ENTITY, + name=state.name, + id=state.entity_id, + ), + ], + ) return response +class IntentCategory(Enum): + """Category of an intent.""" + + ACTION = "action" + """Trigger an action like turning an entity on or off""" + + QUERY = "query" + """Get information about the state of an entity""" + + class Intent: """Hold the intent.""" - __slots__ = ["hass", "platform", "intent_type", "slots", "text_input", "context"] + __slots__ = [ + "hass", + "platform", + "intent_type", + "slots", + "text_input", + "context", + "language", + "category", + ] def __init__( self, @@ -228,6 +265,8 @@ class Intent: slots: _SlotsType, text_input: str | None, context: Context, + language: str, + category: IntentCategory | None = None, ) -> None: """Initialize an intent.""" self.hass = hass @@ -236,36 +275,117 @@ class Intent: self.slots = slots self.text_input = text_input self.context = context + self.language = language + self.category = category @callback def create_response(self) -> IntentResponse: """Create a response.""" - return IntentResponse(self) + return IntentResponse(language=self.language, intent=self) + + +class IntentResponseType(Enum): + """Type of the intent response.""" + + ACTION_DONE = "action_done" + """Intent caused an action to occur""" + + PARTIAL_ACTION_DONE = "partial_action_done" + """Intent caused an action, but it could only be partially done""" + + QUERY_ANSWER = "query_answer" + """Response is an answer to a query""" + + ERROR = "error" + """Response is an error""" + + +class IntentResponseErrorCode(str, Enum): + """Reason for an intent response error.""" + + NO_INTENT_MATCH = "no_intent_match" + """Text could not be matched to an intent""" + + NO_VALID_TARGETS = "no_valid_targets" + """Intent was matched, but no valid areas/devices/entities were targeted""" + + FAILED_TO_HANDLE = "failed_to_handle" + """Unexpected error occurred while handling intent""" + + UNKNOWN = "unknown" + """Error outside the scope of intent processing""" + + +class IntentResponseTargetType(str, Enum): + """Type of target for an intent response.""" + + AREA = "area" + DEVICE = "device" + ENTITY = "entity" + DOMAIN = "domain" + DEVICE_CLASS = "device_class" + CUSTOM = "custom" + + +@dataclass +class IntentResponseTarget: + """Target of the intent response.""" + + name: str + type: IntentResponseTargetType + id: str | None = None class IntentResponse: """Response to an intent.""" - def __init__(self, intent: Intent | None = None) -> None: + def __init__( + self, + language: str, + intent: Intent | None = None, + ) -> None: """Initialize an IntentResponse.""" + self.language = language self.intent = intent self.speech: dict[str, dict[str, Any]] = {} self.reprompt: dict[str, dict[str, Any]] = {} self.card: dict[str, dict[str, str]] = {} + self.error_code: IntentResponseErrorCode | None = None + self.intent_targets: list[IntentResponseTarget] = [] + self.success_results: list[IntentResponseTarget] = [] + self.failed_results: list[IntentResponseTarget] = [] + + if (self.intent is not None) and (self.intent.category == IntentCategory.QUERY): + # speech will be the answer to the query + self.response_type = IntentResponseType.QUERY_ANSWER + else: + self.response_type = IntentResponseType.ACTION_DONE @callback def async_set_speech( - self, speech: str, speech_type: str = "plain", extra_data: Any | None = None + self, + speech: str, + speech_type: str = "plain", + extra_data: Any | None = None, ) -> None: """Set speech response.""" - self.speech[speech_type] = {"speech": speech, "extra_data": extra_data} + self.speech[speech_type] = { + "speech": speech, + "extra_data": extra_data, + } @callback def async_set_reprompt( - self, speech: str, speech_type: str = "plain", extra_data: Any | None = None + self, + speech: str, + speech_type: str = "plain", + extra_data: Any | None = None, ) -> None: """Set reprompt response.""" - self.reprompt[speech_type] = {"reprompt": speech, "extra_data": extra_data} + self.reprompt[speech_type] = { + "reprompt": speech, + "extra_data": extra_data, + } @callback def async_set_card( @@ -275,10 +395,65 @@ class IntentResponse: self.card[card_type] = {"title": title, "content": content} @callback - def as_dict(self) -> dict[str, dict[str, dict[str, Any]]]: + def async_set_error(self, code: IntentResponseErrorCode, message: str) -> None: + """Set response error.""" + self.response_type = IntentResponseType.ERROR + self.error_code = code + + # Speak error message + self.async_set_speech(message) + + @callback + def async_set_targets( + self, + intent_targets: list[IntentResponseTarget], + ) -> None: + """Set response targets.""" + self.intent_targets = intent_targets + + @callback + def async_set_results( + self, + success_results: list[IntentResponseTarget], + failed_results: list[IntentResponseTarget] | None = None, + ) -> None: + """Set response results.""" + self.success_results = success_results + self.failed_results = failed_results if failed_results is not None else [] + + @callback + def as_dict(self) -> dict[str, Any]: """Return a dictionary representation of an intent response.""" - return ( - {"speech": self.speech, "reprompt": self.reprompt, "card": self.card} - if self.reprompt - else {"speech": self.speech, "card": self.card} - ) + response_dict: dict[str, Any] = { + "speech": self.speech, + "card": self.card, + "language": self.language, + "response_type": self.response_type.value, + } + + if self.reprompt: + response_dict["reprompt"] = self.reprompt + + response_data: dict[str, Any] = {} + + if self.response_type == IntentResponseType.ERROR: + assert self.error_code is not None, "error code is required" + response_data["code"] = self.error_code.value + else: + # action done or query answer + response_data["targets"] = [ + dataclasses.asdict(target) for target in self.intent_targets + ] + + # Add success/failed targets + response_data["success"] = [ + dataclasses.asdict(target) for target in self.success_results + ] + + response_data["failed"] = [ + dataclasses.asdict(target) for target in self.failed_results + ] + + response_dict["data"] = response_data + + return response_dict diff --git a/homeassistant/helpers/location.py b/homeassistant/helpers/location.py index bbc32145706..a22d5fddf0c 100644 --- a/homeassistant/helpers/location.py +++ b/homeassistant/helpers/location.py @@ -84,7 +84,10 @@ def find_coordinates( recursion_history.append(name) if entity_state.state in recursion_history: _LOGGER.error( - "Circular reference detected while trying to find coordinates of an entity. The state of %s has already been checked", + ( + "Circular reference detected while trying to find coordinates of an" + " entity. The state of %s has already been checked" + ), entity_state.state, ) return None diff --git a/homeassistant/helpers/restore_state.py b/homeassistant/helpers/restore_state.py index 73a00898d69..c31fe0f3ce4 100644 --- a/homeassistant/helpers/restore_state.py +++ b/homeassistant/helpers/restore_state.py @@ -1,7 +1,7 @@ """Support for restoring entity states on startup.""" from __future__ import annotations -from abc import abstractmethod +from abc import ABC, abstractmethod import asyncio from datetime import datetime, timedelta import logging @@ -35,7 +35,7 @@ STATE_EXPIRATION = timedelta(days=7) _StoredStateSelfT = TypeVar("_StoredStateSelfT", bound="StoredState") -class ExtraStoredData: +class ExtraStoredData(ABC): """Object to hold extra stored data.""" @abstractmethod diff --git a/homeassistant/helpers/schema_config_entry_flow.py b/homeassistant/helpers/schema_config_entry_flow.py index 8980abbb466..4e319b20cb6 100644 --- a/homeassistant/helpers/schema_config_entry_flow.py +++ b/homeassistant/helpers/schema_config_entry_flow.py @@ -1,7 +1,7 @@ """Helpers for creating schema based data entry flows.""" from __future__ import annotations -from abc import abstractmethod +from abc import ABC, abstractmethod from collections.abc import Callable, Coroutine, Mapping import copy from dataclasses import dataclass @@ -250,7 +250,7 @@ class SchemaCommonFlowHandler: ) -class SchemaConfigFlowHandler(config_entries.ConfigFlow): +class SchemaConfigFlowHandler(config_entries.ConfigFlow, ABC): """Handle a schema based config flow.""" config_flow: Mapping[str, SchemaFlowStep] @@ -401,7 +401,7 @@ class SchemaOptionsFlowHandler(config_entries.OptionsFlowWithConfigEntry): """Finish config flow and create a config entry.""" if self._async_options_flow_finished: self._async_options_flow_finished(self.hass, data) - return super().async_create_entry(title="", data=data, **kwargs) + return super().async_create_entry(data=data, **kwargs) @callback diff --git a/homeassistant/helpers/script.py b/homeassistant/helpers/script.py index 5fc0fdc4706..66a042dbb3d 100644 --- a/homeassistant/helpers/script.py +++ b/homeassistant/helpers/script.py @@ -1538,7 +1538,7 @@ class Script: self, update_state: bool = True, spare: _ScriptRun | None = None ) -> None: """Stop running script.""" - # Collect a a list of script runs to stop. This must be done before calling + # Collect a list of script runs to stop. This must be done before calling # asyncio.shield as asyncio.shield yields to the event loop, which would cause # us to wait for script runs added after the call to async_stop. aws = [ diff --git a/homeassistant/helpers/service.py b/homeassistant/helpers/service.py index 74f8d088ffc..935f1840db5 100644 --- a/homeassistant/helpers/service.py +++ b/homeassistant/helpers/service.py @@ -716,7 +716,10 @@ async def _handle_entity_call( if asyncio.iscoroutine(result): _LOGGER.error( - "Service %s for %s incorrectly returns a coroutine object. Await result instead in service handler. Report bug to integration author", + ( + "Service %s for %s incorrectly returns a coroutine object. Await result" + " instead in service handler. Report bug to integration author" + ), func, entity.entity_id, ) diff --git a/homeassistant/helpers/template.py b/homeassistant/helpers/template.py index 73e1400cedf..0c888784629 100644 --- a/homeassistant/helpers/template.py +++ b/homeassistant/helpers/template.py @@ -38,8 +38,8 @@ from homeassistant.const import ( ATTR_LONGITUDE, ATTR_PERSONS, ATTR_UNIT_OF_MEASUREMENT, - LENGTH_METERS, STATE_UNKNOWN, + UnitOfLength, ) from homeassistant.core import ( Context, @@ -1364,7 +1364,7 @@ def distance(hass, *args): return hass.config.distance(*locations[0]) return hass.config.units.length( - loc_util.distance(*locations[0] + locations[1]), LENGTH_METERS + loc_util.distance(*locations[0] + locations[1]), UnitOfLength.METERS ) @@ -1409,8 +1409,8 @@ def raise_no_default(function: str, value: Any) -> NoReturn: """Log warning if no default is specified.""" template, action = template_cv.get() or ("", "rendering or compiling") raise ValueError( - f"Template error: {function} got invalid input '{value}' when {action} template '{template}' " - "but no default was specified" + f"Template error: {function} got invalid input '{value}' when {action} template" + f" '{template}' but no default was specified" ) @@ -1675,7 +1675,7 @@ def average(*args: Any, default: Any = _SENTINEL) -> Any: if len(args) == 0: raise TypeError("average expected at least 1 argument, got 0") - # If first argument is iterable and more then 1 argument provided but not a named default, + # If first argument is iterable and more than 1 argument provided but not a named default, # then use 2nd argument as default. if isinstance(args[0], Iterable): average_list = args[0] @@ -1800,7 +1800,11 @@ def struct_pack(value: Any | None, format_string: str) -> bytes | None: return pack(format_string, value) except StructError: _LOGGER.warning( - "Template warning: 'pack' unable to pack object '%s' with type '%s' and format_string '%s' see https://docs.python.org/3/library/struct.html for more information", + ( + "Template warning: 'pack' unable to pack object '%s' with type '%s' and" + " format_string '%s' see https://docs.python.org/3/library/struct.html" + " for more information" + ), str(value), type(value).__name__, format_string, @@ -1814,7 +1818,11 @@ def struct_unpack(value: bytes, format_string: str, offset: int = 0) -> Any | No return unpack_from(format_string, value, offset)[0] except StructError: _LOGGER.warning( - "Template warning: 'unpack' unable to unpack object '%s' with format_string '%s' and offset %s see https://docs.python.org/3/library/struct.html for more information", + ( + "Template warning: 'unpack' unable to unpack object '%s' with" + " format_string '%s' and offset %s see" + " https://docs.python.org/3/library/struct.html for more information" + ), value, format_string, offset, diff --git a/homeassistant/helpers/template_entity.py b/homeassistant/helpers/template_entity.py index 83d321e3fa9..e824b2f2c8b 100644 --- a/homeassistant/helpers/template_entity.py +++ b/homeassistant/helpers/template_entity.py @@ -106,9 +106,11 @@ class _TemplateAttribute: """Handle a template result event callback.""" if isinstance(result, TemplateError): _LOGGER.error( - "TemplateError('%s') " - "while processing template '%s' " - "for attribute '%s' in entity '%s'", + ( + "TemplateError('%s') " + "while processing template '%s' " + "for attribute '%s' in entity '%s'" + ), result, self.template, self._attribute, @@ -130,10 +132,12 @@ class _TemplateAttribute: validated = self.validator(result) except vol.Invalid as ex: _LOGGER.error( - "Error validating template result '%s' " - "from template '%s' " - "for attribute '%s' in entity %s " - "validation message '%s'", + ( + "Error validating template result '%s' " + "from template '%s' " + "for attribute '%s' in entity %s " + "validation message '%s'" + ), result, self.template, self._attribute, @@ -310,7 +314,10 @@ class TemplateEntity(Entity): if self._self_ref_update_count > len(self._template_attrs): for update in updates: _LOGGER.warning( - "Template loop detected while processing event: %s, skipping template render for Template[%s]", + ( + "Template loop detected while processing event: %s, skipping" + " template render for Template[%s]" + ), event, update.template.template, ) diff --git a/homeassistant/helpers/translation.py b/homeassistant/helpers/translation.py index d1953b2fd00..049a37e0086 100644 --- a/homeassistant/helpers/translation.py +++ b/homeassistant/helpers/translation.py @@ -118,7 +118,10 @@ def _merge_resources( domain_resources.update(new_value) else: _LOGGER.error( - "An integration providing translations for %s provided invalid data: %s", + ( + "An integration providing translations for %s provided invalid" + " data: %s" + ), domain, new_value, ) diff --git a/homeassistant/loader.py b/homeassistant/loader.py index d43eecda778..da3f2aaa6d3 100644 --- a/homeassistant/loader.py +++ b/homeassistant/loader.py @@ -318,7 +318,11 @@ def async_process_zeroconf_match_dict(entry: dict[str, Any]) -> dict[str, Any]: for moved_prop in MOVED_ZEROCONF_PROPS: if value := entry_without_type.pop(moved_prop, None): _LOGGER.warning( - 'Matching the zeroconf property "%s" at top-level is deprecated and should be moved into a properties dict; Check the developer documentation', + ( + 'Matching the zeroconf property "%s" at top-level is deprecated and' + " should be moved into a properties dict; Check the developer" + " documentation" + ), moved_prop, ) if "properties" not in entry_without_type: @@ -489,9 +493,12 @@ class Integration: _LOGGER.warning(CUSTOM_WARNING, integration.domain) if integration.version is None: _LOGGER.error( - "The custom integration '%s' does not have a " - "version key in the manifest file and was blocked from loading. " - "See https://developers.home-assistant.io/blog/2021/01/29/custom-integration-changes#versions for more details", + ( + "The custom integration '%s' does not have a version key in the" + " manifest file and was blocked from loading. See" + " https://developers.home-assistant.io/blog/2021/01/29/custom-integration-changes#versions" + " for more details" + ), integration.domain, ) return None @@ -508,9 +515,12 @@ class Integration: ) except AwesomeVersionException: _LOGGER.error( - "The custom integration '%s' does not have a " - "valid version key (%s) in the manifest file and was blocked from loading. " - "See https://developers.home-assistant.io/blog/2021/01/29/custom-integration-changes#versions for more details", + ( + "The custom integration '%s' does not have a valid version key" + " (%s) in the manifest file and was blocked from loading. See" + " https://developers.home-assistant.io/blog/2021/01/29/custom-integration-changes#versions" + " for more details" + ), integration.domain, integration.version, ) @@ -683,14 +693,20 @@ class Integration: self._all_dependencies_resolved = True except IntegrationNotFound as err: _LOGGER.error( - "Unable to resolve dependencies for %s: we are unable to resolve (sub)dependency %s", + ( + "Unable to resolve dependencies for %s: we are unable to resolve" + " (sub)dependency %s" + ), self.domain, err.domain, ) self._all_dependencies_resolved = False except CircularDependency as err: _LOGGER.error( - "Unable to resolve dependencies for %s: it contains a circular dependency: %s -> %s", + ( + "Unable to resolve dependencies for %s: it contains a circular" + " dependency: %s -> %s" + ), self.domain, err.from_domain, err.to_domain, @@ -919,7 +935,7 @@ def _load_file( if str(err) not in white_listed_errors: _LOGGER.exception( - ("Error loading %s. Make sure all dependencies are installed"), path + "Error loading %s. Make sure all dependencies are installed", path ) return None diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index cf6cf03c472..c585c3c69e4 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -4,26 +4,26 @@ aiodiscover==1.4.13 aiohttp==3.8.1 aiohttp_cors==0.7.0 astral==2.2 -async-upnp-client==0.32.3 +async-upnp-client==0.33.0 async_timeout==4.0.2 atomicwrites-homeassistant==1.4.1 -attrs==21.2.0 +attrs==22.1.0 awesomeversion==22.9.0 bcrypt==3.1.7 -bleak-retry-connector==2.10.2 +bleak-retry-connector==2.13.0 bleak==0.19.2 -bluetooth-adapters==0.12.0 +bluetooth-adapters==0.15.2 bluetooth-auto-recovery==1.0.3 bluetooth-data-tools==0.3.1 certifi>=2021.5.30 -ciso8601==2.2.0 +ciso8601==2.3.0 cryptography==38.0.3 -dbus-fast==1.75.0 +dbus-fast==1.82.0 fnvhash==0.1.0 hass-nabucasa==0.61.0 -home-assistant-bluetooth==1.8.1 -home-assistant-frontend==20221213.1 -httpx==0.23.1 +home-assistant-bluetooth==1.9.2 +home-assistant-frontend==20230104.0 +httpx==0.23.2 ifaddr==0.1.7 janus==1.0.0 jinja2==3.1.2 @@ -44,7 +44,7 @@ typing-extensions>=4.4.0,<5.0 voluptuous-serialize==2.5.0 voluptuous==0.13.1 yarl==1.8.1 -zeroconf==0.39.4 +zeroconf==0.47.1 # Constrain pycryptodome to avoid vulnerability # see https://github.com/home-assistant/core/pull/16238 @@ -60,8 +60,8 @@ httplib2>=0.19.0 # gRPC is an implicit dependency that we want to make explicit so we manage # upgrades intentionally. It is a large package to build from source and we # want to ensure we have wheels built. -grpcio==1.48.0 -grpcio-status==1.48.0 +grpcio==1.51.1 +grpcio-status==1.51.1 # libcst >=0.4.0 requires a newer Rust than we currently have available, # thus our wheels builds fail. This pins it to the last working version, @@ -90,7 +90,7 @@ regex==2021.8.28 # requirements so we can directly link HA versions to these library versions. anyio==3.6.2 h11==0.14.0 -httpcore==0.16.2 +httpcore==0.16.3 # Ensure we have a hyperframe version that works in Python 3.10 # 5.2.0 fixed a collections abc deprecation diff --git a/homeassistant/requirements.py b/homeassistant/requirements.py index 27472a2bbd8..5710c313903 100644 --- a/homeassistant/requirements.py +++ b/homeassistant/requirements.py @@ -261,7 +261,10 @@ class RequirementsManager: for req in missing: if req in self.install_failure_history: _LOGGER.info( - "Multiple attempts to install %s failed, install will be retried after next configuration check or restart", + ( + "Multiple attempts to install %s failed, install will be" + " retried after next configuration check or restart" + ), req, ) raise RequirementsNotFound(integration, [req]) diff --git a/homeassistant/scripts/benchmark/__init__.py b/homeassistant/scripts/benchmark/__init__.py index efbfec5e961..d3165ad6cac 100644 --- a/homeassistant/scripts/benchmark/__init__.py +++ b/homeassistant/scripts/benchmark/__init__.py @@ -33,7 +33,7 @@ def run(args): # Disable logging logging.getLogger("homeassistant.core").setLevel(logging.CRITICAL) - parser = argparse.ArgumentParser(description=("Run a Home Assistant benchmark.")) + parser = argparse.ArgumentParser(description="Run a Home Assistant benchmark.") parser.add_argument("name", choices=BENCHMARKS) parser.add_argument("--script", choices=["benchmark"]) diff --git a/homeassistant/scripts/ensure_config.py b/homeassistant/scripts/ensure_config.py index f25639c7986..6dbda59522f 100644 --- a/homeassistant/scripts/ensure_config.py +++ b/homeassistant/scripts/ensure_config.py @@ -12,7 +12,7 @@ from homeassistant.core import HomeAssistant def run(args): """Handle ensure config commandline script.""" parser = argparse.ArgumentParser( - description=("Ensure a Home Assistant config exists, creates one if necessary.") + description="Ensure a Home Assistant config exists, creates one if necessary." ) parser.add_argument( "-c", diff --git a/homeassistant/scripts/macos/__init__.py b/homeassistant/scripts/macos/__init__.py index e6d128fa846..7668c73be9d 100644 --- a/homeassistant/scripts/macos/__init__.py +++ b/homeassistant/scripts/macos/__init__.py @@ -32,10 +32,7 @@ def install_osx(): os.popen(f"launchctl load -w -F {path}") - print( - "Home Assistant has been installed. \ - Open it here: http://localhost:8123" - ) + print("Home Assistant has been installed. Open it here: http://localhost:8123") def uninstall_osx(): diff --git a/homeassistant/setup.py b/homeassistant/setup.py index 1172ab00bd7..13467713e2d 100644 --- a/homeassistant/setup.py +++ b/homeassistant/setup.py @@ -253,8 +253,10 @@ async def _async_setup_component( result = await task except asyncio.TimeoutError: _LOGGER.error( - "Setup of %s is taking longer than %s seconds." - " Startup will proceed without waiting any longer", + ( + "Setup of %s is taking longer than %s seconds." + " Startup will proceed without waiting any longer" + ), domain, SLOW_SETUP_MAX_WAIT, ) diff --git a/homeassistant/strings.json b/homeassistant/strings.json index 86155be7b4d..1c38fb6d064 100644 --- a/homeassistant/strings.json +++ b/homeassistant/strings.json @@ -23,7 +23,7 @@ "via_hassio_addon": "{name} via Home Assistant add-on" }, "description": { - "confirm_setup": "Do you want to start set up?" + "confirm_setup": "Do you want to start setup?" }, "data": { "device": "Device", diff --git a/homeassistant/util/__init__.py b/homeassistant/util/__init__.py index 315c8ebda74..eb3dabe75a0 100644 --- a/homeassistant/util/__init__.py +++ b/homeassistant/util/__init__.py @@ -105,7 +105,7 @@ class Throttle: """A class for throttling the execution of tasks. This method decorator adds a cooldown to a method to prevent it from being - called more then 1 time within the timedelta interval `min_time` after it + called more than 1 time within the timedelta interval `min_time` after it returned its result. Calling a method a second time during the interval will return None. diff --git a/homeassistant/util/async_.py b/homeassistant/util/async_.py index e9c5a41062e..53c788436fe 100644 --- a/homeassistant/util/async_.py +++ b/homeassistant/util/async_.py @@ -100,7 +100,7 @@ def check_loop( """Warn if called inside the event loop. Raise if `strict` is True. The default advisory message is 'Use `await hass.async_add_executor_job()' - Set `advise_msg` to an alternate message if the the solution differs. + Set `advise_msg` to an alternate message if the solution differs. """ try: get_running_loop() @@ -155,8 +155,11 @@ def check_loop( extra = "" _LOGGER.warning( - "Detected blocking call to %s inside the event loop. This is causing stability issues. " - "Please report issue%s for %s doing blocking calls at %s, line %s: %s", + ( + "Detected blocking call to %s inside the event loop. This is causing" + " stability issues. Please report issue%s for %s doing blocking calls at" + " %s, line %s: %s" + ), func.__name__, extra, integration, @@ -166,9 +169,10 @@ def check_loop( ) if strict: raise RuntimeError( - "Blocking calls must be done in the executor or a separate thread; " - f"{advise_msg or 'Use `await hass.async_add_executor_job()`'}; " - f"at {found_frame.filename[index:]}, line {found_frame.lineno}: {(found_frame.line or '?').strip()}" + "Blocking calls must be done in the executor or a separate thread;" + f" {advise_msg or 'Use `await hass.async_add_executor_job()`'}; at" + f" {found_frame.filename[index:]}, line {found_frame.lineno}:" + f" {(found_frame.line or '?').strip()}" ) diff --git a/homeassistant/util/distance.py b/homeassistant/util/distance.py index 719379d4c61..509760fff19 100644 --- a/homeassistant/util/distance.py +++ b/homeassistant/util/distance.py @@ -3,7 +3,8 @@ from __future__ import annotations from collections.abc import Callable -from homeassistant.const import ( # pylint: disable=unused-import # noqa: F401 +# pylint: disable-next=unused-import,hass-deprecated-import +from homeassistant.const import ( # noqa: F401 LENGTH, LENGTH_CENTIMETERS, LENGTH_FEET, @@ -47,9 +48,11 @@ METERS_TO: dict[str, Callable[[float], float]] = { def convert(value: float, from_unit: str, to_unit: str) -> float: """Convert one unit of measurement to another.""" report( - "uses distance utility. This is deprecated since 2022.10 and will " - "stop working in Home Assistant 2023.4, it should be updated to use " - "unit_conversion.DistanceConverter instead", + ( + "uses distance utility. This is deprecated since 2022.10 and will " + "stop working in Home Assistant 2023.4, it should be updated to use " + "unit_conversion.DistanceConverter instead" + ), error_if_core=False, ) return DistanceConverter.convert(value, from_unit, to_unit) diff --git a/homeassistant/util/json.py b/homeassistant/util/json.py index 1413f6d9b15..eb71d9da7eb 100644 --- a/homeassistant/util/json.py +++ b/homeassistant/util/json.py @@ -84,7 +84,10 @@ def save_json( dump = _orjson_default_encoder json_data = _orjson_default_encoder(data) except TypeError as error: - msg = f"Failed to serialize to JSON: {filename}. Bad data at {format_unserializable_data(find_paths_unserializable_data(data, dump=dump))}" + formatted_data = format_unserializable_data( + find_paths_unserializable_data(data, dump=dump) + ) + msg = f"Failed to serialize to JSON: {filename}. Bad data at {formatted_data}" _LOGGER.error(msg) raise SerializationError(msg) from error diff --git a/homeassistant/util/pressure.py b/homeassistant/util/pressure.py index d6d0c79741f..eccd358ad81 100644 --- a/homeassistant/util/pressure.py +++ b/homeassistant/util/pressure.py @@ -1,7 +1,8 @@ """Pressure util functions.""" from __future__ import annotations -from homeassistant.const import ( # pylint: disable=unused-import # noqa: F401 +# pylint: disable-next=unused-import,hass-deprecated-import +from homeassistant.const import ( # noqa: F401 PRESSURE, PRESSURE_BAR, PRESSURE_CBAR, @@ -26,9 +27,11 @@ VALID_UNITS = PressureConverter.VALID_UNITS def convert(value: float, from_unit: str, to_unit: str) -> float: """Convert one unit of measurement to another.""" report( - "uses pressure utility. This is deprecated since 2022.10 and will " - "stop working in Home Assistant 2023.4, it should be updated to use " - "unit_conversion.PressureConverter instead", + ( + "uses pressure utility. This is deprecated since 2022.10 and will " + "stop working in Home Assistant 2023.4, it should be updated to use " + "unit_conversion.PressureConverter instead" + ), error_if_core=False, ) return PressureConverter.convert(value, from_unit, to_unit) diff --git a/homeassistant/util/speed.py b/homeassistant/util/speed.py index f531e2d78f7..de076701c55 100644 --- a/homeassistant/util/speed.py +++ b/homeassistant/util/speed.py @@ -1,7 +1,8 @@ """Distance util functions.""" from __future__ import annotations -from homeassistant.const import ( # pylint: disable=unused-import # noqa: F401 +# pylint: disable-next=unused-import,hass-deprecated-import +from homeassistant.const import ( # noqa: F401 SPEED, SPEED_FEET_PER_SECOND, SPEED_INCHES_PER_DAY, @@ -33,9 +34,11 @@ VALID_UNITS = SpeedConverter.VALID_UNITS def convert(value: float, from_unit: str, to_unit: str) -> float: """Convert one unit of measurement to another.""" report( - "uses speed utility. This is deprecated since 2022.10 and will " - "stop working in Home Assistant 2023.4, it should be updated to use " - "unit_conversion.SpeedConverter instead", + ( + "uses speed utility. This is deprecated since 2022.10 and will " + "stop working in Home Assistant 2023.4, it should be updated to use " + "unit_conversion.SpeedConverter instead" + ), error_if_core=False, ) return SpeedConverter.convert(value, from_unit, to_unit) diff --git a/homeassistant/util/temperature.py b/homeassistant/util/temperature.py index 0c2608eb4b5..409fecd1090 100644 --- a/homeassistant/util/temperature.py +++ b/homeassistant/util/temperature.py @@ -1,5 +1,6 @@ """Temperature util functions.""" -from homeassistant.const import ( # pylint: disable=unused-import # noqa: F401 +# pylint: disable-next=unused-import,hass-deprecated-import +from homeassistant.const import ( # noqa: F401 TEMP_CELSIUS, TEMP_FAHRENHEIT, TEMP_KELVIN, @@ -38,9 +39,11 @@ def convert( ) -> float: """Convert a temperature from one unit to another.""" report( - "uses temperature utility. This is deprecated since 2022.10 and will " - "stop working in Home Assistant 2023.4, it should be updated to use " - "unit_conversion.TemperatureConverter instead", + ( + "uses temperature utility. This is deprecated since 2022.10 and will " + "stop working in Home Assistant 2023.4, it should be updated to use " + "unit_conversion.TemperatureConverter instead" + ), error_if_core=False, ) if interval: diff --git a/homeassistant/util/unit_conversion.py b/homeassistant/util/unit_conversion.py index aa2782e423f..f9f4d78899a 100644 --- a/homeassistant/util/unit_conversion.py +++ b/homeassistant/util/unit_conversion.py @@ -3,19 +3,18 @@ from __future__ import annotations from homeassistant.const import ( UNIT_NOT_RECOGNIZED_TEMPLATE, - VOLUME_CUBIC_FEET, - VOLUME_CUBIC_METERS, - VOLUME_FLUID_OUNCE, - VOLUME_GALLONS, - VOLUME_LITERS, - VOLUME_MILLILITERS, + UnitOfDataRate, + UnitOfElectricCurrent, + UnitOfElectricPotential, UnitOfEnergy, + UnitOfInformation, UnitOfLength, UnitOfMass, UnitOfPower, UnitOfPressure, UnitOfSpeed, UnitOfTemperature, + UnitOfVolume, UnitOfVolumetricFlux, ) from homeassistant.exceptions import HomeAssistantError @@ -38,7 +37,8 @@ _DAYS_TO_SECS = 24 * _HRS_TO_SECS # 1 day = 24 hours = 86400 seconds # Mass conversion constants _POUND_TO_G = 453.59237 -_OUNCE_TO_G = _POUND_TO_G / 16 +_OUNCE_TO_G = _POUND_TO_G / 16 # 16 ounces to a pound +_STONE_TO_G = _POUND_TO_G * 14 # 14 pounds to a stone # Pressure conversion constants _STANDARD_GRAVITY = 9.80665 @@ -90,6 +90,28 @@ class BaseUnitConverter: return cls._UNIT_CONVERSION[from_unit] / cls._UNIT_CONVERSION[to_unit] +class DataRateConverter(BaseUnitConverter): + """Utility to convert data rate values.""" + + UNIT_CLASS = "data_rate" + NORMALIZED_UNIT = UnitOfDataRate.BITS_PER_SECOND + # Units in terms of bits + _UNIT_CONVERSION: dict[str, float] = { + UnitOfDataRate.BITS_PER_SECOND: 1, + UnitOfDataRate.KILOBITS_PER_SECOND: 1 / 1e3, + UnitOfDataRate.MEGABITS_PER_SECOND: 1 / 1e6, + UnitOfDataRate.GIGABITS_PER_SECOND: 1 / 1e9, + UnitOfDataRate.BYTES_PER_SECOND: 1 / 8, + UnitOfDataRate.KILOBYTES_PER_SECOND: 1 / 8e3, + UnitOfDataRate.MEGABYTES_PER_SECOND: 1 / 8e6, + UnitOfDataRate.GIGABYTES_PER_SECOND: 1 / 8e9, + UnitOfDataRate.KIBIBYTES_PER_SECOND: 1 / 2**13, + UnitOfDataRate.MEBIBYTES_PER_SECOND: 1 / 2**23, + UnitOfDataRate.GIBIBYTES_PER_SECOND: 1 / 2**33, + } + VALID_UNITS = set(UnitOfDataRate) + + class DistanceConverter(BaseUnitConverter): """Utility to convert distance values.""" @@ -117,6 +139,33 @@ class DistanceConverter(BaseUnitConverter): } +class ElectricCurrentConverter(BaseUnitConverter): + """Utility to convert electric current values.""" + + UNIT_CLASS = "electric_current" + NORMALIZED_UNIT = UnitOfElectricCurrent.AMPERE + _UNIT_CONVERSION: dict[str, float] = { + UnitOfElectricCurrent.AMPERE: 1, + UnitOfElectricCurrent.MILLIAMPERE: 1e3, + } + VALID_UNITS = set(UnitOfElectricCurrent) + + +class ElectricPotentialConverter(BaseUnitConverter): + """Utility to convert electric potential values.""" + + UNIT_CLASS = "voltage" + NORMALIZED_UNIT = UnitOfElectricPotential.VOLT + _UNIT_CONVERSION: dict[str, float] = { + UnitOfElectricPotential.VOLT: 1, + UnitOfElectricPotential.MILLIVOLT: 1e3, + } + VALID_UNITS = { + UnitOfElectricPotential.VOLT, + UnitOfElectricPotential.MILLIVOLT, + } + + class EnergyConverter(BaseUnitConverter): """Utility to convert energy values.""" @@ -136,6 +185,38 @@ class EnergyConverter(BaseUnitConverter): } +class InformationConverter(BaseUnitConverter): + """Utility to convert information values.""" + + UNIT_CLASS = "information" + NORMALIZED_UNIT = UnitOfInformation.BITS + # Units in terms of bits + _UNIT_CONVERSION: dict[str, float] = { + UnitOfInformation.BITS: 1, + UnitOfInformation.KILOBITS: 1 / 1e3, + UnitOfInformation.MEGABITS: 1 / 1e6, + UnitOfInformation.GIGABITS: 1 / 1e9, + UnitOfInformation.BYTES: 1 / 8, + UnitOfInformation.KILOBYTES: 1 / 8e3, + UnitOfInformation.MEGABYTES: 1 / 8e6, + UnitOfInformation.GIGABYTES: 1 / 8e9, + UnitOfInformation.TERABYTES: 1 / 8e12, + UnitOfInformation.PETABYTES: 1 / 8e15, + UnitOfInformation.EXABYTES: 1 / 8e18, + UnitOfInformation.ZETTABYTES: 1 / 8e21, + UnitOfInformation.YOTTABYTES: 1 / 8e24, + UnitOfInformation.KIBIBYTES: 1 / 2**13, + UnitOfInformation.MEBIBYTES: 1 / 2**23, + UnitOfInformation.GIBIBYTES: 1 / 2**33, + UnitOfInformation.TEBIBYTES: 1 / 2**43, + UnitOfInformation.PEBIBYTES: 1 / 2**53, + UnitOfInformation.EXBIBYTES: 1 / 2**63, + UnitOfInformation.ZEBIBYTES: 1 / 2**73, + UnitOfInformation.YOBIBYTES: 1 / 2**83, + } + VALID_UNITS = set(UnitOfInformation) + + class MassConverter(BaseUnitConverter): """Utility to convert mass values.""" @@ -148,6 +229,7 @@ class MassConverter(BaseUnitConverter): UnitOfMass.KILOGRAMS: 1 / 1000, UnitOfMass.OUNCES: 1 / _OUNCE_TO_G, UnitOfMass.POUNDS: 1 / _POUND_TO_G, + UnitOfMass.STONES: 1 / _STONE_TO_G, } VALID_UNITS = { UnitOfMass.GRAMS, @@ -156,6 +238,7 @@ class MassConverter(BaseUnitConverter): UnitOfMass.MICROGRAMS, UnitOfMass.OUNCES, UnitOfMass.POUNDS, + UnitOfMass.STONES, } @@ -332,21 +415,23 @@ class VolumeConverter(BaseUnitConverter): """Utility to convert volume values.""" UNIT_CLASS = "volume" - NORMALIZED_UNIT = VOLUME_CUBIC_METERS + NORMALIZED_UNIT = UnitOfVolume.CUBIC_METERS # Units in terms of m³ _UNIT_CONVERSION: dict[str, float] = { - VOLUME_LITERS: 1 / _L_TO_CUBIC_METER, - VOLUME_MILLILITERS: 1 / _ML_TO_CUBIC_METER, - VOLUME_GALLONS: 1 / _GALLON_TO_CUBIC_METER, - VOLUME_FLUID_OUNCE: 1 / _FLUID_OUNCE_TO_CUBIC_METER, - VOLUME_CUBIC_METERS: 1, - VOLUME_CUBIC_FEET: 1 / _CUBIC_FOOT_TO_CUBIC_METER, + UnitOfVolume.LITERS: 1 / _L_TO_CUBIC_METER, + UnitOfVolume.MILLILITERS: 1 / _ML_TO_CUBIC_METER, + UnitOfVolume.GALLONS: 1 / _GALLON_TO_CUBIC_METER, + UnitOfVolume.FLUID_OUNCES: 1 / _FLUID_OUNCE_TO_CUBIC_METER, + UnitOfVolume.CUBIC_METERS: 1, + UnitOfVolume.CUBIC_FEET: 1 / _CUBIC_FOOT_TO_CUBIC_METER, + UnitOfVolume.CENTUM_CUBIC_FEET: 1 / (100 * _CUBIC_FOOT_TO_CUBIC_METER), } VALID_UNITS = { - VOLUME_LITERS, - VOLUME_MILLILITERS, - VOLUME_GALLONS, - VOLUME_FLUID_OUNCE, - VOLUME_CUBIC_METERS, - VOLUME_CUBIC_FEET, + UnitOfVolume.LITERS, + UnitOfVolume.MILLILITERS, + UnitOfVolume.GALLONS, + UnitOfVolume.FLUID_OUNCES, + UnitOfVolume.CUBIC_METERS, + UnitOfVolume.CUBIC_FEET, + UnitOfVolume.CENTUM_CUBIC_FEET, } diff --git a/homeassistant/util/unit_system.py b/homeassistant/util/unit_system.py index 9c6b6045cf9..7aa910e90b2 100644 --- a/homeassistant/util/unit_system.py +++ b/homeassistant/util/unit_system.py @@ -84,14 +84,14 @@ class UnitSystem: self, name: str, *, - accumulated_precipitation: str, + accumulated_precipitation: UnitOfPrecipitationDepth, conversions: dict[tuple[SensorDeviceClass | str | None, str | None], str], - length: str, - mass: str, - pressure: str, - temperature: str, - volume: str, - wind_speed: str, + length: UnitOfLength, + mass: UnitOfMass, + pressure: UnitOfPressure, + temperature: UnitOfTemperature, + volume: UnitOfVolume, + wind_speed: UnitOfSpeed, ) -> None: """Initialize the unit system object.""" errors: str = ", ".join( @@ -125,9 +125,11 @@ class UnitSystem: def name(self) -> str: """Return the name of the unit system.""" report( - "accesses the `name` property of the unit system. " - "This is deprecated and will stop working in Home Assistant 2023.1. " - "Please adjust to use instance check instead.", + ( + "accesses the `name` property of the unit system. " + "This is deprecated and will stop working in Home Assistant 2023.1. " + "Please adjust to use instance check instead." + ), error_if_core=False, ) if self is IMPERIAL_SYSTEM: @@ -139,9 +141,11 @@ class UnitSystem: def is_metric(self) -> bool: """Determine if this is the metric unit system.""" report( - "accesses the `is_metric` property of the unit system. " - "This is deprecated and will stop working in Home Assistant 2023.1. " - "Please adjust to use instance check instead.", + ( + "accesses the `is_metric` property of the unit system. " + "This is deprecated and will stop working in Home Assistant 2023.1. " + "Please adjust to use instance check instead." + ), error_if_core=False, ) return self is METRIC_SYSTEM @@ -250,6 +254,12 @@ METRIC_SYSTEM = UnitSystem( _CONF_UNIT_SYSTEM_METRIC, accumulated_precipitation=UnitOfPrecipitationDepth.MILLIMETERS, conversions={ + # Force atmospheric pressures to hPa + **{ + ("atmospheric_pressure", unit): UnitOfPressure.HPA + for unit in UnitOfPressure + if unit != UnitOfPressure.HPA + }, # Convert non-metric distances ("distance", UnitOfLength.FEET): UnitOfLength.METERS, ("distance", UnitOfLength.INCHES): UnitOfLength.MILLIMETERS, @@ -259,6 +269,9 @@ METRIC_SYSTEM = UnitSystem( ("gas", UnitOfVolume.CUBIC_FEET): UnitOfVolume.CUBIC_METERS, # Convert non-metric precipitation ("precipitation", UnitOfLength.INCHES): UnitOfLength.MILLIMETERS, + # Convert non-metric pressure + ("pressure", UnitOfPressure.PSI): UnitOfPressure.KPA, + ("pressure", UnitOfPressure.INHG): UnitOfPressure.HPA, # Convert non-metric speeds except knots to km/h ("speed", UnitOfSpeed.FEET_PER_SECOND): UnitOfSpeed.KILOMETERS_PER_HOUR, ("speed", UnitOfSpeed.MILES_PER_HOUR): UnitOfSpeed.KILOMETERS_PER_HOUR, @@ -282,6 +295,12 @@ US_CUSTOMARY_SYSTEM = UnitSystem( _CONF_UNIT_SYSTEM_US_CUSTOMARY, accumulated_precipitation=UnitOfPrecipitationDepth.INCHES, conversions={ + # Force atmospheric pressures to inHg + **{ + ("atmospheric_pressure", unit): UnitOfPressure.INHG + for unit in UnitOfPressure + if unit != UnitOfPressure.INHG + }, # Convert non-USCS distances ("distance", UnitOfLength.CENTIMETERS): UnitOfLength.INCHES, ("distance", UnitOfLength.KILOMETERS): UnitOfLength.MILES, @@ -291,6 +310,14 @@ US_CUSTOMARY_SYSTEM = UnitSystem( ("gas", UnitOfVolume.CUBIC_METERS): UnitOfVolume.CUBIC_FEET, # Convert non-USCS precipitation ("precipitation", UnitOfLength.MILLIMETERS): UnitOfLength.INCHES, + # Convert non-USCS pressure + ("pressure", UnitOfPressure.MBAR): UnitOfPressure.PSI, + ("pressure", UnitOfPressure.CBAR): UnitOfPressure.PSI, + ("pressure", UnitOfPressure.BAR): UnitOfPressure.PSI, + ("pressure", UnitOfPressure.PA): UnitOfPressure.PSI, + ("pressure", UnitOfPressure.HPA): UnitOfPressure.PSI, + ("pressure", UnitOfPressure.KPA): UnitOfPressure.PSI, + ("pressure", UnitOfPressure.MMHG): UnitOfPressure.INHG, # Convert non-USCS speeds except knots to mph ("speed", UnitOfSpeed.METERS_PER_SECOND): UnitOfSpeed.MILES_PER_HOUR, ("speed", UnitOfSpeed.KILOMETERS_PER_HOUR): UnitOfSpeed.MILES_PER_HOUR, diff --git a/homeassistant/util/variance.py b/homeassistant/util/variance.py index 626b111817f..2e0835d1cfe 100644 --- a/homeassistant/util/variance.py +++ b/homeassistant/util/variance.py @@ -6,36 +6,39 @@ from datetime import datetime, timedelta import functools from typing import Any, TypeVar, overload -T = TypeVar("T", int, float, datetime) +from typing_extensions import ParamSpec + +_R = TypeVar("_R", int, float, datetime) +_P = ParamSpec("_P") @overload def ignore_variance( - func: Callable[..., int], ignored_variance: int -) -> Callable[..., int]: + func: Callable[_P, int], ignored_variance: int +) -> Callable[_P, int]: ... @overload def ignore_variance( - func: Callable[..., float], ignored_variance: float -) -> Callable[..., float]: + func: Callable[_P, float], ignored_variance: float +) -> Callable[_P, float]: ... @overload def ignore_variance( - func: Callable[..., datetime], ignored_variance: timedelta -) -> Callable[..., datetime]: + func: Callable[_P, datetime], ignored_variance: timedelta +) -> Callable[_P, datetime]: ... -def ignore_variance(func: Callable[..., T], ignored_variance: Any) -> Callable[..., T]: +def ignore_variance(func: Callable[_P, _R], ignored_variance: Any) -> Callable[_P, _R]: """Wrap a function that returns old result if new result does not vary enough.""" - last_value: T | None = None + last_value: _R | None = None @functools.wraps(func) - def wrapper(*args: Any, **kwargs: Any) -> T: + def wrapper(*args: _P.args, **kwargs: _P.kwargs) -> _R: nonlocal last_value value = func(*args, **kwargs) diff --git a/homeassistant/util/volume.py b/homeassistant/util/volume.py index e21cebd2982..7d70d23c00c 100644 --- a/homeassistant/util/volume.py +++ b/homeassistant/util/volume.py @@ -1,7 +1,8 @@ """Volume conversion util functions.""" from __future__ import annotations -from homeassistant.const import ( # pylint: disable=unused-import # noqa: F401 +# pylint: disable-next=unused-import,hass-deprecated-import +from homeassistant.const import ( # noqa: F401 UNIT_NOT_RECOGNIZED_TEMPLATE, VOLUME, VOLUME_CUBIC_FEET, @@ -41,9 +42,11 @@ def cubic_feet_to_cubic_meter(cubic_feet: float) -> float: def convert(volume: float, from_unit: str, to_unit: str) -> float: """Convert a volume from one unit to another.""" report( - "uses volume utility. This is deprecated since 2022.10 and will " - "stop working in Home Assistant 2023.4, it should be updated to use " - "unit_conversion.VolumeConverter instead", + ( + "uses volume utility. This is deprecated since 2022.10 and will " + "stop working in Home Assistant 2023.4, it should be updated to use " + "unit_conversion.VolumeConverter instead" + ), error_if_core=False, ) return VolumeConverter.convert(volume, from_unit, to_unit) diff --git a/homeassistant/util/yaml/loader.py b/homeassistant/util/yaml/loader.py index 62d754329c4..626cf65d1e2 100644 --- a/homeassistant/util/yaml/loader.py +++ b/homeassistant/util/yaml/loader.py @@ -85,7 +85,10 @@ class Secrets: _LOGGER.setLevel(logging.DEBUG) else: _LOGGER.error( - "Error in secrets.yaml: 'logger: debug' expected, but 'logger: %s' found", + ( + "Error in secrets.yaml: 'logger: debug' expected, but" + " 'logger: %s' found" + ), logger, ) del secrets["logger"] @@ -142,7 +145,7 @@ class SafeLineLoader(yaml.SafeLoader): def get_stream_name(self) -> str: """Get the name of the stream.""" - return self.stream.name or "" + return getattr(self.stream, "name", "") LoaderType = Union[SafeLineLoader, SafeLoader] diff --git a/mypy.ini b/mypy.ini index 5a6615d0f48..2e0c5d2ae0d 100644 --- a/mypy.ini +++ b/mypy.ini @@ -313,6 +313,16 @@ disallow_untyped_defs = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.analytics.*] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +warn_return_any = true +warn_unreachable = true + [mypy-homeassistant.components.anthemav.*] check_untyped_defs = true disallow_incomplete_defs = true @@ -673,6 +683,17 @@ disallow_untyped_defs = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.diagnostics.*] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +warn_return_any = true +warn_unreachable = true +no_implicit_reexport = true + [mypy-homeassistant.components.dlna_dmr.*] check_untyped_defs = true disallow_incomplete_defs = true @@ -1013,6 +1034,36 @@ disallow_untyped_defs = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.hardkernel.*] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +warn_return_any = true +warn_unreachable = true + +[mypy-homeassistant.components.hardware.*] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +warn_return_any = true +warn_unreachable = true + +[mypy-homeassistant.components.here_travel_time.*] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +warn_return_any = true +warn_unreachable = true + [mypy-homeassistant.components.history.*] check_untyped_defs = true disallow_incomplete_defs = true @@ -1043,6 +1094,36 @@ disallow_untyped_defs = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.homeassistant_hardware.*] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +warn_return_any = true +warn_unreachable = true + +[mypy-homeassistant.components.homeassistant_sky_connect.*] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +warn_return_any = true +warn_unreachable = true + +[mypy-homeassistant.components.homeassistant_yellow.*] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +warn_return_any = true +warn_unreachable = true + [mypy-homeassistant.components.homekit] check_untyped_defs = true disallow_incomplete_defs = true @@ -1273,7 +1354,7 @@ disallow_untyped_defs = true warn_return_any = true warn_unreachable = true -[mypy-homeassistant.components.image.*] +[mypy-homeassistant.components.image_processing.*] check_untyped_defs = true disallow_incomplete_defs = true disallow_subclassing_any = true @@ -1283,7 +1364,7 @@ disallow_untyped_defs = true warn_return_any = true warn_unreachable = true -[mypy-homeassistant.components.image_processing.*] +[mypy-homeassistant.components.image_upload.*] check_untyped_defs = true disallow_incomplete_defs = true disallow_subclassing_any = true @@ -1543,6 +1624,16 @@ disallow_untyped_defs = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.mastodon.*] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +warn_return_any = true +warn_unreachable = true + [mypy-homeassistant.components.matter.*] check_untyped_defs = true disallow_incomplete_defs = true @@ -1933,6 +2024,16 @@ disallow_untyped_defs = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.purpleair.*] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +warn_return_any = true +warn_unreachable = true + [mypy-homeassistant.components.pvoutput.*] check_untyped_defs = true disallow_incomplete_defs = true @@ -1973,6 +2074,16 @@ disallow_untyped_defs = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.raspberry_pi.*] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +warn_return_any = true +warn_unreachable = true + [mypy-homeassistant.components.rdw.*] check_untyped_defs = true disallow_incomplete_defs = true @@ -2213,6 +2324,16 @@ disallow_untyped_defs = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.simplepush.*] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +warn_return_any = true +warn_unreachable = true + [mypy-homeassistant.components.simplisafe.*] check_untyped_defs = true disallow_incomplete_defs = true @@ -2283,6 +2404,16 @@ disallow_untyped_defs = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.speedtestdotnet.*] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +warn_return_any = true +warn_unreachable = true + [mypy-homeassistant.components.ssdp.*] check_untyped_defs = true disallow_incomplete_defs = true @@ -2848,9 +2979,6 @@ warn_unreachable = true [mypy-homeassistant.components.application_credentials.*] no_implicit_reexport = true -[mypy-homeassistant.components.diagnostics.*] -no_implicit_reexport = true - [mypy-homeassistant.components.spotify.*] no_implicit_reexport = true diff --git a/pylint/plugins/hass_enforce_type_hints.py b/pylint/plugins/hass_enforce_type_hints.py index a60b1c7de85..1e3ab900793 100644 --- a/pylint/plugins/hass_enforce_type_hints.py +++ b/pylint/plugins/hass_enforce_type_hints.py @@ -732,7 +732,7 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { matches=[ TypeHintMatch( function_name="device_class", - return_type=["BinarySensorDeviceClass", "str", None], + return_type=["BinarySensorDeviceClass", None], ), TypeHintMatch( function_name="is_on", @@ -1086,7 +1086,7 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { matches=[ TypeHintMatch( function_name="device_class", - return_type=["CoverDeviceClass", "str", None], + return_type=["CoverDeviceClass", None], ), TypeHintMatch( function_name="current_cover_position", @@ -1372,6 +1372,10 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { function_name="confidence", return_type=["float", None], ), + TypeHintMatch( + function_name="device_class", + return_type=["ImageProcessingDeviceClass", None], + ), TypeHintMatch( function_name="process_image", arg_types={1: "bytes"}, @@ -1413,7 +1417,7 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { ), TypeHintMatch( function_name="device_class", - return_type=["HumidifierDeviceClass", "str", None], + return_type=["HumidifierDeviceClass", None], ), TypeHintMatch( function_name="min_humidity", @@ -1649,7 +1653,7 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { matches=[ TypeHintMatch( function_name="device_class", - return_type=["MediaPlayerDeviceClass", "str", None], + return_type=["MediaPlayerDeviceClass", None], ), TypeHintMatch( function_name="state", @@ -1987,7 +1991,7 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { matches=[ TypeHintMatch( function_name="device_class", - return_type=["NumberDeviceClass", "str", None], + return_type=["NumberDeviceClass", None], ), TypeHintMatch( function_name="capability_attributes", @@ -2153,7 +2157,7 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { matches=[ TypeHintMatch( function_name="device_class", - return_type=["SensorDeviceClass", "str", None], + return_type=["SensorDeviceClass", None], ), TypeHintMatch( function_name="state_class", @@ -2269,7 +2273,7 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { matches=[ TypeHintMatch( function_name="device_class", - return_type=["SwitchDeviceClass", "str", None], + return_type=["SwitchDeviceClass", None], ), ], ), @@ -2325,7 +2329,7 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { ), TypeHintMatch( function_name="device_class", - return_type=["UpdateDeviceClass", "str", None], + return_type=["UpdateDeviceClass", None], ), TypeHintMatch( function_name="in_progress", diff --git a/pylint/plugins/hass_imports.py b/pylint/plugins/hass_imports.py index 678773abcb9..0a9291ec5ec 100644 --- a/pylint/plugins/hass_imports.py +++ b/pylint/plugins/hass_imports.py @@ -260,16 +260,76 @@ _OBSOLETE_IMPORT: dict[str, list[ObsoleteImportMatch]] = { ], "homeassistant.const": [ ObsoleteImportMatch( - reason="replaced by SensorDeviceClass enum", - constant=re.compile(r"^DEVICE_CLASS_(\w*)$"), + reason="replaced by local constants", + constant=re.compile(r"^CONF_UNIT_SYSTEM_(\w+)$"), + ), + ObsoleteImportMatch( + reason="replaced by unit enums", + constant=re.compile(r"^DATA_(\w+)$"), + ), + ObsoleteImportMatch( + reason="replaced by ***DeviceClass enum", + constant=re.compile(r"^DEVICE_CLASS_(\w+)$"), + ), + ObsoleteImportMatch( + reason="replaced by unit enums", + constant=re.compile(r"^ELECTRIC_(\w+)$"), + ), + ObsoleteImportMatch( + reason="replaced by unit enums", + constant=re.compile(r"^ENERGY_(\w+)$"), ), ObsoleteImportMatch( reason="replaced by EntityCategory enum", - constant=re.compile(r"^(ENTITY_CATEGORY_(\w*))|(ENTITY_CATEGORIES)$"), + constant=re.compile(r"^(ENTITY_CATEGORY_(\w+))|(ENTITY_CATEGORIES)$"), ), ObsoleteImportMatch( - reason="replaced by local constants", - constant=re.compile(r"^(CONF_UNIT_SYSTEM_(\w*))$"), + reason="replaced by unit enums", + constant=re.compile(r"^FREQUENCY_(\w+)$"), + ), + ObsoleteImportMatch( + reason="replaced by unit enums", + constant=re.compile(r"^IRRADIATION_(\w+)$"), + ), + ObsoleteImportMatch( + reason="replaced by unit enums", + constant=re.compile(r"^LENGTH_(\w+)$"), + ), + ObsoleteImportMatch( + reason="replaced by unit enums", + constant=re.compile(r"^MASS_(\w+)$"), + ), + ObsoleteImportMatch( + reason="replaced by unit enums", + constant=re.compile(r"^POWER_(?!VOLT_AMPERE_REACTIVE)(\w+)$"), + ), + ObsoleteImportMatch( + reason="replaced by unit enums", + constant=re.compile(r"^PRECIPITATION_(\w+)$"), + ), + ObsoleteImportMatch( + reason="replaced by unit enums", + constant=re.compile(r"^PRESSURE_(\w+)$"), + ), + ObsoleteImportMatch( + reason="replaced by unit enums", + constant=re.compile(r"^SOUND_PRESSURE_(\w+)$"), + ), + ObsoleteImportMatch( + reason="replaced by unit enums", + constant=re.compile(r"^SPEED_(\w+)$"), + ), + ObsoleteImportMatch( + reason="replaced by unit enums", + constant=re.compile(r"^TEMP_(\w+)$"), + ), + ObsoleteImportMatch( + reason="replaced by unit enums", + constant=re.compile(r"^TIME_(\w+)$"), + ), + ObsoleteImportMatch( + reason="replaced by unit enums", + constant=re.compile(r"^VOLUME_(\w+)$"), ), ], "homeassistant.core": [ diff --git a/pyproject.toml b/pyproject.toml index 05fda7fa5a0..7d577d96705 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "homeassistant" -version = "2022.12.9" +version = "2023.1.0" license = {text = "Apache-2.0"} description = "Open-source home automation platform running on Python 3." readme = "README.rst" @@ -27,16 +27,16 @@ dependencies = [ "aiohttp==3.8.1", "astral==2.2", "async_timeout==4.0.2", - "attrs==21.2.0", + "attrs==22.1.0", "atomicwrites-homeassistant==1.4.1", "awesomeversion==22.9.0", "bcrypt==3.1.7", "certifi>=2021.5.30", - "ciso8601==2.2.0", + "ciso8601==2.3.0", # When bumping httpx, please check the version pins of # httpcore, anyio, and h11 in gen_requirements_all - "httpx==0.23.1", - "home-assistant-bluetooth==1.8.1", + "httpx==0.23.2", + "home-assistant-bluetooth==1.9.2", "ifaddr==0.1.7", "jinja2==3.1.2", "lru-dict==1.1.8", @@ -74,7 +74,7 @@ include = ["homeassistant*"] [tool.black] target-version = ["py39", "py310"] -exclude = 'generated' +extend-exclude = "/generated/" [tool.isort] # https://github.com/PyCQA/isort/wiki/isort-Settings diff --git a/requirements.txt b/requirements.txt index eca20f3047b..8438cedd87d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,14 +4,14 @@ aiohttp==3.8.1 astral==2.2 async_timeout==4.0.2 -attrs==21.2.0 +attrs==22.1.0 atomicwrites-homeassistant==1.4.1 awesomeversion==22.9.0 bcrypt==3.1.7 certifi>=2021.5.30 -ciso8601==2.2.0 -httpx==0.23.1 -home-assistant-bluetooth==1.8.1 +ciso8601==2.3.0 +httpx==0.23.2 +home-assistant-bluetooth==1.9.2 ifaddr==0.1.7 jinja2==3.1.2 lru-dict==1.1.8 diff --git a/requirements_all.txt b/requirements_all.txt index a3bd7cfe7f0..2777f4c5226 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -10,6 +10,9 @@ AIOAladdinConnect==0.1.48 # homeassistant.components.adax Adax-local==0.1.5 +# homeassistant.components.homekit +HAP-python==4.6.0 + # homeassistant.components.mastodon Mastodon.py==1.5.1 @@ -37,7 +40,7 @@ PyRMVtransport==0.3.3 PySocks==1.7.1 # homeassistant.components.switchbot -PySwitchbot==0.23.2 +PySwitchbot==0.36.1 # homeassistant.components.transport_nsw PyTransportNSW==0.1.1 @@ -47,7 +50,7 @@ PyTransportNSW==0.1.1 PyTurboJPEG==1.6.7 # homeassistant.components.vicare -PyViCare==2.19.0 +PyViCare==2.21.0 # homeassistant.components.xiaomi_aqara PyXiaomiGateway==0.14.3 @@ -83,7 +86,7 @@ adb-shell[async]==0.4.3 adext==0.4.2 # homeassistant.components.adguard -adguardhome==0.5.1 +adguardhome==0.6.1 # homeassistant.components.advantage_air advantage_air==0.4.1 @@ -116,7 +119,7 @@ aio_georss_gdacs==0.7 aioairq==0.2.4 # homeassistant.components.airzone -aioairzone==0.5.1 +aioairzone==0.5.2 # homeassistant.components.ambient_station aioambient==2021.11.0 @@ -131,7 +134,7 @@ aioasuswrt==1.4.0 aioazuredevops==1.3.5 # homeassistant.components.baf -aiobafi6==0.7.2 +aiobafi6==0.7.3 # homeassistant.components.aws aiobotocore==2.1.0 @@ -174,7 +177,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.4.1 +aiohomekit==2.4.3 # homeassistant.components.emulated_hue # homeassistant.components.http @@ -199,13 +202,13 @@ aiolifx==0.8.7 aiolifx_effects==0.3.1 # homeassistant.components.lifx -aiolifx_themes==0.2.0 +aiolifx_themes==0.4.0 # homeassistant.components.livisi aiolivisi==0.0.14 # homeassistant.components.lookin -aiolookin==0.1.1 +aiolookin==1.0.0 # homeassistant.components.lyric aiolyric==1.0.9 @@ -234,6 +237,9 @@ aioopenexchangerates==0.4.0 # homeassistant.components.acmeda aiopulse==0.4.3 +# homeassistant.components.purpleair +aiopurpleair==2022.12.1 + # homeassistant.components.hunterdouglas_powerview aiopvapi==2.0.4 @@ -246,7 +252,7 @@ aiopvpc==3.0.0 aiopyarr==22.11.0 # homeassistant.components.qnap_qsw -aioqsw==0.2.2 +aioqsw==0.3.1 # homeassistant.components.recollect_waste aiorecollect==1.0.8 @@ -261,7 +267,7 @@ aiosenseme==0.6.1 aiosenz==1.0.0 # homeassistant.components.shelly -aioshelly==5.1.2 +aioshelly==5.2.0 # homeassistant.components.skybell aioskybell==22.7.0 @@ -282,7 +288,7 @@ aiosyncthing==0.5.1 aiotractive==0.5.5 # homeassistant.components.unifi -aiounifi==42 +aiounifi==43 # homeassistant.components.vlc_telnet aiovlc==0.1.0 @@ -333,7 +339,7 @@ anthemav==1.4.1 apcaccess==0.0.13 # homeassistant.components.apprise -apprise==1.2.0 +apprise==1.2.1 # homeassistant.components.aprs aprslib==0.7.0 @@ -362,7 +368,7 @@ asterisk_mbox==0.5.0 # homeassistant.components.ssdp # homeassistant.components.upnp # homeassistant.components.yeelight -async-upnp-client==0.32.3 +async-upnp-client==0.33.0 # homeassistant.components.supla asyncpysupla==0.0.5 @@ -422,7 +428,7 @@ bimmer_connected==0.10.4 bizkaibus==0.1.1 # homeassistant.components.bluetooth -bleak-retry-connector==2.10.2 +bleak-retry-connector==2.13.0 # homeassistant.components.bluetooth bleak==0.19.2 @@ -447,7 +453,7 @@ bluemaestro-ble==0.2.0 # bluepy==1.3.0 # homeassistant.components.bluetooth -bluetooth-adapters==0.12.0 +bluetooth-adapters==0.15.2 # homeassistant.components.bluetooth bluetooth-auto-recovery==1.0.3 @@ -470,7 +476,7 @@ boto3==1.20.24 broadlink==0.18.3 # homeassistant.components.brother -brother==2.0.0 +brother==2.1.1 # homeassistant.components.brottsplatskartan brottsplatskartan==0.0.1 @@ -482,7 +488,7 @@ brunt==1.2.0 bt_proximity==0.2.1 # homeassistant.components.bthome -bthome-ble==2.3.1 +bthome-ble==2.4.0 # homeassistant.components.bt_home_hub_5 bthomehub5-devicelist==0.1.1 @@ -553,10 +559,10 @@ datadog==0.15.0 datapoint==0.9.8 # homeassistant.components.bluetooth -dbus-fast==1.75.0 +dbus-fast==1.82.0 # homeassistant.components.debugpy -debugpy==1.6.3 +debugpy==1.6.4 # homeassistant.components.decora # decora==0.6 @@ -582,7 +588,7 @@ denonavr==0.10.12 devolo-home-control-api==0.18.2 # homeassistant.components.devolo_home_network -devolo-plc-api==0.8.0 +devolo-plc-api==0.9.0 # homeassistant.components.directv directv==0.4.0 @@ -627,7 +633,7 @@ elgato==3.0.0 eliqonline==1.2.2 # homeassistant.components.elkm1 -elkm1-lib==2.0.2 +elkm1-lib==2.2.1 # homeassistant.components.elmax elmax_api==0.0.2 @@ -734,11 +740,11 @@ fritzconnection==1.10.3 # homeassistant.components.google_translate gTTS==2.2.4 -# homeassistant.components.garages_amsterdam -garages-amsterdam==3.0.0 +# homeassistant.components.google_assistant_sdk +gassist-text==0.0.7 # homeassistant.components.google -gcal-sync==4.0.4 +gcal-sync==4.1.0 # homeassistant.components.geniushub geniushub-client==0.6.30 @@ -758,6 +764,7 @@ georss_ign_sismologia_client==0.3 # homeassistant.components.qld_bushfire georss_qld_bushfire_alert_client==0.5 +# homeassistant.components.dlna_dmr # homeassistant.components.kef # homeassistant.components.minecraft_server # homeassistant.components.nmap_tracker @@ -766,7 +773,7 @@ georss_qld_bushfire_alert_client==0.5 getmac==0.8.2 # homeassistant.components.gios -gios==2.1.0 +gios==2.3.0 # homeassistant.components.gitter gitterpy==0.1.7 @@ -781,7 +788,7 @@ goalzero==0.2.1 goodwe==0.2.18 # homeassistant.components.google_pubsub -google-cloud-pubsub==2.13.10 +google-cloud-pubsub==2.13.11 # homeassistant.components.google_cloud google-cloud-texttospeech==2.12.3 @@ -796,7 +803,7 @@ googlemaps==2.5.1 goslide-api==0.5.1 # homeassistant.components.govee_ble -govee-ble==0.19.3 +govee-ble==0.21.0 # homeassistant.components.remote_rpi_gpio gpiozero==1.6.2 @@ -831,9 +838,6 @@ guppy3==3.1.2 # homeassistant.components.iaqualink h2==4.1.0 -# homeassistant.components.homekit -ha-HAP-python==4.5.2 - # homeassistant.components.generic # homeassistant.components.stream ha-av==10.0.0 @@ -863,10 +867,10 @@ hdate==0.10.4 heatmiserV3==1.1.18 # homeassistant.components.here_travel_time -here_routing==0.1.1 +here_routing==0.2.0 # homeassistant.components.here_travel_time -here_transit==1.0.0 +here_transit==1.2.0 # homeassistant.components.hikvisioncam hikvision==0.4 @@ -878,19 +882,19 @@ hkavr==0.0.5 hlk-sw16==0.0.9 # homeassistant.components.pi_hole -hole==0.7.0 +hole==0.8.0 # homeassistant.components.workday holidays==0.17.2 # homeassistant.components.frontend -home-assistant-frontend==20221213.1 +home-assistant-frontend==20230104.0 # homeassistant.components.home_connect homeconnect==0.7.2 # homeassistant.components.homematicip_cloud -homematicip==1.0.11 +homematicip==1.0.13 # homeassistant.components.home_plus_control homepluscontrol==0.0.5 @@ -926,7 +930,7 @@ ibm-watson==5.2.2 ibmiotf==0.3.4 # homeassistant.components.local_calendar -ical==4.2.4 +ical==4.2.8 # homeassistant.components.ping icmplib==3.0 @@ -1052,7 +1056,7 @@ london-tube-status==0.5 luftdaten==0.7.4 # homeassistant.components.lupusec -lupupy==0.2.3 +lupupy==0.2.4 # homeassistant.components.lw12wifi lw12==0.9.2 @@ -1106,7 +1110,7 @@ mill-local==0.2.0 millheater==0.10.0 # homeassistant.components.minio -minio==5.0.10 +minio==7.1.12 # homeassistant.components.moat moat-ble==0.1.1 @@ -1115,7 +1119,7 @@ moat-ble==0.1.1 moehlenhoff-alpha2==1.2.1 # homeassistant.components.motion_blinds -motionblinds==0.6.13 +motionblinds==0.6.15 # homeassistant.components.motioneye motioneye-client==0.3.12 @@ -1151,7 +1155,7 @@ netdisco==3.0.0 netmap==0.7.0.2 # homeassistant.components.nam -nettigo-air-monitor==1.5.0 +nettigo-air-monitor==1.6.0 # homeassistant.components.neurio_energy neurio==0.3.1 @@ -1169,7 +1173,7 @@ nextcord==2.0.0a8 nextdns==1.2.2 # homeassistant.components.nibe_heatpump -nibe==1.3.0 +nibe==1.6.0 # homeassistant.components.niko_home_control niko-home-control==0.2.1 @@ -1215,6 +1219,9 @@ oauth2client==4.1.3 # homeassistant.components.profiler objgraph==3.5.0 +# homeassistant.components.garages_amsterdam +odp-amsterdam==5.0.0 + # homeassistant.components.oem oemthermostat==1.1.1 @@ -1314,7 +1321,7 @@ pilight==0.1.1 # homeassistant.components.doods # homeassistant.components.generic -# homeassistant.components.image +# homeassistant.components.image_upload # homeassistant.components.proxy # homeassistant.components.qrcode # homeassistant.components.seven_segments @@ -1326,7 +1333,7 @@ pillow==9.3.0 pizzapi==0.0.3 # homeassistant.components.plex -plexapi==4.13.1 +plexapi==4.13.2 # homeassistant.components.plex plexauth==0.0.6 @@ -1432,7 +1439,7 @@ pyRFXtrx==0.30.0 pySwitchmate==0.5.1 # homeassistant.components.tibber -pyTibber==0.26.6 +pyTibber==0.26.7 # homeassistant.components.dlink pyW215==0.7.0 @@ -1456,7 +1463,8 @@ pyaftership==21.11.0 pyairnow==1.1.0 # homeassistant.components.airvisual -pyairvisual==2022.11.1 +# homeassistant.components.airvisual_pro +pyairvisual==2022.12.1 # homeassistant.components.almond pyalmond==0.0.2 @@ -1465,7 +1473,7 @@ pyalmond==0.0.2 pyatag==0.3.5.3 # homeassistant.components.netatmo -pyatmo==7.4.0 +pyatmo==7.5.0 # homeassistant.components.atome pyatome==0.1.1 @@ -1492,10 +1500,10 @@ pyblackbird==0.5 pybotvac==0.0.23 # homeassistant.components.braviatv -pybravia==0.2.3 +pybravia==0.2.5 # homeassistant.components.nissan_leaf -pycarwings2==2.13 +pycarwings2==2.14 # homeassistant.components.cloudflare pycfdns==2.0.1 @@ -1516,7 +1524,7 @@ pycmus==0.1.1 pycocotools==2.0.1 # homeassistant.components.comfoconnect -pycomfoconnect==0.4 +pycomfoconnect==0.5.1 # homeassistant.components.coolmaster pycoolmasternet-async==0.1.2 @@ -1534,7 +1542,7 @@ pydaikin==2.8.0 pydanfossair==0.1.0 # homeassistant.components.deconz -pydeconz==105 +pydeconz==106 # homeassistant.components.delijn pydelijn==1.0.0 @@ -1552,7 +1560,7 @@ pydroid-ipcam==2.0.0 pyebox==1.1.4 # homeassistant.components.econet -pyeconet==0.1.15 +pyeconet==0.1.17 # homeassistant.components.edimax pyedimax==0.2.1 @@ -1666,7 +1674,7 @@ pyirishrail==0.0.2 pyiss==1.0.1 # homeassistant.components.isy994 -pyisy==3.0.8 +pyisy==3.0.10 # homeassistant.components.itach pyitachip2ir==0.0.7 @@ -1711,7 +1719,7 @@ pylibrespot-java==0.1.1 pylitejet==0.3.0 # homeassistant.components.litterrobot -pylitterbot==2022.11.0 +pylitterbot==2022.12.0 # homeassistant.components.lutron_caseta pylutron-caseta==0.17.1 @@ -1759,13 +1767,13 @@ pymyq==3.1.4 pymysensors==0.24.0 # homeassistant.components.netgear -pynetgear==0.10.8 +pynetgear==0.10.9 # homeassistant.components.netio pynetio==0.1.9.1 # homeassistant.components.nina -pynina==0.1.8 +pynina==0.2.0 # homeassistant.components.nobo_hub pynobo==1.6.0 @@ -1809,10 +1817,10 @@ pyotgw==2.1.3 # homeassistant.auth.mfa_modules.notify # homeassistant.auth.mfa_modules.totp # homeassistant.components.otp -pyotp==2.7.0 +pyotp==2.8.0 # homeassistant.components.overkiz -pyoverkiz==1.7.2 +pyoverkiz==1.7.3 # homeassistant.components.openweathermap pyowm==3.2.0 @@ -1824,7 +1832,7 @@ pyownet==0.10.0.post1 pypca==0.0.7 # homeassistant.components.lcn -pypck==0.7.15 +pypck==0.7.16 # homeassistant.components.pjlink pypjlink2==1.2.1 @@ -1857,7 +1865,7 @@ pyqwikswitch==0.93 pyrail==0.0.3 # homeassistant.components.rainbird -pyrainbird==0.6.3 +pyrainbird==0.7.1 # homeassistant.components.recswitch pyrecswitch==1.0.2 @@ -1931,7 +1939,7 @@ pysmarty==0.8 pysml==0.0.8 # homeassistant.components.snmp -pysnmplib==5.0.15 +pysnmplib==5.0.20 # homeassistant.components.snooz pysnooz==0.8.3 @@ -1952,7 +1960,7 @@ pystiebeleltron==0.0.1.dev2 pysuez==0.1.19 # homeassistant.components.switchbee -pyswitchbee==1.6.1 +pyswitchbee==1.7.3 # homeassistant.components.syncthru pysyncthru==0.7.10 @@ -1997,7 +2005,7 @@ python-family-hub-local==0.0.2 python-forecastio==1.4.0 # homeassistant.components.fully_kiosk -python-fullykiosk==0.0.11 +python-fullykiosk==0.0.12 # homeassistant.components.sms # python-gammu==3.2.4 @@ -2030,7 +2038,7 @@ python-kasa==0.5.0 # python-lirc==1.2.3 # homeassistant.components.matter -python-matter-server==1.0.7 +python-matter-server==1.0.8 # homeassistant.components.xiaomi_miio python-miio==0.5.12 @@ -2181,6 +2189,9 @@ regenmaschine==2022.11.0 # homeassistant.components.renault renault-api==0.1.11 +# homeassistant.components.reolink +reolink-aio==0.1.1 + # homeassistant.components.python_script restrictedpython==5.2 @@ -2209,7 +2220,7 @@ rokuecp==0.17.0 roombapy==1.6.5 # homeassistant.components.roon -roonapi==0.1.1 +roonapi==0.1.2 # homeassistant.components.rova rova==0.3.0 @@ -2244,9 +2255,6 @@ satel_integra==0.3.7 # homeassistant.components.dhcp scapy==2.4.5 -# homeassistant.components.deutsche_bahn -schiene==0.23 - # homeassistant.components.screenlogic screenlogicpy==0.5.4 @@ -2267,13 +2275,13 @@ sense_energy==0.11.0 sensirion-ble==0.0.1 # homeassistant.components.sensorpro -sensorpro-ble==0.5.0 +sensorpro-ble==0.5.1 # homeassistant.components.sensorpush sensorpush-ble==1.5.2 # homeassistant.components.sentry -sentry-sdk==1.11.0 +sentry-sdk==1.12.1 # homeassistant.components.sharkiq sharkiq==0.0.1 @@ -2300,7 +2308,7 @@ sisyphus-control==3.1.2 slackclient==2.5.0 # homeassistant.components.xmpp -slixmpp==1.8.2 +slixmpp==1.8.3 # homeassistant.components.smart_meter_texas smart-meter-texas==0.4.7 @@ -2339,7 +2347,7 @@ speedtest-cli==2.1.3 spiderpy==1.6.1 # homeassistant.components.spotify -spotipy==2.21.0 +spotipy==2.22.0 # homeassistant.components.recorder # homeassistant.components.sql @@ -2427,7 +2435,7 @@ tesla-wall-connector==1.0.2 # tf-models-official==2.5.0 # homeassistant.components.thermobeacon -thermobeacon-ble==0.4.0 +thermobeacon-ble==0.6.0 # homeassistant.components.thermopro thermopro-ble==0.4.3 @@ -2463,7 +2471,7 @@ total_connect_client==2022.10 tp-connected==0.0.4 # homeassistant.components.transmission -transmissionrpc==0.11 +transmission-rpc==3.4.0 # homeassistant.components.twinkly ttls==1.5.1 @@ -2507,7 +2515,7 @@ url-normalize==1.4.3 uvcclient==0.11.0 # homeassistant.components.vallox -vallox-websocket-api==2.12.0 +vallox-websocket-api==3.0.0 # homeassistant.components.rdw vehicle==0.4.0 @@ -2550,7 +2558,7 @@ wallbox==0.4.12 waqiasync==1.0.0 # homeassistant.components.folder_watcher -watchdog==2.1.9 +watchdog==2.2.0 # homeassistant.components.waterfurnace waterfurnace==1.1.0 @@ -2574,7 +2582,7 @@ wirelesstagpy==0.8.1 withings-api==2.4.0 # homeassistant.components.wled -wled==0.14.1 +wled==0.15.0 # homeassistant.components.wolflink wolf_smartset==0.1.11 @@ -2589,7 +2597,7 @@ xboxapi==2.0.1 xiaomi-ble==0.12.2 # homeassistant.components.knx -xknx==2.1.0 +xknx==2.2.0 # homeassistant.components.bluesound # homeassistant.components.fritz @@ -2606,13 +2614,13 @@ xs1-api-client==3.0.0 yalesmartalarmclient==0.3.9 # homeassistant.components.yalexs_ble -yalexs-ble==1.10.2 +yalexs-ble==1.12.5 # homeassistant.components.august yalexs==1.2.6 # homeassistant.components.august -yalexs_ble==1.10.2 +yalexs_ble==1.12.5 # homeassistant.components.yeelight yeelight==0.7.10 @@ -2630,16 +2638,16 @@ youless-api==0.16 youtube_dl==2021.12.17 # homeassistant.components.zamg -zamg==0.1.1 +zamg==0.2.2 # homeassistant.components.zengge zengge==0.2 # homeassistant.components.zeroconf -zeroconf==0.39.4 +zeroconf==0.47.1 # homeassistant.components.zha -zha-quirks==0.0.89 +zha-quirks==0.0.90 # homeassistant.components.zhong_hong zhong_hong_hvac==1.0.9 @@ -2666,7 +2674,7 @@ zigpy==0.52.3 zm-py==0.5.2 # homeassistant.components.zwave_js -zwave-js-server-python==0.43.1 +zwave-js-server-python==0.44.0 # homeassistant.components.zwave_me zwave_me_ws==0.3.0 diff --git a/requirements_test.txt b/requirements_test.txt index 2bed839649c..72720b04a4c 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -9,21 +9,22 @@ -r requirements_test_pre_commit.txt astroid==2.12.13 codecov==2.1.12 -coverage==6.4.4 +coverage==7.0.0 freezegun==1.2.2 mock-open==1.4.0 mypy==0.991 pre-commit==2.20.0 -pylint==2.15.7 +pylint==2.15.8 pipdeptree==2.3.1 pytest-asyncio==0.20.2 pytest-aiohttp==1.0.4 pytest-cov==3.0.0 -pytest-freezegun==0.4.2 +pytest-freezer==0.4.6 pytest-socket==0.5.1 pytest-test-groups==1.0.3 pytest-sugar==0.9.5 pytest-timeout==2.1.0 +pytest-unordered==0.5.2 pytest-xdist==2.5.0 pytest==7.2.0 requests_mock==1.10.0 @@ -32,18 +33,17 @@ stdlib-list==0.7.0 tomli==2.0.1;python_version<"3.11" tqdm==4.64.0 types-atomicwrites==1.4.1 -types-croniter==1.0.0 +types-croniter==1.0.6 types-backports==0.1.3 -types-certifi==0.1.4 types-chardet==0.1.5 -types-decorator==0.1.7 -types-enum34==0.1.8 -types-ipaddress==0.1.5 +types-decorator==5.1.1 +types-enum34==1.1.8 +types-ipaddress==1.0.8 +types-paho-mqtt==1.6.0.1 types-pkg-resources==0.1.3 -types-python-dateutil==2.8.19.2 +types-python-dateutil==2.8.19.5 types-python-slugify==0.1.2 -types-pytz==2021.1.2 -types-PyYAML==5.4.6 -types-requests==2.28.0 -types-toml==0.1.5 -types-ujson==0.1.1 +types-pytz==2022.7.0.0 +types-PyYAML==6.0.12.2 +types-requests==2.28.11.6 +types-toml==0.10.8.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index b0fc670a396..75dd5bbf45d 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -12,6 +12,9 @@ AIOAladdinConnect==0.1.48 # homeassistant.components.adax Adax-local==0.1.5 +# homeassistant.components.homekit +HAP-python==4.6.0 + # homeassistant.components.flick_electric PyFlick==0.0.2 @@ -33,7 +36,7 @@ PyRMVtransport==0.3.3 PySocks==1.7.1 # homeassistant.components.switchbot -PySwitchbot==0.23.2 +PySwitchbot==0.36.1 # homeassistant.components.transport_nsw PyTransportNSW==0.1.1 @@ -43,7 +46,7 @@ PyTransportNSW==0.1.1 PyTurboJPEG==1.6.7 # homeassistant.components.vicare -PyViCare==2.19.0 +PyViCare==2.21.0 # homeassistant.components.xiaomi_aqara PyXiaomiGateway==0.14.3 @@ -73,7 +76,7 @@ adb-shell[async]==0.4.3 adext==0.4.2 # homeassistant.components.adguard -adguardhome==0.5.1 +adguardhome==0.6.1 # homeassistant.components.advantage_air advantage_air==0.4.1 @@ -103,7 +106,7 @@ aio_georss_gdacs==0.7 aioairq==0.2.4 # homeassistant.components.airzone -aioairzone==0.5.1 +aioairzone==0.5.2 # homeassistant.components.ambient_station aioambient==2021.11.0 @@ -118,7 +121,7 @@ aioasuswrt==1.4.0 aioazuredevops==1.3.5 # homeassistant.components.baf -aiobafi6==0.7.2 +aiobafi6==0.7.3 # homeassistant.components.aws aiobotocore==2.1.0 @@ -158,7 +161,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==2.4.1 +aiohomekit==2.4.3 # homeassistant.components.emulated_hue # homeassistant.components.http @@ -177,13 +180,13 @@ aiolifx==0.8.7 aiolifx_effects==0.3.1 # homeassistant.components.lifx -aiolifx_themes==0.2.0 +aiolifx_themes==0.4.0 # homeassistant.components.livisi aiolivisi==0.0.14 # homeassistant.components.lookin -aiolookin==0.1.1 +aiolookin==1.0.0 # homeassistant.components.lyric aiolyric==1.0.9 @@ -209,6 +212,9 @@ aioopenexchangerates==0.4.0 # homeassistant.components.acmeda aiopulse==0.4.3 +# homeassistant.components.purpleair +aiopurpleair==2022.12.1 + # homeassistant.components.hunterdouglas_powerview aiopvapi==2.0.4 @@ -221,7 +227,7 @@ aiopvpc==3.0.0 aiopyarr==22.11.0 # homeassistant.components.qnap_qsw -aioqsw==0.2.2 +aioqsw==0.3.1 # homeassistant.components.recollect_waste aiorecollect==1.0.8 @@ -236,7 +242,7 @@ aiosenseme==0.6.1 aiosenz==1.0.0 # homeassistant.components.shelly -aioshelly==5.1.2 +aioshelly==5.2.0 # homeassistant.components.skybell aioskybell==22.7.0 @@ -257,7 +263,7 @@ aiosyncthing==0.5.1 aiotractive==0.5.5 # homeassistant.components.unifi -aiounifi==42 +aiounifi==43 # homeassistant.components.vlc_telnet aiovlc==0.1.0 @@ -299,7 +305,7 @@ anthemav==1.4.1 apcaccess==0.0.13 # homeassistant.components.apprise -apprise==1.2.0 +apprise==1.2.1 # homeassistant.components.aprs aprslib==0.7.0 @@ -316,7 +322,7 @@ arcam-fmj==1.0.1 # homeassistant.components.ssdp # homeassistant.components.upnp # homeassistant.components.yeelight -async-upnp-client==0.32.3 +async-upnp-client==0.33.0 # homeassistant.components.sleepiq asyncsleepiq==1.2.3 @@ -346,7 +352,7 @@ bellows==0.34.5 bimmer_connected==0.10.4 # homeassistant.components.bluetooth -bleak-retry-connector==2.10.2 +bleak-retry-connector==2.13.0 # homeassistant.components.bluetooth bleak==0.19.2 @@ -361,7 +367,7 @@ blinkpy==0.19.2 bluemaestro-ble==0.2.0 # homeassistant.components.bluetooth -bluetooth-adapters==0.12.0 +bluetooth-adapters==0.15.2 # homeassistant.components.bluetooth bluetooth-auto-recovery==1.0.3 @@ -380,13 +386,13 @@ boschshcpy==0.2.35 broadlink==0.18.3 # homeassistant.components.brother -brother==2.0.0 +brother==2.1.1 # homeassistant.components.brunt brunt==1.2.0 # homeassistant.components.bthome -bthome-ble==2.3.1 +bthome-ble==2.4.0 # homeassistant.components.buienradar buienradar==1.0.5 @@ -433,10 +439,10 @@ datadog==0.15.0 datapoint==0.9.8 # homeassistant.components.bluetooth -dbus-fast==1.75.0 +dbus-fast==1.82.0 # homeassistant.components.debugpy -debugpy==1.6.3 +debugpy==1.6.4 # homeassistant.components.ihc # homeassistant.components.namecheapdns @@ -456,7 +462,7 @@ denonavr==0.10.12 devolo-home-control-api==0.18.2 # homeassistant.components.devolo_home_network -devolo-plc-api==0.8.0 +devolo-plc-api==0.9.0 # homeassistant.components.directv directv==0.4.0 @@ -480,7 +486,7 @@ eagle100==0.1.1 elgato==3.0.0 # homeassistant.components.elkm1 -elkm1-lib==2.0.2 +elkm1-lib==2.2.1 # homeassistant.components.elmax elmax_api==0.0.2 @@ -550,11 +556,11 @@ fritzconnection==1.10.3 # homeassistant.components.google_translate gTTS==2.2.4 -# homeassistant.components.garages_amsterdam -garages-amsterdam==3.0.0 +# homeassistant.components.google_assistant_sdk +gassist-text==0.0.7 # homeassistant.components.google -gcal-sync==4.0.4 +gcal-sync==4.1.0 # homeassistant.components.geocaching geocachingapi==0.2.1 @@ -571,6 +577,7 @@ georss_ign_sismologia_client==0.3 # homeassistant.components.qld_bushfire georss_qld_bushfire_alert_client==0.5 +# homeassistant.components.dlna_dmr # homeassistant.components.kef # homeassistant.components.minecraft_server # homeassistant.components.nmap_tracker @@ -579,7 +586,7 @@ georss_qld_bushfire_alert_client==0.5 getmac==0.8.2 # homeassistant.components.gios -gios==2.1.0 +gios==2.3.0 # homeassistant.components.glances glances_api==0.4.1 @@ -591,7 +598,7 @@ goalzero==0.2.1 goodwe==0.2.18 # homeassistant.components.google_pubsub -google-cloud-pubsub==2.13.10 +google-cloud-pubsub==2.13.11 # homeassistant.components.nest google-nest-sdm==2.1.0 @@ -600,7 +607,7 @@ google-nest-sdm==2.1.0 googlemaps==2.5.1 # homeassistant.components.govee_ble -govee-ble==0.19.3 +govee-ble==0.21.0 # homeassistant.components.gree greeclimate==1.3.0 @@ -623,9 +630,6 @@ guppy3==3.1.2 # homeassistant.components.iaqualink h2==4.1.0 -# homeassistant.components.homekit -ha-HAP-python==4.5.2 - # homeassistant.components.generic # homeassistant.components.stream ha-av==10.0.0 @@ -649,28 +653,28 @@ hatasmota==0.6.1 hdate==0.10.4 # homeassistant.components.here_travel_time -here_routing==0.1.1 +here_routing==0.2.0 # homeassistant.components.here_travel_time -here_transit==1.0.0 +here_transit==1.2.0 # homeassistant.components.hlk_sw16 hlk-sw16==0.0.9 # homeassistant.components.pi_hole -hole==0.7.0 +hole==0.8.0 # homeassistant.components.workday holidays==0.17.2 # homeassistant.components.frontend -home-assistant-frontend==20221213.1 +home-assistant-frontend==20230104.0 # homeassistant.components.home_connect homeconnect==0.7.2 # homeassistant.components.homematicip_cloud -homematicip==1.0.11 +homematicip==1.0.13 # homeassistant.components.home_plus_control homepluscontrol==0.0.5 @@ -691,7 +695,7 @@ iaqualink==0.5.0 ibeacon_ble==1.0.1 # homeassistant.components.local_calendar -ical==4.2.4 +ical==4.2.8 # homeassistant.components.ping icmplib==3.0 @@ -808,7 +812,7 @@ mill-local==0.2.0 millheater==0.10.0 # homeassistant.components.minio -minio==5.0.10 +minio==7.1.12 # homeassistant.components.moat moat-ble==0.1.1 @@ -817,7 +821,7 @@ moat-ble==0.1.1 moehlenhoff-alpha2==1.2.1 # homeassistant.components.motion_blinds -motionblinds==0.6.13 +motionblinds==0.6.15 # homeassistant.components.motioneye motioneye-client==0.3.12 @@ -844,7 +848,7 @@ netdisco==3.0.0 netmap==0.7.0.2 # homeassistant.components.nam -nettigo-air-monitor==1.5.0 +nettigo-air-monitor==1.6.0 # homeassistant.components.nexia nexia==2.0.6 @@ -856,7 +860,7 @@ nextcord==2.0.0a8 nextdns==1.2.2 # homeassistant.components.nibe_heatpump -nibe==1.3.0 +nibe==1.6.0 # homeassistant.components.nfandroidtv notifications-android-tv==0.1.5 @@ -887,6 +891,9 @@ oauth2client==4.1.3 # homeassistant.components.profiler objgraph==3.5.0 +# homeassistant.components.garages_amsterdam +odp-amsterdam==5.0.0 + # homeassistant.components.omnilogic omnilogic==0.4.5 @@ -944,7 +951,7 @@ pilight==0.1.1 # homeassistant.components.doods # homeassistant.components.generic -# homeassistant.components.image +# homeassistant.components.image_upload # homeassistant.components.proxy # homeassistant.components.qrcode # homeassistant.components.seven_segments @@ -953,7 +960,7 @@ pilight==0.1.1 pillow==9.3.0 # homeassistant.components.plex -plexapi==4.13.1 +plexapi==4.13.2 # homeassistant.components.plex plexauth==0.0.6 @@ -1032,7 +1039,7 @@ pyMetno==0.9.0 pyRFXtrx==0.30.0 # homeassistant.components.tibber -pyTibber==0.26.6 +pyTibber==0.26.7 # homeassistant.components.nextbus py_nextbusnext==0.1.5 @@ -1044,7 +1051,8 @@ pyaehw4a1==0.3.9 pyairnow==1.1.0 # homeassistant.components.airvisual -pyairvisual==2022.11.1 +# homeassistant.components.airvisual_pro +pyairvisual==2022.12.1 # homeassistant.components.almond pyalmond==0.0.2 @@ -1053,7 +1061,7 @@ pyalmond==0.0.2 pyatag==0.3.5.3 # homeassistant.components.netatmo -pyatmo==7.4.0 +pyatmo==7.5.0 # homeassistant.components.apple_tv pyatv==0.10.3 @@ -1071,7 +1079,7 @@ pyblackbird==0.5 pybotvac==0.0.23 # homeassistant.components.braviatv -pybravia==0.2.3 +pybravia==0.2.5 # homeassistant.components.cloudflare pycfdns==2.0.1 @@ -1080,7 +1088,7 @@ pycfdns==2.0.1 pychromecast==13.0.4 # homeassistant.components.comfoconnect -pycomfoconnect==0.4 +pycomfoconnect==0.5.1 # homeassistant.components.coolmaster pycoolmasternet-async==0.1.2 @@ -1089,7 +1097,7 @@ pycoolmasternet-async==0.1.2 pydaikin==2.8.0 # homeassistant.components.deconz -pydeconz==105 +pydeconz==106 # homeassistant.components.dexcom pydexcom==0.2.3 @@ -1098,7 +1106,7 @@ pydexcom==0.2.3 pydroid-ipcam==2.0.0 # homeassistant.components.econet -pyeconet==0.1.15 +pyeconet==0.1.17 # homeassistant.components.efergy pyefergy==22.1.1 @@ -1179,7 +1187,7 @@ pyiqvia==2022.04.0 pyiss==1.0.1 # homeassistant.components.isy994 -pyisy==3.0.8 +pyisy==3.0.10 # homeassistant.components.kaleidescape pykaleidescape==1.0.1 @@ -1212,7 +1220,7 @@ pylibrespot-java==0.1.1 pylitejet==0.3.0 # homeassistant.components.litterrobot -pylitterbot==2022.11.0 +pylitterbot==2022.12.0 # homeassistant.components.lutron_caseta pylutron-caseta==0.17.1 @@ -1248,10 +1256,10 @@ pymyq==3.1.4 pymysensors==0.24.0 # homeassistant.components.netgear -pynetgear==0.10.8 +pynetgear==0.10.9 # homeassistant.components.nina -pynina==0.1.8 +pynina==0.2.0 # homeassistant.components.nobo_hub pynobo==1.6.0 @@ -1286,10 +1294,10 @@ pyotgw==2.1.3 # homeassistant.auth.mfa_modules.notify # homeassistant.auth.mfa_modules.totp # homeassistant.components.otp -pyotp==2.7.0 +pyotp==2.8.0 # homeassistant.components.overkiz -pyoverkiz==1.7.2 +pyoverkiz==1.7.3 # homeassistant.components.openweathermap pyowm==3.2.0 @@ -1298,7 +1306,7 @@ pyowm==3.2.0 pyownet==0.10.0.post1 # homeassistant.components.lcn -pypck==0.7.15 +pypck==0.7.16 # homeassistant.components.plaato pyplaato==0.0.18 @@ -1321,6 +1329,9 @@ pyps4-2ndscreen==1.3.1 # homeassistant.components.qwikswitch pyqwikswitch==0.93 +# homeassistant.components.rainbird +pyrainbird==0.7.1 + # homeassistant.components.risco pyrisco==0.5.7 @@ -1366,7 +1377,7 @@ pysmartapp==0.3.3 pysmartthings==0.7.6 # homeassistant.components.snmp -pysnmplib==5.0.15 +pysnmplib==5.0.20 # homeassistant.components.snooz pysnooz==0.8.3 @@ -1381,7 +1392,7 @@ pyspcwebgw==0.4.0 pysqueezebox==0.6.1 # homeassistant.components.switchbee -pyswitchbee==1.6.1 +pyswitchbee==1.7.3 # homeassistant.components.syncthru pysyncthru==0.7.10 @@ -1402,7 +1413,7 @@ python-ecobee-api==0.2.14 python-forecastio==1.4.0 # homeassistant.components.fully_kiosk -python-fullykiosk==0.0.11 +python-fullykiosk==0.0.12 # homeassistant.components.homewizard python-homewizard-energy==1.3.1 @@ -1417,7 +1428,7 @@ python-juicenet==1.1.0 python-kasa==0.5.0 # homeassistant.components.matter -python-matter-server==1.0.7 +python-matter-server==1.0.8 # homeassistant.components.xiaomi_miio python-miio==0.5.12 @@ -1517,6 +1528,9 @@ regenmaschine==2022.11.0 # homeassistant.components.renault renault-api==0.1.11 +# homeassistant.components.reolink +reolink-aio==0.1.1 + # homeassistant.components.python_script restrictedpython==5.2 @@ -1533,7 +1547,7 @@ rokuecp==0.17.0 roombapy==1.6.5 # homeassistant.components.roon -roonapi==0.1.1 +roonapi==0.1.2 # homeassistant.components.rpi_power rpi-bad-power==0.1.0 @@ -1570,13 +1584,13 @@ sense_energy==0.11.0 sensirion-ble==0.0.1 # homeassistant.components.sensorpro -sensorpro-ble==0.5.0 +sensorpro-ble==0.5.1 # homeassistant.components.sensorpush sensorpush-ble==1.5.2 # homeassistant.components.sentry -sentry-sdk==1.11.0 +sentry-sdk==1.12.1 # homeassistant.components.sharkiq sharkiq==0.0.1 @@ -1624,7 +1638,7 @@ speedtest-cli==2.1.3 spiderpy==1.6.1 # homeassistant.components.spotify -spotipy==2.21.0 +spotipy==2.22.0 # homeassistant.components.recorder # homeassistant.components.sql @@ -1679,7 +1693,7 @@ tesla-powerwall==0.3.18 tesla-wall-connector==1.0.2 # homeassistant.components.thermobeacon -thermobeacon-ble==0.4.0 +thermobeacon-ble==0.6.0 # homeassistant.components.thermopro thermopro-ble==0.4.3 @@ -1700,7 +1714,7 @@ toonapi==0.2.1 total_connect_client==2022.10 # homeassistant.components.transmission -transmissionrpc==0.11 +transmission-rpc==3.4.0 # homeassistant.components.twinkly ttls==1.5.1 @@ -1741,7 +1755,7 @@ url-normalize==1.4.3 uvcclient==0.11.0 # homeassistant.components.vallox -vallox-websocket-api==2.12.0 +vallox-websocket-api==3.0.0 # homeassistant.components.rdw vehicle==0.4.0 @@ -1775,7 +1789,7 @@ wakeonlan==2.1.0 wallbox==0.4.12 # homeassistant.components.folder_watcher -watchdog==2.1.9 +watchdog==2.2.0 # homeassistant.components.whirlpool whirlpool-sixth-sense==0.17.0 @@ -1790,7 +1804,7 @@ wiffi==1.1.0 withings-api==2.4.0 # homeassistant.components.wled -wled==0.14.1 +wled==0.15.0 # homeassistant.components.wolflink wolf_smartset==0.1.11 @@ -1802,7 +1816,7 @@ xbox-webapi==2.0.11 xiaomi-ble==0.12.2 # homeassistant.components.knx -xknx==2.1.0 +xknx==2.2.0 # homeassistant.components.bluesound # homeassistant.components.fritz @@ -1816,13 +1830,13 @@ xmltodict==0.13.0 yalesmartalarmclient==0.3.9 # homeassistant.components.yalexs_ble -yalexs-ble==1.10.2 +yalexs-ble==1.12.5 # homeassistant.components.august yalexs==1.2.6 # homeassistant.components.august -yalexs_ble==1.10.2 +yalexs_ble==1.12.5 # homeassistant.components.yeelight yeelight==0.7.10 @@ -1834,13 +1848,13 @@ yolink-api==0.1.5 youless-api==0.16 # homeassistant.components.zamg -zamg==0.1.1 +zamg==0.2.2 # homeassistant.components.zeroconf -zeroconf==0.39.4 +zeroconf==0.47.1 # homeassistant.components.zha -zha-quirks==0.0.89 +zha-quirks==0.0.90 # homeassistant.components.zha zigpy-deconz==0.19.2 @@ -1858,7 +1872,7 @@ zigpy-znp==0.9.2 zigpy==0.52.3 # homeassistant.components.zwave_js -zwave-js-server-python==0.43.1 +zwave-js-server-python==0.44.0 # homeassistant.components.zwave_me zwave_me_ws==0.3.0 diff --git a/requirements_test_pre_commit.txt b/requirements_test_pre_commit.txt index da49f1e7bc4..cb80f4544c2 100644 --- a/requirements_test_pre_commit.txt +++ b/requirements_test_pre_commit.txt @@ -2,16 +2,16 @@ autoflake==2.0.0 bandit==1.7.4 -black==22.10.0 +black==22.12.0 codespell==2.2.2 flake8-comprehensions==3.10.1 flake8-docstrings==1.6.0 flake8-noqa==1.3.0 flake8==6.0.0 -isort==5.10.1 +isort==5.11.4 mccabe==0.7.0 pycodestyle==2.10.0 pydocstyle==6.1.1 pyflakes==3.0.1 -pyupgrade==3.2.2 +pyupgrade==3.3.1 yamllint==1.28.0 diff --git a/script/bootstrap b/script/bootstrap index 68f02961d27..0ea2b1476cd 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -7,5 +7,6 @@ set -e cd "$(dirname "$0")/.." echo "Installing development dependencies..." -python3 -m pip install wheel --constraint homeassistant/package_constraints.txt -python3 -m pip install colorlog pre-commit $(grep mypy requirements_test.txt) $(grep stdlib-list requirements_test.txt) $(grep tqdm requirements_test.txt) $(grep pipdeptree requirements_test.txt) $(grep awesomeversion requirements.txt) --constraint homeassistant/package_constraints.txt --use-deprecated=legacy-resolver +python3 -m pip install wheel --constraint homeassistant/package_constraints.txt --upgrade +python3 -m pip install colorlog pre-commit $(grep awesomeversion requirements.txt) --constraint homeassistant/package_constraints.txt --use-deprecated=legacy-resolver --upgrade +python3 -m pip install -r requirements_test.txt -c homeassistant/package_constraints.txt --use-deprecated legacy-resolver --upgrade diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index 355caa6cb62..513960c2030 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -71,8 +71,8 @@ httplib2>=0.19.0 # gRPC is an implicit dependency that we want to make explicit so we manage # upgrades intentionally. It is a large package to build from source and we # want to ensure we have wheels built. -grpcio==1.48.0 -grpcio-status==1.48.0 +grpcio==1.51.1 +grpcio-status==1.51.1 # libcst >=0.4.0 requires a newer Rust than we currently have available, # thus our wheels builds fail. This pins it to the last working version, @@ -101,7 +101,7 @@ regex==2021.8.28 # requirements so we can directly link HA versions to these library versions. anyio==3.6.2 h11==0.14.0 -httpcore==0.16.2 +httpcore==0.16.3 # Ensure we have a hyperframe version that works in Python 3.10 # 5.2.0 fixed a collections abc deprecation diff --git a/script/hassfest/manifest.py b/script/hassfest/manifest.py index 130d3288ab6..5a41d71be21 100644 --- a/script/hassfest/manifest.py +++ b/script/hassfest/manifest.py @@ -62,7 +62,7 @@ NO_IOT_CLASS = [ "homeassistant_hardware", "homeassistant_sky_connect", "homeassistant_yellow", - "image", + "image_upload", "input_boolean", "input_button", "input_datetime", diff --git a/script/hassfest/model.py b/script/hassfest/model.py index ee0a3ab32d9..52af5f6c791 100644 --- a/script/hassfest/model.py +++ b/script/hassfest/model.py @@ -1,44 +1,43 @@ """Models for manifest validator.""" from __future__ import annotations +from dataclasses import dataclass, field import json import pathlib from typing import Any -import attr - -@attr.s +@dataclass class Error: """Error validating an integration.""" - plugin: str = attr.ib() - error: str = attr.ib() - fixable: bool = attr.ib(default=False) + plugin: str + error: str + fixable: bool = False def __str__(self) -> str: """Represent error as string.""" return f"[{self.plugin.upper()}] {self.error}" -@attr.s +@dataclass class Config: """Config for the run.""" - specific_integrations: list[pathlib.Path] | None = attr.ib() - root: pathlib.Path = attr.ib() - action: str = attr.ib() - requirements: bool = attr.ib() - errors: list[Error] = attr.ib(factory=list) - cache: dict[str, Any] = attr.ib(factory=dict) - plugins: set[str] = attr.ib(factory=set) + specific_integrations: list[pathlib.Path] | None + root: pathlib.Path + action: str + requirements: bool + errors: list[Error] = field(default_factory=list) + cache: dict[str, Any] = field(default_factory=dict) + plugins: set[str] = field(default_factory=set) def add_error(self, *args: Any, **kwargs: Any) -> None: """Add an error.""" self.errors.append(Error(*args, **kwargs)) -@attr.s +@dataclass class Brand: """Represent a brand in our validator.""" @@ -54,8 +53,8 @@ class Brand: return brands - path: pathlib.Path = attr.ib() - _brand: dict[str, Any] | None = attr.ib(default=None) + path: pathlib.Path + _brand: dict[str, Any] | None = None @property def brand(self) -> dict[str, Any]: @@ -100,7 +99,7 @@ class Brand: self._brand = brand -@attr.s +@dataclass class Integration: """Represent an integration in our validator.""" @@ -129,11 +128,11 @@ class Integration: return integrations - path: pathlib.Path = attr.ib() - _manifest: dict[str, Any] | None = attr.ib(default=None) - errors: list[Error] = attr.ib(factory=list) - warnings: list[Error] = attr.ib(factory=list) - translated_name: bool = attr.ib(default=False) + path: pathlib.Path + _manifest: dict[str, Any] | None = None + errors: list[Error] = field(default_factory=list) + warnings: list[Error] = field(default_factory=list) + translated_name: bool = False @property def manifest(self) -> dict[str, Any]: diff --git a/script/hassfest/translations.py b/script/hassfest/translations.py index 824ebc6b825..a9612cf1943 100644 --- a/script/hassfest/translations.py +++ b/script/hassfest/translations.py @@ -224,6 +224,18 @@ def gen_strings_schema(config: Config, integration: Integration) -> vol.Schema: cv.schema_with_slug_keys(str, slug_validator=lowercase_validator), slug_validator=vol.Any("_", cv.slug), ), + vol.Optional("state_attributes"): cv.schema_with_slug_keys( + cv.schema_with_slug_keys( + { + vol.Optional("name"): str, + vol.Optional("state"): cv.schema_with_slug_keys( + str, slug_validator=lowercase_validator + ), + }, + slug_validator=lowercase_validator, + ), + slug_validator=vol.Any("_", cv.slug), + ), vol.Optional("system_health"): { vol.Optional("info"): {str: cv.string_with_no_html} }, @@ -255,6 +267,19 @@ def gen_strings_schema(config: Config, integration: Integration) -> vol.Schema: ), ) }, + vol.Optional("entity"): { + str: { + str: { + vol.Optional("state_attributes"): { + str: { + vol.Optional("name"): cv.string_with_no_html, + vol.Optional("state"): {str: cv.string_with_no_html}, + } + }, + vol.Optional("state"): {str: cv.string_with_no_html}, + } + } + }, } ) diff --git a/tests/common.py b/tests/common.py index 46022022df6..69c569e445f 100644 --- a/tests/common.py +++ b/tests/common.py @@ -1113,6 +1113,11 @@ class MockEntity(entity.Entity): """Info about supported features.""" return self._handle("supported_features") + @property + def translation_key(self): + """Return the translation key.""" + return self._handle("translation_key") + @property def unique_id(self): """Return the unique ID of the entity.""" @@ -1298,6 +1303,38 @@ def assert_lists_same(a, b): assert i in a +_SENTINEL = object() + + +class _HA_ANY: + """A helper object that compares equal to everything. + + Based on unittest.mock.ANY, but modified to not show up in pytest's equality + assertion diffs. + """ + + _other = _SENTINEL + + def __eq__(self, other): + """Test equal.""" + self._other = other + return True + + def __ne__(self, other): + """Test not equal.""" + self._other = other + return False + + def __repr__(self): + """Return repr() other to not show up in pytest quality diffs.""" + if self._other is _SENTINEL: + return "" + return repr(self._other) + + +ANY = _HA_ANY() + + def raise_contains_mocks(val): """Raise for mocks.""" if isinstance(val, Mock): diff --git a/tests/components/accuweather/test_sensor.py b/tests/components/accuweather/test_sensor.py index 55052b5ca3e..251f5a16932 100644 --- a/tests/components/accuweather/test_sensor.py +++ b/tests/components/accuweather/test_sensor.py @@ -60,9 +60,10 @@ async def test_sensor_without_forecast(hass): assert state.state == "0.0" assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == LENGTH_MILLIMETERS - assert state.attributes.get(ATTR_ICON) == "mdi:weather-rainy" + assert state.attributes.get(ATTR_ICON) is None assert state.attributes.get("type") is None assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT + assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.PRECIPITATION entry = registry.async_get("sensor.home_precipitation") assert entry @@ -73,12 +74,13 @@ async def test_sensor_without_forecast(hass): assert state.state == "falling" assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION assert state.attributes.get(ATTR_ICON) == "mdi:gauge" - assert state.attributes.get(ATTR_DEVICE_CLASS) == "accuweather__pressure_tendency" + assert state.attributes.get(ATTR_DEVICE_CLASS) is None assert state.attributes.get(ATTR_STATE_CLASS) is None entry = registry.async_get("sensor.home_pressure_tendency") assert entry assert entry.unique_id == "0123456-pressuretendency" + assert entry.translation_key == "pressure_tendency" state = hass.states.get("sensor.home_realfeel_temperature") assert state @@ -434,7 +436,7 @@ async def test_sensor_enabled_without_forecast(hass): assert state.state == "20.3" assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == SPEED_KILOMETERS_PER_HOUR - assert state.attributes.get(ATTR_ICON) == "mdi:weather-windy" + assert state.attributes.get(ATTR_ICON) is None assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.WIND_SPEED @@ -447,7 +449,7 @@ async def test_sensor_enabled_without_forecast(hass): assert state.state == "14.5" assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == SPEED_KILOMETERS_PER_HOUR - assert state.attributes.get(ATTR_ICON) == "mdi:weather-windy" + assert state.attributes.get(ATTR_ICON) is None assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.WIND_SPEED @@ -581,7 +583,7 @@ async def test_sensor_enabled_without_forecast(hass): assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == SPEED_KILOMETERS_PER_HOUR assert state.attributes.get("direction") == "SSE" - assert state.attributes.get(ATTR_ICON) == "mdi:weather-windy" + assert state.attributes.get(ATTR_ICON) is None assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.WIND_SPEED entry = registry.async_get("sensor.home_wind_day_0d") @@ -594,7 +596,7 @@ async def test_sensor_enabled_without_forecast(hass): assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == SPEED_KILOMETERS_PER_HOUR assert state.attributes.get("direction") == "WNW" - assert state.attributes.get(ATTR_ICON) == "mdi:weather-windy" + assert state.attributes.get(ATTR_ICON) is None assert state.attributes.get(ATTR_STATE_CLASS) is None assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.WIND_SPEED @@ -608,7 +610,7 @@ async def test_sensor_enabled_without_forecast(hass): assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == SPEED_KILOMETERS_PER_HOUR assert state.attributes.get("direction") == "S" - assert state.attributes.get(ATTR_ICON) == "mdi:weather-windy" + assert state.attributes.get(ATTR_ICON) is None assert state.attributes.get(ATTR_STATE_CLASS) is None assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.WIND_SPEED @@ -622,7 +624,7 @@ async def test_sensor_enabled_without_forecast(hass): assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == SPEED_KILOMETERS_PER_HOUR assert state.attributes.get("direction") == "WSW" - assert state.attributes.get(ATTR_ICON) == "mdi:weather-windy" + assert state.attributes.get(ATTR_ICON) is None assert state.attributes.get(ATTR_STATE_CLASS) is None assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.WIND_SPEED diff --git a/tests/components/airvisual/conftest.py b/tests/components/airvisual/conftest.py index 3e83b41a5af..8ef060c3116 100644 --- a/tests/components/airvisual/conftest.py +++ b/tests/components/airvisual/conftest.py @@ -1,6 +1,6 @@ """Define test fixtures for AirVisual.""" import json -from unittest.mock import patch +from unittest.mock import AsyncMock, Mock, patch import pytest @@ -56,17 +56,27 @@ def data_fixture(): return json.loads(load_fixture("data.json", "airvisual")) +@pytest.fixture(name="pro_data", scope="session") +def pro_data_fixture(): + """Define an update coordinator data example for the Pro.""" + return json.loads(load_fixture("data.json", "airvisual_pro")) + + +@pytest.fixture(name="pro") +def pro_fixture(pro_data): + """Define a mocked NodeSamba object.""" + return Mock( + async_connect=AsyncMock(), + async_disconnect=AsyncMock(), + async_get_latest_measurements=AsyncMock(return_value=pro_data), + ) + + @pytest.fixture(name="setup_airvisual") async def setup_airvisual_fixture(hass, config, data): """Define a fixture to set up AirVisual.""" with patch("pyairvisual.air_quality.AirQuality.city"), patch( "pyairvisual.air_quality.AirQuality.nearest_city", return_value=data - ), patch("pyairvisual.node.NodeSamba.async_connect"), patch( - "pyairvisual.node.NodeSamba.async_get_latest_measurements" - ), patch( - "pyairvisual.node.NodeSamba.async_disconnect" - ), patch( - "homeassistant.components.airvisual.PLATFORMS", [] ): assert await async_setup_component(hass, DOMAIN, config) await hass.async_block_till_done() diff --git a/tests/components/airvisual/test_config_flow.py b/tests/components/airvisual/test_config_flow.py index 7603917eddc..7bad9af1002 100644 --- a/tests/components/airvisual/test_config_flow.py +++ b/tests/components/airvisual/test_config_flow.py @@ -8,11 +8,10 @@ from pyairvisual.cloud_api import ( UnauthorizedError, ) from pyairvisual.errors import AirVisualError -from pyairvisual.node import NodeProError import pytest from homeassistant import data_entry_flow -from homeassistant.components.airvisual.const import ( +from homeassistant.components.airvisual import ( CONF_CITY, CONF_COUNTRY, CONF_GEOGRAPHIES, @@ -22,6 +21,7 @@ from homeassistant.components.airvisual.const import ( INTEGRATION_TYPE_GEOGRAPHY_NAME, INTEGRATION_TYPE_NODE_PRO, ) +from homeassistant.components.airvisual_pro import DOMAIN as AIRVISUAL_PRO_DOMAIN from homeassistant.config_entries import SOURCE_REAUTH, SOURCE_USER from homeassistant.const import ( CONF_API_KEY, @@ -32,38 +32,17 @@ from homeassistant.const import ( CONF_SHOW_ON_MAP, CONF_STATE, ) +from homeassistant.helpers import device_registry as dr, issue_registry as ir + +from tests.common import MockConfigEntry -@pytest.mark.parametrize( - "config,data,unique_id", - [ - ( - { - CONF_API_KEY: "abcde12345", - CONF_LATITUDE: 51.528308, - CONF_LONGITUDE: -0.3817765, - }, - { - "type": INTEGRATION_TYPE_GEOGRAPHY_COORDS, - }, - "51.528308, -0.3817765", - ), - ( - { - CONF_IP_ADDRESS: "192.168.1.100", - CONF_PASSWORD: "12345", - }, - { - "type": INTEGRATION_TYPE_NODE_PRO, - }, - "192.168.1.100", - ), - ], -) -async def test_duplicate_error(hass, config, config_entry, data): +async def test_duplicate_error(hass, config, config_entry, data, setup_airvisual): """Test that errors are shown when duplicate entries are added.""" result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_USER}, data=data + DOMAIN, + context={"source": SOURCE_USER}, + data={"type": INTEGRATION_TYPE_GEOGRAPHY_COORDS}, ) result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input=config @@ -130,15 +109,6 @@ async def test_duplicate_error(hass, config, config_entry, data): {"base": "unknown"}, INTEGRATION_TYPE_GEOGRAPHY_NAME, ), - ( - { - CONF_IP_ADDRESS: "192.168.1.100", - CONF_PASSWORD: "my_password", - }, - NodeProError, - {CONF_IP_ADDRESS: "cannot_connect"}, - INTEGRATION_TYPE_NODE_PRO, - ), ], ) async def test_errors(hass, data, exc, errors, integration_type): @@ -174,8 +144,8 @@ async def test_errors(hass, data, exc, errors, integration_type): ) ], ) -async def test_migration(hass, config, config_entry, setup_airvisual, unique_id): - """Test migrating from version 1 to the current version.""" +async def test_migration_1_2(hass, config, config_entry, setup_airvisual, unique_id): + """Test migrating from version 1 to 2.""" config_entries = hass.config_entries.async_entries(DOMAIN) assert len(config_entries) == 2 @@ -199,6 +169,46 @@ async def test_migration(hass, config, config_entry, setup_airvisual, unique_id) } +async def test_migration_2_3(hass, pro): + """Test migrating from version 2 to 3.""" + old_pro_entry = MockConfigEntry( + domain=DOMAIN, + unique_id="192.168.1.100", + data={ + CONF_IP_ADDRESS: "192.168.1.100", + CONF_PASSWORD: "abcde12345", + CONF_INTEGRATION_TYPE: INTEGRATION_TYPE_NODE_PRO, + }, + version=2, + ) + old_pro_entry.add_to_hass(hass) + + device_registry = dr.async_get(hass) + device_registry.async_get_or_create( + name="192.168.1.100", + config_entry_id=old_pro_entry.entry_id, + identifiers={(DOMAIN, "ABCDE12345")}, + ) + + with patch( + "homeassistant.components.airvisual.automation.automations_with_device", + return_value=["automation.test_automation"], + ), patch( + "homeassistant.components.airvisual_pro.NodeSamba", return_value=pro + ), patch( + "homeassistant.components.airvisual_pro.config_flow.NodeSamba", return_value=pro + ): + await hass.config_entries.async_setup(old_pro_entry.entry_id) + await hass.async_block_till_done() + + for domain, entry_count in ((DOMAIN, 0), (AIRVISUAL_PRO_DOMAIN, 1)): + entries = hass.config_entries.async_entries(domain) + assert len(entries) == entry_count + + issue_registry = ir.async_get(hass) + assert len(issue_registry.issues) == 1 + + async def test_options_flow(hass, config_entry): """Test config flow options.""" with patch( @@ -272,32 +282,6 @@ async def test_step_geography_by_name(hass, config, setup_airvisual): } -@pytest.mark.parametrize( - "config", - [ - { - CONF_IP_ADDRESS: "192.168.1.100", - CONF_PASSWORD: "my_password", - } - ], -) -async def test_step_node_pro(hass, config, setup_airvisual): - """Test the Node/Pro step.""" - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_USER}, data={"type": "AirVisual Node/Pro"} - ) - result = await hass.config_entries.flow.async_configure( - result["flow_id"], user_input=config - ) - assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY - assert result["title"] == "Node/Pro (192.168.1.100)" - assert result["data"] == { - CONF_IP_ADDRESS: "192.168.1.100", - CONF_PASSWORD: "my_password", - CONF_INTEGRATION_TYPE: INTEGRATION_TYPE_NODE_PRO, - } - - async def test_step_reauth(hass, config_entry, setup_airvisual): """Test that the reauth step works.""" result = await hass.config_entries.flow.async_init( @@ -350,12 +334,3 @@ async def test_step_user(hass): assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["step_id"] == "geography_by_name" - - result = await hass.config_entries.flow.async_init( - DOMAIN, - context={"source": SOURCE_USER}, - data={"type": INTEGRATION_TYPE_NODE_PRO}, - ) - - assert result["type"] == data_entry_flow.FlowResultType.FORM - assert result["step_id"] == "node_pro" diff --git a/tests/components/airvisual/test_diagnostics.py b/tests/components/airvisual/test_diagnostics.py index 72ed5298f96..37a8437dcdf 100644 --- a/tests/components/airvisual/test_diagnostics.py +++ b/tests/components/airvisual/test_diagnostics.py @@ -9,7 +9,7 @@ async def test_entry_diagnostics(hass, config_entry, hass_client, setup_airvisua assert await get_diagnostics_for_config_entry(hass, hass_client, config_entry) == { "entry": { "entry_id": config_entry.entry_id, - "version": 2, + "version": 3, "domain": "airvisual", "title": REDACTED, "data": { diff --git a/tests/components/airvisual_pro/__init__.py b/tests/components/airvisual_pro/__init__.py new file mode 100644 index 00000000000..7fe3b734343 --- /dev/null +++ b/tests/components/airvisual_pro/__init__.py @@ -0,0 +1 @@ +"""Tests for the AirVisual Pro integration.""" diff --git a/tests/components/airvisual_pro/conftest.py b/tests/components/airvisual_pro/conftest.py new file mode 100644 index 00000000000..5846a988688 --- /dev/null +++ b/tests/components/airvisual_pro/conftest.py @@ -0,0 +1,71 @@ +"""Define test fixtures for AirVisual Pro.""" +import json +from unittest.mock import AsyncMock, Mock, patch + +import pytest + +from homeassistant.components.airvisual_pro.const import DOMAIN +from homeassistant.const import CONF_IP_ADDRESS, CONF_PASSWORD +from homeassistant.setup import async_setup_component + +from tests.common import MockConfigEntry, load_fixture + + +@pytest.fixture(name="config_entry") +def config_entry_fixture(hass, config): + """Define a config entry fixture.""" + entry = MockConfigEntry(domain=DOMAIN, unique_id="XXXXXXX", data=config) + entry.add_to_hass(hass) + return entry + + +@pytest.fixture(name="config") +def config_fixture(hass): + """Define a config entry data fixture.""" + return { + CONF_IP_ADDRESS: "192.168.1.101", + CONF_PASSWORD: "password123", + } + + +@pytest.fixture(name="connect") +def connect_fixture(): + """Define a mocked async_connect method.""" + return AsyncMock(return_value=True) + + +@pytest.fixture(name="disconnect") +def disconnect_fixture(): + """Define a mocked async_connect method.""" + return AsyncMock() + + +@pytest.fixture(name="data", scope="session") +def data_fixture(): + """Define an update coordinator data example.""" + return json.loads(load_fixture("data.json", "airvisual_pro")) + + +@pytest.fixture(name="pro") +def pro_fixture(connect, data, disconnect): + """Define a mocked NodeSamba object.""" + return Mock( + async_connect=connect, + async_disconnect=disconnect, + async_get_latest_measurements=AsyncMock(return_value=data), + ) + + +@pytest.fixture(name="setup_airvisual_pro") +async def setup_airvisual_pro_fixture(hass, config, pro): + """Define a fixture to set up AirVisual Pro.""" + with patch( + "homeassistant.components.airvisual_pro.config_flow.NodeSamba", return_value=pro + ), patch( + "homeassistant.components.airvisual_pro.NodeSamba", return_value=pro + ), patch( + "homeassistant.components.airvisual.PLATFORMS", [] + ): + assert await async_setup_component(hass, DOMAIN, config) + await hass.async_block_till_done() + yield diff --git a/tests/components/airvisual_pro/fixtures/__init__.py b/tests/components/airvisual_pro/fixtures/__init__.py new file mode 100644 index 00000000000..526edd09229 --- /dev/null +++ b/tests/components/airvisual_pro/fixtures/__init__.py @@ -0,0 +1 @@ +"""Define data fixtures for AirVisual Pro.""" diff --git a/tests/components/airvisual_pro/fixtures/data.json b/tests/components/airvisual_pro/fixtures/data.json new file mode 100644 index 00000000000..1865542488b --- /dev/null +++ b/tests/components/airvisual_pro/fixtures/data.json @@ -0,0 +1,65 @@ +{ + "date_and_time": { + "date": "2022/10/06", + "time": "16:00:44", + "timestamp": "1665072044" + }, + "measurements": { + "co2": "472", + "humidity": "57", + "pm0_1": "0", + "pm1_0": "0", + "aqi_cn": "0", + "aqi_us": "0", + "pm2_5": "0", + "temperature_C": "23.0", + "temperature_F": "73.4", + "voc": "-1" + }, + "serial_number": "XXXXXXX", + "settings": { + "follow_mode": "station", + "followed_station": "0", + "is_aqi_usa": true, + "is_concentration_showed": true, + "is_indoor": true, + "is_lcd_on": true, + "is_network_time": true, + "is_temperature_celsius": false, + "language": "en-US", + "lcd_brightness": 80, + "node_name": "Office", + "power_saving": { + "2slots": [ + { "hour_off": 9, "hour_on": 7 }, + { "hour_off": 22, "hour_on": 18 } + ], + "mode": "yes", + "running_time": 99, + "yes": [ + { "hour": 8, "minute": 0 }, + { "hour": 21, "minute": 0 } + ] + }, + "sensor_mode": { "custom_mode_interval": 3, "mode": 1 }, + "speed_unit": "mph", + "timezone": "America/New York", + "tvoc_unit": "ppb" + }, + "status": { + "app_version": "1.1826", + "battery": 100, + "datetime": 1665072044, + "device_name": "AIRVISUAL-XXXXXXX", + "ip_address": "192.168.1.101", + "mac_address": "1234567890ab", + "model": 20, + "sensor_life": { "pm2_5": 1567924345130 }, + "sensor_pm25_serial": "00000005050224011145", + "sync_time": 250000, + "system_version": "KBG63F84", + "used_memory": 3, + "wifi_strength": 4 + }, + "last_measurement_timestamp": 1665072044 +} diff --git a/tests/components/airvisual_pro/test_config_flow.py b/tests/components/airvisual_pro/test_config_flow.py new file mode 100644 index 00000000000..61cd43f058d --- /dev/null +++ b/tests/components/airvisual_pro/test_config_flow.py @@ -0,0 +1,125 @@ +"""Test the AirVisual Pro config flow.""" +from unittest.mock import AsyncMock, patch + +from pyairvisual.node import ( + InvalidAuthenticationError, + NodeConnectionError, + NodeProError, +) +import pytest + +from homeassistant import data_entry_flow +from homeassistant.components.airvisual_pro.const import DOMAIN +from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_REAUTH, SOURCE_USER +from homeassistant.const import CONF_IP_ADDRESS, CONF_PASSWORD + + +@pytest.mark.parametrize( + "connect_mock,connect_errors", + [ + (AsyncMock(side_effect=Exception), {"base": "unknown"}), + (AsyncMock(side_effect=InvalidAuthenticationError), {"base": "invalid_auth"}), + (AsyncMock(side_effect=NodeConnectionError), {"base": "cannot_connect"}), + (AsyncMock(side_effect=NodeProError), {"base": "unknown"}), + ], +) +async def test_create_entry( + hass, config, connect_errors, connect_mock, pro, setup_airvisual_pro +): + """Test creating an entry.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_USER} + ) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "user" + + # Test errors that can arise when connecting to a Pro: + with patch.object(pro, "async_connect", connect_mock): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input=config + ) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["errors"] == connect_errors + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input=config + ) + assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY + assert result["title"] == "192.168.1.101" + assert result["data"] == { + CONF_IP_ADDRESS: "192.168.1.101", + CONF_PASSWORD: "password123", + } + + +async def test_duplicate_error(hass, config, config_entry, setup_airvisual_pro): + """Test that errors are shown when duplicates are added.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_USER} + ) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "user" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input=config + ) + assert result["type"] == data_entry_flow.FlowResultType.ABORT + assert result["reason"] == "already_configured" + + +async def test_step_import(hass, config, setup_airvisual_pro): + """Test that the user step works.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_IMPORT}, data=config + ) + assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY + assert result["title"] == "192.168.1.101" + assert result["data"] == { + CONF_IP_ADDRESS: "192.168.1.101", + CONF_PASSWORD: "password123", + } + + +@pytest.mark.parametrize( + "connect_mock,connect_errors", + [ + (AsyncMock(side_effect=Exception), {"base": "unknown"}), + (AsyncMock(side_effect=InvalidAuthenticationError), {"base": "invalid_auth"}), + (AsyncMock(side_effect=NodeConnectionError), {"base": "cannot_connect"}), + (AsyncMock(side_effect=NodeProError), {"base": "unknown"}), + ], +) +async def test_reauth( + hass, config, config_entry, connect_errors, connect_mock, pro, setup_airvisual_pro +): + """Test re-auth (including errors).""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={ + "source": SOURCE_REAUTH, + "entry_id": config_entry.entry_id, + "unique_id": config_entry.unique_id, + }, + data=config, + ) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "reauth_confirm" + + # Test errors that can arise when connecting to a Pro: + with patch.object(pro, "async_connect", connect_mock): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={CONF_PASSWORD: "new_password"} + ) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["errors"] == connect_errors + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={CONF_PASSWORD: "new_password"} + ) + + # Allow reload to finish: + await hass.async_block_till_done() + + assert result["type"] == data_entry_flow.FlowResultType.ABORT + assert result["reason"] == "reauth_successful" + assert len(hass.config_entries.async_entries()) == 1 diff --git a/tests/components/airvisual_pro/test_diagnostics.py b/tests/components/airvisual_pro/test_diagnostics.py new file mode 100644 index 00000000000..5847dd7ed88 --- /dev/null +++ b/tests/components/airvisual_pro/test_diagnostics.py @@ -0,0 +1,85 @@ +"""Test AirVisual Pro diagnostics.""" +from homeassistant.components.diagnostics import REDACTED + +from tests.components.diagnostics import get_diagnostics_for_config_entry + + +async def test_entry_diagnostics(hass, config_entry, hass_client, setup_airvisual_pro): + """Test config entry diagnostics.""" + assert await get_diagnostics_for_config_entry(hass, hass_client, config_entry) == { + "entry": { + "entry_id": config_entry.entry_id, + "version": 1, + "domain": "airvisual_pro", + "title": "Mock Title", + "data": {"ip_address": "192.168.1.101", "password": REDACTED}, + "options": {}, + "pref_disable_new_entities": False, + "pref_disable_polling": False, + "source": "user", + "unique_id": "XXXXXXX", + "disabled_by": None, + }, + "data": { + "date_and_time": { + "date": "2022/10/06", + "time": "16:00:44", + "timestamp": "1665072044", + }, + "measurements": { + "co2": "472", + "humidity": "57", + "pm0_1": "0", + "pm1_0": "0", + "aqi_cn": "0", + "aqi_us": "0", + "pm2_5": "0", + "temperature_C": "23.0", + "temperature_F": "73.4", + "voc": "-1", + }, + "serial_number": REDACTED, + "settings": { + "follow_mode": "station", + "followed_station": "0", + "is_aqi_usa": True, + "is_concentration_showed": True, + "is_indoor": True, + "is_lcd_on": True, + "is_network_time": True, + "is_temperature_celsius": False, + "language": "en-US", + "lcd_brightness": 80, + "node_name": "Office", + "power_saving": { + "2slots": [ + {"hour_off": 9, "hour_on": 7}, + {"hour_off": 22, "hour_on": 18}, + ], + "mode": "yes", + "running_time": 99, + "yes": [{"hour": 8, "minute": 0}, {"hour": 21, "minute": 0}], + }, + "sensor_mode": {"custom_mode_interval": 3, "mode": 1}, + "speed_unit": "mph", + "timezone": "America/New York", + "tvoc_unit": "ppb", + }, + "status": { + "app_version": "1.1826", + "battery": 100, + "datetime": 1665072044, + "device_name": "AIRVISUAL-XXXXXXX", + "ip_address": "192.168.1.101", + "mac_address": REDACTED, + "model": 20, + "sensor_life": {"pm2_5": 1567924345130}, + "sensor_pm25_serial": "00000005050224011145", + "sync_time": 250000, + "system_version": "KBG63F84", + "used_memory": 3, + "wifi_strength": 4, + }, + "last_measurement_timestamp": 1665072044, + }, + } diff --git a/tests/components/airzone/test_binary_sensor.py b/tests/components/airzone/test_binary_sensor.py index 138801d1dc0..860bc50e93c 100644 --- a/tests/components/airzone/test_binary_sensor.py +++ b/tests/components/airzone/test_binary_sensor.py @@ -78,3 +78,9 @@ async def test_airzone_create_binary_sensors(hass: HomeAssistant) -> None: state = hass.states.get("binary_sensor.salon_problem") assert state.state == STATE_OFF + + state = hass.states.get("binary_sensor.airzone_2_1_battery_low") + assert state is None + + state = hass.states.get("binary_sensor.airzone_2_1_problem") + assert state.state == STATE_OFF diff --git a/tests/components/airzone/test_climate.py b/tests/components/airzone/test_climate.py index a492abd8c61..640826bb30f 100644 --- a/tests/components/airzone/test_climate.py +++ b/tests/components/airzone/test_climate.py @@ -131,6 +131,20 @@ async def test_airzone_create_climates(hass: HomeAssistant) -> None: assert state.attributes.get(ATTR_TARGET_TEMP_STEP) == API_TEMPERATURE_STEP assert state.attributes.get(ATTR_TEMPERATURE) == 19.1 + state = hass.states.get("climate.airzone_2_1") + assert state.state == HVACMode.OFF + assert state.attributes.get(ATTR_CURRENT_HUMIDITY) == 62 + assert state.attributes.get(ATTR_CURRENT_TEMPERATURE) == 22.3 + assert state.attributes.get(ATTR_HVAC_ACTION) == HVACAction.OFF + assert state.attributes.get(ATTR_HVAC_MODES) == [ + HVACMode.HEAT_COOL, + HVACMode.OFF, + ] + assert state.attributes.get(ATTR_MAX_TEMP) == 30 + assert state.attributes.get(ATTR_MIN_TEMP) == 15 + assert state.attributes.get(ATTR_TARGET_TEMP_STEP) == API_TEMPERATURE_STEP + assert state.attributes.get(ATTR_TEMPERATURE) == 19.0 + async def test_airzone_climate_turn_on_off(hass: HomeAssistant) -> None: """Test turning on.""" @@ -187,6 +201,31 @@ async def test_airzone_climate_turn_on_off(hass: HomeAssistant) -> None: state = hass.states.get("climate.salon") assert state.state == HVACMode.OFF + HVAC_MOCK = { + API_DATA: [ + { + API_SYSTEM_ID: 2, + API_ZONE_ID: 1, + API_ON: 1, + } + ] + } + with patch( + "homeassistant.components.airzone.AirzoneLocalApi.put_hvac", + return_value=HVAC_MOCK, + ): + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_TURN_ON, + { + ATTR_ENTITY_ID: "climate.airzone_2_1", + }, + blocking=True, + ) + + state = hass.states.get("climate.airzone_2_1") + assert state.state == HVACMode.HEAT_COOL + async def test_airzone_climate_set_hvac_mode(hass: HomeAssistant) -> None: """Test setting the HVAC mode.""" @@ -246,6 +285,32 @@ async def test_airzone_climate_set_hvac_mode(hass: HomeAssistant) -> None: state = hass.states.get("climate.salon") assert state.state == HVACMode.OFF + HVAC_MOCK_3 = { + API_DATA: [ + { + API_SYSTEM_ID: 2, + API_ZONE_ID: 1, + API_ON: 1, + } + ] + } + with patch( + "homeassistant.components.airzone.AirzoneLocalApi.put_hvac", + return_value=HVAC_MOCK_3, + ): + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_HVAC_MODE, + { + ATTR_ENTITY_ID: "climate.airzone_2_1", + ATTR_HVAC_MODE: HVACMode.HEAT_COOL, + }, + blocking=True, + ) + + state = hass.states.get("climate.airzone_2_1") + assert state.state == HVACMode.HEAT_COOL + async def test_airzone_climate_set_hvac_slave_error(hass: HomeAssistant) -> None: """Test setting the HVAC mode for a slave zone.""" diff --git a/tests/components/airzone/test_sensor.py b/tests/components/airzone/test_sensor.py index bd57129cae0..248dc020732 100644 --- a/tests/components/airzone/test_sensor.py +++ b/tests/components/airzone/test_sensor.py @@ -48,3 +48,9 @@ async def test_airzone_create_sensors( state = hass.states.get("sensor.salon_humidity") assert state.state == "34" + + state = hass.states.get("sensor.airzone_2_1_temperature") + assert state.state == "22.3" + + state = hass.states.get("sensor.airzone_2_1_humidity") + assert state.state == "62" diff --git a/tests/components/airzone/util.py b/tests/components/airzone/util.py index 5bed0fc1d99..a29b035648b 100644 --- a/tests/components/airzone/util.py +++ b/tests/components/airzone/util.py @@ -177,7 +177,27 @@ HVAC_MOCK = { API_FLOOR_DEMAND: 0, }, ] - } + }, + { + API_DATA: [ + { + API_SYSTEM_ID: 2, + API_ZONE_ID: 1, + API_ON: 0, + API_MAX_TEMP: 30, + API_MIN_TEMP: 15, + API_SET_POINT: 19, + API_ROOM_TEMP: 22.299999, + API_COLD_STAGES: 1, + API_COLD_STAGE: 1, + API_HEAT_STAGES: 1, + API_HEAT_STAGE: 1, + API_HUMIDITY: 62, + API_UNITS: 0, + API_ERRORS: [], + }, + ] + }, ] } diff --git a/tests/components/analytics/test_analytics.py b/tests/components/analytics/test_analytics.py index b73338add35..cc580b16e08 100644 --- a/tests/components/analytics/test_analytics.py +++ b/tests/components/analytics/test_analytics.py @@ -10,11 +10,9 @@ from homeassistant.components.analytics.const import ( ANALYTICS_ENDPOINT_URL_DEV, ATTR_BASE, ATTR_DIAGNOSTICS, - ATTR_PREFERENCES, ATTR_STATISTICS, ATTR_USAGE, ) -from homeassistant.components.api import ATTR_UUID from homeassistant.const import ATTR_DOMAIN from homeassistant.loader import IntegrationNotFound from homeassistant.setup import async_setup_component @@ -58,7 +56,7 @@ async def test_load_with_supervisor_diagnostics(hass): async def test_load_with_supervisor_without_diagnostics(hass): """Test loading with a supervisor that has not diagnostics enabled.""" analytics = Analytics(hass) - analytics._data[ATTR_PREFERENCES][ATTR_DIAGNOSTICS] = True + analytics._data.preferences[ATTR_DIAGNOSTICS] = True assert analytics.preferences[ATTR_DIAGNOSTICS] @@ -349,7 +347,7 @@ async def test_reusing_uuid(hass, aioclient_mock): """Test reusing the stored UUID.""" aioclient_mock.post(ANALYTICS_ENDPOINT_URL, status=200) analytics = Analytics(hass) - analytics._data[ATTR_UUID] = "NOT_MOCK_UUID" + analytics._data.uuid = "NOT_MOCK_UUID" await analytics.save_preferences({ATTR_BASE: True}) diff --git a/tests/components/androidtv/test_config_flow.py b/tests/components/androidtv/test_config_flow.py index a0fb86eb803..03101733019 100644 --- a/tests/components/androidtv/test_config_flow.py +++ b/tests/components/androidtv/test_config_flow.py @@ -1,5 +1,4 @@ """Tests for the AndroidTV config flow.""" -import json from unittest.mock import patch import pytest @@ -410,7 +409,7 @@ async def test_options_flow(hass): result = await hass.config_entries.options.async_configure( result["flow_id"], user_input={ - CONF_RULE_VALUES: json.dumps({"a": "b"}), + CONF_RULE_VALUES: {"a": "b"}, }, ) assert result["type"] == data_entry_flow.FlowResultType.FORM @@ -421,7 +420,7 @@ async def test_options_flow(hass): result = await hass.config_entries.options.async_configure( result["flow_id"], user_input={ - CONF_RULE_VALUES: json.dumps(["standby"]), + CONF_RULE_VALUES: ["standby"], }, ) assert result["type"] == data_entry_flow.FlowResultType.FORM @@ -442,7 +441,7 @@ async def test_options_flow(hass): result["flow_id"], user_input={ CONF_RULE_ID: "rule2", - CONF_RULE_VALUES: json.dumps(VALID_DETECT_RULE), + CONF_RULE_VALUES: VALID_DETECT_RULE, }, ) assert result["type"] == data_entry_flow.FlowResultType.FORM diff --git a/tests/components/apcupsd/test_config_flow.py b/tests/components/apcupsd/test_config_flow.py index 79bdd68bf17..4c47f5a2947 100644 --- a/tests/components/apcupsd/test_config_flow.py +++ b/tests/components/apcupsd/test_config_flow.py @@ -5,8 +5,8 @@ from unittest.mock import patch import pytest from homeassistant.components.apcupsd import DOMAIN -from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_USER -from homeassistant.const import CONF_HOST, CONF_PORT, CONF_RESOURCES, CONF_SOURCE +from homeassistant.config_entries import SOURCE_USER +from homeassistant.const import CONF_HOST, CONF_PORT, CONF_SOURCE from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType @@ -161,25 +161,3 @@ async def test_flow_minimal_status( assert result["data"] == CONF_DATA assert result["title"] == expected_title mock_setup.assert_called_once() - - -async def test_flow_import(hass: HomeAssistant) -> None: - """Test successful creation of config entries via YAML import.""" - with patch("apcaccess.status.parse", return_value=MOCK_STATUS), patch( - "apcaccess.status.get", return_value=b"" - ), _patch_setup() as mock_setup: - # Importing from YAML will create an extra field CONF_RESOURCES in the config - # entry, here we test if it is properly stored. - resources = ["MODEL"] - - result = await hass.config_entries.flow.async_init( - DOMAIN, - context={"source": SOURCE_IMPORT}, - data=CONF_DATA | {CONF_RESOURCES: resources}, - ) - await hass.async_block_till_done() - assert result["type"] == FlowResultType.CREATE_ENTRY - assert result["title"] == MOCK_STATUS["MODEL"] - assert result["data"] == CONF_DATA | {CONF_RESOURCES: resources} - - mock_setup.assert_called_once() diff --git a/tests/components/application_credentials/test_init.py b/tests/components/application_credentials/test_init.py index 04112e2a00d..7685787d4d9 100644 --- a/tests/components/application_credentials/test_init.py +++ b/tests/components/application_credentials/test_init.py @@ -795,3 +795,32 @@ async def test_remove_config_entry_without_app_credentials( "config_entry", {"config_entry_id": entries[0].entry_id} ) assert "application_credential_id" not in result + + +async def test_websocket_create_strips_whitespace(ws_client: ClientFixture): + """Test websocket create command with whitespace in the credentials.""" + client = await ws_client() + result = await client.cmd_result( + "create", + { + CONF_DOMAIN: TEST_DOMAIN, + CONF_CLIENT_ID: f" {CLIENT_ID} ", + CONF_CLIENT_SECRET: f" {CLIENT_SECRET} ", + }, + ) + assert result == { + CONF_DOMAIN: TEST_DOMAIN, + CONF_CLIENT_ID: CLIENT_ID, + CONF_CLIENT_SECRET: CLIENT_SECRET, + "id": ID, + } + + result = await client.cmd_result("list") + assert result == [ + { + CONF_DOMAIN: TEST_DOMAIN, + CONF_CLIENT_ID: CLIENT_ID, + CONF_CLIENT_SECRET: CLIENT_SECRET, + "id": ID, + } + ] diff --git a/tests/components/aranet/test_sensor.py b/tests/components/aranet/test_sensor.py index a0edcca4803..91733b37813 100644 --- a/tests/components/aranet/test_sensor.py +++ b/tests/components/aranet/test_sensor.py @@ -25,7 +25,7 @@ async def test_sensors(hass): assert len(hass.states.async_all("sensor")) == 0 inject_bluetooth_service_info(hass, VALID_DATA_SERVICE_INFO) await hass.async_block_till_done() - assert len(hass.states.async_all("sensor")) == 6 + assert len(hass.states.async_all("sensor")) == 5 batt_sensor = hass.states.get("sensor.aranet4_12345_battery") batt_sensor_attrs = batt_sensor.attributes @@ -62,13 +62,6 @@ async def test_sensors(hass): assert press_sensor_attrs[ATTR_UNIT_OF_MEASUREMENT] == "hPa" assert press_sensor_attrs[ATTR_STATE_CLASS] == "measurement" - interval_sensor = hass.states.get("sensor.aranet4_12345_update_interval") - interval_sensor_attrs = interval_sensor.attributes - assert interval_sensor.state == "300" - assert interval_sensor_attrs[ATTR_FRIENDLY_NAME] == "Aranet4 12345 Update Interval" - assert interval_sensor_attrs[ATTR_UNIT_OF_MEASUREMENT] == "s" - assert interval_sensor_attrs[ATTR_STATE_CLASS] == "measurement" - assert await hass.config_entries.async_unload(entry.entry_id) await hass.async_block_till_done() @@ -87,7 +80,7 @@ async def test_smart_home_integration_disabled(hass): assert len(hass.states.async_all("sensor")) == 0 inject_bluetooth_service_info(hass, DISABLED_INTEGRATIONS_SERVICE_INFO) await hass.async_block_till_done() - assert len(hass.states.async_all("sensor")) == 6 + assert len(hass.states.async_all("sensor")) == 5 batt_sensor = hass.states.get("sensor.aranet4_12345_battery") assert batt_sensor.state == "unavailable" @@ -104,8 +97,5 @@ async def test_smart_home_integration_disabled(hass): press_sensor = hass.states.get("sensor.aranet4_12345_pressure") assert press_sensor.state == "unavailable" - interval_sensor = hass.states.get("sensor.aranet4_12345_update_interval") - assert interval_sensor.state == "unavailable" - assert await hass.config_entries.async_unload(entry.entry_id) await hass.async_block_till_done() diff --git a/tests/components/asuswrt/test_config_flow.py b/tests/components/asuswrt/test_config_flow.py index f9af800166a..6ba273a6c08 100644 --- a/tests/components/asuswrt/test_config_flow.py +++ b/tests/components/asuswrt/test_config_flow.py @@ -12,6 +12,7 @@ from homeassistant.components.asuswrt.const import ( CONF_SSH_KEY, CONF_TRACK_UNKNOWN, DOMAIN, + PROTOCOL_TELNET, ) from homeassistant.components.device_tracker import CONF_CONSIDER_HOME from homeassistant.config_entries import SOURCE_USER @@ -34,8 +35,8 @@ SSH_KEY = "1234" CONFIG_DATA = { CONF_HOST: HOST, - CONF_PORT: 22, - CONF_PROTOCOL: "telnet", + CONF_PORT: 23, + CONF_PROTOCOL: PROTOCOL_TELNET, CONF_USERNAME: "user", CONF_PASSWORD: "pwd", CONF_MODE: "ap", diff --git a/tests/components/asuswrt/test_sensor.py b/tests/components/asuswrt/test_sensor.py index f519b936129..1854a32fd2b 100644 --- a/tests/components/asuswrt/test_sensor.py +++ b/tests/components/asuswrt/test_sensor.py @@ -6,7 +6,11 @@ from aioasuswrt.asuswrt import Device import pytest from homeassistant.components import device_tracker, sensor -from homeassistant.components.asuswrt.const import CONF_INTERFACE, DOMAIN +from homeassistant.components.asuswrt.const import ( + CONF_INTERFACE, + DOMAIN, + PROTOCOL_TELNET, +) from homeassistant.components.asuswrt.router import DEFAULT_NAME from homeassistant.components.device_tracker import CONF_CONSIDER_HOME from homeassistant.config_entries import ConfigEntryState @@ -27,13 +31,15 @@ from homeassistant.util.dt import utcnow from tests.common import MockConfigEntry, async_fire_time_changed +ASUSWRT_LIB = "homeassistant.components.asuswrt.router.AsusWrt" + HOST = "myrouter.asuswrt.com" IP_ADDRESS = "192.168.1.1" CONFIG_DATA = { CONF_HOST: HOST, CONF_PORT: 22, - CONF_PROTOCOL: "telnet", + CONF_PROTOCOL: PROTOCOL_TELNET, CONF_USERNAME: "user", CONF_PASSWORD: "pwd", CONF_MODE: "router", @@ -50,20 +56,37 @@ MOCK_MAC_2 = "A2:B2:C2:D2:E2:F2" MOCK_MAC_3 = "A3:B3:C3:D3:E3:F3" MOCK_MAC_4 = "A4:B4:C4:D4:E4:F4" -SENSOR_NAMES = [ - "Devices Connected", +SENSORS_DEFAULT = [ "Download Speed", "Download", "Upload Speed", "Upload", +] + +SENSORS_LOADAVG = [ "Load Avg (1m)", "Load Avg (5m)", "Load Avg (15m)", +] + +SENSORS_TEMP = [ "2.4GHz Temperature", "5GHz Temperature", "CPU Temperature", ] +SENSORS_ALL = [*SENSORS_DEFAULT, *SENSORS_LOADAVG, *SENSORS_TEMP] + +PATCH_SETUP_ENTRY = patch( + "homeassistant.components.asuswrt.async_setup_entry", + return_value=True, +) + + +def new_device(mac, ip, name): + """Return a new device for specific protocol.""" + return Device(mac, ip, name) + @pytest.fixture(name="mock_devices") def mock_devices_fixture(): @@ -75,11 +98,9 @@ def mock_devices_fixture(): @pytest.fixture(name="mock_available_temps") -def mock_available_temps_list(): +def mock_available_temps_fixture(): """Mock a list of available temperature sensors.""" - - # Only length of 3 booleans is valid. First checking the exception handling. - return [True, False] + return [True, False, True] @pytest.fixture(name="create_device_registry_devices") @@ -103,13 +124,14 @@ def create_device_registry_devices_fixture(hass): @pytest.fixture(name="connect") def mock_controller_connect(mock_devices, mock_available_temps): - """Mock a successful connection.""" - with patch("homeassistant.components.asuswrt.router.AsusWrt") as service_mock: + """Mock a successful connection with AsusWrt library.""" + with patch(ASUSWRT_LIB) as service_mock: service_mock.return_value.connection.async_connect = AsyncMock() service_mock.return_value.is_connected = True service_mock.return_value.connection.disconnect = Mock() service_mock.return_value.async_get_nvram = AsyncMock( return_value={ + "label_mac": MAC_ADDR, "model": "abcd", "firmver": "efg", "buildno": "123", @@ -138,8 +160,8 @@ def mock_controller_connect(mock_devices, mock_available_temps): @pytest.fixture(name="connect_sens_fail") def mock_controller_connect_sens_fail(): - """Mock a successful connection with sensor fail.""" - with patch("homeassistant.components.asuswrt.router.AsusWrt") as service_mock: + """Mock a successful connection using AsusWrt library with sensors failing.""" + with patch(ASUSWRT_LIB) as service_mock: service_mock.return_value.connection.async_connect = AsyncMock() service_mock.return_value.is_connected = True service_mock.return_value.connection.disconnect = Mock() @@ -159,14 +181,14 @@ def mock_controller_connect_sens_fail(): yield service_mock -def _setup_entry(hass, unique_id=None): - """Create mock config entry.""" +def _setup_entry(hass, config, sensors, unique_id=None): + """Create mock config entry with enabled sensors.""" entity_reg = er.async_get(hass) # init config entry config_entry = MockConfigEntry( domain=DOMAIN, - data=CONFIG_DATA, + data=config, options={CONF_CONSIDER_HOME: 60}, unique_id=unique_id, ) @@ -176,27 +198,13 @@ def _setup_entry(hass, unique_id=None): sensor_prefix = f"{sensor.DOMAIN}.{obj_prefix}" # Pre-enable the status sensor - for sensor_name in SENSOR_NAMES: + for sensor_name in sensors: sensor_id = slugify(sensor_name) entity_reg.async_get_or_create( sensor.DOMAIN, DOMAIN, f"{DOMAIN} {unique_id or DEFAULT_NAME} {sensor_name}", suggested_object_id=f"{obj_prefix}_{sensor_id}", - disabled_by=None, - ) - - # Create the first device tracker to test mac conversion - for mac, name in { - MOCK_MAC_1: "test", - dr.format_mac(MOCK_MAC_2): "testtwo", - MOCK_MAC_2: "testremove", - }.items(): - entity_reg.async_get_or_create( - device_tracker.DOMAIN, - DOMAIN, - mac, - suggested_object_id=name, config_entry=config_entry, disabled_by=None, ) @@ -212,12 +220,30 @@ async def test_sensors( hass, connect, mock_devices, - mock_available_temps, create_device_registry_devices, entry_unique_id, ): - """Test creating an AsusWRT sensor.""" - config_entry, sensor_prefix = _setup_entry(hass, entry_unique_id) + """Test creating AsusWRT default sensors and tracker.""" + config_entry, sensor_prefix = _setup_entry( + hass, CONFIG_DATA, SENSORS_DEFAULT, entry_unique_id + ) + + # Create the first device tracker to test mac conversion + entity_reg = er.async_get(hass) + for mac, name in { + MOCK_MAC_1: "test", + dr.format_mac(MOCK_MAC_2): "testtwo", + MOCK_MAC_2: "testremove", + }.items(): + entity_reg.async_get_or_create( + device_tracker.DOMAIN, + DOMAIN, + mac, + suggested_object_id=name, + config_entry=config_entry, + disabled_by=None, + ) + config_entry.add_to_hass(hass) # initial devices setup @@ -232,17 +258,9 @@ async def test_sensors( assert hass.states.get(f"{sensor_prefix}_download").state == "60.0" assert hass.states.get(f"{sensor_prefix}_upload_speed").state == "80.0" assert hass.states.get(f"{sensor_prefix}_upload").state == "50.0" - assert hass.states.get(f"{sensor_prefix}_load_avg_1m").state == "1.1" - assert hass.states.get(f"{sensor_prefix}_load_avg_5m").state == "1.2" - assert hass.states.get(f"{sensor_prefix}_load_avg_15m").state == "1.3" assert hass.states.get(f"{sensor_prefix}_devices_connected").state == "2" - # assert temperature availability exception is handled correctly - assert not hass.states.get(f"{sensor_prefix}_2_4ghz_temperature") - assert not hass.states.get(f"{sensor_prefix}_5ghz_temperature") - assert not hass.states.get(f"{sensor_prefix}_cpu_temperature") - - # remove first track device + # remove first tracked device mock_devices.pop(MOCK_MAC_1) async_fire_time_changed(hass, utcnow() + timedelta(seconds=30)) @@ -253,11 +271,11 @@ async def test_sensors( assert hass.states.get(f"{device_tracker.DOMAIN}.testtwo").state == STATE_HOME assert hass.states.get(f"{sensor_prefix}_devices_connected").state == "1" - # add 2 new device, one unnamed that should be ignored but counted - mock_devices[MOCK_MAC_3] = Device(MOCK_MAC_3, "192.168.1.4", "TestThree") - mock_devices[MOCK_MAC_4] = Device(MOCK_MAC_4, "192.168.1.5", None) + # add 2 new devices, one unnamed that should be ignored but counted + mock_devices[MOCK_MAC_3] = new_device(MOCK_MAC_3, "192.168.1.4", "TestThree") + mock_devices[MOCK_MAC_4] = new_device(MOCK_MAC_4, "192.168.1.5", None) - # change consider home settings to have status not home of removed track device + # change consider home settings to have status not home of removed tracked device hass.config_entries.async_update_entry( config_entry, options={CONF_CONSIDER_HOME: 0} ) @@ -271,23 +289,67 @@ async def test_sensors( assert hass.states.get(f"{device_tracker.DOMAIN}.testthree").state == STATE_HOME assert hass.states.get(f"{sensor_prefix}_devices_connected").state == "3" - # checking temperature sensors without exceptions - mock_available_temps.append(True) - await hass.config_entries.async_reload(config_entry.entry_id) + +async def test_loadavg_sensors( + hass, + connect, +): + """Test creating an AsusWRT load average sensors.""" + config_entry, sensor_prefix = _setup_entry(hass, CONFIG_DATA, SENSORS_LOADAVG) + config_entry.add_to_hass(hass) + + # initial devices setup + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + async_fire_time_changed(hass, utcnow() + timedelta(seconds=30)) await hass.async_block_till_done() + # assert temperature sensor available + assert hass.states.get(f"{sensor_prefix}_load_avg_1m").state == "1.1" + assert hass.states.get(f"{sensor_prefix}_load_avg_5m").state == "1.2" + assert hass.states.get(f"{sensor_prefix}_load_avg_15m").state == "1.3" + + +async def test_temperature_sensors_fail( + hass, + connect, + mock_available_temps, +): + """Test fail creating AsusWRT temperature sensors.""" + config_entry, sensor_prefix = _setup_entry(hass, CONFIG_DATA, SENSORS_TEMP) + config_entry.add_to_hass(hass) + + # Only length of 3 booleans is valid. Checking the exception handling. + mock_available_temps.pop(2) + + # initial devices setup + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + # assert temperature availability exception is handled correctly + assert not hass.states.get(f"{sensor_prefix}_2_4ghz_temperature") + assert not hass.states.get(f"{sensor_prefix}_5ghz_temperature") + assert not hass.states.get(f"{sensor_prefix}_cpu_temperature") + + +async def test_temperature_sensors( + hass, + connect, +): + """Test creating a AsusWRT temperature sensors.""" + config_entry, sensor_prefix = _setup_entry(hass, CONFIG_DATA, SENSORS_TEMP) + config_entry.add_to_hass(hass) + + # initial devices setup + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + async_fire_time_changed(hass, utcnow() + timedelta(seconds=30)) + await hass.async_block_till_done() + + # assert temperature sensor available assert hass.states.get(f"{sensor_prefix}_2_4ghz_temperature").state == "40.0" assert not hass.states.get(f"{sensor_prefix}_5ghz_temperature") assert hass.states.get(f"{sensor_prefix}_cpu_temperature").state == "71.2" - assert hass.states.get(f"{sensor_prefix}_devices_connected").state == "3" - - # change an option that require integration reload - hass.config_entries.async_update_entry( - config_entry, options={CONF_CONSIDER_HOME: 60, CONF_INTERFACE: "eth1"} - ) - await hass.async_block_till_done() - assert config_entry.state is ConfigEntryState.LOADED - assert hass.states.get(f"{sensor_prefix}_devices_connected").state == "3" @pytest.mark.parametrize( @@ -304,10 +366,11 @@ async def test_connect_fail(hass, side_effect): ) config_entry.add_to_hass(hass) - with patch("homeassistant.components.asuswrt.router.AsusWrt") as asus_wrt: + with patch(ASUSWRT_LIB) as asus_wrt: asus_wrt.return_value.connection.async_connect = AsyncMock( side_effect=side_effect ) + asus_wrt.return_value.async_get_nvram = AsyncMock() asus_wrt.return_value.is_connected = False # initial setup fail @@ -316,12 +379,9 @@ async def test_connect_fail(hass, side_effect): assert config_entry.state is ConfigEntryState.SETUP_RETRY -async def test_sensors_polling_fails( - hass, - connect_sens_fail, -): +async def test_sensors_polling_fails(hass, connect_sens_fail): """Test AsusWRT sensors are unavailable when polling fails.""" - config_entry, sensor_prefix = _setup_entry(hass) + config_entry, sensor_prefix = _setup_entry(hass, CONFIG_DATA, SENSORS_ALL) config_entry.add_to_hass(hass) # initial devices setup @@ -330,21 +390,30 @@ async def test_sensors_polling_fails( async_fire_time_changed(hass, utcnow() + timedelta(seconds=30)) await hass.async_block_till_done() - assert hass.states.get(f"{sensor_prefix}_download_speed").state == STATE_UNAVAILABLE - assert hass.states.get(f"{sensor_prefix}_download").state == STATE_UNAVAILABLE - assert hass.states.get(f"{sensor_prefix}_upload_speed").state == STATE_UNAVAILABLE - assert hass.states.get(f"{sensor_prefix}_upload").state == STATE_UNAVAILABLE - assert hass.states.get(f"{sensor_prefix}_load_avg_1m").state == STATE_UNAVAILABLE - assert hass.states.get(f"{sensor_prefix}_load_avg_5m").state == STATE_UNAVAILABLE - assert hass.states.get(f"{sensor_prefix}_load_avg_15m").state == STATE_UNAVAILABLE + for sensor_name in SENSORS_ALL: + assert ( + hass.states.get(f"{sensor_prefix}_{slugify(sensor_name)}").state + == STATE_UNAVAILABLE + ) assert hass.states.get(f"{sensor_prefix}_devices_connected").state == "0" - assert ( - hass.states.get(f"{sensor_prefix}_2_4ghz_temperature").state - == STATE_UNAVAILABLE - ) - assert ( - hass.states.get(f"{sensor_prefix}_5ghz_temperature").state == STATE_UNAVAILABLE - ) - assert ( - hass.states.get(f"{sensor_prefix}_cpu_temperature").state == STATE_UNAVAILABLE - ) + + +async def test_options_reload(hass, connect): + """Test AsusWRT integration is reload changing an options that require this.""" + config_entry = MockConfigEntry(domain=DOMAIN, data=CONFIG_DATA, unique_id=MAC_ADDR) + config_entry.add_to_hass(hass) + + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + async_fire_time_changed(hass, utcnow() + timedelta(seconds=30)) + await hass.async_block_till_done() + + with PATCH_SETUP_ENTRY as setup_entry_call: + # change an option that requires integration reload + hass.config_entries.async_update_entry( + config_entry, options={CONF_INTERFACE: "eth1"} + ) + await hass.async_block_till_done() + + assert setup_entry_call.called + assert config_entry.state is ConfigEntryState.LOADED diff --git a/tests/components/aurora/test_config_flow.py b/tests/components/aurora/test_config_flow.py index f106b19cea9..8d2c6054a21 100644 --- a/tests/components/aurora/test_config_flow.py +++ b/tests/components/aurora/test_config_flow.py @@ -110,5 +110,4 @@ async def test_option_flow(hass): ) assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY - assert result["title"] == "" assert result["data"]["forecast_threshold"] == 65 diff --git a/tests/components/automation/test_init.py b/tests/components/automation/test_init.py index 1ce6cb616da..5ec77a43e8c 100644 --- a/tests/components/automation/test_init.py +++ b/tests/components/automation/test_init.py @@ -1332,20 +1332,81 @@ async def test_automation_not_trigger_on_bootstrap(hass): assert ["hello.world"] == calls[0].data.get(ATTR_ENTITY_ID) -async def test_automation_bad_trigger(hass, caplog): - """Test bad trigger configuration.""" +@pytest.mark.parametrize( + "broken_config, problem, details", + ( + ( + {}, + "could not be validated", + "required key not provided @ data['action']", + ), + ( + { + "trigger": {"platform": "automation"}, + "action": [], + }, + "failed to setup triggers", + "Integration 'automation' does not provide trigger support.", + ), + ( + { + "trigger": {"platform": "event", "event_type": "test_event"}, + "condition": { + "condition": "state", + # The UUID will fail being resolved to en entity_id + "entity_id": "abcdabcdabcdabcdabcdabcdabcdabcd", + "state": "blah", + }, + "action": [], + }, + "failed to setup conditions", + "Unknown entity registry entry abcdabcdabcdabcdabcdabcdabcdabcd.", + ), + ( + { + "trigger": {"platform": "event", "event_type": "test_event"}, + "action": { + "condition": "state", + # The UUID will fail being resolved to en entity_id + "entity_id": "abcdabcdabcdabcdabcdabcdabcdabcd", + "state": "blah", + }, + }, + "failed to setup actions", + "Unknown entity registry entry abcdabcdabcdabcdabcdabcdabcdabcd.", + ), + ), +) +async def test_automation_bad_config_validation( + hass: HomeAssistant, caplog, broken_config, problem, details +): + """Test bad automation configuration which can be detected during validation.""" assert await async_setup_component( hass, automation.DOMAIN, { - automation.DOMAIN: { - "alias": "hello", - "trigger": {"platform": "automation"}, - "action": [], - } + automation.DOMAIN: [ + {"alias": "bad_automation", **broken_config}, + { + "alias": "good_automation", + "trigger": {"platform": "event", "event_type": "test_event"}, + "action": { + "service": "test.automation", + "entity_id": "hello.world", + }, + }, + ] }, ) - assert "Integration 'automation' does not provide trigger support." in caplog.text + + # Check we get the expected error message + assert ( + f"Automation with alias 'bad_automation' {problem} and has been disabled: {details}" + in caplog.text + ) + + # Make sure one bad automation does not prevent other automations from setting up + assert hass.states.async_entity_ids("automation") == ["automation.good_automation"] async def test_automation_with_error_in_script( @@ -1885,7 +1946,36 @@ async def test_blueprint_automation(hass, calls): ] -async def test_blueprint_automation_bad_config(hass, caplog): +@pytest.mark.parametrize( + "blueprint_inputs, problem, details", + ( + ( + # No input + {}, + "Failed to generate automation from blueprint", + "Missing input a_number, service_to_call, trigger_event", + ), + ( + # Missing input + {"trigger_event": "blueprint_event", "a_number": 5}, + "Failed to generate automation from blueprint", + "Missing input service_to_call", + ), + ( + # Wrong input + { + "trigger_event": "blueprint_event", + "service_to_call": {"dict": "not allowed"}, + "a_number": 5, + }, + "Blueprint 'Call service based on event' generated invalid automation", + "value should be a string for dictionary value @ data['action'][0]['service']", + ), + ), +) +async def test_blueprint_automation_bad_config( + hass, caplog, blueprint_inputs, problem, details +): """Test blueprint automation with bad inputs.""" assert await async_setup_component( hass, @@ -1894,16 +1984,42 @@ async def test_blueprint_automation_bad_config(hass, caplog): "automation": { "use_blueprint": { "path": "test_event_service.yaml", - "input": { - "trigger_event": "blueprint_event", - "service_to_call": {"dict": "not allowed"}, - "a_number": 5, - }, + "input": blueprint_inputs, } } }, ) - assert "generated invalid automation" in caplog.text + assert problem in caplog.text + assert details in caplog.text + + +async def test_blueprint_automation_fails_substitution(hass, caplog): + """Test blueprint automation with bad inputs.""" + with patch( + "homeassistant.components.blueprint.models.BlueprintInputs.async_substitute", + side_effect=yaml.UndefinedSubstitution("blah"), + ): + assert await async_setup_component( + hass, + "automation", + { + "automation": { + "use_blueprint": { + "path": "test_event_service.yaml", + "input": { + "trigger_event": "test_event", + "service_to_call": "test.automation", + "a_number": 5, + }, + } + } + }, + ) + assert ( + "Blueprint 'Call service based on event' failed to generate automation with inputs " + "{'trigger_event': 'test_event', 'service_to_call': 'test.automation', 'a_number': 5}:" + " No substitution found for input blah" in caplog.text + ) async def test_trigger_service(hass, calls): diff --git a/tests/components/awair/test_sensor.py b/tests/components/awair/test_sensor.py index 162b68d913b..32a4578bebc 100644 --- a/tests/components/awair/test_sensor.py +++ b/tests/components/awair/test_sensor.py @@ -287,7 +287,7 @@ async def test_awair_omni_sensors(hass: HomeAssistant, user, cloud_devices, omni "sensor.living_room_sound_level", f"{AWAIR_UUID}_{SENSOR_TYPES_MAP[API_SPL_A].unique_id_tag}", "47.0", - {ATTR_UNIT_OF_MEASUREMENT: "dBa"}, + {ATTR_UNIT_OF_MEASUREMENT: "dBA"}, ) assert_expected_properties( diff --git a/tests/components/blebox/conftest.py b/tests/components/blebox/conftest.py index 548c7a5dc38..4bda47bb414 100644 --- a/tests/components/blebox/conftest.py +++ b/tests/components/blebox/conftest.py @@ -8,7 +8,6 @@ import pytest from homeassistant.components.blebox.const import DOMAIN from homeassistant.const import CONF_HOST, CONF_PORT from homeassistant.helpers import entity_registry as er -from homeassistant.setup import async_setup_component from tests.common import MockConfigEntry from tests.components.light.conftest import mock_light_profiles # noqa: F401 @@ -76,18 +75,20 @@ def feature_fixture(request): return request.getfixturevalue(request.param) -async def async_setup_entities(hass, config, entity_ids): +async def async_setup_entities(hass, entity_ids): """Return configured entries with the given entity ids.""" config_entry = mock_config() config_entry.add_to_hass(hass) - assert await async_setup_component(hass, DOMAIN, config) + + assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() + entity_registry = er.async_get(hass) return [entity_registry.async_get(entity_id) for entity_id in entity_ids] -async def async_setup_entity(hass, config, entity_id): +async def async_setup_entity(hass, entity_id): """Return a configured entry with the given entity_id.""" - return (await async_setup_entities(hass, config, [entity_id]))[0] + return (await async_setup_entities(hass, [entity_id]))[0] diff --git a/tests/components/blebox/test_binary_sensor.py b/tests/components/blebox/test_binary_sensor.py index c9181762f3e..662697305e1 100644 --- a/tests/components/blebox/test_binary_sensor.py +++ b/tests/components/blebox/test_binary_sensor.py @@ -28,10 +28,10 @@ def airsensor_fixture() -> tuple[AsyncMock, str]: return feature, "binary_sensor.windrainsensor_0_rain" -async def test_init(rainsensor: AsyncMock, hass: HomeAssistant, config: dict): +async def test_init(rainsensor: AsyncMock, hass: HomeAssistant): """Test binary_sensor initialisation.""" _, entity_id = rainsensor - entry = await async_setup_entity(hass, config, entity_id) + entry = await async_setup_entity(hass, entity_id) assert entry.unique_id == "BleBox-windRainSensor-ea68e74f4f49-0.rain" state = hass.states.get(entity_id) diff --git a/tests/components/blebox/test_button.py b/tests/components/blebox/test_button.py index 8d89f31d260..12ef6adcc7a 100644 --- a/tests/components/blebox/test_button.py +++ b/tests/components/blebox/test_button.py @@ -39,12 +39,12 @@ def tv_lift_box_fixture(caplog): return (feature, "button.tvliftbox_open_or_stop") -async def test_tvliftbox_init(tvliftbox, hass, config, caplog): +async def test_tvliftbox_init(tvliftbox, hass, caplog): """Test tvLiftBox initialisation.""" caplog.set_level(logging.ERROR) _, entity_id = tvliftbox - entry = await async_setup_entity(hass, config, entity_id) + entry = await async_setup_entity(hass, entity_id) state = hass.states.get(entity_id) assert entry.unique_id == "BleBox-tvLiftBox-4a3fdaad90aa-open_or_stop" @@ -53,13 +53,13 @@ async def test_tvliftbox_init(tvliftbox, hass, config, caplog): @pytest.mark.parametrize("input", query_icon_matching) -async def test_get_icon(input, tvliftbox, hass, config, caplog): +async def test_get_icon(input, tvliftbox, hass, caplog): """Test if proper icon is returned.""" caplog.set_level(logging.ERROR) feature_mock, entity_id = tvliftbox feature_mock.query_string = input[0] - _ = await async_setup_entity(hass, config, entity_id) + _ = await async_setup_entity(hass, entity_id) state = hass.states.get(entity_id) assert state.attributes[ATTR_ICON] == input[1] diff --git a/tests/components/blebox/test_climate.py b/tests/components/blebox/test_climate.py index 7bef83e632a..229a24c44e0 100644 --- a/tests/components/blebox/test_climate.py +++ b/tests/components/blebox/test_climate.py @@ -43,6 +43,8 @@ def saunabox_fixture(): current=None, min_temp=-54.3, max_temp=124.3, + mode=None, + hvac_action=None, ) product = feature.product type(product).name = PropertyMock(return_value="My sauna") @@ -50,11 +52,34 @@ def saunabox_fixture(): return (feature, "climate.saunabox_thermostat") -async def test_init(saunabox, hass, config): +@pytest.fixture(name="thermobox") +def thermobox_fixture(): + """Return a default climate entity mock.""" + feature = mock_feature( + "climates", + blebox_uniapi.climate.Climate, + unique_id="BleBox-thermoBox-1afe34db9437-thermostat", + full_name="thermoBox-thermostat", + device_class=None, + is_on=None, + desired=None, + current=None, + min_temp=-54.3, + max_temp=124.3, + mode=2, + hvac_action=1, + ) + product = feature.product + type(product).name = PropertyMock(return_value="My thermo") + type(product).model = PropertyMock(return_value="thermoBox") + return (feature, "climate.thermobox_thermostat") + + +async def test_init(saunabox, hass): """Test default state.""" _, entity_id = saunabox - entry = await async_setup_entity(hass, config, entity_id) + entry = await async_setup_entity(hass, entity_id) assert entry.unique_id == "BleBox-saunaBox-1afe34db9437-thermostat" state = hass.states.get(entity_id) @@ -63,7 +88,7 @@ async def test_init(saunabox, hass, config): supported_features = state.attributes[ATTR_SUPPORTED_FEATURES] assert supported_features & ClimateEntityFeature.TARGET_TEMPERATURE - assert state.attributes[ATTR_HVAC_MODES] == [HVACMode.OFF, HVACMode.HEAT] + assert state.attributes[ATTR_HVAC_MODES] == [HVACMode.OFF, None] assert ATTR_DEVICE_CLASS not in state.attributes assert ATTR_HVAC_MODE not in state.attributes @@ -97,7 +122,7 @@ async def test_update(saunabox, hass, config): feature_mock.current = 40.9 feature_mock.async_update = AsyncMock(side_effect=initial_update) - await async_setup_entity(hass, config, entity_id) + await async_setup_entity(hass, entity_id) state = hass.states.get(entity_id) assert state.attributes[ATTR_HVAC_ACTION] == HVACAction.OFF @@ -106,7 +131,7 @@ async def test_update(saunabox, hass, config): assert state.state == HVACMode.OFF -async def test_on_when_below_desired(saunabox, hass, config): +async def test_on_when_below_desired(saunabox, hass): """Test when temperature is below desired.""" feature_mock, entity_id = saunabox @@ -115,7 +140,7 @@ async def test_on_when_below_desired(saunabox, hass, config): feature_mock.is_on = False feature_mock.async_update = AsyncMock(side_effect=initial_update) - await async_setup_entity(hass, config, entity_id) + await async_setup_entity(hass, entity_id) feature_mock.async_update = AsyncMock() def turn_on(): @@ -140,7 +165,7 @@ async def test_on_when_below_desired(saunabox, hass, config): assert state.state == HVACMode.HEAT -async def test_on_when_above_desired(saunabox, hass, config): +async def test_on_when_above_desired(saunabox, hass): """Test when temperature is below desired.""" feature_mock, entity_id = saunabox @@ -149,7 +174,7 @@ async def test_on_when_above_desired(saunabox, hass, config): feature_mock.is_on = False feature_mock.async_update = AsyncMock(side_effect=initial_update) - await async_setup_entity(hass, config, entity_id) + await async_setup_entity(hass, entity_id) feature_mock.async_update = AsyncMock() def turn_on(): @@ -175,7 +200,7 @@ async def test_on_when_above_desired(saunabox, hass, config): assert state.state == HVACMode.HEAT -async def test_off(saunabox, hass, config): +async def test_off(saunabox, hass): """Test turning off.""" feature_mock, entity_id = saunabox @@ -185,7 +210,7 @@ async def test_off(saunabox, hass, config): feature_mock.is_heating = False feature_mock.async_update = AsyncMock(side_effect=initial_update) - await async_setup_entity(hass, config, entity_id) + await async_setup_entity(hass, entity_id) feature_mock.async_update = AsyncMock() def turn_off(): @@ -210,7 +235,7 @@ async def test_off(saunabox, hass, config): assert state.state == HVACMode.OFF -async def test_set_thermo(saunabox, hass, config): +async def test_set_thermo(saunabox, hass): """Test setting thermostat.""" feature_mock, entity_id = saunabox @@ -220,7 +245,7 @@ async def test_set_thermo(saunabox, hass, config): feature_mock.is_heating = False feature_mock.async_update = AsyncMock(side_effect=update) - await async_setup_entity(hass, config, entity_id) + await async_setup_entity(hass, entity_id) feature_mock.async_update = AsyncMock() def set_temp(temp): @@ -244,13 +269,63 @@ async def test_set_thermo(saunabox, hass, config): assert state.state == HVACMode.HEAT -async def test_update_failure(saunabox, hass, config, caplog): +async def test_update_failure(saunabox, hass, caplog): """Test that update failures are logged.""" caplog.set_level(logging.ERROR) feature_mock, entity_id = saunabox feature_mock.async_update = AsyncMock(side_effect=blebox_uniapi.error.ClientError) - await async_setup_entity(hass, config, entity_id) + await async_setup_entity(hass, entity_id) assert f"Updating '{feature_mock.full_name}' failed: " in caplog.text + + +async def test_reding_hvac_actions(saunabox, hass, caplog): + """Test hvac action for given device(mock) state.""" + + caplog.set_level(logging.ERROR) + + feature_mock, entity_id = saunabox + await async_setup_entity(hass, entity_id) + + def set_heating(): + feature_mock.is_on = True + feature_mock.hvac_action = 1 + feature_mock.mode = 1 + + feature_mock.async_update = AsyncMock(side_effect=set_heating) + + await hass.services.async_call( + "climate", + SERVICE_SET_TEMPERATURE, + {"entity_id": entity_id, ATTR_TEMPERATURE: 43.21}, + blocking=True, + ) + state = hass.states.get(entity_id) + assert state.attributes[ATTR_HVAC_ACTION] == HVACAction.HEATING + assert state.attributes[ATTR_HVAC_MODES] == [HVACMode.OFF, HVACMode.HEAT] + + +async def test_thermo_off(thermobox, hass, caplog): + """Test hvac action off fir given device state.""" + caplog.set_level(logging.ERROR) + + feature_mock, entity_id = thermobox + await async_setup_entity(hass, entity_id) + + def set_off(): + feature_mock.is_on = False + feature_mock.hvac_action = 0 + + feature_mock.async_update = AsyncMock(side_effect=set_off) + + await hass.services.async_call( + "climate", + SERVICE_SET_HVAC_MODE, + {"entity_id": entity_id, ATTR_HVAC_MODE: HVACMode.OFF}, + blocking=True, + ) + state = hass.states.get(entity_id) + assert state.attributes[ATTR_HVAC_ACTION] == HVACAction.OFF + assert state.attributes[ATTR_HVAC_MODES] == [HVACMode.OFF, HVACMode.COOL] diff --git a/tests/components/blebox/test_config_flow.py b/tests/components/blebox/test_config_flow.py index 7db216e294e..030da33fcfe 100644 --- a/tests/components/blebox/test_config_flow.py +++ b/tests/components/blebox/test_config_flow.py @@ -6,10 +6,14 @@ import blebox_uniapi import pytest from homeassistant import config_entries, data_entry_flow +from homeassistant.components import zeroconf from homeassistant.components.blebox import config_flow +from homeassistant.const import CONF_IP_ADDRESS +from homeassistant.data_entry_flow import FlowResultType from homeassistant.setup import async_setup_component -from .conftest import mock_config, mock_only_feature, setup_product_mock +from ...common import MockConfigEntry +from .conftest import mock_config, mock_feature, mock_only_feature, setup_product_mock def create_valid_feature_mock(path="homeassistant.components.blebox.Products"): @@ -190,3 +194,110 @@ async def test_async_remove_entry(hass, valid_feature_mock): assert hass.config_entries.async_entries() == [] assert config.state is config_entries.ConfigEntryState.NOT_LOADED + + +async def test_flow_with_zeroconf(hass): + """Test setup from zeroconf discovery.""" + result = await hass.config_entries.flow.async_init( + config_flow.DOMAIN, + context={"source": config_entries.SOURCE_ZEROCONF}, + data=zeroconf.ZeroconfServiceInfo( + host="172.100.123.4", + addresses=["172.100.123.4"], + port=80, + hostname="bbx-bbtest123456.local.", + type="_bbxsrv._tcp.local.", + name="bbx-bbtest123456._bbxsrv._tcp.local.", + properties={"_raw": {}}, + ), + ) + + assert result["type"] == FlowResultType.FORM + + with patch("homeassistant.components.blebox.async_setup_entry", return_value=True): + result2 = await hass.config_entries.flow.async_configure(result["flow_id"], {}) + await hass.async_block_till_done() + + assert result2["type"] == FlowResultType.CREATE_ENTRY + assert result2["data"] == {"host": "172.100.123.4", "port": 80} + + +async def test_flow_with_zeroconf_when_already_configured(hass): + """Test behaviour if device already configured.""" + entry = MockConfigEntry( + domain=config_flow.DOMAIN, + data={CONF_IP_ADDRESS: "172.100.123.4"}, + unique_id="abcd0123ef5678", + ) + entry.add_to_hass(hass) + feature: AsyncMock = mock_feature( + "sensors", + blebox_uniapi.sensor.Temperature, + ) + with patch( + "homeassistant.components.blebox.config_flow.Box.async_from_host", + return_value=feature.product, + ): + result2 = await hass.config_entries.flow.async_init( + config_flow.DOMAIN, + context={"source": config_entries.SOURCE_ZEROCONF}, + data=zeroconf.ZeroconfServiceInfo( + host="172.100.123.4", + addresses=["172.100.123.4"], + port=80, + hostname="bbx-bbtest123456.local.", + type="_bbxsrv._tcp.local.", + name="bbx-bbtest123456._bbxsrv._tcp.local.", + properties={"_raw": {}}, + ), + ) + + assert result2["type"] == FlowResultType.ABORT + assert result2["reason"] == "already_configured" + + +async def test_flow_with_zeroconf_when_device_unsupported(hass): + """Test behaviour when device is not supported.""" + with patch( + "homeassistant.components.blebox.config_flow.Box.async_from_host", + side_effect=blebox_uniapi.error.UnsupportedBoxVersion, + ): + result = await hass.config_entries.flow.async_init( + config_flow.DOMAIN, + context={"source": config_entries.SOURCE_ZEROCONF}, + data=zeroconf.ZeroconfServiceInfo( + host="172.100.123.4", + addresses=["172.100.123.4"], + port=80, + hostname="bbx-bbtest123456.local.", + type="_bbxsrv._tcp.local.", + name="bbx-bbtest123456._bbxsrv._tcp.local.", + properties={"_raw": {}}, + ), + ) + assert result["type"] == data_entry_flow.FlowResultType.ABORT + assert result["reason"] == "unsupported_device_version" + + +async def test_flow_with_zeroconf_when_device_response_unsupported(hass): + """Test behaviour when device returned unsupported response.""" + + with patch( + "homeassistant.components.blebox.config_flow.Box.async_from_host", + side_effect=blebox_uniapi.error.UnsupportedBoxResponse, + ): + result = await hass.config_entries.flow.async_init( + config_flow.DOMAIN, + context={"source": config_entries.SOURCE_ZEROCONF}, + data=zeroconf.ZeroconfServiceInfo( + host="172.100.123.4", + addresses=["172.100.123.4"], + port=80, + hostname="bbx-bbtest123456.local.", + type="_bbxsrv._tcp.local.", + name="bbx-bbtest123456._bbxsrv._tcp.local.", + properties={"_raw": {}}, + ), + ) + assert result["type"] == data_entry_flow.FlowResultType.ABORT + assert result["reason"] == "unsupported_device_response" diff --git a/tests/components/blebox/test_cover.py b/tests/components/blebox/test_cover.py index 86655cfbd0a..46f7151c5ab 100644 --- a/tests/components/blebox/test_cover.py +++ b/tests/components/blebox/test_cover.py @@ -92,11 +92,11 @@ def gate_fixture(): return (feature, "cover.gatecontroller_position") -async def test_init_gatecontroller(gatecontroller, hass, config): +async def test_init_gatecontroller(gatecontroller, hass): """Test gateController default state.""" _, entity_id = gatecontroller - entry = await async_setup_entity(hass, config, entity_id) + entry = await async_setup_entity(hass, entity_id) assert entry.unique_id == "BleBox-gateController-2bee34e750b8-position" state = hass.states.get(entity_id) @@ -122,11 +122,11 @@ async def test_init_gatecontroller(gatecontroller, hass, config): assert device.sw_version == "1.23" -async def test_init_shutterbox(shutterbox, hass, config): +async def test_init_shutterbox(shutterbox, hass): """Test gateBox default state.""" _, entity_id = shutterbox - entry = await async_setup_entity(hass, config, entity_id) + entry = await async_setup_entity(hass, entity_id) assert entry.unique_id == "BleBox-shutterBox-2bee34e750b8-position" state = hass.states.get(entity_id) @@ -152,11 +152,11 @@ async def test_init_shutterbox(shutterbox, hass, config): assert device.sw_version == "1.23" -async def test_init_gatebox(gatebox, hass, config): +async def test_init_gatebox(gatebox, hass): """Test cover default state.""" _, entity_id = gatebox - entry = await async_setup_entity(hass, config, entity_id) + entry = await async_setup_entity(hass, entity_id) assert entry.unique_id == "BleBox-gateBox-1afe34db9437-position" state = hass.states.get(entity_id) @@ -185,7 +185,7 @@ async def test_init_gatebox(gatebox, hass, config): @pytest.mark.parametrize("feature", ALL_COVER_FIXTURES, indirect=["feature"]) -async def test_open(feature, hass, config): +async def test_open(feature, hass): """Test cover opening.""" feature_mock, entity_id = feature @@ -199,7 +199,7 @@ async def test_open(feature, hass, config): feature_mock.async_update = AsyncMock(side_effect=initial_update) feature_mock.async_open = AsyncMock(side_effect=open_gate) - await async_setup_entity(hass, config, entity_id) + await async_setup_entity(hass, entity_id) assert hass.states.get(entity_id).state == STATE_CLOSED feature_mock.async_update = AsyncMock() @@ -213,7 +213,7 @@ async def test_open(feature, hass, config): @pytest.mark.parametrize("feature", ALL_COVER_FIXTURES, indirect=["feature"]) -async def test_close(feature, hass, config): +async def test_close(feature, hass): """Test cover closing.""" feature_mock, entity_id = feature @@ -227,7 +227,7 @@ async def test_close(feature, hass, config): feature_mock.async_update = AsyncMock(side_effect=initial_update) feature_mock.async_close = AsyncMock(side_effect=close) - await async_setup_entity(hass, config, entity_id) + await async_setup_entity(hass, entity_id) assert hass.states.get(entity_id).state == STATE_OPEN feature_mock.async_update = AsyncMock() @@ -251,13 +251,13 @@ def opening_to_stop_feature_mock(feature_mock): @pytest.mark.parametrize("feature", FIXTURES_SUPPORTING_STOP, indirect=["feature"]) -async def test_stop(feature, hass, config): +async def test_stop(feature, hass): """Test cover stopping.""" feature_mock, entity_id = feature opening_to_stop_feature_mock(feature_mock) - await async_setup_entity(hass, config, entity_id) + await async_setup_entity(hass, entity_id) assert hass.states.get(entity_id).state == STATE_OPENING feature_mock.async_update = AsyncMock() @@ -268,7 +268,7 @@ async def test_stop(feature, hass, config): @pytest.mark.parametrize("feature", ALL_COVER_FIXTURES, indirect=["feature"]) -async def test_update(feature, hass, config): +async def test_update(feature, hass): """Test cover updating.""" feature_mock, entity_id = feature @@ -279,7 +279,7 @@ async def test_update(feature, hass, config): feature_mock.async_update = AsyncMock(side_effect=initial_update) - await async_setup_entity(hass, config, entity_id) + await async_setup_entity(hass, entity_id) state = hass.states.get(entity_id) assert state.attributes[ATTR_CURRENT_POSITION] == 71 # 100 - 29 @@ -289,7 +289,7 @@ async def test_update(feature, hass, config): @pytest.mark.parametrize( "feature", ["gatecontroller", "shutterbox"], indirect=["feature"] ) -async def test_set_position(feature, hass, config): +async def test_set_position(feature, hass): """Test cover position setting.""" feature_mock, entity_id = feature @@ -305,7 +305,7 @@ async def test_set_position(feature, hass, config): feature_mock.async_update = AsyncMock(side_effect=initial_update) feature_mock.async_set_position = AsyncMock(side_effect=set_position) - await async_setup_entity(hass, config, entity_id) + await async_setup_entity(hass, entity_id) assert hass.states.get(entity_id).state == STATE_CLOSED feature_mock.async_update = AsyncMock() @@ -318,7 +318,7 @@ async def test_set_position(feature, hass, config): assert hass.states.get(entity_id).state == STATE_OPENING -async def test_unknown_position(shutterbox, hass, config): +async def test_unknown_position(shutterbox, hass): """Test cover position setting.""" feature_mock, entity_id = shutterbox @@ -329,35 +329,35 @@ async def test_unknown_position(shutterbox, hass, config): feature_mock.async_update = AsyncMock(side_effect=initial_update) - await async_setup_entity(hass, config, entity_id) + await async_setup_entity(hass, entity_id) state = hass.states.get(entity_id) assert state.state == STATE_OPEN assert ATTR_CURRENT_POSITION not in state.attributes -async def test_with_stop(gatebox, hass, config): +async def test_with_stop(gatebox, hass): """Test stop capability is available.""" feature_mock, entity_id = gatebox opening_to_stop_feature_mock(feature_mock) feature_mock.has_stop = True - await async_setup_entity(hass, config, entity_id) + await async_setup_entity(hass, entity_id) state = hass.states.get(entity_id) supported_features = state.attributes[ATTR_SUPPORTED_FEATURES] assert supported_features & CoverEntityFeature.STOP -async def test_with_no_stop(gatebox, hass, config): +async def test_with_no_stop(gatebox, hass): """Test stop capability is not available.""" feature_mock, entity_id = gatebox opening_to_stop_feature_mock(feature_mock) feature_mock.has_stop = False - await async_setup_entity(hass, config, entity_id) + await async_setup_entity(hass, entity_id) state = hass.states.get(entity_id) supported_features = state.attributes[ATTR_SUPPORTED_FEATURES] @@ -365,20 +365,20 @@ async def test_with_no_stop(gatebox, hass, config): @pytest.mark.parametrize("feature", ALL_COVER_FIXTURES, indirect=["feature"]) -async def test_update_failure(feature, hass, config, caplog): +async def test_update_failure(feature, hass, caplog): """Test that update failures are logged.""" caplog.set_level(logging.ERROR) feature_mock, entity_id = feature feature_mock.async_update = AsyncMock(side_effect=blebox_uniapi.error.ClientError) - await async_setup_entity(hass, config, entity_id) + await async_setup_entity(hass, entity_id) assert f"Updating '{feature_mock.full_name}' failed: " in caplog.text @pytest.mark.parametrize("feature", ALL_COVER_FIXTURES, indirect=["feature"]) -async def test_opening_state(feature, hass, config): +async def test_opening_state(feature, hass): """Test that entity properties work.""" feature_mock, entity_id = feature @@ -387,12 +387,12 @@ async def test_opening_state(feature, hass, config): feature_mock.state = 1 # opening feature_mock.async_update = AsyncMock(side_effect=initial_update) - await async_setup_entity(hass, config, entity_id) + await async_setup_entity(hass, entity_id) assert hass.states.get(entity_id).state == STATE_OPENING @pytest.mark.parametrize("feature", ALL_COVER_FIXTURES, indirect=["feature"]) -async def test_closing_state(feature, hass, config): +async def test_closing_state(feature, hass): """Test that entity properties work.""" feature_mock, entity_id = feature @@ -401,12 +401,12 @@ async def test_closing_state(feature, hass, config): feature_mock.state = 0 # closing feature_mock.async_update = AsyncMock(side_effect=initial_update) - await async_setup_entity(hass, config, entity_id) + await async_setup_entity(hass, entity_id) assert hass.states.get(entity_id).state == STATE_CLOSING @pytest.mark.parametrize("feature", ALL_COVER_FIXTURES, indirect=["feature"]) -async def test_closed_state(feature, hass, config): +async def test_closed_state(feature, hass): """Test that entity properties work.""" feature_mock, entity_id = feature @@ -415,5 +415,5 @@ async def test_closed_state(feature, hass, config): feature_mock.state = 3 # closed feature_mock.async_update = AsyncMock(side_effect=initial_update) - await async_setup_entity(hass, config, entity_id) + await async_setup_entity(hass, entity_id) assert hass.states.get(entity_id).state == STATE_CLOSED diff --git a/tests/components/blebox/test_light.py b/tests/components/blebox/test_light.py index 7afb78e5b03..dfb2576d262 100644 --- a/tests/components/blebox/test_light.py +++ b/tests/components/blebox/test_light.py @@ -49,11 +49,11 @@ def dimmer_fixture(): return (feature, "light.dimmerbox_brightness") -async def test_dimmer_init(dimmer, hass, config): +async def test_dimmer_init(dimmer, hass): """Test cover default state.""" _, entity_id = dimmer - entry = await async_setup_entity(hass, config, entity_id) + entry = await async_setup_entity(hass, entity_id) assert entry.unique_id == "BleBox-dimmerBox-1afe34e750b8-brightness" state = hass.states.get(entity_id) @@ -75,7 +75,7 @@ async def test_dimmer_init(dimmer, hass, config): assert device.sw_version == "1.23" -async def test_dimmer_update(dimmer, hass, config): +async def test_dimmer_update(dimmer, hass): """Test light updating.""" feature_mock, entity_id = dimmer @@ -84,14 +84,14 @@ async def test_dimmer_update(dimmer, hass, config): feature_mock.brightness = 53 feature_mock.async_update = AsyncMock(side_effect=initial_update) - await async_setup_entity(hass, config, entity_id) + await async_setup_entity(hass, entity_id) state = hass.states.get(entity_id) assert state.attributes[ATTR_BRIGHTNESS] == 53 assert state.state == STATE_ON -async def test_dimmer_on(dimmer, hass, config): +async def test_dimmer_on(dimmer, hass): """Test light on.""" feature_mock, entity_id = dimmer @@ -102,7 +102,7 @@ async def test_dimmer_on(dimmer, hass, config): feature_mock.sensible_on_value = 254 feature_mock.async_update = AsyncMock(side_effect=initial_update) - await async_setup_entity(hass, config, entity_id) + await async_setup_entity(hass, entity_id) feature_mock.async_update = AsyncMock() state = hass.states.get(entity_id) @@ -126,7 +126,7 @@ async def test_dimmer_on(dimmer, hass, config): assert state.attributes[ATTR_BRIGHTNESS] == 254 -async def test_dimmer_on_with_brightness(dimmer, hass, config): +async def test_dimmer_on_with_brightness(dimmer, hass): """Test light on with a brightness value.""" feature_mock, entity_id = dimmer @@ -137,7 +137,7 @@ async def test_dimmer_on_with_brightness(dimmer, hass, config): feature_mock.sensible_on_value = 254 feature_mock.async_update = AsyncMock(side_effect=initial_update) - await async_setup_entity(hass, config, entity_id) + await async_setup_entity(hass, entity_id) feature_mock.async_update = AsyncMock() state = hass.states.get(entity_id) @@ -167,7 +167,7 @@ async def test_dimmer_on_with_brightness(dimmer, hass, config): assert state.state == STATE_ON -async def test_dimmer_off(dimmer, hass, config): +async def test_dimmer_off(dimmer, hass): """Test light off.""" feature_mock, entity_id = dimmer @@ -176,7 +176,7 @@ async def test_dimmer_off(dimmer, hass, config): feature_mock.is_on = True feature_mock.async_update = AsyncMock(side_effect=initial_update) - await async_setup_entity(hass, config, entity_id) + await async_setup_entity(hass, entity_id) feature_mock.async_update = AsyncMock() state = hass.states.get(entity_id) @@ -222,11 +222,11 @@ def wlightboxs_fixture(): return (feature, "light.wlightboxs_color") -async def test_wlightbox_s_init(wlightbox_s, hass, config): +async def test_wlightbox_s_init(wlightbox_s, hass): """Test cover default state.""" _, entity_id = wlightbox_s - entry = await async_setup_entity(hass, config, entity_id) + entry = await async_setup_entity(hass, entity_id) assert entry.unique_id == "BleBox-wLightBoxS-1afe34e750b8-color" state = hass.states.get(entity_id) @@ -248,7 +248,7 @@ async def test_wlightbox_s_init(wlightbox_s, hass, config): assert device.sw_version == "1.23" -async def test_wlightbox_s_update(wlightbox_s, hass, config): +async def test_wlightbox_s_update(wlightbox_s, hass): """Test light updating.""" feature_mock, entity_id = wlightbox_s @@ -259,14 +259,14 @@ async def test_wlightbox_s_update(wlightbox_s, hass, config): feature_mock.async_update = AsyncMock(side_effect=initial_update) - await async_setup_entity(hass, config, entity_id) + await async_setup_entity(hass, entity_id) state = hass.states.get(entity_id) assert state.state == STATE_ON assert state.attributes[ATTR_BRIGHTNESS] == 0xAB -async def test_wlightbox_s_on(wlightbox_s, hass, config): +async def test_wlightbox_s_on(wlightbox_s, hass): """Test light on.""" feature_mock, entity_id = wlightbox_s @@ -276,7 +276,7 @@ async def test_wlightbox_s_on(wlightbox_s, hass, config): feature_mock.sensible_on_value = 254 feature_mock.async_update = AsyncMock(side_effect=initial_update) - await async_setup_entity(hass, config, entity_id) + await async_setup_entity(hass, entity_id) feature_mock.async_update = AsyncMock() state = hass.states.get(entity_id) @@ -325,11 +325,11 @@ def wlightbox_fixture(): return (feature, "light.wlightbox_color") -async def test_wlightbox_init(wlightbox, hass, config): +async def test_wlightbox_init(wlightbox, hass): """Test cover default state.""" _, entity_id = wlightbox - entry = await async_setup_entity(hass, config, entity_id) + entry = await async_setup_entity(hass, entity_id) assert entry.unique_id == "BleBox-wLightBox-1afe34e750b8-color" state = hass.states.get(entity_id) @@ -352,7 +352,7 @@ async def test_wlightbox_init(wlightbox, hass, config): assert device.sw_version == "1.23" -async def test_wlightbox_update(wlightbox, hass, config): +async def test_wlightbox_update(wlightbox, hass): """Test light updating.""" feature_mock, entity_id = wlightbox @@ -363,14 +363,14 @@ async def test_wlightbox_update(wlightbox, hass, config): feature_mock.white_value = 0x3A feature_mock.async_update = AsyncMock(side_effect=initial_update) - await async_setup_entity(hass, config, entity_id) + await async_setup_entity(hass, entity_id) state = hass.states.get(entity_id) assert state.attributes[ATTR_RGBW_COLOR] == (0xFA, 0x00, 0x20, 0x3A) assert state.state == STATE_ON -async def test_wlightbox_on_rgbw(wlightbox, hass, config): +async def test_wlightbox_on_rgbw(wlightbox, hass): """Test light on.""" feature_mock, entity_id = wlightbox @@ -379,7 +379,7 @@ async def test_wlightbox_on_rgbw(wlightbox, hass, config): feature_mock.is_on = False feature_mock.async_update = AsyncMock(side_effect=initial_update) - await async_setup_entity(hass, config, entity_id) + await async_setup_entity(hass, entity_id) feature_mock.async_update = AsyncMock() state = hass.states.get(entity_id) @@ -420,7 +420,7 @@ async def test_wlightbox_on_rgbw(wlightbox, hass, config): assert state.attributes[ATTR_RGBW_COLOR] == (0xC1, 0xD2, 0xF3, 0xC7) -async def test_wlightbox_on_to_last_color(wlightbox, hass, config): +async def test_wlightbox_on_to_last_color(wlightbox, hass): """Test light on.""" feature_mock, entity_id = wlightbox @@ -429,7 +429,7 @@ async def test_wlightbox_on_to_last_color(wlightbox, hass, config): feature_mock.is_on = False feature_mock.async_update = AsyncMock(side_effect=initial_update) - await async_setup_entity(hass, config, entity_id) + await async_setup_entity(hass, entity_id) feature_mock.async_update = AsyncMock() state = hass.states.get(entity_id) @@ -456,7 +456,7 @@ async def test_wlightbox_on_to_last_color(wlightbox, hass, config): assert state.state == STATE_ON -async def test_wlightbox_off(wlightbox, hass, config): +async def test_wlightbox_off(wlightbox, hass): """Test light off.""" feature_mock, entity_id = wlightbox @@ -465,7 +465,7 @@ async def test_wlightbox_off(wlightbox, hass, config): feature_mock.is_on = True feature_mock.async_update = AsyncMock(side_effect=initial_update) - await async_setup_entity(hass, config, entity_id) + await async_setup_entity(hass, entity_id) feature_mock.async_update = AsyncMock() state = hass.states.get(entity_id) @@ -491,27 +491,27 @@ async def test_wlightbox_off(wlightbox, hass, config): @pytest.mark.parametrize("feature", ALL_LIGHT_FIXTURES, indirect=["feature"]) -async def test_update_failure(feature, hass, config, caplog): +async def test_update_failure(feature, hass, caplog): """Test that update failures are logged.""" caplog.set_level(logging.ERROR) feature_mock, entity_id = feature feature_mock.async_update = AsyncMock(side_effect=blebox_uniapi.error.ClientError) - await async_setup_entity(hass, config, entity_id) + await async_setup_entity(hass, entity_id) assert f"Updating '{feature_mock.full_name}' failed: " in caplog.text @pytest.mark.parametrize("feature", ALL_LIGHT_FIXTURES, indirect=["feature"]) -async def test_turn_on_failure(feature, hass, config, caplog): +async def test_turn_on_failure(feature, hass, caplog): """Test that turn_on failures are logged.""" caplog.set_level(logging.ERROR) feature_mock, entity_id = feature feature_mock.async_on = AsyncMock(side_effect=ValueError) - await async_setup_entity(hass, config, entity_id) + await async_setup_entity(hass, entity_id) feature_mock.sensible_on_value = 123 with pytest.raises(ValueError) as info: @@ -527,7 +527,7 @@ async def test_turn_on_failure(feature, hass, config, caplog): ) -async def test_wlightbox_on_effect(wlightbox, hass, config): +async def test_wlightbox_on_effect(wlightbox, hass): """Test light on.""" feature_mock, entity_id = wlightbox @@ -536,7 +536,7 @@ async def test_wlightbox_on_effect(wlightbox, hass, config): feature_mock.is_on = False feature_mock.async_update = AsyncMock(side_effect=initial_update) - await async_setup_entity(hass, config, entity_id) + await async_setup_entity(hass, entity_id) feature_mock.async_update = AsyncMock() state = hass.states.get(entity_id) diff --git a/tests/components/blebox/test_sensor.py b/tests/components/blebox/test_sensor.py index d876da8b0b6..dd54e5272e2 100644 --- a/tests/components/blebox/test_sensor.py +++ b/tests/components/blebox/test_sensor.py @@ -55,11 +55,11 @@ def tempsensor_fixture(): return (feature, "sensor.tempsensor_0_temperature") -async def test_init(tempsensor, hass, config): +async def test_init(tempsensor, hass): """Test sensor default state.""" _, entity_id = tempsensor - entry = await async_setup_entity(hass, config, entity_id) + entry = await async_setup_entity(hass, entity_id) assert entry.unique_id == "BleBox-tempSensor-1afe34db9437-0.temperature" state = hass.states.get(entity_id) @@ -79,7 +79,7 @@ async def test_init(tempsensor, hass, config): assert device.sw_version == "1.23" -async def test_update(tempsensor, hass, config): +async def test_update(tempsensor, hass): """Test sensor update.""" feature_mock, entity_id = tempsensor @@ -88,30 +88,30 @@ async def test_update(tempsensor, hass, config): feature_mock.native_value = 25.18 feature_mock.async_update = AsyncMock(side_effect=initial_update) - await async_setup_entity(hass, config, entity_id) + await async_setup_entity(hass, entity_id) state = hass.states.get(entity_id) assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == TEMP_CELSIUS assert state.state == "25.18" -async def test_update_failure(tempsensor, hass, config, caplog): +async def test_update_failure(tempsensor, hass, caplog): """Test that update failures are logged.""" caplog.set_level(logging.ERROR) feature_mock, entity_id = tempsensor feature_mock.async_update = AsyncMock(side_effect=blebox_uniapi.error.ClientError) - await async_setup_entity(hass, config, entity_id) + await async_setup_entity(hass, entity_id) assert f"Updating '{feature_mock.full_name}' failed: " in caplog.text -async def test_airsensor_init(airsensor, hass, config): +async def test_airsensor_init(airsensor, hass): """Test airSensor default state.""" _, entity_id = airsensor - entry = await async_setup_entity(hass, config, entity_id) + entry = await async_setup_entity(hass, entity_id) assert entry.unique_id == "BleBox-airSensor-1afe34db9437-0.air" state = hass.states.get(entity_id) @@ -130,7 +130,7 @@ async def test_airsensor_init(airsensor, hass, config): assert device.sw_version == "1.23" -async def test_airsensor_update(airsensor, hass, config): +async def test_airsensor_update(airsensor, hass): """Test air quality sensor state after update.""" feature_mock, entity_id = airsensor @@ -139,7 +139,7 @@ async def test_airsensor_update(airsensor, hass, config): feature_mock.native_value = 49 feature_mock.async_update = AsyncMock(side_effect=initial_update) - await async_setup_entity(hass, config, entity_id) + await async_setup_entity(hass, entity_id) state = hass.states.get(entity_id) assert ( diff --git a/tests/components/blebox/test_switch.py b/tests/components/blebox/test_switch.py index b494687e539..1941bda0157 100644 --- a/tests/components/blebox/test_switch.py +++ b/tests/components/blebox/test_switch.py @@ -49,7 +49,7 @@ async def test_switchbox_init(switchbox, hass, config): feature_mock, entity_id = switchbox feature_mock.async_update = AsyncMock() - entry = await async_setup_entity(hass, config, entity_id) + entry = await async_setup_entity(hass, entity_id) assert entry.unique_id == "BleBox-switchBox-1afe34e750b8-0.relay" state = hass.states.get(entity_id) @@ -69,7 +69,7 @@ async def test_switchbox_init(switchbox, hass, config): assert device.sw_version == "1.23" -async def test_switchbox_update_when_off(switchbox, hass, config): +async def test_switchbox_update_when_off(switchbox, hass): """Test switch updating when off.""" feature_mock, entity_id = switchbox @@ -78,13 +78,13 @@ async def test_switchbox_update_when_off(switchbox, hass, config): feature_mock.is_on = False feature_mock.async_update = AsyncMock(side_effect=initial_update) - await async_setup_entity(hass, config, entity_id) + await async_setup_entity(hass, entity_id) state = hass.states.get(entity_id) assert state.state == STATE_OFF -async def test_switchbox_update_when_on(switchbox, hass, config): +async def test_switchbox_update_when_on(switchbox, hass): """Test switch updating when on.""" feature_mock, entity_id = switchbox @@ -93,13 +93,13 @@ async def test_switchbox_update_when_on(switchbox, hass, config): feature_mock.is_on = True feature_mock.async_update = AsyncMock(side_effect=initial_update) - await async_setup_entity(hass, config, entity_id) + await async_setup_entity(hass, entity_id) state = hass.states.get(entity_id) assert state.state == STATE_ON -async def test_switchbox_on(switchbox, hass, config): +async def test_switchbox_on(switchbox, hass): """Test turning switch on.""" feature_mock, entity_id = switchbox @@ -108,7 +108,7 @@ async def test_switchbox_on(switchbox, hass, config): feature_mock.is_on = False feature_mock.async_update = AsyncMock(side_effect=initial_update) - await async_setup_entity(hass, config, entity_id) + await async_setup_entity(hass, entity_id) feature_mock.async_update = AsyncMock() def turn_on(): @@ -127,7 +127,7 @@ async def test_switchbox_on(switchbox, hass, config): assert state.state == STATE_ON -async def test_switchbox_off(switchbox, hass, config): +async def test_switchbox_off(switchbox, hass): """Test turning switch off.""" feature_mock, entity_id = switchbox @@ -136,7 +136,7 @@ async def test_switchbox_off(switchbox, hass, config): feature_mock.is_on = True feature_mock.async_update = AsyncMock(side_effect=initial_update) - await async_setup_entity(hass, config, entity_id) + await async_setup_entity(hass, entity_id) feature_mock.async_update = AsyncMock() def turn_off(): @@ -188,14 +188,14 @@ def switchbox_d_fixture(): return (features, ["switch.switchboxd_0_relay", "switch.switchboxd_1_relay"]) -async def test_switchbox_d_init(switchbox_d, hass, config): +async def test_switchbox_d_init(switchbox_d, hass): """Test switch default state.""" feature_mocks, entity_ids = switchbox_d feature_mocks[0].async_update = AsyncMock() feature_mocks[1].async_update = AsyncMock() - entries = await async_setup_entities(hass, config, entity_ids) + entries = await async_setup_entities(hass, entity_ids) entry = entries[0] assert entry.unique_id == "BleBox-switchBoxD-1afe34e750b8-0.relay" @@ -232,7 +232,7 @@ async def test_switchbox_d_init(switchbox_d, hass, config): assert device.sw_version == "1.23" -async def test_switchbox_d_update_when_off(switchbox_d, hass, config): +async def test_switchbox_d_update_when_off(switchbox_d, hass): """Test switch updating when off.""" feature_mocks, entity_ids = switchbox_d @@ -243,13 +243,13 @@ async def test_switchbox_d_update_when_off(switchbox_d, hass, config): feature_mocks[0].async_update = AsyncMock(side_effect=initial_update0) feature_mocks[1].async_update = AsyncMock() - await async_setup_entities(hass, config, entity_ids) + await async_setup_entities(hass, entity_ids) assert hass.states.get(entity_ids[0]).state == STATE_OFF assert hass.states.get(entity_ids[1]).state == STATE_OFF -async def test_switchbox_d_update_when_second_off(switchbox_d, hass, config): +async def test_switchbox_d_update_when_second_off(switchbox_d, hass): """Test switch updating when off.""" feature_mocks, entity_ids = switchbox_d @@ -260,13 +260,13 @@ async def test_switchbox_d_update_when_second_off(switchbox_d, hass, config): feature_mocks[0].async_update = AsyncMock(side_effect=initial_update0) feature_mocks[1].async_update = AsyncMock() - await async_setup_entities(hass, config, entity_ids) + await async_setup_entities(hass, entity_ids) assert hass.states.get(entity_ids[0]).state == STATE_ON assert hass.states.get(entity_ids[1]).state == STATE_OFF -async def test_switchbox_d_turn_first_on(switchbox_d, hass, config): +async def test_switchbox_d_turn_first_on(switchbox_d, hass): """Test turning switch on.""" feature_mocks, entity_ids = switchbox_d @@ -277,7 +277,7 @@ async def test_switchbox_d_turn_first_on(switchbox_d, hass, config): feature_mocks[0].async_update = AsyncMock(side_effect=initial_update0) feature_mocks[1].async_update = AsyncMock() - await async_setup_entities(hass, config, entity_ids) + await async_setup_entities(hass, entity_ids) feature_mocks[0].async_update = AsyncMock() def turn_on0(): @@ -295,7 +295,7 @@ async def test_switchbox_d_turn_first_on(switchbox_d, hass, config): assert hass.states.get(entity_ids[1]).state == STATE_OFF -async def test_switchbox_d_second_on(switchbox_d, hass, config): +async def test_switchbox_d_second_on(switchbox_d, hass): """Test turning switch on.""" feature_mocks, entity_ids = switchbox_d @@ -306,7 +306,7 @@ async def test_switchbox_d_second_on(switchbox_d, hass, config): feature_mocks[0].async_update = AsyncMock(side_effect=initial_update0) feature_mocks[1].async_update = AsyncMock() - await async_setup_entities(hass, config, entity_ids) + await async_setup_entities(hass, entity_ids) feature_mocks[0].async_update = AsyncMock() def turn_on1(): @@ -324,7 +324,7 @@ async def test_switchbox_d_second_on(switchbox_d, hass, config): assert hass.states.get(entity_ids[1]).state == STATE_ON -async def test_switchbox_d_first_off(switchbox_d, hass, config): +async def test_switchbox_d_first_off(switchbox_d, hass): """Test turning switch on.""" feature_mocks, entity_ids = switchbox_d @@ -335,7 +335,7 @@ async def test_switchbox_d_first_off(switchbox_d, hass, config): feature_mocks[0].async_update = AsyncMock(side_effect=initial_update_any) feature_mocks[1].async_update = AsyncMock() - await async_setup_entities(hass, config, entity_ids) + await async_setup_entities(hass, entity_ids) feature_mocks[0].async_update = AsyncMock() def turn_off0(): @@ -353,7 +353,7 @@ async def test_switchbox_d_first_off(switchbox_d, hass, config): assert hass.states.get(entity_ids[1]).state == STATE_ON -async def test_switchbox_d_second_off(switchbox_d, hass, config): +async def test_switchbox_d_second_off(switchbox_d, hass): """Test turning switch on.""" feature_mocks, entity_ids = switchbox_d @@ -364,7 +364,7 @@ async def test_switchbox_d_second_off(switchbox_d, hass, config): feature_mocks[0].async_update = AsyncMock(side_effect=initial_update_any) feature_mocks[1].async_update = AsyncMock() - await async_setup_entities(hass, config, entity_ids) + await async_setup_entities(hass, entity_ids) feature_mocks[0].async_update = AsyncMock() def turn_off1(): @@ -385,7 +385,7 @@ ALL_SWITCH_FIXTURES = ["switchbox", "switchbox_d"] @pytest.mark.parametrize("feature", ALL_SWITCH_FIXTURES, indirect=["feature"]) -async def test_update_failure(feature, hass, config, caplog): +async def test_update_failure(feature, hass, caplog): """Test that update failures are logged.""" caplog.set_level(logging.ERROR) @@ -399,6 +399,6 @@ async def test_update_failure(feature, hass, config, caplog): entity_id = entity_id[0] feature_mock.async_update = AsyncMock(side_effect=blebox_uniapi.error.ClientError) - await async_setup_entity(hass, config, entity_id) + await async_setup_entity(hass, entity_id) assert f"Updating '{feature_mock.full_name}' failed: " in caplog.text diff --git a/tests/components/bluetooth/__init__.py b/tests/components/bluetooth/__init__.py index e36d1d4b644..91016206e8a 100644 --- a/tests/components/bluetooth/__init__.py +++ b/tests/components/bluetooth/__init__.py @@ -17,6 +17,7 @@ from homeassistant.components.bluetooth import ( async_get_advertisement_callback, models, ) +from homeassistant.components.bluetooth.base_scanner import BaseHaScanner from homeassistant.components.bluetooth.manager import BluetoothManager from homeassistant.core import HomeAssistant from homeassistant.setup import async_setup_component @@ -223,3 +224,19 @@ class MockBleakClient(BleakClient): async def clear_cache(self, *args, **kwargs): """Mock clear_cache.""" return True + + +class FakeScanner(BaseHaScanner): + """Fake scanner.""" + + @property + def discovered_devices(self) -> list[BLEDevice]: + """Return a list of discovered devices.""" + return [] + + @property + def discovered_devices_and_advertisement_data( + self, + ) -> dict[str, tuple[BLEDevice, AdvertisementData]]: + """Return a list of discovered devices and their advertisement data.""" + return {} diff --git a/tests/components/bluetooth/conftest.py b/tests/components/bluetooth/conftest.py index a6593adef49..ffa353fd0c4 100644 --- a/tests/components/bluetooth/conftest.py +++ b/tests/components/bluetooth/conftest.py @@ -140,6 +140,7 @@ def two_adapters_fixture(): "product": "Bluetooth Adapter 5.0", "product_id": "aa01", "vendor_id": "cc01", + "connection_slots": 1, }, "hci1": { "address": "00:00:00:00:00:02", @@ -150,6 +151,7 @@ def two_adapters_fixture(): "product": "Bluetooth Adapter 5.0", "product_id": "aa01", "vendor_id": "cc01", + "connection_slots": 2, }, }, ): diff --git a/tests/components/bluetooth/fixtures/bluetooth.remote_scanners b/tests/components/bluetooth/fixtures/bluetooth.remote_scanners new file mode 100644 index 00000000000..f503752151c --- /dev/null +++ b/tests/components/bluetooth/fixtures/bluetooth.remote_scanners @@ -0,0 +1,1384 @@ +{ + "version": 1, + "minor_version": 1, + "key": "bluetooth.remote_scanners", + "data": { + "atom-bluetooth-proxy-ceaac4": { + "connectable": true, + "expire_seconds": 195, + "discovered_device_advertisement_datas": { + "51:8E:5B:A8:44:8D": { + "device": { + "address": "51:8E:5B:A8:44:8D", + "name": "98E7829A01E23895E9", + "rssi": -80, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": "98E7829A01E23895E9", + "manufacturer_data": { + "1447": "051001000000000000022200ca" + }, + "service_data": {}, + "service_uuids": [ + "0000fe07-0000-1000-8000-00805f9b34fb" + ], + "rssi": -80, + "tx_power": -127, + "platform_data": [] + } + }, + "16:9A:28:FC:D5:A8": { + "device": { + "address": "16:9A:28:FC:D5:A8", + "name": "", + "rssi": -77, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "0906036dc0a86b7e130c0a59cff1970c9f010401030c" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -77, + "tx_power": -127, + "platform_data": [] + } + }, + "08:3A:F2:1E:32:69": { + "device": { + "address": "08:3A:F2:1E:32:69", + "name": "LOOKin_98F3320D", + "rssi": -91, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": "LOOKin_98F3320D", + "manufacturer_data": {}, + "service_data": {}, + "service_uuids": [ + "00001812-0000-1000-8000-00805f9b34fb" + ], + "rssi": -91, + "tx_power": -127, + "platform_data": [] + } + }, + "34:AB:95:85:6E:F1": { + "device": { + "address": "34:AB:95:85:6E:F1", + "name": "LOOKin_98F330B5", + "rssi": -96, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": "LOOKin_98F330B5", + "manufacturer_data": {}, + "service_data": {}, + "service_uuids": [ + "00001812-0000-1000-8000-00805f9b34fb" + ], + "rssi": -96, + "tx_power": -127, + "platform_data": [] + } + }, + "D8:2E:AD:CD:0D:85": { + "device": { + "address": "D8:2E:AD:CD:0D:85", + "name": "", + "rssi": -81, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "89": "d82eadcd0d85" + }, + "service_data": { + "00000d00-0000-1000-8000-00805f9b34fb": "481061" + }, + "service_uuids": [ + "cba20d00-224d-11e6-9fb8-0002a5d5c51b" + ], + "rssi": -81, + "tx_power": -127, + "platform_data": [] + } + }, + "D4:B2:A1:7C:42:17": { + "device": { + "address": "D4:B2:A1:7C:42:17", + "name": "RZSS", + "rssi": -70, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": "RZSS", + "manufacturer_data": { + "1033": "0c246232" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -70, + "tx_power": -127, + "platform_data": [] + } + }, + "D8:EF:2F:41:B1:34": { + "device": { + "address": "D8:EF:2F:41:B1:34", + "name": "RZSS", + "rssi": -64, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": "RZSS", + "manufacturer_data": { + "1033": "0c246064" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -64, + "tx_power": -127, + "platform_data": [] + } + }, + "60:55:F9:29:FA:2A": { + "device": { + "address": "60:55:F9:29:FA:2A", + "name": "", + "rssi": -62, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "2409": "6055f929fa2a010000000000" + }, + "service_data": { + "0000fd3d-0000-1000-8000-00805f9b34fb": "6a0064" + }, + "service_uuids": [], + "rssi": -62, + "tx_power": -127, + "platform_data": [] + } + }, + "F3:33:CE:84:58:4A": { + "device": { + "address": "F3:33:CE:84:58:4A", + "name": "98E7822ED4F830AEE9", + "rssi": -78, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": "98E7822ED4F830AEE9", + "manufacturer_data": { + "1447": "051001000000000000022200ca" + }, + "service_data": {}, + "service_uuids": [ + "0000fe07-0000-1000-8000-00805f9b34fb" + ], + "rssi": -78, + "tx_power": -127, + "platform_data": [] + } + }, + "CB:39:CD:C4:3D:46": { + "device": { + "address": "CB:39:CD:C4:3D:46", + "name": "", + "rssi": -84, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "2409": "cb39cdc43d46c02e5c55ffff84" + }, + "service_data": { + "0000fd3d-0000-1000-8000-00805f9b34fb": "64005a445c55ffff84" + }, + "service_uuids": [], + "rssi": -84, + "tx_power": -127, + "platform_data": [] + } + }, + "E2:4B:AB:57:4F:CC": { + "device": { + "address": "E2:4B:AB:57:4F:CC", + "name": "", + "rssi": -73, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "89": "e24bab574fcc" + }, + "service_data": { + "00000d00-0000-1000-8000-00805f9b34fb": "4810d7" + }, + "service_uuids": [ + "cba20d00-224d-11e6-9fb8-0002a5d5c51b" + ], + "rssi": -73, + "tx_power": -127, + "platform_data": [] + } + }, + "7F:B3:F7:28:DD:5B": { + "device": { + "address": "7F:B3:F7:28:DD:5B", + "name": "", + "rssi": -80, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "1005229829e5ab" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -80, + "tx_power": -127, + "platform_data": [] + } + }, + "B4:E8:42:51:76:67": { + "device": { + "address": "B4:E8:42:51:76:67", + "name": "LEDnetWF010097517666", + "rssi": -86, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": "LEDnetWF010097517666", + "manufacturer_data": { + "23041": "5205b4e84251766600971603000000000000000000000000000000" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -86, + "tx_power": -127, + "platform_data": [] + } + }, + "EA:CF:16:0C:16:11": { + "device": { + "address": "EA:CF:16:0C:16:11", + "name": "", + "rssi": -79, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "12020003" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -79, + "tx_power": -127, + "platform_data": [] + } + }, + "08:3A:F2:1E:28:F1": { + "device": { + "address": "08:3A:F2:1E:28:F1", + "name": "LOOKin_98F33208", + "rssi": -86, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": "LOOKin_98F33208", + "manufacturer_data": {}, + "service_data": {}, + "service_uuids": [ + "00001812-0000-1000-8000-00805f9b34fb" + ], + "rssi": -86, + "tx_power": -127, + "platform_data": [] + } + }, + "08:3A:F2:1E:2B:2D": { + "device": { + "address": "08:3A:F2:1E:2B:2D", + "name": "LOOKin_98F330F3", + "rssi": -80, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": "LOOKin_98F330F3", + "manufacturer_data": {}, + "service_data": {}, + "service_uuids": [ + "00001812-0000-1000-8000-00805f9b34fb" + ], + "rssi": -80, + "tx_power": -127, + "platform_data": [] + } + }, + "3D:AF:40:AD:B3:8B": { + "device": { + "address": "3D:AF:40:AD:B3:8B", + "name": "", + "rssi": -88, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "0906036fc0a86b3e130c0a98ce54cc75e4010401030c" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -88, + "tx_power": -127, + "platform_data": [] + } + }, + "61:25:DF:19:90:98": { + "device": { + "address": "61:25:DF:19:90:98", + "name": "", + "rssi": -74, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "1007321fcbeaadd248" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -74, + "tx_power": -127, + "platform_data": [] + } + }, + "34:AB:95:85:6E:F9": { + "device": { + "address": "34:AB:95:85:6E:F9", + "name": "LOOKin_98F330B4", + "rssi": -91, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": "LOOKin_98F330B4", + "manufacturer_data": {}, + "service_data": {}, + "service_uuids": [ + "00001812-0000-1000-8000-00805f9b34fb" + ], + "rssi": -91, + "tx_power": -127, + "platform_data": [] + } + }, + "54:E6:1B:F0:20:97": { + "device": { + "address": "54:E6:1B:F0:20:97", + "name": "", + "rssi": -54, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "0f08c00ad7f8b400440c10020100" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -54, + "tx_power": -127, + "platform_data": [] + } + }, + "41:CC:AF:67:18:EE": { + "device": { + "address": "41:CC:AF:67:18:EE", + "name": "", + "rssi": -80, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "0c0e089c6a11d70cc17e3e52928b96b71006411d75808748" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -80, + "tx_power": -127, + "platform_data": [] + } + }, + "8C:EA:48:4D:93:3B": { + "device": { + "address": "8C:EA:48:4D:93:3B", + "name": "", + "rssi": -98, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "117": "42040180668cea484d933b8eea484d933a01000000000000" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -98, + "tx_power": -127, + "platform_data": [] + } + }, + "C5:2D:5E:AB:52:76": { + "device": { + "address": "C5:2D:5E:AB:52:76", + "name": "", + "rssi": -66, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "89": "c52d5eab5276" + }, + "service_data": { + "00000d00-0000-1000-8000-00805f9b34fb": "481063" + }, + "service_uuids": [ + "cba20d00-224d-11e6-9fb8-0002a5d5c51b" + ], + "rssi": -66, + "tx_power": -127, + "platform_data": [] + } + }, + "C0:49:EF:8C:1F:86": { + "device": { + "address": "C0:49:EF:8C:1F:86", + "name": "ShellyPlugUS-C049EF8C1F84", + "rssi": -36, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": "ShellyPlugUS-C049EF8C1F84", + "manufacturer_data": {}, + "service_data": {}, + "service_uuids": [], + "rssi": -36, + "tx_power": -127, + "platform_data": [] + } + }, + "F8:04:2E:E1:71:9D": { + "device": { + "address": "F8:04:2E:E1:71:9D", + "name": "", + "rssi": -90, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "117": "42040180a0f8042ee1719dfa042ee1719c01000000000000" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -90, + "tx_power": -127, + "platform_data": [] + } + }, + "C6:D5:B5:C1:2A:22": { + "device": { + "address": "C6:D5:B5:C1:2A:22", + "name": "", + "rssi": -73, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "89": "c6d5b5c12a22" + }, + "service_data": { + "00000d00-0000-1000-8000-00805f9b34fb": "4810e1" + }, + "service_uuids": [ + "cba20d00-224d-11e6-9fb8-0002a5d5c51b" + ], + "rssi": -73, + "tx_power": -127, + "platform_data": [] + } + }, + "31:FA:70:5F:9C:AC": { + "device": { + "address": "31:FA:70:5F:9C:AC", + "name": "", + "rssi": -78, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "0906038ec0a86a5e" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -78, + "tx_power": -127, + "platform_data": [] + } + }, + "D5:C1:1D:1A:23:57": { + "device": { + "address": "D5:C1:1D:1A:23:57", + "name": "RZSS", + "rssi": -70, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": "RZSS", + "manufacturer_data": { + "1033": "0c246200" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -70, + "tx_power": -127, + "platform_data": [] + } + }, + "2C:BF:C0:1C:D7:4B": { + "device": { + "address": "2C:BF:C0:1C:D7:4B", + "name": "", + "rssi": -89, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "0906035cc0a86a9d" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -89, + "tx_power": -127, + "platform_data": [] + } + }, + "B4:E8:42:DB:12:85": { + "device": { + "address": "B4:E8:42:DB:12:85", + "name": "LEDnetWF010097DB1284", + "rssi": -66, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": "LEDnetWF010097DB1284", + "manufacturer_data": { + "23041": "5205b4e842db128400971603000000000000000000000000000000" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -66, + "tx_power": -127, + "platform_data": [] + } + }, + "6E:48:DA:52:2F:13": { + "device": { + "address": "6E:48:DA:52:2F:13", + "name": "", + "rssi": -92, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "10051a1c2e6061" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -92, + "tx_power": -127, + "platform_data": [] + } + }, + "34:AB:95:85:66:6D": { + "device": { + "address": "34:AB:95:85:66:6D", + "name": "LOOKin_98F33163", + "rssi": -91, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": "LOOKin_98F33163", + "manufacturer_data": {}, + "service_data": {}, + "service_uuids": [ + "00001812-0000-1000-8000-00805f9b34fb" + ], + "rssi": -91, + "tx_power": -127, + "platform_data": [] + } + }, + "F8:04:2E:E1:9F:19": { + "device": { + "address": "F8:04:2E:E1:9F:19", + "name": "", + "rssi": -92, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "117": "42040180a0f8042ee19f19fa042ee19f1801000000000000" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -92, + "tx_power": -127, + "platform_data": [] + } + }, + "68:3C:69:9E:A1:18": { + "device": { + "address": "68:3C:69:9E:A1:18", + "name": "", + "rssi": -78, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "0c0e089c6a11d70cc17e3e52928b96b71006411d75808748" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -78, + "tx_power": -127, + "platform_data": [] + } + }, + "07:68:27:D9:15:40": { + "device": { + "address": "07:68:27:D9:15:40", + "name": "", + "rssi": -74, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": {}, + "service_data": { + "0000fd6f-0000-1000-8000-00805f9b34fb": "1b8fb635708f1ef520edb36597fb0586b58f7214" + }, + "service_uuids": [ + "0000fd6f-0000-1000-8000-00805f9b34fb" + ], + "rssi": -74, + "tx_power": -127, + "platform_data": [] + } + }, + "F5:C7:51:3D:B7:33": { + "device": { + "address": "F5:C7:51:3D:B7:33", + "name": "RZSS", + "rssi": -81, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": "RZSS", + "manufacturer_data": { + "1033": "0c246164" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -81, + "tx_power": -127, + "platform_data": [] + } + }, + "E0:1F:A6:3A:C5:66": { + "device": { + "address": "E0:1F:A6:3A:C5:66", + "name": "RZSS", + "rssi": -90, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": "RZSS", + "manufacturer_data": { + "1033": "0c246200" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -90, + "tx_power": -127, + "platform_data": [] + } + }, + "78:9C:85:08:E6:2A": { + "device": { + "address": "78:9C:85:08:E6:2A", + "name": "M1029QJ", + "rssi": -73, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": "M1029QJ", + "manufacturer_data": { + "76": "06310080e7146a373406009c2104023cb9eb0e" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -73, + "tx_power": -127, + "platform_data": [] + } + }, + "4F:EB:42:E2:57:4A": { + "device": { + "address": "4F:EB:42:E2:57:4A", + "name": "a25bd0d9", + "rssi": -86, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": "a25bd0d9", + "manufacturer_data": { + "1015": "1a00000000004d617569204f776c" + }, + "service_data": {}, + "service_uuids": [ + "39d6b333-adad-45c8-b6ee-eac6c4cd0000" + ], + "rssi": -86, + "tx_power": -127, + "platform_data": [] + } + }, + "A8:51:AB:8A:5A:84": { + "device": { + "address": "A8:51:AB:8A:5A:84", + "name": "", + "rssi": -94, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "1005021484867e" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -94, + "tx_power": -127, + "platform_data": [] + } + }, + "5E:5C:AC:CF:CD:2D": { + "device": { + "address": "5E:5C:AC:CF:CD:2D", + "name": "", + "rssi": -82, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "0f059000da640210022d04" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -82, + "tx_power": -127, + "platform_data": [] + } + }, + "0A:73:BD:22:FE:E2": { + "device": { + "address": "0A:73:BD:22:FE:E2", + "name": "", + "rssi": -92, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "09060318c0a86bef" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -92, + "tx_power": -127, + "platform_data": [] + } + }, + "B4:E8:42:9E:A4:F5": { + "device": { + "address": "B4:E8:42:9E:A4:F5", + "name": "LEDnetWF0100979EA4F4", + "rssi": -82, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": "LEDnetWF0100979EA4F4", + "manufacturer_data": { + "23041": "5202b4e8429ea4f400970c03010203040506070809a1a2a3a4a5a6" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -82, + "tx_power": -127, + "platform_data": [] + } + }, + "B4:E8:42:53:56:F9": { + "device": { + "address": "B4:E8:42:53:56:F9", + "name": "LEDnetWF0100975356F8", + "rssi": -98, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": "LEDnetWF0100975356F8", + "manufacturer_data": { + "23041": "5205b4e8425356f800971603000000000000000000000000000000" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -98, + "tx_power": -127, + "platform_data": [] + } + }, + "D4:2C:41:97:56:6E": { + "device": { + "address": "D4:2C:41:97:56:6E", + "name": "RZSS", + "rssi": -82, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": "RZSS", + "manufacturer_data": { + "1033": "0c245f32" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -82, + "tx_power": -127, + "platform_data": [] + } + }, + "64:04:FF:2C:E3:55": { + "device": { + "address": "64:04:FF:2C:E3:55", + "name": "", + "rssi": -88, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "0f0590007f5fd910022504" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -88, + "tx_power": -127, + "platform_data": [] + } + }, + "A4:C1:38:BD:91:BB": { + "device": { + "address": "A4:C1:38:BD:91:BB", + "name": "LYWSD03MMC", + "rssi": -78, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": "LYWSD03MMC", + "manufacturer_data": {}, + "service_data": { + "0000fe95-0000-1000-8000-00805f9b34fb": "30585b055bbb91bd38c1a408" + }, + "service_uuids": [], + "rssi": -78, + "tx_power": -127, + "platform_data": [] + } + }, + "B4:E8:42:DA:BA:23": { + "device": { + "address": "B4:E8:42:DA:BA:23", + "name": "LEDnetWF010097DABA22", + "rssi": -92, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": "LEDnetWF010097DABA22", + "manufacturer_data": { + "23041": "5205b4e842daba2200971603000000000000000000000000000000" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -92, + "tx_power": -127, + "platform_data": [] + } + }, + "E3:A5:63:3E:5E:23": { + "device": { + "address": "E3:A5:63:3E:5E:23", + "name": "", + "rssi": -70, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "12020003" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -70, + "tx_power": -127, + "platform_data": [] + } + }, + "68:E3:C2:4D:85:EE": { + "device": { + "address": "68:E3:C2:4D:85:EE", + "name": "", + "rssi": -97, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "10051a1c53afb1" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -97, + "tx_power": -127, + "platform_data": [] + } + }, + "B4:E8:42:52:4B:AD": { + "device": { + "address": "B4:E8:42:52:4B:AD", + "name": "LEDnetWF010097524BAC", + "rssi": -84, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": "LEDnetWF010097524BAC", + "manufacturer_data": { + "23041": "5205b4e842524bac00971603000000000000000000000000000000" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -84, + "tx_power": -127, + "platform_data": [] + } + }, + "58:2D:34:60:D5:CD": { + "device": { + "address": "58:2D:34:60:D5:CD", + "name": "Qingping Motion & Light", + "rssi": -83, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": "Qingping Motion & Light", + "manufacturer_data": {}, + "service_data": { + "0000fe95-0000-1000-8000-00805f9b34fb": "3058830a02cdd560342d5808", + "0000fdcd-0000-1000-8000-00805f9b34fb": "4812cdd560342d580804010f00000f0166" + }, + "service_uuids": [], + "rssi": -83, + "tx_power": -127, + "platform_data": [] + } + }, + "72:90:B0:53:F1:15": { + "device": { + "address": "72:90:B0:53:F1:15", + "name": "", + "rssi": -94, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "0f059000dc161910022d04" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -94, + "tx_power": -127, + "platform_data": [] + } + }, + "D5:1C:FB:39:78:56": { + "device": { + "address": "D5:1C:FB:39:78:56", + "name": "", + "rssi": -93, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "89": "d51cfb397856" + }, + "service_data": { + "00000d00-0000-1000-8000-00805f9b34fb": "4890d7" + }, + "service_uuids": [ + "cba20d00-224d-11e6-9fb8-0002a5d5c51b" + ], + "rssi": -93, + "tx_power": -127, + "platform_data": [] + } + }, + "08:3A:F2:1E:2B:05": { + "device": { + "address": "08:3A:F2:1E:2B:05", + "name": "LOOKin_98F330C2", + "rssi": -98, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": "LOOKin_98F330C2", + "manufacturer_data": {}, + "service_data": {}, + "service_uuids": [ + "00001812-0000-1000-8000-00805f9b34fb" + ], + "rssi": -98, + "tx_power": -127, + "platform_data": [] + } + }, + "FA:8F:F4:A8:9E:BC": { + "device": { + "address": "FA:8F:F4:A8:9E:BC", + "name": "", + "rssi": -92, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "12025400" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -92, + "tx_power": -127, + "platform_data": [] + } + }, + "EB:0B:36:35:6F:A4": { + "device": { + "address": "EB:0B:36:35:6F:A4", + "name": "", + "rssi": -90, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "12199021f9370af25a5ec7825f703a4ac47a7b76c08b5695e101ba" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -90, + "tx_power": -127, + "platform_data": [] + } + }, + "AC:67:B2:6A:18:2A": { + "device": { + "address": "AC:67:B2:6A:18:2A", + "name": "", + "rssi": -68, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "741": "ac67b26a182a" + }, + "service_data": { + "00000d00-0000-1000-8000-00805f9b34fb": "65000000613d7300" + }, + "service_uuids": [ + "cba20d00-224d-11e6-9fb8-0002a5d5c51b" + ], + "rssi": -68, + "tx_power": -127, + "platform_data": [] + } + } + }, + "discovered_device_timestamps": { + "51:8E:5B:A8:44:8D": 1670722715.9919367, + "16:9A:28:FC:D5:A8": 1670722719.3087196, + "08:3A:F2:1E:32:69": 1670722719.1660142, + "34:AB:95:85:6E:F1": 1670722720.2908847, + "D8:2E:AD:CD:0D:85": 1670722720.2915154, + "D4:B2:A1:7C:42:17": 1670722719.0863516, + "D8:EF:2F:41:B1:34": 1670722718.7625487, + "60:55:F9:29:FA:2A": 1670722720.291449, + "F3:33:CE:84:58:4A": 1670722720.291406, + "CB:39:CD:C4:3D:46": 1670722715.599468, + "E2:4B:AB:57:4F:CC": 1670722720.291419, + "7F:B3:F7:28:DD:5B": 1670722718.9691222, + "B4:E8:42:51:76:67": 1670722710.6735754, + "EA:CF:16:0C:16:11": 1670722715.9919817, + "08:3A:F2:1E:28:F1": 1670722720.291503, + "08:3A:F2:1E:2B:2D": 1670722720.2914848, + "3D:AF:40:AD:B3:8B": 1670722718.85895, + "61:25:DF:19:90:98": 1670722717.941397, + "34:AB:95:85:6E:F9": 1670722718.7632113, + "54:E6:1B:F0:20:97": 1670722717.1185102, + "41:CC:AF:67:18:EE": 1670722720.2912233, + "8C:EA:48:4D:93:3B": 1670722720.2910836, + "C5:2D:5E:AB:52:76": 1670722718.558827, + "C0:49:EF:8C:1F:86": 1670722717.9688299, + "F8:04:2E:E1:71:9D": 1670722717.526199, + "C6:D5:B5:C1:2A:22": 1670722717.0176404, + "31:FA:70:5F:9C:AC": 1670722720.2911327, + "D5:C1:1D:1A:23:57": 1670722717.639859, + "2C:BF:C0:1C:D7:4B": 1670722720.2914364, + "B4:E8:42:DB:12:85": 1670722719.0764725, + "6E:48:DA:52:2F:13": 1670722720.2913177, + "34:AB:95:85:66:6D": 1670722720.2912607, + "F8:04:2E:E1:9F:19": 1670722720.2911522, + "68:3C:69:9E:A1:18": 1670722719.1658278, + "07:68:27:D9:15:40": 1670722720.2912369, + "F5:C7:51:3D:B7:33": 1670722720.2911801, + "E0:1F:A6:3A:C5:66": 1670722713.7567363, + "78:9C:85:08:E6:2A": 1670722712.5362735, + "4F:EB:42:E2:57:4A": 1670722716.5097647, + "A8:51:AB:8A:5A:84": 1670722711.9280567, + "5E:5C:AC:CF:CD:2D": 1670722716.2974086, + "0A:73:BD:22:FE:E2": 1670722720.2914622, + "B4:E8:42:9E:A4:F5": 1670722718.5590498, + "B4:E8:42:53:56:F9": 1670722712.4074357, + "D4:2C:41:97:56:6E": 1670722718.3550117, + "64:04:FF:2C:E3:55": 1670722718.1470506, + "A4:C1:38:BD:91:BB": 1670722712.7268498, + "B4:E8:42:DA:BA:23": 1670722720.2910104, + "E3:A5:63:3E:5E:23": 1670722713.8445594, + "68:E3:C2:4D:85:EE": 1670722713.8488224, + "B4:E8:42:52:4B:AD": 1670722714.0646405, + "58:2D:34:60:D5:CD": 1670722715.377663, + "72:90:B0:53:F1:15": 1670722718.1468875, + "D5:1C:FB:39:78:56": 1670722715.2835894, + "08:3A:F2:1E:2B:05": 1670722719.0629613, + "FA:8F:F4:A8:9E:BC": 1670722717.22681, + "EB:0B:36:35:6F:A4": 1670722717.6297317, + "AC:67:B2:6A:18:2A": 1670722719.086469 + } + } + } +} \ No newline at end of file diff --git a/tests/components/bluetooth/fixtures/bluetooth.remote_scanners.corrupt b/tests/components/bluetooth/fixtures/bluetooth.remote_scanners.corrupt new file mode 100644 index 00000000000..71d084d33a3 --- /dev/null +++ b/tests/components/bluetooth/fixtures/bluetooth.remote_scanners.corrupt @@ -0,0 +1,1380 @@ +{ + "version": 1, + "minor_version": 1, + "key": "bluetooth.remote_scanners", + "data": { + "atom-bluetooth-proxy-ceaac4": { + "connectable": true, + "expire_seconds": 195, + "discovered_device_advertisement_datas": { + "51:8E:5B:A8:44:8D": { + "device": { + "address": "51:8E:5B:A8:44:8D", + "name": "98E7829A01E23895E9", + "rssi": -80, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": "98E7829A01E23895E9", + "manufacturer_data": { + "1447": "051001000000000000022200ca" + }, + "service_data": {}, + "service_uuids": [ + "0000fe07-0000-1000-8000-00805f9b34fb" + ], + "rssi": -80, + "tx_power": -127, + "platform_data": [] + } + }, + "16:9A:28:FC:D5:A8": { + "device": { + "address": "16:9A:28:FC:D5:A8", + "name": "", + "rssi": -77, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "0906036dc0a86b7e130c0a59cff1970c9f010401030c" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -77, + "tx_power": -127, + "platform_data": [] + } + }, + "08:3A:F2:1E:32:69": { + "device": { + "address": "08:3A:F2:1E:32:69", + "name": "LOOKin_98F3320D", + "rssi": -91, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": "LOOKin_98F3320D", + "manufacturer_data": {}, + "service_data": {}, + "service_uuids": [ + "00001812-0000-1000-8000-00805f9b34fb" + ], + "rssi": -91, + "tx_power": -127, + "platform_data": [] + } + }, + "34:AB:95:85:6E:F1": { + "device": { + "address": "34:AB:95:85:6E:F1", + "name": "LOOKin_98F330B5", + "rssi": -96, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": "LOOKin_98F330B5", + "manufacturer_data": {}, + "service_data": {}, + "service_uuids": [ + "00001812-0000-1000-8000-00805f9b34fb" + ], + "rssi": -96, + "tx_power": -127, + "platform_data": [] + } + }, + "D8:2E:AD:CD:0D:85": { + "device": { + "address": "D8:2E:AD:CD:0D:85", + "name": "", + "rssi": -81, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "89": "d82eadcd0d85" + }, + "service_data": { + "00000d00-0000-1000-8000-00805f9b34fb": "481061" + }, + "service_uuids": [ + "cba20d00-224d-11e6-9fb8-0002a5d5c51b" + ], + "rssi": -81, + "tx_power": -127, + "platform_data": [] + } + }, + "D4:B2:A1:7C:42:17": { + "device": { + "address": "D4:B2:A1:7C:42:17", + "name": "RZSS", + "rssi": -70, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": "RZSS", + "manufacturer_data": { + "1033": "0c246232" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -70, + "tx_power": -127, + "platform_data": [] + } + }, + "D8:EF:2F:41:B1:34": { + "device": { + "address": "D8:EF:2F:41:B1:34", + "name": "RZSS", + "rssi": -64, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": "RZSS", + "manufacturer_data": { + "1033": "0c246064" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -64, + "tx_power": -127, + "platform_data": [] + } + }, + "60:55:F9:29:FA:2A": { + "device": { + "address": "60:55:F9:29:FA:2A", + "name": "", + "rssi": -62, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "2409": "6055f929fa2a010000000000" + }, + "service_data": { + "0000fd3d-0000-1000-8000-00805f9b34fb": "6a0064" + }, + "service_uuids": [], + "rssi": -62, + "tx_power": -127, + "platform_data": [] + } + }, + "F3:33:CE:84:58:4A": { + "device": { + "address": "F3:33:CE:84:58:4A", + "name": "98E7822ED4F830AEE9", + "rssi": -78, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": "98E7822ED4F830AEE9", + "manufacturer_data": { + "1447": "051001000000000000022200ca" + }, + "service_data": {}, + "service_uuids": [ + "0000fe07-0000-1000-8000-00805f9b34fb" + ], + "rssi": -78, + "tx_power": -127, + "platform_data": [] + } + }, + "CB:39:CD:C4:3D:46": { + "device": { + "address": "CB:39:CD:C4:3D:46", + "name": "", + "rssi": -84, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "2409": "cb39cdc43d46c02e5c55ffff84" + }, + "service_data": { + "0000fd3d-0000-1000-8000-00805f9b34fb": "64005a445c55ffff84" + }, + "service_uuids": [], + "rssi": -84, + "tx_power": -127, + "platform_data": [] + } + }, + "E2:4B:AB:57:4F:CC": { + "device": { + "address": "E2:4B:AB:57:4F:CC", + "name": "", + "rssi": -73, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "89": "e24bab574fcc" + }, + "service_data": { + "00000d00-0000-1000-8000-00805f9b34fb": "4810d7" + }, + "service_uuids": [ + "cba20d00-224d-11e6-9fb8-0002a5d5c51b" + ], + "rssi": -73, + "tx_power": -127, + "platform_data": [] + } + }, + "7F:B3:F7:28:DD:5B": { + "device": { + "address": "7F:B3:F7:28:DD:5B", + "name": "", + "rssi": -80, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "1005229829e5ab" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -80, + "tx_power": -127, + "platform_data": [] + } + }, + "B4:E8:42:51:76:67": { + "device": { + "address": "B4:E8:42:51:76:67", + "name": "LEDnetWF010097517666", + "rssi": -86, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": "LEDnetWF010097517666", + "manufacturer_data": { + "23041": "5205b4e84251766600971603000000000000000000000000000000" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -86, + "tx_power": -127, + "platform_data": [] + } + }, + "EA:CF:16:0C:16:11": { + "device": { + "address": "EA:CF:16:0C:16:11", + "name": "", + "rssi": -79, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "12020003" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -79, + "tx_power": -127, + "platform_data": [] + } + }, + "08:3A:F2:1E:28:F1": { + "device": { + "address": "08:3A:F2:1E:28:F1", + "name": "LOOKin_98F33208", + "rssi": -86, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": "LOOKin_98F33208", + "manufacturer_data": {}, + "service_data": {}, + "service_uuids": [ + "00001812-0000-1000-8000-00805f9b34fb" + ], + "rssi": -86, + "tx_power": -127, + "platform_data": [] + } + }, + "08:3A:F2:1E:2B:2D": { + "device": { + "address": "08:3A:F2:1E:2B:2D", + "name": "LOOKin_98F330F3", + "rssi": -80, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": "LOOKin_98F330F3", + "manufacturer_data": {}, + "service_data": {}, + "service_uuids": [ + "00001812-0000-1000-8000-00805f9b34fb" + ], + "rssi": -80, + "tx_power": -127, + "platform_data": [] + } + }, + "3D:AF:40:AD:B3:8B": { + "device": { + "address": "3D:AF:40:AD:B3:8B", + "name": "", + "rssi": -88, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "0906036fc0a86b3e130c0a98ce54cc75e4010401030c" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -88, + "tx_power": -127, + "platform_data": [] + } + }, + "61:25:DF:19:90:98": { + "device": { + "address": "61:25:DF:19:90:98", + "name": "", + "rssi": -74, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "1007321fcbeaadd248" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -74, + "tx_power": -127, + "platform_data": [] + } + }, + "34:AB:95:85:6E:F9": { + "device": { + "address": "34:AB:95:85:6E:F9", + "name": "LOOKin_98F330B4", + "rssi": -91, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": "LOOKin_98F330B4", + "manufacturer_data": {}, + "service_data": {}, + "service_uuids": [ + "00001812-0000-1000-8000-00805f9b34fb" + ], + "rssi": -91, + "tx_power": -127, + "platform_data": [] + } + }, + "54:E6:1B:F0:20:97": { + "device": { + "address": "54:E6:1B:F0:20:97", + "name": "", + "rssi": -54, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "0f08c00ad7f8b400440c10020100" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -54, + "tx_power": -127, + "platform_data": [] + } + }, + "41:CC:AF:67:18:EE": { + "device": { + "address": "41:CC:AF:67:18:EE", + "name": "", + "rssi": -80, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "0c0e089c6a11d70cc17e3e52928b96b71006411d75808748" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -80, + "tx_power": -127, + "platform_data": [] + } + }, + "8C:EA:48:4D:93:3B": { + "device": { + "address": "8C:EA:48:4D:93:3B", + "name": "", + "rssi": -98, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "117": "42040180668cea484d933b8eea484d933a01000000000000" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -98, + "tx_power": -127, + "platform_data": [] + } + }, + "C5:2D:5E:AB:52:76": { + "device": { + "address": "C5:2D:5E:AB:52:76", + "name": "", + "rssi": -66, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "89": "c52d5eab5276" + }, + "service_data": { + "00000d00-0000-1000-8000-00805f9b34fb": "481063" + }, + "service_uuids": [ + "cba20d00-224d-11e6-9fb8-0002a5d5c51b" + ], + "rssi": -66, + "tx_power": -127, + "platform_data": [] + } + }, + "C0:49:EF:8C:1F:86": { + "device": { + "address": "C0:49:EF:8C:1F:86", + "name": "ShellyPlugUS-C049EF8C1F84", + "rssi": -36, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": "ShellyPlugUS-C049EF8C1F84", + "manufacturer_data": {}, + "service_data": {}, + "service_uuids": [], + "rssi": -36, + "tx_power": -127, + "platform_data": [] + } + }, + "F8:04:2E:E1:71:9D": { + "device": { + "address": "F8:04:2E:E1:71:9D", + "name": "", + "rssi": -90, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "117": "42040180a0f8042ee1719dfa042ee1719c01000000000000" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -90, + "tx_power": -127, + "platform_data": [] + } + }, + "C6:D5:B5:C1:2A:22": { + "device": { + "address": "C6:D5:B5:C1:2A:22", + "name": "", + "rssi": -73, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "89": "c6d5b5c12a22" + }, + "service_data": { + "00000d00-0000-1000-8000-00805f9b34fb": "4810e1" + }, + "service_uuids": [ + "cba20d00-224d-11e6-9fb8-0002a5d5c51b" + ], + "rssi": -73, + "tx_power": -127, + "platform_data": [] + } + }, + "31:FA:70:5F:9C:AC": { + "device": { + "address": "31:FA:70:5F:9C:AC", + "name": "", + "rssi": -78, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "0906038ec0a86a5e" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -78, + "tx_power": -127, + "platform_data": [] + } + }, + "D5:C1:1D:1A:23:57": { + "device": { + "address": "D5:C1:1D:1A:23:57", + "name": "RZSS", + "rssi": -70, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": "RZSS", + "manufacturer_data": { + "1033": "0c246200" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -70, + "tx_power": -127, + "platform_data": [] + } + }, + "2C:BF:C0:1C:D7:4B": { + "device": { + "address": "2C:BF:C0:1C:D7:4B", + "name": "", + "rssi": -89, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "0906035cc0a86a9d" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -89, + "tx_power": -127, + "platform_data": [] + } + }, + "B4:E8:42:DB:12:85": { + "device": { + "address": "B4:E8:42:DB:12:85", + "name": "LEDnetWF010097DB1284", + "rssi": -66, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": "LEDnetWF010097DB1284", + "manufacturer_data": { + "23041": "5205b4e842db128400971603000000000000000000000000000000" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -66, + "tx_power": -127, + "platform_data": [] + } + }, + "6E:48:DA:52:2F:13": { + "device": { + "address": "6E:48:DA:52:2F:13", + "name": "", + "rssi": -92, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "10051a1c2e6061" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -92, + "tx_power": -127, + "platform_data": [] + } + }, + "34:AB:95:85:66:6D": { + "device": { + "address": "34:AB:95:85:66:6D", + "name": "LOOKin_98F33163", + "rssi": -91, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": "LOOKin_98F33163", + "manufacturer_data": {}, + "service_data": {}, + "service_uuids": [ + "00001812-0000-1000-8000-00805f9b34fb" + ], + "rssi": -91, + "tx_power": -127, + "platform_data": [] + } + }, + "F8:04:2E:E1:9F:19": { + "device": { + "address": "F8:04:2E:E1:9F:19", + "name": "", + "rssi": -92, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "117": "42040180a0f8042ee19f19fa042ee19f1801000000000000" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -92, + "tx_power": -127, + "platform_data": [] + } + }, + "68:3C:69:9E:A1:18": { + "device": { + "address": "68:3C:69:9E:A1:18", + "name": "", + "rssi": -78, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "0c0e089c6a11d70cc17e3e52928b96b71006411d75808748" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -78, + "tx_power": -127, + "platform_data": [] + } + }, + "07:68:27:D9:15:40": { + "device": { + "address": "07:68:27:D9:15:40", + "name": "", + "rssi": -74, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": {}, + "service_data": { + "0000fd6f-0000-1000-8000-00805f9b34fb": "1b8fb635708f1ef520edb36597fb0586b58f7214" + }, + "service_uuids": [ + "0000fd6f-0000-1000-8000-00805f9b34fb" + ], + "rssi": -74, + "tx_power": -127, + "platform_data": [] + } + }, + "F5:C7:51:3D:B7:33": { + "device": { + "address": "F5:C7:51:3D:B7:33", + "name": "RZSS", + "rssi": -81, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": "RZSS", + "manufacturer_data": { + "1033": "0c246164" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -81, + "tx_power": -127, + "platform_data": [] + } + }, + "E0:1F:A6:3A:C5:66": { + "device": { + "address": "E0:1F:A6:3A:C5:66", + "name": "RZSS", + "rssi": -90, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": "RZSS", + "manufacturer_data": { + "1033": "0c246200" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -90, + "tx_power": -127, + "platform_data": [] + } + }, + "78:9C:85:08:E6:2A": { + "device": { + "address": "78:9C:85:08:E6:2A", + "name": "M1029QJ", + "rssi": -73, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": "M1029QJ", + "manufacturer_data": { + "76": "06310080e7146a373406009c2104023cb9eb0e" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -73, + "tx_power": -127, + "platform_data": [] + } + }, + "4F:EB:42:E2:57:4A": { + "device": { + "address": "4F:EB:42:E2:57:4A", + "name": "a25bd0d9", + "rssi": -86, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": "a25bd0d9", + "manufacturer_data": { + "1015": "1a00000000004d617569204f776c" + }, + "service_data": {}, + "service_uuids": [ + "39d6b333-adad-45c8-b6ee-eac6c4cd0000" + ], + "rssi": -86, + "tx_power": -127, + "platform_data": [] + } + }, + "A8:51:AB:8A:5A:84": { + "device": { + "address": "A8:51:AB:8A:5A:84", + "name": "", + "rssi": -94, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "1005021484867e" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -94, + "tx_power": -127, + "platform_data": [] + } + }, + "5E:5C:AC:CF:CD:2D": { + "device": { + "address": "5E:5C:AC:CF:CD:2D", + "name": "", + "rssi": -82, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "0f059000da640210022d04" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -82, + "tx_power": -127, + "platform_data": [] + } + }, + "0A:73:BD:22:FE:E2": { + "device": { + "address": "0A:73:BD:22:FE:E2", + "name": "", + "rssi": -92, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "09060318c0a86bef" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -92, + "tx_power": -127, + "platform_data": [] + } + }, + "B4:E8:42:9E:A4:F5": { + "device": { + "address": "B4:E8:42:9E:A4:F5", + "name": "LEDnetWF0100979EA4F4", + "rssi": -82, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": "LEDnetWF0100979EA4F4", + "manufacturer_data": { + "23041": "5202b4e8429ea4f400970c03010203040506070809a1a2a3a4a5a6" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -82, + "tx_power": -127, + "platform_data": [] + } + }, + "B4:E8:42:53:56:F9": { + "device": { + "address": "B4:E8:42:53:56:F9", + "name": "LEDnetWF0100975356F8", + "rssi": -98, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": "LEDnetWF0100975356F8", + "manufacturer_data": { + "23041": "5205b4e8425356f800971603000000000000000000000000000000" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -98, + "tx_power": -127, + "platform_data": [] + } + }, + "D4:2C:41:97:56:6E": { + "device": { + "address": "D4:2C:41:97:56:6E", + "name": "RZSS", + "rssi": -82, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": "RZSS", + "manufacturer_data": { + "1033": "0c245f32" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -82, + "tx_power": -127, + "platform_data": [] + } + }, + "64:04:FF:2C:E3:55": { + "device": { + "address": "64:04:FF:2C:E3:55", + "name": "", + "rssi": -88, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "0f0590007f5fd910022504" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -88, + "tx_power": -127, + "platform_data": [] + } + }, + "A4:C1:38:BD:91:BB": { + "device": { + "address": "A4:C1:38:BD:91:BB", + "name": "LYWSD03MMC", + "rssi": -78, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": "LYWSD03MMC", + "manufacturer_data": {}, + "service_data": { + "0000fe95-0000-1000-8000-00805f9b34fb": "30585b055bbb91bd38c1a408" + }, + "service_uuids": [], + "rssi": -78, + "tx_power": -127, + "platform_data": [] + } + }, + "B4:E8:42:DA:BA:23": { + "device": { + "address": "B4:E8:42:DA:BA:23", + "name": "LEDnetWF010097DABA22", + "rssi": -92, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": "LEDnetWF010097DABA22", + "manufacturer_data": { + "23041": "5205b4e842daba2200971603000000000000000000000000000000" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -92, + "tx_power": -127, + "platform_data": [] + } + }, + "E3:A5:63:3E:5E:23": { + "device": { + "address": "E3:A5:63:3E:5E:23", + "name": "", + "rssi": -70, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "12020003" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -70, + "tx_power": -127, + "platform_data": [] + } + }, + "68:E3:C2:4D:85:EE": { + "device": { + "address": "68:E3:C2:4D:85:EE", + "name": "", + "rssi": -97, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "10051a1c53afb1" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -97, + "tx_power": -127, + "platform_data": [] + } + }, + "B4:E8:42:52:4B:AD": { + "device": { + "address": "B4:E8:42:52:4B:AD", + "name": "LEDnetWF010097524BAC", + "rssi": -84, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": "LEDnetWF010097524BAC", + "manufacturer_data": { + "23041": "5205b4e842524bac00971603000000000000000000000000000000" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -84, + "tx_power": -127, + "platform_data": [] + } + }, + "58:2D:34:60:D5:CD": { + "device": { + "address": "58:2D:34:60:D5:CD", + "name": "Qingping Motion & Light", + "rssi": -83, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": "Qingping Motion & Light", + "manufacturer_data": {}, + "service_data": { + "0000fe95-0000-1000-8000-00805f9b34fb": "3058830a02cdd560342d5808", + "0000fdcd-0000-1000-8000-00805f9b34fb": "4812cdd560342d580804010f00000f0166" + }, + "service_uuids": [], + "rssi": -83, + "tx_power": -127, + "platform_data": [] + } + }, + "72:90:B0:53:F1:15": { + "device": { + "address": "72:90:B0:53:F1:15", + "name": "", + "rssi": -94, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "0f059000dc161910022d04" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -94, + "tx_power": -127, + "platform_data": [] + } + }, + "D5:1C:FB:39:78:56": { + "device": { + "address": "D5:1C:FB:39:78:56", + "name": "", + "rssi": -93, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "89": "d51cfb397856" + }, + "service_data": { + "00000d00-0000-1000-8000-00805f9b34fb": "4890d7" + }, + "service_uuids": [ + "cba20d00-224d-11e6-9fb8-0002a5d5c51b" + ], + "rssi": -93, + "tx_power": -127, + "platform_data": [] + } + }, + "08:3A:F2:1E:2B:05": { + "device": { + "address": "08:3A:F2:1E:2B:05", + "name": "LOOKin_98F330C2", + "rssi": -98, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": "LOOKin_98F330C2", + "manufacturer_data": {}, + "service_data": {}, + "service_uuids": [ + "00001812-0000-1000-8000-00805f9b34fb" + ], + "rssi": -98, + "tx_power": -127, + "platform_data": [] + } + }, + "FA:8F:F4:A8:9E:BC": { + "device": { + "address": "FA:8F:F4:A8:9E:BC", + "rssi": -92, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "12025400" + }, + "service_data": {}, + "platform_data": [] + } + }, + "EB:0B:36:35:6F:A4": { + "device": { + "address": "EB:0B:36:35:6F:A4", + "name": "", + "rssi": -90, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 1 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "76": "12199021f9370af25a5ec7825f703a4ac47a7b76c08b5695e101ba" + }, + "service_data": {}, + "service_uuids": [], + "rssi": -90, + "tx_power": -127, + "platform_data": [] + } + }, + "AC:67:B2:6A:18:2A": { + "device": { + "address": "AC:67:B2:6A:18:2A", + "name": "", + "rssi": -68, + "details": { + "source": "atom-bluetooth-proxy-ceaac4", + "address_type": 0 + } + }, + "advertisement_data": { + "local_name": null, + "manufacturer_data": { + "741": "ac67b26a182a" + }, + "service_data": { + "00000d00-0000-1000-8000-00805f9b34fb": "65000000613d7300" + }, + "service_uuids": [ + "cba20d00-224d-11e6-9fb8-0002a5d5c51b" + ], + "rssi": -68, + "tx_power": -127, + "platform_data": [] + } + } + }, + "discovered_device_timestamps": { + "51:8E:5B:A8:44:8D": 1670722715.9919367, + "16:9A:28:FC:D5:A8": 1670722719.3087196, + "08:3A:F2:1E:32:69": 1670722719.1660142, + "34:AB:95:85:6E:F1": 1670722720.2908847, + "D8:2E:AD:CD:0D:85": 1670722720.2915154, + "D4:B2:A1:7C:42:17": 1670722719.0863516, + "D8:EF:2F:41:B1:34": 1670722718.7625487, + "60:55:F9:29:FA:2A": 1670722720.291449, + "F3:33:CE:84:58:4A": 1670722720.291406, + "CB:39:CD:C4:3D:46": 1670722715.599468, + "E2:4B:AB:57:4F:CC": 1670722720.291419, + "7F:B3:F7:28:DD:5B": 1670722718.9691222, + "B4:E8:42:51:76:67": 1670722710.6735754, + "EA:CF:16:0C:16:11": 1670722715.9919817, + "08:3A:F2:1E:28:F1": 1670722720.291503, + "08:3A:F2:1E:2B:2D": 1670722720.2914848, + "3D:AF:40:AD:B3:8B": 1670722718.85895, + "61:25:DF:19:90:98": 1670722717.941397, + "34:AB:95:85:6E:F9": 1670722718.7632113, + "54:E6:1B:F0:20:97": 1670722717.1185102, + "41:CC:AF:67:18:EE": 1670722720.2912233, + "8C:EA:48:4D:93:3B": 1670722720.2910836, + "C5:2D:5E:AB:52:76": 1670722718.558827, + "C0:49:EF:8C:1F:86": 1670722717.9688299, + "F8:04:2E:E1:71:9D": 1670722717.526199, + "C6:D5:B5:C1:2A:22": 1670722717.0176404, + "31:FA:70:5F:9C:AC": 1670722720.2911327, + "D5:C1:1D:1A:23:57": 1670722717.639859, + "2C:BF:C0:1C:D7:4B": 1670722720.2914364, + "B4:E8:42:DB:12:85": 1670722719.0764725, + "6E:48:DA:52:2F:13": 1670722720.2913177, + "34:AB:95:85:66:6D": 1670722720.2912607, + "F8:04:2E:E1:9F:19": 1670722720.2911522, + "68:3C:69:9E:A1:18": 1670722719.1658278, + "07:68:27:D9:15:40": 1670722720.2912369, + "F5:C7:51:3D:B7:33": 1670722720.2911801, + "E0:1F:A6:3A:C5:66": 1670722713.7567363, + "78:9C:85:08:E6:2A": 1670722712.5362735, + "4F:EB:42:E2:57:4A": 1670722716.5097647, + "A8:51:AB:8A:5A:84": 1670722711.9280567, + "5E:5C:AC:CF:CD:2D": 1670722716.2974086, + "0A:73:BD:22:FE:E2": 1670722720.2914622, + "B4:E8:42:9E:A4:F5": 1670722718.5590498, + "B4:E8:42:53:56:F9": 1670722712.4074357, + "D4:2C:41:97:56:6E": 1670722718.3550117, + "64:04:FF:2C:E3:55": 1670722718.1470506, + "A4:C1:38:BD:91:BB": 1670722712.7268498, + "B4:E8:42:DA:BA:23": 1670722720.2910104, + "E3:A5:63:3E:5E:23": 1670722713.8445594, + "68:E3:C2:4D:85:EE": 1670722713.8488224, + "B4:E8:42:52:4B:AD": 1670722714.0646405, + "58:2D:34:60:D5:CD": 1670722715.377663, + "72:90:B0:53:F1:15": 1670722718.1468875, + "D5:1C:FB:39:78:56": 1670722715.2835894, + "08:3A:F2:1E:2B:05": 1670722719.0629613, + "FA:8F:F4:A8:9E:BC": 1670722717.22681, + "EB:0B:36:35:6F:A4": 1670722717.6297317, + "AC:67:B2:6A:18:2A": 1670722719.086469 + } + } + } +} diff --git a/tests/components/bluetooth/test_active_update_coordinator.py b/tests/components/bluetooth/test_active_update_coordinator.py index 000a415b19b..a82de1e0e08 100644 --- a/tests/components/bluetooth/test_active_update_coordinator.py +++ b/tests/components/bluetooth/test_active_update_coordinator.py @@ -1,19 +1,23 @@ -"""Tests for the Bluetooth integration PassiveBluetoothDataUpdateCoordinator.""" +"""Tests for the Bluetooth integration ActiveBluetoothDataUpdateCoordinator.""" from __future__ import annotations import asyncio +from collections.abc import Callable, Coroutine import logging -from unittest.mock import MagicMock, call +from typing import Any +from unittest.mock import MagicMock -from bleak import BleakError +from bleak.exc import BleakError from homeassistant.components.bluetooth import ( DOMAIN, + BluetoothChange, BluetoothScanningMode, BluetoothServiceInfoBleak, ) from homeassistant.components.bluetooth.active_update_coordinator import ( - ActiveBluetoothProcessorCoordinator, + _T, + ActiveBluetoothDataUpdateCoordinator, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.debounce import Debouncer @@ -36,337 +40,344 @@ GENERIC_BLUETOOTH_SERVICE_INFO = BluetoothServiceInfo( service_uuids=[], source="local", ) + GENERIC_BLUETOOTH_SERVICE_INFO_2 = BluetoothServiceInfo( name="Generic", address="aa:bb:cc:dd:ee:ff", rssi=-95, - manufacturer_data={1: b"\x01\x01\x01\x01\x01\x01\x01\x01", 2: b"\x02"}, + manufacturer_data={ + 2: b"\x01\x01\x01\x01\x01\x01\x01\x01", + }, service_data={}, service_uuids=[], source="local", ) -async def test_basic_usage( - hass: HomeAssistant, mock_bleak_scanner_start, mock_bluetooth_adapters -): - """Test basic usage of the ActiveBluetoothProcessorCoordinator.""" +class MyCoordinator(ActiveBluetoothDataUpdateCoordinator[dict[str, Any]]): + """An example coordinator that subclasses ActiveBluetoothDataUpdateCoordinator.""" + + def __init__( + self, + hass: HomeAssistant, + logger: logging.Logger, + *, + address: str, + mode: BluetoothScanningMode, + needs_poll_method: Callable[[BluetoothServiceInfoBleak, float | None], bool], + poll_method: Callable[ + [BluetoothServiceInfoBleak], + Coroutine[Any, Any, _T], + ] + | None = None, + poll_debouncer: Debouncer[Coroutine[Any, Any, None]] | None = None, + connectable: bool = True, + ) -> None: + """Initialize the coordinator.""" + self.passive_data: dict[str, Any] = {} + super().__init__( + hass=hass, + logger=logger, + address=address, + mode=mode, + needs_poll_method=needs_poll_method, + poll_method=poll_method, + poll_debouncer=poll_debouncer, + connectable=connectable, + ) + + def _async_handle_bluetooth_event( + self, + service_info: BluetoothServiceInfo, + change: BluetoothChange, + ) -> None: + """Handle a Bluetooth event.""" + self.passive_data = {"rssi": service_info.rssi} + super()._async_handle_bluetooth_event(service_info, change) + + +async def test_basic_usage(hass, mock_bleak_scanner_start, mock_bluetooth_adapters): + """Test basic usage of the ActiveBluetoothDataUpdateCoordinator.""" await async_setup_component(hass, DOMAIN, {DOMAIN: {}}) - def _update_method(service_info: BluetoothServiceInfoBleak): - return {"testdata": 0} - - def _poll_needed(*args, **kwargs): + def _needs_poll( + service_info: BluetoothServiceInfoBleak, seconds_since_last_poll: float | None + ) -> bool: return True - async def _poll(*args, **kwargs): - return {"testdata": 1} + async def _poll_method(service_info: BluetoothServiceInfoBleak) -> dict[str, Any]: + return {"fake": "data"} - coordinator = ActiveBluetoothProcessorCoordinator( - hass, - _LOGGER, + coordinator = MyCoordinator( + hass=hass, + logger=_LOGGER, address="aa:bb:cc:dd:ee:ff", mode=BluetoothScanningMode.ACTIVE, - update_method=_update_method, - needs_poll_method=_poll_needed, - poll_method=_poll, + needs_poll_method=_needs_poll, + poll_method=_poll_method, ) assert coordinator.available is False # no data yet - processor = MagicMock() - coordinator.async_register_processor(processor) - async_handle_update = processor.async_handle_update + mock_listener = MagicMock() + unregister_listener = coordinator.async_add_listener(mock_listener) cancel = coordinator.async_start() inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO) await hass.async_block_till_done() - - assert coordinator.available is True - - # async_handle_update should have been called twice - # The first time, it was passed the data from parsing the advertisement - # The second time, it was passed the data from polling - assert len(async_handle_update.mock_calls) == 2 - assert async_handle_update.mock_calls[0] == call({"testdata": 0}) - assert async_handle_update.mock_calls[1] == call({"testdata": 1}) + assert coordinator.passive_data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO.rssi} + assert coordinator.data == {"fake": "data"} cancel() + unregister_listener() -async def test_poll_can_be_skipped( - hass: HomeAssistant, mock_bleak_scanner_start, mock_bluetooth_adapters +async def test_bleak_error_during_polling( + hass, mock_bleak_scanner_start, mock_bluetooth_adapters ): - """Test need_poll callback works and can skip a poll if its not needed.""" + """Test bleak error during polling ActiveBluetoothDataUpdateCoordinator.""" await async_setup_component(hass, DOMAIN, {DOMAIN: {}}) + poll_count = 0 - flag = True - - def _update_method(service_info: BluetoothServiceInfoBleak): - return {"testdata": None} - - def _poll_needed(*args, **kwargs): - nonlocal flag - return flag - - async def _poll(*args, **kwargs): - return {"testdata": flag} - - coordinator = ActiveBluetoothProcessorCoordinator( - hass, - _LOGGER, - address="aa:bb:cc:dd:ee:ff", - mode=BluetoothScanningMode.ACTIVE, - update_method=_update_method, - needs_poll_method=_poll_needed, - poll_method=_poll, - poll_debouncer=Debouncer( - hass, - _LOGGER, - cooldown=0, - immediate=True, - ), - ) - assert coordinator.available is False # no data yet - - processor = MagicMock() - coordinator.async_register_processor(processor) - async_handle_update = processor.async_handle_update - - cancel = coordinator.async_start() - - inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO) - await hass.async_block_till_done() - assert async_handle_update.mock_calls[-1] == call({"testdata": True}) - - flag = False - - inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO_2) - await hass.async_block_till_done() - assert async_handle_update.mock_calls[-1] == call({"testdata": None}) - - flag = True - - inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO) - await hass.async_block_till_done() - assert async_handle_update.mock_calls[-1] == call({"testdata": True}) - - cancel() - - -async def test_bleak_error_and_recover( - hass: HomeAssistant, mock_bleak_scanner_start, mock_bluetooth_adapters, caplog -): - """Test bleak error handling and recovery.""" - await async_setup_component(hass, DOMAIN, {DOMAIN: {}}) - - flag = True - - def _update_method(service_info: BluetoothServiceInfoBleak): - return {"testdata": None} - - def _poll_needed(*args, **kwargs): + def _needs_poll( + service_info: BluetoothServiceInfoBleak, seconds_since_last_poll: float | None + ) -> bool: return True - async def _poll(*args, **kwargs): - nonlocal flag - if flag: - raise BleakError("Connection was aborted") - return {"testdata": flag} + async def _poll_method(service_info: BluetoothServiceInfoBleak) -> dict[str, Any]: + nonlocal poll_count + poll_count += 1 + if poll_count == 1: + raise BleakError("fake bleak error") + return {"fake": "data"} - coordinator = ActiveBluetoothProcessorCoordinator( - hass, - _LOGGER, + coordinator = MyCoordinator( + hass=hass, + logger=_LOGGER, address="aa:bb:cc:dd:ee:ff", mode=BluetoothScanningMode.ACTIVE, - update_method=_update_method, - needs_poll_method=_poll_needed, - poll_method=_poll, - poll_debouncer=Debouncer( - hass, - _LOGGER, - cooldown=0, - immediate=True, - ), + needs_poll_method=_needs_poll, + poll_method=_poll_method, + poll_debouncer=Debouncer(hass, _LOGGER, cooldown=0, immediate=True), ) assert coordinator.available is False # no data yet - processor = MagicMock() - coordinator.async_register_processor(processor) - async_handle_update = processor.async_handle_update + mock_listener = MagicMock() + unregister_listener = coordinator.async_add_listener(mock_listener) cancel = coordinator.async_start() - # First poll fails inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO) await hass.async_block_till_done() - assert async_handle_update.mock_calls[-1] == call({"testdata": None}) + assert coordinator.passive_data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO.rssi} + assert coordinator.data is None + assert coordinator.last_poll_successful is False - assert ( - "aa:bb:cc:dd:ee:ff: Bluetooth error whilst polling: Connection was aborted" - in caplog.text - ) - - # Second poll works - flag = False inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO_2) await hass.async_block_till_done() - assert async_handle_update.mock_calls[-1] == call({"testdata": False}) + assert coordinator.passive_data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO_2.rssi} + assert coordinator.data == {"fake": "data"} + assert coordinator.last_poll_successful is True cancel() + unregister_listener() -async def test_poll_failure_and_recover( - hass: HomeAssistant, mock_bleak_scanner_start, mock_bluetooth_adapters +async def test_generic_exception_during_polling( + hass, mock_bleak_scanner_start, mock_bluetooth_adapters ): - """Test error handling and recovery.""" + """Test generic exception during polling ActiveBluetoothDataUpdateCoordinator.""" await async_setup_component(hass, DOMAIN, {DOMAIN: {}}) + poll_count = 0 - flag = True - - def _update_method(service_info: BluetoothServiceInfoBleak): - return {"testdata": None} - - def _poll_needed(*args, **kwargs): + def _needs_poll( + service_info: BluetoothServiceInfoBleak, seconds_since_last_poll: float | None + ) -> bool: return True - async def _poll(*args, **kwargs): - nonlocal flag - if flag: - raise RuntimeError("Poll failure") - return {"testdata": flag} + async def _poll_method(service_info: BluetoothServiceInfoBleak) -> dict[str, Any]: + nonlocal poll_count + poll_count += 1 + if poll_count == 1: + raise ValueError("fake error") + return {"fake": "data"} - coordinator = ActiveBluetoothProcessorCoordinator( - hass, - _LOGGER, + coordinator = MyCoordinator( + hass=hass, + logger=_LOGGER, address="aa:bb:cc:dd:ee:ff", mode=BluetoothScanningMode.ACTIVE, - update_method=_update_method, - needs_poll_method=_poll_needed, - poll_method=_poll, - poll_debouncer=Debouncer( - hass, - _LOGGER, - cooldown=0, - immediate=True, - ), + needs_poll_method=_needs_poll, + poll_method=_poll_method, + poll_debouncer=Debouncer(hass, _LOGGER, cooldown=0, immediate=True), ) assert coordinator.available is False # no data yet - processor = MagicMock() - coordinator.async_register_processor(processor) - async_handle_update = processor.async_handle_update + mock_listener = MagicMock() + unregister_listener = coordinator.async_add_listener(mock_listener) cancel = coordinator.async_start() - # First poll fails inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO) await hass.async_block_till_done() - assert async_handle_update.mock_calls[-1] == call({"testdata": None}) + assert coordinator.passive_data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO.rssi} + assert coordinator.data is None + assert coordinator.last_poll_successful is False - # Second poll works - flag = False inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO_2) await hass.async_block_till_done() - assert async_handle_update.mock_calls[-1] == call({"testdata": False}) + assert coordinator.passive_data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO_2.rssi} + assert coordinator.data == {"fake": "data"} + assert coordinator.last_poll_successful is True cancel() + unregister_listener() -async def test_second_poll_needed( - hass: HomeAssistant, mock_bleak_scanner_start, mock_bluetooth_adapters +async def test_polling_debounce( + hass, mock_bleak_scanner_start, mock_bluetooth_adapters ): - """If a poll is queued, by the time it starts it may no longer be needed.""" + """Test basic usage of the ActiveBluetoothDataUpdateCoordinator.""" await async_setup_component(hass, DOMAIN, {DOMAIN: {}}) + poll_count = 0 - count = 0 - - def _update_method(service_info: BluetoothServiceInfoBleak): - return {"testdata": None} - - # Only poll once - def _poll_needed(*args, **kwargs): - nonlocal count - return count == 0 - - async def _poll(*args, **kwargs): - nonlocal count - count += 1 - return {"testdata": count} - - coordinator = ActiveBluetoothProcessorCoordinator( - hass, - _LOGGER, - address="aa:bb:cc:dd:ee:ff", - mode=BluetoothScanningMode.ACTIVE, - update_method=_update_method, - needs_poll_method=_poll_needed, - poll_method=_poll, - ) - assert coordinator.available is False # no data yet - - processor = MagicMock() - coordinator.async_register_processor(processor) - async_handle_update = processor.async_handle_update - - cancel = coordinator.async_start() - - # First poll gets queued - inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO) - # Second poll gets stuck behind first poll - inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO_2) - - await hass.async_block_till_done() - assert async_handle_update.mock_calls[-1] == call({"testdata": 1}) - - cancel() - - -async def test_rate_limit( - hass: HomeAssistant, mock_bleak_scanner_start, mock_bluetooth_adapters -): - """Test error handling and recovery.""" - await async_setup_component(hass, DOMAIN, {DOMAIN: {}}) - - count = 0 - - def _update_method(service_info: BluetoothServiceInfoBleak): - return {"testdata": None} - - def _poll_needed(*args, **kwargs): + def _needs_poll( + service_info: BluetoothServiceInfoBleak, seconds_since_last_poll: float | None + ) -> bool: return True - async def _poll(*args, **kwargs): - nonlocal count - count += 1 - await asyncio.sleep(0) - return {"testdata": count} + async def _poll_method(service_info: BluetoothServiceInfoBleak) -> dict[str, Any]: + nonlocal poll_count + poll_count += 1 + await asyncio.sleep(0.0001) + return {"poll_count": poll_count} - coordinator = ActiveBluetoothProcessorCoordinator( - hass, - _LOGGER, + coordinator = MyCoordinator( + hass=hass, + logger=_LOGGER, address="aa:bb:cc:dd:ee:ff", mode=BluetoothScanningMode.ACTIVE, - update_method=_update_method, - needs_poll_method=_poll_needed, - poll_method=_poll, + needs_poll_method=_needs_poll, + poll_method=_poll_method, ) assert coordinator.available is False # no data yet - processor = MagicMock() - coordinator.async_register_processor(processor) - async_handle_update = processor.async_handle_update + mock_listener = MagicMock() + unregister_listener = coordinator.async_add_listener(mock_listener) cancel = coordinator.async_start() - # First poll gets queued inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO) - # Second poll gets stuck behind first poll inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO_2) - # Third poll gets stuck behind first poll doesn't get queued - inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO) - await hass.async_block_till_done() - assert async_handle_update.mock_calls[-1] == call({"testdata": 1}) + assert coordinator.passive_data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO.rssi} + # We should only get one poll because of the debounce + assert coordinator.data == {"poll_count": 1} cancel() + unregister_listener() + + +async def test_polling_debounce_with_custom_debouncer( + hass, mock_bleak_scanner_start, mock_bluetooth_adapters +): + """Test basic usage of the ActiveBluetoothDataUpdateCoordinator.""" + await async_setup_component(hass, DOMAIN, {DOMAIN: {}}) + poll_count = 0 + + def _needs_poll( + service_info: BluetoothServiceInfoBleak, seconds_since_last_poll: float | None + ) -> bool: + return True + + async def _poll_method(service_info: BluetoothServiceInfoBleak) -> dict[str, Any]: + nonlocal poll_count + poll_count += 1 + await asyncio.sleep(0.0001) + return {"poll_count": poll_count} + + coordinator = MyCoordinator( + hass=hass, + logger=_LOGGER, + address="aa:bb:cc:dd:ee:ff", + mode=BluetoothScanningMode.ACTIVE, + needs_poll_method=_needs_poll, + poll_method=_poll_method, + poll_debouncer=Debouncer(hass, _LOGGER, cooldown=0.1, immediate=True), + ) + assert coordinator.available is False # no data yet + + mock_listener = MagicMock() + unregister_listener = coordinator.async_add_listener(mock_listener) + + cancel = coordinator.async_start() + + inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO) + inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO_2) + await hass.async_block_till_done() + assert coordinator.passive_data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO.rssi} + # We should only get one poll because of the debounce + assert coordinator.data == {"poll_count": 1} + + cancel() + unregister_listener() + + +async def test_polling_rejecting_the_first_time( + hass, mock_bleak_scanner_start, mock_bluetooth_adapters +): + """Test need_poll rejects the first time ActiveBluetoothDataUpdateCoordinator.""" + await async_setup_component(hass, DOMAIN, {DOMAIN: {}}) + attempt = 0 + + def _needs_poll( + service_info: BluetoothServiceInfoBleak, seconds_since_last_poll: float | None + ) -> bool: + nonlocal attempt + attempt += 1 + return attempt != 1 + + async def _poll_method(service_info: BluetoothServiceInfoBleak) -> dict[str, Any]: + return {"fake": "data"} + + coordinator = MyCoordinator( + hass=hass, + logger=_LOGGER, + address="aa:bb:cc:dd:ee:ff", + mode=BluetoothScanningMode.ACTIVE, + needs_poll_method=_needs_poll, + poll_method=_poll_method, + ) + assert coordinator.available is False # no data yet + + mock_listener = MagicMock() + unregister_listener = coordinator.async_add_listener(mock_listener) + + cancel = coordinator.async_start() + + inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO) + await hass.async_block_till_done() + assert coordinator.passive_data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO.rssi} + # First poll is rejected, so no data yet + assert coordinator.data is None + + inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO) + await hass.async_block_till_done() + assert coordinator.passive_data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO.rssi} + # Data is the same so no poll check + assert coordinator.data is None + + inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO_2) + await hass.async_block_till_done() + assert coordinator.passive_data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO_2.rssi} + # Data is different so poll is done + assert coordinator.data == {"fake": "data"} + + inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO) + await hass.async_block_till_done() + assert coordinator.passive_data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO.rssi} + # Data is different again so poll is done + assert coordinator.data == {"fake": "data"} + + cancel() + unregister_listener() diff --git a/tests/components/bluetooth/test_active_update_processor.py b/tests/components/bluetooth/test_active_update_processor.py new file mode 100644 index 00000000000..99df97061fb --- /dev/null +++ b/tests/components/bluetooth/test_active_update_processor.py @@ -0,0 +1,372 @@ +"""Tests for the Bluetooth integration PassiveBluetoothDataUpdateCoordinator.""" +from __future__ import annotations + +import asyncio +import logging +from unittest.mock import MagicMock, call + +from bleak import BleakError + +from homeassistant.components.bluetooth import ( + DOMAIN, + BluetoothScanningMode, + BluetoothServiceInfoBleak, +) +from homeassistant.components.bluetooth.active_update_processor import ( + ActiveBluetoothProcessorCoordinator, +) +from homeassistant.core import HomeAssistant +from homeassistant.helpers.debounce import Debouncer +from homeassistant.helpers.service_info.bluetooth import BluetoothServiceInfo +from homeassistant.setup import async_setup_component + +from . import inject_bluetooth_service_info + +_LOGGER = logging.getLogger(__name__) + + +GENERIC_BLUETOOTH_SERVICE_INFO = BluetoothServiceInfo( + name="Generic", + address="aa:bb:cc:dd:ee:ff", + rssi=-95, + manufacturer_data={ + 1: b"\x01\x01\x01\x01\x01\x01\x01\x01", + }, + service_data={}, + service_uuids=[], + source="local", +) +GENERIC_BLUETOOTH_SERVICE_INFO_2 = BluetoothServiceInfo( + name="Generic", + address="aa:bb:cc:dd:ee:ff", + rssi=-95, + manufacturer_data={1: b"\x01\x01\x01\x01\x01\x01\x01\x01", 2: b"\x02"}, + service_data={}, + service_uuids=[], + source="local", +) + + +async def test_basic_usage( + hass: HomeAssistant, mock_bleak_scanner_start, mock_bluetooth_adapters +): + """Test basic usage of the ActiveBluetoothProcessorCoordinator.""" + await async_setup_component(hass, DOMAIN, {DOMAIN: {}}) + + def _update_method(service_info: BluetoothServiceInfoBleak): + return {"testdata": 0} + + def _poll_needed(*args, **kwargs): + return True + + async def _poll(*args, **kwargs): + return {"testdata": 1} + + coordinator = ActiveBluetoothProcessorCoordinator( + hass, + _LOGGER, + address="aa:bb:cc:dd:ee:ff", + mode=BluetoothScanningMode.ACTIVE, + update_method=_update_method, + needs_poll_method=_poll_needed, + poll_method=_poll, + ) + assert coordinator.available is False # no data yet + + processor = MagicMock() + coordinator.async_register_processor(processor) + async_handle_update = processor.async_handle_update + + cancel = coordinator.async_start() + + inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO) + await hass.async_block_till_done() + + assert coordinator.available is True + + # async_handle_update should have been called twice + # The first time, it was passed the data from parsing the advertisement + # The second time, it was passed the data from polling + assert len(async_handle_update.mock_calls) == 2 + assert async_handle_update.mock_calls[0] == call({"testdata": 0}) + assert async_handle_update.mock_calls[1] == call({"testdata": 1}) + + cancel() + + +async def test_poll_can_be_skipped( + hass: HomeAssistant, mock_bleak_scanner_start, mock_bluetooth_adapters +): + """Test need_poll callback works and can skip a poll if its not needed.""" + await async_setup_component(hass, DOMAIN, {DOMAIN: {}}) + + flag = True + + def _update_method(service_info: BluetoothServiceInfoBleak): + return {"testdata": None} + + def _poll_needed(*args, **kwargs): + nonlocal flag + return flag + + async def _poll(*args, **kwargs): + return {"testdata": flag} + + coordinator = ActiveBluetoothProcessorCoordinator( + hass, + _LOGGER, + address="aa:bb:cc:dd:ee:ff", + mode=BluetoothScanningMode.ACTIVE, + update_method=_update_method, + needs_poll_method=_poll_needed, + poll_method=_poll, + poll_debouncer=Debouncer( + hass, + _LOGGER, + cooldown=0, + immediate=True, + ), + ) + assert coordinator.available is False # no data yet + + processor = MagicMock() + coordinator.async_register_processor(processor) + async_handle_update = processor.async_handle_update + + cancel = coordinator.async_start() + + inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO) + await hass.async_block_till_done() + assert async_handle_update.mock_calls[-1] == call({"testdata": True}) + + flag = False + + inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO_2) + await hass.async_block_till_done() + assert async_handle_update.mock_calls[-1] == call({"testdata": None}) + + flag = True + + inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO) + await hass.async_block_till_done() + assert async_handle_update.mock_calls[-1] == call({"testdata": True}) + + cancel() + + +async def test_bleak_error_and_recover( + hass: HomeAssistant, mock_bleak_scanner_start, mock_bluetooth_adapters, caplog +): + """Test bleak error handling and recovery.""" + await async_setup_component(hass, DOMAIN, {DOMAIN: {}}) + + flag = True + + def _update_method(service_info: BluetoothServiceInfoBleak): + return {"testdata": None} + + def _poll_needed(*args, **kwargs): + return True + + async def _poll(*args, **kwargs): + nonlocal flag + if flag: + raise BleakError("Connection was aborted") + return {"testdata": flag} + + coordinator = ActiveBluetoothProcessorCoordinator( + hass, + _LOGGER, + address="aa:bb:cc:dd:ee:ff", + mode=BluetoothScanningMode.ACTIVE, + update_method=_update_method, + needs_poll_method=_poll_needed, + poll_method=_poll, + poll_debouncer=Debouncer( + hass, + _LOGGER, + cooldown=0, + immediate=True, + ), + ) + assert coordinator.available is False # no data yet + + processor = MagicMock() + coordinator.async_register_processor(processor) + async_handle_update = processor.async_handle_update + + cancel = coordinator.async_start() + + # First poll fails + inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO) + await hass.async_block_till_done() + assert async_handle_update.mock_calls[-1] == call({"testdata": None}) + + assert ( + "aa:bb:cc:dd:ee:ff: Bluetooth error whilst polling: Connection was aborted" + in caplog.text + ) + + # Second poll works + flag = False + inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO_2) + await hass.async_block_till_done() + assert async_handle_update.mock_calls[-1] == call({"testdata": False}) + + cancel() + + +async def test_poll_failure_and_recover( + hass: HomeAssistant, mock_bleak_scanner_start, mock_bluetooth_adapters +): + """Test error handling and recovery.""" + await async_setup_component(hass, DOMAIN, {DOMAIN: {}}) + + flag = True + + def _update_method(service_info: BluetoothServiceInfoBleak): + return {"testdata": None} + + def _poll_needed(*args, **kwargs): + return True + + async def _poll(*args, **kwargs): + nonlocal flag + if flag: + raise RuntimeError("Poll failure") + return {"testdata": flag} + + coordinator = ActiveBluetoothProcessorCoordinator( + hass, + _LOGGER, + address="aa:bb:cc:dd:ee:ff", + mode=BluetoothScanningMode.ACTIVE, + update_method=_update_method, + needs_poll_method=_poll_needed, + poll_method=_poll, + poll_debouncer=Debouncer( + hass, + _LOGGER, + cooldown=0, + immediate=True, + ), + ) + assert coordinator.available is False # no data yet + + processor = MagicMock() + coordinator.async_register_processor(processor) + async_handle_update = processor.async_handle_update + + cancel = coordinator.async_start() + + # First poll fails + inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO) + await hass.async_block_till_done() + assert async_handle_update.mock_calls[-1] == call({"testdata": None}) + + # Second poll works + flag = False + inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO_2) + await hass.async_block_till_done() + assert async_handle_update.mock_calls[-1] == call({"testdata": False}) + + cancel() + + +async def test_second_poll_needed( + hass: HomeAssistant, mock_bleak_scanner_start, mock_bluetooth_adapters +): + """If a poll is queued, by the time it starts it may no longer be needed.""" + await async_setup_component(hass, DOMAIN, {DOMAIN: {}}) + + count = 0 + + def _update_method(service_info: BluetoothServiceInfoBleak): + return {"testdata": None} + + # Only poll once + def _poll_needed(*args, **kwargs): + nonlocal count + return count == 0 + + async def _poll(*args, **kwargs): + nonlocal count + count += 1 + return {"testdata": count} + + coordinator = ActiveBluetoothProcessorCoordinator( + hass, + _LOGGER, + address="aa:bb:cc:dd:ee:ff", + mode=BluetoothScanningMode.ACTIVE, + update_method=_update_method, + needs_poll_method=_poll_needed, + poll_method=_poll, + ) + assert coordinator.available is False # no data yet + + processor = MagicMock() + coordinator.async_register_processor(processor) + async_handle_update = processor.async_handle_update + + cancel = coordinator.async_start() + + # First poll gets queued + inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO) + # Second poll gets stuck behind first poll + inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO_2) + + await hass.async_block_till_done() + assert async_handle_update.mock_calls[-1] == call({"testdata": 1}) + + cancel() + + +async def test_rate_limit( + hass: HomeAssistant, mock_bleak_scanner_start, mock_bluetooth_adapters +): + """Test error handling and recovery.""" + await async_setup_component(hass, DOMAIN, {DOMAIN: {}}) + + count = 0 + + def _update_method(service_info: BluetoothServiceInfoBleak): + return {"testdata": None} + + def _poll_needed(*args, **kwargs): + return True + + async def _poll(*args, **kwargs): + nonlocal count + count += 1 + await asyncio.sleep(0) + return {"testdata": count} + + coordinator = ActiveBluetoothProcessorCoordinator( + hass, + _LOGGER, + address="aa:bb:cc:dd:ee:ff", + mode=BluetoothScanningMode.ACTIVE, + update_method=_update_method, + needs_poll_method=_poll_needed, + poll_method=_poll, + ) + assert coordinator.available is False # no data yet + + processor = MagicMock() + coordinator.async_register_processor(processor) + async_handle_update = processor.async_handle_update + + cancel = coordinator.async_start() + + # First poll gets queued + inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO) + # Second poll gets stuck behind first poll + inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO_2) + # Third poll gets stuck behind first poll doesn't get queued + inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO) + + await hass.async_block_till_done() + assert async_handle_update.mock_calls[-1] == call({"testdata": 1}) + + cancel() diff --git a/tests/components/bluetooth/test_advertisement_tracker.py b/tests/components/bluetooth/test_advertisement_tracker.py index 29787f92910..d4255a8cc91 100644 --- a/tests/components/bluetooth/test_advertisement_tracker.py +++ b/tests/components/bluetooth/test_advertisement_tracker.py @@ -4,10 +4,9 @@ from datetime import timedelta import time from unittest.mock import patch -from bleak.backends.scanner import AdvertisementData, BLEDevice +from bleak.backends.scanner import BLEDevice from homeassistant.components.bluetooth import ( - BaseHaScanner, async_register_scanner, async_track_unavailable, ) @@ -22,6 +21,7 @@ from homeassistant.core import callback from homeassistant.util import dt as dt_util from . import ( + FakeScanner, generate_advertisement_data, inject_advertisement_with_time_and_source, inject_advertisement_with_time_and_source_connectable, @@ -304,16 +304,6 @@ async def test_advertisment_interval_longer_than_adapter_stack_timeout_adapter_c ) switchbot_device_went_unavailable = False - class FakeScanner(BaseHaScanner): - """Fake scanner.""" - - @property - def discovered_devices_and_advertisement_data( - self, - ) -> dict[str, tuple[BLEDevice, AdvertisementData]]: - """Return a list of discovered devices.""" - return {} - scanner = FakeScanner(hass, "new", "fake_adapter") cancel_scanner = async_register_scanner(hass, scanner, False) diff --git a/tests/components/bluetooth/test_api.py b/tests/components/bluetooth/test_api.py index b1d7e68972b..acb09c22ba7 100644 --- a/tests/components/bluetooth/test_api.py +++ b/tests/components/bluetooth/test_api.py @@ -2,13 +2,15 @@ from homeassistant.components import bluetooth -from homeassistant.components.bluetooth import BaseHaScanner, async_scanner_by_source +from homeassistant.components.bluetooth import async_scanner_by_source + +from . import FakeScanner async def test_scanner_by_source(hass, enable_bluetooth): """Test we can get a scanner by source.""" - hci2_scanner = BaseHaScanner(hass, "hci2", "hci2") + hci2_scanner = FakeScanner(hass, "hci2", "hci2") cancel_hci2 = bluetooth.async_register_scanner(hass, hci2_scanner, True) assert async_scanner_by_source(hass, "hci2") is hci2_scanner diff --git a/tests/components/bluetooth/test_base_scanner.py b/tests/components/bluetooth/test_base_scanner.py index 19c47361a6a..53c2716b0bd 100644 --- a/tests/components/bluetooth/test_base_scanner.py +++ b/tests/components/bluetooth/test_base_scanner.py @@ -8,16 +8,23 @@ from unittest.mock import patch from bleak.backends.device import BLEDevice from bleak.backends.scanner import AdvertisementData -from homeassistant.components.bluetooth import BaseHaRemoteScanner, HaBluetoothConnector +from homeassistant.components import bluetooth +from homeassistant.components.bluetooth import ( + BaseHaRemoteScanner, + HaBluetoothConnector, + storage, +) from homeassistant.components.bluetooth.const import ( CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS, FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS, ) +from homeassistant.helpers.json import json_loads +from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util from . import MockBleakClient, _get_manager, generate_advertisement_data -from tests.common import async_fire_time_changed +from tests.common import async_fire_time_changed, load_fixture async def test_remote_scanner(hass, enable_bluetooth): @@ -72,7 +79,7 @@ async def test_remote_scanner(hass, enable_bluetooth): HaBluetoothConnector(MockBleakClient, "mock_bleak_client", lambda: False), ) scanner = FakeScanner(hass, "esp32", "esp32", new_info_callback, connector, True) - scanner.async_setup() + unsetup = scanner.async_setup() cancel = manager.async_register_scanner(scanner, True) scanner.inject_advertisement(switchbot_device, switchbot_device_adv) @@ -103,6 +110,7 @@ async def test_remote_scanner(hass, enable_bluetooth): } cancel() + unsetup() async def test_remote_scanner_expires_connectable(hass, enable_bluetooth): @@ -143,7 +151,7 @@ async def test_remote_scanner_expires_connectable(hass, enable_bluetooth): HaBluetoothConnector(MockBleakClient, "mock_bleak_client", lambda: False), ) scanner = FakeScanner(hass, "esp32", "esp32", new_info_callback, connector, True) - scanner.async_setup() + unsetup = scanner.async_setup() cancel = manager.async_register_scanner(scanner, True) start_time_monotonic = time.monotonic() @@ -174,6 +182,7 @@ async def test_remote_scanner_expires_connectable(hass, enable_bluetooth): assert len(scanner.discovered_devices_and_advertisement_data) == 0 cancel() + unsetup() async def test_remote_scanner_expires_non_connectable(hass, enable_bluetooth): @@ -214,7 +223,7 @@ async def test_remote_scanner_expires_non_connectable(hass, enable_bluetooth): HaBluetoothConnector(MockBleakClient, "mock_bleak_client", lambda: False), ) scanner = FakeScanner(hass, "esp32", "esp32", new_info_callback, connector, False) - scanner.async_setup() + unsetup = scanner.async_setup() cancel = manager.async_register_scanner(scanner, True) start_time_monotonic = time.monotonic() @@ -268,6 +277,7 @@ async def test_remote_scanner_expires_non_connectable(hass, enable_bluetooth): assert len(scanner.discovered_devices_and_advertisement_data) == 0 cancel() + unsetup() async def test_base_scanner_connecting_behavior(hass, enable_bluetooth): @@ -308,7 +318,7 @@ async def test_base_scanner_connecting_behavior(hass, enable_bluetooth): HaBluetoothConnector(MockBleakClient, "mock_bleak_client", lambda: False), ) scanner = FakeScanner(hass, "esp32", "esp32", new_info_callback, connector, False) - scanner.async_setup() + unsetup = scanner.async_setup() cancel = manager.async_register_scanner(scanner, True) with scanner.connecting(): @@ -327,3 +337,60 @@ async def test_base_scanner_connecting_behavior(hass, enable_bluetooth): assert devices[0].name == "wohand" cancel() + unsetup() + + +async def test_restore_history_remote_adapter(hass, hass_storage): + """Test we can restore history for a remote adapter.""" + + data = hass_storage[storage.REMOTE_SCANNER_STORAGE_KEY] = json_loads( + load_fixture("bluetooth.remote_scanners", bluetooth.DOMAIN) + ) + now = time.time() + timestamps = data["data"]["atom-bluetooth-proxy-ceaac4"][ + "discovered_device_timestamps" + ] + for address in timestamps: + if address != "E3:A5:63:3E:5E:23": + timestamps[address] = now + + with patch("bluetooth_adapters.systems.linux.LinuxAdapters.history", {},), patch( + "bluetooth_adapters.systems.linux.LinuxAdapters.refresh", + ): + assert await async_setup_component(hass, bluetooth.DOMAIN, {}) + await hass.async_block_till_done() + + connector = ( + HaBluetoothConnector(MockBleakClient, "mock_bleak_client", lambda: False), + ) + scanner = BaseHaRemoteScanner( + hass, + "atom-bluetooth-proxy-ceaac4", + "atom-bluetooth-proxy-ceaac4", + lambda adv: None, + connector, + True, + ) + unsetup = scanner.async_setup() + cancel = _get_manager().async_register_scanner(scanner, True) + + assert "EB:0B:36:35:6F:A4" in scanner.discovered_devices_and_advertisement_data + assert "E3:A5:63:3E:5E:23" not in scanner.discovered_devices_and_advertisement_data + cancel() + unsetup() + + scanner = BaseHaRemoteScanner( + hass, + "atom-bluetooth-proxy-ceaac4", + "atom-bluetooth-proxy-ceaac4", + lambda adv: None, + connector, + True, + ) + unsetup = scanner.async_setup() + cancel = _get_manager().async_register_scanner(scanner, True) + assert "EB:0B:36:35:6F:A4" in scanner.discovered_devices_and_advertisement_data + assert "E3:A5:63:3E:5E:23" not in scanner.discovered_devices_and_advertisement_data + + cancel() + unsetup() diff --git a/tests/components/bluetooth/test_diagnostics.py b/tests/components/bluetooth/test_diagnostics.py index 417375e9820..a40f4b5c024 100644 --- a/tests/components/bluetooth/test_diagnostics.py +++ b/tests/components/bluetooth/test_diagnostics.py @@ -3,12 +3,18 @@ from unittest.mock import ANY, patch -from bleak.backends.scanner import BLEDevice +from bleak.backends.scanner import AdvertisementData, BLEDevice from bluetooth_adapters import DEFAULT_ADDRESS from homeassistant.components import bluetooth +from homeassistant.components.bluetooth import BaseHaRemoteScanner, HaBluetoothConnector -from . import generate_advertisement_data, inject_advertisement +from . import ( + MockBleakClient, + _get_manager, + generate_advertisement_data, + inject_advertisement, +) from tests.common import MockConfigEntry from tests.components.diagnostics import get_diagnostics_for_config_entry @@ -80,6 +86,7 @@ async def test_diagnostics( "product": "Bluetooth Adapter 5.0", "product_id": "aa01", "vendor_id": "cc01", + "connection_slots": 1, }, "hci1": { "address": "00:00:00:00:00:02", @@ -90,6 +97,7 @@ async def test_diagnostics( "product": "Bluetooth Adapter 5.0", "product_id": "aa01", "vendor_id": "cc01", + "connection_slots": 2, }, }, "dbus": { @@ -109,6 +117,11 @@ async def test_diagnostics( } }, "manager": { + "slot_manager": { + "adapter_slots": {"hci0": 5, "hci1": 2}, + "allocations_by_adapter": {"hci0": [], "hci1": []}, + "manager": False, + }, "adapters": { "hci0": { "address": "00:00:00:00:00:01", @@ -119,6 +132,7 @@ async def test_diagnostics( "product": "Bluetooth Adapter 5.0", "product_id": "aa01", "vendor_id": "cc01", + "connection_slots": 1, }, "hci1": { "address": "00:00:00:00:00:02", @@ -129,6 +143,7 @@ async def test_diagnostics( "product": "Bluetooth Adapter 5.0", "product_id": "aa01", "vendor_id": "cc01", + "connection_slots": 2, }, }, "advertisement_tracker": { @@ -153,13 +168,15 @@ async def test_diagnostics( -127, [[]], ], + "details": None, "name": "x", "rssi": -60, - "details": None, } ], "last_detection": ANY, + "monotonic_time": ANY, "name": "hci0 (00:00:00:00:00:01)", + "scanning": True, "source": "00:00:00:00:00:01", "start_time": ANY, "type": "HaScanner", @@ -178,13 +195,15 @@ async def test_diagnostics( -127, [[]], ], + "details": None, "name": "x", "rssi": -60, - "details": None, } ], "last_detection": ANY, + "monotonic_time": ANY, "name": "hci0 (00:00:00:00:00:01)", + "scanning": True, "source": "00:00:00:00:00:01", "start_time": ANY, "type": "HaScanner", @@ -203,13 +222,15 @@ async def test_diagnostics( -127, [[]], ], + "details": None, "name": "x", "rssi": -60, - "details": None, } ], "last_detection": ANY, + "monotonic_time": ANY, "name": "hci1 (00:00:00:00:00:02)", + "scanning": True, "source": "00:00:00:00:00:02", "start_time": ANY, "type": "HaScanner", @@ -222,7 +243,7 @@ async def test_diagnostics( async def test_diagnostics_macos( hass, hass_client, mock_bleak_scanner_start, mock_bluetooth_adapters, macos_adapter ): - """Test we can setup and unsetup bluetooth with multiple adapters.""" + """Test diagnostics for macos.""" # Normally we do not want to patch our classes, but since bleak will import # a different scanner based on the operating system, we need to patch here # because we cannot import the scanner class directly without it throwing an @@ -262,6 +283,7 @@ async def test_diagnostics_macos( inject_advertisement(hass, switchbot_device, switchbot_adv) diag = await get_diagnostics_for_config_entry(hass, hass_client, entry1) + assert diag == { "adapters": { "Core Bluetooth": { @@ -275,6 +297,11 @@ async def test_diagnostics_macos( } }, "manager": { + "slot_manager": { + "adapter_slots": {"Core Bluetooth": 5}, + "allocations_by_adapter": {"Core Bluetooth": []}, + "manager": False, + }, "adapters": { "Core Bluetooth": { "address": "00:00:00:00:00:00", @@ -367,13 +394,15 @@ async def test_diagnostics_macos( -127, [[]], ], + "details": None, "name": "x", "rssi": -60, - "details": None, } ], "last_detection": ANY, + "monotonic_time": ANY, "name": "Core Bluetooth", + "scanning": True, "source": "Core Bluetooth", "start_time": ANY, "type": "HaScanner", @@ -381,3 +410,226 @@ async def test_diagnostics_macos( ], }, } + + +async def test_diagnostics_remote_adapter( + hass, + hass_client, + mock_bleak_scanner_start, + mock_bluetooth_adapters, + enable_bluetooth, + one_adapter, +): + """Test diagnostics for remote adapter.""" + manager = _get_manager() + switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand") + switchbot_adv = generate_advertisement_data( + local_name="wohand", service_uuids=[], manufacturer_data={1: b"\x01"} + ) + + class FakeScanner(BaseHaRemoteScanner): + def inject_advertisement( + self, device: BLEDevice, advertisement_data: AdvertisementData + ) -> None: + """Inject an advertisement.""" + self._async_on_advertisement( + device.address, + advertisement_data.rssi, + device.name, + advertisement_data.service_uuids, + advertisement_data.service_data, + advertisement_data.manufacturer_data, + advertisement_data.tx_power, + {"scanner_specific_data": "test"}, + ) + + with patch( + "homeassistant.components.bluetooth.diagnostics.platform.system", + return_value="Linux", + ), patch( + "homeassistant.components.bluetooth.diagnostics.get_dbus_managed_objects", + return_value={}, + ): + + entry1 = MockConfigEntry( + domain=bluetooth.DOMAIN, data={}, unique_id="00:00:00:00:00:01" + ) + entry1.add_to_hass(hass) + + assert await hass.config_entries.async_setup(entry1.entry_id) + await hass.async_block_till_done() + new_info_callback = manager.scanner_adv_received + connector = ( + HaBluetoothConnector(MockBleakClient, "mock_bleak_client", lambda: False), + ) + scanner = FakeScanner( + hass, "esp32", "esp32", new_info_callback, connector, False + ) + unsetup = scanner.async_setup() + cancel = manager.async_register_scanner(scanner, True) + + scanner.inject_advertisement(switchbot_device, switchbot_adv) + inject_advertisement(hass, switchbot_device, switchbot_adv) + + diag = await get_diagnostics_for_config_entry(hass, hass_client, entry1) + + assert diag == { + "adapters": { + "hci0": { + "address": "00:00:00:00:00:01", + "hw_version": "usb:v1D6Bp0246d053F", + "manufacturer": "ACME", + "passive_scan": False, + "product": "Bluetooth Adapter 5.0", + "product_id": "aa01", + "sw_version": "homeassistant", + "vendor_id": "cc01", + } + }, + "dbus": {}, + "manager": { + "slot_manager": { + "adapter_slots": {"hci0": 5}, + "allocations_by_adapter": {"hci0": []}, + "manager": False, + }, + "adapters": { + "hci0": { + "address": "00:00:00:00:00:01", + "hw_version": "usb:v1D6Bp0246d053F", + "manufacturer": "ACME", + "passive_scan": False, + "product": "Bluetooth Adapter 5.0", + "product_id": "aa01", + "sw_version": "homeassistant", + "vendor_id": "cc01", + } + }, + "advertisement_tracker": { + "intervals": {}, + "sources": {"44:44:33:11:23:45": "esp32"}, + "timings": {"44:44:33:11:23:45": [ANY]}, + }, + "all_history": [ + { + "address": "44:44:33:11:23:45", + "advertisement": [ + "wohand", + {"1": {"__type": "", "repr": "b'\\x01'"}}, + {}, + [], + -127, + -127, + [], + ], + "connectable": False, + "device": { + "__type": "", + "repr": "BLEDevice(44:44:33:11:23:45, " "wohand)", + }, + "manufacturer_data": { + "1": {"__type": "", "repr": "b'\\x01'"} + }, + "name": "wohand", + "rssi": -127, + "service_data": {}, + "service_uuids": [], + "source": "esp32", + "time": ANY, + } + ], + "connectable_history": [ + { + "address": "44:44:33:11:23:45", + "advertisement": [ + "wohand", + {"1": {"__type": "", "repr": "b'\\x01'"}}, + {}, + [], + -127, + -127, + [[]], + ], + "connectable": True, + "device": { + "__type": "", + "repr": "BLEDevice(44:44:33:11:23:45, " "wohand)", + }, + "manufacturer_data": { + "1": {"__type": "", "repr": "b'\\x01'"} + }, + "name": "wohand", + "rssi": -127, + "service_data": {}, + "service_uuids": [], + "source": "local", + "time": ANY, + } + ], + "scanners": [ + { + "adapter": "hci0", + "discovered_devices_and_advertisement_data": [], + "last_detection": ANY, + "monotonic_time": ANY, + "name": "hci0 (00:00:00:00:00:01)", + "scanning": True, + "source": "00:00:00:00:00:01", + "start_time": ANY, + "type": "HaScanner", + }, + { + "adapter": "hci0", + "discovered_devices_and_advertisement_data": [], + "last_detection": ANY, + "monotonic_time": ANY, + "name": "hci0 (00:00:00:00:00:01)", + "scanning": True, + "source": "00:00:00:00:00:01", + "start_time": ANY, + "type": "HaScanner", + }, + { + "connectable": False, + "discovered_device_timestamps": {"44:44:33:11:23:45": ANY}, + "time_since_last_device_detection": {"44:44:33:11:23:45": ANY}, + "discovered_devices_and_advertisement_data": [ + { + "address": "44:44:33:11:23:45", + "advertisement_data": [ + "wohand", + { + "1": { + "__type": "", + "repr": "b'\\x01'", + } + }, + {}, + [], + -127, + -127, + [], + ], + "details": { + "scanner_specific_data": "test", + "source": "esp32", + }, + "name": "wohand", + "rssi": -127, + } + ], + "last_detection": ANY, + "monotonic_time": ANY, + "name": "esp32", + "scanning": True, + "source": "esp32", + "storage": None, + "type": "FakeScanner", + "start_time": ANY, + }, + ], + }, + } + + cancel() + unsetup() diff --git a/tests/components/bluetooth/test_init.py b/tests/components/bluetooth/test_init.py index 9af1c0f313b..332e211571b 100644 --- a/tests/components/bluetooth/test_init.py +++ b/tests/components/bluetooth/test_init.py @@ -11,7 +11,6 @@ import pytest from homeassistant.components import bluetooth from homeassistant.components.bluetooth import ( - BaseHaScanner, BluetoothChange, BluetoothScanningMode, BluetoothServiceInfo, @@ -45,6 +44,7 @@ from homeassistant.setup import async_setup_component from homeassistant.util import dt as dt_util from . import ( + FakeScanner, _get_manager, async_setup_with_default_adapter, generate_advertisement_data, @@ -2649,7 +2649,7 @@ async def test_getting_the_scanner_returns_the_wrapped_instance(hass, enable_blu async def test_scanner_count_connectable(hass, enable_bluetooth): """Test getting the connectable scanner count.""" - scanner = BaseHaScanner(hass, "any", "any") + scanner = FakeScanner(hass, "any", "any") cancel = bluetooth.async_register_scanner(hass, scanner, False) assert bluetooth.async_scanner_count(hass, connectable=True) == 1 cancel() @@ -2657,7 +2657,7 @@ async def test_scanner_count_connectable(hass, enable_bluetooth): async def test_scanner_count(hass, enable_bluetooth): """Test getting the connectable and non-connectable scanner count.""" - scanner = BaseHaScanner(hass, "any", "any") + scanner = FakeScanner(hass, "any", "any") cancel = bluetooth.async_register_scanner(hass, scanner, False) assert bluetooth.async_scanner_count(hass, connectable=False) == 2 cancel() diff --git a/tests/components/bluetooth/test_manager.py b/tests/components/bluetooth/test_manager.py index 3ec77a2ecd9..d0ae23a5226 100644 --- a/tests/components/bluetooth/test_manager.py +++ b/tests/components/bluetooth/test_manager.py @@ -11,7 +11,6 @@ import pytest from homeassistant.components import bluetooth from homeassistant.components.bluetooth import ( BaseHaRemoteScanner, - BaseHaScanner, BluetoothChange, BluetoothScanningMode, BluetoothServiceInfo, @@ -21,16 +20,19 @@ from homeassistant.components.bluetooth import ( async_get_advertisement_callback, async_scanner_count, async_track_unavailable, + storage, ) from homeassistant.components.bluetooth.const import UNAVAILABLE_TRACK_SECONDS from homeassistant.components.bluetooth.manager import ( FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS, ) from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.json import json_loads from homeassistant.setup import async_setup_component from homeassistant.util import dt as dt_util from . import ( + FakeScanner, MockBleakClient, _get_manager, generate_advertisement_data, @@ -39,13 +41,13 @@ from . import ( inject_advertisement_with_time_and_source_connectable, ) -from tests.common import async_fire_time_changed +from tests.common import async_fire_time_changed, load_fixture @pytest.fixture def register_hci0_scanner(hass: HomeAssistant) -> None: """Register an hci0 scanner.""" - hci0_scanner = BaseHaScanner(hass, "hci0", "hci0") + hci0_scanner = FakeScanner(hass, "hci0", "hci0") cancel = bluetooth.async_register_scanner(hass, hci0_scanner, True) yield cancel() @@ -54,7 +56,7 @@ def register_hci0_scanner(hass: HomeAssistant) -> None: @pytest.fixture def register_hci1_scanner(hass: HomeAssistant) -> None: """Register an hci1 scanner.""" - hci1_scanner = BaseHaScanner(hass, "hci1", "hci1") + hci1_scanner = FakeScanner(hass, "hci1", "hci1") cancel = bluetooth.async_register_scanner(hass, hci1_scanner, True) yield cancel() @@ -301,6 +303,76 @@ async def test_restore_history_from_dbus(hass, one_adapter): assert bluetooth.async_ble_device_from_address(hass, address) is ble_device +async def test_restore_history_from_dbus_and_remote_adapters( + hass, one_adapter, hass_storage +): + """Test we can restore history from dbus along with remote adapters.""" + address = "AA:BB:CC:CC:CC:FF" + + data = hass_storage[storage.REMOTE_SCANNER_STORAGE_KEY] = json_loads( + load_fixture("bluetooth.remote_scanners", bluetooth.DOMAIN) + ) + now = time.time() + timestamps = data["data"]["atom-bluetooth-proxy-ceaac4"][ + "discovered_device_timestamps" + ] + for address in timestamps: + timestamps[address] = now + + ble_device = BLEDevice(address, "name") + history = { + address: AdvertisementHistory( + ble_device, generate_advertisement_data(local_name="name"), "hci0" + ) + } + + with patch( + "bluetooth_adapters.systems.linux.LinuxAdapters.history", + history, + ): + assert await async_setup_component(hass, bluetooth.DOMAIN, {}) + await hass.async_block_till_done() + + assert bluetooth.async_ble_device_from_address(hass, address) is not None + assert ( + bluetooth.async_ble_device_from_address(hass, "EB:0B:36:35:6F:A4") is not None + ) + + +async def test_restore_history_from_dbus_and_corrupted_remote_adapters( + hass, one_adapter, hass_storage +): + """Test we can restore history from dbus when the remote adapters data is corrupted.""" + address = "AA:BB:CC:CC:CC:FF" + + data = hass_storage[storage.REMOTE_SCANNER_STORAGE_KEY] = json_loads( + load_fixture("bluetooth.remote_scanners.corrupt", bluetooth.DOMAIN) + ) + now = time.time() + timestamps = data["data"]["atom-bluetooth-proxy-ceaac4"][ + "discovered_device_timestamps" + ] + for address in timestamps: + timestamps[address] = now + + ble_device = BLEDevice(address, "name") + history = { + address: AdvertisementHistory( + ble_device, generate_advertisement_data(local_name="name"), "hci0" + ) + } + + with patch( + "bluetooth_adapters.systems.linux.LinuxAdapters.history", + history, + ): + assert await async_setup_component(hass, bluetooth.DOMAIN, {}) + await hass.async_block_till_done() + + assert bluetooth.async_ble_device_from_address(hass, address) is not None + assert bluetooth.async_ble_device_from_address(hass, "EB:0B:36:35:6F:A4") is None + + async def test_switching_adapters_based_on_rssi_connectable_to_non_connectable( hass, enable_bluetooth, register_hci0_scanner, register_hci1_scanner ): @@ -437,7 +509,7 @@ async def test_switching_adapters_when_one_goes_away( ): """Test switching adapters when one goes away.""" cancel_hci2 = bluetooth.async_register_scanner( - hass, BaseHaScanner(hass, "hci2", "hci2"), True + hass, FakeScanner(hass, "hci2", "hci2"), True ) address = "44:44:33:11:23:45" @@ -487,7 +559,7 @@ async def test_switching_adapters_when_one_stop_scanning( hass, enable_bluetooth, register_hci0_scanner ): """Test switching adapters when stops scanning.""" - hci2_scanner = BaseHaScanner(hass, "hci2", "hci2") + hci2_scanner = FakeScanner(hass, "hci2", "hci2") cancel_hci2 = bluetooth.async_register_scanner(hass, hci2_scanner, True) address = "44:44:33:11:23:45" diff --git a/tests/components/bluetooth/test_models.py b/tests/components/bluetooth/test_models.py index e200450f656..b653e1f5de3 100644 --- a/tests/components/bluetooth/test_models.py +++ b/tests/components/bluetooth/test_models.py @@ -9,7 +9,11 @@ from bleak.backends.device import BLEDevice from bleak.backends.scanner import AdvertisementData import pytest -from homeassistant.components.bluetooth import BaseHaScanner, HaBluetoothConnector +from homeassistant.components.bluetooth import ( + BaseHaRemoteScanner, + BaseHaScanner, + HaBluetoothConnector, +) from homeassistant.components.bluetooth.wrappers import ( HaBleakClientWrapper, HaBleakScannerWrapper, @@ -57,6 +61,67 @@ async def test_wrapped_bleak_client_set_disconnected_callback_before_connected( client.set_disconnected_callback(lambda client: None) +async def test_wrapped_bleak_client_local_adapter_only( + hass, enable_bluetooth, one_adapter +): + """Test wrapped bleak client with only a local adapter.""" + manager = _get_manager() + + switchbot_device = BLEDevice( + "44:44:33:11:23:45", + "wohand", + {"path": "/org/bluez/hci0/dev_44_44_33_11_23_45"}, + ) + switchbot_adv = generate_advertisement_data( + local_name="wohand", service_uuids=[], manufacturer_data={1: b"\x01"}, rssi=-100 + ) + + class FakeScanner(BaseHaScanner): + @property + def discovered_devices(self) -> list[BLEDevice]: + """Return a list of discovered devices.""" + return [] + + @property + def discovered_devices_and_advertisement_data( + self, + ) -> dict[str, tuple[BLEDevice, AdvertisementData]]: + """Return a list of discovered devices.""" + return { + switchbot_device.address: ( + switchbot_device, + switchbot_adv, + ) + } + + async def async_get_device_by_address(self, address: str) -> BLEDevice | None: + """Return a list of discovered devices.""" + if address == switchbot_device.address: + return switchbot_adv + return None + + scanner = FakeScanner( + hass, + "00:00:00:00:00:01", + "hci0", + ) + cancel = manager.async_register_scanner(scanner, True) + inject_advertisement_with_source( + hass, switchbot_device, switchbot_adv, "00:00:00:00:00:01" + ) + + client = HaBleakClientWrapper(switchbot_device) + with patch( + "bleak.backends.bluezdbus.client.BleakClientBlueZDBus.connect", + return_value=True, + ), patch("bleak.backends.bluezdbus.client.BleakClientBlueZDBus.is_connected", True): + assert await client.connect() is True + assert client.is_connected is True + client.set_disconnected_callback(lambda client: None) + await client.disconnect() + cancel() + + async def test_wrapped_bleak_client_set_disconnected_callback_after_connected( hass, enable_bluetooth, one_adapter ): @@ -67,9 +132,7 @@ async def test_wrapped_bleak_client_set_disconnected_callback_after_connected( "44:44:33:11:23:45", "wohand", { - "connector": HaBluetoothConnector( - MockBleakClient, "mock_bleak_client", lambda: True - ), + "source": "esp32_has_connection_slot", "path": "/org/bluez/hci0/dev_44_44_33_11_23_45", }, rssi=-40, @@ -89,17 +152,12 @@ async def test_wrapped_bleak_client_set_disconnected_callback_after_connected( local_name="wohand", service_uuids=[], manufacturer_data={1: b"\x01"}, rssi=-100 ) - inject_advertisement_with_source( - hass, switchbot_device, switchbot_adv, "00:00:00:00:00:01" - ) - inject_advertisement_with_source( - hass, - switchbot_proxy_device_has_connection_slot, - switchbot_proxy_device_adv_has_connection_slot, - "esp32_has_connection_slot", - ) + class FakeScanner(BaseHaRemoteScanner): + @property + def discovered_devices(self) -> list[BLEDevice]: + """Return a list of discovered devices.""" + return [] - class FakeScanner(BaseHaScanner): @property def discovered_devices_and_advertisement_data( self, @@ -118,31 +176,50 @@ async def test_wrapped_bleak_client_set_disconnected_callback_after_connected( return switchbot_proxy_device_has_connection_slot return None - scanner = FakeScanner(hass, "esp32", "esp32") + connector = HaBluetoothConnector( + MockBleakClient, "esp32_has_connection_slot", lambda: True + ) + scanner = FakeScanner( + hass, + "esp32_has_connection_slot", + "esp32_has_connection_slot", + lambda info: None, + connector, + True, + ) cancel = manager.async_register_scanner(scanner, True) - + inject_advertisement_with_source( + hass, switchbot_device, switchbot_adv, "00:00:00:00:00:01" + ) + inject_advertisement_with_source( + hass, + switchbot_proxy_device_has_connection_slot, + switchbot_proxy_device_adv_has_connection_slot, + "esp32_has_connection_slot", + ) client = HaBleakClientWrapper(switchbot_proxy_device_has_connection_slot) - with patch("bleak.backends.bluezdbus.client.BleakClientBlueZDBus.connect"): - await client.connect() - assert client.is_connected is True + with patch( + "bleak.backends.bluezdbus.client.BleakClientBlueZDBus.connect", + return_value=True, + ), patch("bleak.backends.bluezdbus.client.BleakClientBlueZDBus.is_connected", True): + assert await client.connect() is True + assert client.is_connected is True client.set_disconnected_callback(lambda client: None) await client.disconnect() cancel() -async def test_ble_device_with_proxy_client_out_of_connections( +async def test_ble_device_with_proxy_client_out_of_connections_no_scanners( hass, enable_bluetooth, one_adapter ): - """Test we switch to the next available proxy when one runs out of connections.""" + """Test we switch to the next available proxy when one runs out of connections with no scanners.""" manager = _get_manager() switchbot_proxy_device_no_connection_slot = BLEDevice( "44:44:33:11:23:45", "wohand", { - "connector": HaBluetoothConnector( - MockBleakClient, "mock_bleak_client", lambda: False - ), + "source": "esp32", "path": "/org/bluez/hci0/dev_44_44_33_11_23_45", }, rssi=-30, @@ -169,17 +246,17 @@ async def test_ble_device_with_proxy_client_out_of_connections( await client.disconnect() -async def test_ble_device_with_proxy_clear_cache(hass, enable_bluetooth, one_adapter): - """Test we can clear cache on the proxy.""" +async def test_ble_device_with_proxy_client_out_of_connections( + hass, enable_bluetooth, one_adapter +): + """Test handling all scanners are out of connection slots.""" manager = _get_manager() - switchbot_proxy_device_with_connection_slot = BLEDevice( + switchbot_proxy_device_no_connection_slot = BLEDevice( "44:44:33:11:23:45", "wohand", { - "connector": HaBluetoothConnector( - MockBleakClient, "mock_bleak_client", lambda: True - ), + "source": "esp32", "path": "/org/bluez/hci0/dev_44_44_33_11_23_45", }, rssi=-30, @@ -188,7 +265,75 @@ async def test_ble_device_with_proxy_clear_cache(hass, enable_bluetooth, one_ada local_name="wohand", service_uuids=[], manufacturer_data={1: b"\x01"} ) - class FakeScanner(BaseHaScanner): + class FakeScanner(BaseHaRemoteScanner): + @property + def discovered_devices(self) -> list[BLEDevice]: + """Return a list of discovered devices.""" + return [] + + @property + def discovered_devices_and_advertisement_data( + self, + ) -> dict[str, tuple[BLEDevice, AdvertisementData]]: + """Return a list of discovered devices.""" + return { + switchbot_proxy_device_no_connection_slot.address: ( + switchbot_proxy_device_no_connection_slot, + switchbot_adv, + ) + } + + async def async_get_device_by_address(self, address: str) -> BLEDevice | None: + """Return a list of discovered devices.""" + if address == switchbot_proxy_device_no_connection_slot.address: + return switchbot_adv + return None + + connector = HaBluetoothConnector(MockBleakClient, "esp32", lambda: False) + scanner = FakeScanner(hass, "esp32", "esp32", lambda info: None, connector, True) + cancel = manager.async_register_scanner(scanner, True) + inject_advertisement_with_source( + hass, switchbot_proxy_device_no_connection_slot, switchbot_adv, "esp32" + ) + + assert manager.async_discovered_devices(True) == [ + switchbot_proxy_device_no_connection_slot + ] + + client = HaBleakClientWrapper(switchbot_proxy_device_no_connection_slot) + with patch( + "bleak.backends.bluezdbus.client.BleakClientBlueZDBus.connect" + ), pytest.raises(BleakError): + await client.connect() + assert client.is_connected is False + client.set_disconnected_callback(lambda client: None) + await client.disconnect() + cancel() + + +async def test_ble_device_with_proxy_clear_cache(hass, enable_bluetooth, one_adapter): + """Test we can clear cache on the proxy.""" + manager = _get_manager() + + switchbot_proxy_device_with_connection_slot = BLEDevice( + "44:44:33:11:23:45", + "wohand", + { + "source": "esp32", + "path": "/org/bluez/hci0/dev_44_44_33_11_23_45", + }, + rssi=-30, + ) + switchbot_adv = generate_advertisement_data( + local_name="wohand", service_uuids=[], manufacturer_data={1: b"\x01"} + ) + + class FakeScanner(BaseHaRemoteScanner): + @property + def discovered_devices(self) -> list[BLEDevice]: + """Return a list of discovered devices.""" + return [] + @property def discovered_devices_and_advertisement_data( self, @@ -207,7 +352,8 @@ async def test_ble_device_with_proxy_clear_cache(hass, enable_bluetooth, one_ada return switchbot_adv return None - scanner = FakeScanner(hass, "esp32", "esp32") + connector = HaBluetoothConnector(MockBleakClient, "esp32", lambda: True) + scanner = FakeScanner(hass, "esp32", "esp32", lambda info: None, connector, True) cancel = manager.async_register_scanner(scanner, True) inject_advertisement_with_source( hass, switchbot_proxy_device_with_connection_slot, switchbot_adv, "esp32" @@ -235,9 +381,7 @@ async def test_ble_device_with_proxy_client_out_of_connections_uses_best_availab "44:44:33:11:23:45", "wohand", { - "connector": HaBluetoothConnector( - MockBleakClient, "mock_bleak_client", lambda: False - ), + "source": "esp32_no_connection_slot", "path": "/org/bluez/hci0/dev_44_44_33_11_23_45", }, ) @@ -251,9 +395,7 @@ async def test_ble_device_with_proxy_client_out_of_connections_uses_best_availab "44:44:33:11:23:45", "wohand", { - "connector": HaBluetoothConnector( - MockBleakClient, "mock_bleak_client", lambda: True - ), + "source": "esp32_has_connection_slot", "path": "/org/bluez/hci0/dev_44_44_33_11_23_45", }, rssi=-40, @@ -289,7 +431,12 @@ async def test_ble_device_with_proxy_client_out_of_connections_uses_best_availab "esp32_no_connection_slot", ) - class FakeScanner(BaseHaScanner): + class FakeScanner(BaseHaRemoteScanner): + @property + def discovered_devices(self) -> list[BLEDevice]: + """Return a list of discovered devices.""" + return [] + @property def discovered_devices_and_advertisement_data( self, @@ -308,7 +455,17 @@ async def test_ble_device_with_proxy_client_out_of_connections_uses_best_availab return switchbot_proxy_device_has_connection_slot return None - scanner = FakeScanner(hass, "esp32", "esp32") + connector = HaBluetoothConnector( + MockBleakClient, "esp32_has_connection_slot", lambda: True + ) + scanner = FakeScanner( + hass, + "esp32_has_connection_slot", + "esp32_has_connection_slot", + lambda info: None, + connector, + True, + ) cancel = manager.async_register_scanner(scanner, True) assert manager.async_discovered_devices(True) == [ switchbot_proxy_device_no_connection_slot @@ -333,9 +490,7 @@ async def test_ble_device_with_proxy_client_out_of_connections_uses_best_availab "44:44:33:11:23:45", "wohand_no_connection_slot", { - "connector": HaBluetoothConnector( - MockBleakClient, "mock_bleak_client", lambda: False - ), + "source": "esp32_no_connection_slot", "path": "/org/bluez/hci0/dev_44_44_33_11_23_45", }, rssi=-30, @@ -351,9 +506,7 @@ async def test_ble_device_with_proxy_client_out_of_connections_uses_best_availab "44:44:33:11:23:45", "wohand_has_connection_slot", { - "connector": HaBluetoothConnector( - MockBleakClient, "mock_bleak_client", lambda: True - ), + "source": "esp32_has_connection_slot", "path": "/org/bluez/hci0/dev_44_44_33_11_23_45", }, ) @@ -395,7 +548,12 @@ async def test_ble_device_with_proxy_client_out_of_connections_uses_best_availab "esp32_no_connection_slot", ) - class FakeScanner(BaseHaScanner): + class FakeScanner(BaseHaRemoteScanner): + @property + def discovered_devices(self) -> list[BLEDevice]: + """Return a list of discovered devices.""" + return [] + @property def discovered_devices_and_advertisement_data( self, @@ -414,7 +572,17 @@ async def test_ble_device_with_proxy_client_out_of_connections_uses_best_availab return switchbot_proxy_device_has_connection_slot return None - scanner = FakeScanner(hass, "esp32", "esp32") + connector = HaBluetoothConnector( + MockBleakClient, "esp32_has_connection_slot", lambda: True + ) + scanner = FakeScanner( + hass, + "esp32_has_connection_slot", + "esp32_has_connection_slot", + lambda info: None, + connector, + True, + ) cancel = manager.async_register_scanner(scanner, True) assert manager.async_discovered_devices(True) == [ switchbot_proxy_device_no_connection_slot diff --git a/tests/components/bluetooth/test_scanner.py b/tests/components/bluetooth/test_scanner.py index c3a08ac3361..4a15edf6938 100644 --- a/tests/components/bluetooth/test_scanner.py +++ b/tests/components/bluetooth/test_scanner.py @@ -204,7 +204,7 @@ async def test_recovery_from_dbus_restart(hass, one_adapter): # Ensure we don't restart the scanner if we don't need to with patch( - "homeassistant.components.bluetooth.scanner.MONOTONIC_TIME", + "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME", return_value=start_time_monotonic + 10, ): async_fire_time_changed(hass, dt_util.utcnow() + SCANNER_WATCHDOG_INTERVAL) @@ -214,7 +214,7 @@ async def test_recovery_from_dbus_restart(hass, one_adapter): # Fire a callback to reset the timer with patch( - "homeassistant.components.bluetooth.scanner.MONOTONIC_TIME", + "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME", return_value=start_time_monotonic, ): _callback( @@ -224,7 +224,7 @@ async def test_recovery_from_dbus_restart(hass, one_adapter): # Ensure we don't restart the scanner if we don't need to with patch( - "homeassistant.components.bluetooth.scanner.MONOTONIC_TIME", + "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME", return_value=start_time_monotonic + 20, ): async_fire_time_changed(hass, dt_util.utcnow() + SCANNER_WATCHDOG_INTERVAL) @@ -234,7 +234,7 @@ async def test_recovery_from_dbus_restart(hass, one_adapter): # We hit the timer, so we restart the scanner with patch( - "homeassistant.components.bluetooth.scanner.MONOTONIC_TIME", + "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME", return_value=start_time_monotonic + SCANNER_WATCHDOG_TIMEOUT + 20, ): async_fire_time_changed( @@ -279,7 +279,7 @@ async def test_adapter_recovery(hass, one_adapter): start_time_monotonic = time.monotonic() with patch( - "homeassistant.components.bluetooth.scanner.MONOTONIC_TIME", + "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME", return_value=start_time_monotonic, ), patch( "homeassistant.components.bluetooth.scanner.OriginalBleakScanner", @@ -294,7 +294,7 @@ async def test_adapter_recovery(hass, one_adapter): # Ensure we don't restart the scanner if we don't need to with patch( - "homeassistant.components.bluetooth.scanner.MONOTONIC_TIME", + "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME", return_value=start_time_monotonic + 10, ): async_fire_time_changed(hass, dt_util.utcnow() + SCANNER_WATCHDOG_INTERVAL) @@ -304,7 +304,7 @@ async def test_adapter_recovery(hass, one_adapter): # Ensure we don't restart the scanner if we don't need to with patch( - "homeassistant.components.bluetooth.scanner.MONOTONIC_TIME", + "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME", return_value=start_time_monotonic + 20, ): async_fire_time_changed(hass, dt_util.utcnow() + SCANNER_WATCHDOG_INTERVAL) @@ -314,7 +314,7 @@ async def test_adapter_recovery(hass, one_adapter): # We hit the timer with no detections, so we reset the adapter and restart the scanner with patch( - "homeassistant.components.bluetooth.scanner.MONOTONIC_TIME", + "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME", return_value=start_time_monotonic + SCANNER_WATCHDOG_TIMEOUT + SCANNER_WATCHDOG_INTERVAL.total_seconds(), @@ -366,7 +366,7 @@ async def test_adapter_scanner_fails_to_start_first_time(hass, one_adapter): start_time_monotonic = time.monotonic() with patch( - "homeassistant.components.bluetooth.scanner.MONOTONIC_TIME", + "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME", return_value=start_time_monotonic, ), patch( "homeassistant.components.bluetooth.scanner.OriginalBleakScanner", @@ -381,7 +381,7 @@ async def test_adapter_scanner_fails_to_start_first_time(hass, one_adapter): # Ensure we don't restart the scanner if we don't need to with patch( - "homeassistant.components.bluetooth.scanner.MONOTONIC_TIME", + "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME", return_value=start_time_monotonic + 10, ): async_fire_time_changed(hass, dt_util.utcnow() + SCANNER_WATCHDOG_INTERVAL) @@ -391,7 +391,7 @@ async def test_adapter_scanner_fails_to_start_first_time(hass, one_adapter): # Ensure we don't restart the scanner if we don't need to with patch( - "homeassistant.components.bluetooth.scanner.MONOTONIC_TIME", + "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME", return_value=start_time_monotonic + 20, ): async_fire_time_changed(hass, dt_util.utcnow() + SCANNER_WATCHDOG_INTERVAL) @@ -401,7 +401,7 @@ async def test_adapter_scanner_fails_to_start_first_time(hass, one_adapter): # We hit the timer with no detections, so we reset the adapter and restart the scanner with patch( - "homeassistant.components.bluetooth.scanner.MONOTONIC_TIME", + "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME", return_value=start_time_monotonic + SCANNER_WATCHDOG_TIMEOUT + SCANNER_WATCHDOG_INTERVAL.total_seconds(), @@ -417,7 +417,7 @@ async def test_adapter_scanner_fails_to_start_first_time(hass, one_adapter): # We hit the timer again the previous start call failed, make sure # we try again with patch( - "homeassistant.components.bluetooth.scanner.MONOTONIC_TIME", + "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME", return_value=start_time_monotonic + SCANNER_WATCHDOG_TIMEOUT + SCANNER_WATCHDOG_INTERVAL.total_seconds(), @@ -474,7 +474,7 @@ async def test_adapter_fails_to_start_and_takes_a_bit_to_init( "homeassistant.components.bluetooth.scanner.ADAPTER_INIT_TIME", 0, ), patch( - "homeassistant.components.bluetooth.scanner.MONOTONIC_TIME", + "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME", return_value=start_time_monotonic, ), patch( "homeassistant.components.bluetooth.scanner.OriginalBleakScanner", @@ -523,7 +523,7 @@ async def test_restart_takes_longer_than_watchdog_time(hass, one_adapter, caplog "homeassistant.components.bluetooth.scanner.ADAPTER_INIT_TIME", 0, ), patch( - "homeassistant.components.bluetooth.scanner.MONOTONIC_TIME", + "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME", return_value=start_time_monotonic, ), patch( "homeassistant.components.bluetooth.scanner.OriginalBleakScanner", @@ -538,7 +538,7 @@ async def test_restart_takes_longer_than_watchdog_time(hass, one_adapter, caplog # Now force a recover adapter 2x for _ in range(2): with patch( - "homeassistant.components.bluetooth.scanner.MONOTONIC_TIME", + "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME", return_value=start_time_monotonic + SCANNER_WATCHDOG_TIMEOUT + SCANNER_WATCHDOG_INTERVAL.total_seconds(), diff --git a/tests/components/bluetooth/test_wrappers.py b/tests/components/bluetooth/test_wrappers.py new file mode 100644 index 00000000000..dd051ebb1fe --- /dev/null +++ b/tests/components/bluetooth/test_wrappers.py @@ -0,0 +1,363 @@ +"""Tests for the Bluetooth integration.""" + + +from collections.abc import Callable +from typing import Union +from unittest.mock import patch + +import bleak +from bleak.backends.device import BLEDevice +from bleak.backends.scanner import AdvertisementData +import pytest + +from homeassistant.components.bluetooth import ( + BaseHaRemoteScanner, + BluetoothServiceInfoBleak, + HaBluetoothConnector, + async_get_advertisement_callback, +) +from homeassistant.components.bluetooth.usage import ( + install_multiple_bleak_catcher, + uninstall_multiple_bleak_catcher, +) +from homeassistant.core import HomeAssistant + +from . import _get_manager, generate_advertisement_data + + +class FakeScanner(BaseHaRemoteScanner): + """Fake scanner.""" + + def __init__( + self, + hass: HomeAssistant, + scanner_id: str, + name: str, + new_info_callback: Callable[[BluetoothServiceInfoBleak], None], + connector: None, + connectable: bool, + ) -> None: + """Initialize the scanner.""" + super().__init__( + hass, scanner_id, name, new_info_callback, connector, connectable + ) + self._details: dict[str, str | HaBluetoothConnector] = {} + + def __repr__(self) -> str: + """Return the representation.""" + return f"FakeScanner({self.name})" + + def inject_advertisement( + self, device: BLEDevice, advertisement_data: AdvertisementData + ) -> None: + """Inject an advertisement.""" + self._async_on_advertisement( + device.address, + advertisement_data.rssi, + device.name, + advertisement_data.service_uuids, + advertisement_data.service_data, + advertisement_data.manufacturer_data, + advertisement_data.tx_power, + device.details | {"scanner_specific_data": "test"}, + ) + + +class BaseFakeBleakClient: + """Base class for fake bleak clients.""" + + def __init__(self, address_or_ble_device: Union[BLEDevice, str], **kwargs): + """Initialize the fake bleak client.""" + self._device_path = "/dev/test" + self._device = address_or_ble_device + self._address = address_or_ble_device.address + + async def disconnect(self, *args, **kwargs): + """Disconnect.""" "" + + async def get_services(self, *args, **kwargs): + """Get services.""" + return [] + + +class FakeBleakClient(BaseFakeBleakClient): + """Fake bleak client.""" + + async def connect(self, *args, **kwargs): + """Connect.""" + return True + + +class FakeBleakClientFailsToConnect(BaseFakeBleakClient): + """Fake bleak client that fails to connect.""" + + async def connect(self, *args, **kwargs): + """Connect.""" + return False + + +class FakeBleakClientRaisesOnConnect(BaseFakeBleakClient): + """Fake bleak client that raises on connect.""" + + async def connect(self, *args, **kwargs): + """Connect.""" + raise Exception("Test exception") + + +def _generate_ble_device_and_adv_data( + interface: str, mac: str, rssi: int +) -> tuple[BLEDevice, AdvertisementData]: + """Generate a BLE device with adv data.""" + return ( + BLEDevice( + mac, + "any", + delegate="", + details={"path": f"/org/bluez/{interface}/dev_{mac}"}, + ), + generate_advertisement_data(rssi=rssi), + ) + + +@pytest.fixture(name="install_bleak_catcher") +def install_bleak_catcher_fixture(): + """Fixture that installs the bleak catcher.""" + install_multiple_bleak_catcher() + yield + uninstall_multiple_bleak_catcher() + + +@pytest.fixture(name="mock_platform_client") +def mock_platform_client_fixture(): + """Fixture that mocks the platform client.""" + with patch( + "homeassistant.components.bluetooth.wrappers.get_platform_client_backend_type", + return_value=FakeBleakClient, + ): + yield + + +@pytest.fixture(name="mock_platform_client_that_fails_to_connect") +def mock_platform_client_that_fails_to_connect_fixture(): + """Fixture that mocks the platform client that fails to connect.""" + with patch( + "homeassistant.components.bluetooth.wrappers.get_platform_client_backend_type", + return_value=FakeBleakClientFailsToConnect, + ): + yield + + +@pytest.fixture(name="mock_platform_client_that_raises_on_connect") +def mock_platform_client_that_raises_on_connect_fixture(): + """Fixture that mocks the platform client that fails to connect.""" + with patch( + "homeassistant.components.bluetooth.wrappers.get_platform_client_backend_type", + return_value=FakeBleakClientRaisesOnConnect, + ): + yield + + +def _generate_scanners_with_fake_devices(hass): + """Generate scanners with fake devices.""" + manager = _get_manager() + hci0_device_advs = {} + for i in range(10): + device, adv_data = _generate_ble_device_and_adv_data( + "hci0", f"00:00:00:00:00:{i:02x}", rssi=-60 + ) + hci0_device_advs[device.address] = (device, adv_data) + hci1_device_advs = {} + for i in range(10): + device, adv_data = _generate_ble_device_and_adv_data( + "hci1", f"00:00:00:00:00:{i:02x}", rssi=-80 + ) + hci1_device_advs[device.address] = (device, adv_data) + + new_info_callback = async_get_advertisement_callback(hass) + scanner_hci0 = FakeScanner( + hass, "00:00:00:00:00:01", "hci0", new_info_callback, None, True + ) + scanner_hci1 = FakeScanner( + hass, "00:00:00:00:00:02", "hci1", new_info_callback, None, True + ) + + for (device, adv_data) in hci0_device_advs.values(): + scanner_hci0.inject_advertisement(device, adv_data) + + for (device, adv_data) in hci1_device_advs.values(): + scanner_hci1.inject_advertisement(device, adv_data) + + cancel_hci0 = manager.async_register_scanner(scanner_hci0, True, 2) + cancel_hci1 = manager.async_register_scanner(scanner_hci1, True, 1) + + return hci0_device_advs, cancel_hci0, cancel_hci1 + + +async def test_test_switch_adapters_when_out_of_slots( + hass, two_adapters, enable_bluetooth, install_bleak_catcher, mock_platform_client +): + """Ensure we try another scanner when one runs out of slots.""" + manager = _get_manager() + hci0_device_advs, cancel_hci0, cancel_hci1 = _generate_scanners_with_fake_devices( + hass + ) + # hci0 has 2 slots, hci1 has 1 slot + with patch.object( + manager.slot_manager, "release_slot" + ) as release_slot_mock, patch.object( + manager.slot_manager, "allocate_slot", return_value=True + ) as allocate_slot_mock: + ble_device = hci0_device_advs["00:00:00:00:00:01"][0] + client = bleak.BleakClient(ble_device) + assert await client.connect() is True + assert allocate_slot_mock.call_count == 1 + assert release_slot_mock.call_count == 0 + + # All adapters are out of slots + with patch.object( + manager.slot_manager, "release_slot" + ) as release_slot_mock, patch.object( + manager.slot_manager, "allocate_slot", return_value=False + ) as allocate_slot_mock: + ble_device = hci0_device_advs["00:00:00:00:00:02"][0] + client = bleak.BleakClient(ble_device) + with pytest.raises(bleak.exc.BleakError): + await client.connect() + assert allocate_slot_mock.call_count == 2 + assert release_slot_mock.call_count == 0 + + # When hci0 runs out of slots, we should try hci1 + def _allocate_slot_mock(ble_device: BLEDevice): + if "hci1" in ble_device.details["path"]: + return True + return False + + with patch.object( + manager.slot_manager, "release_slot" + ) as release_slot_mock, patch.object( + manager.slot_manager, "allocate_slot", _allocate_slot_mock + ) as allocate_slot_mock: + ble_device = hci0_device_advs["00:00:00:00:00:03"][0] + client = bleak.BleakClient(ble_device) + await client.connect() is True + assert release_slot_mock.call_count == 0 + + cancel_hci0() + cancel_hci1() + + +async def test_release_slot_on_connect_failure( + hass, + two_adapters, + enable_bluetooth, + install_bleak_catcher, + mock_platform_client_that_fails_to_connect, +): + """Ensure the slot gets released on connection failure.""" + manager = _get_manager() + hci0_device_advs, cancel_hci0, cancel_hci1 = _generate_scanners_with_fake_devices( + hass + ) + # hci0 has 2 slots, hci1 has 1 slot + with patch.object( + manager.slot_manager, "release_slot" + ) as release_slot_mock, patch.object( + manager.slot_manager, "allocate_slot", return_value=True + ) as allocate_slot_mock: + ble_device = hci0_device_advs["00:00:00:00:00:01"][0] + client = bleak.BleakClient(ble_device) + assert await client.connect() is False + assert allocate_slot_mock.call_count == 1 + assert release_slot_mock.call_count == 1 + + cancel_hci0() + cancel_hci1() + + +async def test_release_slot_on_connect_exception( + hass, + two_adapters, + enable_bluetooth, + install_bleak_catcher, + mock_platform_client_that_raises_on_connect, +): + """Ensure the slot gets released on connection exception.""" + manager = _get_manager() + hci0_device_advs, cancel_hci0, cancel_hci1 = _generate_scanners_with_fake_devices( + hass + ) + # hci0 has 2 slots, hci1 has 1 slot + with patch.object( + manager.slot_manager, "release_slot" + ) as release_slot_mock, patch.object( + manager.slot_manager, "allocate_slot", return_value=True + ) as allocate_slot_mock: + ble_device = hci0_device_advs["00:00:00:00:00:01"][0] + client = bleak.BleakClient(ble_device) + with pytest.raises(Exception): + assert await client.connect() is False + assert allocate_slot_mock.call_count == 1 + assert release_slot_mock.call_count == 1 + + cancel_hci0() + cancel_hci1() + + +async def test_we_switch_adapters_on_failure( + hass, + two_adapters, + enable_bluetooth, + install_bleak_catcher, +): + """Ensure we try the next best adapter after a failure.""" + hci0_device_advs, cancel_hci0, cancel_hci1 = _generate_scanners_with_fake_devices( + hass + ) + ble_device = hci0_device_advs["00:00:00:00:00:01"][0] + client = bleak.BleakClient(ble_device) + + class FakeBleakClientFailsHCI0Only(BaseFakeBleakClient): + """Fake bleak client that fails to connect.""" + + async def connect(self, *args, **kwargs): + """Connect.""" + if "/hci0/" in self._device.details["path"]: + return False + return True + + with patch( + "homeassistant.components.bluetooth.wrappers.get_platform_client_backend_type", + return_value=FakeBleakClientFailsHCI0Only, + ): + assert await client.connect() is False + + with patch( + "homeassistant.components.bluetooth.wrappers.get_platform_client_backend_type", + return_value=FakeBleakClientFailsHCI0Only, + ): + assert await client.connect() is False + + # After two tries we should switch to hci1 + with patch( + "homeassistant.components.bluetooth.wrappers.get_platform_client_backend_type", + return_value=FakeBleakClientFailsHCI0Only, + ): + assert await client.connect() is True + + # ..and we remember that hci1 works as long as the client doesn't change + with patch( + "homeassistant.components.bluetooth.wrappers.get_platform_client_backend_type", + return_value=FakeBleakClientFailsHCI0Only, + ): + assert await client.connect() is True + + # If we replace the client, we should try hci0 again + client = bleak.BleakClient(ble_device) + + with patch( + "homeassistant.components.bluetooth.wrappers.get_platform_client_backend_type", + return_value=FakeBleakClientFailsHCI0Only, + ): + assert await client.connect() is False + cancel_hci0() + cancel_hci1() diff --git a/tests/components/braviatv/test_config_flow.py b/tests/components/braviatv/test_config_flow.py index 18576207a30..40b1b7499a9 100644 --- a/tests/components/braviatv/test_config_flow.py +++ b/tests/components/braviatv/test_config_flow.py @@ -124,7 +124,14 @@ async def test_ssdp_discovery(hass): assert result["step_id"] == "authorize" result = await hass.config_entries.flow.async_configure( - result["flow_id"], user_input={CONF_PIN: "1234", CONF_USE_PSK: False} + result["flow_id"], user_input={CONF_USE_PSK: False} + ) + + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "pin" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={CONF_PIN: "1234"} ) assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY @@ -185,68 +192,76 @@ async def test_user_invalid_host(hass): assert result["errors"] == {CONF_HOST: "invalid_host"} -async def test_authorize_invalid_auth(hass): - """Test that authorization errors shown on the authorization step.""" +@pytest.mark.parametrize( + "side_effect, error_message", + [ + (BraviaTVAuthError, "invalid_auth"), + (BraviaTVNotSupported, "unsupported_model"), + (BraviaTVConnectionError, "cannot_connect"), + ], +) +async def test_pin_form_error(hass, side_effect, error_message): + """Test that PIN form errors are correct.""" with patch( "pybravia.BraviaTV.connect", - side_effect=BraviaTVAuthError, + side_effect=side_effect, ), patch("pybravia.BraviaTV.pair"): result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER}, data={CONF_HOST: "bravia-host"} ) + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={CONF_USE_PSK: False} + ) result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={CONF_PIN: "1234"} ) - assert result["errors"] == {"base": "invalid_auth"} + assert result["errors"] == {"base": error_message} -async def test_authorize_cannot_connect(hass): - """Test that errors are shown when cannot connect to host at the authorize step.""" +@pytest.mark.parametrize( + "side_effect, error_message", + [ + (BraviaTVAuthError, "invalid_auth"), + (BraviaTVNotSupported, "unsupported_model"), + (BraviaTVConnectionError, "cannot_connect"), + ], +) +async def test_psk_form_error(hass, side_effect, error_message): + """Test that PSK form errors are correct.""" with patch( "pybravia.BraviaTV.connect", - side_effect=BraviaTVConnectionError, - ), patch("pybravia.BraviaTV.pair"): + side_effect=side_effect, + ): result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER}, data={CONF_HOST: "bravia-host"} ) result = await hass.config_entries.flow.async_configure( - result["flow_id"], user_input={CONF_PIN: "1234"} + result["flow_id"], user_input={CONF_USE_PSK: True} ) - - assert result["errors"] == {"base": "cannot_connect"} - - -async def test_authorize_model_unsupported(hass): - """Test that errors are shown when the TV is not supported at the authorize step.""" - with patch( - "pybravia.BraviaTV.connect", - side_effect=BraviaTVNotSupported, - ), patch("pybravia.BraviaTV.pair"): - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_USER}, data={CONF_HOST: "10.10.10.12"} - ) - result = await hass.config_entries.flow.async_configure( - result["flow_id"], user_input={CONF_PIN: "1234"} + result["flow_id"], user_input={CONF_PIN: "mypsk"} ) - assert result["errors"] == {"base": "unsupported_model"} + assert result["errors"] == {"base": error_message} -async def test_authorize_no_ip_control(hass): - """Test that errors are shown when IP Control is disabled on the TV.""" +async def test_no_ip_control(hass): + """Test that error are shown when IP Control is disabled on the TV.""" with patch("pybravia.BraviaTV.pair", side_effect=BraviaTVError): result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER}, data={CONF_HOST: "bravia-host"} ) + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={CONF_USE_PSK: False} + ) assert result["type"] == data_entry_flow.FlowResultType.ABORT assert result["reason"] == "no_ip_control" async def test_duplicate_error(hass): - """Test that errors are shown when duplicates are added.""" + """Test that error are shown when duplicates are added.""" config_entry = MockConfigEntry( domain=DOMAIN, unique_id="very_unique_string", @@ -268,6 +283,9 @@ async def test_duplicate_error(hass): result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER}, data={CONF_HOST: "bravia-host"} ) + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={CONF_USE_PSK: False} + ) result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={CONF_PIN: "1234"} ) @@ -277,7 +295,7 @@ async def test_duplicate_error(hass): async def test_create_entry(hass): - """Test that the user step works.""" + """Test that entry is added correctly with PIN auth.""" uuid = await instance_id.async_get(hass) with patch("pybravia.BraviaTV.connect"), patch("pybravia.BraviaTV.pair"), patch( @@ -296,7 +314,14 @@ async def test_create_entry(hass): assert result["step_id"] == "authorize" result = await hass.config_entries.flow.async_configure( - result["flow_id"], user_input={CONF_PIN: "1234", CONF_USE_PSK: False} + result["flow_id"], user_input={CONF_USE_PSK: False} + ) + + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "pin" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={CONF_PIN: "1234"} ) assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY @@ -312,47 +337,9 @@ async def test_create_entry(hass): } -async def test_create_entry_with_ipv6_address(hass): - """Test that the user step works with device IPv6 address.""" - uuid = await instance_id.async_get(hass) - - with patch("pybravia.BraviaTV.connect"), patch("pybravia.BraviaTV.pair"), patch( - "pybravia.BraviaTV.set_wol_mode" - ), patch( - "pybravia.BraviaTV.get_system_info", - return_value=BRAVIA_SYSTEM_INFO, - ), patch( - "homeassistant.components.braviatv.async_setup_entry", return_value=True - ): - result = await hass.config_entries.flow.async_init( - DOMAIN, - context={"source": SOURCE_USER}, - data={CONF_HOST: "2001:db8::1428:57ab"}, - ) - - assert result["type"] == data_entry_flow.FlowResultType.FORM - assert result["step_id"] == "authorize" - - result = await hass.config_entries.flow.async_configure( - result["flow_id"], user_input={CONF_PIN: "1234", CONF_USE_PSK: False} - ) - - assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY - assert result["result"].unique_id == "very_unique_string" - assert result["title"] == "TV-Model" - assert result["data"] == { - CONF_HOST: "2001:db8::1428:57ab", - CONF_PIN: "1234", - CONF_USE_PSK: False, - CONF_MAC: "AA:BB:CC:DD:EE:FF", - CONF_CLIENT_ID: uuid, - CONF_NICKNAME: f"{NICKNAME_PREFIX} {uuid[:6]}", - } - - async def test_create_entry_psk(hass): - """Test that the user step works with PSK auth.""" - with patch("pybravia.BraviaTV.connect"), patch("pybravia.BraviaTV.pair"), patch( + """Test that entry is added correctly with PSK auth.""" + with patch("pybravia.BraviaTV.connect"), patch( "pybravia.BraviaTV.set_wol_mode" ), patch( "pybravia.BraviaTV.get_system_info", @@ -368,7 +355,14 @@ async def test_create_entry_psk(hass): assert result["step_id"] == "authorize" result = await hass.config_entries.flow.async_configure( - result["flow_id"], user_input={CONF_PIN: "mypsk", CONF_USE_PSK: True} + result["flow_id"], user_input={CONF_USE_PSK: True} + ) + + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "psk" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={CONF_PIN: "mypsk"} ) assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY @@ -474,11 +468,14 @@ async def test_options_flow_error(hass: HomeAssistant) -> None: @pytest.mark.parametrize( - "user_input", - [{CONF_PIN: "mypsk", CONF_USE_PSK: True}, {CONF_PIN: "1234", CONF_USE_PSK: False}], + "use_psk, new_pin", + [ + (True, "7777"), + (False, "newpsk"), + ], ) -async def test_reauth_successful(hass, user_input): - """Test starting a reauthentication flow.""" +async def test_reauth_successful(hass, use_psk, new_pin): + """Test that the reauthorization is successful.""" config_entry = MockConfigEntry( domain=DOMAIN, unique_id="very_unique_string", @@ -508,73 +505,15 @@ async def test_reauth_successful(hass, user_input): ) assert result["type"] == data_entry_flow.FlowResultType.FORM - assert result["step_id"] == "reauth_confirm" + assert result["step_id"] == "authorize" result = await hass.config_entries.flow.async_configure( - result["flow_id"], - user_input=user_input, + result["flow_id"], user_input={CONF_USE_PSK: use_psk} + ) + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={CONF_PIN: new_pin} ) assert result["type"] == data_entry_flow.FlowResultType.ABORT assert result["reason"] == "reauth_successful" - - -async def test_reauth_unsuccessful(hass): - """Test reauthentication flow failed.""" - config_entry = MockConfigEntry( - domain=DOMAIN, - unique_id="very_unique_string", - data={ - CONF_HOST: "bravia-host", - CONF_PIN: "1234", - CONF_MAC: "AA:BB:CC:DD:EE:FF", - }, - title="TV-Model", - ) - config_entry.add_to_hass(hass) - - with patch( - "pybravia.BraviaTV.connect", - side_effect=BraviaTVAuthError, - ), patch("pybravia.BraviaTV.pair"): - result = await hass.config_entries.flow.async_init( - DOMAIN, - context={"source": SOURCE_REAUTH, "entry_id": config_entry.entry_id}, - data=config_entry.data, - ) - - assert result["type"] == data_entry_flow.FlowResultType.FORM - assert result["step_id"] == "reauth_confirm" - - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - user_input={CONF_PIN: "mypsk", CONF_USE_PSK: True}, - ) - - assert result["type"] == data_entry_flow.FlowResultType.ABORT - assert result["reason"] == "reauth_unsuccessful" - - -async def test_reauth_unsuccessful_during_pairing(hass): - """Test reauthentication flow failed because of pairing error.""" - config_entry = MockConfigEntry( - domain=DOMAIN, - unique_id="very_unique_string", - data={ - CONF_HOST: "bravia-host", - CONF_PIN: "1234", - CONF_MAC: "AA:BB:CC:DD:EE:FF", - }, - title="TV-Model", - ) - config_entry.add_to_hass(hass) - - with patch("pybravia.BraviaTV.pair", side_effect=BraviaTVError): - result = await hass.config_entries.flow.async_init( - DOMAIN, - context={"source": SOURCE_REAUTH, "entry_id": config_entry.entry_id}, - data=config_entry.data, - ) - - assert result["type"] == data_entry_flow.FlowResultType.ABORT - assert result["reason"] == "reauth_unsuccessful" + assert config_entry.data[CONF_PIN] == new_pin diff --git a/tests/components/bthome/test_sensor.py b/tests/components/bthome/test_sensor.py index dce82ab454f..5744642e3dd 100644 --- a/tests/components/bthome/test_sensor.py +++ b/tests/components/bthome/test_sensor.py @@ -790,6 +790,57 @@ async def test_v1_sensors( }, ], ), + ( + "A4:C1:38:8D:18:B2", + make_bthome_v2_adv( + "A4:C1:38:8D:18:B2", + b"\x40\x47\x87\x56", + ), + None, + [ + { + "sensor_entity": "sensor.test_device_18b2_volume", + "friendly_name": "Test Device 18B2 Volume", + "unit_of_measurement": "L", + "state_class": "measurement", + "expected_state": "2215.1", + }, + ], + ), + ( + "A4:C1:38:8D:18:B2", + make_bthome_v2_adv( + "A4:C1:38:8D:18:B2", + b"\x40\x48\xDC\x87", + ), + None, + [ + { + "sensor_entity": "sensor.test_device_18b2_volume", + "friendly_name": "Test Device 18B2 Volume", + "unit_of_measurement": "mL", + "state_class": "measurement", + "expected_state": "34780", + }, + ], + ), + ( + "A4:C1:38:8D:18:B2", + make_bthome_v2_adv( + "A4:C1:38:8D:18:B2", + b"\x40\x49\xDC\x87", + ), + None, + [ + { + "sensor_entity": "sensor.test_device_18b2_volume_flow_rate", + "friendly_name": "Test Device 18B2 Volume Flow Rate", + "unit_of_measurement": "m³/h", + "state_class": "measurement", + "expected_state": "34.78", + }, + ], + ), ( "A4:C1:38:8D:18:B2", make_bthome_v2_adv( diff --git a/tests/components/caldav/test_calendar.py b/tests/components/caldav/test_calendar.py index b936a02db87..c7031fa9c04 100644 --- a/tests/components/caldav/test_calendar.py +++ b/tests/components/caldav/test_calendar.py @@ -214,6 +214,47 @@ DESCRIPTION:The bell tolls for thee RRULE:FREQ=HOURLY;INTERVAL=1;COUNT=12 END:VEVENT END:VCALENDAR +""", + """BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Global Corp.//CalDAV Client//EN +BEGIN:VEVENT +UID:14 +DTSTAMP:20151125T000000Z +DTSTART:20151127T000000Z +DTEND:20151127T003000Z +RRULE:FREQ=HOURLY;INTERVAL=1;COUNT=12 +END:VEVENT +END:VCALENDAR +""", + """BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Global Corp.//CalDAV Client//EN +BEGIN:VTIMEZONE +TZID:Europe/London +BEGIN:STANDARD +DTSTART:19961027T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU +TZNAME:GMT +TZOFFSETFROM:+0100 +TZOFFSETTO:+0000 +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19810329T010000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:BST +TZOFFSETFROM:+0000 +TZOFFSETTO:+0100 +END:DAYLIGHT +END:VTIMEZONE +BEGIN:VEVENT +UID:15 +DTSTAMP:20221125T000000Z +DTSTART;TZID=Europe/London:20221127T000000 +DTEND;TZID=Europe/London:20221127T003000 +SUMMARY:Event with a provided Timezone +END:VEVENT +END:VCALENDAR """, ] @@ -917,7 +958,7 @@ async def test_get_events(hass, calendar, get_api_events): await hass.async_block_till_done() events = await get_api_events("calendar.private") - assert len(events) == 14 + assert len(events) == 16 assert calendar.call diff --git a/tests/components/calendar/test_init.py b/tests/components/calendar/test_init.py index 02c7f01b42d..38c17b15b04 100644 --- a/tests/components/calendar/test_init.py +++ b/tests/components/calendar/test_init.py @@ -3,6 +3,8 @@ from datetime import timedelta from http import HTTPStatus from unittest.mock import patch +import pytest + from homeassistant.bootstrap import async_setup_component from homeassistant.exceptions import HomeAssistantError import homeassistant.util.dt as dt_util @@ -67,3 +69,91 @@ async def test_calendars_http_api(hass, hass_client): {"entity_id": "calendar.calendar_1", "name": "Calendar 1"}, {"entity_id": "calendar.calendar_2", "name": "Calendar 2"}, ] + + +@pytest.mark.parametrize( + "payload,code", + [ + ( + { + "type": "calendar/event/create", + "entity_id": "calendar.calendar_1", + "event": { + "summary": "Bastille Day Party", + "dtstart": "1997-07-14T17:00:00+00:00", + "dtend": "1997-07-15T04:00:00+00:00", + }, + }, + "not_supported", + ), + ( + { + "type": "calendar/event/create", + "entity_id": "calendar.calendar_99", + "event": { + "summary": "Bastille Day Party", + "dtstart": "1997-07-14T17:00:00+00:00", + "dtend": "1997-07-15T04:00:00+00:00", + }, + }, + "not_found", + ), + ( + { + "type": "calendar/event/delete", + "entity_id": "calendar.calendar_1", + "uid": "some-uid", + }, + "not_supported", + ), + ( + { + "type": "calendar/event/delete", + "entity_id": "calendar.calendar_99", + "uid": "some-uid", + }, + "not_found", + ), + ( + { + "type": "calendar/event/update", + "entity_id": "calendar.calendar_1", + "uid": "some-uid", + "event": { + "summary": "Bastille Day Party", + "dtstart": "1997-07-14T17:00:00+00:00", + "dtend": "1997-07-15T04:00:00+00:00", + }, + }, + "not_supported", + ), + ( + { + "type": "calendar/event/update", + "entity_id": "calendar.calendar_99", + "uid": "some-uid", + "event": { + "summary": "Bastille Day Party", + "dtstart": "1997-07-14T17:00:00+00:00", + "dtend": "1997-07-15T04:00:00+00:00", + }, + }, + "not_found", + ), + ], +) +async def test_unsupported_websocket(hass, hass_ws_client, payload, code): + """Test unsupported websocket command.""" + await async_setup_component(hass, "calendar", {"calendar": {"platform": "demo"}}) + await hass.async_block_till_done() + client = await hass_ws_client(hass) + await client.send_json( + { + "id": 1, + **payload, + } + ) + resp = await client.receive_json() + assert resp.get("id") == 1 + assert resp.get("error") + assert resp["error"].get("code") == code diff --git a/tests/components/calendar/test_trigger.py b/tests/components/calendar/test_trigger.py index 24b4b06b493..ac2547c81f7 100644 --- a/tests/components/calendar/test_trigger.py +++ b/tests/components/calendar/test_trigger.py @@ -462,7 +462,7 @@ async def test_invalid_calendar_id(hass, caplog): }, ) await hass.async_block_till_done() - assert "Invalid config for [automation]" in caplog.text + assert "Entity ID invalid-calendar-id is an invalid entity ID" in caplog.text async def test_legacy_entity_type(hass, caplog): diff --git a/tests/components/climate/test_device_trigger.py b/tests/components/climate/test_device_trigger.py index 25e1a9d920d..00099538a6f 100644 --- a/tests/components/climate/test_device_trigger.py +++ b/tests/components/climate/test_device_trigger.py @@ -270,7 +270,23 @@ async def test_get_trigger_capabilities_hvac_mode(hass): assert voluptuous_serialize.convert( capabilities["extra_fields"], custom_serializer=cv.custom_serializer - ) == [{"name": "for", "optional": True, "type": "positive_time_period_dict"}] + ) == [ + { + "name": "to", + "options": [ + ("off", "off"), + ("heat", "heat"), + ("cool", "cool"), + ("heat_cool", "heat_cool"), + ("auto", "auto"), + ("dry", "dry"), + ("fan_only", "fan_only"), + ], + "required": True, + "type": "select", + }, + {"name": "for", "optional": True, "type": "positive_time_period_dict"}, + ] @pytest.mark.parametrize( diff --git a/tests/components/cloud/test_repairs.py b/tests/components/cloud/test_repairs.py index a7f8b2332d7..7df1c1cdc22 100644 --- a/tests/components/cloud/test_repairs.py +++ b/tests/components/cloud/test_repairs.py @@ -152,7 +152,6 @@ async def test_legacy_subscription_repair_flow( "type": "create_entry", "flow_id": flow_id, "handler": DOMAIN, - "title": "", "description": None, "description_placeholders": None, } diff --git a/tests/components/cloudflare/test_init.py b/tests/components/cloudflare/test_init.py index 6e7f450d711..c476b3ef376 100644 --- a/tests/components/cloudflare/test_init.py +++ b/tests/components/cloudflare/test_init.py @@ -4,6 +4,7 @@ from unittest.mock import patch from pycfdns.exceptions import ( CloudflareAuthenticationException, CloudflareConnectionException, + CloudflareZoneException, ) import pytest @@ -31,14 +32,21 @@ async def test_unload_entry(hass, cfupdate): assert not hass.data.get(DOMAIN) -async def test_async_setup_raises_entry_not_ready(hass, cfupdate): +@pytest.mark.parametrize( + "side_effect", + ( + CloudflareConnectionException(), + CloudflareZoneException(), + ), +) +async def test_async_setup_raises_entry_not_ready(hass, cfupdate, side_effect): """Test that it throws ConfigEntryNotReady when exception occurs during setup.""" instance = cfupdate.return_value entry = MockConfigEntry(domain=DOMAIN, data=ENTRY_CONFIG) entry.add_to_hass(hass) - instance.get_zone_id.side_effect = CloudflareConnectionException() + instance.get_zone_id.side_effect = side_effect await hass.config_entries.async_setup(entry.entry_id) assert entry.state is ConfigEntryState.SETUP_RETRY diff --git a/tests/components/config/test_area_registry.py b/tests/components/config/test_area_registry.py index 497a395ed30..de3cb78d349 100644 --- a/tests/components/config/test_area_registry.py +++ b/tests/components/config/test_area_registry.py @@ -1,9 +1,10 @@ """Test area_registry API.""" import pytest +from pytest_unordered import unordered from homeassistant.components.config import area_registry -from tests.common import mock_area_registry +from tests.common import ANY, mock_area_registry @pytest.fixture @@ -21,31 +22,68 @@ def registry(hass): async def test_list_areas(hass, client, registry): """Test list entries.""" - registry.async_create("mock 1") - registry.async_create("mock 2", "/image/example.png") + area1 = registry.async_create("mock 1") + area2 = registry.async_create( + "mock 2", aliases={"alias_1", "alias_2"}, picture="/image/example.png" + ) await client.send_json({"id": 1, "type": "config/area_registry/list"}) msg = await client.receive_json() - - assert len(msg["result"]) == len(registry.areas) - assert msg["result"][0]["name"] == "mock 1" - assert msg["result"][0]["picture"] is None - assert msg["result"][1]["name"] == "mock 2" - assert msg["result"][1]["picture"] == "/image/example.png" + assert msg["result"] == [ + { + "aliases": [], + "area_id": area1.id, + "name": "mock 1", + "picture": None, + }, + { + "aliases": unordered(["alias_1", "alias_2"]), + "area_id": area2.id, + "name": "mock 2", + "picture": "/image/example.png", + }, + ] async def test_create_area(hass, client, registry): """Test create entry.""" + # Create area with only mandatory parameters await client.send_json( {"id": 1, "name": "mock", "type": "config/area_registry/create"} ) msg = await client.receive_json() - assert "mock" in msg["result"]["name"] + assert msg["result"] == { + "aliases": [], + "area_id": ANY, + "name": "mock", + "picture": None, + } assert len(registry.areas) == 1 + # Create area with all parameters + await client.send_json( + { + "id": 2, + "aliases": ["alias_1", "alias_2"], + "name": "mock 2", + "picture": "/image/example.png", + "type": "config/area_registry/create", + } + ) + + msg = await client.receive_json() + + assert msg["result"] == { + "aliases": unordered(["alias_1", "alias_2"]), + "area_id": ANY, + "name": "mock 2", + "picture": "/image/example.png", + } + assert len(registry.areas) == 2 + async def test_create_area_with_name_already_in_use(hass, client, registry): """Test create entry that should fail.""" @@ -100,6 +138,7 @@ async def test_update_area(hass, client, registry): await client.send_json( { "id": 1, + "aliases": ["alias_1", "alias_2"], "area_id": area.id, "name": "mock 2", "picture": "/image/example.png", @@ -109,14 +148,18 @@ async def test_update_area(hass, client, registry): msg = await client.receive_json() - assert msg["result"]["area_id"] == area.id - assert msg["result"]["name"] == "mock 2" - assert msg["result"]["picture"] == "/image/example.png" + assert msg["result"] == { + "aliases": unordered(["alias_1", "alias_2"]), + "area_id": area.id, + "name": "mock 2", + "picture": "/image/example.png", + } assert len(registry.areas) == 1 await client.send_json( { "id": 2, + "aliases": ["alias_1", "alias_1"], "area_id": area.id, "picture": None, "type": "config/area_registry/update", @@ -125,9 +168,12 @@ async def test_update_area(hass, client, registry): msg = await client.receive_json() - assert msg["result"]["area_id"] == area.id - assert msg["result"]["name"] == "mock 2" - assert msg["result"]["picture"] is None + assert msg["result"] == { + "aliases": ["alias_1"], + "area_id": area.id, + "name": "mock 2", + "picture": None, + } assert len(registry.areas) == 1 diff --git a/tests/components/config/test_automation.py b/tests/components/config/test_automation.py index 0a5a79c7d15..ff5c55a2a67 100644 --- a/tests/components/config/test_automation.py +++ b/tests/components/config/test_automation.py @@ -76,6 +76,36 @@ async def test_update_automation_config( assert new_data[1] == {"id": "moon", "trigger": [], "condition": [], "action": []} +@pytest.mark.parametrize("automation_config", ({},)) +async def test_update_automation_config_with_error( + hass: HomeAssistant, hass_client, hass_config_store, setup_automation, caplog +): + """Test updating automation config with errors.""" + with patch.object(config, "SECTIONS", ["automation"]): + await async_setup_component(hass, "config", {}) + + assert sorted(hass.states.async_entity_ids("automation")) == [] + + client = await hass_client() + + orig_data = [{"id": "sun"}, {"id": "moon"}] + hass_config_store["automations.yaml"] = orig_data + + resp = await client.post( + "/api/config/automation/config/moon", + data=json.dumps({"action": []}), + ) + await hass.async_block_till_done() + assert sorted(hass.states.async_entity_ids("automation")) == [] + + assert resp.status != HTTPStatus.OK + result = await resp.json() + validation_error = "required key not provided @ data['trigger']" + assert result == {"message": f"Message malformed: {validation_error}"} + # Assert the validation error is not logged + assert validation_error not in caplog.text + + @pytest.mark.parametrize("automation_config", ({},)) async def test_update_remove_key_automation_config( hass: HomeAssistant, hass_client, hass_config_store, setup_automation diff --git a/tests/components/config/test_entity_registry.py b/tests/components/config/test_entity_registry.py index ee46838b01c..9caa9cbf3f2 100644 --- a/tests/components/config/test_entity_registry.py +++ b/tests/components/config/test_entity_registry.py @@ -1,7 +1,6 @@ """Test entity_registry API.""" -from unittest.mock import ANY - import pytest +from pytest_unordered import unordered from homeassistant.components.config import entity_registry from homeassistant.const import ATTR_ICON @@ -15,6 +14,7 @@ from homeassistant.helpers.entity_registry import ( ) from tests.common import ( + ANY, MockConfigEntry, MockEntity, MockEntityPlatform, @@ -70,10 +70,11 @@ async def test_list_entities(hass, client): "hidden_by": None, "icon": None, "id": ANY, - "unique_id": ANY, "name": "Hello World", "original_name": None, "platform": "test_platform", + "translation_key": None, + "unique_id": ANY, }, { "area_id": None, @@ -86,10 +87,11 @@ async def test_list_entities(hass, client): "hidden_by": None, "icon": None, "id": ANY, - "unique_id": ANY, "name": None, "original_name": None, "platform": "test_platform", + "translation_key": None, + "unique_id": ANY, }, ] @@ -124,10 +126,11 @@ async def test_list_entities(hass, client): "hidden_by": None, "icon": None, "id": ANY, - "unique_id": ANY, "name": "Hello World", "original_name": None, "platform": "test_platform", + "translation_key": None, + "unique_id": ANY, }, ] @@ -157,6 +160,7 @@ async def test_get_entity(hass, client): msg = await client.receive_json() assert msg["result"] == { + "aliases": [], "area_id": None, "capabilities": None, "config_entry_id": None, @@ -165,16 +169,17 @@ async def test_get_entity(hass, client): "disabled_by": None, "entity_category": None, "entity_id": "test_domain.name", + "has_entity_name": False, "hidden_by": None, "icon": None, "id": ANY, - "has_entity_name": False, "name": "Hello World", "options": {}, "original_device_class": None, "original_icon": None, "original_name": None, "platform": "test_platform", + "translation_key": None, "unique_id": "1234", } @@ -188,6 +193,7 @@ async def test_get_entity(hass, client): msg = await client.receive_json() assert msg["result"] == { + "aliases": [], "area_id": None, "capabilities": None, "config_entry_id": None, @@ -196,16 +202,17 @@ async def test_get_entity(hass, client): "disabled_by": None, "entity_category": None, "entity_id": "test_domain.no_name", + "has_entity_name": False, "hidden_by": None, "icon": None, "id": ANY, - "has_entity_name": False, "name": None, "options": {}, "original_device_class": None, "original_icon": None, "original_name": None, "platform": "test_platform", + "translation_key": None, "unique_id": "6789", } @@ -240,6 +247,7 @@ async def test_update_entity(hass, client): "id": 6, "type": "config/entity_registry/update", "entity_id": "test_domain.world", + "aliases": ["alias_1", "alias_2"], "area_id": "mock-area-id", "device_class": "custom_device_class", "hidden_by": "user", # We exchange strings over the WS API, not enums @@ -252,6 +260,7 @@ async def test_update_entity(hass, client): assert msg["result"] == { "entity_entry": { + "aliases": unordered(["alias_1", "alias_2"]), "area_id": "mock-area-id", "capabilities": None, "config_entry_id": None, @@ -260,16 +269,17 @@ async def test_update_entity(hass, client): "disabled_by": None, "entity_category": None, "entity_id": "test_domain.world", + "has_entity_name": False, "hidden_by": "user", # We exchange strings over the WS API, not enums "icon": "icon:after update", "id": ANY, - "has_entity_name": False, "name": "after update", "options": {}, "original_device_class": None, "original_icon": None, "original_name": None, "platform": "test_platform", + "translation_key": None, "unique_id": "1234", } } @@ -325,6 +335,7 @@ async def test_update_entity(hass, client): assert msg["result"] == { "entity_entry": { + "aliases": unordered(["alias_1", "alias_2"]), "area_id": "mock-area-id", "capabilities": None, "config_entry_id": None, @@ -333,16 +344,17 @@ async def test_update_entity(hass, client): "disabled_by": None, "entity_category": None, "entity_id": "test_domain.world", + "has_entity_name": False, "hidden_by": "user", # We exchange strings over the WS API, not enums "icon": "icon:after update", "id": ANY, - "has_entity_name": False, "name": "after update", "options": {}, "original_device_class": None, "original_icon": None, "original_name": None, "platform": "test_platform", + "translation_key": None, "unique_id": "1234", }, "require_restart": True, @@ -363,6 +375,7 @@ async def test_update_entity(hass, client): assert msg["result"] == { "entity_entry": { + "aliases": unordered(["alias_1", "alias_2"]), "area_id": "mock-area-id", "capabilities": None, "config_entry_id": None, @@ -371,16 +384,17 @@ async def test_update_entity(hass, client): "disabled_by": None, "entity_category": None, "entity_id": "test_domain.world", + "has_entity_name": False, "hidden_by": "user", # We exchange strings over the WS API, not enums "icon": "icon:after update", "id": ANY, - "has_entity_name": False, "name": "after update", "options": {"sensor": {"unit_of_measurement": "beard_second"}}, "original_device_class": None, "original_icon": None, "original_name": None, "platform": "test_platform", + "translation_key": None, "unique_id": "1234", }, } @@ -413,6 +427,7 @@ async def test_update_entity_require_restart(hass, client): assert msg["result"] == { "entity_entry": { + "aliases": [], "area_id": None, "capabilities": None, "config_entry_id": config_entry.entry_id, @@ -421,16 +436,17 @@ async def test_update_entity_require_restart(hass, client): "disabled_by": None, "entity_category": None, "entity_id": entity_id, + "has_entity_name": False, + "hidden_by": None, "icon": None, "id": ANY, - "hidden_by": None, - "has_entity_name": False, "name": None, "options": {}, "original_device_class": None, "original_icon": None, "original_name": None, "platform": "test_platform", + "translation_key": None, "unique_id": "1234", }, "require_restart": True, @@ -519,6 +535,7 @@ async def test_update_entity_no_changes(hass, client): assert msg["result"] == { "entity_entry": { + "aliases": [], "area_id": None, "capabilities": None, "config_entry_id": None, @@ -527,16 +544,17 @@ async def test_update_entity_no_changes(hass, client): "disabled_by": None, "entity_category": None, "entity_id": "test_domain.world", + "has_entity_name": False, "hidden_by": None, "icon": None, "id": ANY, - "has_entity_name": False, "name": "name of entity", "options": {}, "original_device_class": None, "original_icon": None, "original_name": None, "platform": "test_platform", + "translation_key": None, "unique_id": "1234", } } @@ -606,6 +624,7 @@ async def test_update_entity_id(hass, client): assert msg["result"] == { "entity_entry": { + "aliases": [], "area_id": None, "capabilities": None, "config_entry_id": None, @@ -614,16 +633,17 @@ async def test_update_entity_id(hass, client): "disabled_by": None, "entity_category": None, "entity_id": "test_domain.planet", + "has_entity_name": False, "hidden_by": None, "icon": None, "id": ANY, - "has_entity_name": False, "name": None, "options": {}, "original_device_class": None, "original_icon": None, "original_name": None, "platform": "test_platform", + "translation_key": None, "unique_id": "1234", } } diff --git a/tests/components/config/test_script.py b/tests/components/config/test_script.py index b8e980c29b9..98757f894cb 100644 --- a/tests/components/config/test_script.py +++ b/tests/components/config/test_script.py @@ -1,11 +1,13 @@ """Tests for config/script.""" from http import HTTPStatus +import json from unittest.mock import patch import pytest from homeassistant.bootstrap import async_setup_component from homeassistant.components import config +from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er from tests.components.blueprint.conftest import stub_blueprint_populate # noqa: F401 @@ -17,6 +19,119 @@ async def setup_script(hass, script_config, stub_blueprint_populate): # noqa: F assert await async_setup_component(hass, "script", {"script": script_config}) +@pytest.mark.parametrize("script_config", ({},)) +async def test_get_script_config(hass: HomeAssistant, hass_client, hass_config_store): + """Test getting script config.""" + with patch.object(config, "SECTIONS", ["script"]): + await async_setup_component(hass, "config", {}) + + client = await hass_client() + + hass_config_store["scripts.yaml"] = { + "sun": {"alias": "Sun"}, + "moon": {"alias": "Moon"}, + } + + resp = await client.get("/api/config/script/config/moon") + + assert resp.status == HTTPStatus.OK + result = await resp.json() + + assert result == {"alias": "Moon"} + + +@pytest.mark.parametrize("script_config", ({},)) +async def test_update_script_config( + hass: HomeAssistant, hass_client, hass_config_store +): + """Test updating script config.""" + with patch.object(config, "SECTIONS", ["script"]): + await async_setup_component(hass, "config", {}) + + assert sorted(hass.states.async_entity_ids("script")) == [] + + client = await hass_client() + + orig_data = {"sun": {"alias": "Sun"}, "moon": {"alias": "Moon"}} + hass_config_store["scripts.yaml"] = orig_data + + resp = await client.post( + "/api/config/script/config/moon", + data=json.dumps({"alias": "Moon updated", "sequence": []}), + ) + await hass.async_block_till_done() + assert sorted(hass.states.async_entity_ids("script")) == ["script.moon"] + + assert resp.status == HTTPStatus.OK + result = await resp.json() + assert result == {"result": "ok"} + + new_data = hass_config_store["scripts.yaml"] + assert list(new_data["moon"]) == ["alias", "sequence"] + assert new_data["moon"] == {"alias": "Moon updated", "sequence": []} + + +@pytest.mark.parametrize("script_config", ({},)) +async def test_update_script_config_with_error( + hass: HomeAssistant, hass_client, hass_config_store, caplog +): + """Test updating script config with errors.""" + with patch.object(config, "SECTIONS", ["script"]): + await async_setup_component(hass, "config", {}) + + assert sorted(hass.states.async_entity_ids("script")) == [] + + client = await hass_client() + + orig_data = {"sun": {}, "moon": {}} + hass_config_store["scripts.yaml"] = orig_data + + resp = await client.post( + "/api/config/script/config/moon", + data=json.dumps({}), + ) + await hass.async_block_till_done() + assert sorted(hass.states.async_entity_ids("script")) == [] + + assert resp.status != HTTPStatus.OK + result = await resp.json() + validation_error = "required key not provided @ data['sequence']" + assert result == {"message": f"Message malformed: {validation_error}"} + # Assert the validation error is not logged + assert validation_error not in caplog.text + + +@pytest.mark.parametrize("script_config", ({},)) +async def test_update_remove_key_script_config( + hass: HomeAssistant, hass_client, hass_config_store +): + """Test updating script config while removing a key.""" + with patch.object(config, "SECTIONS", ["script"]): + await async_setup_component(hass, "config", {}) + + assert sorted(hass.states.async_entity_ids("script")) == [] + + client = await hass_client() + + orig_data = {"sun": {"key": "value"}, "moon": {"key": "value"}} + hass_config_store["scripts.yaml"] = orig_data + + resp = await client.post( + "/api/config/script/config/moon", + data=json.dumps({"sequence": []}), + ) + await hass.async_block_till_done() + assert sorted(hass.states.async_entity_ids("script")) == ["script.moon"] + + assert resp.status == HTTPStatus.OK + result = await resp.json() + assert result == {"result": "ok"} + + new_data = hass_config_store["scripts.yaml"] + assert list(new_data["moon"]) == ["sequence"] + assert new_data["moon"] == {"sequence": []} + + @pytest.mark.parametrize( "script_config", ( @@ -26,7 +141,7 @@ async def setup_script(hass, script_config, stub_blueprint_populate): # noqa: F }, ), ) -async def test_delete_script(hass, hass_client, hass_config_store): +async def test_delete_script(hass: HomeAssistant, hass_client, hass_config_store): """Test deleting a script.""" with patch.object(config, "SECTIONS", ["script"]): await async_setup_component(hass, "config", {}) diff --git a/tests/components/conversation/test_init.py b/tests/components/conversation/test_init.py index cbaa3278e9c..c25df45ce78 100644 --- a/tests/components/conversation/test_init.py +++ b/tests/components/conversation/test_init.py @@ -1,5 +1,6 @@ """The tests for the Conversation component.""" from http import HTTPStatus +from unittest.mock import ANY, patch import pytest @@ -11,6 +12,14 @@ from homeassistant.setup import async_setup_component from tests.common import async_mock_intent, async_mock_service +@pytest.fixture +async def init_components(hass): + """Initialize relevant components with empty configs.""" + assert await async_setup_component(hass, "homeassistant", {}) + assert await async_setup_component(hass, "conversation", {}) + assert await async_setup_component(hass, "intent", {}) + + async def test_calling_intent(hass): """Test calling an intent from a conversation.""" intents = async_mock_intent(hass, "OrderBeer") @@ -122,22 +131,104 @@ async def test_http_processing_intent(hass, hass_client, hass_admin_user): data = await resp.json() assert data == { - "card": { - "simple": {"content": "You chose a Grolsch.", "title": "Beer ordered"} + "response": { + "response_type": "action_done", + "card": { + "simple": {"content": "You chose a Grolsch.", "title": "Beer ordered"} + }, + "speech": { + "plain": { + "extra_data": None, + "speech": "I've ordered a Grolsch!", + } + }, + "language": hass.config.language, + "data": {"targets": [], "success": [], "failed": []}, }, - "speech": {"plain": {"extra_data": None, "speech": "I've ordered a Grolsch!"}}, + "conversation_id": None, + } + + +async def test_http_failed_action(hass, hass_client, hass_admin_user): + """Test processing intent via HTTP API with a partial completion.""" + + class TestIntentHandler(intent.IntentHandler): + """Test Intent Handler.""" + + intent_type = "TurnOffLights" + + async def async_handle(self, handle_intent: intent.Intent): + """Handle the intent.""" + response = handle_intent.create_response() + area = handle_intent.slots["area"]["value"] + + # Mark some targets as successful, others as failed + response.async_set_targets( + intent_targets=[ + intent.IntentResponseTarget( + type=intent.IntentResponseTargetType.AREA, name=area, id=area + ) + ] + ) + response.async_set_results( + success_results=[ + intent.IntentResponseTarget( + type=intent.IntentResponseTargetType.ENTITY, + name="light1", + id="light.light1", + ) + ], + failed_results=[ + intent.IntentResponseTarget( + type=intent.IntentResponseTargetType.ENTITY, + name="light2", + id="light.light2", + ) + ], + ) + + return response + + intent.async_register(hass, TestIntentHandler()) + + result = await async_setup_component( + hass, + "conversation", + { + "conversation": { + "intents": {"TurnOffLights": ["turn off the lights in the {area}"]} + } + }, + ) + assert result + + client = await hass_client() + resp = await client.post( + "/api/conversation/process", json={"text": "Turn off the lights in the kitchen"} + ) + + assert resp.status == HTTPStatus.OK + data = await resp.json() + + assert data == { + "response": { + "response_type": "action_done", + "card": {}, + "speech": {}, + "language": hass.config.language, + "data": { + "targets": [{"type": "area", "id": "kitchen", "name": "kitchen"}], + "success": [{"type": "entity", "id": "light.light1", "name": "light1"}], + "failed": [{"type": "entity", "id": "light.light2", "name": "light2"}], + }, + }, + "conversation_id": None, } @pytest.mark.parametrize("sentence", ("turn on kitchen", "turn kitchen on")) -async def test_turn_on_intent(hass, sentence): +async def test_turn_on_intent(hass, init_components, sentence): """Test calling the turn on intent.""" - result = await async_setup_component(hass, "homeassistant", {}) - assert result - - result = await async_setup_component(hass, "conversation", {}) - assert result - hass.states.async_set("light.kitchen", "off") calls = async_mock_service(hass, HASS_DOMAIN, "turn_on") @@ -154,14 +245,8 @@ async def test_turn_on_intent(hass, sentence): @pytest.mark.parametrize("sentence", ("turn off kitchen", "turn kitchen off")) -async def test_turn_off_intent(hass, sentence): +async def test_turn_off_intent(hass, init_components, sentence): """Test calling the turn on intent.""" - result = await async_setup_component(hass, "homeassistant", {}) - assert result - - result = await async_setup_component(hass, "conversation", {}) - assert result - hass.states.async_set("light.kitchen", "on") calls = async_mock_service(hass, HASS_DOMAIN, "turn_off") @@ -178,14 +263,8 @@ async def test_turn_off_intent(hass, sentence): @pytest.mark.parametrize("sentence", ("toggle kitchen", "kitchen toggle")) -async def test_toggle_intent(hass, sentence): +async def test_toggle_intent(hass, init_components, sentence): """Test calling the turn on intent.""" - result = await async_setup_component(hass, "homeassistant", {}) - assert result - - result = await async_setup_component(hass, "conversation", {}) - assert result - hass.states.async_set("light.kitchen", "on") calls = async_mock_service(hass, HASS_DOMAIN, "toggle") @@ -201,12 +280,8 @@ async def test_toggle_intent(hass, sentence): assert call.data == {"entity_id": "light.kitchen"} -async def test_http_api(hass, hass_client): +async def test_http_api(hass, init_components, hass_client): """Test the HTTP conversation API.""" - assert await async_setup_component(hass, "homeassistant", {}) - assert await async_setup_component(hass, "conversation", {}) - assert await async_setup_component(hass, "intent", {}) - client = await hass_client() hass.states.async_set("light.kitchen", "off") calls = async_mock_service(hass, HASS_DOMAIN, "turn_on") @@ -215,6 +290,28 @@ async def test_http_api(hass, hass_client): "/api/conversation/process", json={"text": "Turn the kitchen on"} ) assert resp.status == HTTPStatus.OK + data = await resp.json() + + assert data == { + "response": { + "card": {}, + "speech": {"plain": {"extra_data": None, "speech": "Turned kitchen on"}}, + "language": hass.config.language, + "response_type": "action_done", + "data": { + "targets": [], + "success": [ + { + "type": "entity", + "name": "kitchen", + "id": "light.kitchen", + }, + ], + "failed": [], + }, + }, + "conversation_id": None, + } assert len(calls) == 1 call = calls[0] @@ -223,14 +320,105 @@ async def test_http_api(hass, hass_client): assert call.data == {"entity_id": "light.kitchen"} -async def test_http_api_wrong_data(hass, hass_client): +async def test_http_api_no_match(hass, init_components, hass_client): + """Test the HTTP conversation API with an intent match failure.""" + client = await hass_client() + + # Sentence should not match any intents + resp = await client.post("/api/conversation/process", json={"text": "do something"}) + assert resp.status == HTTPStatus.OK + data = await resp.json() + + assert data == { + "response": { + "card": {}, + "speech": { + "plain": { + "extra_data": None, + "speech": "Sorry, I didn't understand that", + }, + }, + "language": hass.config.language, + "response_type": "error", + "data": { + "code": "no_intent_match", + }, + }, + "conversation_id": None, + } + + +async def test_http_api_no_valid_targets(hass, init_components, hass_client): + """Test the HTTP conversation API with no valid targets.""" + client = await hass_client() + + # No kitchen light + resp = await client.post( + "/api/conversation/process", json={"text": "turn on the kitchen"} + ) + assert resp.status == HTTPStatus.OK + data = await resp.json() + + assert data == { + "response": { + "response_type": "error", + "card": {}, + "speech": { + "plain": { + "extra_data": None, + "speech": "Unable to find an entity called kitchen", + }, + }, + "language": hass.config.language, + "data": { + "code": "no_valid_targets", + }, + }, + "conversation_id": None, + } + + +async def test_http_api_handle_failure(hass, init_components, hass_client): + """Test the HTTP conversation API with an error during handling.""" + client = await hass_client() + + hass.states.async_set("light.kitchen", "off") + + # Raise an "unexpected" error during intent handling + def async_handle_error(*args, **kwargs): + raise intent.IntentUnexpectedError( + "Unexpected error turning on the kitchen light" + ) + + with patch("homeassistant.helpers.intent.async_handle", new=async_handle_error): + resp = await client.post( + "/api/conversation/process", json={"text": "turn on the kitchen"} + ) + + assert resp.status == HTTPStatus.OK + data = await resp.json() + + assert data == { + "response": { + "response_type": "error", + "card": {}, + "speech": { + "plain": { + "extra_data": None, + "speech": "Unexpected error turning on the kitchen light", + } + }, + "language": hass.config.language, + "data": { + "code": "failed_to_handle", + }, + }, + "conversation_id": None, + } + + +async def test_http_api_wrong_data(hass, init_components, hass_client): """Test the HTTP conversation API.""" - result = await async_setup_component(hass, "homeassistant", {}) - assert result - - result = await async_setup_component(hass, "conversation", {}) - assert result - client = await hass_client() resp = await client.post("/api/conversation/process", json={"text": 123}) @@ -248,12 +436,14 @@ async def test_custom_agent(hass, hass_client, hass_admin_user): class MyAgent(conversation.AbstractConversationAgent): """Test Agent.""" - async def async_process(self, text, context, conversation_id): + async def async_process(self, text, context, conversation_id, language): """Process some text.""" - calls.append((text, context, conversation_id)) - response = intent.IntentResponse() + calls.append((text, context, conversation_id, language)) + response = intent.IntentResponse(language=language) response.async_set_speech("Test response") - return response + return conversation.ConversationResult( + response=response, conversation_id=conversation_id + ) conversation.async_set_agent(hass, MyAgent()) @@ -263,15 +453,83 @@ async def test_custom_agent(hass, hass_client, hass_admin_user): resp = await client.post( "/api/conversation/process", - json={"text": "Test Text", "conversation_id": "test-conv-id"}, + json={ + "text": "Test Text", + "conversation_id": "test-conv-id", + "language": "test-language", + }, ) assert resp.status == HTTPStatus.OK assert await resp.json() == { - "card": {}, - "speech": {"plain": {"extra_data": None, "speech": "Test response"}}, + "response": { + "response_type": "action_done", + "card": {}, + "speech": { + "plain": { + "extra_data": None, + "speech": "Test response", + } + }, + "language": "test-language", + "data": {"targets": [], "success": [], "failed": []}, + }, + "conversation_id": "test-conv-id", } assert len(calls) == 1 assert calls[0][0] == "Test Text" assert calls[0][1].user_id == hass_admin_user.id assert calls[0][2] == "test-conv-id" + assert calls[0][3] == "test-language" + + +@pytest.mark.parametrize( + "payload", + [ + { + "text": "Test Text", + }, + { + "text": "Test Text", + "language": "test-language", + }, + { + "text": "Test Text", + "conversation_id": "test-conv-id", + }, + { + "text": "Test Text", + "conversation_id": None, + }, + { + "text": "Test Text", + "conversation_id": "test-conv-id", + "language": "test-language", + }, + ], +) +async def test_ws_api(hass, hass_ws_client, payload): + """Test the Websocket conversation API.""" + assert await async_setup_component(hass, "conversation", {}) + client = await hass_ws_client(hass) + + await client.send_json({"id": 5, "type": "conversation/process", **payload}) + + msg = await client.receive_json() + + assert msg["success"] + assert msg["result"] == { + "response": { + "response_type": "error", + "card": {}, + "speech": { + "plain": { + "extra_data": None, + "speech": "Sorry, I didn't understand that", + } + }, + "language": payload.get("language", hass.config.language), + "data": {"code": "no_intent_match"}, + }, + "conversation_id": payload.get("conversation_id") or ANY, + } diff --git a/tests/components/cpuspeed/test_sensor.py b/tests/components/cpuspeed/test_sensor.py index 068d24b6f3c..625f80a6814 100644 --- a/tests/components/cpuspeed/test_sensor.py +++ b/tests/components/cpuspeed/test_sensor.py @@ -8,6 +8,7 @@ from homeassistant.components.homeassistant import ( DOMAIN as HOME_ASSISTANT_DOMAIN, SERVICE_UPDATE_ENTITY, ) +from homeassistant.components.sensor import SensorDeviceClass from homeassistant.const import ( ATTR_DEVICE_CLASS, ATTR_ENTITY_ID, @@ -42,7 +43,7 @@ async def test_sensor( assert state.state == "3.2" assert state.attributes.get(ATTR_FRIENDLY_NAME) == "CPU Speed" assert state.attributes.get(ATTR_ICON) == "mdi:pulse" - assert ATTR_DEVICE_CLASS not in state.attributes + assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.FREQUENCY assert state.attributes.get(ATTR_ARCH) == "aargh" assert state.attributes.get(ATTR_BRAND) == "Intel Ryzen 7" diff --git a/tests/components/deconz/test_logbook.py b/tests/components/deconz/test_logbook.py index 1680854302b..6a2c244207c 100644 --- a/tests/components/deconz/test_logbook.py +++ b/tests/components/deconz/test_logbook.py @@ -66,6 +66,9 @@ async def test_humanifying_deconz_alarm_event(hass, aioclient_mock): identifiers={(DECONZ_DOMAIN, keypad_serial)} ) + removed_device_event_id = "removed_device" + removed_device_serial = "00:00:00:00:00:00:00:05" + hass.config.components.add("recorder") assert await async_setup_component(hass, "logbook", {}) @@ -82,6 +85,17 @@ async def test_humanifying_deconz_alarm_event(hass, aioclient_mock): CONF_UNIQUE_ID: keypad_serial, }, ), + # Event of a removed device + MockRow( + CONF_DECONZ_ALARM_EVENT, + { + CONF_CODE: 1234, + CONF_DEVICE_ID: "ff99ff99ff99ff99ff99ff99ff99ff99", + CONF_EVENT: STATE_ALARM_ARMED_AWAY, + CONF_ID: removed_device_event_id, + CONF_UNIQUE_ID: removed_device_serial, + }, + ), ], ) @@ -89,6 +103,10 @@ async def test_humanifying_deconz_alarm_event(hass, aioclient_mock): assert events[0]["domain"] == "deconz" assert events[0]["message"] == "fired event 'armed_away'" + assert events[1]["name"] == "removed_device" + assert events[1]["domain"] == "deconz" + assert events[1]["message"] == "fired event 'armed_away'" + async def test_humanifying_deconz_event(hass, aioclient_mock): """Test humanifying deCONZ event.""" @@ -155,6 +173,9 @@ async def test_humanifying_deconz_event(hass, aioclient_mock): identifiers={(DECONZ_DOMAIN, faulty_serial)} ) + removed_device_event_id = "removed_device" + removed_device_serial = "00:00:00:00:00:00:00:05" + hass.config.components.add("recorder") assert await async_setup_component(hass, "logbook", {}) @@ -211,6 +232,16 @@ async def test_humanifying_deconz_event(hass, aioclient_mock): CONF_UNIQUE_ID: faulty_serial, }, ), + # Event of a removed device + MockRow( + CONF_DECONZ_EVENT, + { + CONF_DEVICE_ID: "ff99ff99ff99ff99ff99ff99ff99ff99", + CONF_EVENT: 2000, + CONF_ID: removed_device_event_id, + CONF_UNIQUE_ID: removed_device_serial, + }, + ), ], ) @@ -233,3 +264,7 @@ async def test_humanifying_deconz_event(hass, aioclient_mock): assert events[4]["name"] == "Faulty event" assert events[4]["domain"] == "deconz" assert events[4]["message"] == "fired an unknown event" + + assert events[5]["name"] == "removed_device" + assert events[5]["domain"] == "deconz" + assert events[5]["message"] == "fired event '2000'" diff --git a/tests/components/deconz/test_sensor.py b/tests/components/deconz/test_sensor.py index d877e63864a..73df9c3fd1a 100644 --- a/tests/components/deconz/test_sensor.py +++ b/tests/components/deconz/test_sensor.py @@ -100,10 +100,9 @@ TEST_DATA = [ "old_unique_id": "00:12:4b:00:14:4d:00:07-ppb", "state": "809", "entity_category": None, - "device_class": SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS, + "device_class": None, "state_class": SensorStateClass.MEASUREMENT, "attributes": { - "device_class": "volatile_organic_compounds", "friendly_name": "BOSCH Air quality sensor PPB", "state_class": "measurement", "unit_of_measurement": "ppb", diff --git a/tests/components/demo/test_init.py b/tests/components/demo/test_init.py index e5156f35317..45fa602b143 100644 --- a/tests/components/demo/test_init.py +++ b/tests/components/demo/test_init.py @@ -243,7 +243,6 @@ async def test_issues_created(mock_history, hass, hass_client, hass_ws_client): "description_placeholders": None, "flow_id": flow_id, "handler": "demo", - "title": "", "type": "create_entry", "version": 1, } diff --git a/tests/components/devolo_home_control/mocks.py b/tests/components/devolo_home_control/mocks.py index 129c4a377ef..0fc01d61841 100644 --- a/tests/components/devolo_home_control/mocks.py +++ b/tests/components/devolo_home_control/mocks.py @@ -11,6 +11,7 @@ from devolo_home_control_api.properties.binary_sensor_property import ( from devolo_home_control_api.properties.binary_switch_property import ( BinarySwitchProperty, ) +from devolo_home_control_api.properties.consumption_property import ConsumptionProperty from devolo_home_control_api.properties.multi_level_sensor_property import ( MultiLevelSensorProperty, ) @@ -43,6 +44,19 @@ class BinarySwitchPropertyMock(BinarySwitchProperty): self.element_uid = "Test" +class ConsumptionPropertyMock(ConsumptionProperty): + """devolo Home Control binary sensor mock.""" + + def __init__(self, **kwargs: Any) -> None: + """Initialize the mock.""" + self._logger = MagicMock() + self.element_uid = "devolo.Meter:Test" + self.current_unit = "W" + self.total_unit = "kWh" + self._current = 0.0 + self._total = 0.0 + + class MultiLevelSensorPropertyMock(MultiLevelSensorProperty): """devolo Home Control multi level sensor mock.""" @@ -135,6 +149,19 @@ class ClimateMock(DeviceMock): self.multi_level_sensor_property = {"Test": MultiLevelSensorPropertyMock()} +class ConsumptionMock(DeviceMock): + """devolo Home Control meter device mock.""" + + def __init__(self) -> None: + """Initialize the mock.""" + super().__init__() + + self.consumption_property = {"devolo.Meter:Test": ConsumptionPropertyMock()} + self.multi_level_sensor_property = { + "devolo.VoltageMultiLevelSensor:Test": MultiLevelSensorPropertyMock() + } + + class CoverMock(DeviceMock): """devolo Home Control cover device mock.""" @@ -195,6 +222,17 @@ class SirenMock(DeviceMock): self.settings_property["tone"] = SettingsMock() +class SensorMock(DeviceMock): + """devolo Home Control sensor device mock.""" + + def __init__(self) -> None: + """Initialize the mock.""" + super().__init__() + self.multi_level_sensor_property = { + "devolo.MultiLevelSensor:Test": MultiLevelSensorPropertyMock() + } + + class HomeControlMock(HomeControl): """devolo Home Control gateway mock.""" @@ -203,7 +241,7 @@ class HomeControlMock(HomeControl): self.devices = {} self.publisher = MagicMock() - def websocket_disconnect(self, event: str): + def websocket_disconnect(self, event: str = "") -> None: """Mock disconnect of the websocket.""" @@ -234,6 +272,19 @@ class HomeControlMockClimate(HomeControlMock): self.publisher.unregister = MagicMock() +class HomeControlMockConsumption(HomeControlMock): + """devolo Home Control gateway mock with meter devices.""" + + def __init__(self, **kwargs: Any) -> None: + """Initialize the mock.""" + super().__init__() + self.devices = { + "Test": ConsumptionMock(), + } + self.publisher = Publisher(self.devices.keys()) + self.publisher.unregister = MagicMock() + + class HomeControlMockCover(HomeControlMock): """devolo Home Control gateway mock with cover devices.""" @@ -280,6 +331,19 @@ class HomeControlMockDisabledBinarySensor(HomeControlMock): self.devices = {"Test": DisabledBinarySensorMock()} +class HomeControlMockSensor(HomeControlMock): + """devolo Home Control gateway mock with sensor devices.""" + + def __init__(self, **kwargs: Any) -> None: + """Initialize the mock.""" + super().__init__() + self.devices = { + "Test": SensorMock(), + } + self.publisher = Publisher(self.devices.keys()) + self.publisher.unregister = MagicMock() + + class HomeControlMockSiren(HomeControlMock): """devolo Home Control gateway mock with siren device.""" diff --git a/tests/components/devolo_home_control/test_sensor.py b/tests/components/devolo_home_control/test_sensor.py new file mode 100644 index 00000000000..c6740e4bc42 --- /dev/null +++ b/tests/components/devolo_home_control/test_sensor.py @@ -0,0 +1,177 @@ +"""Tests for the devolo Home Control sensor platform.""" +from unittest.mock import patch + +from homeassistant.components.sensor import ( + ATTR_STATE_CLASS, + DOMAIN, + SensorDeviceClass, + SensorStateClass, +) +from homeassistant.const import ( + ATTR_DEVICE_CLASS, + ATTR_UNIT_OF_MEASUREMENT, + PERCENTAGE, + STATE_UNAVAILABLE, +) +from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry +from homeassistant.helpers.entity import EntityCategory + +from . import configure_integration +from .mocks import HomeControlMock, HomeControlMockConsumption, HomeControlMockSensor + + +async def test_temperature_sensor(hass: HomeAssistant): + """Test setup of a temperature sensor device.""" + entry = configure_integration(hass) + test_gateway = HomeControlMockSensor() + with patch( + "homeassistant.components.devolo_home_control.HomeControl", + side_effect=[test_gateway, HomeControlMock()], + ): + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + state = hass.states.get(f"{DOMAIN}.test_temperature") + assert state is not None + assert state.state == str( + test_gateway.devices["Test"] + .multi_level_sensor_property["devolo.MultiLevelSensor:Test"] + .value + ) + assert state.attributes[ATTR_STATE_CLASS] == SensorStateClass.MEASUREMENT + assert state.attributes[ATTR_DEVICE_CLASS] == SensorDeviceClass.TEMPERATURE + + +async def test_battery_sensor(hass: HomeAssistant): + """Test setup and state change of a battery sensor device.""" + entry = configure_integration(hass) + er = entity_registry.async_get(hass) + test_gateway = HomeControlMockSensor() + test_gateway.devices["Test"].battery_level = 25 + with patch( + "homeassistant.components.devolo_home_control.HomeControl", + side_effect=[test_gateway, HomeControlMock()], + ): + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + state = hass.states.get(f"{DOMAIN}.test_battery_level") + assert state is not None + assert state.state == str(test_gateway.devices["Test"].battery_level) + assert state.attributes[ATTR_STATE_CLASS] == SensorStateClass.MEASUREMENT + assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == PERCENTAGE + assert state.attributes[ATTR_DEVICE_CLASS] == SensorDeviceClass.BATTERY + assert ( + er.async_get(f"{DOMAIN}.test_battery_level").entity_category + is EntityCategory.DIAGNOSTIC + ) + + # Emulate websocket message: value changed + test_gateway.publisher.dispatch("Test", ("Test", 10, "battery_level")) + await hass.async_block_till_done() + assert hass.states.get(f"{DOMAIN}.test_battery_level").state == "10" + + +async def test_consumption_sensor(hass: HomeAssistant): + """Test setup and state change of a consumption sensor device.""" + entry = configure_integration(hass) + test_gateway = HomeControlMockConsumption() + with patch( + "homeassistant.components.devolo_home_control.HomeControl", + side_effect=[test_gateway, HomeControlMock()], + ): + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + state = hass.states.get(f"{DOMAIN}.test_current_consumption") + assert state is not None + assert state.state == str( + test_gateway.devices["Test"].consumption_property["devolo.Meter:Test"].current + ) + assert state.attributes[ATTR_STATE_CLASS] == SensorStateClass.MEASUREMENT + assert state.attributes[ATTR_DEVICE_CLASS] == SensorDeviceClass.POWER + + state = hass.states.get(f"{DOMAIN}.test_total_consumption") + assert state is not None + assert state.state == str( + test_gateway.devices["Test"].consumption_property["devolo.Meter:Test"].total + ) + assert state.attributes[ATTR_STATE_CLASS] == SensorStateClass.TOTAL_INCREASING + assert state.attributes[ATTR_DEVICE_CLASS] == SensorDeviceClass.ENERGY + + # Emulate websocket message: value changed + test_gateway.devices["Test"].consumption_property["devolo.Meter:Test"].total = 50.0 + test_gateway.publisher.dispatch("Test", ("devolo.Meter:Test", 50.0)) + await hass.async_block_till_done() + assert hass.states.get(f"{DOMAIN}.test_total_consumption").state == "50.0" + + # Emulate websocket message: device went offline + test_gateway.devices["Test"].status = 1 + test_gateway.publisher.dispatch("Test", ("Status", False, "status")) + await hass.async_block_till_done() + assert ( + hass.states.get(f"{DOMAIN}.test_current_consumption").state == STATE_UNAVAILABLE + ) + assert ( + hass.states.get(f"{DOMAIN}.test_total_consumption").state == STATE_UNAVAILABLE + ) + + +async def test_voltage_sensor(hass: HomeAssistant): + """Test disabled setup of a voltage sensor device.""" + entry = configure_integration(hass) + test_gateway = HomeControlMockConsumption() + with patch( + "homeassistant.components.devolo_home_control.HomeControl", + side_effect=[test_gateway, HomeControlMock()], + ): + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + state = hass.states.get(f"{DOMAIN}.test_voltage") + assert state is None + + +async def test_sensor_change(hass: HomeAssistant): + """Test state change of a sensor device.""" + entry = configure_integration(hass) + test_gateway = HomeControlMockSensor() + with patch( + "homeassistant.components.devolo_home_control.HomeControl", + side_effect=[test_gateway, HomeControlMock()], + ): + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + # Emulate websocket message: value changed + test_gateway.publisher.dispatch("Test", ("devolo.MultiLevelSensor:Test", 50.0)) + await hass.async_block_till_done() + state = hass.states.get(f"{DOMAIN}.test_temperature") + assert state.state == "50.0" + + # Emulate websocket message: device went offline + test_gateway.devices["Test"].status = 1 + test_gateway.publisher.dispatch("Test", ("Status", False, "status")) + await hass.async_block_till_done() + assert hass.states.get(f"{DOMAIN}.test_temperature").state == STATE_UNAVAILABLE + + +async def test_remove_from_hass(hass: HomeAssistant): + """Test removing entity.""" + entry = configure_integration(hass) + test_gateway = HomeControlMockSensor() + with patch( + "homeassistant.components.devolo_home_control.HomeControl", + side_effect=[test_gateway, HomeControlMock()], + ): + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + state = hass.states.get(f"{DOMAIN}.test_temperature") + assert state is not None + await hass.config_entries.async_remove(entry.entry_id) + await hass.async_block_till_done() + + assert len(hass.states.async_all()) == 0 + assert test_gateway.publisher.unregister.call_count == 1 diff --git a/tests/components/dlna_dmr/conftest.py b/tests/components/dlna_dmr/conftest.py index 521f770a8fa..81225173d51 100644 --- a/tests/components/dlna_dmr/conftest.py +++ b/tests/components/dlna_dmr/conftest.py @@ -11,22 +11,23 @@ import pytest from homeassistant.components.dlna_dmr.const import DOMAIN as DLNA_DOMAIN from homeassistant.components.dlna_dmr.data import DlnaDmrData -from homeassistant.const import CONF_DEVICE_ID, CONF_TYPE, CONF_URL +from homeassistant.const import CONF_DEVICE_ID, CONF_MAC, CONF_TYPE, CONF_URL from homeassistant.core import HomeAssistant from tests.common import MockConfigEntry -MOCK_DEVICE_BASE_URL = "http://192.88.99.4" -MOCK_DEVICE_LOCATION = MOCK_DEVICE_BASE_URL + "/dmr_description.xml" +MOCK_DEVICE_HOST_ADDR = "198.51.100.4" +MOCK_DEVICE_LOCATION = f"http://{MOCK_DEVICE_HOST_ADDR}/dmr_description.xml" MOCK_DEVICE_NAME = "Test Renderer Device" MOCK_DEVICE_TYPE = "urn:schemas-upnp-org:device:MediaRenderer:1" MOCK_DEVICE_UDN = "uuid:7cc6da13-7f5d-4ace-9729-58b275c52f1e" MOCK_DEVICE_USN = f"{MOCK_DEVICE_UDN}::{MOCK_DEVICE_TYPE}" +MOCK_MAC_ADDRESS = "ab:cd:ef:01:02:03" -LOCAL_IP = "192.88.99.1" -EVENT_CALLBACK_URL = "http://192.88.99.1/notify" +LOCAL_IP = "198.51.100.1" +EVENT_CALLBACK_URL = "http://198.51.100.1/notify" -NEW_DEVICE_LOCATION = "http://192.88.99.7" + "/dmr_description.xml" +NEW_DEVICE_LOCATION = "http://198.51.100.7" + "/dmr_description.xml" @pytest.fixture @@ -80,6 +81,24 @@ def domain_data_mock(hass: HomeAssistant) -> Iterable[Mock]: @pytest.fixture def config_entry_mock() -> MockConfigEntry: """Mock a config entry for this platform.""" + mock_entry = MockConfigEntry( + unique_id=MOCK_DEVICE_UDN, + domain=DLNA_DOMAIN, + data={ + CONF_URL: MOCK_DEVICE_LOCATION, + CONF_DEVICE_ID: MOCK_DEVICE_UDN, + CONF_TYPE: MOCK_DEVICE_TYPE, + CONF_MAC: MOCK_MAC_ADDRESS, + }, + title=MOCK_DEVICE_NAME, + options={}, + ) + return mock_entry + + +@pytest.fixture +def config_entry_mock_no_mac() -> MockConfigEntry: + """Mock a config entry that does not already contain a MAC address.""" mock_entry = MockConfigEntry( unique_id=MOCK_DEVICE_UDN, domain=DLNA_DOMAIN, @@ -105,7 +124,7 @@ def dmr_device_mock(domain_data_mock: Mock) -> Iterable[Mock]: device.profile_device = ( domain_data_mock.upnp_factory.async_create_device.return_value ) - device.media_image_url = "http://192.88.99.20:8200/AlbumArt/2624-17620.jpg" + device.media_image_url = "http://198.51.100.20:8200/AlbumArt/2624-17620.jpg" device.udn = "device_udn" device.manufacturer = "device_manufacturer" device.model_name = "device_model_name" diff --git a/tests/components/dlna_dmr/test_config_flow.py b/tests/components/dlna_dmr/test_config_flow.py index 8035b7ee822..c3251cd31a2 100644 --- a/tests/components/dlna_dmr/test_config_flow.py +++ b/tests/components/dlna_dmr/test_config_flow.py @@ -1,8 +1,9 @@ """Test the DLNA config flow.""" from __future__ import annotations +from collections.abc import Iterable import dataclasses -from unittest.mock import Mock +from unittest.mock import Mock, patch from async_upnp_client.client import UpnpDevice from async_upnp_client.exceptions import UpnpError @@ -17,14 +18,16 @@ from homeassistant.components.dlna_dmr.const import ( CONF_POLL_AVAILABILITY, DOMAIN as DLNA_DOMAIN, ) -from homeassistant.const import CONF_DEVICE_ID, CONF_HOST, CONF_TYPE, CONF_URL +from homeassistant.const import CONF_DEVICE_ID, CONF_HOST, CONF_MAC, CONF_TYPE, CONF_URL from homeassistant.core import HomeAssistant from .conftest import ( + MOCK_DEVICE_HOST_ADDR, MOCK_DEVICE_LOCATION, MOCK_DEVICE_NAME, MOCK_DEVICE_TYPE, MOCK_DEVICE_UDN, + MOCK_MAC_ADDRESS, NEW_DEVICE_LOCATION, ) @@ -37,6 +40,8 @@ pytestmark = [ ] WRONG_DEVICE_TYPE = "urn:schemas-upnp-org:device:InternetGatewayDevice:1" +CHANGED_DEVICE_LOCATION = "http://198.51.100.55/dmr_description.xml" +CHANGED_DEVICE_UDN = "uuid:7cc6da13-7f5d-4ace-9729-badbadbadbad" MOCK_ROOT_DEVICE_UDN = "ROOT_DEVICE" @@ -45,6 +50,7 @@ MOCK_DISCOVERY = ssdp.SsdpServiceInfo( ssdp_location=MOCK_DEVICE_LOCATION, ssdp_udn=MOCK_DEVICE_UDN, ssdp_st=MOCK_DEVICE_TYPE, + ssdp_headers={"_host": MOCK_DEVICE_HOST_ADDR}, upnp={ ssdp.ATTR_UPNP_UDN: MOCK_ROOT_DEVICE_UDN, ssdp.ATTR_UPNP_DEVICE_TYPE: MOCK_DEVICE_TYPE, @@ -79,6 +85,16 @@ MOCK_DISCOVERY = ssdp.SsdpServiceInfo( ) +@pytest.fixture(autouse=True) +def mock_get_mac_address() -> Iterable[Mock]: + """Mock the get_mac_address function to prevent network access and assist tests.""" + with patch( + "homeassistant.components.dlna_dmr.config_flow.get_mac_address", autospec=True + ) as gma_mock: + gma_mock.return_value = MOCK_MAC_ADDRESS + yield gma_mock + + async def test_user_flow_undiscovered_manual(hass: HomeAssistant) -> None: """Test user-init'd flow, no discovered devices, user entering a valid URL.""" result = await hass.config_entries.flow.async_init( @@ -98,6 +114,7 @@ async def test_user_flow_undiscovered_manual(hass: HomeAssistant) -> None: CONF_URL: MOCK_DEVICE_LOCATION, CONF_DEVICE_ID: MOCK_DEVICE_UDN, CONF_TYPE: MOCK_DEVICE_TYPE, + CONF_MAC: MOCK_MAC_ADDRESS, } assert result["options"] == {CONF_POLL_AVAILABILITY: True} @@ -140,6 +157,7 @@ async def test_user_flow_discovered_manual( CONF_URL: MOCK_DEVICE_LOCATION, CONF_DEVICE_ID: MOCK_DEVICE_UDN, CONF_TYPE: MOCK_DEVICE_TYPE, + CONF_MAC: MOCK_MAC_ADDRESS, } assert result["options"] == {CONF_POLL_AVAILABILITY: True} @@ -172,6 +190,7 @@ async def test_user_flow_selected(hass: HomeAssistant, ssdp_scanner_mock: Mock) CONF_URL: MOCK_DEVICE_LOCATION, CONF_DEVICE_ID: MOCK_DEVICE_UDN, CONF_TYPE: MOCK_DEVICE_TYPE, + CONF_MAC: MOCK_MAC_ADDRESS, } assert result["options"] == {} @@ -235,6 +254,7 @@ async def test_user_flow_embedded_st( CONF_URL: MOCK_DEVICE_LOCATION, CONF_DEVICE_ID: MOCK_DEVICE_UDN, CONF_TYPE: MOCK_DEVICE_TYPE, + CONF_MAC: MOCK_MAC_ADDRESS, } assert result["options"] == {CONF_POLL_AVAILABILITY: True} @@ -285,6 +305,7 @@ async def test_ssdp_flow_success(hass: HomeAssistant) -> None: CONF_URL: MOCK_DEVICE_LOCATION, CONF_DEVICE_ID: MOCK_DEVICE_UDN, CONF_TYPE: MOCK_DEVICE_TYPE, + CONF_MAC: MOCK_MAC_ADDRESS, } assert result["options"] == {} @@ -318,6 +339,7 @@ async def test_ssdp_flow_unavailable( CONF_URL: MOCK_DEVICE_LOCATION, CONF_DEVICE_ID: MOCK_DEVICE_UDN, CONF_TYPE: MOCK_DEVICE_TYPE, + CONF_MAC: MOCK_MAC_ADDRESS, } assert result["options"] == {} @@ -348,10 +370,80 @@ async def test_ssdp_flow_existing( async def test_ssdp_flow_duplicate_location( - hass: HomeAssistant, config_entry_mock: MockConfigEntry + hass: HomeAssistant, config_entry_mock: MockConfigEntry, mock_get_mac_address: Mock ) -> None: """Test that discovery of device with URL matching existing entry gets aborted.""" + # Prevent matching based on MAC address + mock_get_mac_address.return_value = None config_entry_mock.add_to_hass(hass) + + # New discovery with different UDN but same location + discovery = dataclasses.replace(MOCK_DISCOVERY, ssdp_udn=CHANGED_DEVICE_UDN) + result = await hass.config_entries.flow.async_init( + DLNA_DOMAIN, + context={"source": config_entries.SOURCE_SSDP}, + data=discovery, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT + assert result["reason"] == "already_configured" + assert config_entry_mock.data[CONF_URL] == MOCK_DEVICE_LOCATION + + +async def test_ssdp_duplicate_mac_ignored_entry( + hass: HomeAssistant, config_entry_mock: MockConfigEntry +) -> None: + """Test SSDP with different UDN but matching MAC for ignored config entry is ignored.""" + # Add an ignored entry + config_entry_mock.source = config_entries.SOURCE_IGNORE + config_entry_mock.add_to_hass(hass) + + # Prevent matching based on location or UDN + discovery = dataclasses.replace( + MOCK_DISCOVERY, + ssdp_location=CHANGED_DEVICE_LOCATION, + ssdp_udn=CHANGED_DEVICE_UDN, + ) + + # SSDP discovery should be aborted + result = await hass.config_entries.flow.async_init( + DLNA_DOMAIN, + context={"source": config_entries.SOURCE_SSDP}, + data=discovery, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT + assert result["reason"] == "already_configured" + + +async def test_ssdp_duplicate_mac_configured_entry( + hass: HomeAssistant, config_entry_mock: MockConfigEntry +) -> None: + """Test SSDP with different UDN but matching MAC for existing entry is ignored.""" + config_entry_mock.add_to_hass(hass) + + # Prevent matching based on location or UDN + discovery = dataclasses.replace( + MOCK_DISCOVERY, + ssdp_location=CHANGED_DEVICE_LOCATION, + ssdp_udn=CHANGED_DEVICE_UDN, + ) + + # SSDP discovery should be aborted + result = await hass.config_entries.flow.async_init( + DLNA_DOMAIN, + context={"source": config_entries.SOURCE_SSDP}, + data=discovery, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT + assert result["reason"] == "already_configured" + + +async def test_ssdp_add_mac( + hass: HomeAssistant, config_entry_mock_no_mac: MockConfigEntry +) -> None: + """Test adding of MAC to existing entry that didn't have one.""" + config_entry_mock_no_mac.add_to_hass(hass) + + # Start a discovery that adds the MAC address (due to auto-use mock_get_mac_address) result = await hass.config_entries.flow.async_init( DLNA_DOMAIN, context={"source": config_entries.SOURCE_SSDP}, @@ -359,7 +451,31 @@ async def test_ssdp_flow_duplicate_location( ) assert result["type"] == data_entry_flow.FlowResultType.ABORT assert result["reason"] == "already_configured" - assert config_entry_mock.data[CONF_URL] == MOCK_DEVICE_LOCATION + await hass.async_block_till_done() + + # Config entry should be updated to have a MAC address + assert config_entry_mock_no_mac.data[CONF_MAC] == MOCK_MAC_ADDRESS + + +async def test_ssdp_dont_remove_mac( + hass: HomeAssistant, config_entry_mock: MockConfigEntry +) -> None: + """SSDP with failure to resolve MAC should not remove MAC from config entry.""" + config_entry_mock.add_to_hass(hass) + + # Start a discovery that fails when resolving the MAC + mock_get_mac_address.return_value = None + result = await hass.config_entries.flow.async_init( + DLNA_DOMAIN, + context={"source": config_entries.SOURCE_SSDP}, + data=MOCK_DISCOVERY, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT + assert result["reason"] == "already_configured" + await hass.async_block_till_done() + + # Config entry should still have a MAC address + assert config_entry_mock.data[CONF_MAC] == MOCK_MAC_ADDRESS async def test_ssdp_flow_upnp_udn( @@ -497,9 +613,62 @@ async def test_ssdp_ignore_device(hass: HomeAssistant) -> None: assert result["reason"] == "alternative_integration" +async def test_ignore_flow(hass: HomeAssistant, ssdp_scanner_mock: Mock) -> None: + """Test ignoring an SSDP discovery fills in config entry data from SSDP.""" + # Device found via SSDP, matching the 2nd device type tried + ssdp_scanner_mock.async_get_discovery_info_by_udn_st.side_effect = [ + None, + MOCK_DISCOVERY, + None, + None, + None, + ] + + result = await hass.config_entries.flow.async_init( + DLNA_DOMAIN, + context={"source": config_entries.SOURCE_IGNORE}, + data={"unique_id": MOCK_DEVICE_UDN, "title": MOCK_DEVICE_NAME}, + ) + await hass.async_block_till_done() + + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result["title"] == MOCK_DEVICE_NAME + assert result["data"] == { + CONF_URL: MOCK_DEVICE_LOCATION, + CONF_DEVICE_ID: MOCK_DEVICE_UDN, + CONF_TYPE: MOCK_DEVICE_TYPE, + CONF_MAC: MOCK_MAC_ADDRESS, + } + + +async def test_ignore_flow_no_ssdp( + hass: HomeAssistant, ssdp_scanner_mock: Mock +) -> None: + """Test ignoring a flow without SSDP info still creates a config entry.""" + # Nothing found from SSDP + ssdp_scanner_mock.async_get_discovery_info_by_udn_st.return_value = None + + result = await hass.config_entries.flow.async_init( + DLNA_DOMAIN, + context={"source": config_entries.SOURCE_IGNORE}, + data={"unique_id": MOCK_DEVICE_UDN, "title": MOCK_DEVICE_NAME}, + ) + await hass.async_block_till_done() + + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result["title"] == MOCK_DEVICE_NAME + assert result["data"] == { + CONF_URL: None, + CONF_DEVICE_ID: MOCK_DEVICE_UDN, + CONF_TYPE: None, + CONF_MAC: None, + } + + async def test_unignore_flow(hass: HomeAssistant, ssdp_scanner_mock: Mock) -> None: """Test a config flow started by unignoring a device.""" - # Create ignored entry + # Create ignored entry (with no extra info from SSDP) + ssdp_scanner_mock.async_get_discovery_info_by_udn_st.return_value = None result = await hass.config_entries.flow.async_init( DLNA_DOMAIN, context={"source": config_entries.SOURCE_IGNORE}, @@ -509,7 +678,6 @@ async def test_unignore_flow(hass: HomeAssistant, ssdp_scanner_mock: Mock) -> No assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY assert result["title"] == MOCK_DEVICE_NAME - assert result["data"] == {} # Device was found via SSDP, matching the 2nd device type tried ssdp_scanner_mock.async_get_discovery_info_by_udn_st.side_effect = [ @@ -540,6 +708,7 @@ async def test_unignore_flow(hass: HomeAssistant, ssdp_scanner_mock: Mock) -> No CONF_URL: MOCK_DEVICE_LOCATION, CONF_DEVICE_ID: MOCK_DEVICE_UDN, CONF_TYPE: MOCK_DEVICE_TYPE, + CONF_MAC: MOCK_MAC_ADDRESS, } assert result["options"] == {} @@ -551,7 +720,8 @@ async def test_unignore_flow_offline( hass: HomeAssistant, ssdp_scanner_mock: Mock ) -> None: """Test a config flow started by unignoring a device, but the device is offline.""" - # Create ignored entry + # Create ignored entry (with no extra info from SSDP) + ssdp_scanner_mock.async_get_discovery_info_by_udn_st.return_value = None result = await hass.config_entries.flow.async_init( DLNA_DOMAIN, context={"source": config_entries.SOURCE_IGNORE}, @@ -561,7 +731,6 @@ async def test_unignore_flow_offline( assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY assert result["title"] == MOCK_DEVICE_NAME - assert result["data"] == {} # Device is not in the SSDP discoveries (perhaps HA restarted between ignore and unignore) ssdp_scanner_mock.async_get_discovery_info_by_udn_st.return_value = None @@ -576,6 +745,74 @@ async def test_unignore_flow_offline( assert result["reason"] == "discovery_error" +async def test_get_mac_address_ipv4( + hass: HomeAssistant, mock_get_mac_address: Mock +) -> None: + """Test getting MAC address from IPv4 address for SSDP discovery.""" + # Init'ing the flow should be enough to get the MAC address + result = await hass.config_entries.flow.async_init( + DLNA_DOMAIN, + context={"source": config_entries.SOURCE_SSDP}, + data=MOCK_DISCOVERY, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["step_id"] == "confirm" + + mock_get_mac_address.assert_called_once_with(ip=MOCK_DEVICE_HOST_ADDR) + + +async def test_get_mac_address_ipv6( + hass: HomeAssistant, mock_get_mac_address: Mock +) -> None: + """Test getting MAC address from IPv6 address for SSDP discovery.""" + # Use a scoped link-local IPv6 address for the host + IPV6_HOST_UNSCOPED = "fe80::1ff:fe23:4567:890a" + IPV6_HOST = f"{IPV6_HOST_UNSCOPED}%eth2" + IPV6_DEVICE_LOCATION = f"http://{IPV6_HOST}/dmr_description.xml" + discovery = dataclasses.replace(MOCK_DISCOVERY, ssdp_location=IPV6_DEVICE_LOCATION) + discovery.ssdp_headers = dict(discovery.ssdp_headers) + discovery.ssdp_headers["_host"] = IPV6_HOST + + # Init'ing the flow should be enough to get the MAC address + result = await hass.config_entries.flow.async_init( + DLNA_DOMAIN, + context={"source": config_entries.SOURCE_SSDP}, + data=discovery, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["step_id"] == "confirm" + + # The scope must be removed for get_mac_address to work correctly + mock_get_mac_address.assert_called_once_with(ip6=IPV6_HOST_UNSCOPED) + + +async def test_get_mac_address_host( + hass: HomeAssistant, mock_get_mac_address: Mock +) -> None: + """Test getting MAC address from hostname for manual location entry.""" + # Create device via manual URL entry, so that it must be contacted directly, + # not via the ssdp component. + DEVICE_HOSTNAME = "local-dmr" + DEVICE_LOCATION = f"http://{DEVICE_HOSTNAME}/dmr_description.xml" + + result = await hass.config_entries.flow.async_init( + DLNA_DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={CONF_URL: DEVICE_LOCATION} + ) + assert result["data"] == { + CONF_URL: DEVICE_LOCATION, + CONF_DEVICE_ID: MOCK_DEVICE_UDN, + CONF_TYPE: MOCK_DEVICE_TYPE, + CONF_MAC: MOCK_MAC_ADDRESS, + } + assert result["options"] == {CONF_POLL_AVAILABILITY: True} + await hass.async_block_till_done() + + mock_get_mac_address.assert_called_once_with(hostname=DEVICE_HOSTNAME) + + async def test_options_flow( hass: HomeAssistant, config_entry_mock: MockConfigEntry ) -> None: diff --git a/tests/components/dlna_dmr/test_data.py b/tests/components/dlna_dmr/test_data.py index 5b2c0b1815c..06d5e01558c 100644 --- a/tests/components/dlna_dmr/test_data.py +++ b/tests/components/dlna_dmr/test_data.py @@ -73,7 +73,7 @@ async def test_event_notifier( # Different address should give different notifier listen_addr_3 = EventListenAddr( - "192.88.99.4", 9999, "http://192.88.99.4:9999/notify" + "198.51.100.4", 9999, "http://198.51.100.4:9999/notify" ) event_notifier_3 = await domain_data.async_get_event_notifier(listen_addr_3, hass) assert event_notifier_3 is not None @@ -82,8 +82,8 @@ async def test_event_notifier( # Check that the parameters were passed through to the AiohttpNotifyServer aiohttp_notify_servers_mock.assert_called_with( requester=ANY, - source=("192.88.99.4", 9999), - callback_url="http://192.88.99.4:9999/notify", + source=("198.51.100.4", 9999), + callback_url="http://198.51.100.4:9999/notify", loop=ANY, ) diff --git a/tests/components/dlna_dmr/test_media_player.py b/tests/components/dlna_dmr/test_media_player.py index 0091826db84..e9c6bbfda15 100644 --- a/tests/components/dlna_dmr/test_media_player.py +++ b/tests/components/dlna_dmr/test_media_player.py @@ -40,9 +40,18 @@ from homeassistant.components.media_player import ( const as mp_const, ) from homeassistant.components.media_source import DOMAIN as MS_DOMAIN, PlayMedia -from homeassistant.const import ATTR_ENTITY_ID +from homeassistant.const import ( + ATTR_ENTITY_ID, + CONF_DEVICE_ID, + CONF_MAC, + CONF_TYPE, + CONF_URL, +) from homeassistant.core import HomeAssistant -from homeassistant.helpers.device_registry import async_get as async_get_dr +from homeassistant.helpers.device_registry import ( + CONNECTION_NETWORK_MAC, + async_get as async_get_dr, +) from homeassistant.helpers.entity_component import async_update_entity from homeassistant.helpers.entity_registry import ( async_entries_for_config_entry, @@ -57,6 +66,7 @@ from .conftest import ( MOCK_DEVICE_TYPE, MOCK_DEVICE_UDN, MOCK_DEVICE_USN, + MOCK_MAC_ADDRESS, NEW_DEVICE_LOCATION, ) @@ -269,7 +279,7 @@ async def test_setup_entry_with_options( config_entry_mock.options = MappingProxyType( { CONF_LISTEN_PORT: 2222, - CONF_CALLBACK_URL_OVERRIDE: "http://192.88.99.10/events", + CONF_CALLBACK_URL_OVERRIDE: "http://198.51.100.10/events", CONF_POLL_AVAILABILITY: True, } ) @@ -283,7 +293,7 @@ async def test_setup_entry_with_options( ) # Check event notifiers are acquired with the configured port and callback URL domain_data_mock.async_get_event_notifier.assert_awaited_once_with( - EventListenAddr(LOCAL_IP, 2222, "http://192.88.99.10/events"), hass + EventListenAddr(LOCAL_IP, 2222, "http://198.51.100.10/events"), hass ) # Check UPnP services are subscribed dmr_device_mock.async_subscribe_services.assert_awaited_once_with( @@ -324,6 +334,40 @@ async def test_setup_entry_with_options( assert mock_state.state == ha_const.STATE_UNAVAILABLE +async def test_setup_entry_mac_address( + hass: HomeAssistant, + domain_data_mock: Mock, + config_entry_mock: MockConfigEntry, + ssdp_scanner_mock: Mock, + dmr_device_mock: Mock, +) -> None: + """Entry with a MAC address will set up and set the device registry connection.""" + await setup_mock_component(hass, config_entry_mock) + + # Check the device registry connections for MAC address + dev_reg = async_get_dr(hass) + device = dev_reg.async_get_device(identifiers={(DLNA_DOMAIN, MOCK_DEVICE_UDN)}) + assert device is not None + assert (CONNECTION_NETWORK_MAC, MOCK_MAC_ADDRESS) in device.connections + + +async def test_setup_entry_no_mac_address( + hass: HomeAssistant, + domain_data_mock: Mock, + config_entry_mock_no_mac: MockConfigEntry, + ssdp_scanner_mock: Mock, + dmr_device_mock: Mock, +) -> None: + """Test setting up an entry without a MAC address will succeed.""" + await setup_mock_component(hass, config_entry_mock_no_mac) + + # Check the device registry connections does not include the MAC address + dev_reg = async_get_dr(hass) + device = dev_reg.async_get_device(identifiers={(DLNA_DOMAIN, MOCK_DEVICE_UDN)}) + assert device is not None + assert (CONNECTION_NETWORK_MAC, MOCK_MAC_ADDRESS) not in device.connections + + async def test_event_subscribe_failure( hass: HomeAssistant, config_entry_mock: MockConfigEntry, dmr_device_mock: Mock ) -> None: @@ -630,21 +674,21 @@ async def test_play_media_stopped( { ATTR_ENTITY_ID: mock_entity_id, mp_const.ATTR_MEDIA_CONTENT_TYPE: MediaType.MUSIC, - mp_const.ATTR_MEDIA_CONTENT_ID: "http://192.88.99.20:8200/MediaItems/17621.mp3", + mp_const.ATTR_MEDIA_CONTENT_ID: "http://198.51.100.20:8200/MediaItems/17621.mp3", mp_const.ATTR_MEDIA_ENQUEUE: False, }, blocking=True, ) dmr_device_mock.construct_play_media_metadata.assert_awaited_once_with( - media_url="http://192.88.99.20:8200/MediaItems/17621.mp3", + media_url="http://198.51.100.20:8200/MediaItems/17621.mp3", media_title="Home Assistant", override_upnp_class="object.item.audioItem.musicTrack", meta_data={}, ) dmr_device_mock.async_stop.assert_awaited_once_with() dmr_device_mock.async_set_transport_uri.assert_awaited_once_with( - "http://192.88.99.20:8200/MediaItems/17621.mp3", "Home Assistant", ANY + "http://198.51.100.20:8200/MediaItems/17621.mp3", "Home Assistant", ANY ) dmr_device_mock.async_wait_for_can_play.assert_awaited_once_with() dmr_device_mock.async_play.assert_awaited_once_with() @@ -662,21 +706,21 @@ async def test_play_media_playing( { ATTR_ENTITY_ID: mock_entity_id, mp_const.ATTR_MEDIA_CONTENT_TYPE: MediaType.MUSIC, - mp_const.ATTR_MEDIA_CONTENT_ID: "http://192.88.99.20:8200/MediaItems/17621.mp3", + mp_const.ATTR_MEDIA_CONTENT_ID: "http://198.51.100.20:8200/MediaItems/17621.mp3", mp_const.ATTR_MEDIA_ENQUEUE: False, }, blocking=True, ) dmr_device_mock.construct_play_media_metadata.assert_awaited_once_with( - media_url="http://192.88.99.20:8200/MediaItems/17621.mp3", + media_url="http://198.51.100.20:8200/MediaItems/17621.mp3", media_title="Home Assistant", override_upnp_class="object.item.audioItem.musicTrack", meta_data={}, ) dmr_device_mock.async_stop.assert_not_awaited() dmr_device_mock.async_set_transport_uri.assert_awaited_once_with( - "http://192.88.99.20:8200/MediaItems/17621.mp3", "Home Assistant", ANY + "http://198.51.100.20:8200/MediaItems/17621.mp3", "Home Assistant", ANY ) dmr_device_mock.async_wait_for_can_play.assert_not_awaited() dmr_device_mock.async_play.assert_not_awaited() @@ -695,7 +739,7 @@ async def test_play_media_no_autoplay( { ATTR_ENTITY_ID: mock_entity_id, mp_const.ATTR_MEDIA_CONTENT_TYPE: MediaType.MUSIC, - mp_const.ATTR_MEDIA_CONTENT_ID: "http://192.88.99.20:8200/MediaItems/17621.mp3", + mp_const.ATTR_MEDIA_CONTENT_ID: "http://198.51.100.20:8200/MediaItems/17621.mp3", mp_const.ATTR_MEDIA_ENQUEUE: False, mp_const.ATTR_MEDIA_EXTRA: {"autoplay": False}, }, @@ -703,14 +747,14 @@ async def test_play_media_no_autoplay( ) dmr_device_mock.construct_play_media_metadata.assert_awaited_once_with( - media_url="http://192.88.99.20:8200/MediaItems/17621.mp3", + media_url="http://198.51.100.20:8200/MediaItems/17621.mp3", media_title="Home Assistant", override_upnp_class="object.item.audioItem.musicTrack", meta_data={}, ) dmr_device_mock.async_stop.assert_awaited_once_with() dmr_device_mock.async_set_transport_uri.assert_awaited_once_with( - "http://192.88.99.20:8200/MediaItems/17621.mp3", "Home Assistant", ANY + "http://198.51.100.20:8200/MediaItems/17621.mp3", "Home Assistant", ANY ) dmr_device_mock.async_wait_for_can_play.assert_not_awaited() dmr_device_mock.async_play.assert_not_awaited() @@ -726,11 +770,11 @@ async def test_play_media_metadata( { ATTR_ENTITY_ID: mock_entity_id, mp_const.ATTR_MEDIA_CONTENT_TYPE: MediaType.MUSIC, - mp_const.ATTR_MEDIA_CONTENT_ID: "http://192.88.99.20:8200/MediaItems/17621.mp3", + mp_const.ATTR_MEDIA_CONTENT_ID: "http://198.51.100.20:8200/MediaItems/17621.mp3", mp_const.ATTR_MEDIA_ENQUEUE: False, mp_const.ATTR_MEDIA_EXTRA: { "title": "Mock song", - "thumb": "http://192.88.99.20:8200/MediaItems/17621.jpg", + "thumb": "http://198.51.100.20:8200/MediaItems/17621.jpg", "metadata": {"artist": "Mock artist", "album": "Mock album"}, }, }, @@ -738,13 +782,13 @@ async def test_play_media_metadata( ) dmr_device_mock.construct_play_media_metadata.assert_awaited_once_with( - media_url="http://192.88.99.20:8200/MediaItems/17621.mp3", + media_url="http://198.51.100.20:8200/MediaItems/17621.mp3", media_title="Mock song", override_upnp_class="object.item.audioItem.musicTrack", meta_data={ "artist": "Mock artist", "album": "Mock album", - "album_art_uri": "http://192.88.99.20:8200/MediaItems/17621.jpg", + "album_art_uri": "http://198.51.100.20:8200/MediaItems/17621.jpg", }, ) @@ -756,7 +800,7 @@ async def test_play_media_metadata( { ATTR_ENTITY_ID: mock_entity_id, mp_const.ATTR_MEDIA_CONTENT_TYPE: MediaType.TVSHOW, - mp_const.ATTR_MEDIA_CONTENT_ID: "http://192.88.99.20:8200/MediaItems/123.mkv", + mp_const.ATTR_MEDIA_CONTENT_ID: "http://198.51.100.20:8200/MediaItems/123.mkv", mp_const.ATTR_MEDIA_ENQUEUE: False, mp_const.ATTR_MEDIA_EXTRA: { "title": "Mock show", @@ -767,7 +811,7 @@ async def test_play_media_metadata( ) dmr_device_mock.construct_play_media_metadata.assert_awaited_once_with( - media_url="http://192.88.99.20:8200/MediaItems/123.mkv", + media_url="http://198.51.100.20:8200/MediaItems/123.mkv", media_title="Mock show", override_upnp_class="object.item.videoItem.videoBroadcast", meta_data={"episodeSeason": 1, "episodeNumber": 12}, @@ -1236,7 +1280,7 @@ async def test_unavailable_device( mp_const.SERVICE_PLAY_MEDIA, { mp_const.ATTR_MEDIA_CONTENT_TYPE: MediaType.MUSIC, - mp_const.ATTR_MEDIA_CONTENT_ID: "http://192.88.99.20:8200/MediaItems/17621.mp3", + mp_const.ATTR_MEDIA_CONTENT_ID: "http://198.51.100.20:8200/MediaItems/17621.mp3", mp_const.ATTR_MEDIA_ENQUEUE: False, }, ), @@ -2146,3 +2190,39 @@ async def test_config_update_poll_availability( mock_state = hass.states.get(mock_entity_id) assert mock_state is not None assert mock_state.state == MediaPlayerState.IDLE + + +async def test_config_update_mac_address( + hass: HomeAssistant, + domain_data_mock: Mock, + config_entry_mock_no_mac: MockConfigEntry, + ssdp_scanner_mock: Mock, + dmr_device_mock: Mock, +) -> None: + """Test discovering the MAC address post-setup will update the device registry.""" + await setup_mock_component(hass, config_entry_mock_no_mac) + + domain_data_mock.upnp_factory.async_create_device.reset_mock() + + # Check the device registry connections does not include the MAC address + dev_reg = async_get_dr(hass) + device = dev_reg.async_get_device(identifiers={(DLNA_DOMAIN, MOCK_DEVICE_UDN)}) + assert device is not None + assert (CONNECTION_NETWORK_MAC, MOCK_MAC_ADDRESS) not in device.connections + + # MAC address discovered and set by config flow + hass.config_entries.async_update_entry( + config_entry_mock_no_mac, + data={ + CONF_URL: MOCK_DEVICE_LOCATION, + CONF_DEVICE_ID: MOCK_DEVICE_UDN, + CONF_TYPE: MOCK_DEVICE_TYPE, + CONF_MAC: MOCK_MAC_ADDRESS, + }, + ) + await hass.async_block_till_done() + + # Device registry connections should now include the MAC address + device = dev_reg.async_get_device(identifiers={(DLNA_DOMAIN, MOCK_DEVICE_UDN)}) + assert device is not None + assert (CONNECTION_NETWORK_MAC, MOCK_MAC_ADDRESS) in device.connections diff --git a/tests/components/dsmr/test_sensor.py b/tests/components/dsmr/test_sensor.py index e8cf763f98e..ee0ffa5db5f 100644 --- a/tests/components/dsmr/test_sensor.py +++ b/tests/components/dsmr/test_sensor.py @@ -13,6 +13,7 @@ from unittest.mock import DEFAULT, MagicMock from homeassistant import config_entries from homeassistant.components.sensor import ( + ATTR_OPTIONS, ATTR_STATE_CLASS, SensorDeviceClass, SensorStateClass, @@ -22,8 +23,10 @@ from homeassistant.const import ( ATTR_ICON, ATTR_UNIT_OF_MEASUREMENT, ENERGY_KILO_WATT_HOUR, + STATE_UNAVAILABLE, STATE_UNKNOWN, VOLUME_CUBIC_METERS, + UnitOfPower, ) from homeassistant.helpers import entity_registry as er @@ -55,13 +58,13 @@ async def test_default_setup(hass, dsmr_connection_fixture): telegram = { CURRENT_ELECTRICITY_USAGE: CosemObject( - [{"value": Decimal("0.0"), "unit": ENERGY_KILO_WATT_HOUR}] + [{"value": Decimal("0.0"), "unit": UnitOfPower.WATT}] ), ELECTRICITY_ACTIVE_TARIFF: CosemObject([{"value": "0001", "unit": ""}]), GAS_METER_READING: MBusObject( [ {"value": datetime.datetime.fromtimestamp(1551642213)}, - {"value": Decimal(745.695), "unit": "m3"}, + {"value": Decimal(745.695), "unit": VOLUME_CUBIC_METERS}, ] ), } @@ -87,9 +90,9 @@ async def test_default_setup(hass, dsmr_connection_fixture): telegram_callback = connection_factory.call_args_list[0][0][2] - # make sure entities have been created and return 'unknown' state + # make sure entities have been created and return 'unavailable' state power_consumption = hass.states.get("sensor.electricity_meter_power_consumption") - assert power_consumption.state == STATE_UNKNOWN + assert power_consumption.state == STATE_UNAVAILABLE assert ( power_consumption.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.POWER ) @@ -109,15 +112,14 @@ async def test_default_setup(hass, dsmr_connection_fixture): # ensure entities have new state value after incoming telegram power_consumption = hass.states.get("sensor.electricity_meter_power_consumption") assert power_consumption.state == "0.0" - assert ( - power_consumption.attributes.get("unit_of_measurement") == ENERGY_KILO_WATT_HOUR - ) + assert power_consumption.attributes.get("unit_of_measurement") == UnitOfPower.WATT # tariff should be translated in human readable and have no unit active_tariff = hass.states.get("sensor.electricity_meter_active_tariff") assert active_tariff.state == "low" - assert active_tariff.attributes.get(ATTR_DEVICE_CLASS) is None + assert active_tariff.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.ENUM assert active_tariff.attributes.get(ATTR_ICON) == "mdi:flash" + assert active_tariff.attributes.get(ATTR_OPTIONS) == ["low", "normal"] assert active_tariff.attributes.get(ATTR_STATE_CLASS) is None assert active_tariff.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == "" @@ -215,8 +217,9 @@ async def test_v4_meter(hass, dsmr_connection_fixture): # tariff should be translated in human readable and have no unit active_tariff = hass.states.get("sensor.electricity_meter_active_tariff") assert active_tariff.state == "low" - assert active_tariff.attributes.get(ATTR_DEVICE_CLASS) is None + assert active_tariff.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.ENUM assert active_tariff.attributes.get(ATTR_ICON) == "mdi:flash" + assert active_tariff.attributes.get(ATTR_OPTIONS) == ["low", "normal"] assert active_tariff.attributes.get(ATTR_STATE_CLASS) is None assert active_tariff.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == "" @@ -286,8 +289,9 @@ async def test_v5_meter(hass, dsmr_connection_fixture): # tariff should be translated in human readable and have no unit active_tariff = hass.states.get("sensor.electricity_meter_active_tariff") assert active_tariff.state == "low" - assert active_tariff.attributes.get(ATTR_DEVICE_CLASS) is None + assert active_tariff.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.ENUM assert active_tariff.attributes.get(ATTR_ICON) == "mdi:flash" + assert active_tariff.attributes.get(ATTR_OPTIONS) == ["low", "normal"] assert active_tariff.attributes.get(ATTR_STATE_CLASS) is None assert active_tariff.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == "" @@ -440,8 +444,9 @@ async def test_belgian_meter(hass, dsmr_connection_fixture): # tariff should be translated in human readable and have no unit active_tariff = hass.states.get("sensor.electricity_meter_active_tariff") assert active_tariff.state == "normal" - assert active_tariff.attributes.get(ATTR_DEVICE_CLASS) is None + assert active_tariff.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.ENUM assert active_tariff.attributes.get(ATTR_ICON) == "mdi:flash" + assert active_tariff.attributes.get(ATTR_OPTIONS) == ["low", "normal"] assert active_tariff.attributes.get(ATTR_STATE_CLASS) is None assert active_tariff.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == "" @@ -499,8 +504,9 @@ async def test_belgian_meter_low(hass, dsmr_connection_fixture): # tariff should be translated in human readable and have no unit active_tariff = hass.states.get("sensor.electricity_meter_active_tariff") assert active_tariff.state == "low" - assert active_tariff.attributes.get(ATTR_DEVICE_CLASS) is None + assert active_tariff.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.ENUM assert active_tariff.attributes.get(ATTR_ICON) == "mdi:flash" + assert active_tariff.attributes.get(ATTR_OPTIONS) == ["low", "normal"] assert active_tariff.attributes.get(ATTR_STATE_CLASS) is None assert active_tariff.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == "" @@ -778,6 +784,10 @@ async def test_reconnect(hass, dsmr_connection_fixture): assert connection_factory.call_count == 1 + state = hass.states.get("sensor.electricity_meter_power_consumption") + assert state + assert state.state == STATE_UNKNOWN + # indicate disconnect, release wait lock and allow reconnect to happen closed.set() # wait for lock set to resolve diff --git a/tests/components/esphome/conftest.py b/tests/components/esphome/conftest.py index 4252e9d605b..3382e978a19 100644 --- a/tests/components/esphome/conftest.py +++ b/tests/components/esphome/conftest.py @@ -1,5 +1,11 @@ """esphome session fixtures.""" +from __future__ import annotations + +from unittest.mock import AsyncMock, Mock, patch + +from aioesphomeapi import APIClient import pytest +from zeroconf import Zeroconf from homeassistant.components.esphome import CONF_NOISE_PSK, DOMAIN from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT @@ -8,6 +14,11 @@ from homeassistant.core import HomeAssistant from tests.common import MockConfigEntry +@pytest.fixture(autouse=True) +def mock_bluetooth(enable_bluetooth): + """Auto mock bluetooth.""" + + @pytest.fixture(autouse=True) def esphome_mock_async_zeroconf(mock_async_zeroconf): """Auto mock zeroconf.""" @@ -25,7 +36,7 @@ def mock_config_entry() -> MockConfigEntry: CONF_PASSWORD: "pwd", CONF_NOISE_PSK: "12345678123456781234567812345678", }, - unique_id="esphome-device", + unique_id="11:22:33:44:55:aa", ) @@ -40,3 +51,37 @@ async def init_integration( await hass.async_block_till_done() return mock_config_entry + + +@pytest.fixture +def mock_client(): + """Mock APIClient.""" + mock_client = Mock(spec=APIClient) + + def mock_constructor( + address: str, + port: int, + password: str | None, + *, + client_info: str = "aioesphomeapi", + keepalive: float = 15.0, + zeroconf_instance: Zeroconf = None, + noise_psk: str | None = None, + expected_name: str | None = None, + ): + """Fake the client constructor.""" + mock_client.host = address + mock_client.port = port + mock_client.password = password + mock_client.zeroconf_instance = zeroconf_instance + mock_client.noise_psk = noise_psk + return mock_client + + mock_client.side_effect = mock_constructor + mock_client.connect = AsyncMock() + mock_client.disconnect = AsyncMock() + + with patch("homeassistant.components.esphome.APIClient", mock_client), patch( + "homeassistant.components.esphome.config_flow.APIClient", mock_client + ): + yield mock_client diff --git a/tests/components/esphome/test_config_flow.py b/tests/components/esphome/test_config_flow.py index efeb2d376cf..4a40f4ec4d7 100644 --- a/tests/components/esphome/test_config_flow.py +++ b/tests/components/esphome/test_config_flow.py @@ -1,9 +1,9 @@ """Test config flow.""" -from collections import namedtuple from unittest.mock import AsyncMock, MagicMock, patch from aioesphomeapi import ( APIConnectionError, + DeviceInfo, InvalidAuthAPIError, InvalidEncryptionKeyAPIError, RequiresEncryptionAPIError, @@ -19,34 +19,10 @@ from homeassistant.data_entry_flow import FlowResultType from tests.common import MockConfigEntry -MockDeviceInfo = namedtuple("DeviceInfo", ["uses_password", "name"]) VALID_NOISE_PSK = "bOFFzzvfpg5DB94DuBGLXD/hMnhpDKgP9UQyBulwWVU=" INVALID_NOISE_PSK = "lSYBYEjQI1bVL8s2Vask4YytGMj1f1epNtmoim2yuTM=" -@pytest.fixture -def mock_client(): - """Mock APIClient.""" - with patch("homeassistant.components.esphome.config_flow.APIClient") as mock_client: - - def mock_constructor( - host, port, password, zeroconf_instance=None, noise_psk=None - ): - """Fake the client constructor.""" - mock_client.host = host - mock_client.port = port - mock_client.password = password - mock_client.zeroconf_instance = zeroconf_instance - mock_client.noise_psk = noise_psk - return mock_client - - mock_client.side_effect = mock_constructor - mock_client.connect = AsyncMock() - mock_client.disconnect = AsyncMock() - - yield mock_client - - @pytest.fixture(autouse=True) def mock_setup_entry(): """Mock setting up a config entry.""" @@ -65,7 +41,11 @@ async def test_user_connection_works(hass, mock_client, mock_zeroconf): assert result["type"] == FlowResultType.FORM assert result["step_id"] == "user" - mock_client.device_info = AsyncMock(return_value=MockDeviceInfo(False, "test")) + mock_client.device_info = AsyncMock( + return_value=DeviceInfo( + uses_password=False, name="test", mac_address="mock-mac" + ) + ) result = await hass.config_entries.flow.async_init( "esphome", @@ -81,7 +61,7 @@ async def test_user_connection_works(hass, mock_client, mock_zeroconf): CONF_NOISE_PSK: "", } assert result["title"] == "test" - assert result["result"].unique_id == "test" + assert result["result"].unique_id == "mock-mac" assert len(mock_client.connect.mock_calls) == 1 assert len(mock_client.device_info.mock_calls) == 1 @@ -97,7 +77,7 @@ async def test_user_connection_updates_host(hass, mock_client, mock_zeroconf): entry = MockConfigEntry( domain=DOMAIN, data={CONF_HOST: "test.local", CONF_PORT: 6053, CONF_PASSWORD: ""}, - unique_id="test", + unique_id="mock-mac", ) entry.add_to_hass(hass) result = await hass.config_entries.flow.async_init( @@ -109,7 +89,11 @@ async def test_user_connection_updates_host(hass, mock_client, mock_zeroconf): assert result["type"] == FlowResultType.FORM assert result["step_id"] == "user" - mock_client.device_info = AsyncMock(return_value=MockDeviceInfo(False, "test")) + mock_client.device_info = AsyncMock( + return_value=DeviceInfo( + uses_password=False, name="test", mac_address="mock-mac" + ) + ) result = await hass.config_entries.flow.async_init( "esphome", @@ -165,7 +149,9 @@ async def test_user_connection_error(hass, mock_client, mock_zeroconf): async def test_user_with_password(hass, mock_client, mock_zeroconf): """Test user step with password.""" - mock_client.device_info = AsyncMock(return_value=MockDeviceInfo(True, "test")) + mock_client.device_info = AsyncMock( + return_value=DeviceInfo(uses_password=True, name="test") + ) result = await hass.config_entries.flow.async_init( "esphome", @@ -192,7 +178,9 @@ async def test_user_with_password(hass, mock_client, mock_zeroconf): async def test_user_invalid_password(hass, mock_client, mock_zeroconf): """Test user step with invalid password.""" - mock_client.device_info = AsyncMock(return_value=MockDeviceInfo(True, "test")) + mock_client.device_info = AsyncMock( + return_value=DeviceInfo(uses_password=True, name="test") + ) result = await hass.config_entries.flow.async_init( "esphome", @@ -216,7 +204,9 @@ async def test_user_invalid_password(hass, mock_client, mock_zeroconf): async def test_login_connection_error(hass, mock_client, mock_zeroconf): """Test user step with connection error on login attempt.""" - mock_client.device_info = AsyncMock(return_value=MockDeviceInfo(True, "test")) + mock_client.device_info = AsyncMock( + return_value=DeviceInfo(uses_password=True, name="test") + ) result = await hass.config_entries.flow.async_init( "esphome", @@ -240,7 +230,11 @@ async def test_login_connection_error(hass, mock_client, mock_zeroconf): async def test_discovery_initiation(hass, mock_client, mock_zeroconf): """Test discovery importing works.""" - mock_client.device_info = AsyncMock(return_value=MockDeviceInfo(False, "test8266")) + mock_client.device_info = AsyncMock( + return_value=DeviceInfo( + uses_password=False, name="test8266", mac_address="11:22:33:44:55:aa" + ) + ) service_info = zeroconf.ZeroconfServiceInfo( host="192.168.43.183", @@ -248,7 +242,9 @@ async def test_discovery_initiation(hass, mock_client, mock_zeroconf): hostname="test8266.local.", name="mock_name", port=6053, - properties={}, + properties={ + "mac": "1122334455aa", + }, type="mock_type", ) flow = await hass.config_entries.flow.async_init( @@ -265,18 +261,11 @@ async def test_discovery_initiation(hass, mock_client, mock_zeroconf): assert result["data"][CONF_PORT] == 6053 assert result["result"] - assert result["result"].unique_id == "test8266" + assert result["result"].unique_id == "11:22:33:44:55:aa" -async def test_discovery_already_configured_hostname(hass, mock_client): - """Test discovery aborts if already configured via hostname.""" - entry = MockConfigEntry( - domain=DOMAIN, - data={CONF_HOST: "test8266.local", CONF_PORT: 6053, CONF_PASSWORD: ""}, - ) - - entry.add_to_hass(hass) - +async def test_discovery_no_mac(hass, mock_client, mock_zeroconf): + """Test discovery aborted if old ESPHome without mac in zeroconf.""" service_info = zeroconf.ZeroconfServiceInfo( host="192.168.43.183", addresses=["192.168.43.183"], @@ -286,21 +275,19 @@ async def test_discovery_already_configured_hostname(hass, mock_client): properties={}, type="mock_type", ) - result = await hass.config_entries.flow.async_init( + flow = await hass.config_entries.flow.async_init( "esphome", context={"source": config_entries.SOURCE_ZEROCONF}, data=service_info ) - - assert result["type"] == FlowResultType.ABORT - assert result["reason"] == "already_configured" - - assert entry.unique_id == "test8266" + assert flow["type"] == FlowResultType.ABORT + assert flow["reason"] == "mdns_missing_mac" -async def test_discovery_already_configured_ip(hass, mock_client): - """Test discovery aborts if already configured via static IP.""" +async def test_discovery_already_configured(hass, mock_client): + """Test discovery aborts if already configured via hostname.""" entry = MockConfigEntry( domain=DOMAIN, - data={CONF_HOST: "192.168.43.183", CONF_PORT: 6053, CONF_PASSWORD: ""}, + data={CONF_HOST: "test8266.local", CONF_PORT: 6053, CONF_PASSWORD: ""}, + unique_id="11:22:33:44:55:aa", ) entry.add_to_hass(hass) @@ -311,7 +298,7 @@ async def test_discovery_already_configured_ip(hass, mock_client): hostname="test8266.local.", name="mock_name", port=6053, - properties={"address": "192.168.43.183"}, + properties={"mac": "1122334455aa"}, type="mock_type", ) result = await hass.config_entries.flow.async_init( @@ -321,41 +308,6 @@ async def test_discovery_already_configured_ip(hass, mock_client): assert result["type"] == FlowResultType.ABORT assert result["reason"] == "already_configured" - assert entry.unique_id == "test8266" - - -async def test_discovery_already_configured_name(hass, mock_client): - """Test discovery aborts if already configured via name.""" - entry = MockConfigEntry( - domain=DOMAIN, - data={CONF_HOST: "192.168.43.183", CONF_PORT: 6053, CONF_PASSWORD: ""}, - ) - entry.add_to_hass(hass) - - mock_entry_data = MagicMock() - mock_entry_data.device_info.name = "test8266" - domain_data = DomainData.get(hass) - domain_data.set_entry_data(entry, mock_entry_data) - - service_info = zeroconf.ZeroconfServiceInfo( - host="192.168.43.184", - addresses=["192.168.43.184"], - hostname="test8266.local.", - name="mock_name", - port=6053, - properties={"address": "test8266.local"}, - type="mock_type", - ) - result = await hass.config_entries.flow.async_init( - "esphome", context={"source": config_entries.SOURCE_ZEROCONF}, data=service_info - ) - - assert result["type"] == FlowResultType.ABORT - assert result["reason"] == "already_configured" - - assert entry.unique_id == "test8266" - assert entry.data[CONF_HOST] == "192.168.43.184" - async def test_discovery_duplicate_data(hass, mock_client): """Test discovery aborts if same mDNS packet arrives.""" @@ -365,11 +317,13 @@ async def test_discovery_duplicate_data(hass, mock_client): hostname="test8266.local.", name="mock_name", port=6053, - properties={"address": "test8266.local"}, + properties={"address": "test8266.local", "mac": "1122334455aa"}, type="mock_type", ) - mock_client.device_info = AsyncMock(return_value=MockDeviceInfo(False, "test8266")) + mock_client.device_info = AsyncMock( + return_value=DeviceInfo(uses_password=False, name="test8266") + ) result = await hass.config_entries.flow.async_init( "esphome", data=service_info, context={"source": config_entries.SOURCE_ZEROCONF} @@ -389,6 +343,7 @@ async def test_discovery_updates_unique_id(hass, mock_client): entry = MockConfigEntry( domain=DOMAIN, data={CONF_HOST: "192.168.43.183", CONF_PORT: 6053, CONF_PASSWORD: ""}, + unique_id="11:22:33:44:55:aa", ) entry.add_to_hass(hass) @@ -399,7 +354,7 @@ async def test_discovery_updates_unique_id(hass, mock_client): hostname="test8266.local.", name="mock_name", port=6053, - properties={"address": "test8266.local"}, + properties={"address": "test8266.local", "mac": "1122334455aa"}, type="mock_type", ) result = await hass.config_entries.flow.async_init( @@ -409,7 +364,7 @@ async def test_discovery_updates_unique_id(hass, mock_client): assert result["type"] == FlowResultType.ABORT assert result["reason"] == "already_configured" - assert entry.unique_id == "test8266" + assert entry.unique_id == "11:22:33:44:55:aa" async def test_user_requires_psk(hass, mock_client, mock_zeroconf): @@ -445,7 +400,9 @@ async def test_encryption_key_valid_psk(hass, mock_client, mock_zeroconf): assert result["type"] == FlowResultType.FORM assert result["step_id"] == "encryption_key" - mock_client.device_info = AsyncMock(return_value=MockDeviceInfo(False, "test")) + mock_client.device_info = AsyncMock( + return_value=DeviceInfo(uses_password=False, name="test") + ) result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={CONF_NOISE_PSK: VALID_NOISE_PSK} ) @@ -522,7 +479,9 @@ async def test_reauth_confirm_valid(hass, mock_client, mock_zeroconf): }, ) - mock_client.device_info = AsyncMock(return_value=MockDeviceInfo(False, "test")) + mock_client.device_info = AsyncMock( + return_value=DeviceInfo(uses_password=False, name="test") + ) result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={CONF_NOISE_PSK: VALID_NOISE_PSK} ) @@ -559,7 +518,9 @@ async def test_reauth_confirm_invalid(hass, mock_client, mock_zeroconf): assert result["errors"] assert result["errors"]["base"] == "invalid_psk" - mock_client.device_info = AsyncMock(return_value=MockDeviceInfo(False, "test")) + mock_client.device_info = AsyncMock( + return_value=DeviceInfo(uses_password=False, name="test") + ) result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={CONF_NOISE_PSK: VALID_NOISE_PSK} ) @@ -597,7 +558,9 @@ async def test_reauth_confirm_invalid_with_unique_id(hass, mock_client, mock_zer assert result["errors"] assert result["errors"]["base"] == "invalid_psk" - mock_client.device_info = AsyncMock(return_value=MockDeviceInfo(False, "test")) + mock_client.device_info = AsyncMock( + return_value=DeviceInfo(uses_password=False, name="test") + ) result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={CONF_NOISE_PSK: VALID_NOISE_PSK} ) @@ -612,18 +575,14 @@ async def test_discovery_dhcp_updates_host(hass, mock_client): entry = MockConfigEntry( domain=DOMAIN, data={CONF_HOST: "192.168.43.183", CONF_PORT: 6053, CONF_PASSWORD: ""}, + unique_id="11:22:33:44:55:aa", ) entry.add_to_hass(hass) - mock_entry_data = MagicMock() - mock_entry_data.device_info.name = "test8266" - domain_data = DomainData.get(hass) - domain_data.set_entry_data(entry, mock_entry_data) - service_info = dhcp.DhcpServiceInfo( ip="192.168.43.184", hostname="test8266", - macaddress="00:00:00:00:00:00", + macaddress="1122334455aa", ) result = await hass.config_entries.flow.async_init( "esphome", context={"source": config_entries.SOURCE_DHCP}, data=service_info @@ -632,7 +591,6 @@ async def test_discovery_dhcp_updates_host(hass, mock_client): assert result["type"] == FlowResultType.ABORT assert result["reason"] == "already_configured" - assert entry.unique_id == "test8266" assert entry.data[CONF_HOST] == "192.168.43.184" @@ -661,5 +619,4 @@ async def test_discovery_dhcp_no_changes(hass, mock_client): assert result["type"] == FlowResultType.ABORT assert result["reason"] == "already_configured" - assert entry.unique_id == "test8266" assert entry.data[CONF_HOST] == "192.168.43.183" diff --git a/tests/components/esphome/test_diagnostics.py b/tests/components/esphome/test_diagnostics.py index 522ae0c8345..9f55b83a47c 100644 --- a/tests/components/esphome/test_diagnostics.py +++ b/tests/components/esphome/test_diagnostics.py @@ -27,4 +27,4 @@ async def test_diagnostics( CONF_PASSWORD: "**REDACTED**", CONF_NOISE_PSK: "**REDACTED**", } - assert result["config"]["unique_id"] == "esphome-device" + assert result["config"]["unique_id"] == "11:22:33:44:55:aa" diff --git a/tests/components/esphome/test_init.py b/tests/components/esphome/test_init.py new file mode 100644 index 00000000000..15fe57ffba8 --- /dev/null +++ b/tests/components/esphome/test_init.py @@ -0,0 +1,30 @@ +"""ESPHome set up tests.""" +from unittest.mock import AsyncMock + +from aioesphomeapi import DeviceInfo + +from homeassistant.components.esphome import DOMAIN +from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT + +from tests.common import MockConfigEntry + + +async def test_unique_id_updated_to_mac(hass, mock_client, mock_zeroconf): + """Test we update config entry unique ID to MAC address.""" + entry = MockConfigEntry( + domain=DOMAIN, + data={CONF_HOST: "test.local", CONF_PORT: 6053, CONF_PASSWORD: ""}, + unique_id="mock-config-name", + ) + entry.add_to_hass(hass) + + mock_client.device_info = AsyncMock( + return_value=DeviceInfo( + mac_address="1122334455aa", + ) + ) + + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert entry.unique_id == "11:22:33:44:55:aa" diff --git a/tests/components/file_upload/test_init.py b/tests/components/file_upload/test_init.py index 699fb6f9b84..ff9cca5fe47 100644 --- a/tests/components/file_upload/test_init.py +++ b/tests/components/file_upload/test_init.py @@ -9,7 +9,7 @@ from homeassistant.components import file_upload from homeassistant.core import HomeAssistant from homeassistant.setup import async_setup_component -from tests.components.image import TEST_IMAGE +from tests.components.image_upload import TEST_IMAGE @pytest.fixture diff --git a/tests/components/filter/test_sensor.py b/tests/components/filter/test_sensor.py index b440ac7889b..bb01b5b4c5c 100644 --- a/tests/components/filter/test_sensor.py +++ b/tests/components/filter/test_sensor.py @@ -21,9 +21,11 @@ from homeassistant.components.sensor import ( ) from homeassistant.const import ( ATTR_DEVICE_CLASS, + ATTR_UNIT_OF_MEASUREMENT, SERVICE_RELOAD, STATE_UNAVAILABLE, STATE_UNKNOWN, + UnitOfTemperature, ) import homeassistant.core as ha from homeassistant.helpers import entity_registry as er @@ -263,6 +265,7 @@ async def test_setup(recorder_mock, hass): { "icon": "mdi:test", ATTR_DEVICE_CLASS: SensorDeviceClass.TEMPERATURE, + ATTR_UNIT_OF_MEASUREMENT: UnitOfTemperature.CELSIUS, ATTR_STATE_CLASS: SensorStateClass.TOTAL_INCREASING, }, ) diff --git a/tests/components/flux_led/test_config_flow.py b/tests/components/flux_led/test_config_flow.py index 3f1704f7e8c..76f83ad62ef 100644 --- a/tests/components/flux_led/test_config_flow.py +++ b/tests/components/flux_led/test_config_flow.py @@ -101,7 +101,6 @@ async def test_discovery(hass: HomeAssistant): CONF_REMOTE_ACCESS_ENABLED: True, CONF_REMOTE_ACCESS_HOST: "the.cloud", CONF_REMOTE_ACCESS_PORT: 8816, - CONF_MINOR_VERSION: 0x04, } mock_setup.assert_called_once() mock_setup_entry.assert_called_once() @@ -176,7 +175,6 @@ async def test_discovery_legacy(hass: HomeAssistant): CONF_REMOTE_ACCESS_ENABLED: True, CONF_REMOTE_ACCESS_HOST: "the.cloud", CONF_REMOTE_ACCESS_PORT: 8816, - CONF_MINOR_VERSION: 0x04, } mock_setup.assert_called_once() mock_setup_entry.assert_called_once() @@ -258,7 +256,6 @@ async def test_discovery_with_existing_device_present(hass: HomeAssistant): CONF_REMOTE_ACCESS_ENABLED: True, CONF_REMOTE_ACCESS_HOST: "the.cloud", CONF_REMOTE_ACCESS_PORT: 8816, - CONF_MINOR_VERSION: 0x04, } await hass.async_block_till_done() @@ -334,7 +331,6 @@ async def test_manual_working_discovery(hass: HomeAssistant): CONF_REMOTE_ACCESS_ENABLED: True, CONF_REMOTE_ACCESS_HOST: "the.cloud", CONF_REMOTE_ACCESS_PORT: 8816, - CONF_MINOR_VERSION: 0x04, } # Duplicate @@ -447,7 +443,6 @@ async def test_discovered_by_discovery(hass): CONF_REMOTE_ACCESS_ENABLED: True, CONF_REMOTE_ACCESS_HOST: "the.cloud", CONF_REMOTE_ACCESS_PORT: 8816, - CONF_MINOR_VERSION: 0x04, } assert mock_async_setup.called assert mock_async_setup_entry.called @@ -484,7 +479,6 @@ async def test_discovered_by_dhcp_udp_responds(hass): CONF_REMOTE_ACCESS_ENABLED: True, CONF_REMOTE_ACCESS_HOST: "the.cloud", CONF_REMOTE_ACCESS_PORT: 8816, - CONF_MINOR_VERSION: 0x04, } assert mock_async_setup.called assert mock_async_setup_entry.called diff --git a/tests/components/forecast_solar/conftest.py b/tests/components/forecast_solar/conftest.py index ea6eb40b542..007a6b7d2ae 100644 --- a/tests/components/forecast_solar/conftest.py +++ b/tests/components/forecast_solar/conftest.py @@ -60,7 +60,8 @@ def mock_forecast_solar(hass) -> Generator[None, MagicMock, None]: hass fixture included because it sets the time zone. """ with patch( - "homeassistant.components.forecast_solar.ForecastSolar", autospec=True + "homeassistant.components.forecast_solar.coordinator.ForecastSolar", + autospec=True, ) as forecast_solar_mock: forecast_solar = forecast_solar_mock.return_value now = datetime(2021, 6, 27, 6, 0, tzinfo=dt_util.DEFAULT_TIME_ZONE) diff --git a/tests/components/forecast_solar/test_init.py b/tests/components/forecast_solar/test_init.py index a0a8f802e5a..a7696fe8f53 100644 --- a/tests/components/forecast_solar/test_init.py +++ b/tests/components/forecast_solar/test_init.py @@ -29,7 +29,7 @@ async def test_load_unload_config_entry( @patch( - "homeassistant.components.forecast_solar.ForecastSolar.estimate", + "homeassistant.components.forecast_solar.coordinator.ForecastSolar.estimate", side_effect=ForecastSolarConnectionError, ) async def test_config_entry_not_ready( diff --git a/tests/components/fully_kiosk/test_sensor.py b/tests/components/fully_kiosk/test_sensor.py index b54627f85b2..c6fade9fcd1 100644 --- a/tests/components/fully_kiosk/test_sensor.py +++ b/tests/components/fully_kiosk/test_sensor.py @@ -74,7 +74,7 @@ async def test_sensors_sensors( state = hass.states.get("sensor.amazon_fire_internal_storage_free_space") assert state assert state.state == "11675.5" - assert state.attributes.get(ATTR_DEVICE_CLASS) is None + assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.DATA_SIZE assert ( state.attributes.get(ATTR_FRIENDLY_NAME) == "Amazon Fire Internal storage free space" @@ -89,7 +89,7 @@ async def test_sensors_sensors( state = hass.states.get("sensor.amazon_fire_internal_storage_total_space") assert state assert state.state == "12938.5" - assert state.attributes.get(ATTR_DEVICE_CLASS) is None + assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.DATA_SIZE assert ( state.attributes.get(ATTR_FRIENDLY_NAME) == "Amazon Fire Internal storage total space" @@ -104,7 +104,7 @@ async def test_sensors_sensors( state = hass.states.get("sensor.amazon_fire_free_memory") assert state assert state.state == "362.4" - assert state.attributes.get(ATTR_DEVICE_CLASS) is None + assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.DATA_SIZE assert state.attributes.get(ATTR_FRIENDLY_NAME) == "Amazon Fire Free memory" assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT @@ -116,7 +116,7 @@ async def test_sensors_sensors( state = hass.states.get("sensor.amazon_fire_total_memory") assert state assert state.state == "1440.1" - assert state.attributes.get(ATTR_DEVICE_CLASS) is None + assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.DATA_SIZE assert state.attributes.get(ATTR_FRIENDLY_NAME) == "Amazon Fire Total memory" assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT diff --git a/tests/components/garages_amsterdam/conftest.py b/tests/components/garages_amsterdam/conftest.py index aced2894d67..fb59ba26569 100644 --- a/tests/components/garages_amsterdam/conftest.py +++ b/tests/components/garages_amsterdam/conftest.py @@ -9,7 +9,7 @@ import pytest def mock_cases(): """Mock garages_amsterdam garages.""" with patch( - "garages_amsterdam.GaragesAmsterdam.all_garages", + "odp_amsterdam.ODPAmsterdam.all_garages", return_value=[ Mock( garage_name="IJDok", diff --git a/tests/components/garages_amsterdam/test_config_flow.py b/tests/components/garages_amsterdam/test_config_flow.py index 3dd9c67bc15..dfb4531fa1d 100644 --- a/tests/components/garages_amsterdam/test_config_flow.py +++ b/tests/components/garages_amsterdam/test_config_flow.py @@ -53,7 +53,7 @@ async def test_error_handling( """Test we get the form.""" with patch( - "homeassistant.components.garages_amsterdam.config_flow.GaragesAmsterdam.all_garages", + "homeassistant.components.garages_amsterdam.config_flow.ODPAmsterdam.all_garages", side_effect=side_effect, ): result = await hass.config_entries.flow.async_init( diff --git a/tests/components/gios/test_sensor.py b/tests/components/gios/test_sensor.py index 4cdb7208dd3..f7475b1ef0f 100644 --- a/tests/components/gios/test_sensor.py +++ b/tests/components/gios/test_sensor.py @@ -60,7 +60,7 @@ async def test_sensor(hass): assert state.state == "252" assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION assert state.attributes.get(ATTR_STATION) == "Test Name 1" - assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.CO + assert state.attributes.get(ATTR_DEVICE_CLASS) is None assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT assert ( state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) diff --git a/tests/components/google/conftest.py b/tests/components/google/conftest.py index ad27e971ece..88e75499678 100644 --- a/tests/components/google/conftest.py +++ b/tests/components/google/conftest.py @@ -64,7 +64,7 @@ CLIENT_SECRET = "client-secret" @pytest.fixture(name="calendar_access_role") def test_calendar_access_role() -> str: """Default access role to use for test_api_calendar in tests.""" - return "reader" + return "owner" @pytest.fixture diff --git a/tests/components/google/test_calendar.py b/tests/components/google/test_calendar.py index 0e53642548d..7a0cd180a1f 100644 --- a/tests/components/google/test_calendar.py +++ b/tests/components/google/test_calendar.py @@ -2,17 +2,21 @@ from __future__ import annotations +from collections.abc import Awaitable, Callable import datetime from http import HTTPStatus from typing import Any from unittest.mock import patch import urllib +from aiohttp import ClientWebSocketResponse from aiohttp.client_exceptions import ClientError +from gcal_sync.auth import API_BASE_URL import pytest -from homeassistant.components.google.const import DOMAIN +from homeassistant.components.google.const import CONF_CALENDAR_ACCESS, DOMAIN from homeassistant.const import STATE_OFF, STATE_ON, Platform +from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er from homeassistant.helpers.template import DATE_STR_FORMAT import homeassistant.util.dt as dt_util @@ -23,9 +27,12 @@ from .conftest import ( TEST_API_ENTITY_NAME, TEST_YAML_ENTITY, TEST_YAML_ENTITY_NAME, + ApiResult, + ComponentSetup, ) from tests.common import async_fire_time_changed +from tests.test_util.aiohttp import AiohttpClientMocker TEST_ENTITY = TEST_API_ENTITY TEST_ENTITY_NAME = TEST_API_ENTITY_NAME @@ -60,14 +67,6 @@ TEST_EVENT = { } -@pytest.fixture( - autouse=True, scope="module", params=["reader", "owner", "freeBusyReader"] -) -def calendar_access_role(request) -> str: - """Fixture to exercise access roles in tests.""" - return request.param - - @pytest.fixture(autouse=True) def mock_test_setup( test_api_calendar, @@ -99,8 +98,55 @@ def upcoming_event_url(entity: str = TEST_ENTITY) -> str: return get_events_url(entity, start, end) +class Client: + """Test client with helper methods for calendar websocket.""" + + def __init__(self, client): + """Initialize Client.""" + self.client = client + self.id = 0 + + async def cmd(self, cmd: str, payload: dict[str, Any] = None) -> dict[str, Any]: + """Send a command and receive the json result.""" + self.id += 1 + await self.client.send_json( + { + "id": self.id, + "type": f"calendar/event/{cmd}", + **(payload if payload is not None else {}), + } + ) + resp = await self.client.receive_json() + assert resp.get("id") == self.id + return resp + + async def cmd_result(self, cmd: str, payload: dict[str, Any] = None) -> Any: + """Send a command and parse the result.""" + resp = await self.cmd(cmd, payload) + assert resp.get("success") + assert resp.get("type") == "result" + return resp.get("result") + + +ClientFixture = Callable[[], Awaitable[Client]] + + +@pytest.fixture +async def ws_client( + hass: HomeAssistant, + hass_ws_client: Callable[[HomeAssistant], Awaitable[ClientWebSocketResponse]], +) -> ClientFixture: + """Fixture for creating the test websocket client.""" + + async def create_client() -> Client: + ws_client = await hass_ws_client(hass) + return Client(ws_client) + + return create_client + + async def test_all_day_event(hass, mock_events_list_items, component_setup): - """Test that we can create an event trigger on device.""" + """Test for an all day calendar event.""" week_from_today = dt_util.now().date() + datetime.timedelta(days=7) end_event = week_from_today + datetime.timedelta(days=1) event = { @@ -124,11 +170,12 @@ async def test_all_day_event(hass, mock_events_list_items, component_setup): "end_time": end_event.strftime(DATE_STR_FORMAT), "location": event["location"], "description": event["description"], + "supported_features": 3, } async def test_future_event(hass, mock_events_list_items, component_setup): - """Test that we can create an event trigger on device.""" + """Test for an upcoming event.""" one_hour_from_now = dt_util.now() + datetime.timedelta(minutes=30) end_event = one_hour_from_now + datetime.timedelta(minutes=60) event = { @@ -152,11 +199,12 @@ async def test_future_event(hass, mock_events_list_items, component_setup): "end_time": end_event.strftime(DATE_STR_FORMAT), "location": event["location"], "description": event["description"], + "supported_features": 3, } async def test_in_progress_event(hass, mock_events_list_items, component_setup): - """Test that we can create an event trigger on device.""" + """Test an event that is active now.""" middle_of_event = dt_util.now() - datetime.timedelta(minutes=30) end_event = middle_of_event + datetime.timedelta(minutes=60) event = { @@ -180,11 +228,12 @@ async def test_in_progress_event(hass, mock_events_list_items, component_setup): "end_time": end_event.strftime(DATE_STR_FORMAT), "location": event["location"], "description": event["description"], + "supported_features": 3, } async def test_offset_in_progress_event(hass, mock_events_list_items, component_setup): - """Test that we can create an event trigger on device.""" + """Test an event that is active now with an offset.""" middle_of_event = dt_util.now() + datetime.timedelta(minutes=14) end_event = middle_of_event + datetime.timedelta(minutes=60) event_summary = "Test Event in Progress" @@ -210,13 +259,14 @@ async def test_offset_in_progress_event(hass, mock_events_list_items, component_ "end_time": end_event.strftime(DATE_STR_FORMAT), "location": event["location"], "description": event["description"], + "supported_features": 3, } async def test_all_day_offset_in_progress_event( hass, mock_events_list_items, component_setup ): - """Test that we can create an event trigger on device.""" + """Test an all day event that is currently in progress due to an offset.""" tomorrow = dt_util.now().date() + datetime.timedelta(days=1) end_event = tomorrow + datetime.timedelta(days=1) event_summary = "Test All Day Event Offset In Progress" @@ -242,11 +292,12 @@ async def test_all_day_offset_in_progress_event( "end_time": end_event.strftime(DATE_STR_FORMAT), "location": event["location"], "description": event["description"], + "supported_features": 3, } async def test_all_day_offset_event(hass, mock_events_list_items, component_setup): - """Test that we can create an event trigger on device.""" + """Test an all day event that not in progress due to an offset.""" now = dt_util.now() day_after_tomorrow = now.date() + datetime.timedelta(days=2) end_event = day_after_tomorrow + datetime.timedelta(days=1) @@ -274,11 +325,12 @@ async def test_all_day_offset_event(hass, mock_events_list_items, component_setu "end_time": end_event.strftime(DATE_STR_FORMAT), "location": event["location"], "description": event["description"], + "supported_features": 3, } async def test_missing_summary(hass, mock_events_list_items, component_setup): - """Test that we can create an event trigger on device.""" + """Test that a summary is optional.""" start_event = dt_util.now() + datetime.timedelta(minutes=14) end_event = start_event + datetime.timedelta(minutes=60) event = { @@ -303,6 +355,7 @@ async def test_missing_summary(hass, mock_events_list_items, component_setup): "end_time": end_event.strftime(DATE_STR_FORMAT), "location": event["location"], "description": event["description"], + "supported_features": 3, } @@ -779,3 +832,385 @@ async def test_all_day_iter_order( assert response.status == HTTPStatus.OK events = await response.json() assert [event["summary"] for event in events] == event_order + + +async def test_websocket_create( + hass: HomeAssistant, + component_setup: ComponentSetup, + test_api_calendar: dict[str, Any], + mock_insert_event: Callable[[str, dict[str, Any]], None], + mock_events_list: ApiResult, + aioclient_mock: AiohttpClientMocker, + ws_client: ClientFixture, +) -> None: + """Test websocket create command that sets a date/time range.""" + mock_events_list({}) + assert await component_setup() + + aioclient_mock.clear_requests() + mock_insert_event( + calendar_id=CALENDAR_ID, + ) + + client = await ws_client() + await client.cmd_result( + "create", + { + "entity_id": TEST_ENTITY, + "event": { + "summary": "Bastille Day Party", + "dtstart": "1997-07-14T17:00:00+00:00", + "dtend": "1997-07-15T04:00:00+00:00", + }, + }, + ) + assert len(aioclient_mock.mock_calls) == 1 + assert aioclient_mock.mock_calls[0][2] == { + "summary": "Bastille Day Party", + "description": None, + "start": { + "dateTime": "1997-07-14T11:00:00-06:00", + "timeZone": "America/Regina", + }, + "end": {"dateTime": "1997-07-14T22:00:00-06:00", "timeZone": "America/Regina"}, + } + + +async def test_websocket_create_all_day( + hass: HomeAssistant, + component_setup: ComponentSetup, + test_api_calendar: dict[str, Any], + mock_insert_event: Callable[[str, dict[str, Any]], None], + mock_events_list: ApiResult, + aioclient_mock: AiohttpClientMocker, + ws_client: ClientFixture, +) -> None: + """Test websocket create command for an all day event.""" + mock_events_list({}) + assert await component_setup() + + aioclient_mock.clear_requests() + mock_insert_event( + calendar_id=CALENDAR_ID, + ) + + client = await ws_client() + await client.cmd_result( + "create", + { + "entity_id": TEST_ENTITY, + "event": { + "summary": "Bastille Day Party", + "dtstart": "1997-07-14", + "dtend": "1997-07-15", + "rrule": "FREQ=YEARLY", + }, + }, + ) + assert len(aioclient_mock.mock_calls) == 1 + assert aioclient_mock.mock_calls[0][2] == { + "summary": "Bastille Day Party", + "description": None, + "start": { + "date": "1997-07-14", + }, + "end": {"date": "1997-07-15"}, + "recurrence": ["RRULE:FREQ=YEARLY"], + } + + +async def test_websocket_delete( + ws_client: ClientFixture, + hass_client, + component_setup, + mock_events_list: ApiResult, + mock_events_list_items: ApiResult, + aioclient_mock, +): + """Test websocket delete command.""" + mock_events_list_items( + [ + { + **TEST_EVENT, + "id": "event-id-1", + "iCalUID": "event-id-1@google.com", + "summary": "All Day Event", + "start": {"date": "2022-10-08"}, + "end": {"date": "2022-10-09"}, + }, + ] + ) + assert await component_setup() + assert len(aioclient_mock.mock_calls) == 2 + + aioclient_mock.clear_requests() + + # Expect a delete request as well as a follow up to sync state from server + aioclient_mock.delete(f"{API_BASE_URL}/calendars/{CALENDAR_ID}/events/event-id-1") + mock_events_list_items([]) + + client = await ws_client() + await client.cmd_result( + "delete", + { + "entity_id": TEST_ENTITY, + "uid": "event-id-1@google.com", + }, + ) + + assert len(aioclient_mock.mock_calls) == 2 + assert aioclient_mock.mock_calls[0][0] == "delete" + + +async def test_websocket_delete_recurring_event_instance( + ws_client: ClientFixture, + hass_client, + component_setup, + mock_events_list: ApiResult, + mock_events_list_items: ApiResult, + aioclient_mock, +): + """Test websocket delete command with recurring events.""" + mock_events_list_items( + [ + { + **TEST_EVENT, + "id": "event-id-1", + "iCalUID": "event-id-1@google.com", + "summary": "All Day Event", + "start": {"date": "2022-10-08"}, + "end": {"date": "2022-10-09"}, + "recurrence": ["RRULE:FREQ=WEEKLY"], + }, + ] + ) + assert await component_setup() + assert len(aioclient_mock.mock_calls) == 2 + + # Get a time range for the first event and the second instance of the + # recurring event. + web_client = await hass_client() + response = await web_client.get( + get_events_url(TEST_ENTITY, "2022-10-06T00:00:00Z", "2022-10-20T00:00:00Z") + ) + assert response.status == HTTPStatus.OK + events = await response.json() + assert len(events) == 2 + + # Delete the second instance + event = events[1] + assert event["uid"] == "event-id-1@google.com" + assert event["recurrence_id"] == "event-id-1_20221015" + assert event["rrule"] == "FREQ=WEEKLY" + + # Expect a delete request as well as a follow up to sync state from server + aioclient_mock.clear_requests() + aioclient_mock.patch( + f"{API_BASE_URL}/calendars/{CALENDAR_ID}/events/event-id-1_20221015" + ) + mock_events_list_items([]) + + client = await ws_client() + await client.cmd_result( + "delete", + { + "entity_id": TEST_ENTITY, + "uid": event["uid"], + "recurrence_id": event["recurrence_id"], + }, + ) + + assert len(aioclient_mock.mock_calls) == 2 + assert aioclient_mock.mock_calls[0][0] == "patch" + # Request to cancel the second instance of the recurring event + assert aioclient_mock.mock_calls[0][2] == { + "id": "event-id-1_20221015", + "status": "cancelled", + } + + # Attempt delete again, but this time for all future instances + aioclient_mock.clear_requests() + aioclient_mock.patch(f"{API_BASE_URL}/calendars/{CALENDAR_ID}/events/event-id-1") + mock_events_list_items([]) + + client = await ws_client() + await client.cmd_result( + "delete", + { + "entity_id": TEST_ENTITY, + "uid": event["uid"], + "recurrence_id": event["recurrence_id"], + "recurrence_range": "THISANDFUTURE", + }, + ) + + assert len(aioclient_mock.mock_calls) == 2 + assert aioclient_mock.mock_calls[0][0] == "patch" + # Request to cancel all events after the second instance + assert aioclient_mock.mock_calls[0][2] == { + "id": "event-id-1", + "recurrence": ["RRULE:FREQ=WEEKLY;UNTIL=20221014"], + } + + +@pytest.mark.parametrize( + "calendar_access_role,token_scopes,config_entry_options", + [ + ( + "reader", + ["https://www.googleapis.com/auth/calendar"], + {CONF_CALENDAR_ACCESS: "read_write"}, + ), + ( + "reader", + ["https://www.googleapis.com/auth/calendar.readonly"], + {CONF_CALENDAR_ACCESS: "read_only"}, + ), + ( + "owner", + ["https://www.googleapis.com/auth/calendar.readonly"], + {CONF_CALENDAR_ACCESS: "read_only"}, + ), + ], +) +async def test_readonly_websocket_create( + hass: HomeAssistant, + component_setup: ComponentSetup, + test_api_calendar: dict[str, Any], + mock_insert_event: Callable[[str, dict[str, Any]], None], + mock_events_list: ApiResult, + aioclient_mock: AiohttpClientMocker, + ws_client: ClientFixture, +) -> None: + """Test websocket create command with read only access.""" + mock_events_list({}) + assert await component_setup() + + aioclient_mock.clear_requests() + mock_insert_event( + calendar_id=CALENDAR_ID, + ) + + client = await ws_client() + result = await client.cmd( + "create", + { + "entity_id": TEST_ENTITY, + "event": { + "summary": "Bastille Day Party", + "dtstart": "1997-07-14T17:00:00+00:00", + "dtend": "1997-07-15T04:00:00+00:00", + }, + }, + ) + assert result.get("error") + assert result["error"].get("code") == "not_supported" + + +@pytest.mark.parametrize( + "calendars_config", + [ + [ + { + "cal_id": CALENDAR_ID, + "entities": [ + { + "device_id": "backyard_light", + "name": "Backyard Light", + "search": "#Backyard", + }, + ], + } + ], + ], +) +async def test_readonly_search_calendar( + hass: HomeAssistant, + component_setup: ComponentSetup, + mock_calendars_yaml, + mock_insert_event: Callable[[str, dict[str, Any]], None], + mock_events_list: ApiResult, + aioclient_mock: AiohttpClientMocker, + ws_client: ClientFixture, +) -> None: + """Test calendar configured with yaml/search does not support mutation.""" + mock_events_list({}) + assert await component_setup() + + aioclient_mock.clear_requests() + mock_insert_event( + calendar_id=CALENDAR_ID, + ) + + client = await ws_client() + result = await client.cmd( + "create", + { + "entity_id": TEST_YAML_ENTITY, + "event": { + "summary": "Bastille Day Party", + "dtstart": "1997-07-14T17:00:00+00:00", + "dtend": "1997-07-15T04:00:00+00:00", + }, + }, + ) + assert result.get("error") + assert result["error"].get("code") == "not_supported" + + +@pytest.mark.parametrize("calendar_access_role", ["reader", "freeBusyReader"]) +async def test_all_day_reader_access(hass, mock_events_list_items, component_setup): + """Test that reader / freebusy reader access can load properly.""" + week_from_today = dt_util.now().date() + datetime.timedelta(days=7) + end_event = week_from_today + datetime.timedelta(days=1) + event = { + **TEST_EVENT, + "start": {"date": week_from_today.isoformat()}, + "end": {"date": end_event.isoformat()}, + } + mock_events_list_items([event]) + + assert await component_setup() + + state = hass.states.get(TEST_ENTITY) + assert state.name == TEST_ENTITY_NAME + assert state.state == STATE_OFF + assert dict(state.attributes) == { + "friendly_name": TEST_ENTITY_NAME, + "message": event["summary"], + "all_day": True, + "offset_reached": False, + "start_time": week_from_today.strftime(DATE_STR_FORMAT), + "end_time": end_event.strftime(DATE_STR_FORMAT), + "location": event["location"], + "description": event["description"], + } + + +@pytest.mark.parametrize("calendar_access_role", ["reader", "freeBusyReader"]) +async def test_reader_in_progress_event(hass, mock_events_list_items, component_setup): + """Test reader access for an event in process.""" + middle_of_event = dt_util.now() - datetime.timedelta(minutes=30) + end_event = middle_of_event + datetime.timedelta(minutes=60) + event = { + **TEST_EVENT, + "start": {"dateTime": middle_of_event.isoformat()}, + "end": {"dateTime": end_event.isoformat()}, + } + mock_events_list_items([event]) + + assert await component_setup() + + state = hass.states.get(TEST_ENTITY) + assert state.name == TEST_ENTITY_NAME + assert state.state == STATE_ON + assert dict(state.attributes) == { + "friendly_name": TEST_ENTITY_NAME, + "message": event["summary"], + "all_day": False, + "offset_reached": False, + "start_time": middle_of_event.strftime(DATE_STR_FORMAT), + "end_time": end_event.strftime(DATE_STR_FORMAT), + "location": event["location"], + "description": event["description"], + } diff --git a/tests/components/google/test_config_flow.py b/tests/components/google/test_config_flow.py index d8ddd6fe588..bce3f4855c7 100644 --- a/tests/components/google/test_config_flow.py +++ b/tests/components/google/test_config_flow.py @@ -104,7 +104,7 @@ async def primary_calendar( """Fixture to return the primary calendar.""" mock_calendar_get( "primary", - {"id": primary_calendar_email, "summary": "Personal"}, + {"id": primary_calendar_email, "summary": "Personal", "accessRole": "owner"}, exc=primary_calendar_error, ) diff --git a/tests/components/google/test_init.py b/tests/components/google/test_init.py index 5e7696eec68..452b2300e5b 100644 --- a/tests/components/google/test_init.py +++ b/tests/components/google/test_init.py @@ -152,11 +152,12 @@ async def test_calendar_yaml_missing_required_fields( component_setup: ComponentSetup, calendars_config: list[dict[str, Any]], mock_calendars_yaml: None, + config_entry: MockConfigEntry, ) -> None: """Test setup with a missing schema fields, ignores the error and continues.""" - assert await component_setup() + assert not await component_setup() - assert not hass.states.get(TEST_YAML_ENTITY) + assert config_entry.state is ConfigEntryState.SETUP_ERROR @pytest.mark.parametrize("calendars_config", [[{"missing-cal_id": "invalid-schema"}]]) @@ -165,23 +166,12 @@ async def test_invalid_calendar_yaml( component_setup: ComponentSetup, calendars_config: list[dict[str, Any]], mock_calendars_yaml: None, - mock_calendars_list: ApiResult, - test_api_calendar: dict[str, Any], - mock_events_list: ApiResult, + config_entry: MockConfigEntry, ) -> None: """Test setup with missing entity id fields fails to load the platform.""" - mock_calendars_list({"items": [test_api_calendar]}) - mock_events_list({}) + assert not await component_setup() - assert await component_setup() - - entries = hass.config_entries.async_entries(DOMAIN) - assert len(entries) == 1 - entry = entries[0] - assert entry.state is ConfigEntryState.LOADED - - assert not hass.states.get(TEST_YAML_ENTITY) - assert not hass.states.get(TEST_API_ENTITY) + assert config_entry.state is ConfigEntryState.SETUP_ERROR async def test_calendar_yaml_error( @@ -768,7 +758,7 @@ async def test_assign_unique_id( mock_calendar_get( "primary", - {"id": EMAIL_ADDRESS, "summary": "Personal"}, + {"id": EMAIL_ADDRESS, "summary": "Personal", "accessRole": "owner"}, ) mock_calendars_list({"items": [test_api_calendar]}) diff --git a/tests/components/google_assistant/test_smart_home.py b/tests/components/google_assistant/test_smart_home.py index af61cfedf40..26b85f19c58 100644 --- a/tests/components/google_assistant/test_smart_home.py +++ b/tests/components/google_assistant/test_smart_home.py @@ -3,6 +3,7 @@ import asyncio from unittest.mock import ANY, call, patch import pytest +from pytest_unordered import unordered from homeassistant.components import camera from homeassistant.components.climate import ATTR_MAX_TEMP, ATTR_MIN_TEMP, HVACMode @@ -101,10 +102,21 @@ async def test_async_handle_message(hass): await hass.async_block_till_done() -async def test_sync_message(hass): +async def test_sync_message(hass, registries): """Test a sync message.""" + entity = registries.entity.async_get_or_create( + "light", + "test", + "unique-demo-light", + suggested_object_id="demo_light", + ) + registries.entity.async_update_entity( + entity.entity_id, + aliases={"Stay", "Healthy"}, + ) + light = DemoLight( - None, + "unique-demo-light", "Demo Light", state=False, hs_color=(180, 75), @@ -150,7 +162,15 @@ async def test_sync_message(hass): "id": "light.demo_light", "name": { "name": "Demo Light", - "nicknames": ["Demo Light", "Hello", "World"], + "nicknames": unordered( + [ + "Demo Light", + "Hello", + "World", + "Stay", + "Healthy", + ] + ), }, "traits": [ trait.TRAIT_BRIGHTNESS, @@ -181,7 +201,10 @@ async def test_sync_message(hass): { "setting_name": "none", "setting_values": [ - {"lang": "en", "setting_synonym": ["none"]} + { + "lang": "en", + "setting_synonym": ["none"], + } ], }, ], diff --git a/tests/components/google_assistant_sdk/__init__.py b/tests/components/google_assistant_sdk/__init__.py new file mode 100644 index 00000000000..7b5fee00ad0 --- /dev/null +++ b/tests/components/google_assistant_sdk/__init__.py @@ -0,0 +1 @@ +"""Tests for the Google Assistant SDK integration.""" diff --git a/tests/components/google_assistant_sdk/conftest.py b/tests/components/google_assistant_sdk/conftest.py new file mode 100644 index 00000000000..9730c0fef17 --- /dev/null +++ b/tests/components/google_assistant_sdk/conftest.py @@ -0,0 +1,92 @@ +"""PyTest fixtures and test helpers.""" +from collections.abc import Awaitable, Callable, Generator +import time + +from google.oauth2.credentials import Credentials +import pytest + +from homeassistant.components.application_credentials import ( + ClientCredential, + async_import_client_credential, +) +from homeassistant.components.google_assistant_sdk.const import DOMAIN +from homeassistant.core import HomeAssistant +from homeassistant.setup import async_setup_component + +from tests.common import MockConfigEntry + +ComponentSetup = Callable[[], Awaitable[None]] + +CLIENT_ID = "1234" +CLIENT_SECRET = "5678" +ACCESS_TOKEN = "mock-access-token" + + +@pytest.fixture +async def setup_credentials(hass: HomeAssistant) -> None: + """Fixture to setup credentials.""" + assert await async_setup_component(hass, "application_credentials", {}) + await async_import_client_credential( + hass, + DOMAIN, + ClientCredential(CLIENT_ID, CLIENT_SECRET), + ) + + +@pytest.fixture(name="scopes") +def mock_scopes() -> list[str]: + """Fixture to set the scopes present in the OAuth token.""" + return ["https://www.googleapis.com/auth/assistant-sdk-prototype"] + + +@pytest.fixture(name="expires_at") +def mock_expires_at() -> int: + """Fixture to set the oauth token expiration time.""" + return time.time() + 3600 + + +@pytest.fixture(name="config_entry") +def mock_config_entry(expires_at: int, scopes: list[str]) -> MockConfigEntry: + """Fixture for MockConfigEntry.""" + return MockConfigEntry( + domain=DOMAIN, + data={ + "auth_implementation": DOMAIN, + "token": { + "access_token": ACCESS_TOKEN, + "refresh_token": "mock-refresh-token", + "expires_at": expires_at, + "scope": " ".join(scopes), + }, + }, + ) + + +@pytest.fixture(name="setup_integration") +async def mock_setup_integration( + hass: HomeAssistant, config_entry: MockConfigEntry +) -> Generator[ComponentSetup, None, None]: + """Fixture for setting up the component.""" + config_entry.add_to_hass(hass) + + assert await async_setup_component(hass, "application_credentials", {}) + await async_import_client_credential( + hass, + DOMAIN, + ClientCredential("client-id", "client-secret"), + DOMAIN, + ) + + async def func() -> None: + assert await async_setup_component(hass, DOMAIN, {}) + await hass.async_block_till_done() + + yield func + + +class ExpectedCredentials: + """Assert credentials have the expected access token.""" + + def __eq__(self, other: Credentials): + """Return true if credentials have the expected access token.""" + return other.token == ACCESS_TOKEN diff --git a/tests/components/google_assistant_sdk/test_config_flow.py b/tests/components/google_assistant_sdk/test_config_flow.py new file mode 100644 index 00000000000..af5f0e73c75 --- /dev/null +++ b/tests/components/google_assistant_sdk/test_config_flow.py @@ -0,0 +1,259 @@ +"""Test the Google Assistant SDK config flow.""" +from unittest.mock import patch + +import oauth2client + +from homeassistant import config_entries +from homeassistant.components.google_assistant_sdk.const import DOMAIN +from homeassistant.core import HomeAssistant +from homeassistant.helpers import config_entry_oauth2_flow + +from .conftest import CLIENT_ID, ComponentSetup + +from tests.common import MockConfigEntry + +TITLE = "Google Assistant SDK" + + +async def test_full_flow( + hass: HomeAssistant, + hass_client_no_auth, + aioclient_mock, + current_request_with_host, + setup_credentials, +) -> None: + """Check full flow.""" + result = await hass.config_entries.flow.async_init( + "google_assistant_sdk", context={"source": config_entries.SOURCE_USER} + ) + state = config_entry_oauth2_flow._encode_jwt( + hass, + { + "flow_id": result["flow_id"], + "redirect_uri": "https://example.com/auth/external/callback", + }, + ) + + assert result["url"] == ( + f"{oauth2client.GOOGLE_AUTH_URI}?response_type=code&client_id={CLIENT_ID}" + "&redirect_uri=https://example.com/auth/external/callback" + f"&state={state}&scope=https://www.googleapis.com/auth/assistant-sdk-prototype" + "&access_type=offline&prompt=consent" + ) + + client = await hass_client_no_auth() + resp = await client.get(f"/auth/external/callback?code=abcd&state={state}") + assert resp.status == 200 + assert resp.headers["content-type"] == "text/html; charset=utf-8" + + aioclient_mock.post( + oauth2client.GOOGLE_TOKEN_URI, + json={ + "refresh_token": "mock-refresh-token", + "access_token": "mock-access-token", + "type": "Bearer", + "expires_in": 60, + }, + ) + + with patch( + "homeassistant.components.google_assistant_sdk.async_setup_entry", + return_value=True, + ) as mock_setup: + result = await hass.config_entries.flow.async_configure(result["flow_id"]) + + assert len(hass.config_entries.async_entries(DOMAIN)) == 1 + assert len(mock_setup.mock_calls) == 1 + + assert result.get("type") == "create_entry" + assert result.get("title") == TITLE + assert "result" in result + assert result.get("result").unique_id is None + assert "token" in result.get("result").data + assert result.get("result").data["token"].get("access_token") == "mock-access-token" + assert ( + result.get("result").data["token"].get("refresh_token") == "mock-refresh-token" + ) + + +async def test_reauth( + hass: HomeAssistant, + hass_client_no_auth, + aioclient_mock, + current_request_with_host, + setup_credentials, +) -> None: + """Test the reauthentication case updates the existing config entry.""" + + config_entry = MockConfigEntry( + domain=DOMAIN, + data={ + "token": { + "access_token": "mock-access-token", + }, + }, + ) + config_entry.add_to_hass(hass) + + config_entry.async_start_reauth(hass) + await hass.async_block_till_done() + + flows = hass.config_entries.flow.async_progress() + assert len(flows) == 1 + result = flows[0] + assert result["step_id"] == "reauth_confirm" + + result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) + state = config_entry_oauth2_flow._encode_jwt( + hass, + { + "flow_id": result["flow_id"], + "redirect_uri": "https://example.com/auth/external/callback", + }, + ) + assert result["url"] == ( + f"{oauth2client.GOOGLE_AUTH_URI}?response_type=code&client_id={CLIENT_ID}" + "&redirect_uri=https://example.com/auth/external/callback" + f"&state={state}&scope=https://www.googleapis.com/auth/assistant-sdk-prototype" + "&access_type=offline&prompt=consent" + ) + client = await hass_client_no_auth() + resp = await client.get(f"/auth/external/callback?code=abcd&state={state}") + assert resp.status == 200 + assert resp.headers["content-type"] == "text/html; charset=utf-8" + + aioclient_mock.post( + oauth2client.GOOGLE_TOKEN_URI, + json={ + "refresh_token": "mock-refresh-token", + "access_token": "updated-access-token", + "type": "Bearer", + "expires_in": 60, + }, + ) + + with patch( + "homeassistant.components.google_assistant_sdk.async_setup_entry", + return_value=True, + ) as mock_setup: + result = await hass.config_entries.flow.async_configure(result["flow_id"]) + + assert len(hass.config_entries.async_entries(DOMAIN)) == 1 + assert len(mock_setup.mock_calls) == 1 + + assert result.get("type") == "abort" + assert result.get("reason") == "reauth_successful" + + assert config_entry.unique_id is None + assert "token" in config_entry.data + # Verify access token is refreshed + assert config_entry.data["token"].get("access_token") == "updated-access-token" + assert config_entry.data["token"].get("refresh_token") == "mock-refresh-token" + + +async def test_single_instance_allowed( + hass: HomeAssistant, + hass_client_no_auth, + aioclient_mock, + current_request_with_host, + setup_credentials, +) -> None: + """Test case where config flow allows a single test.""" + config_entry = MockConfigEntry( + domain=DOMAIN, + data={ + "token": { + "access_token": "mock-access-token", + }, + }, + ) + config_entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + "google_assistant_sdk", context={"source": config_entries.SOURCE_USER} + ) + state = config_entry_oauth2_flow._encode_jwt( + hass, + { + "flow_id": result["flow_id"], + "redirect_uri": "https://example.com/auth/external/callback", + }, + ) + + assert result["url"] == ( + f"{oauth2client.GOOGLE_AUTH_URI}?response_type=code&client_id={CLIENT_ID}" + "&redirect_uri=https://example.com/auth/external/callback" + f"&state={state}&scope=https://www.googleapis.com/auth/assistant-sdk-prototype" + "&access_type=offline&prompt=consent" + ) + + client = await hass_client_no_auth() + resp = await client.get(f"/auth/external/callback?code=abcd&state={state}") + assert resp.status == 200 + assert resp.headers["content-type"] == "text/html; charset=utf-8" + + aioclient_mock.post( + oauth2client.GOOGLE_TOKEN_URI, + json={ + "refresh_token": "mock-refresh-token", + "access_token": "mock-access-token", + "type": "Bearer", + "expires_in": 60, + }, + ) + + result = await hass.config_entries.flow.async_configure(result["flow_id"]) + assert result.get("type") == "abort" + assert result.get("reason") == "single_instance_allowed" + + +async def test_options_flow( + hass: HomeAssistant, + setup_integration: ComponentSetup, + config_entry: MockConfigEntry, +) -> None: + """Test options flow.""" + await setup_integration() + assert not config_entry.options + + # Trigger options flow, first time + result = await hass.config_entries.options.async_init(config_entry.entry_id) + assert result["type"] == "form" + assert result["step_id"] == "init" + data_schema = result["data_schema"].schema + assert set(data_schema) == {"language_code"} + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={"language_code": "es-ES"}, + ) + assert result["type"] == "create_entry" + assert config_entry.options == {"language_code": "es-ES"} + + # Retrigger options flow, not change language + result = await hass.config_entries.options.async_init(config_entry.entry_id) + assert result["type"] == "form" + assert result["step_id"] == "init" + data_schema = result["data_schema"].schema + assert set(data_schema) == {"language_code"} + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={"language_code": "es-ES"}, + ) + assert result["type"] == "create_entry" + assert config_entry.options == {"language_code": "es-ES"} + + # Retrigger options flow, change language + result = await hass.config_entries.options.async_init(config_entry.entry_id) + assert result["type"] == "form" + assert result["step_id"] == "init" + data_schema = result["data_schema"].schema + assert set(data_schema) == {"language_code"} + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={"language_code": "en-US"}, + ) + assert result["type"] == "create_entry" + assert config_entry.options == {"language_code": "en-US"} diff --git a/tests/components/google_assistant_sdk/test_helpers.py b/tests/components/google_assistant_sdk/test_helpers.py new file mode 100644 index 00000000000..03a04097d67 --- /dev/null +++ b/tests/components/google_assistant_sdk/test_helpers.py @@ -0,0 +1,47 @@ +"""Test the Google Assistant SDK helpers.""" +from homeassistant.components.google_assistant_sdk.const import SUPPORTED_LANGUAGE_CODES +from homeassistant.components.google_assistant_sdk.helpers import ( + DEFAULT_LANGUAGE_CODES, + default_language_code, +) +from homeassistant.core import HomeAssistant + + +def test_default_language_codes(hass: HomeAssistant) -> None: + """Test all supported languages have a default language_code.""" + for language_code in SUPPORTED_LANGUAGE_CODES: + lang = language_code.split("-", maxsplit=1)[0] + assert DEFAULT_LANGUAGE_CODES.get(lang) + + +def test_default_language_code(hass: HomeAssistant) -> None: + """Test default_language_code.""" + assert default_language_code(hass) == "en-US" + + hass.config.language = "en" + hass.config.country = "US" + assert default_language_code(hass) == "en-US" + + hass.config.language = "en" + hass.config.country = "GB" + assert default_language_code(hass) == "en-GB" + + hass.config.language = "en" + hass.config.country = "ES" + assert default_language_code(hass) == "en-US" + + hass.config.language = "es" + hass.config.country = "ES" + assert default_language_code(hass) == "es-ES" + + hass.config.language = "es" + hass.config.country = "MX" + assert default_language_code(hass) == "es-MX" + + hass.config.language = "es" + hass.config.country = None + assert default_language_code(hass) == "es-ES" + + hass.config.language = "el" + hass.config.country = "GR" + assert default_language_code(hass) == "en-US" diff --git a/tests/components/google_assistant_sdk/test_init.py b/tests/components/google_assistant_sdk/test_init.py new file mode 100644 index 00000000000..afc5e77042f --- /dev/null +++ b/tests/components/google_assistant_sdk/test_init.py @@ -0,0 +1,179 @@ +"""Tests for Google Assistant SDK.""" +import http +import time +from unittest.mock import call, patch + +import aiohttp +import pytest + +from homeassistant.components.google_assistant_sdk import DOMAIN +from homeassistant.config_entries import ConfigEntryState +from homeassistant.core import HomeAssistant + +from .conftest import ComponentSetup, ExpectedCredentials + +from tests.test_util.aiohttp import AiohttpClientMocker + + +async def test_setup_success( + hass: HomeAssistant, setup_integration: ComponentSetup +) -> None: + """Test successful setup and unload.""" + await setup_integration() + + entries = hass.config_entries.async_entries(DOMAIN) + assert len(entries) == 1 + assert entries[0].state is ConfigEntryState.LOADED + + await hass.config_entries.async_unload(entries[0].entry_id) + await hass.async_block_till_done() + + assert not hass.data.get(DOMAIN) + assert entries[0].state is ConfigEntryState.NOT_LOADED + assert not hass.services.async_services().get(DOMAIN, {}) + + +@pytest.mark.parametrize("expires_at", [time.time() - 3600], ids=["expired"]) +async def test_expired_token_refresh_success( + hass: HomeAssistant, + setup_integration: ComponentSetup, + aioclient_mock: AiohttpClientMocker, +) -> None: + """Test expired token is refreshed.""" + + aioclient_mock.post( + "https://oauth2.googleapis.com/token", + json={ + "access_token": "updated-access-token", + "refresh_token": "updated-refresh-token", + "expires_at": time.time() + 3600, + "expires_in": 3600, + }, + ) + + await setup_integration() + + entries = hass.config_entries.async_entries(DOMAIN) + assert len(entries) == 1 + assert entries[0].state is ConfigEntryState.LOADED + assert entries[0].data["token"]["access_token"] == "updated-access-token" + assert entries[0].data["token"]["expires_in"] == 3600 + + +@pytest.mark.parametrize( + "expires_at,status,expected_state", + [ + ( + time.time() - 3600, + http.HTTPStatus.UNAUTHORIZED, + ConfigEntryState.SETUP_ERROR, + ), + ( + time.time() - 3600, + http.HTTPStatus.INTERNAL_SERVER_ERROR, + ConfigEntryState.SETUP_RETRY, + ), + ], + ids=["failure_requires_reauth", "transient_failure"], +) +async def test_expired_token_refresh_failure( + hass: HomeAssistant, + setup_integration: ComponentSetup, + aioclient_mock: AiohttpClientMocker, + status: http.HTTPStatus, + expected_state: ConfigEntryState, +) -> None: + """Test failure while refreshing token with a transient error.""" + + aioclient_mock.post( + "https://oauth2.googleapis.com/token", + status=status, + ) + + await setup_integration() + + # Verify a transient failure has occurred + entries = hass.config_entries.async_entries(DOMAIN) + assert entries[0].state is expected_state + + +@pytest.mark.parametrize( + "configured_language_code,expected_language_code", + [("", "en-US"), ("en-US", "en-US"), ("es-ES", "es-ES")], + ids=["default", "english", "spanish"], +) +async def test_send_text_command( + hass: HomeAssistant, + setup_integration: ComponentSetup, + configured_language_code: str, + expected_language_code: str, +) -> None: + """Test service call send_text_command calls TextAssistant.""" + await setup_integration() + + entries = hass.config_entries.async_entries(DOMAIN) + assert len(entries) == 1 + assert entries[0].state is ConfigEntryState.LOADED + if configured_language_code: + entries[0].options = {"language_code": configured_language_code} + + command = "turn on home assistant unsupported device" + with patch( + "homeassistant.components.google_assistant_sdk.helpers.TextAssistant" + ) as mock_text_assistant: + await hass.services.async_call( + DOMAIN, + "send_text_command", + {"command": command}, + blocking=True, + ) + mock_text_assistant.assert_called_once_with( + ExpectedCredentials(), expected_language_code + ) + mock_text_assistant.assert_has_calls([call().__enter__().assist(command)]) + + +@pytest.mark.parametrize( + "status,requires_reauth", + [ + ( + http.HTTPStatus.UNAUTHORIZED, + True, + ), + ( + http.HTTPStatus.INTERNAL_SERVER_ERROR, + False, + ), + ], + ids=["failure_requires_reauth", "transient_failure"], +) +async def test_send_text_command_expired_token_refresh_failure( + hass: HomeAssistant, + setup_integration: ComponentSetup, + aioclient_mock: AiohttpClientMocker, + status: http.HTTPStatus, + requires_reauth: ConfigEntryState, +) -> None: + """Test failure refreshing token in send_text_command.""" + await setup_integration() + + entries = hass.config_entries.async_entries(DOMAIN) + assert len(entries) == 1 + entry = entries[0] + assert entry.state is ConfigEntryState.LOADED + + entry.data["token"]["expires_at"] = time.time() - 3600 + aioclient_mock.post( + "https://oauth2.googleapis.com/token", + status=status, + ) + + with pytest.raises(aiohttp.ClientResponseError): + await hass.services.async_call( + DOMAIN, + "send_text_command", + {"command": "turn on tv"}, + blocking=True, + ) + + assert any(entry.async_get_active_flows(hass, {"reauth"})) == requires_reauth diff --git a/tests/components/google_assistant_sdk/test_notify.py b/tests/components/google_assistant_sdk/test_notify.py new file mode 100644 index 00000000000..5a2d11b861b --- /dev/null +++ b/tests/components/google_assistant_sdk/test_notify.py @@ -0,0 +1,134 @@ +"""Tests for the Google Assistant notify.""" +from unittest.mock import call, patch + +from homeassistant.components import notify +from homeassistant.components.google_assistant_sdk import DOMAIN +from homeassistant.components.google_assistant_sdk.const import SUPPORTED_LANGUAGE_CODES +from homeassistant.components.google_assistant_sdk.notify import broadcast_commands +from homeassistant.core import HomeAssistant + +from .conftest import ComponentSetup, ExpectedCredentials + + +async def test_broadcast_no_targets( + hass: HomeAssistant, setup_integration: ComponentSetup +) -> None: + """Test broadcast to all.""" + await setup_integration() + + message = "time for dinner" + expected_command = "broadcast time for dinner" + with patch( + "homeassistant.components.google_assistant_sdk.helpers.TextAssistant" + ) as mock_text_assistant: + await hass.services.async_call( + notify.DOMAIN, + DOMAIN, + {notify.ATTR_MESSAGE: message}, + ) + await hass.async_block_till_done() + mock_text_assistant.assert_called_once_with(ExpectedCredentials(), "en-US") + mock_text_assistant.assert_has_calls([call().__enter__().assist(expected_command)]) + + +async def test_broadcast_one_target( + hass: HomeAssistant, setup_integration: ComponentSetup +) -> None: + """Test broadcast to one target.""" + await setup_integration() + + message = "time for dinner" + target = "basement" + expected_command = "broadcast to basement time for dinner" + with patch( + "homeassistant.components.google_assistant_sdk.helpers.TextAssistant.assist", + return_value=["text_response", None], + ) as mock_assist_call: + await hass.services.async_call( + notify.DOMAIN, + DOMAIN, + {notify.ATTR_MESSAGE: message, notify.ATTR_TARGET: [target]}, + ) + await hass.async_block_till_done() + mock_assist_call.assert_called_once_with(expected_command) + + +async def test_broadcast_two_targets( + hass: HomeAssistant, setup_integration: ComponentSetup +) -> None: + """Test broadcast to two targets.""" + await setup_integration() + + message = "time for dinner" + target1 = "basement" + target2 = "master bedroom" + expected_command1 = "broadcast to basement time for dinner" + expected_command2 = "broadcast to master bedroom time for dinner" + with patch( + "homeassistant.components.google_assistant_sdk.helpers.TextAssistant.assist", + return_value=["text_response", None], + ) as mock_assist_call: + await hass.services.async_call( + notify.DOMAIN, + DOMAIN, + {notify.ATTR_MESSAGE: message, notify.ATTR_TARGET: [target1, target2]}, + ) + await hass.async_block_till_done() + mock_assist_call.assert_has_calls( + [call(expected_command1), call(expected_command2)] + ) + + +async def test_broadcast_empty_message( + hass: HomeAssistant, setup_integration: ComponentSetup +) -> None: + """Test broadcast empty message.""" + await setup_integration() + + with patch( + "homeassistant.components.google_assistant_sdk.helpers.TextAssistant.assist", + return_value=["text_response", None], + ) as mock_assist_call: + await hass.services.async_call( + notify.DOMAIN, + DOMAIN, + {notify.ATTR_MESSAGE: ""}, + ) + await hass.async_block_till_done() + mock_assist_call.assert_not_called() + + +async def test_broadcast_spanish( + hass: HomeAssistant, setup_integration: ComponentSetup +) -> None: + """Test broadcast in Spanish.""" + await setup_integration() + + entry = hass.config_entries.async_entries(DOMAIN)[0] + entry.options = {"language_code": "es-ES"} + + message = "comida" + expected_command = "Anuncia comida" + with patch( + "homeassistant.components.google_assistant_sdk.helpers.TextAssistant" + ) as mock_text_assistant: + await hass.services.async_call( + notify.DOMAIN, + DOMAIN, + {notify.ATTR_MESSAGE: message}, + ) + await hass.async_block_till_done() + mock_text_assistant.assert_called_once_with(ExpectedCredentials(), "es-ES") + mock_text_assistant.assert_has_calls([call().__enter__().assist(expected_command)]) + + +def test_broadcast_language_mapping( + hass: HomeAssistant, setup_integration: ComponentSetup +) -> None: + """Test all supported languages have a mapped broadcast command.""" + for language_code in SUPPORTED_LANGUAGE_CODES: + cmds = broadcast_commands(language_code) + assert cmds + assert len(cmds) == 2 + assert cmds[0] + assert cmds[1] diff --git a/tests/components/google_sheets/test_init.py b/tests/components/google_sheets/test_init.py index f77edcbb491..4c708544099 100644 --- a/tests/components/google_sheets/test_init.py +++ b/tests/components/google_sheets/test_init.py @@ -92,7 +92,7 @@ async def test_setup_success( assert not hass.data.get(DOMAIN) assert entries[0].state is ConfigEntryState.NOT_LOADED - assert not len(hass.services.async_services().get(DOMAIN, {})) + assert not hass.services.async_services().get(DOMAIN, {}) @pytest.mark.parametrize( @@ -125,7 +125,6 @@ async def test_missing_required_scopes_requires_reauth( async def test_expired_token_refresh_success( hass: HomeAssistant, setup_integration: ComponentSetup, - scopes: list[str], aioclient_mock: AiohttpClientMocker, ) -> None: """Test expired token is refreshed.""" @@ -168,7 +167,6 @@ async def test_expired_token_refresh_success( async def test_expired_token_refresh_failure( hass: HomeAssistant, setup_integration: ComponentSetup, - scopes: list[str], aioclient_mock: AiohttpClientMocker, status: http.HTTPStatus, expected_state: ConfigEntryState, diff --git a/tests/components/google_translate/test_tts.py b/tests/components/google_translate/test_tts.py index 562f35655d0..67e840b1902 100644 --- a/tests/components/google_translate/test_tts.py +++ b/tests/components/google_translate/test_tts.py @@ -84,6 +84,7 @@ async def test_service_say(hass, mock_gtts, calls): assert mock_gtts.mock_calls[0][2] == { "text": "There is a person at the front door.", "lang": "en", + "tld": "com", } @@ -112,6 +113,7 @@ async def test_service_say_german_config(hass, mock_gtts, calls): assert mock_gtts.mock_calls[0][2] == { "text": "There is a person at the front door.", "lang": "de", + "tld": "com", } @@ -141,6 +143,96 @@ async def test_service_say_german_service(hass, mock_gtts, calls): assert mock_gtts.mock_calls[0][2] == { "text": "There is a person at the front door.", "lang": "de", + "tld": "com", + } + + +async def test_service_say_en_uk_config(hass, mock_gtts, calls): + """Test service call say with en-uk code in the config.""" + + await async_setup_component( + hass, + tts.DOMAIN, + {tts.DOMAIN: {"platform": "google_translate", "language": "en-uk"}}, + ) + + await hass.services.async_call( + tts.DOMAIN, + "google_translate_say", + { + "entity_id": "media_player.something", + tts.ATTR_MESSAGE: "There is a person at the front door.", + }, + blocking=True, + ) + + assert len(calls) == 1 + await get_media_source_url(hass, calls[0].data[ATTR_MEDIA_CONTENT_ID]) + assert len(mock_gtts.mock_calls) == 2 + assert mock_gtts.mock_calls[0][2] == { + "text": "There is a person at the front door.", + "lang": "en", + "tld": "co.uk", + } + + +async def test_service_say_en_uk_service(hass, mock_gtts, calls): + """Test service call say with en-uk code in the config.""" + + await async_setup_component( + hass, + tts.DOMAIN, + {tts.DOMAIN: {"platform": "google_translate"}}, + ) + + await hass.services.async_call( + tts.DOMAIN, + "google_translate_say", + { + "entity_id": "media_player.something", + tts.ATTR_MESSAGE: "There is a person at the front door.", + tts.ATTR_LANGUAGE: "en-uk", + }, + blocking=True, + ) + + assert len(calls) == 1 + await get_media_source_url(hass, calls[0].data[ATTR_MEDIA_CONTENT_ID]) + assert len(mock_gtts.mock_calls) == 2 + assert mock_gtts.mock_calls[0][2] == { + "text": "There is a person at the front door.", + "lang": "en", + "tld": "co.uk", + } + + +async def test_service_say_en_couk(hass, mock_gtts, calls): + """Test service call say in co.uk tld accent.""" + + await async_setup_component( + hass, tts.DOMAIN, {tts.DOMAIN: {"platform": "google_translate"}} + ) + + await hass.services.async_call( + tts.DOMAIN, + "google_translate_say", + { + "entity_id": "media_player.something", + tts.ATTR_MESSAGE: "There is a person at the front door.", + tts.ATTR_OPTIONS: {"tld": "co.uk"}, + }, + blocking=True, + ) + + assert len(calls) == 1 + url = await get_media_source_url(hass, calls[0].data[ATTR_MEDIA_CONTENT_ID]) + assert len(mock_gtts.mock_calls) == 2 + assert url.endswith(".mp3") + + assert mock_gtts.mock_calls[0][2] == { + "text": "There is a person at the front door.", + "lang": "en", + "tld": "co.uk", } diff --git a/tests/components/govee_ble/__init__.py b/tests/components/govee_ble/__init__.py index c440317fa43..54e7c1ee777 100644 --- a/tests/components/govee_ble/__init__.py +++ b/tests/components/govee_ble/__init__.py @@ -36,3 +36,16 @@ GVH5177_SERVICE_INFO = BluetoothServiceInfo( service_data={}, source="local", ) + +GVH5178_SERVICE_INFO_ERROR = BluetoothServiceInfo( + name="B51782BC8", + address="A4:C1:38:75:2B:C8", + rssi=-66, + manufacturer_data={ + 1: b"\x01\x01\x01\x00\x03\xe7\xe4\x00\x01", + 76: b"\x02\x15INTELLI_ROCKS_HWPu\xf2\xff\xc2", + }, + service_data={}, + service_uuids=["0000ec88-0000-1000-8000-00805f9b34fb"], + source="local", +) diff --git a/tests/components/govee_ble/test_sensor.py b/tests/components/govee_ble/test_sensor.py index 0e52d2278c3..b27868c44bf 100644 --- a/tests/components/govee_ble/test_sensor.py +++ b/tests/components/govee_ble/test_sensor.py @@ -3,9 +3,13 @@ from homeassistant.components.govee_ble.const import DOMAIN from homeassistant.components.sensor import ATTR_STATE_CLASS -from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT +from homeassistant.const import ( + ATTR_FRIENDLY_NAME, + ATTR_UNIT_OF_MEASUREMENT, + STATE_UNAVAILABLE, +) -from . import GVH5075_SERVICE_INFO +from . import GVH5075_SERVICE_INFO, GVH5178_SERVICE_INFO_ERROR from tests.common import MockConfigEntry from tests.components.bluetooth import inject_bluetooth_service_info @@ -29,10 +33,33 @@ async def test_sensors(hass): temp_sensor = hass.states.get("sensor.h5075_2762_temperature") temp_sensor_attribtes = temp_sensor.attributes - assert temp_sensor.state == "21.34" + assert temp_sensor.state == "21.3" assert temp_sensor_attribtes[ATTR_FRIENDLY_NAME] == "H5075 2762 Temperature" assert temp_sensor_attribtes[ATTR_UNIT_OF_MEASUREMENT] == "°C" assert temp_sensor_attribtes[ATTR_STATE_CLASS] == "measurement" assert await hass.config_entries.async_unload(entry.entry_id) await hass.async_block_till_done() + + +async def test_gvh5178_error(hass): + """Test H5178 Remote in error marks state as unavailable.""" + entry = MockConfigEntry( + domain=DOMAIN, + unique_id="A4:C1:38:75:2B:C8", + ) + entry.add_to_hass(hass) + + assert await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert len(hass.states.async_all()) == 0 + inject_bluetooth_service_info(hass, GVH5178_SERVICE_INFO_ERROR) + await hass.async_block_till_done() + assert len(hass.states.async_all()) == 4 + + temp_sensor = hass.states.get("sensor.b51782bc8_remote_temperature") + assert temp_sensor.state == STATE_UNAVAILABLE + + assert await hass.config_entries.async_unload(entry.entry_id) + await hass.async_block_till_done() diff --git a/tests/components/harmony/test_select.py b/tests/components/harmony/test_select.py index 60bb85ed0c3..f9f14963105 100644 --- a/tests/components/harmony/test_select.py +++ b/tests/components/harmony/test_select.py @@ -9,7 +9,6 @@ from homeassistant.components.select import ( SERVICE_SELECT_OPTION, ) from homeassistant.const import ( - ATTR_DEVICE_CLASS, ATTR_ENTITY_ID, CONF_HOST, CONF_NAME, @@ -74,7 +73,6 @@ async def test_options(mock_hc, hass, mock_write_config): "Play Music", "Watch TV", ] - assert state.attributes.get(ATTR_DEVICE_CLASS) == "harmony__activities" async def test_select_option(mock_hc, hass, mock_write_config): diff --git a/tests/components/here_travel_time/test_init.py b/tests/components/here_travel_time/test_init.py index 18ef2e45410..682d8c560bb 100644 --- a/tests/components/here_travel_time/test_init.py +++ b/tests/components/here_travel_time/test_init.py @@ -1,9 +1,17 @@ """The test for the HERE Travel Time integration.""" +from datetime import datetime + import pytest from homeassistant.components.here_travel_time.config_flow import DEFAULT_OPTIONS -from homeassistant.components.here_travel_time.const import DOMAIN +from homeassistant.components.here_travel_time.const import ( + CONF_ARRIVAL_TIME, + CONF_DEPARTURE_TIME, + CONF_ROUTE_MODE, + DOMAIN, + ROUTE_MODE_FASTEST, +) from homeassistant.core import HomeAssistant from .const import DEFAULT_CONFIG @@ -12,13 +20,30 @@ from tests.common import MockConfigEntry @pytest.mark.usefixtures("valid_response") -async def test_unload_entry(hass: HomeAssistant) -> None: +@pytest.mark.parametrize( + "options", + [ + DEFAULT_OPTIONS, + { + CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, + CONF_DEPARTURE_TIME: datetime.now(), + }, + { + CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, + CONF_ARRIVAL_TIME: datetime.now(), + }, + { + CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, + }, + ], +) +async def test_unload_entry(hass: HomeAssistant, options) -> None: """Test that unloading an entry works.""" entry = MockConfigEntry( domain=DOMAIN, unique_id="0123456789", data=DEFAULT_CONFIG, - options=DEFAULT_OPTIONS, + options=options, ) entry.add_to_hass(hass) diff --git a/tests/components/here_travel_time/test_sensor.py b/tests/components/here_travel_time/test_sensor.py index 144ac063040..f9f12504891 100644 --- a/tests/components/here_travel_time/test_sensor.py +++ b/tests/components/here_travel_time/test_sensor.py @@ -1,14 +1,22 @@ """The test for the HERE Travel Time sensor platform.""" +from datetime import timedelta from unittest.mock import MagicMock, patch from here_routing import ( HERERoutingError, + HERERoutingTooManyRequestsError, Place, Return, RoutingMode, Spans, TransportMode, ) +from here_transit import ( + HERETransitDepartureArrivalTooCloseError, + HERETransitNoRouteFoundError, + HERETransitNoTransitRouteFoundError, + HERETransitTooManyRequestsError, +) import pytest from homeassistant.components.here_travel_time.config_flow import DEFAULT_OPTIONS @@ -22,6 +30,7 @@ from homeassistant.components.here_travel_time.const import ( CONF_ORIGIN_LATITUDE, CONF_ORIGIN_LONGITUDE, CONF_ROUTE_MODE, + DEFAULT_SCAN_INTERVAL, DOMAIN, ICON_BICYCLE, ICON_CAR, @@ -34,6 +43,7 @@ from homeassistant.components.here_travel_time.const import ( TRAVEL_MODE_PUBLIC, TRAVEL_MODE_TRUCK, ) +from homeassistant.components.here_travel_time.coordinator import BACKOFF_MULTIPLIER from homeassistant.components.sensor import ( ATTR_LAST_RESET, ATTR_STATE_CLASS, @@ -54,7 +64,9 @@ from homeassistant.const import ( ) from homeassistant.core import CoreState, HomeAssistant, State from homeassistant.setup import async_setup_component +from homeassistant.util.dt import utcnow +from .conftest import RESPONSE, TRANSIT_RESPONSE from .const import ( API_KEY, DEFAULT_CONFIG, @@ -64,7 +76,11 @@ from .const import ( ORIGIN_LONGITUDE, ) -from tests.common import MockConfigEntry, mock_restore_cache_with_extra_data +from tests.common import ( + MockConfigEntry, + async_fire_time_changed, + mock_restore_cache_with_extra_data, +) @pytest.mark.parametrize( @@ -583,3 +599,153 @@ async def test_restore_state(hass): state = hass.states.get("sensor.test_destination") assert state.state == "Destination Address 1" + + +@pytest.mark.parametrize( + "exception,expected_message", + [ + ( + HERETransitNoRouteFoundError, + "Error fetching here_travel_time data", + ), + ( + HERETransitNoTransitRouteFoundError, + "Error fetching here_travel_time data", + ), + ( + HERETransitDepartureArrivalTooCloseError, + "Ignoring HERETransitDepartureArrivalTooCloseError", + ), + ], +) +async def test_transit_errors(hass: HomeAssistant, caplog, exception, expected_message): + """Test that transit errors are correctly handled.""" + with patch( + "here_transit.HERETransitApi.route", + side_effect=exception(), + ): + entry = MockConfigEntry( + domain=DOMAIN, + unique_id="0123456789", + data={ + CONF_ORIGIN_LATITUDE: float(ORIGIN_LATITUDE), + CONF_ORIGIN_LONGITUDE: float(ORIGIN_LONGITUDE), + CONF_DESTINATION_LATITUDE: float(DESTINATION_LATITUDE), + CONF_DESTINATION_LONGITUDE: float(DESTINATION_LONGITUDE), + CONF_API_KEY: API_KEY, + CONF_MODE: TRAVEL_MODE_PUBLIC, + CONF_NAME: "test", + }, + options=DEFAULT_OPTIONS, + ) + entry.add_to_hass(hass) + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + hass.bus.async_fire(EVENT_HOMEASSISTANT_START) + await hass.async_block_till_done() + + assert expected_message in caplog.text + + +async def test_routing_rate_limit(hass: HomeAssistant, caplog): + """Test that rate limiting is applied when encountering HTTP 429.""" + with patch( + "here_routing.HERERoutingApi.route", + return_value=RESPONSE, + ): + entry = MockConfigEntry( + domain=DOMAIN, + unique_id="0123456789", + data=DEFAULT_CONFIG, + options=DEFAULT_OPTIONS, + ) + entry.add_to_hass(hass) + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + hass.bus.async_fire(EVENT_HOMEASSISTANT_START) + await hass.async_block_till_done() + + assert hass.states.get("sensor.test_distance").state == "13.682" + + with patch( + "here_routing.HERERoutingApi.route", + side_effect=HERERoutingTooManyRequestsError( + "Rate limit for this service has been reached" + ), + ): + async_fire_time_changed( + hass, utcnow() + timedelta(seconds=DEFAULT_SCAN_INTERVAL + 1) + ) + await hass.async_block_till_done() + + assert hass.states.get("sensor.test_distance").state == "unavailable" + assert "Increasing update interval to" in caplog.text + + with patch( + "here_routing.HERERoutingApi.route", + return_value=RESPONSE, + ): + async_fire_time_changed( + hass, + utcnow() + + timedelta(seconds=DEFAULT_SCAN_INTERVAL * BACKOFF_MULTIPLIER + 1), + ) + await hass.async_block_till_done() + assert hass.states.get("sensor.test_distance").state == "13.682" + assert "Resetting update interval to" in caplog.text + + +async def test_transit_rate_limit(hass: HomeAssistant, caplog): + """Test that rate limiting is applied when encountering HTTP 429.""" + with patch( + "here_transit.HERETransitApi.route", + return_value=TRANSIT_RESPONSE, + ): + entry = MockConfigEntry( + domain=DOMAIN, + unique_id="0123456789", + data={ + CONF_ORIGIN_LATITUDE: float(ORIGIN_LATITUDE), + CONF_ORIGIN_LONGITUDE: float(ORIGIN_LONGITUDE), + CONF_DESTINATION_LATITUDE: float(DESTINATION_LATITUDE), + CONF_DESTINATION_LONGITUDE: float(DESTINATION_LONGITUDE), + CONF_API_KEY: API_KEY, + CONF_MODE: TRAVEL_MODE_PUBLIC, + CONF_NAME: "test", + }, + options=DEFAULT_OPTIONS, + ) + entry.add_to_hass(hass) + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + hass.bus.async_fire(EVENT_HOMEASSISTANT_START) + await hass.async_block_till_done() + + assert hass.states.get("sensor.test_distance").state == "1.883" + + with patch( + "here_transit.HERETransitApi.route", + side_effect=HERETransitTooManyRequestsError( + "Rate limit for this service has been reached" + ), + ): + async_fire_time_changed( + hass, utcnow() + timedelta(seconds=DEFAULT_SCAN_INTERVAL + 1) + ) + await hass.async_block_till_done() + + assert hass.states.get("sensor.test_distance").state == "unavailable" + assert "Increasing update interval to" in caplog.text + + with patch( + "here_transit.HERETransitApi.route", + return_value=TRANSIT_RESPONSE, + ): + async_fire_time_changed( + hass, + utcnow() + + timedelta(seconds=DEFAULT_SCAN_INTERVAL * BACKOFF_MULTIPLIER + 1), + ) + await hass.async_block_till_done() + assert hass.states.get("sensor.test_distance").state == "1.883" + assert "Resetting update interval to" in caplog.text diff --git a/tests/components/homeassistant_alerts/fixtures/alerts_1.json b/tests/components/homeassistant_alerts/fixtures/alerts_1.json index 0f480f66b31..4462f7020d2 100644 --- a/tests/components/homeassistant_alerts/fixtures/alerts_1.json +++ b/tests/components/homeassistant_alerts/fixtures/alerts_1.json @@ -1,5 +1,6 @@ [ { + "id": "aladdin_connect", "title": "Aladdin Connect is turning off their previous connection method", "created": "2022-07-14T06:00:00.000Z", "integrations": [ @@ -15,6 +16,7 @@ "alert_url": "https://alerts.home-assistant.io/#aladdin_connect.markdown" }, { + "id": "dark_sky", "title": "Dark Sky API closed for new users", "created": "2020-03-31T14:40:00.000Z", "integrations": [ @@ -31,6 +33,7 @@ "alert_url": "https://alerts.home-assistant.io/#dark_sky.markdown" }, { + "id": "hassio", "title": "Supervisor November beta issue impacting users on Home Assistant beta/dev channels", "created": "2022-11-16T06:00:00.000Z", "integrations": [ @@ -51,6 +54,7 @@ "alert_url": "https://alerts.home-assistant.io/#hassio.markdown" }, { + "id": "hikvision", "title": "Hikvision Security Vulnerability", "created": "2021-09-20T22:08:00.000Z", "integrations": [ @@ -68,6 +72,7 @@ } }, { + "id": "hive_us", "title": "Hive shutting down North American Servers", "created": "2021-11-13T13:58:00.000Z", "integrations": [ @@ -83,6 +88,7 @@ "alert_url": "https://alerts.home-assistant.io/#hive_us.markdown" }, { + "id": "homematicip_cloud", "title": "HomematicIP (EQ-3) blocks public IP addresses, if access to the Cloud is too frequent.", "created": "2020-12-20T12:00:00.000Z", "integrations": [ @@ -103,6 +109,7 @@ "alert_url": "https://alerts.home-assistant.io/#homematicip_cloud.markdown" }, { + "id": "logi_circle", "title": "Logitech no longer accepting API applications", "created": "2022-05-16T12:00:00.000Z", "integrations": [ @@ -119,6 +126,7 @@ "alert_url": "https://alerts.home-assistant.io/#logi_circle.markdown" }, { + "id": "neato", "title": "New Neato Botvacs Do Not Support Existing API", "created": "2021-12-20T13:27:00.000Z", "integrations": [ @@ -134,6 +142,7 @@ "alert_url": "https://alerts.home-assistant.io/#neato.markdown" }, { + "id": "nest", "title": "Nest Desktop Auth Deprecation", "created": "2022-05-12T14:04:00.000Z", "integrations": [ @@ -149,6 +158,7 @@ } }, { + "id": "senseme", "title": "Haiku Firmware Update Protocol Change", "created": "2022-04-05T00:00:00.000Z", "integrations": [ @@ -163,6 +173,7 @@ } }, { + "id": "sochain", "title": "The SoChain integration is disabled due to a dependency conflict", "created": "2022-02-01T00:00:00.000Z", "integrations": [ @@ -177,6 +188,7 @@ } }, { + "id": "yeelight", "title": "Yeelight-manufactured Xiaomi-branded devices removed Local Control", "created": "2021-03-29T06:00:00.000Z", "integrations": [ diff --git a/tests/components/homeassistant_alerts/fixtures/alerts_2.json b/tests/components/homeassistant_alerts/fixtures/alerts_2.json index 2941d9da143..55f8f5ffae1 100644 --- a/tests/components/homeassistant_alerts/fixtures/alerts_2.json +++ b/tests/components/homeassistant_alerts/fixtures/alerts_2.json @@ -1,5 +1,6 @@ [ { + "id": "dark_sky", "title": "Dark Sky API closed for new users", "created": "2020-03-31T14:40:00.000Z", "integrations": [ @@ -16,6 +17,7 @@ "alert_url": "https://alerts.home-assistant.io/#dark_sky.markdown" }, { + "id": "hikvision", "title": "Hikvision Security Vulnerability", "created": "2021-09-20T22:08:00.000Z", "integrations": [ @@ -33,6 +35,7 @@ } }, { + "id": "hive_us", "title": "Hive shutting down North American Servers", "created": "2021-11-13T13:58:00.000Z", "integrations": [ @@ -48,6 +51,7 @@ "alert_url": "https://alerts.home-assistant.io/#hive_us.markdown" }, { + "id": "homematicip_cloud", "title": "HomematicIP (EQ-3) blocks public IP addresses, if access to the Cloud is too frequent.", "created": "2020-12-20T12:00:00.000Z", "integrations": [ @@ -68,6 +72,7 @@ "alert_url": "https://alerts.home-assistant.io/#homematicip_cloud.markdown" }, { + "id": "logi_circle", "title": "Logitech no longer accepting API applications", "created": "2022-05-16T12:00:00.000Z", "integrations": [ @@ -84,6 +89,7 @@ "alert_url": "https://alerts.home-assistant.io/#logi_circle.markdown" }, { + "id": "neato", "title": "New Neato Botvacs Do Not Support Existing API", "created": "2021-12-20T13:27:00.000Z", "integrations": [ @@ -99,6 +105,7 @@ "alert_url": "https://alerts.home-assistant.io/#neato.markdown" }, { + "id": "nest", "title": "Nest Desktop Auth Deprecation", "created": "2022-05-12T14:04:00.000Z", "integrations": [ @@ -114,6 +121,7 @@ } }, { + "id": "senseme", "title": "Haiku Firmware Update Protocol Change", "created": "2022-04-05T00:00:00.000Z", "integrations": [ @@ -128,6 +136,7 @@ } }, { + "id": "sochain", "title": "The SoChain integration is disabled due to a dependency conflict", "created": "2022-02-01T00:00:00.000Z", "integrations": [ @@ -142,6 +151,7 @@ } }, { + "id": "yeelight", "title": "Yeelight-manufactured Xiaomi-branded devices removed Local Control", "created": "2021-03-29T06:00:00.000Z", "integrations": [ diff --git a/tests/components/homeassistant_alerts/fixtures/alerts_no_integrations.json b/tests/components/homeassistant_alerts/fixtures/alerts_no_integrations.json index 25ce79d7e7c..5b798b3cd53 100644 --- a/tests/components/homeassistant_alerts/fixtures/alerts_no_integrations.json +++ b/tests/components/homeassistant_alerts/fixtures/alerts_no_integrations.json @@ -1,5 +1,6 @@ [ { + "id": "dark_sky", "title": "Dark Sky API closed for new users", "created": "2020-03-31T14:40:00.000Z", "integrations": [ @@ -16,6 +17,7 @@ "alert_url": "https://alerts.home-assistant.io/#dark_sky.markdown" }, { + "id": "hikvision", "title": "Hikvision Security Vulnerability", "created": "2021-09-20T22:08:00.000Z", "filename": "hikvision.markdown", diff --git a/tests/components/homeassistant_alerts/fixtures/alerts_no_package.json b/tests/components/homeassistant_alerts/fixtures/alerts_no_package.json index bcc0a0223ee..de506b0a2d4 100644 --- a/tests/components/homeassistant_alerts/fixtures/alerts_no_package.json +++ b/tests/components/homeassistant_alerts/fixtures/alerts_no_package.json @@ -1,5 +1,6 @@ [ { + "id": "dark_sky", "title": "Dark Sky API closed for new users", "created": "2020-03-31T14:40:00.000Z", "integrations": [ @@ -16,6 +17,7 @@ "alert_url": "https://alerts.home-assistant.io/#dark_sky.markdown" }, { + "id": "hikvision", "title": "Hikvision Security Vulnerability", "created": "2021-09-20T22:08:00.000Z", "integrations": [ diff --git a/tests/components/homeassistant_alerts/test_init.py b/tests/components/homeassistant_alerts/test_init.py index 41fdff425b3..cb1f5f169fe 100644 --- a/tests/components/homeassistant_alerts/test_init.py +++ b/tests/components/homeassistant_alerts/test_init.py @@ -17,15 +17,11 @@ from tests.common import assert_lists_same, async_fire_time_changed, load_fixtur from tests.test_util.aiohttp import AiohttpClientMocker -def stub_alert(aioclient_mock, filename): +def stub_alert(aioclient_mock, alert_id): """Stub an alert.""" aioclient_mock.get( - f"https://alerts.home-assistant.io/alerts/{filename}", - text=f"""--- -title: Title for {filename} ---- -Content for {filename} -""", + f"https://alerts.home-assistant.io/alerts/{alert_id}.json", + json={"title": f"Title for {alert_id}", "content": f"Content for {alert_id}"}, ) @@ -42,50 +38,50 @@ async def setup_repairs(hass): "2022.7.0", {"version": "2022.11.0"}, [ - ("aladdin_connect.markdown", "aladdin_connect"), - ("dark_sky.markdown", "darksky"), - ("hassio.markdown", "hassio"), - ("hikvision.markdown", "hikvision"), - ("hikvision.markdown", "hikvisioncam"), - ("hive_us.markdown", "hive"), - ("homematicip_cloud.markdown", "homematicip_cloud"), - ("logi_circle.markdown", "logi_circle"), - ("neato.markdown", "neato"), - ("nest.markdown", "nest"), - ("senseme.markdown", "senseme"), - ("sochain.markdown", "sochain"), + ("aladdin_connect", "aladdin_connect"), + ("dark_sky", "darksky"), + ("hassio", "hassio"), + ("hikvision", "hikvision"), + ("hikvision", "hikvisioncam"), + ("hive_us", "hive"), + ("homematicip_cloud", "homematicip_cloud"), + ("logi_circle", "logi_circle"), + ("neato", "neato"), + ("nest", "nest"), + ("senseme", "senseme"), + ("sochain", "sochain"), ], ), ( "2022.8.0", {"version": "2022.11.1"}, [ - ("dark_sky.markdown", "darksky"), - ("hikvision.markdown", "hikvision"), - ("hikvision.markdown", "hikvisioncam"), - ("hive_us.markdown", "hive"), - ("homematicip_cloud.markdown", "homematicip_cloud"), - ("logi_circle.markdown", "logi_circle"), - ("neato.markdown", "neato"), - ("nest.markdown", "nest"), - ("senseme.markdown", "senseme"), - ("sochain.markdown", "sochain"), + ("dark_sky", "darksky"), + ("hikvision", "hikvision"), + ("hikvision", "hikvisioncam"), + ("hive_us", "hive"), + ("homematicip_cloud", "homematicip_cloud"), + ("logi_circle", "logi_circle"), + ("neato", "neato"), + ("nest", "nest"), + ("senseme", "senseme"), + ("sochain", "sochain"), ], ), ( "2021.10.0", None, [ - ("aladdin_connect.markdown", "aladdin_connect"), - ("dark_sky.markdown", "darksky"), - ("hikvision.markdown", "hikvision"), - ("hikvision.markdown", "hikvisioncam"), - ("homematicip_cloud.markdown", "homematicip_cloud"), - ("logi_circle.markdown", "logi_circle"), - ("neato.markdown", "neato"), - ("nest.markdown", "nest"), - ("senseme.markdown", "senseme"), - ("sochain.markdown", "sochain"), + ("aladdin_connect", "aladdin_connect"), + ("dark_sky", "darksky"), + ("hikvision", "hikvision"), + ("hikvision", "hikvisioncam"), + ("homematicip_cloud", "homematicip_cloud"), + ("logi_circle", "logi_circle"), + ("neato", "neato"), + ("nest", "nest"), + ("senseme", "senseme"), + ("sochain", "sochain"), ], ), ), @@ -153,17 +149,17 @@ async def test_alerts( "domain": "homeassistant_alerts", "ignored": False, "is_fixable": False, - "issue_id": f"{alert}_{integration}", + "issue_id": f"{alert_id}.markdown_{integration}", "issue_domain": integration, "learn_more_url": None, "severity": "warning", "translation_key": "alert", "translation_placeholders": { - "title": f"Title for {alert}", - "description": f"Content for {alert}", + "title": f"Title for {alert_id}", + "description": f"Content for {alert_id}", }, } - for alert, integration in expected_alerts + for alert_id, integration in expected_alerts ] } @@ -175,15 +171,15 @@ async def test_alerts( "2022.7.0", "alerts_no_integrations.json", [ - ("dark_sky.markdown", "darksky"), + ("dark_sky", "darksky"), ], ), ( "2022.7.0", "alerts_no_package.json", [ - ("dark_sky.markdown", "darksky"), - ("hikvision.markdown", "hikvision"), + ("dark_sky", "darksky"), + ("hikvision", "hikvision"), ], ), ), @@ -204,7 +200,7 @@ async def test_bad_alerts( text=fixture_content, ) for alert in json.loads(fixture_content): - stub_alert(aioclient_mock, alert["filename"]) + stub_alert(aioclient_mock, alert["id"]) activated_components = ( "darksky", @@ -234,17 +230,17 @@ async def test_bad_alerts( "domain": "homeassistant_alerts", "ignored": False, "is_fixable": False, - "issue_id": f"{alert}_{integration}", + "issue_id": f"{alert_id}.markdown_{integration}", "issue_domain": integration, "learn_more_url": None, "severity": "warning", "translation_key": "alert", "translation_placeholders": { - "title": f"Title for {alert}", - "description": f"Content for {alert}", + "title": f"Title for {alert_id}", + "description": f"Content for {alert_id}", }, } - for alert, integration in expected_alerts + for alert_id, integration in expected_alerts ] } @@ -279,60 +275,60 @@ async def test_no_alerts( "2022.7.0", "alerts_1.json", [ - ("aladdin_connect.markdown", "aladdin_connect"), - ("dark_sky.markdown", "darksky"), - ("hikvision.markdown", "hikvision"), - ("hikvision.markdown", "hikvisioncam"), - ("hive_us.markdown", "hive"), - ("homematicip_cloud.markdown", "homematicip_cloud"), - ("logi_circle.markdown", "logi_circle"), - ("neato.markdown", "neato"), - ("nest.markdown", "nest"), - ("senseme.markdown", "senseme"), - ("sochain.markdown", "sochain"), + ("aladdin_connect", "aladdin_connect"), + ("dark_sky", "darksky"), + ("hikvision", "hikvision"), + ("hikvision", "hikvisioncam"), + ("hive_us", "hive"), + ("homematicip_cloud", "homematicip_cloud"), + ("logi_circle", "logi_circle"), + ("neato", "neato"), + ("nest", "nest"), + ("senseme", "senseme"), + ("sochain", "sochain"), ], "alerts_2.json", [ - ("dark_sky.markdown", "darksky"), - ("hikvision.markdown", "hikvision"), - ("hikvision.markdown", "hikvisioncam"), - ("hive_us.markdown", "hive"), - ("homematicip_cloud.markdown", "homematicip_cloud"), - ("logi_circle.markdown", "logi_circle"), - ("neato.markdown", "neato"), - ("nest.markdown", "nest"), - ("senseme.markdown", "senseme"), - ("sochain.markdown", "sochain"), + ("dark_sky", "darksky"), + ("hikvision", "hikvision"), + ("hikvision", "hikvisioncam"), + ("hive_us", "hive"), + ("homematicip_cloud", "homematicip_cloud"), + ("logi_circle", "logi_circle"), + ("neato", "neato"), + ("nest", "nest"), + ("senseme", "senseme"), + ("sochain", "sochain"), ], ), ( "2022.7.0", "alerts_2.json", [ - ("dark_sky.markdown", "darksky"), - ("hikvision.markdown", "hikvision"), - ("hikvision.markdown", "hikvisioncam"), - ("hive_us.markdown", "hive"), - ("homematicip_cloud.markdown", "homematicip_cloud"), - ("logi_circle.markdown", "logi_circle"), - ("neato.markdown", "neato"), - ("nest.markdown", "nest"), - ("senseme.markdown", "senseme"), - ("sochain.markdown", "sochain"), + ("dark_sky", "darksky"), + ("hikvision", "hikvision"), + ("hikvision", "hikvisioncam"), + ("hive_us", "hive"), + ("homematicip_cloud", "homematicip_cloud"), + ("logi_circle", "logi_circle"), + ("neato", "neato"), + ("nest", "nest"), + ("senseme", "senseme"), + ("sochain", "sochain"), ], "alerts_1.json", [ - ("aladdin_connect.markdown", "aladdin_connect"), - ("dark_sky.markdown", "darksky"), - ("hikvision.markdown", "hikvision"), - ("hikvision.markdown", "hikvisioncam"), - ("hive_us.markdown", "hive"), - ("homematicip_cloud.markdown", "homematicip_cloud"), - ("logi_circle.markdown", "logi_circle"), - ("neato.markdown", "neato"), - ("nest.markdown", "nest"), - ("senseme.markdown", "senseme"), - ("sochain.markdown", "sochain"), + ("aladdin_connect", "aladdin_connect"), + ("dark_sky", "darksky"), + ("hikvision", "hikvision"), + ("hikvision", "hikvisioncam"), + ("hive_us", "hive"), + ("homematicip_cloud", "homematicip_cloud"), + ("logi_circle", "logi_circle"), + ("neato", "neato"), + ("nest", "nest"), + ("senseme", "senseme"), + ("sochain", "sochain"), ], ), ), @@ -355,7 +351,7 @@ async def test_alerts_change( text=fixture_1_content, ) for alert in json.loads(fixture_1_content): - stub_alert(aioclient_mock, alert["filename"]) + stub_alert(aioclient_mock, alert["id"]) activated_components = ( "aladdin_connect", @@ -396,17 +392,17 @@ async def test_alerts_change( "domain": "homeassistant_alerts", "ignored": False, "is_fixable": False, - "issue_id": f"{alert}_{integration}", + "issue_id": f"{alert_id}.markdown_{integration}", "issue_domain": integration, "learn_more_url": None, "severity": "warning", "translation_key": "alert", "translation_placeholders": { - "title": f"Title for {alert}", - "description": f"Content for {alert}", + "title": f"Title for {alert_id}", + "description": f"Content for {alert_id}", }, } - for alert, integration in expected_alerts_1 + for alert_id, integration in expected_alerts_1 ], ) @@ -417,7 +413,7 @@ async def test_alerts_change( text=fixture_2_content, ) for alert in json.loads(fixture_2_content): - stub_alert(aioclient_mock, alert["filename"]) + stub_alert(aioclient_mock, alert["id"]) future = now + UPDATE_INTERVAL + timedelta(seconds=1) async_fire_time_changed(hass, future) @@ -436,16 +432,16 @@ async def test_alerts_change( "domain": "homeassistant_alerts", "ignored": False, "is_fixable": False, - "issue_id": f"{alert}_{integration}", + "issue_id": f"{alert_id}.markdown_{integration}", "issue_domain": integration, "learn_more_url": None, "severity": "warning", "translation_key": "alert", "translation_placeholders": { - "title": f"Title for {alert}", - "description": f"Content for {alert}", + "title": f"Title for {alert_id}", + "description": f"Content for {alert_id}", }, } - for alert, integration in expected_alerts_2 + for alert_id, integration in expected_alerts_2 ], ) diff --git a/tests/components/homeassistant_hardware/test_silabs_multiprotocol_addon.py b/tests/components/homeassistant_hardware/test_silabs_multiprotocol_addon.py index fbf6cdda4c7..0d703b04bfb 100644 --- a/tests/components/homeassistant_hardware/test_silabs_multiprotocol_addon.py +++ b/tests/components/homeassistant_hardware/test_silabs_multiprotocol_addon.py @@ -79,6 +79,10 @@ class TestOptionsFlow(silabs_multiprotocol_addon.OptionsFlowHandler): """Return the ZHA name.""" return "Test Multi-PAN" + def _hardware_name(self) -> str: + """Return the name of the hardware.""" + return "Test" + @pytest.fixture(autouse=True) def config_flow_handler( diff --git a/tests/components/homekit/test_get_accessories.py b/tests/components/homekit/test_get_accessories.py index 12113ada5cb..320407f480a 100644 --- a/tests/components/homekit/test_get_accessories.py +++ b/tests/components/homekit/test_get_accessories.py @@ -258,7 +258,6 @@ def test_type_media_player(type_name, entity_id, state, attrs, config): {ATTR_DEVICE_CLASS: "humidity", ATTR_UNIT_OF_MEASUREMENT: PERCENTAGE}, ), ("LightSensor", "sensor.light", "900", {ATTR_DEVICE_CLASS: "illuminance"}), - ("LightSensor", "sensor.light", "900", {ATTR_UNIT_OF_MEASUREMENT: "lm"}), ("LightSensor", "sensor.light", "900", {ATTR_UNIT_OF_MEASUREMENT: LIGHT_LUX}), ( "TemperatureSensor", diff --git a/tests/components/homekit/test_type_thermostats.py b/tests/components/homekit/test_type_thermostats.py index 33b45e54081..902c70aba5d 100644 --- a/tests/components/homekit/test_type_thermostats.py +++ b/tests/components/homekit/test_type_thermostats.py @@ -2445,7 +2445,6 @@ async def test_thermostat_with_supported_features_target_temp_but_fan_mode_set( ATTR_FAN_MODE: FAN_AUTO, ATTR_FAN_MODES: None, ATTR_HVAC_ACTION: HVACAction.IDLE, - ATTR_FAN_MODE: FAN_AUTO, ATTR_PRESET_MODE: "home", ATTR_FRIENDLY_NAME: "Rec Room", ATTR_HVAC_MODES: [ diff --git a/tests/components/homekit_controller/specific_devices/test_nanoleaf_strip_nl55.py b/tests/components/homekit_controller/specific_devices/test_nanoleaf_strip_nl55.py index 4afb61b19f3..2bbd6bf3a10 100644 --- a/tests/components/homekit_controller/specific_devices/test_nanoleaf_strip_nl55.py +++ b/tests/components/homekit_controller/specific_devices/test_nanoleaf_strip_nl55.py @@ -57,6 +57,16 @@ async def test_nanoleaf_nl55_setup(hass): friendly_name="Nanoleaf Strip 3B32 Thread Capabilities", unique_id="00:00:00:00:00:00_1_31_115", entity_category=EntityCategory.DIAGNOSTIC, + capabilities={ + "options": [ + "border_router_capable", + "full", + "minimal", + "none", + "router_eligible", + "sleepy", + ] + }, state="border_router_capable", ), EntityTestInfo( @@ -64,6 +74,17 @@ async def test_nanoleaf_nl55_setup(hass): friendly_name="Nanoleaf Strip 3B32 Thread Status", unique_id="00:00:00:00:00:00_1_31_117", entity_category=EntityCategory.DIAGNOSTIC, + capabilities={ + "options": [ + "border_router", + "child", + "detached", + "disabled", + "joining", + "leader", + "router", + ] + }, state="border_router", ), ], diff --git a/tests/components/homematicip_cloud/test_device.py b/tests/components/homematicip_cloud/test_device.py index 327f53f129c..597a82ea810 100644 --- a/tests/components/homematicip_cloud/test_device.py +++ b/tests/components/homematicip_cloud/test_device.py @@ -22,7 +22,7 @@ async def test_hmip_load_all_supported_devices(hass, default_mock_hap_factory): test_devices=None, test_groups=None ) - assert len(mock_hap.hmip_device_by_entity_id) == 267 + assert len(mock_hap.hmip_device_by_entity_id) == 270 async def test_hmip_remove_device(hass, default_mock_hap_factory): diff --git a/tests/components/homematicip_cloud/test_light.py b/tests/components/homematicip_cloud/test_light.py index 8286acbb1d4..9baa6900d1a 100644 --- a/tests/components/homematicip_cloud/test_light.py +++ b/tests/components/homematicip_cloud/test_light.py @@ -325,3 +325,174 @@ async def test_hmip_wired_multi_dimmer(hass, default_mock_hap_factory): ha_state = hass.states.get(entity_id) assert ha_state.state == STATE_OFF assert not ha_state.attributes.get(ATTR_BRIGHTNESS) + + +async def test_hmip_din_rail_dimmer_3_channel1(hass, default_mock_hap_factory): + """Test HomematicIP DinRailDimmer3 Channel 1.""" + entity_id = "light.3_dimmer_channel1" + entity_name = "3-Dimmer Channel1" + device_model = "HmIP-DRDI3" + mock_hap = await default_mock_hap_factory.async_get_mock_hap( + test_devices=["3-Dimmer"] + ) + + ha_state, hmip_device = get_and_check_entity_basics( + hass, mock_hap, entity_id, entity_name, device_model + ) + + assert ha_state.state == STATE_ON + assert ha_state.attributes[ATTR_SUPPORTED_COLOR_MODES] == [ColorMode.BRIGHTNESS] + assert ha_state.attributes[ATTR_SUPPORTED_FEATURES] == 0 + service_call_counter = len(hmip_device.mock_calls) + + await hass.services.async_call( + "light", "turn_on", {"entity_id": entity_id}, blocking=True + ) + assert hmip_device.mock_calls[-1][0] == "set_dim_level" + assert hmip_device.mock_calls[-1][1] == (1, 1) + + await hass.services.async_call( + "light", + "turn_on", + {"entity_id": entity_id, "brightness": "100"}, + blocking=True, + ) + assert len(hmip_device.mock_calls) == service_call_counter + 2 + assert hmip_device.mock_calls[-1][0] == "set_dim_level" + assert hmip_device.mock_calls[-1][1] == (0.39215686274509803, 1) + await async_manipulate_test_data(hass, hmip_device, "dimLevel", 1, channel=1) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_ON + assert ha_state.attributes[ATTR_BRIGHTNESS] == 255 + assert ha_state.attributes[ATTR_COLOR_MODE] == ColorMode.BRIGHTNESS + assert ha_state.attributes[ATTR_SUPPORTED_COLOR_MODES] == [ColorMode.BRIGHTNESS] + assert ha_state.attributes[ATTR_SUPPORTED_FEATURES] == 0 + + await hass.services.async_call( + "light", "turn_off", {"entity_id": entity_id}, blocking=True + ) + assert len(hmip_device.mock_calls) == service_call_counter + 4 + assert hmip_device.mock_calls[-1][0] == "set_dim_level" + assert hmip_device.mock_calls[-1][1] == (0, 1) + await async_manipulate_test_data(hass, hmip_device, "dimLevel", 0, channel=1) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_OFF + + await async_manipulate_test_data(hass, hmip_device, "dimLevel", None, channel=1) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_OFF + assert not ha_state.attributes.get(ATTR_BRIGHTNESS) + + +async def test_hmip_din_rail_dimmer_3_channel2(hass, default_mock_hap_factory): + """Test HomematicIP DinRailDimmer3 Channel 2.""" + entity_id = "light.3_dimmer_channel2" + entity_name = "3-Dimmer Channel2" + device_model = "HmIP-DRDI3" + mock_hap = await default_mock_hap_factory.async_get_mock_hap( + test_devices=["3-Dimmer"] + ) + + ha_state, hmip_device = get_and_check_entity_basics( + hass, mock_hap, entity_id, entity_name, device_model + ) + + assert ha_state.state == STATE_ON + assert ha_state.attributes[ATTR_SUPPORTED_COLOR_MODES] == [ColorMode.BRIGHTNESS] + assert ha_state.attributes[ATTR_SUPPORTED_FEATURES] == 0 + service_call_counter = len(hmip_device.mock_calls) + + await hass.services.async_call( + "light", "turn_on", {"entity_id": entity_id}, blocking=True + ) + assert hmip_device.mock_calls[-1][0] == "set_dim_level" + assert hmip_device.mock_calls[-1][1] == (1, 2) + + await hass.services.async_call( + "light", + "turn_on", + {"entity_id": entity_id, "brightness": "100"}, + blocking=True, + ) + assert len(hmip_device.mock_calls) == service_call_counter + 2 + assert hmip_device.mock_calls[-1][0] == "set_dim_level" + assert hmip_device.mock_calls[-1][1] == (0.39215686274509803, 2) + await async_manipulate_test_data(hass, hmip_device, "dimLevel", 1, channel=2) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_ON + assert ha_state.attributes[ATTR_BRIGHTNESS] == 255 + assert ha_state.attributes[ATTR_COLOR_MODE] == ColorMode.BRIGHTNESS + assert ha_state.attributes[ATTR_SUPPORTED_COLOR_MODES] == [ColorMode.BRIGHTNESS] + assert ha_state.attributes[ATTR_SUPPORTED_FEATURES] == 0 + + await hass.services.async_call( + "light", "turn_off", {"entity_id": entity_id}, blocking=True + ) + assert len(hmip_device.mock_calls) == service_call_counter + 4 + assert hmip_device.mock_calls[-1][0] == "set_dim_level" + assert hmip_device.mock_calls[-1][1] == (0, 2) + await async_manipulate_test_data(hass, hmip_device, "dimLevel", 0, channel=2) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_OFF + + await async_manipulate_test_data(hass, hmip_device, "dimLevel", None, channel=2) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_OFF + assert not ha_state.attributes.get(ATTR_BRIGHTNESS) + + +async def test_hmip_din_rail_dimmer_3_channel3(hass, default_mock_hap_factory): + """Test HomematicIP DinRailDimmer3 Channel 3.""" + entity_id = "light.esstisch" + entity_name = "Esstisch" + device_model = "HmIP-DRDI3" + mock_hap = await default_mock_hap_factory.async_get_mock_hap( + test_devices=["3-Dimmer"] + ) + + ha_state, hmip_device = get_and_check_entity_basics( + hass, mock_hap, entity_id, entity_name, device_model + ) + + assert ha_state.state == STATE_ON + assert ha_state.attributes[ATTR_SUPPORTED_COLOR_MODES] == [ColorMode.BRIGHTNESS] + assert ha_state.attributes[ATTR_SUPPORTED_FEATURES] == 0 + service_call_counter = len(hmip_device.mock_calls) + + await hass.services.async_call( + "light", "turn_on", {"entity_id": entity_id}, blocking=True + ) + assert hmip_device.mock_calls[-1][0] == "set_dim_level" + assert hmip_device.mock_calls[-1][1] == (1, 3) + + await hass.services.async_call( + "light", + "turn_on", + {"entity_id": entity_id, "brightness": "100"}, + blocking=True, + ) + assert len(hmip_device.mock_calls) == service_call_counter + 2 + assert hmip_device.mock_calls[-1][0] == "set_dim_level" + assert hmip_device.mock_calls[-1][1] == (0.39215686274509803, 3) + await async_manipulate_test_data(hass, hmip_device, "dimLevel", 1, channel=3) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_ON + assert ha_state.attributes[ATTR_BRIGHTNESS] == 255 + assert ha_state.attributes[ATTR_COLOR_MODE] == ColorMode.BRIGHTNESS + assert ha_state.attributes[ATTR_SUPPORTED_COLOR_MODES] == [ColorMode.BRIGHTNESS] + assert ha_state.attributes[ATTR_SUPPORTED_FEATURES] == 0 + + await hass.services.async_call( + "light", "turn_off", {"entity_id": entity_id}, blocking=True + ) + assert len(hmip_device.mock_calls) == service_call_counter + 4 + assert hmip_device.mock_calls[-1][0] == "set_dim_level" + assert hmip_device.mock_calls[-1][1] == (0, 3) + await async_manipulate_test_data(hass, hmip_device, "dimLevel", 0, channel=3) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_OFF + + await async_manipulate_test_data(hass, hmip_device, "dimLevel", None, channel=3) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_OFF + assert not ha_state.attributes.get(ATTR_BRIGHTNESS) diff --git a/tests/components/homewizard/test_config_flow.py b/tests/components/homewizard/test_config_flow.py index cdd67cbf7ea..453a29c74f8 100644 --- a/tests/components/homewizard/test_config_flow.py +++ b/tests/components/homewizard/test_config_flow.py @@ -46,8 +46,7 @@ async def test_manual_flow_works(hass, aioclient_mock): assert len(hass.config_entries.async_entries(DOMAIN)) == 1 - assert len(device.device.mock_calls) == 1 - assert len(device.close.mock_calls) == 1 + assert len(device.close.mock_calls) == len(device.device.mock_calls) assert len(mock_setup_entry.mock_calls) == 1 @@ -142,8 +141,7 @@ async def test_config_flow_imports_entry(aioclient_mock, hass): assert result["data"][CONF_IP_ADDRESS] == "1.2.3.4" assert len(hass.config_entries.async_entries(DOMAIN)) == 1 - assert len(device.device.mock_calls) == 1 - assert len(device.close.mock_calls) == 1 + assert len(device.device.mock_calls) == len(device.close.mock_calls) assert len(mock_setup_entry.mock_calls) == 1 @@ -174,19 +172,25 @@ async def test_discovery_disabled_api(hass, aioclient_mock): assert result["type"] == FlowResultType.FORM + def mock_initialize(): + raise DisabledError + + device = get_mock_device() + device.device.side_effect = mock_initialize + with patch( "homeassistant.components.homewizard.async_setup_entry", return_value=True, ), patch( "homeassistant.components.homewizard.config_flow.HomeWizardEnergy", - return_value=get_mock_device(), + return_value=device, ): result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={"ip_address": "192.168.43.183"} ) - assert result["type"] == FlowResultType.ABORT - assert result["reason"] == "api_not_enabled" + assert result["type"] == FlowResultType.FORM + assert result["errors"] == {"base": "api_not_enabled"} async def test_discovery_missing_data_in_service_info(hass, aioclient_mock): @@ -271,8 +275,8 @@ async def test_check_disabled_api(hass, aioclient_mock): result["flow_id"], {CONF_IP_ADDRESS: "2.2.2.2"} ) - assert result["type"] == FlowResultType.ABORT - assert result["reason"] == "api_not_enabled" + assert result["type"] == FlowResultType.FORM + assert result["errors"] == {"base": "api_not_enabled"} async def test_check_error_handling_api(hass, aioclient_mock): @@ -355,5 +359,71 @@ async def test_check_requesterror(hass, aioclient_mock): result["flow_id"], {CONF_IP_ADDRESS: "2.2.2.2"} ) - assert result["type"] == FlowResultType.ABORT - assert result["reason"] == "unknown_error" + assert result["type"] == FlowResultType.FORM + assert result["errors"] == {"base": "network_error"} + + +async def test_reauth_flow(hass, aioclient_mock): + """Test reauth flow while API is enabled.""" + + mock_entry = MockConfigEntry( + domain="homewizard_energy", data={CONF_IP_ADDRESS: "1.2.3.4"} + ) + + mock_entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={ + "source": config_entries.SOURCE_REAUTH, + "entry_id": mock_entry.entry_id, + }, + ) + + assert result["type"] == "form" + assert result["step_id"] == "reauth_confirm" + + device = get_mock_device() + with patch( + "homeassistant.components.homewizard.config_flow.HomeWizardEnergy", + return_value=device, + ): + result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) + + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "reauth_successful" + + +async def test_reauth_error(hass, aioclient_mock): + """Test reauth flow while API is still disabled.""" + + def mock_initialize(): + raise DisabledError() + + mock_entry = MockConfigEntry( + domain="homewizard_energy", data={CONF_IP_ADDRESS: "1.2.3.4"} + ) + + mock_entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={ + "source": config_entries.SOURCE_REAUTH, + "entry_id": mock_entry.entry_id, + }, + ) + + assert result["type"] == "form" + assert result["step_id"] == "reauth_confirm" + + device = get_mock_device() + device.device.side_effect = mock_initialize + with patch( + "homeassistant.components.homewizard.config_flow.HomeWizardEnergy", + return_value=device, + ): + result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) + + assert result["type"] == FlowResultType.FORM + assert result["errors"] == {"base": "api_not_enabled"} diff --git a/tests/components/homewizard/test_init.py b/tests/components/homewizard/test_init.py index c7cc5bd7bdb..dea2972d79f 100644 --- a/tests/components/homewizard/test_init.py +++ b/tests/components/homewizard/test_init.py @@ -6,7 +6,7 @@ from homewizard_energy.errors import DisabledError, HomeWizardEnergyException from homeassistant import config_entries from homeassistant.components.homewizard.const import DOMAIN -from homeassistant.config_entries import ConfigEntryState +from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntryState from homeassistant.const import CONF_IP_ADDRESS from homeassistant.helpers import entity_registry as er @@ -187,6 +187,52 @@ async def test_load_detect_api_disabled(aioclient_mock, hass): assert entry.state is ConfigEntryState.SETUP_RETRY + flows = hass.config_entries.flow.async_progress() + assert len(flows) == 1 + + flow = flows[0] + assert flow.get("step_id") == "reauth_confirm" + assert flow.get("handler") == DOMAIN + + assert "context" in flow + assert flow["context"].get("source") == SOURCE_REAUTH + assert flow["context"].get("entry_id") == entry.entry_id + + +async def test_load_removes_reauth_flow(aioclient_mock, hass): + """Test setup removes reauth flow when API is enabled.""" + + device = get_mock_device() + + entry = MockConfigEntry( + domain=DOMAIN, + data={CONF_IP_ADDRESS: "1.1.1.1"}, + unique_id=DOMAIN, + ) + entry.add_to_hass(hass) + + # Add reauth flow from 'previously' failed init + entry.async_start_reauth(hass) + await hass.async_block_till_done() + + flows = hass.config_entries.flow.async_progress_by_handler(DOMAIN) + assert len(flows) == 1 + + # Initialize entry + with patch( + "homeassistant.components.homewizard.coordinator.HomeWizardEnergy", + return_value=device, + ): + await hass.config_entries.async_setup(entry.entry_id) + + await hass.async_block_till_done() + + assert entry.state is ConfigEntryState.LOADED + + # Flow should be removed + flows = hass.config_entries.flow.async_progress_by_handler(DOMAIN) + assert len(flows) == 0 + async def test_load_handles_homewizardenergy_exception(aioclient_mock, hass): """Test setup handles exception from API.""" diff --git a/tests/components/homewizard/test_sensor.py b/tests/components/homewizard/test_sensor.py index d405a713edb..8ed6f9365c8 100644 --- a/tests/components/homewizard/test_sensor.py +++ b/tests/components/homewizard/test_sensor.py @@ -774,13 +774,3 @@ async def test_api_disabled(hass, mock_config_entry_data, mock_config_entry): ).state == "unavailable" ) - - api.data.side_effect = None - async_fire_time_changed(hass, utcnow + timedelta(seconds=10)) - await hass.async_block_till_done() - assert ( - hass.states.get( - "sensor.product_name_aabbccddeeff_total_power_import_t1" - ).state - == "1234.123" - ) diff --git a/tests/components/html5/test_notify.py b/tests/components/html5/test_notify.py index b77986441ed..b51d31ebcdd 100644 --- a/tests/components/html5/test_notify.py +++ b/tests/components/html5/test_notify.py @@ -12,6 +12,7 @@ from homeassistant.setup import async_setup_component CONFIG_FILE = "file.conf" VAPID_CONF = { + "platform": "html5", "vapid_pub_key": "BJMA2gDZEkHaXRhf1fhY_" + "QbKbhVIHlSJXI0bFyo0eJXnUPOjdgycCAbj-2bMKMKNKs" + "_rM8JoSnyKGCXAY2dbONI", @@ -70,7 +71,7 @@ async def mock_client(hass, hass_client, registrations=None): with patch( "homeassistant.components.html5.notify._load_config", return_value=registrations ): - await async_setup_component(hass, "notify", {"notify": {"platform": "html5"}}) + await async_setup_component(hass, "notify", {"notify": VAPID_CONF}) await hass.async_block_till_done() return await hass_client() @@ -85,7 +86,7 @@ class TestHtml5Notify: m = mock_open() with patch("homeassistant.util.json.open", m, create=True): - service = html5.get_service(hass, {}) + service = html5.get_service(hass, VAPID_CONF) assert service is not None @@ -99,7 +100,7 @@ class TestHtml5Notify: m = mock_open(read_data=json.dumps(data)) with patch("homeassistant.util.json.open", m, create=True): - service = html5.get_service(hass, {"gcm_sender_id": "100"}) + service = html5.get_service(hass, VAPID_CONF) assert service is not None @@ -111,7 +112,7 @@ class TestHtml5Notify: assert mock_wp.mock_calls[2][1][0] == SUBSCRIPTION_1["subscription"] # Call to send - payload = json.loads(mock_wp.mock_calls[3][1][0]) + payload = json.loads(mock_wp.mock_calls[3][2]["data"]) assert payload["dismiss"] is True assert payload["tag"] == "test" @@ -126,7 +127,7 @@ class TestHtml5Notify: m = mock_open(read_data=json.dumps(data)) with patch("homeassistant.util.json.open", m, create=True): - service = html5.get_service(hass, {"gcm_sender_id": "100"}) + service = html5.get_service(hass, VAPID_CONF) assert service is not None @@ -140,39 +141,11 @@ class TestHtml5Notify: assert mock_wp.mock_calls[2][1][0] == SUBSCRIPTION_1["subscription"] # Call to send - payload = json.loads(mock_wp.mock_calls[3][1][0]) + payload = json.loads(mock_wp.mock_calls[3][2]["data"]) assert payload["body"] == "Hello" assert payload["icon"] == "beer.png" - @patch("homeassistant.components.html5.notify.WebPusher") - def test_gcm_key_include(self, mock_wp): - """Test if the gcm_key is only included for GCM endpoints.""" - hass = MagicMock() - mock_wp().send().status_code = 201 - - data = {"chrome": SUBSCRIPTION_1, "firefox": SUBSCRIPTION_2} - - m = mock_open(read_data=json.dumps(data)) - with patch("homeassistant.util.json.open", m, create=True): - service = html5.get_service( - hass, {"gcm_sender_id": "100", "gcm_api_key": "Y6i0JdZ0mj9LOaSI"} - ) - - assert service is not None - - service.send_message("Hello", target=["chrome", "firefox"]) - - assert len(mock_wp.mock_calls) == 6 - - # WebPusher constructor - assert mock_wp.mock_calls[2][1][0] == SUBSCRIPTION_1["subscription"] - assert mock_wp.mock_calls[4][1][0] == SUBSCRIPTION_2["subscription"] - - # Get the keys passed to the WebPusher's send method - assert mock_wp.mock_calls[3][2]["gcm_key"] is not None - assert mock_wp.mock_calls[5][2]["gcm_key"] is None - @patch("homeassistant.components.html5.notify.WebPusher") def test_fcm_key_include(self, mock_wp): """Test if the FCM header is included.""" @@ -266,14 +239,6 @@ class TestHtml5Notify: assert mock_wp.mock_calls[3][2]["headers"]["priority"] == "normal" -def test_create_vapid_withoutvapid(): - """Test creating empty vapid.""" - resp = html5.create_vapid_headers( - vapid_email=None, vapid_private_key=None, subscription_info=None, timestamp=None - ) - assert resp is None - - async def test_registering_new_device_view(hass, hass_client): """Test that the HTML view works.""" client = await mock_client(hass, hass_client) @@ -479,7 +444,7 @@ async def test_callback_view_with_jwt(hass, hass_client): assert mock_wp.mock_calls[2][1][0] == SUBSCRIPTION_1["subscription"] # Call to send - push_payload = json.loads(mock_wp.mock_calls[3][1][0]) + push_payload = json.loads(mock_wp.mock_calls[3][2]["data"]) assert push_payload["body"] == "Hello" assert push_payload["icon"] == "beer.png" diff --git a/tests/components/huawei_lte/test_config_flow.py b/tests/components/huawei_lte/test_config_flow.py index 56c177a3602..d29c0554bca 100644 --- a/tests/components/huawei_lte/test_config_flow.py +++ b/tests/components/huawei_lte/test_config_flow.py @@ -235,15 +235,6 @@ async def test_success(hass, login_requests_mock): "errors": {}, }, ), - ( - { - ssdp.ATTR_UPNP_FRIENDLY_NAME: "Some other device", - }, - { - "type": data_entry_flow.FlowResultType.ABORT, - "reason": "not_huawei_lte", - }, - ), ), ) async def test_ssdp(hass, upnp_data, expected_result): diff --git a/tests/components/image/__init__.py b/tests/components/image_upload/__init__.py similarity index 61% rename from tests/components/image/__init__.py rename to tests/components/image_upload/__init__.py index b04214669aa..94acd4a5485 100644 --- a/tests/components/image/__init__.py +++ b/tests/components/image_upload/__init__.py @@ -1,4 +1,4 @@ -"""Tests for the Image integration.""" +"""Tests for the Image Upload integration.""" import pathlib TEST_IMAGE = pathlib.Path(__file__).parent / "logo.png" diff --git a/tests/components/image/logo.png b/tests/components/image_upload/logo.png similarity index 100% rename from tests/components/image/logo.png rename to tests/components/image_upload/logo.png diff --git a/tests/components/image/test_init.py b/tests/components/image_upload/test_init.py similarity index 97% rename from tests/components/image/test_init.py rename to tests/components/image_upload/test_init.py index d62717cb894..3bf85ac7a60 100644 --- a/tests/components/image/test_init.py +++ b/tests/components/image_upload/test_init.py @@ -19,7 +19,7 @@ async def test_upload_image(hass, hass_client, hass_ws_client): with tempfile.TemporaryDirectory() as tempdir, patch.object( hass.config, "path", return_value=tempdir ), patch("homeassistant.util.dt.utcnow", return_value=now): - assert await async_setup_component(hass, "image", {}) + assert await async_setup_component(hass, "image_upload", {}) ws_client: ClientWebSocketResponse = await hass_ws_client() client: ClientSession = await hass_client() diff --git a/tests/components/integration/test_sensor.py b/tests/components/integration/test_sensor.py index 8999c1f8d04..9cc1bbb2692 100644 --- a/tests/components/integration/test_sensor.py +++ b/tests/components/integration/test_sensor.py @@ -15,6 +15,7 @@ from homeassistant.const import ( STATE_UNKNOWN, TIME_HOURS, TIME_SECONDS, + UnitOfPower, ) from homeassistant.core import HomeAssistant, State from homeassistant.setup import async_setup_component @@ -422,16 +423,26 @@ async def test_device_class(hass): # include states of None and Unknown hass.states.async_set(entity_id, STATE_UNKNOWN, {}) await hass.async_block_till_done() - hass.states.async_set(entity_id, 100, {"device_class": None}) + hass.states.async_set( + entity_id, 100, {"device_class": None, "unit_of_measurement": None} + ) await hass.async_block_till_done() - hass.states.async_set(entity_id, 200, {"device_class": None}) + hass.states.async_set( + entity_id, 200, {"device_class": None, "unit_of_measurement": None} + ) await hass.async_block_till_done() state = hass.states.get("sensor.integration") assert "device_class" not in state.attributes hass.states.async_set( - entity_id, 300, {"device_class": SensorDeviceClass.POWER}, force_update=True + entity_id, + 300, + { + "device_class": SensorDeviceClass.POWER, + "unit_of_measurement": UnitOfPower.WATT, + }, + force_update=True, ) await hass.async_block_till_done() diff --git a/tests/components/intent/test_init.py b/tests/components/intent/test_init.py index 6ea4d045c4d..a936c11d0fa 100644 --- a/tests/components/intent/test_init.py +++ b/tests/components/intent/test_init.py @@ -46,7 +46,15 @@ async def test_http_handle_intent(hass, hass_client, hass_admin_user): "card": { "simple": {"content": "You chose a Belgian.", "title": "Beer ordered"} }, - "speech": {"plain": {"extra_data": None, "speech": "I've ordered a Belgian!"}}, + "speech": { + "plain": { + "extra_data": None, + "speech": "I've ordered a Belgian!", + } + }, + "language": hass.config.language, + "response_type": "action_done", + "data": {"targets": [], "success": [], "failed": []}, } diff --git a/tests/components/ipp/__init__.py b/tests/components/ipp/__init__.py index 26e2c9b338e..feda6554210 100644 --- a/tests/components/ipp/__init__.py +++ b/tests/components/ipp/__init__.py @@ -3,8 +3,14 @@ import aiohttp from pyipp import IPPConnectionUpgradeRequired, IPPError from homeassistant.components import zeroconf -from homeassistant.components.ipp.const import CONF_BASE_PATH, CONF_UUID, DOMAIN -from homeassistant.const import CONF_HOST, CONF_PORT, CONF_SSL, CONF_VERIFY_SSL +from homeassistant.components.ipp.const import CONF_BASE_PATH, DOMAIN +from homeassistant.const import ( + CONF_HOST, + CONF_PORT, + CONF_SSL, + CONF_UUID, + CONF_VERIFY_SSL, +) from homeassistant.core import HomeAssistant from tests.common import MockConfigEntry, get_fixture_path diff --git a/tests/components/ipp/test_config_flow.py b/tests/components/ipp/test_config_flow.py index a37fdc9de52..4613c0690ca 100644 --- a/tests/components/ipp/test_config_flow.py +++ b/tests/components/ipp/test_config_flow.py @@ -2,9 +2,9 @@ import dataclasses from unittest.mock import patch -from homeassistant.components.ipp.const import CONF_BASE_PATH, CONF_UUID, DOMAIN +from homeassistant.components.ipp.const import CONF_BASE_PATH, DOMAIN from homeassistant.config_entries import SOURCE_USER, SOURCE_ZEROCONF -from homeassistant.const import CONF_HOST, CONF_NAME, CONF_SSL +from homeassistant.const import CONF_HOST, CONF_NAME, CONF_SSL, CONF_UUID from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType diff --git a/tests/components/knx/fixtures/fixture.knxkeys b/tests/components/knx/fixtures/fixture.knxkeys new file mode 100644 index 00000000000..76841091f2b --- /dev/null +++ b/tests/components/knx/fixtures/fixture.knxkeys @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/components/knx/test_config_flow.py b/tests/components/knx/test_config_flow.py index 55f7b2a8891..5626bff5bbe 100644 --- a/tests/components/knx/test_config_flow.py +++ b/tests/components/knx/test_config_flow.py @@ -5,6 +5,8 @@ import pytest from xknx.exceptions.exception import CommunicationError, InvalidSecureConfiguration from xknx.io import DEFAULT_MCAST_GRP, DEFAULT_MCAST_PORT from xknx.io.gateway_scanner import GatewayDescriptor +from xknx.secure.keyring import _load_keyring +from xknx.telegram import IndividualAddress from homeassistant import config_entries from homeassistant.components.knx.config_flow import ( @@ -42,10 +44,13 @@ from homeassistant.const import CONF_HOST, CONF_PORT from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResult, FlowResultType -from tests.common import MockConfigEntry +from tests.common import MockConfigEntry, get_fixture_path + +FIXTURE_KNXKEYS_PASSWORD = "test" +GATEWAY_INDIVIDUAL_ADDRESS = IndividualAddress("1.0.0") -@pytest.fixture(name="knx_setup", autouse=True) +@pytest.fixture(name="knx_setup") def fixture_knx_setup(): """Mock KNX entry setup.""" with patch("homeassistant.components.knx.async_setup", return_value=True), patch( @@ -63,6 +68,7 @@ def _gateway_descriptor( """Get mock gw descriptor.""" descriptor = GatewayDescriptor( name="Test", + individual_address=GATEWAY_INDIVIDUAL_ADDRESS, ip_addr=ip, port=port, local_interface="eth0", @@ -352,9 +358,25 @@ async def test_routing_secure_keyfile( assert result4["step_id"] == "secure_knxkeys" assert not result4["errors"] + # test file without backbone key with patch( - "homeassistant.components.knx.config_flow.load_keyring", return_value=True - ): + "homeassistant.components.knx.config_flow.load_keyring" + ) as mock_load_keyring: + mock_keyring = Mock() + mock_keyring.backbone.key = None + mock_load_keyring.return_value = mock_keyring + secure_knxkeys = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_KNX_KNXKEY_FILENAME: "testcase.knxkeys", + CONF_KNX_KNXKEY_PASSWORD: "password", + }, + ) + assert secure_knxkeys["type"] == FlowResultType.FORM + assert secure_knxkeys["errors"] == {"base": "keyfile_no_backbone_key"} + + # test valid file + with patch("homeassistant.components.knx.config_flow.load_keyring"): routing_secure_knxkeys = await hass.config_entries.flow.async_configure( result4["flow_id"], { @@ -976,22 +998,34 @@ async def test_configure_secure_knxkeys(hass: HomeAssistant, knx_setup): assert not result["errors"] with patch( - "homeassistant.components.knx.config_flow.load_keyring", return_value=True + "xknx.secure.keyring._load_keyring", + return_value=_load_keyring( + str(get_fixture_path("fixture.knxkeys", DOMAIN).absolute()), + FIXTURE_KNXKEYS_PASSWORD, + ), ): secure_knxkeys = await hass.config_entries.flow.async_configure( result["flow_id"], { CONF_KNX_KNXKEY_FILENAME: "testcase.knxkeys", - CONF_KNX_KNXKEY_PASSWORD: "password", + CONF_KNX_KNXKEY_PASSWORD: "test", }, ) - await hass.async_block_till_done() + assert result["type"] == FlowResultType.FORM + assert secure_knxkeys["step_id"] == "knxkeys_tunnel_select" + assert not result["errors"] + secure_knxkeys = await hass.config_entries.flow.async_configure( + secure_knxkeys["flow_id"], + {CONF_KNX_SECURE_USER_ID: CONF_KNX_AUTOMATIC}, + ) + + await hass.async_block_till_done() assert secure_knxkeys["type"] == FlowResultType.CREATE_ENTRY assert secure_knxkeys["data"] == { **DEFAULT_ENTRY_DATA, CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING_TCP_SECURE, CONF_KNX_KNXKEY_FILENAME: "knx/testcase.knxkeys", - CONF_KNX_KNXKEY_PASSWORD: "password", + CONF_KNX_KNXKEY_PASSWORD: "test", CONF_KNX_ROUTING_BACKBONE_KEY: None, CONF_KNX_ROUTING_SYNC_LATENCY_TOLERANCE: None, CONF_KNX_SECURE_DEVICE_AUTHENTICATION: None, @@ -1031,7 +1065,7 @@ async def test_configure_secure_knxkeys_file_not_found(hass: HomeAssistant): ) assert secure_knxkeys["type"] == FlowResultType.FORM assert secure_knxkeys["errors"] - assert secure_knxkeys["errors"][CONF_KNX_KNXKEY_FILENAME] == "file_not_found" + assert secure_knxkeys["errors"][CONF_KNX_KNXKEY_FILENAME] == "keyfile_not_found" async def test_configure_secure_knxkeys_invalid_signature(hass: HomeAssistant): @@ -1059,18 +1093,51 @@ async def test_configure_secure_knxkeys_invalid_signature(hass: HomeAssistant): ) assert secure_knxkeys["type"] == FlowResultType.FORM assert secure_knxkeys["errors"] - assert secure_knxkeys["errors"][CONF_KNX_KNXKEY_PASSWORD] == "invalid_signature" + assert ( + secure_knxkeys["errors"][CONF_KNX_KNXKEY_PASSWORD] + == "keyfile_invalid_signature" + ) + + +async def test_configure_secure_knxkeys_no_tunnel_for_host(hass: HomeAssistant): + """Test configure secure knxkeys but file was not found.""" + menu_step = await _get_menu_step(hass) + + result = await hass.config_entries.flow.async_configure( + menu_step["flow_id"], + {"next_step_id": "secure_knxkeys"}, + ) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "secure_knxkeys" + assert not result["errors"] + + with patch( + "homeassistant.components.knx.config_flow.load_keyring" + ) as mock_load_keyring: + mock_keyring = Mock() + mock_keyring.get_tunnel_interfaces_by_host.return_value = [] + mock_load_keyring.return_value = mock_keyring + secure_knxkeys = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_KNX_KNXKEY_FILENAME: "testcase.knxkeys", + CONF_KNX_KNXKEY_PASSWORD: "password", + }, + ) + assert secure_knxkeys["type"] == FlowResultType.FORM + assert secure_knxkeys["errors"] == {"base": "keyfile_no_tunnel_for_host"} async def test_options_flow_connection_type( - hass: HomeAssistant, knx_setup, mock_config_entry: MockConfigEntry + hass: HomeAssistant, knx, mock_config_entry: MockConfigEntry ) -> None: """Test options flow changing interface.""" - mock_config_entry.add_to_hass(hass) + # run one option flow test with a set up integration (knx fixture) + # instead of mocking async_setup_entry (knx_setup fixture) to test + # usage of the already running XKNX instance for gateway scanner gateway = _gateway_descriptor("192.168.0.1", 3675) - await hass.config_entries.async_setup(mock_config_entry.entry_id) - hass.data[DOMAIN] = Mock() # GatewayScanner uses running XKNX() in options flow + await knx.setup_integration({}) menu_step = await hass.config_entries.options.async_init(mock_config_entry.entry_id) with patch( @@ -1114,7 +1181,6 @@ async def test_options_flow_connection_type( CONF_KNX_STATE_UPDATER: CONF_KNX_DEFAULT_STATE_UPDATER, CONF_KNX_ROUTE_BACK: False, } - knx_setup.assert_called_once() async def test_options_flow_secure_manual_to_keyfile( @@ -1186,24 +1252,36 @@ async def test_options_flow_secure_manual_to_keyfile( assert not result4["errors"] with patch( - "homeassistant.components.knx.config_flow.load_keyring", return_value=True + "xknx.secure.keyring._load_keyring", + return_value=_load_keyring( + str(get_fixture_path("fixture.knxkeys", DOMAIN).absolute()), + FIXTURE_KNXKEYS_PASSWORD, + ), ): secure_knxkeys = await hass.config_entries.options.async_configure( result4["flow_id"], { CONF_KNX_KNXKEY_FILENAME: "testcase.knxkeys", - CONF_KNX_KNXKEY_PASSWORD: "password", + CONF_KNX_KNXKEY_PASSWORD: "test", }, ) - await hass.async_block_till_done() + assert result["type"] == FlowResultType.FORM + assert secure_knxkeys["step_id"] == "knxkeys_tunnel_select" + assert not result["errors"] + secure_knxkeys = await hass.config_entries.options.async_configure( + secure_knxkeys["flow_id"], + {CONF_KNX_SECURE_USER_ID: "2"}, + ) + + await hass.async_block_till_done() assert secure_knxkeys["type"] == FlowResultType.CREATE_ENTRY assert mock_config_entry.data == { **DEFAULT_ENTRY_DATA, CONF_KNX_CONNECTION_TYPE: CONF_KNX_TUNNELING_TCP_SECURE, CONF_KNX_KNXKEY_FILENAME: "knx/testcase.knxkeys", - CONF_KNX_KNXKEY_PASSWORD: "password", + CONF_KNX_KNXKEY_PASSWORD: "test", CONF_KNX_SECURE_DEVICE_AUTHENTICATION: None, - CONF_KNX_SECURE_USER_ID: None, + CONF_KNX_SECURE_USER_ID: 2, CONF_KNX_SECURE_USER_PASSWORD: None, CONF_KNX_ROUTING_BACKBONE_KEY: None, CONF_KNX_ROUTING_SYNC_LATENCY_TOLERANCE: None, diff --git a/tests/components/knx/test_expose.py b/tests/components/knx/test_expose.py index e5030eef461..3cad2cf008e 100644 --- a/tests/components/knx/test_expose.py +++ b/tests/components/knx/test_expose.py @@ -1,4 +1,5 @@ """Test KNX expose.""" +from datetime import timedelta import time from unittest.mock import patch @@ -6,9 +7,12 @@ from homeassistant.components.knx import CONF_KNX_EXPOSE, DOMAIN, KNX_ADDRESS from homeassistant.components.knx.schema import ExposeSchema from homeassistant.const import CONF_ATTRIBUTE, CONF_ENTITY_ID, CONF_TYPE from homeassistant.core import HomeAssistant +from homeassistant.util import dt from .conftest import KNXTestKit +from tests.common import async_fire_time_changed_exact + async def test_binary_expose(hass: HomeAssistant, knx: KNXTestKit): """Test a binary expose to only send telegrams on state change.""" @@ -163,6 +167,37 @@ async def test_expose_string(hass: HomeAssistant, knx: KNXTestKit): ) +async def test_expose_cooldown(hass: HomeAssistant, knx: KNXTestKit): + """Test an expose with cooldown.""" + cooldown_time = 2 + entity_id = "fake.entity" + await knx.setup_integration( + { + CONF_KNX_EXPOSE: { + CONF_TYPE: "percentU8", + KNX_ADDRESS: "1/1/8", + CONF_ENTITY_ID: entity_id, + ExposeSchema.CONF_KNX_EXPOSE_COOLDOWN: cooldown_time, + } + }, + ) + assert not hass.states.async_all() + # Change state to 1 + hass.states.async_set(entity_id, "1", {}) + await knx.assert_write("1/1/8", (1,)) + # Change state to 2 - skip because of cooldown + hass.states.async_set(entity_id, "2", {}) + await knx.assert_no_telegram() + + # Change state to 3 + hass.states.async_set(entity_id, "3", {}) + await knx.assert_no_telegram() + # Wait for cooldown to pass + async_fire_time_changed_exact(hass, dt.utcnow() + timedelta(seconds=cooldown_time)) + await hass.async_block_till_done() + await knx.assert_write("1/1/8", (3,)) + + async def test_expose_conversion_exception(hass: HomeAssistant, knx: KNXTestKit): """Test expose throws exception.""" diff --git a/tests/components/lifx/__init__.py b/tests/components/lifx/__init__.py index bb861c4a683..0278b8ec3a7 100644 --- a/tests/components/lifx/__init__.py +++ b/tests/components/lifx/__init__.py @@ -14,6 +14,7 @@ MODULE = "homeassistant.components.lifx" MODULE_CONFIG_FLOW = "homeassistant.components.lifx.config_flow" IP_ADDRESS = "127.0.0.1" LABEL = "My Bulb" +GROUP = "My Group" SERIAL = "aa:bb:cc:dd:ee:cc" MAC_ADDRESS = "aa:bb:cc:dd:ee:cd" DEFAULT_ENTRY_TITLE = LABEL @@ -81,6 +82,7 @@ def _mocked_bulb() -> Light: bulb = Light(asyncio.get_running_loop(), SERIAL, IP_ADDRESS) bulb.host_firmware_version = "3.00" bulb.label = LABEL + bulb.group = GROUP bulb.color = [1, 2, 3, 4] bulb.power_level = 0 bulb.fire_and_forget = AsyncMock() @@ -88,6 +90,7 @@ def _mocked_bulb() -> Light: bulb.try_sending = AsyncMock() bulb.set_infrared = MockLifxCommand(bulb) bulb.get_label = MockLifxCommand(bulb) + bulb.get_group = MockLifxCommand(bulb) bulb.get_color = MockLifxCommand(bulb) bulb.set_power = MockLifxCommand(bulb) bulb.set_color = MockLifxCommand(bulb) diff --git a/tests/components/lifx/test_config_flow.py b/tests/components/lifx/test_config_flow.py index c9f069d8660..d82209e61b6 100644 --- a/tests/components/lifx/test_config_flow.py +++ b/tests/components/lifx/test_config_flow.py @@ -11,6 +11,8 @@ from homeassistant.components.lifx.const import CONF_SERIAL from homeassistant.const import CONF_DEVICE, CONF_HOST from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType +from homeassistant.helpers import device_registry as dr, entity_registry as er +from homeassistant.setup import async_setup_component from . import ( DEFAULT_ENTRY_TITLE, @@ -19,9 +21,11 @@ from . import ( MAC_ADDRESS, MODULE, SERIAL, + _mocked_bulb, _mocked_failing_bulb, _mocked_relay, _patch_config_flow_try_connect, + _patch_device, _patch_discovery, ) @@ -523,3 +527,41 @@ async def test_refuse_relays(hass: HomeAssistant): await hass.async_block_till_done() assert result2["type"] == "form" assert result2["errors"] == {"base": "cannot_connect"} + + +async def test_suggested_area(hass: HomeAssistant) -> None: + """Test suggested area is populated from lifx group label.""" + + class MockLifxCommandGetGroup: + """Mock the get_group method that gets the group name from the bulb.""" + + def __init__(self, bulb, **kwargs): + """Init command.""" + self.bulb = bulb + self.lifx_group = kwargs.get("lifx_group") + + def __call__(self, *args, **kwargs): + """Call command.""" + self.bulb.group = self.lifx_group + + config_entry = MockConfigEntry( + domain=DOMAIN, data={CONF_HOST: "1.2.3.4"}, unique_id=SERIAL + ) + config_entry.add_to_hass(hass) + bulb = _mocked_bulb() + bulb.group = None + bulb.get_group = MockLifxCommandGetGroup(bulb, lifx_group="My LIFX Group") + + with _patch_discovery(device=bulb), _patch_config_flow_try_connect( + device=bulb + ), _patch_device(device=bulb): + await async_setup_component(hass, DOMAIN, {DOMAIN: {}}) + await hass.async_block_till_done() + + entity_registry = er.async_get(hass) + entity_id = "light.my_bulb" + entity = entity_registry.async_get(entity_id) + + device_registry = dr.async_get(hass) + device = device_registry.async_get(entity.device_id) + assert device.suggested_area == "My LIFX Group" diff --git a/tests/components/litterrobot/test_sensor.py b/tests/components/litterrobot/test_sensor.py index 536e6ddf188..beed13c75dd 100644 --- a/tests/components/litterrobot/test_sensor.py +++ b/tests/components/litterrobot/test_sensor.py @@ -86,7 +86,7 @@ async def test_litter_robot_sensor( assert sensor.attributes["device_class"] == SensorDeviceClass.TIMESTAMP sensor = hass.states.get("sensor.test_status_code") assert sensor.state == "dfs" - assert sensor.attributes["device_class"] == "litterrobot__status_code" + assert sensor.attributes["device_class"] == SensorDeviceClass.ENUM sensor = hass.states.get("sensor.test_litter_level") assert sensor.state == "70.0" assert sensor.attributes["unit_of_measurement"] == PERCENTAGE diff --git a/tests/components/litterrobot/test_update.py b/tests/components/litterrobot/test_update.py new file mode 100644 index 00000000000..f4311992c8e --- /dev/null +++ b/tests/components/litterrobot/test_update.py @@ -0,0 +1,81 @@ +"""Test the Litter-Robot update entity.""" +from unittest.mock import AsyncMock, MagicMock + +from pylitterbot import LitterRobot4 +import pytest + +from homeassistant.components.update import ( + ATTR_INSTALLED_VERSION, + ATTR_LATEST_VERSION, + DOMAIN as PLATFORM_DOMAIN, + SERVICE_INSTALL, + UpdateDeviceClass, +) +from homeassistant.const import ATTR_DEVICE_CLASS, ATTR_ENTITY_ID, STATE_OFF, STATE_ON +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError + +from .conftest import setup_integration + +ENTITY_ID = "update.test_firmware" +OLD_FIRMWARE = "ESP: 1.1.50 / PIC: 10512.2560.2.53 / TOF: 4.0.65.4" +NEW_FIRMWARE = "ESP: 1.1.51 / PIC: 10512.2560.2.53 / TOF: 4.0.65.4" + + +async def test_robot_with_no_update( + hass: HomeAssistant, mock_account_with_litterrobot_4: MagicMock +): + """Tests the update entity was set up.""" + robot: LitterRobot4 = mock_account_with_litterrobot_4.robots[0] + robot.has_firmware_update = AsyncMock(return_value=False) + + entry = await setup_integration( + hass, mock_account_with_litterrobot_4, PLATFORM_DOMAIN + ) + + state = hass.states.get(ENTITY_ID) + assert state + assert state.state == STATE_OFF + assert state.attributes[ATTR_DEVICE_CLASS] == UpdateDeviceClass.FIRMWARE + assert state.attributes[ATTR_INSTALLED_VERSION] == OLD_FIRMWARE + assert state.attributes[ATTR_LATEST_VERSION] == OLD_FIRMWARE + + assert await hass.config_entries.async_unload(entry.entry_id) + await hass.async_block_till_done() + + +async def test_robot_with_update( + hass: HomeAssistant, mock_account_with_litterrobot_4: MagicMock +): + """Tests the update entity was set up.""" + robot: LitterRobot4 = mock_account_with_litterrobot_4.robots[0] + robot.has_firmware_update = AsyncMock(return_value=True) + robot.get_latest_firmware = AsyncMock(return_value=NEW_FIRMWARE) + + await setup_integration(hass, mock_account_with_litterrobot_4, PLATFORM_DOMAIN) + + state = hass.states.get(ENTITY_ID) + assert state + assert state.state == STATE_ON + assert state.attributes[ATTR_DEVICE_CLASS] == UpdateDeviceClass.FIRMWARE + assert state.attributes[ATTR_INSTALLED_VERSION] == OLD_FIRMWARE + assert state.attributes[ATTR_LATEST_VERSION] == NEW_FIRMWARE + + robot.update_firmware = AsyncMock(return_value=False) + + with pytest.raises(HomeAssistantError): + await hass.services.async_call( + PLATFORM_DOMAIN, + SERVICE_INSTALL, + {ATTR_ENTITY_ID: ENTITY_ID}, + blocking=True, + ) + await hass.async_block_till_done() + assert robot.update_firmware.call_count == 1 + + robot.update_firmware = AsyncMock(return_value=True) + await hass.services.async_call( + PLATFORM_DOMAIN, SERVICE_INSTALL, {ATTR_ENTITY_ID: ENTITY_ID}, blocking=True + ) + await hass.async_block_till_done() + assert robot.update_firmware.call_count == 1 diff --git a/tests/components/local_calendar/test_calendar.py b/tests/components/local_calendar/test_calendar.py index 092fcb1c1fb..3cbbf6a19ad 100644 --- a/tests/components/local_calendar/test_calendar.py +++ b/tests/components/local_calendar/test_calendar.py @@ -174,7 +174,7 @@ async def test_empty_calendar( assert state.state == STATE_OFF assert dict(state.attributes) == { "friendly_name": FRIENDLY_NAME, - "supported_features": 3, + "supported_features": 7, } @@ -292,7 +292,7 @@ async def test_active_event( "location": "", "start_time": start.strftime(DATE_STR_FORMAT), "end_time": end.strftime(DATE_STR_FORMAT), - "supported_features": 3, + "supported_features": 7, } @@ -326,10 +326,9 @@ async def test_upcoming_event( "all_day": False, "description": "", "location": "", - "message": "Evening lights", "start_time": start.strftime(DATE_STR_FORMAT), "end_time": end.strftime(DATE_STR_FORMAT), - "supported_features": 3, + "supported_features": 7, } @@ -522,6 +521,238 @@ async def test_websocket_delete_recurring( ] +async def test_websocket_update( + ws_client: ClientFixture, setup_integration: None, get_events: GetEventsFn +): + """Test websocket update command.""" + client = await ws_client() + await client.cmd_result( + "create", + { + "entity_id": TEST_ENTITY, + "event": { + "summary": "Bastille Day Party", + "dtstart": "1997-07-14T17:00:00+00:00", + "dtend": "1997-07-15T04:00:00+00:00", + }, + }, + ) + + events = await get_events("1997-07-14T00:00:00", "1997-07-16T00:00:00") + assert list(map(event_fields, events)) == [ + { + "summary": "Bastille Day Party", + "start": {"dateTime": "1997-07-14T11:00:00-06:00"}, + "end": {"dateTime": "1997-07-14T22:00:00-06:00"}, + } + ] + uid = events[0]["uid"] + + # Update the event + await client.cmd_result( + "update", + { + "entity_id": TEST_ENTITY, + "uid": uid, + "event": { + "summary": "Bastille Day Party [To be rescheduled]", + "dtstart": "1997-07-14", + "dtend": "1997-07-15", + }, + }, + ) + events = await get_events("1997-07-14T00:00:00", "1997-07-16T00:00:00") + assert list(map(event_fields, events)) == [ + { + "summary": "Bastille Day Party [To be rescheduled]", + "start": {"date": "1997-07-14"}, + "end": {"date": "1997-07-15"}, + } + ] + + +async def test_websocket_update_recurring_this_and_future( + ws_client: ClientFixture, setup_integration: None, get_events: GetEventsFn +): + """Test updating a recurring event.""" + client = await ws_client() + await client.cmd_result( + "create", + { + "entity_id": TEST_ENTITY, + "event": { + "summary": "Morning Routine", + "dtstart": "2022-08-22T08:30:00", + "dtend": "2022-08-22T09:00:00", + "rrule": "FREQ=DAILY", + }, + }, + ) + + events = await get_events("2022-08-22T00:00:00", "2022-08-26T00:00:00") + assert list(map(event_fields, events)) == [ + { + "summary": "Morning Routine", + "start": {"dateTime": "2022-08-22T08:30:00-06:00"}, + "end": {"dateTime": "2022-08-22T09:00:00-06:00"}, + "recurrence_id": "20220822T083000", + }, + { + "summary": "Morning Routine", + "start": {"dateTime": "2022-08-23T08:30:00-06:00"}, + "end": {"dateTime": "2022-08-23T09:00:00-06:00"}, + "recurrence_id": "20220823T083000", + }, + { + "summary": "Morning Routine", + "start": {"dateTime": "2022-08-24T08:30:00-06:00"}, + "end": {"dateTime": "2022-08-24T09:00:00-06:00"}, + "recurrence_id": "20220824T083000", + }, + { + "summary": "Morning Routine", + "start": {"dateTime": "2022-08-25T08:30:00-06:00"}, + "end": {"dateTime": "2022-08-25T09:00:00-06:00"}, + "recurrence_id": "20220825T083000", + }, + ] + uid = events[0]["uid"] + assert [event["uid"] for event in events] == [uid] * 4 + + # Update a single instance and confirm the change is reflected + await client.cmd_result( + "update", + { + "entity_id": TEST_ENTITY, + "uid": uid, + "recurrence_id": "20220824T083000", + "recurrence_range": "THISANDFUTURE", + "event": { + "summary": "Morning Routine [Adjusted]", + "dtstart": "2022-08-24T08:00:00", + "dtend": "2022-08-24T08:30:00", + }, + }, + ) + events = await get_events("2022-08-22T00:00:00", "2022-08-26T00:00:00") + assert list(map(event_fields, events)) == [ + { + "summary": "Morning Routine", + "start": {"dateTime": "2022-08-22T08:30:00-06:00"}, + "end": {"dateTime": "2022-08-22T09:00:00-06:00"}, + "recurrence_id": "20220822T083000", + }, + { + "summary": "Morning Routine", + "start": {"dateTime": "2022-08-23T08:30:00-06:00"}, + "end": {"dateTime": "2022-08-23T09:00:00-06:00"}, + "recurrence_id": "20220823T083000", + }, + { + "summary": "Morning Routine [Adjusted]", + "start": {"dateTime": "2022-08-24T08:00:00-06:00"}, + "end": {"dateTime": "2022-08-24T08:30:00-06:00"}, + "recurrence_id": "20220824T080000", + }, + { + "summary": "Morning Routine [Adjusted]", + "start": {"dateTime": "2022-08-25T08:00:00-06:00"}, + "end": {"dateTime": "2022-08-25T08:30:00-06:00"}, + "recurrence_id": "20220825T080000", + }, + ] + + +async def test_websocket_update_recurring( + ws_client: ClientFixture, setup_integration: None, get_events: GetEventsFn +): + """Test updating a recurring event.""" + client = await ws_client() + await client.cmd_result( + "create", + { + "entity_id": TEST_ENTITY, + "event": { + "summary": "Morning Routine", + "dtstart": "2022-08-22T08:30:00", + "dtend": "2022-08-22T09:00:00", + "rrule": "FREQ=DAILY", + }, + }, + ) + + events = await get_events("2022-08-22T00:00:00", "2022-08-26T00:00:00") + assert list(map(event_fields, events)) == [ + { + "summary": "Morning Routine", + "start": {"dateTime": "2022-08-22T08:30:00-06:00"}, + "end": {"dateTime": "2022-08-22T09:00:00-06:00"}, + "recurrence_id": "20220822T083000", + }, + { + "summary": "Morning Routine", + "start": {"dateTime": "2022-08-23T08:30:00-06:00"}, + "end": {"dateTime": "2022-08-23T09:00:00-06:00"}, + "recurrence_id": "20220823T083000", + }, + { + "summary": "Morning Routine", + "start": {"dateTime": "2022-08-24T08:30:00-06:00"}, + "end": {"dateTime": "2022-08-24T09:00:00-06:00"}, + "recurrence_id": "20220824T083000", + }, + { + "summary": "Morning Routine", + "start": {"dateTime": "2022-08-25T08:30:00-06:00"}, + "end": {"dateTime": "2022-08-25T09:00:00-06:00"}, + "recurrence_id": "20220825T083000", + }, + ] + uid = events[0]["uid"] + assert [event["uid"] for event in events] == [uid] * 4 + + # Update a single instance and confirm the change is reflected + await client.cmd_result( + "update", + { + "entity_id": TEST_ENTITY, + "uid": uid, + "recurrence_id": "20220824T083000", + "event": { + "summary": "Morning Routine [Adjusted]", + "dtstart": "2022-08-24T08:00:00", + "dtend": "2022-08-24T08:30:00", + }, + }, + ) + events = await get_events("2022-08-22T00:00:00", "2022-08-26T00:00:00") + assert list(map(event_fields, events)) == [ + { + "summary": "Morning Routine", + "start": {"dateTime": "2022-08-22T08:30:00-06:00"}, + "end": {"dateTime": "2022-08-22T09:00:00-06:00"}, + "recurrence_id": "20220822T083000", + }, + { + "summary": "Morning Routine", + "start": {"dateTime": "2022-08-23T08:30:00-06:00"}, + "end": {"dateTime": "2022-08-23T09:00:00-06:00"}, + "recurrence_id": "20220823T083000", + }, + { + "summary": "Morning Routine [Adjusted]", + "start": {"dateTime": "2022-08-24T08:00:00-06:00"}, + "end": {"dateTime": "2022-08-24T08:30:00-06:00"}, + }, + { + "summary": "Morning Routine", + "start": {"dateTime": "2022-08-25T08:30:00-06:00"}, + "end": {"dateTime": "2022-08-25T09:00:00-06:00"}, + "recurrence_id": "20220825T083000", + }, + ] + + @pytest.mark.parametrize( "rrule", [ @@ -705,3 +936,27 @@ async def test_invalid_date_formats( assert "error" in result assert "code" in result.get("error") assert result["error"]["code"] == "invalid_format" + + +async def test_update_invalid_event_id( + ws_client: ClientFixture, + setup_integration: None, + hass: HomeAssistant, +): + """Test updating an event with an invalid event uid.""" + client = await ws_client() + resp = await client.cmd( + "update", + { + "entity_id": TEST_ENTITY, + "uid": "uid-does-not-exist", + "event": { + "summary": "Bastille Day Party [To be rescheduled]", + "dtstart": "1997-07-14", + "dtend": "1997-07-15", + }, + }, + ) + assert not resp.get("success") + assert "error" in resp + assert resp.get("error").get("code") == "failed" diff --git a/tests/components/lutron_caseta/test_device_trigger.py b/tests/components/lutron_caseta/test_device_trigger.py index e7688f62f06..b8afba1fbec 100644 --- a/tests/components/lutron_caseta/test_device_trigger.py +++ b/tests/components/lutron_caseta/test_device_trigger.py @@ -396,7 +396,7 @@ async def test_validate_trigger_config_unknown_device(hass, calls, device_reg): assert len(calls) == 0 -async def test_validate_trigger_invalid_triggers(hass, device_reg): +async def test_validate_trigger_invalid_triggers(hass, device_reg, caplog): """Test for click_event with invalid triggers.""" config_entry_id = await _async_setup_lutron_with_picos(hass) data: LutronCasetaData = hass.data[DOMAIN][config_entry_id] @@ -415,7 +415,7 @@ async def test_validate_trigger_invalid_triggers(hass, device_reg): CONF_PLATFORM: "device", CONF_DOMAIN: DOMAIN, CONF_DEVICE_ID: device_id, - CONF_TYPE: "press", + CONF_TYPE: "invalid", CONF_SUBTYPE: "on", }, "action": { @@ -427,6 +427,8 @@ async def test_validate_trigger_invalid_triggers(hass, device_reg): }, ) + assert "value must be one of ['press', 'release']" in caplog.text + async def test_if_fires_on_button_event_late_setup(hass, calls): """Test for press trigger firing with integration getting setup late.""" diff --git a/tests/components/manual/test_alarm_control_panel.py b/tests/components/manual/test_alarm_control_panel.py index 56c025ad4e3..686e2ae1b3e 100644 --- a/tests/components/manual/test_alarm_control_panel.py +++ b/tests/components/manual/test_alarm_control_panel.py @@ -354,7 +354,9 @@ async def test_trigger_no_pending(hass): async_fire_time_changed(hass, future) await hass.async_block_till_done() - assert hass.states.get(entity_id).state == STATE_ALARM_TRIGGERED + state = hass.states.get(entity_id) + assert state.attributes["previous_state"] == STATE_ALARM_DISARMED + assert state.state == STATE_ALARM_TRIGGERED async def test_trigger_with_delay(hass): @@ -398,6 +400,7 @@ async def test_trigger_with_delay(hass): await hass.async_block_till_done() state = hass.states.get(entity_id) + assert state.attributes["previous_state"] == STATE_ALARM_ARMED_AWAY assert state.state == STATE_ALARM_TRIGGERED @@ -490,6 +493,7 @@ async def test_trigger_with_pending(hass): await hass.async_block_till_done() state = hass.states.get(entity_id) + assert state.attributes["previous_state"] == STATE_ALARM_DISARMED assert state.state == STATE_ALARM_TRIGGERED future = dt_util.utcnow() + timedelta(seconds=5) @@ -546,6 +550,7 @@ async def test_trigger_with_unused_specific_delay(hass): await hass.async_block_till_done() state = hass.states.get(entity_id) + assert state.attributes["previous_state"] == STATE_ALARM_ARMED_AWAY assert state.state == STATE_ALARM_TRIGGERED @@ -591,6 +596,7 @@ async def test_trigger_with_specific_delay(hass): await hass.async_block_till_done() state = hass.states.get(entity_id) + assert state.attributes["previous_state"] == STATE_ALARM_ARMED_AWAY assert state.state == STATE_ALARM_TRIGGERED @@ -647,6 +653,7 @@ async def test_trigger_with_pending_and_delay(hass): await hass.async_block_till_done() state = hass.states.get(entity_id) + assert state.attributes["previous_state"] == STATE_ALARM_ARMED_AWAY assert state.state == STATE_ALARM_TRIGGERED @@ -704,6 +711,7 @@ async def test_trigger_with_pending_and_specific_delay(hass): await hass.async_block_till_done() state = hass.states.get(entity_id) + assert state.attributes["previous_state"] == STATE_ALARM_ARMED_AWAY assert state.state == STATE_ALARM_TRIGGERED @@ -739,7 +747,9 @@ async def test_trigger_with_specific_pending(hass): async_fire_time_changed(hass, future) await hass.async_block_till_done() - assert hass.states.get(entity_id).state == STATE_ALARM_TRIGGERED + state = hass.states.get(entity_id) + assert state.attributes["previous_state"] == STATE_ALARM_DISARMED + assert state.state == STATE_ALARM_TRIGGERED future = dt_util.utcnow() + timedelta(seconds=5) with patch( @@ -775,7 +785,9 @@ async def test_trigger_with_disarm_after_trigger(hass): await common.async_alarm_trigger(hass, entity_id=entity_id) - assert hass.states.get(entity_id).state == STATE_ALARM_TRIGGERED + state = hass.states.get(entity_id) + assert state.attributes["previous_state"] == STATE_ALARM_DISARMED + assert state.state == STATE_ALARM_TRIGGERED future = dt_util.utcnow() + timedelta(seconds=5) with patch( @@ -839,7 +851,9 @@ async def test_trigger_with_unused_zero_specific_trigger_time(hass): await common.async_alarm_trigger(hass, entity_id=entity_id) - assert hass.states.get(entity_id).state == STATE_ALARM_TRIGGERED + state = hass.states.get(entity_id) + assert state.attributes["previous_state"] == STATE_ALARM_DISARMED + assert state.state == STATE_ALARM_TRIGGERED future = dt_util.utcnow() + timedelta(seconds=5) with patch( @@ -875,7 +889,9 @@ async def test_trigger_with_specific_trigger_time(hass): await common.async_alarm_trigger(hass, entity_id=entity_id) - assert hass.states.get(entity_id).state == STATE_ALARM_TRIGGERED + state = hass.states.get(entity_id) + assert state.attributes["previous_state"] == STATE_ALARM_DISARMED + assert state.state == STATE_ALARM_TRIGGERED future = dt_util.utcnow() + timedelta(seconds=5) with patch( @@ -916,7 +932,9 @@ async def test_trigger_with_no_disarm_after_trigger(hass): await common.async_alarm_trigger(hass, entity_id=entity_id) - assert hass.states.get(entity_id).state == STATE_ALARM_TRIGGERED + state = hass.states.get(entity_id) + assert state.attributes["previous_state"] == STATE_ALARM_ARMED_AWAY + assert state.state == STATE_ALARM_TRIGGERED future = dt_util.utcnow() + timedelta(seconds=5) with patch( @@ -957,7 +975,9 @@ async def test_back_to_back_trigger_with_no_disarm_after_trigger(hass): await common.async_alarm_trigger(hass, entity_id=entity_id) - assert hass.states.get(entity_id).state == STATE_ALARM_TRIGGERED + state = hass.states.get(entity_id) + assert state.attributes["previous_state"] == STATE_ALARM_ARMED_AWAY + assert state.state == STATE_ALARM_TRIGGERED future = dt_util.utcnow() + timedelta(seconds=5) with patch( @@ -971,7 +991,9 @@ async def test_back_to_back_trigger_with_no_disarm_after_trigger(hass): await common.async_alarm_trigger(hass, entity_id=entity_id) - assert hass.states.get(entity_id).state == STATE_ALARM_TRIGGERED + state = hass.states.get(entity_id) + assert state.attributes["previous_state"] == STATE_ALARM_ARMED_AWAY + assert state.state == STATE_ALARM_TRIGGERED future = dt_util.utcnow() + timedelta(seconds=5) with patch( @@ -1033,7 +1055,7 @@ async def test_disarm_during_trigger_with_invalid_code(hass): "platform": "manual", "name": "test", "delay_time": 5, - "code": CODE + "2", + "code": "12345", "disarm_after_trigger": False, } }, @@ -1043,6 +1065,10 @@ async def test_disarm_during_trigger_with_invalid_code(hass): entity_id = "alarm_control_panel.test" assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED + assert ( + hass.states.get(entity_id).attributes[alarm_control_panel.ATTR_CODE_FORMAT] + == alarm_control_panel.CodeFormat.NUMBER + ) await common.async_alarm_trigger(hass) @@ -1060,7 +1086,9 @@ async def test_disarm_during_trigger_with_invalid_code(hass): async_fire_time_changed(hass, future) await hass.async_block_till_done() - assert hass.states.get(entity_id).state == STATE_ALARM_TRIGGERED + state = hass.states.get(entity_id) + assert state.attributes["previous_state"] == STATE_ALARM_DISARMED + assert state.state == STATE_ALARM_TRIGGERED async def test_disarm_with_template_code(hass): @@ -1159,6 +1187,7 @@ async def test_arm_away_after_disabled_disarmed(hass): await hass.async_block_till_done() state = hass.states.get(entity_id) + assert state.attributes["previous_state"] == STATE_ALARM_ARMED_AWAY assert state.state == STATE_ALARM_TRIGGERED @@ -1198,3 +1227,216 @@ async def test_restore_state(hass, expected_state): state = hass.states.get("alarm_control_panel.test") assert state assert state.state == expected_state + + +@pytest.mark.parametrize( + "expected_state", + [ + (STATE_ALARM_ARMED_AWAY), + (STATE_ALARM_ARMED_CUSTOM_BYPASS), + (STATE_ALARM_ARMED_HOME), + (STATE_ALARM_ARMED_NIGHT), + (STATE_ALARM_ARMED_VACATION), + ], +) +async def test_restore_state_arming(hass, expected_state): + """Ensure ARMING state is restored on startup.""" + time = dt_util.utcnow() - timedelta(seconds=15) + entity_id = "alarm_control_panel.test" + attributes = { + "previous_state": STATE_ALARM_DISARMED, + "next_state": expected_state, + } + mock_restore_cache( + hass, (State(entity_id, expected_state, attributes, last_updated=time),) + ) + + hass.state = CoreState.starting + mock_component(hass, "recorder") + + assert await async_setup_component( + hass, + alarm_control_panel.DOMAIN, + { + "alarm_control_panel": { + "platform": "manual", + "name": "test", + "arming_time": 60, + "trigger_time": 0, + "disarm_after_trigger": False, + } + }, + ) + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + assert state + assert state.attributes["previous_state"] == STATE_ALARM_DISARMED + assert state.attributes["next_state"] == expected_state + assert state.state == STATE_ALARM_ARMING + + future = time + timedelta(seconds=61) + with freeze_time(future): + async_fire_time_changed(hass, future) + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + assert state.state == expected_state + + +@pytest.mark.parametrize( + "previous_state", + [ + (STATE_ALARM_ARMED_AWAY), + (STATE_ALARM_ARMED_CUSTOM_BYPASS), + (STATE_ALARM_ARMED_HOME), + (STATE_ALARM_ARMED_NIGHT), + (STATE_ALARM_ARMED_VACATION), + (STATE_ALARM_DISARMED), + ], +) +async def test_restore_state_pending(hass, previous_state): + """Ensure PENDING state is restored on startup.""" + time = dt_util.utcnow() - timedelta(seconds=15) + entity_id = "alarm_control_panel.test" + attributes = { + "previous_state": previous_state, + "next_state": STATE_ALARM_TRIGGERED, + } + mock_restore_cache( + hass, + (State(entity_id, STATE_ALARM_TRIGGERED, attributes, last_updated=time),), + ) + + hass.state = CoreState.starting + mock_component(hass, "recorder") + + assert await async_setup_component( + hass, + alarm_control_panel.DOMAIN, + { + "alarm_control_panel": { + "platform": "manual", + "name": "test", + "arming_time": 0, + "delay_time": 60, + "trigger_time": 60, + "disarm_after_trigger": False, + } + }, + ) + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + assert state + assert state.attributes["previous_state"] == previous_state + assert state.attributes["next_state"] == STATE_ALARM_TRIGGERED + assert state.state == STATE_ALARM_PENDING + + future = time + timedelta(seconds=61) + with freeze_time(future): + async_fire_time_changed(hass, future) + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + assert state.state == STATE_ALARM_TRIGGERED + + future = time + timedelta(seconds=121) + with freeze_time(future): + async_fire_time_changed(hass, future) + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + assert state.state == previous_state + + +@pytest.mark.parametrize( + "previous_state", + [ + (STATE_ALARM_ARMED_AWAY), + (STATE_ALARM_ARMED_CUSTOM_BYPASS), + (STATE_ALARM_ARMED_HOME), + (STATE_ALARM_ARMED_NIGHT), + (STATE_ALARM_ARMED_VACATION), + (STATE_ALARM_DISARMED), + ], +) +async def test_restore_state_triggered(hass, previous_state): + """Ensure PENDING state is resolved to TRIGGERED on startup.""" + time = dt_util.utcnow() - timedelta(seconds=75) + entity_id = "alarm_control_panel.test" + attributes = { + "previous_state": previous_state, + } + mock_restore_cache( + hass, + (State(entity_id, STATE_ALARM_TRIGGERED, attributes, last_updated=time),), + ) + + hass.state = CoreState.starting + mock_component(hass, "recorder") + + assert await async_setup_component( + hass, + alarm_control_panel.DOMAIN, + { + "alarm_control_panel": { + "platform": "manual", + "name": "test", + "arming_time": 0, + "delay_time": 60, + "trigger_time": 60, + "disarm_after_trigger": False, + } + }, + ) + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + assert state + assert state.attributes["previous_state"] == previous_state + assert "next_state" not in state.attributes + assert state.state == STATE_ALARM_TRIGGERED + + future = time + timedelta(seconds=121) + with freeze_time(future): + async_fire_time_changed(hass, future) + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + assert state.state == previous_state + + +async def test_restore_state_triggered_long_ago(hass): + """Ensure TRIGGERED state is resolved on startup.""" + time = dt_util.utcnow() - timedelta(seconds=125) + entity_id = "alarm_control_panel.test" + attributes = { + "previous_state": STATE_ALARM_ARMED_AWAY, + } + mock_restore_cache( + hass, + (State(entity_id, STATE_ALARM_TRIGGERED, attributes, last_updated=time),), + ) + + hass.state = CoreState.starting + mock_component(hass, "recorder") + + assert await async_setup_component( + hass, + alarm_control_panel.DOMAIN, + { + "alarm_control_panel": { + "platform": "manual", + "name": "test", + "arming_time": 0, + "delay_time": 60, + "trigger_time": 60, + "disarm_after_trigger": True, + } + }, + ) + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + assert state.state == STATE_ALARM_DISARMED diff --git a/tests/components/manual_mqtt/test_alarm_control_panel.py b/tests/components/manual_mqtt/test_alarm_control_panel.py index 4296f76d741..14a230718d5 100644 --- a/tests/components/manual_mqtt/test_alarm_control_panel.py +++ b/tests/components/manual_mqtt/test_alarm_control_panel.py @@ -3,12 +3,22 @@ from datetime import timedelta from unittest.mock import patch from freezegun import freeze_time +import pytest from homeassistant.components import alarm_control_panel from homeassistant.const import ( + ATTR_CODE, + ATTR_ENTITY_ID, + SERVICE_ALARM_ARM_AWAY, + SERVICE_ALARM_ARM_CUSTOM_BYPASS, + SERVICE_ALARM_ARM_HOME, + SERVICE_ALARM_ARM_NIGHT, + SERVICE_ALARM_ARM_VACATION, STATE_ALARM_ARMED_AWAY, + STATE_ALARM_ARMED_CUSTOM_BYPASS, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, + STATE_ALARM_ARMED_VACATION, STATE_ALARM_DISARMED, STATE_ALARM_PENDING, STATE_ALARM_TRIGGERED, @@ -57,8 +67,20 @@ async def test_fail_setup_without_command_topic(hass, mqtt_mock_entry_with_yaml_ ) -async def test_arm_home_no_pending(hass, mqtt_mock_entry_with_yaml_config): - """Test arm home method.""" +@pytest.mark.parametrize( + "service,expected_state", + [ + (SERVICE_ALARM_ARM_AWAY, STATE_ALARM_ARMED_AWAY), + (SERVICE_ALARM_ARM_CUSTOM_BYPASS, STATE_ALARM_ARMED_CUSTOM_BYPASS), + (SERVICE_ALARM_ARM_HOME, STATE_ALARM_ARMED_HOME), + (SERVICE_ALARM_ARM_NIGHT, STATE_ALARM_ARMED_NIGHT), + (SERVICE_ALARM_ARM_VACATION, STATE_ALARM_ARMED_VACATION), + ], +) +async def test_no_pending( + hass, service, expected_state, mqtt_mock_entry_with_yaml_config +): + """Test arm method.""" assert await async_setup_component( hass, alarm_control_panel.DOMAIN, @@ -80,16 +102,30 @@ async def test_arm_home_no_pending(hass, mqtt_mock_entry_with_yaml_config): assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED - await common.async_alarm_arm_home(hass, CODE) - await hass.async_block_till_done() + await hass.services.async_call( + alarm_control_panel.DOMAIN, + service, + {ATTR_ENTITY_ID: "alarm_control_panel.test", ATTR_CODE: CODE}, + blocking=True, + ) - assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_HOME + assert hass.states.get(entity_id).state == expected_state -async def test_arm_home_no_pending_when_code_not_req( - hass, mqtt_mock_entry_with_yaml_config +@pytest.mark.parametrize( + "service,expected_state", + [ + (SERVICE_ALARM_ARM_AWAY, STATE_ALARM_ARMED_AWAY), + (SERVICE_ALARM_ARM_CUSTOM_BYPASS, STATE_ALARM_ARMED_CUSTOM_BYPASS), + (SERVICE_ALARM_ARM_HOME, STATE_ALARM_ARMED_HOME), + (SERVICE_ALARM_ARM_NIGHT, STATE_ALARM_ARMED_NIGHT), + (SERVICE_ALARM_ARM_VACATION, STATE_ALARM_ARMED_VACATION), + ], +) +async def test_no_pending_when_code_not_req( + hass, service, expected_state, mqtt_mock_entry_with_yaml_config ): - """Test arm home method.""" + """Test arm method.""" assert await async_setup_component( hass, alarm_control_panel.DOMAIN, @@ -112,14 +148,30 @@ async def test_arm_home_no_pending_when_code_not_req( assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED - await common.async_alarm_arm_home(hass, 0) - await hass.async_block_till_done() + await hass.services.async_call( + alarm_control_panel.DOMAIN, + service, + {ATTR_ENTITY_ID: "alarm_control_panel.test", ATTR_CODE: CODE}, + blocking=True, + ) - assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_HOME + assert hass.states.get(entity_id).state == expected_state -async def test_arm_home_with_pending(hass, mqtt_mock_entry_with_yaml_config): - """Test arm home method.""" +@pytest.mark.parametrize( + "service,expected_state", + [ + (SERVICE_ALARM_ARM_AWAY, STATE_ALARM_ARMED_AWAY), + (SERVICE_ALARM_ARM_CUSTOM_BYPASS, STATE_ALARM_ARMED_CUSTOM_BYPASS), + (SERVICE_ALARM_ARM_HOME, STATE_ALARM_ARMED_HOME), + (SERVICE_ALARM_ARM_NIGHT, STATE_ALARM_ARMED_NIGHT), + (SERVICE_ALARM_ARM_VACATION, STATE_ALARM_ARMED_VACATION), + ], +) +async def test_with_pending( + hass, service, expected_state, mqtt_mock_entry_with_yaml_config +): + """Test arm method.""" assert await async_setup_component( hass, alarm_control_panel.DOMAIN, @@ -141,13 +193,17 @@ async def test_arm_home_with_pending(hass, mqtt_mock_entry_with_yaml_config): assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED - await common.async_alarm_arm_home(hass, CODE, entity_id) - await hass.async_block_till_done() + await hass.services.async_call( + alarm_control_panel.DOMAIN, + service, + {ATTR_ENTITY_ID: "alarm_control_panel.test", ATTR_CODE: CODE}, + blocking=True, + ) assert hass.states.get(entity_id).state == STATE_ALARM_PENDING state = hass.states.get(entity_id) - assert state.attributes["post_pending_state"] == STATE_ALARM_ARMED_HOME + assert state.attributes["post_pending_state"] == expected_state future = dt_util.utcnow() + timedelta(seconds=1) with patch( @@ -157,11 +213,34 @@ async def test_arm_home_with_pending(hass, mqtt_mock_entry_with_yaml_config): async_fire_time_changed(hass, future) await hass.async_block_till_done() - assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_HOME + state = hass.states.get(entity_id) + assert state.state == expected_state + + # Do not go to the pending state when updating to the same state + await hass.services.async_call( + alarm_control_panel.DOMAIN, + service, + {ATTR_ENTITY_ID: "alarm_control_panel.test", ATTR_CODE: CODE}, + blocking=True, + ) + + assert hass.states.get(entity_id).state == expected_state -async def test_arm_home_with_invalid_code(hass, mqtt_mock_entry_with_yaml_config): - """Attempt to arm home without a valid code.""" +@pytest.mark.parametrize( + "service,expected_state", + [ + (SERVICE_ALARM_ARM_AWAY, STATE_ALARM_ARMED_AWAY), + (SERVICE_ALARM_ARM_CUSTOM_BYPASS, STATE_ALARM_ARMED_CUSTOM_BYPASS), + (SERVICE_ALARM_ARM_HOME, STATE_ALARM_ARMED_HOME), + (SERVICE_ALARM_ARM_NIGHT, STATE_ALARM_ARMED_NIGHT), + (SERVICE_ALARM_ARM_VACATION, STATE_ALARM_ARMED_VACATION), + ], +) +async def test_with_invalid_code( + hass, service, expected_state, mqtt_mock_entry_with_yaml_config +): + """Attempt to arm without a valid code.""" assert await async_setup_component( hass, alarm_control_panel.DOMAIN, @@ -183,74 +262,29 @@ async def test_arm_home_with_invalid_code(hass, mqtt_mock_entry_with_yaml_config assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED - await common.async_alarm_arm_home(hass, f"{CODE}2") - await hass.async_block_till_done() - - assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED - - -async def test_arm_away_no_pending(hass, mqtt_mock_entry_with_yaml_config): - """Test arm home method.""" - assert await async_setup_component( - hass, + await hass.services.async_call( alarm_control_panel.DOMAIN, - { - "alarm_control_panel": { - "platform": "manual_mqtt", - "name": "test", - "code": CODE, - "pending_time": 0, - "disarm_after_trigger": False, - "command_topic": "alarm/command", - "state_topic": "alarm/state", - } - }, + service, + {ATTR_ENTITY_ID: "alarm_control_panel.test", ATTR_CODE: f"{CODE}2"}, + blocking=True, ) - await hass.async_block_till_done() - - entity_id = "alarm_control_panel.test" assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED - await common.async_alarm_arm_away(hass, CODE, entity_id) - await hass.async_block_till_done() - assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_AWAY - - -async def test_arm_away_no_pending_when_code_not_req( - hass, mqtt_mock_entry_with_yaml_config +@pytest.mark.parametrize( + "service,expected_state", + [ + (SERVICE_ALARM_ARM_AWAY, STATE_ALARM_ARMED_AWAY), + (SERVICE_ALARM_ARM_CUSTOM_BYPASS, STATE_ALARM_ARMED_CUSTOM_BYPASS), + (SERVICE_ALARM_ARM_HOME, STATE_ALARM_ARMED_HOME), + (SERVICE_ALARM_ARM_NIGHT, STATE_ALARM_ARMED_NIGHT), + (SERVICE_ALARM_ARM_VACATION, STATE_ALARM_ARMED_VACATION), + ], +) +async def test_with_template_code( + hass, service, expected_state, mqtt_mock_entry_with_yaml_config ): - """Test arm home method.""" - assert await async_setup_component( - hass, - alarm_control_panel.DOMAIN, - { - "alarm_control_panel": { - "platform": "manual_mqtt", - "name": "test", - "code_arm_required": False, - "code": CODE, - "pending_time": 0, - "disarm_after_trigger": False, - "command_topic": "alarm/command", - "state_topic": "alarm/state", - } - }, - ) - await hass.async_block_till_done() - - entity_id = "alarm_control_panel.test" - - assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED - - await common.async_alarm_arm_away(hass, 0, entity_id) - await hass.async_block_till_done() - - assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_AWAY - - -async def test_arm_home_with_template_code(hass, mqtt_mock_entry_with_yaml_config): """Attempt to arm with a template-based code.""" assert await async_setup_component( hass, @@ -273,117 +307,31 @@ async def test_arm_home_with_template_code(hass, mqtt_mock_entry_with_yaml_confi assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED - await common.async_alarm_arm_home(hass, "abc") - await hass.async_block_till_done() + await hass.services.async_call( + alarm_control_panel.DOMAIN, + service, + {ATTR_ENTITY_ID: "alarm_control_panel.test", ATTR_CODE: "abc"}, + blocking=True, + ) state = hass.states.get(entity_id) - assert state.state == STATE_ALARM_ARMED_HOME + assert state.state == expected_state -async def test_arm_away_with_pending(hass, mqtt_mock_entry_with_yaml_config): - """Test arm home method.""" - assert await async_setup_component( - hass, - alarm_control_panel.DOMAIN, - { - "alarm_control_panel": { - "platform": "manual_mqtt", - "name": "test", - "code": CODE, - "pending_time": 1, - "disarm_after_trigger": False, - "command_topic": "alarm/command", - "state_topic": "alarm/state", - } - }, - ) - await hass.async_block_till_done() - - entity_id = "alarm_control_panel.test" - - assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED - - await common.async_alarm_arm_away(hass, CODE) - await hass.async_block_till_done() - - assert hass.states.get(entity_id).state == STATE_ALARM_PENDING - - state = hass.states.get(entity_id) - assert state.attributes["post_pending_state"] == STATE_ALARM_ARMED_AWAY - - future = dt_util.utcnow() + timedelta(seconds=1) - with patch( - ("homeassistant.components.manual_mqtt.alarm_control_panel.dt_util.utcnow"), - return_value=future, - ): - async_fire_time_changed(hass, future) - await hass.async_block_till_done() - - assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_AWAY - - -async def test_arm_away_with_invalid_code(hass, mqtt_mock_entry_with_yaml_config): - """Attempt to arm away without a valid code.""" - assert await async_setup_component( - hass, - alarm_control_panel.DOMAIN, - { - "alarm_control_panel": { - "platform": "manual_mqtt", - "name": "test", - "code": CODE, - "pending_time": 1, - "disarm_after_trigger": False, - "command_topic": "alarm/command", - "state_topic": "alarm/state", - } - }, - ) - await hass.async_block_till_done() - - entity_id = "alarm_control_panel.test" - - assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED - - await common.async_alarm_arm_away(hass, f"{CODE}2") - await hass.async_block_till_done() - - assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED - - -async def test_arm_night_no_pending(hass, mqtt_mock_entry_with_yaml_config): - """Test arm night method.""" - assert await async_setup_component( - hass, - alarm_control_panel.DOMAIN, - { - "alarm_control_panel": { - "platform": "manual_mqtt", - "name": "test", - "code": CODE, - "pending_time": 0, - "disarm_after_trigger": False, - "command_topic": "alarm/command", - "state_topic": "alarm/state", - } - }, - ) - await hass.async_block_till_done() - - entity_id = "alarm_control_panel.test" - - assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED - - await common.async_alarm_arm_night(hass, CODE, entity_id) - await hass.async_block_till_done() - - assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_NIGHT - - -async def test_arm_night_no_pending_when_code_not_req( - hass, mqtt_mock_entry_with_yaml_config +@pytest.mark.parametrize( + "service,expected_state", + [ + (SERVICE_ALARM_ARM_AWAY, STATE_ALARM_ARMED_AWAY), + (SERVICE_ALARM_ARM_CUSTOM_BYPASS, STATE_ALARM_ARMED_CUSTOM_BYPASS), + (SERVICE_ALARM_ARM_HOME, STATE_ALARM_ARMED_HOME), + (SERVICE_ALARM_ARM_NIGHT, STATE_ALARM_ARMED_NIGHT), + (SERVICE_ALARM_ARM_VACATION, STATE_ALARM_ARMED_VACATION), + ], +) +async def test_with_specific_pending( + hass, service, expected_state, mqtt_mock_entry_with_yaml_config ): - """Test arm night method.""" + """Test arm method.""" assert await async_setup_component( hass, alarm_control_panel.DOMAIN, @@ -391,10 +339,8 @@ async def test_arm_night_no_pending_when_code_not_req( "alarm_control_panel": { "platform": "manual_mqtt", "name": "test", - "code_arm_required": False, - "code": CODE, - "pending_time": 0, - "disarm_after_trigger": False, + "pending_time": 10, + expected_state: {"pending_time": 2}, "command_topic": "alarm/command", "state_topic": "alarm/state", } @@ -404,46 +350,16 @@ async def test_arm_night_no_pending_when_code_not_req( entity_id = "alarm_control_panel.test" - assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED - - await common.async_alarm_arm_night(hass, 0, entity_id) - await hass.async_block_till_done() - - assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_NIGHT - - -async def test_arm_night_with_pending(hass, mqtt_mock_entry_with_yaml_config): - """Test arm night method.""" - assert await async_setup_component( - hass, + await hass.services.async_call( alarm_control_panel.DOMAIN, - { - "alarm_control_panel": { - "platform": "manual_mqtt", - "name": "test", - "code": CODE, - "pending_time": 1, - "disarm_after_trigger": False, - "command_topic": "alarm/command", - "state_topic": "alarm/state", - } - }, + service, + {ATTR_ENTITY_ID: "alarm_control_panel.test"}, + blocking=True, ) - await hass.async_block_till_done() - - entity_id = "alarm_control_panel.test" - - assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED - - await common.async_alarm_arm_night(hass, CODE) - await hass.async_block_till_done() assert hass.states.get(entity_id).state == STATE_ALARM_PENDING - state = hass.states.get(entity_id) - assert state.attributes["post_pending_state"] == STATE_ALARM_ARMED_NIGHT - - future = dt_util.utcnow() + timedelta(seconds=1) + future = dt_util.utcnow() + timedelta(seconds=2) with patch( ("homeassistant.components.manual_mqtt.alarm_control_panel.dt_util.utcnow"), return_value=future, @@ -451,42 +367,7 @@ async def test_arm_night_with_pending(hass, mqtt_mock_entry_with_yaml_config): async_fire_time_changed(hass, future) await hass.async_block_till_done() - assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_NIGHT - - # Do not go to the pending state when updating to the same state - await common.async_alarm_arm_night(hass, CODE, entity_id) - await hass.async_block_till_done() - - assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_NIGHT - - -async def test_arm_night_with_invalid_code(hass, mqtt_mock_entry_with_yaml_config): - """Attempt to arm night without a valid code.""" - assert await async_setup_component( - hass, - alarm_control_panel.DOMAIN, - { - "alarm_control_panel": { - "platform": "manual_mqtt", - "name": "test", - "code": CODE, - "pending_time": 1, - "disarm_after_trigger": False, - "command_topic": "alarm/command", - "state_topic": "alarm/state", - } - }, - ) - await hass.async_block_till_done() - - entity_id = "alarm_control_panel.test" - - assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED - - await common.async_alarm_arm_night(hass, f"{CODE}2") - await hass.async_block_till_done() - - assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED + assert hass.states.get(entity_id).state == expected_state async def test_trigger_no_pending(hass, mqtt_mock_entry_with_yaml_config): @@ -552,12 +433,10 @@ async def test_trigger_with_delay(hass, mqtt_mock_entry_with_yaml_config): assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED await common.async_alarm_arm_away(hass, CODE) - await hass.async_block_till_done() assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_AWAY await common.async_alarm_trigger(hass, entity_id=entity_id) - await hass.async_block_till_done() state = hass.states.get(entity_id) assert state.state == STATE_ALARM_PENDING @@ -599,7 +478,6 @@ async def test_trigger_zero_trigger_time(hass, mqtt_mock_entry_with_yaml_config) assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED await common.async_alarm_trigger(hass) - await hass.async_block_till_done() assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED @@ -630,7 +508,6 @@ async def test_trigger_zero_trigger_time_with_pending( assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED await common.async_alarm_trigger(hass) - await hass.async_block_till_done() assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED @@ -659,7 +536,6 @@ async def test_trigger_with_pending(hass, mqtt_mock_entry_with_yaml_config): assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED await common.async_alarm_trigger(hass) - await hass.async_block_till_done() assert hass.states.get(entity_id).state == STATE_ALARM_PENDING @@ -713,7 +589,6 @@ async def test_trigger_with_disarm_after_trigger( assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED await common.async_alarm_trigger(hass, entity_id=entity_id) - await hass.async_block_till_done() assert hass.states.get(entity_id).state == STATE_ALARM_TRIGGERED @@ -755,7 +630,6 @@ async def test_trigger_with_zero_specific_trigger_time( assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED await common.async_alarm_trigger(hass, entity_id=entity_id) - await hass.async_block_till_done() assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED @@ -787,7 +661,6 @@ async def test_trigger_with_unused_zero_specific_trigger_time( assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED await common.async_alarm_trigger(hass, entity_id=entity_id) - await hass.async_block_till_done() assert hass.states.get(entity_id).state == STATE_ALARM_TRIGGERED @@ -828,7 +701,6 @@ async def test_trigger_with_specific_trigger_time( assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED await common.async_alarm_trigger(hass, entity_id=entity_id) - await hass.async_block_till_done() assert hass.states.get(entity_id).state == STATE_ALARM_TRIGGERED @@ -869,12 +741,10 @@ async def test_back_to_back_trigger_with_no_disarm_after_trigger( assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED await common.async_alarm_arm_away(hass, CODE, entity_id) - await hass.async_block_till_done() assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_AWAY await common.async_alarm_trigger(hass, entity_id=entity_id) - await hass.async_block_till_done() assert hass.states.get(entity_id).state == STATE_ALARM_TRIGGERED @@ -889,7 +759,6 @@ async def test_back_to_back_trigger_with_no_disarm_after_trigger( assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_AWAY await common.async_alarm_trigger(hass, entity_id=entity_id) - await hass.async_block_till_done() assert hass.states.get(entity_id).state == STATE_ALARM_TRIGGERED @@ -927,12 +796,10 @@ async def test_disarm_while_pending_trigger(hass, mqtt_mock_entry_with_yaml_conf assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED await common.async_alarm_trigger(hass) - await hass.async_block_till_done() assert hass.states.get(entity_id).state == STATE_ALARM_PENDING await common.async_alarm_disarm(hass, entity_id=entity_id) - await hass.async_block_till_done() assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED @@ -959,7 +826,7 @@ async def test_disarm_during_trigger_with_invalid_code( "platform": "manual_mqtt", "name": "test", "pending_time": 5, - "code": f"{CODE}2", + "code": "12345", "disarm_after_trigger": False, "command_topic": "alarm/command", "state_topic": "alarm/state", @@ -971,14 +838,16 @@ async def test_disarm_during_trigger_with_invalid_code( entity_id = "alarm_control_panel.test" assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED + assert ( + hass.states.get(entity_id).attributes[alarm_control_panel.ATTR_CODE_FORMAT] + == alarm_control_panel.CodeFormat.NUMBER + ) await common.async_alarm_trigger(hass) - await hass.async_block_till_done() assert hass.states.get(entity_id).state == STATE_ALARM_PENDING await common.async_alarm_disarm(hass, entity_id=entity_id) - await hass.async_block_till_done() assert hass.states.get(entity_id).state == STATE_ALARM_PENDING @@ -1021,12 +890,10 @@ async def test_trigger_with_unused_specific_delay( assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED await common.async_alarm_arm_away(hass, CODE) - await hass.async_block_till_done() assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_AWAY await common.async_alarm_trigger(hass, entity_id=entity_id) - await hass.async_block_till_done() state = hass.states.get(entity_id) assert state.state == STATE_ALARM_PENDING @@ -1070,12 +937,10 @@ async def test_trigger_with_specific_delay(hass, mqtt_mock_entry_with_yaml_confi assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED await common.async_alarm_arm_away(hass, CODE) - await hass.async_block_till_done() assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_AWAY await common.async_alarm_trigger(hass, entity_id=entity_id) - await hass.async_block_till_done() state = hass.states.get(entity_id) assert state.state == STATE_ALARM_PENDING @@ -1119,12 +984,10 @@ async def test_trigger_with_pending_and_delay(hass, mqtt_mock_entry_with_yaml_co assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED await common.async_alarm_arm_away(hass, CODE) - await hass.async_block_till_done() assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_AWAY await common.async_alarm_trigger(hass, entity_id=entity_id) - await hass.async_block_till_done() state = hass.states.get(entity_id) assert state.state == STATE_ALARM_PENDING @@ -1183,12 +1046,10 @@ async def test_trigger_with_pending_and_specific_delay( assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED await common.async_alarm_arm_away(hass, CODE) - await hass.async_block_till_done() assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_AWAY await common.async_alarm_trigger(hass, entity_id=entity_id) - await hass.async_block_till_done() state = hass.states.get(entity_id) assert state.state == STATE_ALARM_PENDING @@ -1218,116 +1079,6 @@ async def test_trigger_with_pending_and_specific_delay( assert state.state == STATE_ALARM_TRIGGERED -async def test_armed_home_with_specific_pending(hass, mqtt_mock_entry_with_yaml_config): - """Test arm home method.""" - assert await async_setup_component( - hass, - alarm_control_panel.DOMAIN, - { - "alarm_control_panel": { - "platform": "manual_mqtt", - "name": "test", - "pending_time": 10, - "armed_home": {"pending_time": 2}, - "command_topic": "alarm/command", - "state_topic": "alarm/state", - } - }, - ) - await hass.async_block_till_done() - - entity_id = "alarm_control_panel.test" - - await common.async_alarm_arm_home(hass) - await hass.async_block_till_done() - - assert hass.states.get(entity_id).state == STATE_ALARM_PENDING - - future = dt_util.utcnow() + timedelta(seconds=2) - with patch( - ("homeassistant.components.manual_mqtt.alarm_control_panel.dt_util.utcnow"), - return_value=future, - ): - async_fire_time_changed(hass, future) - await hass.async_block_till_done() - - assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_HOME - - -async def test_armed_away_with_specific_pending(hass, mqtt_mock_entry_with_yaml_config): - """Test arm home method.""" - assert await async_setup_component( - hass, - alarm_control_panel.DOMAIN, - { - "alarm_control_panel": { - "platform": "manual_mqtt", - "name": "test", - "pending_time": 10, - "armed_away": {"pending_time": 2}, - "command_topic": "alarm/command", - "state_topic": "alarm/state", - } - }, - ) - await hass.async_block_till_done() - - entity_id = "alarm_control_panel.test" - - await common.async_alarm_arm_away(hass) - await hass.async_block_till_done() - - assert hass.states.get(entity_id).state == STATE_ALARM_PENDING - - future = dt_util.utcnow() + timedelta(seconds=2) - with patch( - ("homeassistant.components.manual_mqtt.alarm_control_panel.dt_util.utcnow"), - return_value=future, - ): - async_fire_time_changed(hass, future) - await hass.async_block_till_done() - - assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_AWAY - - -async def test_armed_night_with_specific_pending( - hass, mqtt_mock_entry_with_yaml_config -): - """Test arm home method.""" - assert await async_setup_component( - hass, - alarm_control_panel.DOMAIN, - { - "alarm_control_panel": { - "platform": "manual_mqtt", - "name": "test", - "pending_time": 10, - "armed_night": {"pending_time": 2}, - "command_topic": "alarm/command", - "state_topic": "alarm/state", - } - }, - ) - await hass.async_block_till_done() - - entity_id = "alarm_control_panel.test" - - await common.async_alarm_arm_night(hass) - await hass.async_block_till_done() - - assert hass.states.get(entity_id).state == STATE_ALARM_PENDING - - future = dt_util.utcnow() + timedelta(seconds=2) - with patch( - ("homeassistant.components.manual_mqtt.alarm_control_panel.dt_util.utcnow"), - return_value=future, - ): - async_fire_time_changed(hass, future) - await hass.async_block_till_done() - - assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_NIGHT - - async def test_trigger_with_specific_pending(hass, mqtt_mock_entry_with_yaml_config): """Test arm home method.""" assert await async_setup_component( @@ -1351,7 +1102,6 @@ async def test_trigger_with_specific_pending(hass, mqtt_mock_entry_with_yaml_con entity_id = "alarm_control_panel.test" await common.async_alarm_trigger(hass) - await hass.async_block_till_done() assert hass.states.get(entity_id).state == STATE_ALARM_PENDING @@ -1376,6 +1126,51 @@ async def test_trigger_with_specific_pending(hass, mqtt_mock_entry_with_yaml_con assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED +async def test_trigger_with_no_disarm_after_trigger( + hass, mqtt_mock_entry_with_yaml_config +): + """Test disarm after trigger.""" + assert await async_setup_component( + hass, + alarm_control_panel.DOMAIN, + { + "alarm_control_panel": { + "platform": "manual_mqtt", + "name": "test", + "trigger_time": 5, + "pending_time": 0, + "delay_time": 0, + "disarm_after_trigger": False, + "command_topic": "alarm/command", + "state_topic": "alarm/state", + } + }, + ) + await hass.async_block_till_done() + + entity_id = "alarm_control_panel.test" + + assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED + + await common.async_alarm_arm_away(hass, CODE, entity_id) + + assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_AWAY + + await common.async_alarm_trigger(hass, entity_id=entity_id) + + assert hass.states.get(entity_id).state == STATE_ALARM_TRIGGERED + + future = dt_util.utcnow() + timedelta(seconds=5) + with patch( + ("homeassistant.components.manual_mqtt.alarm_control_panel.dt_util.utcnow"), + return_value=future, + ): + async_fire_time_changed(hass, future) + await hass.async_block_till_done() + + assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_AWAY + + async def test_arm_away_after_disabled_disarmed(hass, mqtt_mock_entry_with_yaml_config): """Test pending state with and without zero trigger time.""" assert await async_setup_component( @@ -1403,7 +1198,6 @@ async def test_arm_away_after_disabled_disarmed(hass, mqtt_mock_entry_with_yaml_ assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED await common.async_alarm_arm_away(hass, CODE) - await hass.async_block_till_done() state = hass.states.get(entity_id) assert state.state == STATE_ALARM_PENDING @@ -1411,7 +1205,6 @@ async def test_arm_away_after_disabled_disarmed(hass, mqtt_mock_entry_with_yaml_ assert state.attributes["post_pending_state"] == STATE_ALARM_ARMED_AWAY await common.async_alarm_trigger(hass, entity_id=entity_id) - await hass.async_block_till_done() state = hass.states.get(entity_id) assert state.state == STATE_ALARM_PENDING @@ -1427,7 +1220,6 @@ async def test_arm_away_after_disabled_disarmed(hass, mqtt_mock_entry_with_yaml_ assert state.state == STATE_ALARM_ARMED_AWAY await common.async_alarm_trigger(hass, entity_id=entity_id) - await hass.async_block_till_done() state = hass.states.get(entity_id) assert state.state == STATE_ALARM_PENDING @@ -1467,26 +1259,36 @@ async def test_disarm_with_template_code(hass, mqtt_mock_entry_with_yaml_config) assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED await common.async_alarm_arm_home(hass, "def") - await hass.async_block_till_done() state = hass.states.get(entity_id) assert state.state == STATE_ALARM_ARMED_HOME await common.async_alarm_disarm(hass, "def") - await hass.async_block_till_done() state = hass.states.get(entity_id) assert state.state == STATE_ALARM_ARMED_HOME await common.async_alarm_disarm(hass, "abc") - await hass.async_block_till_done() state = hass.states.get(entity_id) assert state.state == STATE_ALARM_DISARMED -async def test_arm_home_via_command_topic(hass, mqtt_mock_entry_with_yaml_config): - """Test arming home via command topic.""" +@pytest.mark.parametrize( + "config,expected_state", + [ + ("payload_arm_away", STATE_ALARM_ARMED_AWAY), + ("payload_arm_custom_bypass", STATE_ALARM_ARMED_CUSTOM_BYPASS), + ("payload_arm_home", STATE_ALARM_ARMED_HOME), + ("payload_arm_night", STATE_ALARM_ARMED_NIGHT), + ("payload_arm_vacation", STATE_ALARM_ARMED_VACATION), + ], +) +async def test_arm_via_command_topic( + hass, config, expected_state, mqtt_mock_entry_with_yaml_config +): + """Test arming via command topic.""" + command = config[8:].upper() assert await async_setup_component( hass, alarm_control_panel.DOMAIN, @@ -1497,7 +1299,7 @@ async def test_arm_home_via_command_topic(hass, mqtt_mock_entry_with_yaml_config "pending_time": 1, "state_topic": "alarm/state", "command_topic": "alarm/command", - "payload_arm_home": "ARM_HOME", + config: command, } }, ) @@ -1507,8 +1309,8 @@ async def test_arm_home_via_command_topic(hass, mqtt_mock_entry_with_yaml_config assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED - # Fire the arm command via MQTT; ensure state changes to pending - async_fire_mqtt_message(hass, "alarm/command", "ARM_HOME") + # Fire the arm command via MQTT; ensure state changes to arming + async_fire_mqtt_message(hass, "alarm/command", command) await hass.async_block_till_done() assert hass.states.get(entity_id).state == STATE_ALARM_PENDING @@ -1521,85 +1323,7 @@ async def test_arm_home_via_command_topic(hass, mqtt_mock_entry_with_yaml_config async_fire_time_changed(hass, future) await hass.async_block_till_done() - assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_HOME - - -async def test_arm_away_via_command_topic(hass, mqtt_mock_entry_with_yaml_config): - """Test arming away via command topic.""" - assert await async_setup_component( - hass, - alarm_control_panel.DOMAIN, - { - alarm_control_panel.DOMAIN: { - "platform": "manual_mqtt", - "name": "test", - "pending_time": 1, - "state_topic": "alarm/state", - "command_topic": "alarm/command", - "payload_arm_away": "ARM_AWAY", - } - }, - ) - await hass.async_block_till_done() - - entity_id = "alarm_control_panel.test" - - assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED - - # Fire the arm command via MQTT; ensure state changes to pending - async_fire_mqtt_message(hass, "alarm/command", "ARM_AWAY") - await hass.async_block_till_done() - assert hass.states.get(entity_id).state == STATE_ALARM_PENDING - - # Fast-forward a little bit - future = dt_util.utcnow() + timedelta(seconds=1) - with patch( - ("homeassistant.components.manual_mqtt.alarm_control_panel.dt_util.utcnow"), - return_value=future, - ): - async_fire_time_changed(hass, future) - await hass.async_block_till_done() - - assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_AWAY - - -async def test_arm_night_via_command_topic(hass, mqtt_mock_entry_with_yaml_config): - """Test arming night via command topic.""" - assert await async_setup_component( - hass, - alarm_control_panel.DOMAIN, - { - alarm_control_panel.DOMAIN: { - "platform": "manual_mqtt", - "name": "test", - "pending_time": 1, - "state_topic": "alarm/state", - "command_topic": "alarm/command", - "payload_arm_night": "ARM_NIGHT", - } - }, - ) - await hass.async_block_till_done() - - entity_id = "alarm_control_panel.test" - - assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED - - # Fire the arm command via MQTT; ensure state changes to pending - async_fire_mqtt_message(hass, "alarm/command", "ARM_NIGHT") - await hass.async_block_till_done() - assert hass.states.get(entity_id).state == STATE_ALARM_PENDING - - # Fast-forward a little bit - future = dt_util.utcnow() + timedelta(seconds=1) - with patch( - ("homeassistant.components.manual_mqtt.alarm_control_panel.dt_util.utcnow"), - return_value=future, - ): - async_fire_time_changed(hass, future) - await hass.async_block_till_done() - - assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_NIGHT + assert hass.states.get(entity_id).state == expected_state async def test_disarm_pending_via_command_topic(hass, mqtt_mock_entry_with_yaml_config): diff --git a/tests/components/matter/conftest.py b/tests/components/matter/conftest.py index b541e7c439e..486e2fd26ac 100644 --- a/tests/components/matter/conftest.py +++ b/tests/components/matter/conftest.py @@ -33,6 +33,9 @@ async def matter_client_fixture() -> AsyncGenerator[MagicMock, None]: """Mock listen.""" if init_ready is not None: init_ready.set() + listen_block = asyncio.Event() + await listen_block.wait() + assert False, "Listen was not cancelled!" client.connect = AsyncMock(side_effect=connect) client.start_listening = AsyncMock(side_effect=listen) diff --git a/tests/components/matter/test_adapter.py b/tests/components/matter/test_adapter.py index 33a439d2b09..6bd341b0f2f 100644 --- a/tests/components/matter/test_adapter.py +++ b/tests/components/matter/test_adapter.py @@ -1,46 +1,53 @@ """Test the adapter.""" from __future__ import annotations -from typing import Any +from unittest.mock import MagicMock +from matter_server.common.helpers.util import dataclass_from_dict +from matter_server.common.models.events import EventType +from matter_server.common.models.node import MatterNode import pytest from homeassistant.components.matter.const import DOMAIN from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry as dr -from .common import setup_integration_with_node_fixture - -# TEMP: Tests need to be fixed -pytestmark = pytest.mark.skip("all tests still WIP") +from .common import load_and_parse_node_fixture, setup_integration_with_node_fixture async def test_device_registry_single_node_device( - hass: HomeAssistant, hass_storage: dict[str, Any] + hass: HomeAssistant, + matter_client: MagicMock, ) -> None: """Test bridge devices are set up correctly with via_device.""" await setup_integration_with_node_fixture( - hass, hass_storage, "lighting-example-app" + hass, + "onoff-light", + matter_client, ) dev_reg = dr.async_get(hass) - entry = dev_reg.async_get_device({(DOMAIN, "BE8F70AA40DDAE41")}) + entry = dev_reg.async_get_device({(DOMAIN, "mock-onoff-light")}) assert entry is not None - assert entry.name == "My Cool Light" + assert entry.name == "Mock OnOff Light" assert entry.manufacturer == "Nabu Casa" - assert entry.model == "M5STAMP Lighting App" + assert entry.model == "Mock Light" assert entry.hw_version == "v1.0" - assert entry.sw_version == "55ab764bea" + assert entry.sw_version == "v1.0" +@pytest.mark.skip("Waiting for a new test fixture") async def test_device_registry_bridge( - hass: HomeAssistant, hass_storage: dict[str, Any] + hass: HomeAssistant, + matter_client: MagicMock, ) -> None: """Test bridge devices are set up correctly with via_device.""" await setup_integration_with_node_fixture( - hass, hass_storage, "fake-bridge-two-light" + hass, + "fake-bridge-two-light", + matter_client, ) dev_reg = dr.async_get(hass) @@ -76,3 +83,29 @@ async def test_device_registry_bridge( assert device2_entry.model == "Mock Light" assert device2_entry.hw_version is None assert device2_entry.sw_version == "1.49.1" + + +async def test_node_added_subscription( + hass: HomeAssistant, + matter_client: MagicMock, + integration: MagicMock, +) -> None: + """Test subscription to new devices work.""" + assert matter_client.subscribe.call_count == 1 + assert matter_client.subscribe.call_args[0][1] == EventType.NODE_ADDED + + node_added_callback = matter_client.subscribe.call_args[0][0] + node_data = load_and_parse_node_fixture("onoff-light") + node = dataclass_from_dict( + MatterNode, + node_data, + ) + + entity_state = hass.states.get("light.mock_onoff_light") + assert not entity_state + + node_added_callback(EventType.NODE_ADDED, node) + await hass.async_block_till_done() + + entity_state = hass.states.get("light.mock_onoff_light") + assert entity_state diff --git a/tests/components/matter/test_config_flow.py b/tests/components/matter/test_config_flow.py index cad8cd91eb0..1a31998484b 100644 --- a/tests/components/matter/test_config_flow.py +++ b/tests/components/matter/test_config_flow.py @@ -99,7 +99,7 @@ async def test_manual_create_entry( assert client_connect.call_count == 1 assert result["type"] == FlowResultType.CREATE_ENTRY - assert result["title"] == "ws://localhost:5580/ws" + assert result["title"] == "Matter" assert result["data"] == { "url": "ws://localhost:5580/ws", "integration_created_addon": False, @@ -172,7 +172,7 @@ async def test_manual_already_configured( assert entry.data["url"] == "ws://localhost:5580/ws" assert entry.data["use_addon"] is False assert entry.data["integration_created_addon"] is False - assert entry.title == "ws://localhost:5580/ws" + assert entry.title == "Matter" assert setup_entry.call_count == 1 @@ -202,7 +202,7 @@ async def test_supervisor_discovery( assert addon_info.call_count == 1 assert client_connect.call_count == 0 assert result["type"] == FlowResultType.CREATE_ENTRY - assert result["title"] == "ws://host1:5581/ws" + assert result["title"] == "Matter" assert result["data"] == { "url": "ws://host1:5581/ws", "use_addon": True, @@ -294,7 +294,7 @@ async def test_clean_supervisor_discovery_on_user_create( assert len(hass.config_entries.flow.async_progress()) == 0 assert client_connect.call_count == 1 assert result["type"] == FlowResultType.CREATE_ENTRY - assert result["title"] == "ws://localhost:5580/ws" + assert result["title"] == "Matter" assert result["data"] == { "url": "ws://localhost:5580/ws", "use_addon": False, @@ -313,7 +313,7 @@ async def test_abort_supervisor_discovery_with_existing_entry( entry = MockConfigEntry( domain=DOMAIN, data={"url": "ws://localhost:5580/ws"}, - title="ws://localhost:5580/ws", + title="Matter", ) entry.add_to_hass(hass) @@ -424,7 +424,7 @@ async def test_supervisor_discovery_addon_not_running( assert start_addon.call_args == call(hass, "core_matter_server") assert client_connect.call_count == 1 assert result["type"] == FlowResultType.CREATE_ENTRY - assert result["title"] == "ws://host1:5581/ws" + assert result["title"] == "Matter" assert result["data"] == { "url": "ws://host1:5581/ws", "use_addon": True, @@ -481,7 +481,7 @@ async def test_supervisor_discovery_addon_not_installed( assert start_addon.call_args == call(hass, "core_matter_server") assert client_connect.call_count == 1 assert result["type"] == FlowResultType.CREATE_ENTRY - assert result["title"] == "ws://host1:5581/ws" + assert result["title"] == "Matter" assert result["data"] == { "url": "ws://host1:5581/ws", "use_addon": True, @@ -521,7 +521,7 @@ async def test_not_addon( assert client_connect.call_count == 1 assert result["type"] == FlowResultType.CREATE_ENTRY - assert result["title"] == "ws://localhost:5581/ws" + assert result["title"] == "Matter" assert result["data"] == { "url": "ws://localhost:5581/ws", "use_addon": False, @@ -555,7 +555,7 @@ async def test_addon_running( assert addon_info.call_count == 1 assert client_connect.call_count == 1 assert result["type"] == FlowResultType.CREATE_ENTRY - assert result["title"] == "ws://host1:5581/ws" + assert result["title"] == "Matter" assert result["data"] == { "url": "ws://host1:5581/ws", "use_addon": True, @@ -656,7 +656,7 @@ async def test_addon_running_already_configured( data={ "url": "ws://localhost:5580/ws", }, - title="ws://localhost:5580/ws", + title="Matter", ) entry.add_to_hass(hass) @@ -676,7 +676,7 @@ async def test_addon_running_already_configured( assert result["type"] == FlowResultType.ABORT assert result["reason"] == "reconfiguration_successful" assert entry.data["url"] == "ws://host1:5581/ws" - assert entry.title == "ws://host1:5581/ws" + assert entry.title == "Matter" assert setup_entry.call_count == 1 @@ -711,7 +711,7 @@ async def test_addon_installed( assert start_addon.call_args == call(hass, "core_matter_server") assert result["type"] == FlowResultType.CREATE_ENTRY - assert result["title"] == "ws://host1:5581/ws" + assert result["title"] == "Matter" assert result["data"] == { "url": "ws://host1:5581/ws", "use_addon": True, @@ -804,7 +804,7 @@ async def test_addon_installed_already_configured( data={ "url": "ws://localhost:5580/ws", }, - title="ws://localhost:5580/ws", + title="Matter", ) entry.add_to_hass(hass) @@ -831,7 +831,7 @@ async def test_addon_installed_already_configured( assert result["type"] == FlowResultType.ABORT assert result["reason"] == "reconfiguration_successful" assert entry.data["url"] == "ws://host1:5581/ws" - assert entry.title == "ws://host1:5581/ws" + assert entry.title == "Matter" assert setup_entry.call_count == 1 @@ -877,7 +877,7 @@ async def test_addon_not_installed( assert start_addon.call_args == call(hass, "core_matter_server") assert result["type"] == FlowResultType.CREATE_ENTRY - assert result["title"] == "ws://host1:5581/ws" + assert result["title"] == "Matter" assert result["data"] == { "url": "ws://host1:5581/ws", "use_addon": True, @@ -938,7 +938,7 @@ async def test_addon_not_installed_already_configured( data={ "url": "ws://localhost:5580/ws", }, - title="ws://localhost:5580/ws", + title="Matter", ) entry.add_to_hass(hass) @@ -975,5 +975,5 @@ async def test_addon_not_installed_already_configured( assert result["type"] == FlowResultType.ABORT assert result["reason"] == "reconfiguration_successful" assert entry.data["url"] == "ws://host1:5581/ws" - assert entry.title == "ws://host1:5581/ws" + assert entry.title == "Matter" assert setup_entry.call_count == 1 diff --git a/tests/components/matter/test_init.py b/tests/components/matter/test_init.py index c40d35ff74a..a3febe799a5 100644 --- a/tests/components/matter/test_init.py +++ b/tests/components/matter/test_init.py @@ -2,20 +2,211 @@ from __future__ import annotations import asyncio -from unittest.mock import AsyncMock, MagicMock, call +from collections.abc import Generator +from unittest.mock import AsyncMock, MagicMock, call, patch -from matter_server.client.exceptions import InvalidServerVersion +from matter_server.client.exceptions import CannotConnect, InvalidServerVersion +from matter_server.common.helpers.util import dataclass_from_dict +from matter_server.common.models.error import MatterError +from matter_server.common.models.node import MatterNode import pytest from homeassistant.components.hassio import HassioAPIError from homeassistant.components.matter.const import DOMAIN from homeassistant.config_entries import ConfigEntryDisabler, ConfigEntryState +from homeassistant.const import STATE_UNAVAILABLE from homeassistant.core import HomeAssistant from homeassistant.helpers import issue_registry as ir +from .common import load_and_parse_node_fixture + from tests.common import MockConfigEntry +@pytest.fixture(name="connect_timeout") +def connect_timeout_fixture() -> Generator[int, None, None]: + """Mock the connect timeout.""" + with patch("homeassistant.components.matter.CONNECT_TIMEOUT", new=0) as timeout: + yield timeout + + +@pytest.fixture(name="listen_ready_timeout") +def listen_ready_timeout_fixture() -> Generator[int, None, None]: + """Mock the listen ready timeout.""" + with patch( + "homeassistant.components.matter.LISTEN_READY_TIMEOUT", new=0 + ) as timeout: + yield timeout + + +async def test_entry_setup_unload( + hass: HomeAssistant, + matter_client: MagicMock, +) -> None: + """Test the integration set up and unload.""" + node_data = load_and_parse_node_fixture("onoff-light") + node = dataclass_from_dict( + MatterNode, + node_data, + ) + matter_client.get_nodes.return_value = [node] + matter_client.get_node.return_value = node + entry = MockConfigEntry(domain="matter", data={"url": "ws://localhost:5580/ws"}) + entry.add_to_hass(hass) + + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert matter_client.connect.call_count == 1 + assert entry.state == ConfigEntryState.LOADED + entity_state = hass.states.get("light.mock_onoff_light") + assert entity_state + assert entity_state.state != STATE_UNAVAILABLE + + await hass.config_entries.async_unload(entry.entry_id) + + assert matter_client.disconnect.call_count == 1 + assert entry.state == ConfigEntryState.NOT_LOADED + entity_state = hass.states.get("light.mock_onoff_light") + assert entity_state + assert entity_state.state == STATE_UNAVAILABLE + + +async def test_home_assistant_stop( + hass: HomeAssistant, + matter_client: MagicMock, + integration: MockConfigEntry, +) -> None: + """Test clean up on home assistant stop.""" + await hass.async_stop() + + assert matter_client.disconnect.call_count == 1 + + +@pytest.mark.parametrize("error", [CannotConnect("Boom"), Exception("Boom")]) +async def test_connect_failed( + hass: HomeAssistant, + matter_client: MagicMock, + error: Exception, +) -> None: + """Test failure during client connection.""" + entry = MockConfigEntry(domain=DOMAIN, data={"url": "ws://localhost:5580/ws"}) + entry.add_to_hass(hass) + matter_client.connect.side_effect = error + + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert entry.state is ConfigEntryState.SETUP_RETRY + + +async def test_connect_timeout( + hass: HomeAssistant, + matter_client: MagicMock, + connect_timeout: int, +) -> None: + """Test timeout during client connection.""" + entry = MockConfigEntry(domain=DOMAIN, data={"url": "ws://localhost:5580/ws"}) + entry.add_to_hass(hass) + + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert entry.state is ConfigEntryState.SETUP_RETRY + + +@pytest.mark.parametrize("error", [MatterError("Boom"), Exception("Boom")]) +async def test_listen_failure_timeout( + hass: HomeAssistant, + listen_ready_timeout: int, + matter_client: MagicMock, + error: Exception, +) -> None: + """Test client listen errors during the first timeout phase.""" + + async def start_listening(listen_ready: asyncio.Event) -> None: + """Mock the client start_listening method.""" + # Set the connect side effect to stop an endless loop on reload. + matter_client.connect.side_effect = MatterError("Boom") + raise error + + matter_client.start_listening.side_effect = start_listening + entry = MockConfigEntry(domain=DOMAIN, data={"url": "ws://localhost:5580/ws"}) + entry.add_to_hass(hass) + + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert entry.state is ConfigEntryState.SETUP_RETRY + + +@pytest.mark.parametrize("error", [MatterError("Boom"), Exception("Boom")]) +async def test_listen_failure_config_entry_not_loaded( + hass: HomeAssistant, + matter_client: MagicMock, + error: Exception, +) -> None: + """Test client listen errors during the final phase before config entry loaded.""" + listen_block = asyncio.Event() + + async def start_listening(listen_ready: asyncio.Event) -> None: + """Mock the client start_listening method.""" + listen_ready.set() + await listen_block.wait() + # Set the connect side effect to stop an endless loop on reload. + matter_client.connect.side_effect = MatterError("Boom") + raise error + + async def get_nodes() -> list[MagicMock]: + """Mock the client get_nodes method.""" + listen_block.set() + return [] + + matter_client.start_listening.side_effect = start_listening + matter_client.get_nodes.side_effect = get_nodes + entry = MockConfigEntry(domain=DOMAIN, data={"url": "ws://localhost:5580/ws"}) + entry.add_to_hass(hass) + + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert entry.state is ConfigEntryState.SETUP_RETRY + assert matter_client.disconnect.call_count == 1 + + +@pytest.mark.parametrize("error", [MatterError("Boom"), Exception("Boom")]) +async def test_listen_failure_config_entry_loaded( + hass: HomeAssistant, + matter_client: MagicMock, + error: Exception, +) -> None: + """Test client listen errors after config entry is loaded.""" + listen_block = asyncio.Event() + + async def start_listening(listen_ready: asyncio.Event) -> None: + """Mock the client start_listening method.""" + listen_ready.set() + await listen_block.wait() + # Set the connect side effect to stop an endless loop on reload. + matter_client.connect.side_effect = MatterError("Boom") + raise error + + matter_client.start_listening.side_effect = start_listening + entry = MockConfigEntry(domain=DOMAIN, data={"url": "ws://localhost:5580/ws"}) + entry.add_to_hass(hass) + + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert entry.state == ConfigEntryState.LOADED + + listen_block.set() + await hass.async_block_till_done() + + assert entry.state == ConfigEntryState.SETUP_RETRY + assert matter_client.disconnect.call_count == 1 + + async def test_raise_addon_task_in_progress( hass: HomeAssistant, addon_not_installed: AsyncMock, diff --git a/tests/components/mazda/__init__.py b/tests/components/mazda/__init__.py index 9d20f78bc00..59b1d474140 100644 --- a/tests/components/mazda/__init__.py +++ b/tests/components/mazda/__init__.py @@ -1,7 +1,7 @@ """Tests for the Mazda Connected Services integration.""" import json -from unittest.mock import AsyncMock, MagicMock, patch +from unittest.mock import AsyncMock, MagicMock, Mock, patch from pymazda import Client as MazdaAPI @@ -35,6 +35,7 @@ async def init_integration( get_ev_vehicle_status_fixture = json.loads( load_fixture("mazda/get_ev_vehicle_status.json") ) + get_hvac_setting_fixture = json.loads(load_fixture("mazda/get_hvac_setting.json")) config_entry = MockConfigEntry(domain=DOMAIN, data=FIXTURE_USER_INPUT) config_entry.add_to_hass(hass) @@ -61,6 +62,13 @@ async def init_integration( client_mock.stop_engine = AsyncMock() client_mock.turn_off_hazard_lights = AsyncMock() client_mock.turn_on_hazard_lights = AsyncMock() + client_mock.refresh_vehicle_status = AsyncMock() + client_mock.get_hvac_setting = AsyncMock(return_value=get_hvac_setting_fixture) + client_mock.get_assumed_hvac_setting = Mock(return_value=get_hvac_setting_fixture) + client_mock.get_assumed_hvac_mode = Mock(return_value=True) + client_mock.set_hvac_setting = AsyncMock() + client_mock.turn_on_hvac = AsyncMock() + client_mock.turn_off_hvac = AsyncMock() with patch( "homeassistant.components.mazda.config_flow.MazdaAPI", diff --git a/tests/components/mazda/fixtures/get_ev_vehicle_status.json b/tests/components/mazda/fixtures/get_ev_vehicle_status.json index ee9825fcbe0..a577cab3054 100644 --- a/tests/components/mazda/fixtures/get_ev_vehicle_status.json +++ b/tests/components/mazda/fixtures/get_ev_vehicle_status.json @@ -1,6 +1,6 @@ { + "lastUpdatedTimestamp": "20210807083956", "chargeInfo": { - "lastUpdatedTimestamp": "20210807083956", "batteryLevelPercentage": 80, "drivingRangeKm": 218, "pluggedIn": true, diff --git a/tests/components/mazda/fixtures/get_hvac_setting.json b/tests/components/mazda/fixtures/get_hvac_setting.json new file mode 100644 index 00000000000..3b95832ba65 --- /dev/null +++ b/tests/components/mazda/fixtures/get_hvac_setting.json @@ -0,0 +1,6 @@ +{ + "temperature": 20, + "temperatureUnit": "C", + "frontDefroster": true, + "rearDefroster": false +} diff --git a/tests/components/mazda/test_climate.py b/tests/components/mazda/test_climate.py new file mode 100644 index 00000000000..ee98b1e8f6c --- /dev/null +++ b/tests/components/mazda/test_climate.py @@ -0,0 +1,327 @@ +"""The climate tests for the Mazda Connected Services integration.""" + +import json +from unittest.mock import patch + +import pytest + +from homeassistant.components.climate import ( + ATTR_HVAC_MODE, + ATTR_PRESET_MODE, + DOMAIN as CLIMATE_DOMAIN, + SERVICE_SET_HVAC_MODE, + SERVICE_SET_PRESET_MODE, + SERVICE_SET_TEMPERATURE, +) +from homeassistant.components.climate.const import ( + ATTR_CURRENT_TEMPERATURE, + ATTR_HVAC_MODES, + ATTR_MAX_TEMP, + ATTR_MIN_TEMP, + ATTR_PRESET_MODES, + ClimateEntityFeature, + HVACMode, +) +from homeassistant.components.mazda.climate import ( + PRESET_DEFROSTER_FRONT, + PRESET_DEFROSTER_FRONT_AND_REAR, + PRESET_DEFROSTER_OFF, + PRESET_DEFROSTER_REAR, +) +from homeassistant.components.mazda.const import DOMAIN +from homeassistant.const import ( + ATTR_ENTITY_ID, + ATTR_FRIENDLY_NAME, + ATTR_SUPPORTED_FEATURES, + ATTR_TEMPERATURE, + CONF_EMAIL, + CONF_PASSWORD, + CONF_REGION, + UnitOfTemperature, +) +from homeassistant.helpers import entity_registry as er +from homeassistant.util.unit_system import US_CUSTOMARY_SYSTEM + +from . import init_integration + +from tests.common import MockConfigEntry, load_fixture + + +async def test_climate_setup(hass): + """Test the setup of the climate entity.""" + await init_integration(hass, electric_vehicle=True) + + entity_registry = er.async_get(hass) + entry = entity_registry.async_get("climate.my_mazda3_climate") + assert entry + assert entry.unique_id == "JM000000000000000" + + state = hass.states.get("climate.my_mazda3_climate") + assert state + assert state.attributes.get(ATTR_FRIENDLY_NAME) == "My Mazda3 Climate" + + +@pytest.mark.parametrize( + "region, hvac_on, target_temperature, temperature_unit, front_defroster, rear_defroster, current_temperature_celsius, expected_hvac_mode, expected_preset_mode, expected_min_temp, expected_max_temp", + [ + # Test with HVAC off + ( + "MNAO", + False, + 20, + "C", + False, + False, + 22, + HVACMode.OFF, + PRESET_DEFROSTER_OFF, + 15.5, + 28.5, + ), + # Test with HVAC on + ( + "MNAO", + True, + 20, + "C", + False, + False, + 22, + HVACMode.HEAT_COOL, + PRESET_DEFROSTER_OFF, + 15.5, + 28.5, + ), + # Test with front defroster on + ( + "MNAO", + False, + 20, + "C", + True, + False, + 22, + HVACMode.OFF, + PRESET_DEFROSTER_FRONT, + 15.5, + 28.5, + ), + # Test with rear defroster on + ( + "MNAO", + False, + 20, + "C", + False, + True, + 22, + HVACMode.OFF, + PRESET_DEFROSTER_REAR, + 15.5, + 28.5, + ), + # Test with front and rear defrosters on + ( + "MNAO", + False, + 20, + "C", + True, + True, + 22, + HVACMode.OFF, + PRESET_DEFROSTER_FRONT_AND_REAR, + 15.5, + 28.5, + ), + # Test with temperature unit F + ( + "MNAO", + False, + 70, + "F", + False, + False, + 22, + HVACMode.OFF, + PRESET_DEFROSTER_OFF, + 61.0, + 83.0, + ), + # Test with Japan region (uses different min/max temp settings) + ( + "MJO", + False, + 20, + "C", + False, + False, + 22, + HVACMode.OFF, + PRESET_DEFROSTER_OFF, + 18.5, + 31.5, + ), + ], +) +async def test_climate_state( + hass, + region, + hvac_on, + target_temperature, + temperature_unit, + front_defroster, + rear_defroster, + current_temperature_celsius, + expected_hvac_mode, + expected_preset_mode, + expected_min_temp, + expected_max_temp, +): + """Test getting the state of the climate entity.""" + if temperature_unit == "F": + hass.config.units = US_CUSTOMARY_SYSTEM + + get_vehicles_fixture = json.loads(load_fixture("mazda/get_vehicles.json")) + get_vehicles_fixture[0]["isElectric"] = True + get_vehicle_status_fixture = json.loads( + load_fixture("mazda/get_vehicle_status.json") + ) + get_ev_vehicle_status_fixture = json.loads( + load_fixture("mazda/get_ev_vehicle_status.json") + ) + get_ev_vehicle_status_fixture["hvacInfo"][ + "interiorTemperatureCelsius" + ] = current_temperature_celsius + get_hvac_setting_fixture = { + "temperature": target_temperature, + "temperatureUnit": temperature_unit, + "frontDefroster": front_defroster, + "rearDefroster": rear_defroster, + } + + with patch( + "homeassistant.components.mazda.MazdaAPI.validate_credentials", + return_value=True, + ), patch( + "homeassistant.components.mazda.MazdaAPI.get_vehicles", + return_value=get_vehicles_fixture, + ), patch( + "homeassistant.components.mazda.MazdaAPI.get_vehicle_status", + return_value=get_vehicle_status_fixture, + ), patch( + "homeassistant.components.mazda.MazdaAPI.get_ev_vehicle_status", + return_value=get_ev_vehicle_status_fixture, + ), patch( + "homeassistant.components.mazda.MazdaAPI.get_assumed_hvac_mode", + return_value=hvac_on, + ), patch( + "homeassistant.components.mazda.MazdaAPI.get_assumed_hvac_setting", + return_value=get_hvac_setting_fixture, + ), patch( + "homeassistant.components.mazda.MazdaAPI.get_hvac_setting", + return_value=get_hvac_setting_fixture, + ): + config_entry = MockConfigEntry( + domain=DOMAIN, + data={ + CONF_EMAIL: "example@example.com", + CONF_PASSWORD: "password", + CONF_REGION: region, + }, + ) + config_entry.add_to_hass(hass) + + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + state = hass.states.get("climate.my_mazda3_climate") + assert state + assert state.state == expected_hvac_mode + assert state.attributes.get(ATTR_FRIENDLY_NAME) == "My Mazda3 Climate" + assert ( + state.attributes.get(ATTR_SUPPORTED_FEATURES) + == ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE + ) + assert state.attributes.get(ATTR_HVAC_MODES) == [HVACMode.HEAT_COOL, HVACMode.OFF] + assert state.attributes.get(ATTR_PRESET_MODES) == [ + PRESET_DEFROSTER_OFF, + PRESET_DEFROSTER_FRONT, + PRESET_DEFROSTER_REAR, + PRESET_DEFROSTER_FRONT_AND_REAR, + ] + assert state.attributes.get(ATTR_MIN_TEMP) == expected_min_temp + assert state.attributes.get(ATTR_MAX_TEMP) == expected_max_temp + assert state.attributes.get(ATTR_CURRENT_TEMPERATURE) == round( + hass.config.units.temperature( + current_temperature_celsius, UnitOfTemperature.CELSIUS + ) + ) + assert state.attributes.get(ATTR_TEMPERATURE) == target_temperature + assert state.attributes.get(ATTR_PRESET_MODE) == expected_preset_mode + + +@pytest.mark.parametrize( + "hvac_mode, api_method", + [ + (HVACMode.HEAT_COOL, "turn_on_hvac"), + (HVACMode.OFF, "turn_off_hvac"), + ], +) +async def test_set_hvac_mode(hass, hvac_mode, api_method): + """Test turning on and off the HVAC system.""" + client_mock = await init_integration(hass, electric_vehicle=True) + + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_HVAC_MODE, + {ATTR_ENTITY_ID: "climate.my_mazda3_climate", ATTR_HVAC_MODE: hvac_mode}, + blocking=True, + ) + await hass.async_block_till_done() + + getattr(client_mock, api_method).assert_called_once_with(12345) + + +async def test_set_target_temperature(hass): + """Test setting the target temperature of the climate entity.""" + client_mock = await init_integration(hass, electric_vehicle=True) + + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_TEMPERATURE, + {ATTR_ENTITY_ID: "climate.my_mazda3_climate", ATTR_TEMPERATURE: 22}, + blocking=True, + ) + await hass.async_block_till_done() + + client_mock.set_hvac_setting.assert_called_once_with(12345, 22, "C", True, False) + + +@pytest.mark.parametrize( + "preset_mode, front_defroster, rear_defroster", + [ + (PRESET_DEFROSTER_OFF, False, False), + (PRESET_DEFROSTER_FRONT, True, False), + (PRESET_DEFROSTER_REAR, False, True), + (PRESET_DEFROSTER_FRONT_AND_REAR, True, True), + ], +) +async def test_set_preset_mode(hass, preset_mode, front_defroster, rear_defroster): + """Test turning on and off the front and rear defrosters.""" + client_mock = await init_integration(hass, electric_vehicle=True) + + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_PRESET_MODE, + { + ATTR_ENTITY_ID: "climate.my_mazda3_climate", + ATTR_PRESET_MODE: preset_mode, + }, + blocking=True, + ) + await hass.async_block_till_done() + + client_mock.set_hvac_setting.assert_called_once_with( + 12345, 20, "C", front_defroster, rear_defroster + ) diff --git a/tests/components/mazda/test_sensor.py b/tests/components/mazda/test_sensor.py index 4484e6b7783..24ade7e0485 100644 --- a/tests/components/mazda/test_sensor.py +++ b/tests/components/mazda/test_sensor.py @@ -13,7 +13,7 @@ from homeassistant.const import ( LENGTH_KILOMETERS, LENGTH_MILES, PERCENTAGE, - PRESSURE_PSI, + UnitOfPressure, ) from homeassistant.helpers import entity_registry as er from homeassistant.util.unit_system import US_CUSTOMARY_SYSTEM @@ -76,9 +76,9 @@ async def test_sensors(hass): ) assert state.attributes.get(ATTR_ICON) == "mdi:car-tire-alert" assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.PRESSURE - assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == PRESSURE_PSI + assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfPressure.KPA assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT - assert state.state == "35" + assert state.state == "241" entry = entity_registry.async_get("sensor.my_mazda3_front_left_tire_pressure") assert entry assert entry.unique_id == "JM000000000000000_front_left_tire_pressure" @@ -92,9 +92,9 @@ async def test_sensors(hass): ) assert state.attributes.get(ATTR_ICON) == "mdi:car-tire-alert" assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.PRESSURE - assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == PRESSURE_PSI + assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfPressure.KPA assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT - assert state.state == "35" + assert state.state == "241" entry = entity_registry.async_get("sensor.my_mazda3_front_right_tire_pressure") assert entry assert entry.unique_id == "JM000000000000000_front_right_tire_pressure" @@ -107,9 +107,9 @@ async def test_sensors(hass): ) assert state.attributes.get(ATTR_ICON) == "mdi:car-tire-alert" assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.PRESSURE - assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == PRESSURE_PSI + assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfPressure.KPA assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT - assert state.state == "33" + assert state.state == "228" entry = entity_registry.async_get("sensor.my_mazda3_rear_left_tire_pressure") assert entry assert entry.unique_id == "JM000000000000000_rear_left_tire_pressure" @@ -122,9 +122,9 @@ async def test_sensors(hass): ) assert state.attributes.get(ATTR_ICON) == "mdi:car-tire-alert" assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.PRESSURE - assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == PRESSURE_PSI + assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfPressure.KPA assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT - assert state.state == "33" + assert state.state == "228" entry = entity_registry.async_get("sensor.my_mazda3_rear_right_tire_pressure") assert entry assert entry.unique_id == "JM000000000000000_rear_right_tire_pressure" diff --git a/tests/components/media_source/test_local_source.py b/tests/components/media_source/test_local_source.py index de36566fb56..0467244a7d6 100644 --- a/tests/components/media_source/test_local_source.py +++ b/tests/components/media_source/test_local_source.py @@ -125,7 +125,7 @@ async def test_media_view(hass, hass_client): async def test_upload_view(hass, hass_client, temp_dir, hass_admin_user): """Allow uploading media.""" - img = (Path(__file__).parent.parent / "image/logo.png").read_bytes() + img = (Path(__file__).parent.parent / "image_upload/logo.png").read_bytes() def get_file(name): pic = io.BytesIO(img) diff --git a/tests/components/moon/test_sensor.py b/tests/components/moon/test_sensor.py index 6f13c97a3be..e4826fce2e8 100644 --- a/tests/components/moon/test_sensor.py +++ b/tests/components/moon/test_sensor.py @@ -16,7 +16,8 @@ from homeassistant.components.moon.sensor import ( STATE_WAXING_CRESCENT, STATE_WAXING_GIBBOUS, ) -from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_ICON +from homeassistant.components.sensor import ATTR_OPTIONS, SensorDeviceClass +from homeassistant.const import ATTR_DEVICE_CLASS, ATTR_FRIENDLY_NAME, ATTR_ICON from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry as dr, entity_registry as er @@ -57,11 +58,23 @@ async def test_moon_day( assert state.state == native_value assert state.attributes[ATTR_ICON] == icon assert state.attributes[ATTR_FRIENDLY_NAME] == "Moon Phase" + assert state.attributes[ATTR_DEVICE_CLASS] == SensorDeviceClass.ENUM + assert state.attributes[ATTR_OPTIONS] == [ + STATE_FIRST_QUARTER, + STATE_FULL_MOON, + STATE_LAST_QUARTER, + STATE_NEW_MOON, + STATE_WANING_CRESCENT, + STATE_WANING_GIBBOUS, + STATE_WAXING_CRESCENT, + STATE_WAXING_GIBBOUS, + ] entity_registry = er.async_get(hass) entry = entity_registry.async_get("sensor.moon_phase") assert entry assert entry.unique_id == mock_config_entry.entry_id + assert entry.translation_key == "phase" device_registry = dr.async_get(hass) assert entry.device_id diff --git a/tests/components/mqtt/test_common.py b/tests/components/mqtt/test_common.py index e5880b981a2..9a5a01ec3fd 100644 --- a/tests/components/mqtt/test_common.py +++ b/tests/components/mqtt/test_common.py @@ -18,6 +18,7 @@ from homeassistant.const import ( SERVICE_RELOAD, STATE_UNAVAILABLE, ) +from homeassistant.core import HomeAssistant from homeassistant.generated.mqtt import MQTT from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers.dispatcher import async_dispatcher_send @@ -1820,3 +1821,15 @@ async def help_test_unload_config_entry_with_platform( discovery_setup_entity = hass.states.get(f"{domain}.discovery_setup") assert discovery_setup_entity is None + + +async def help_test_discovery_setup( + hass: HomeAssistant, domain: str, discovery_data_payload: str, name: str +) -> None: + """Test setting up an MQTT entity using discovery.""" + async_fire_mqtt_message( + hass, f"homeassistant/{domain}/{name}/config", discovery_data_payload + ) + await hass.async_block_till_done() + state = hass.states.get(f"{domain}.{name}") + assert state.state is not None diff --git a/tests/components/mqtt/test_device_tracker.py b/tests/components/mqtt/test_device_tracker.py index 6db5811afd4..ee43fe7da6a 100644 --- a/tests/components/mqtt/test_device_tracker.py +++ b/tests/components/mqtt/test_device_tracker.py @@ -429,6 +429,122 @@ async def test_setting_device_tracker_location_via_lat_lon_message( assert state.state == STATE_UNKNOWN +async def test_setting_device_tracker_location_via_reset_message( + hass, mqtt_mock_entry_no_yaml_config, caplog +): + """Test the automatic inference of zones via MQTT via reset.""" + await mqtt_mock_entry_no_yaml_config() + async_fire_mqtt_message( + hass, + "homeassistant/device_tracker/bla/config", + "{ " + '"name": "test", ' + '"state_topic": "test-topic", ' + '"json_attributes_topic": "attributes-topic" ' + "}", + ) + + hass.states.async_set( + "zone.school", + "zoning", + { + "latitude": 30.0, + "longitude": -100.0, + "radius": 100, + "friendly_name": "School", + }, + ) + + await hass.async_block_till_done() + + state = hass.states.get("device_tracker.test") + assert state.attributes["source_type"] == "gps" + + assert state.state == STATE_UNKNOWN + + hass.config.latitude = 32.87336 + hass.config.longitude = -117.22743 + + # test reset and gps attributes + async_fire_mqtt_message( + hass, + "attributes-topic", + '{"latitude":32.87336,"longitude": -117.22743, "gps_accuracy":1.5}', + ) + async_fire_mqtt_message(hass, "test-topic", "None") + + state = hass.states.get("device_tracker.test") + assert state.attributes["latitude"] == 32.87336 + assert state.attributes["longitude"] == -117.22743 + assert state.attributes["gps_accuracy"] == 1.5 + assert state.attributes["source_type"] == "gps" + assert state.state == STATE_HOME + + # test manual state override + async_fire_mqtt_message(hass, "test-topic", "Work") + + state = hass.states.get("device_tracker.test") + assert state.state == "Work" + + # test reset + async_fire_mqtt_message(hass, "test-topic", "None") + + state = hass.states.get("device_tracker.test") + assert state.state == STATE_HOME + + # test reset inferring correct school area + async_fire_mqtt_message( + hass, + "attributes-topic", + '{"latitude":30.0,"longitude":-100.0,"gps_accuracy":1.5}', + ) + + state = hass.states.get("device_tracker.test") + assert state.state == "School" + + +async def test_setting_device_tracker_location_via_abbr_reset_message( + hass, mqtt_mock_entry_no_yaml_config, caplog +): + """Test the setting of reset via abbreviated names and custom payloads via MQTT.""" + await mqtt_mock_entry_no_yaml_config() + async_fire_mqtt_message( + hass, + "homeassistant/device_tracker/bla/config", + "{ " + '"name": "test", ' + '"state_topic": "test-topic", ' + '"json_attributes_topic": "attributes-topic", ' + '"pl_rst": "reset" ' + "}", + ) + + await hass.async_block_till_done() + + state = hass.states.get("device_tracker.test") + assert state.attributes["source_type"] == "gps" + + assert state.state == STATE_UNKNOWN + + hass.config.latitude = 32.87336 + hass.config.longitude = -117.22743 + + # test custom reset payload and gps attributes + async_fire_mqtt_message( + hass, + "attributes-topic", + '{"latitude":32.87336,"longitude": -117.22743, "gps_accuracy":1.5}', + ) + async_fire_mqtt_message(hass, "test-topic", "reset") + + state = hass.states.get("device_tracker.test") + assert state.attributes["latitude"] == 32.87336 + assert state.attributes["longitude"] == -117.22743 + assert state.attributes["gps_accuracy"] == 1.5 + assert state.attributes["source_type"] == "gps" + assert state.state == STATE_HOME + + async def test_setting_blocked_attribute_via_mqtt_json_message( hass, mqtt_mock_entry_no_yaml_config ): diff --git a/tests/components/mqtt/test_init.py b/tests/components/mqtt/test_init.py index ced93a8c997..38849167959 100644 --- a/tests/components/mqtt/test_init.py +++ b/tests/components/mqtt/test_init.py @@ -1429,14 +1429,14 @@ async def test_handle_message_callback( ): """Test for handling an incoming message callback.""" await mqtt_mock_entry_no_yaml_config() - msg = ReceiveMessage("some-topic", b"test-payload", 0, False) + msg = ReceiveMessage("some-topic", b"test-payload", 1, False) mqtt_client_mock.on_connect(mqtt_client_mock, None, None, 0) await mqtt.async_subscribe(hass, "some-topic", lambda *args: 0) mqtt_client_mock.on_message(mock_mqtt, None, msg) await hass.async_block_till_done() await hass.async_block_till_done() - assert "Received message on some-topic: b'test-payload'" in caplog.text + assert "Received message on some-topic (qos=1): b'test-payload'" in caplog.text async def test_setup_override_configuration(hass, caplog, tmp_path): @@ -2021,6 +2021,37 @@ async def test_mqtt_ws_subscription( response = await client.receive_json() assert response["success"] + # Subscribe with QoS 2 + await client.send_json( + {"id": 9, "type": "mqtt/subscribe", "topic": "test-topic", "qos": 2} + ) + response = await client.receive_json() + assert response["success"] + + async_fire_mqtt_message(hass, "test-topic", "test1", 2) + async_fire_mqtt_message(hass, "test-topic", "test2", 2) + async_fire_mqtt_message(hass, "test-topic", b"\xDE\xAD\xBE\xEF", 2) + + response = await client.receive_json() + assert response["event"]["topic"] == "test-topic" + assert response["event"]["payload"] == "test1" + assert response["event"]["qos"] == 2 + + response = await client.receive_json() + assert response["event"]["topic"] == "test-topic" + assert response["event"]["payload"] == "test2" + assert response["event"]["qos"] == 2 + + response = await client.receive_json() + assert response["event"]["topic"] == "test-topic" + assert response["event"]["payload"] == "b'\\xde\\xad\\xbe\\xef'" + assert response["event"]["qos"] == 2 + + # Unsubscribe + await client.send_json({"id": 15, "type": "unsubscribe_events", "subscription": 9}) + response = await client.receive_json() + assert response["success"] + async def test_mqtt_ws_subscription_not_admin( hass, hass_ws_client, mqtt_mock_entry_no_yaml_config, hass_read_only_access_token diff --git a/tests/components/mqtt/test_select.py b/tests/components/mqtt/test_select.py index e82bd20aa2b..ddfd0074694 100644 --- a/tests/components/mqtt/test_select.py +++ b/tests/components/mqtt/test_select.py @@ -29,6 +29,7 @@ from .test_common import ( help_test_default_availability_payload, help_test_discovery_broken, help_test_discovery_removal, + help_test_discovery_setup, help_test_discovery_update, help_test_discovery_update_attr, help_test_discovery_update_unchanged, @@ -455,7 +456,7 @@ async def test_discovery_update_select(hass, mqtt_mock_entry_no_yaml_config, cap "name": "Milk", "state_topic": "test-topic", "command_topic": "test-topic", - "options": ["milk", "beer"], + "options": ["milk"], } await help_test_discovery_update( @@ -701,3 +702,27 @@ async def test_unload_entry(hass, mqtt_mock_entry_with_yaml_config, tmp_path): await help_test_unload_config_entry_with_platform( hass, mqtt_mock_entry_with_yaml_config, tmp_path, domain, config ) + + +async def test_persistent_state_after_reconfig( + hass: ha.HomeAssistant, mqtt_mock_entry_no_yaml_config +) -> None: + """Test of the state is persistent after reconfiguring the select options.""" + await mqtt_mock_entry_no_yaml_config() + discovery_data = '{ "name": "Milk", "state_topic": "test-topic", "command_topic": "test-topic", "options": ["milk", "beer"]}' + await help_test_discovery_setup(hass, SELECT_DOMAIN, discovery_data, "milk") + + # assign an initial state + async_fire_mqtt_message(hass, "test-topic", "beer") + state = hass.states.get("select.milk") + assert state.state == "beer" + assert state.attributes["options"] == ["milk", "beer"] + + # remove "milk" option + discovery_data = '{ "name": "Milk", "state_topic": "test-topic", "command_topic": "test-topic", "options": ["beer"]}' + await help_test_discovery_setup(hass, SELECT_DOMAIN, discovery_data, "milk") + + # assert the state persistent + state = hass.states.get("select.milk") + assert state.state == "beer" + assert state.attributes["options"] == ["beer"] diff --git a/tests/components/mqtt/test_sensor.py b/tests/components/mqtt/test_sensor.py index 750a6d79edd..3b586380fa0 100644 --- a/tests/components/mqtt/test_sensor.py +++ b/tests/components/mqtt/test_sensor.py @@ -690,6 +690,11 @@ async def test_valid_device_class(hass, mqtt_mock_entry_with_yaml_config): "device_class": "temperature", }, {"name": "Test 2", "state_topic": "test-topic"}, + { + "name": "Test 3", + "state_topic": "test-topic", + "device_class": None, + }, ] } }, @@ -701,6 +706,8 @@ async def test_valid_device_class(hass, mqtt_mock_entry_with_yaml_config): assert state.attributes["device_class"] == "temperature" state = hass.states.get("sensor.test_2") assert "device_class" not in state.attributes + state = hass.states.get("sensor.test_3") + assert "device_class" not in state.attributes async def test_invalid_state_class(hass, mqtt_mock_entry_no_yaml_config): @@ -739,6 +746,11 @@ async def test_valid_state_class(hass, mqtt_mock_entry_with_yaml_config): "state_class": "measurement", }, {"name": "Test 2", "state_topic": "test-topic"}, + { + "name": "Test 3", + "state_topic": "test-topic", + "state_class": None, + }, ] } }, @@ -750,6 +762,8 @@ async def test_valid_state_class(hass, mqtt_mock_entry_with_yaml_config): assert state.attributes["state_class"] == "measurement" state = hass.states.get("sensor.test_2") assert "state_class" not in state.attributes + state = hass.states.get("sensor.test_3") + assert "state_class" not in state.attributes async def test_setting_attribute_via_mqtt_json_message( diff --git a/tests/components/mysensors/conftest.py b/tests/components/mysensors/conftest.py index 7c73ce1a389..8068f6894bf 100644 --- a/tests/components/mysensors/conftest.py +++ b/tests/components/mysensors/conftest.py @@ -2,6 +2,7 @@ from __future__ import annotations from collections.abc import AsyncGenerator, Callable, Generator +from copy import deepcopy import json from typing import Any from unittest.mock import AsyncMock, MagicMock, patch @@ -143,8 +144,26 @@ async def integration_fixture( """Set up the mysensors integration with a config entry.""" config: dict[str, Any] = {} config_entry.add_to_hass(hass) + with patch( + "homeassistant.components.mysensors.device.Debouncer", autospec=True + ) as debouncer_class: + + def debouncer( + *args: Any, function: Callable | None = None, **kwargs: Any + ) -> MagicMock: + """Mock the debouncer.""" + + async def call_debouncer(): + """Mock call to debouncer.""" + if function is not None: + function() + + debounce_instance = MagicMock() + debounce_instance.async_call.side_effect = call_debouncer + return debounce_instance + + debouncer_class.side_effect = debouncer - with patch("homeassistant.components.mysensors.device.UPDATE_DELAY", new=0): await async_setup_component(hass, DOMAIN, config) await hass.async_block_till_done() yield config_entry @@ -178,7 +197,9 @@ def gateway_fixture( def load_nodes_state(fixture_path: str) -> dict: """Load mysensors nodes fixture.""" - return json.loads(load_fixture(fixture_path), cls=MySensorsJSONDecoder) + return json.loads( + load_fixture(fixture_path, integration=DOMAIN), cls=MySensorsJSONDecoder + ) def update_gateway_nodes( @@ -189,16 +210,124 @@ def update_gateway_nodes( return nodes +@pytest.fixture(name="cover_node_binary_state", scope="session") +def cover_node_binary_state_fixture() -> dict: + """Load the cover node state.""" + return load_nodes_state("cover_node_binary_state.json") + + +@pytest.fixture +def cover_node_binary( + gateway_nodes: dict[int, Sensor], cover_node_binary_state: dict +) -> Sensor: + """Load the cover child node.""" + nodes = update_gateway_nodes(gateway_nodes, deepcopy(cover_node_binary_state)) + node = nodes[1] + return node + + +@pytest.fixture(name="cover_node_percentage_state", scope="session") +def cover_node_percentage_state_fixture() -> dict: + """Load the cover node state.""" + return load_nodes_state("cover_node_percentage_state.json") + + +@pytest.fixture +def cover_node_percentage( + gateway_nodes: dict[int, Sensor], cover_node_percentage_state: dict +) -> Sensor: + """Load the cover child node.""" + nodes = update_gateway_nodes(gateway_nodes, deepcopy(cover_node_percentage_state)) + node = nodes[1] + return node + + +@pytest.fixture(name="door_sensor_state", scope="session") +def door_sensor_state_fixture() -> dict: + """Load the door sensor state.""" + return load_nodes_state("door_sensor_state.json") + + +@pytest.fixture +def door_sensor(gateway_nodes: dict[int, Sensor], door_sensor_state: dict) -> Sensor: + """Load the door sensor.""" + nodes = update_gateway_nodes(gateway_nodes, deepcopy(door_sensor_state)) + node = nodes[1] + return node + + @pytest.fixture(name="gps_sensor_state", scope="session") def gps_sensor_state_fixture() -> dict: """Load the gps sensor state.""" - return load_nodes_state("mysensors/gps_sensor_state.json") + return load_nodes_state("gps_sensor_state.json") @pytest.fixture def gps_sensor(gateway_nodes: dict[int, Sensor], gps_sensor_state: dict) -> Sensor: """Load the gps sensor.""" - nodes = update_gateway_nodes(gateway_nodes, gps_sensor_state) + nodes = update_gateway_nodes(gateway_nodes, deepcopy(gps_sensor_state)) + node = nodes[1] + return node + + +@pytest.fixture(name="dimmer_node_state", scope="session") +def dimmer_node_state_fixture() -> dict: + """Load the dimmer node state.""" + return load_nodes_state("dimmer_node_state.json") + + +@pytest.fixture +def dimmer_node(gateway_nodes: dict[int, Sensor], dimmer_node_state: dict) -> Sensor: + """Load the dimmer child node.""" + nodes = update_gateway_nodes(gateway_nodes, deepcopy(dimmer_node_state)) + node = nodes[1] + return node + + +@pytest.fixture(name="hvac_node_auto_state", scope="session") +def hvac_node_auto_state_fixture() -> dict: + """Load the hvac node auto state.""" + return load_nodes_state("hvac_node_auto_state.json") + + +@pytest.fixture +def hvac_node_auto( + gateway_nodes: dict[int, Sensor], hvac_node_auto_state: dict +) -> Sensor: + """Load the hvac auto child node.""" + nodes = update_gateway_nodes(gateway_nodes, deepcopy(hvac_node_auto_state)) + node = nodes[1] + return node + + +@pytest.fixture(name="hvac_node_cool_state", scope="session") +def hvac_node_cool_state_fixture() -> dict: + """Load the hvac node cool state.""" + return load_nodes_state("hvac_node_cool_state.json") + + +@pytest.fixture +def hvac_node_cool( + gateway_nodes: dict[int, Sensor], hvac_node_cool_state: dict +) -> Sensor: + """Load the hvac cool child node.""" + nodes = update_gateway_nodes(gateway_nodes, deepcopy(hvac_node_cool_state)) + node = nodes[1] + return node + + +@pytest.fixture(name="hvac_node_heat_state", scope="session") +def hvac_node_heat_state_fixture() -> dict: + """Load the hvac node heat state.""" + return load_nodes_state("hvac_node_heat_state.json") + + +@pytest.fixture +def hvac_node_heat( + gateway_nodes: dict[int, Sensor], hvac_node_heat_state: dict +) -> Sensor: + """Load the hvac heat child node.""" + nodes = update_gateway_nodes(gateway_nodes, deepcopy(hvac_node_heat_state)) node = nodes[1] return node @@ -206,7 +335,7 @@ def gps_sensor(gateway_nodes: dict[int, Sensor], gps_sensor_state: dict) -> Sens @pytest.fixture(name="power_sensor_state", scope="session") def power_sensor_state_fixture() -> dict: """Load the power sensor state.""" - return load_nodes_state("mysensors/power_sensor_state.json") + return load_nodes_state("power_sensor_state.json") @pytest.fixture @@ -217,10 +346,38 @@ def power_sensor(gateway_nodes: dict[int, Sensor], power_sensor_state: dict) -> return node +@pytest.fixture(name="rgb_node_state", scope="session") +def rgb_node_state_fixture() -> dict: + """Load the rgb node state.""" + return load_nodes_state("rgb_node_state.json") + + +@pytest.fixture +def rgb_node(gateway_nodes: dict[int, Sensor], rgb_node_state: dict) -> Sensor: + """Load the rgb child node.""" + nodes = update_gateway_nodes(gateway_nodes, deepcopy(rgb_node_state)) + node = nodes[1] + return node + + +@pytest.fixture(name="rgbw_node_state", scope="session") +def rgbw_node_state_fixture() -> dict: + """Load the rgbw node state.""" + return load_nodes_state("rgbw_node_state.json") + + +@pytest.fixture +def rgbw_node(gateway_nodes: dict[int, Sensor], rgbw_node_state: dict) -> Sensor: + """Load the rgbw child node.""" + nodes = update_gateway_nodes(gateway_nodes, deepcopy(rgbw_node_state)) + node = nodes[1] + return node + + @pytest.fixture(name="energy_sensor_state", scope="session") def energy_sensor_state_fixture() -> dict: """Load the energy sensor state.""" - return load_nodes_state("mysensors/energy_sensor_state.json") + return load_nodes_state("energy_sensor_state.json") @pytest.fixture @@ -236,7 +393,7 @@ def energy_sensor( @pytest.fixture(name="sound_sensor_state", scope="session") def sound_sensor_state_fixture() -> dict: """Load the sound sensor state.""" - return load_nodes_state("mysensors/sound_sensor_state.json") + return load_nodes_state("sound_sensor_state.json") @pytest.fixture @@ -250,7 +407,7 @@ def sound_sensor(gateway_nodes: dict[int, Sensor], sound_sensor_state: dict) -> @pytest.fixture(name="distance_sensor_state", scope="session") def distance_sensor_state_fixture() -> dict: """Load the distance sensor state.""" - return load_nodes_state("mysensors/distance_sensor_state.json") + return load_nodes_state("distance_sensor_state.json") @pytest.fixture @@ -263,10 +420,40 @@ def distance_sensor( return node +@pytest.fixture(name="ir_transceiver_state", scope="session") +def ir_transceiver_state_fixture() -> dict: + """Load the ir transceiver state.""" + return load_nodes_state("ir_transceiver_state.json") + + +@pytest.fixture +def ir_transceiver( + gateway_nodes: dict[int, Sensor], ir_transceiver_state: dict +) -> Sensor: + """Load the ir transceiver child node.""" + nodes = update_gateway_nodes(gateway_nodes, deepcopy(ir_transceiver_state)) + node = nodes[1] + return node + + +@pytest.fixture(name="relay_node_state", scope="session") +def relay_node_state_fixture() -> dict: + """Load the relay node state.""" + return load_nodes_state("relay_node_state.json") + + +@pytest.fixture +def relay_node(gateway_nodes: dict[int, Sensor], relay_node_state: dict) -> Sensor: + """Load the relay child node.""" + nodes = update_gateway_nodes(gateway_nodes, deepcopy(relay_node_state)) + node = nodes[1] + return node + + @pytest.fixture(name="temperature_sensor_state", scope="session") def temperature_sensor_state_fixture() -> dict: """Load the temperature sensor state.""" - return load_nodes_state("mysensors/temperature_sensor_state.json") + return load_nodes_state("temperature_sensor_state.json") @pytest.fixture @@ -282,7 +469,7 @@ def temperature_sensor( @pytest.fixture(name="text_node_state", scope="session") def text_node_state_fixture() -> dict: """Load the text node state.""" - return load_nodes_state("mysensors/text_node_state.json") + return load_nodes_state("text_node_state.json") @pytest.fixture diff --git a/tests/components/mysensors/fixtures/cover_node_binary_state.json b/tests/components/mysensors/fixtures/cover_node_binary_state.json new file mode 100644 index 00000000000..1b258156e2b --- /dev/null +++ b/tests/components/mysensors/fixtures/cover_node_binary_state.json @@ -0,0 +1,24 @@ +{ + "1": { + "sensor_id": 1, + "children": { + "1": { + "id": 1, + "type": 5, + "description": "", + "values": { + "2": "0", + "29": "0", + "30": "0", + "31": "0" + } + } + }, + "type": 17, + "sketch_name": "Cover Node", + "sketch_version": "1.0", + "battery_level": 0, + "protocol_version": "2.3.2", + "heartbeat": 0 + } +} diff --git a/tests/components/mysensors/fixtures/cover_node_percentage_state.json b/tests/components/mysensors/fixtures/cover_node_percentage_state.json new file mode 100644 index 00000000000..5eccc5f5b7d --- /dev/null +++ b/tests/components/mysensors/fixtures/cover_node_percentage_state.json @@ -0,0 +1,24 @@ +{ + "1": { + "sensor_id": 1, + "children": { + "1": { + "id": 1, + "type": 5, + "description": "", + "values": { + "3": "0", + "29": "0", + "30": "0", + "31": "0" + } + } + }, + "type": 17, + "sketch_name": "Cover Node", + "sketch_version": "1.0", + "battery_level": 0, + "protocol_version": "2.3.2", + "heartbeat": 0 + } +} diff --git a/tests/components/mysensors/fixtures/dimmer_node_state.json b/tests/components/mysensors/fixtures/dimmer_node_state.json new file mode 100644 index 00000000000..25d4c1b6132 --- /dev/null +++ b/tests/components/mysensors/fixtures/dimmer_node_state.json @@ -0,0 +1,22 @@ +{ + "1": { + "sensor_id": 1, + "children": { + "1": { + "id": 1, + "type": 4, + "description": "", + "values": { + "2": "0", + "3": "0" + } + } + }, + "type": 17, + "sketch_name": "Dimmer Node", + "sketch_version": "1.0", + "battery_level": 0, + "protocol_version": "2.3.2", + "heartbeat": 0 + } +} diff --git a/tests/components/mysensors/fixtures/door_sensor_state.json b/tests/components/mysensors/fixtures/door_sensor_state.json new file mode 100644 index 00000000000..0034ec6c92c --- /dev/null +++ b/tests/components/mysensors/fixtures/door_sensor_state.json @@ -0,0 +1,21 @@ +{ + "1": { + "sensor_id": 1, + "children": { + "1": { + "id": 1, + "type": 0, + "description": "", + "values": { + "16": "0" + } + } + }, + "type": 17, + "sketch_name": "Door Sensor", + "sketch_version": "1.0", + "battery_level": 0, + "protocol_version": "2.3.2", + "heartbeat": 0 + } +} diff --git a/tests/components/mysensors/fixtures/hvac_node_auto_state.json b/tests/components/mysensors/fixtures/hvac_node_auto_state.json new file mode 100644 index 00000000000..9e7f137492e --- /dev/null +++ b/tests/components/mysensors/fixtures/hvac_node_auto_state.json @@ -0,0 +1,26 @@ +{ + "1": { + "sensor_id": 1, + "children": { + "1": { + "id": 1, + "type": 29, + "description": "", + "values": { + "0": "20.0", + "21": "Off", + "22": "Normal", + "44": "21.0", + "45": "19.0", + "46": "Auto" + } + } + }, + "type": 17, + "sketch_name": "HVAC Node", + "sketch_version": "1.0", + "battery_level": 0, + "protocol_version": "2.3.2", + "heartbeat": 0 + } +} diff --git a/tests/components/mysensors/fixtures/hvac_node_cool_state.json b/tests/components/mysensors/fixtures/hvac_node_cool_state.json new file mode 100644 index 00000000000..f8716b8373a --- /dev/null +++ b/tests/components/mysensors/fixtures/hvac_node_cool_state.json @@ -0,0 +1,24 @@ +{ + "1": { + "sensor_id": 1, + "children": { + "1": { + "id": 1, + "type": 29, + "description": "", + "values": { + "0": "20.0", + "21": "Off", + "22": "Normal", + "44": "21.0" + } + } + }, + "type": 17, + "sketch_name": "HVAC Node", + "sketch_version": "1.0", + "battery_level": 0, + "protocol_version": "2.3.2", + "heartbeat": 0 + } +} diff --git a/tests/components/mysensors/fixtures/hvac_node_heat_state.json b/tests/components/mysensors/fixtures/hvac_node_heat_state.json new file mode 100644 index 00000000000..c12210c91f5 --- /dev/null +++ b/tests/components/mysensors/fixtures/hvac_node_heat_state.json @@ -0,0 +1,24 @@ +{ + "1": { + "sensor_id": 1, + "children": { + "1": { + "id": 1, + "type": 29, + "description": "", + "values": { + "0": "20.0", + "21": "Off", + "22": "Normal", + "45": "19.0" + } + } + }, + "type": 17, + "sketch_name": "HVAC Node", + "sketch_version": "1.0", + "battery_level": 0, + "protocol_version": "2.3.2", + "heartbeat": 0 + } +} diff --git a/tests/components/mysensors/fixtures/ir_transceiver_state.json b/tests/components/mysensors/fixtures/ir_transceiver_state.json new file mode 100644 index 00000000000..34e16e96787 --- /dev/null +++ b/tests/components/mysensors/fixtures/ir_transceiver_state.json @@ -0,0 +1,23 @@ +{ + "1": { + "sensor_id": 1, + "children": { + "1": { + "id": 1, + "type": 20, + "description": "", + "values": { + "2": "0", + "32": "test_code", + "33": "test_code" + } + } + }, + "type": 17, + "sketch_name": "IR Transceiver", + "sketch_version": "1.0", + "battery_level": 0, + "protocol_version": "2.3.2", + "heartbeat": 0 + } +} diff --git a/tests/components/mysensors/fixtures/relay_node_state.json b/tests/components/mysensors/fixtures/relay_node_state.json new file mode 100644 index 00000000000..00a71f85ad6 --- /dev/null +++ b/tests/components/mysensors/fixtures/relay_node_state.json @@ -0,0 +1,21 @@ +{ + "1": { + "sensor_id": 1, + "children": { + "1": { + "id": 1, + "type": 3, + "description": "", + "values": { + "2": "0" + } + } + }, + "type": 17, + "sketch_name": "Relay Node", + "sketch_version": "1.0", + "battery_level": 0, + "protocol_version": "2.3.2", + "heartbeat": 0 + } +} diff --git a/tests/components/mysensors/fixtures/rgb_node_state.json b/tests/components/mysensors/fixtures/rgb_node_state.json new file mode 100644 index 00000000000..4495b407fa3 --- /dev/null +++ b/tests/components/mysensors/fixtures/rgb_node_state.json @@ -0,0 +1,23 @@ +{ + "1": { + "sensor_id": 1, + "children": { + "1": { + "id": 1, + "type": 26, + "description": "", + "values": { + "2": "0", + "3": "0", + "40": "ffffff" + } + } + }, + "type": 17, + "sketch_name": "RGB Node", + "sketch_version": "1.0", + "battery_level": 0, + "protocol_version": "2.3.2", + "heartbeat": 0 + } +} diff --git a/tests/components/mysensors/fixtures/rgbw_node_state.json b/tests/components/mysensors/fixtures/rgbw_node_state.json new file mode 100644 index 00000000000..7b9349d2f7f --- /dev/null +++ b/tests/components/mysensors/fixtures/rgbw_node_state.json @@ -0,0 +1,23 @@ +{ + "1": { + "sensor_id": 1, + "children": { + "1": { + "id": 1, + "type": 27, + "description": "", + "values": { + "2": "0", + "3": "0", + "41": "ffffffff" + } + } + }, + "type": 17, + "sketch_name": "RGBW Node", + "sketch_version": "1.0", + "battery_level": 0, + "protocol_version": "2.3.2", + "heartbeat": 0 + } +} diff --git a/tests/components/mysensors/test_binary_sensor.py b/tests/components/mysensors/test_binary_sensor.py new file mode 100644 index 00000000000..7dfb188e842 --- /dev/null +++ b/tests/components/mysensors/test_binary_sensor.py @@ -0,0 +1,43 @@ +"""Provide tests for mysensors binary sensor platform.""" +from __future__ import annotations + +from collections.abc import Callable +from unittest.mock import MagicMock + +from mysensors.sensor import Sensor + +from homeassistant.components.binary_sensor import BinarySensorDeviceClass +from homeassistant.const import ATTR_DEVICE_CLASS +from homeassistant.core import HomeAssistant + + +async def test_door_sensor( + hass: HomeAssistant, + door_sensor: Sensor, + receive_message: Callable[[str], None], + transport_write: MagicMock, +) -> None: + """Test a door sensor.""" + entity_id = "binary_sensor.door_sensor_1_1" + + state = hass.states.get(entity_id) + + assert state + assert state.state == "off" + assert state.attributes[ATTR_DEVICE_CLASS] == BinarySensorDeviceClass.DOOR + + receive_message("1;1;1;0;16;1\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == "on" + + receive_message("1;1;1;0;16;0\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == "off" diff --git a/tests/components/mysensors/test_climate.py b/tests/components/mysensors/test_climate.py new file mode 100644 index 00000000000..730960f118d --- /dev/null +++ b/tests/components/mysensors/test_climate.py @@ -0,0 +1,355 @@ +"""Provide tests for mysensors climate platform.""" +from __future__ import annotations + +from collections.abc import Callable +from unittest.mock import MagicMock, call + +from mysensors.sensor import Sensor + +from homeassistant.components.climate import ( + ATTR_CURRENT_TEMPERATURE, + ATTR_FAN_MODE, + ATTR_HVAC_MODE, + ATTR_TARGET_TEMP_HIGH, + ATTR_TARGET_TEMP_LOW, + ATTR_TEMPERATURE, + DOMAIN as CLIMATE_DOMAIN, + SERVICE_SET_FAN_MODE, + SERVICE_SET_HVAC_MODE, + SERVICE_SET_TEMPERATURE, + HVACMode, +) +from homeassistant.const import ATTR_ENTITY_ID +from homeassistant.core import HomeAssistant + + +async def test_hvac_node_auto( + hass: HomeAssistant, + hvac_node_auto: Sensor, + receive_message: Callable[[str], None], + transport_write: MagicMock, +) -> None: + """Test a hvac auto node.""" + entity_id = "climate.hvac_node_1_1" + + state = hass.states.get(entity_id) + + assert state + assert state.state == HVACMode.OFF + + # Test set hvac mode auto + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_HVAC_MODE, + {ATTR_ENTITY_ID: entity_id, ATTR_HVAC_MODE: HVACMode.AUTO}, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;21;AutoChangeOver\n") + + receive_message("1;1;1;0;21;AutoChangeOver\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == HVACMode.AUTO + assert state.attributes[ATTR_TARGET_TEMP_HIGH] == 21.0 + assert state.attributes[ATTR_TARGET_TEMP_LOW] == 19.0 + assert state.attributes[ATTR_FAN_MODE] == "Normal" + assert state.attributes[ATTR_CURRENT_TEMPERATURE] == 20.0 + + transport_write.reset_mock() + + # Test set low/high target temperature + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_TEMPERATURE, + { + ATTR_ENTITY_ID: entity_id, + ATTR_TARGET_TEMP_HIGH: 22.0, + ATTR_TARGET_TEMP_LOW: 20.0, + }, + blocking=True, + ) + + assert transport_write.call_count == 2 + assert transport_write.call_args_list[0] == call("1;1;1;1;45;20.0\n") + assert transport_write.call_args_list[1] == call("1;1;1;1;44;22.0\n") + + receive_message("1;1;1;0;45;20.0\n") + receive_message("1;1;1;0;44;22.0\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == HVACMode.AUTO + assert state.attributes[ATTR_TARGET_TEMP_HIGH] == 22.0 + assert state.attributes[ATTR_TARGET_TEMP_LOW] == 20.0 + assert state.attributes[ATTR_FAN_MODE] == "Normal" + + transport_write.reset_mock() + + # Test set fan mode + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_FAN_MODE, + { + ATTR_ENTITY_ID: entity_id, + ATTR_FAN_MODE: "Max", + }, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;22;Max\n") + + receive_message("1;1;1;0;22;Max\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == HVACMode.AUTO + assert state.attributes[ATTR_FAN_MODE] == "Max" + + transport_write.reset_mock() + + # Test set hvac mode off + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_HVAC_MODE, + {ATTR_ENTITY_ID: entity_id, ATTR_HVAC_MODE: HVACMode.OFF}, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;21;Off\n") + + receive_message("1;1;1;0;21;Off\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == HVACMode.OFF + + +async def test_hvac_node_heat( + hass: HomeAssistant, + hvac_node_heat: Sensor, + receive_message: Callable[[str], None], + transport_write: MagicMock, +) -> None: + """Test a hvac heat node.""" + entity_id = "climate.hvac_node_1_1" + + state = hass.states.get(entity_id) + + assert state + assert state.state == HVACMode.OFF + + # Test set hvac mode heat + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_HVAC_MODE, + {ATTR_ENTITY_ID: entity_id, ATTR_HVAC_MODE: HVACMode.HEAT}, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;21;HeatOn\n") + + receive_message("1;1;1;0;21;HeatOn\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == HVACMode.HEAT + assert state.attributes[ATTR_TEMPERATURE] == 19.0 + assert state.attributes[ATTR_FAN_MODE] == "Normal" + assert state.attributes[ATTR_CURRENT_TEMPERATURE] == 20.0 + + transport_write.reset_mock() + + # Test set low/high target temperature + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_TEMPERATURE, + { + ATTR_ENTITY_ID: entity_id, + ATTR_TEMPERATURE: 20.0, + }, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;45;20.0\n") + + receive_message("1;1;1;0;45;20.0\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == HVACMode.HEAT + assert state.attributes[ATTR_TEMPERATURE] == 20.0 + assert state.attributes[ATTR_FAN_MODE] == "Normal" + + transport_write.reset_mock() + + # Test set fan mode + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_FAN_MODE, + { + ATTR_ENTITY_ID: entity_id, + ATTR_FAN_MODE: "Min", + }, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;22;Min\n") + + receive_message("1;1;1;0;22;Min\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == HVACMode.HEAT + assert state.attributes[ATTR_FAN_MODE] == "Min" + + transport_write.reset_mock() + + # Test set hvac mode off + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_HVAC_MODE, + {ATTR_ENTITY_ID: entity_id, ATTR_HVAC_MODE: HVACMode.OFF}, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;21;Off\n") + + receive_message("1;1;1;0;21;Off\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == HVACMode.OFF + + +async def test_hvac_node_cool( + hass: HomeAssistant, + hvac_node_cool: Sensor, + receive_message: Callable[[str], None], + transport_write: MagicMock, +) -> None: + """Test a hvac cool node.""" + entity_id = "climate.hvac_node_1_1" + + state = hass.states.get(entity_id) + + assert state + assert state.state == HVACMode.OFF + + # Test set hvac mode heat + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_HVAC_MODE, + {ATTR_ENTITY_ID: entity_id, ATTR_HVAC_MODE: HVACMode.COOL}, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;21;CoolOn\n") + + receive_message("1;1;1;0;21;CoolOn\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == HVACMode.COOL + assert state.attributes[ATTR_TEMPERATURE] == 21.0 + assert state.attributes[ATTR_FAN_MODE] == "Normal" + assert state.attributes[ATTR_CURRENT_TEMPERATURE] == 20.0 + + transport_write.reset_mock() + + # Test set low/high target temperature + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_TEMPERATURE, + { + ATTR_ENTITY_ID: entity_id, + ATTR_TEMPERATURE: 20.0, + }, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;44;20.0\n") + + receive_message("1;1;1;0;44;20.0\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == HVACMode.COOL + assert state.attributes[ATTR_TEMPERATURE] == 20.0 + assert state.attributes[ATTR_FAN_MODE] == "Normal" + + transport_write.reset_mock() + + # Test set fan mode + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_FAN_MODE, + { + ATTR_ENTITY_ID: entity_id, + ATTR_FAN_MODE: "Auto", + }, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;22;Auto\n") + + receive_message("1;1;1;0;22;Auto\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == HVACMode.COOL + assert state.attributes[ATTR_FAN_MODE] == "Auto" + + transport_write.reset_mock() + + # Test set hvac mode off + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_HVAC_MODE, + {ATTR_ENTITY_ID: entity_id, ATTR_HVAC_MODE: HVACMode.OFF}, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;21;Off\n") + + receive_message("1;1;1;0;21;Off\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == HVACMode.OFF diff --git a/tests/components/mysensors/test_config_flow.py b/tests/components/mysensors/test_config_flow.py index e14059c4e4f..a2bd8d5c7db 100644 --- a/tests/components/mysensors/test_config_flow.py +++ b/tests/components/mysensors/test_config_flow.py @@ -396,7 +396,10 @@ async def test_config_invalid( CONF_TOPIC_IN_PREFIX: "same1", CONF_TOPIC_OUT_PREFIX: "same2", }, - FlowResult(type="form", errors={CONF_TOPIC_IN_PREFIX: "duplicate_topic"}), + FlowResult( + type=FlowResultType.FORM, + errors={CONF_TOPIC_IN_PREFIX: "duplicate_topic"}, + ), ), ( { @@ -412,7 +415,7 @@ async def test_config_invalid( CONF_TOPIC_IN_PREFIX: "different3", CONF_TOPIC_OUT_PREFIX: "different4", }, - FlowResult(type="create_entry"), + FlowResult(type=FlowResultType.CREATE_ENTRY), ), ( { @@ -428,7 +431,10 @@ async def test_config_invalid( CONF_TOPIC_IN_PREFIX: "same1", CONF_TOPIC_OUT_PREFIX: "different4", }, - FlowResult(type="form", errors={CONF_TOPIC_IN_PREFIX: "duplicate_topic"}), + FlowResult( + type=FlowResultType.FORM, + errors={CONF_TOPIC_IN_PREFIX: "duplicate_topic"}, + ), ), ( { @@ -444,7 +450,10 @@ async def test_config_invalid( CONF_TOPIC_IN_PREFIX: "different1", CONF_TOPIC_OUT_PREFIX: "same1", }, - FlowResult(type="form", errors={CONF_TOPIC_OUT_PREFIX: "duplicate_topic"}), + FlowResult( + type=FlowResultType.FORM, + errors={CONF_TOPIC_OUT_PREFIX: "duplicate_topic"}, + ), ), ( { @@ -460,7 +469,10 @@ async def test_config_invalid( CONF_TOPIC_IN_PREFIX: "same1", CONF_TOPIC_OUT_PREFIX: "different1", }, - FlowResult(type="form", errors={CONF_TOPIC_IN_PREFIX: "duplicate_topic"}), + FlowResult( + type=FlowResultType.FORM, + errors={CONF_TOPIC_IN_PREFIX: "duplicate_topic"}, + ), ), ( { @@ -478,7 +490,8 @@ async def test_config_invalid( CONF_VERSION: "2.3", }, FlowResult( - type="form", errors={"persistence_file": "duplicate_persistence_file"} + type=FlowResultType.FORM, + errors={"persistence_file": "duplicate_persistence_file"}, ), ), ( @@ -495,7 +508,7 @@ async def test_config_invalid( CONF_TCP_PORT: 343, CONF_VERSION: "2.3", }, - FlowResult(type="create_entry"), + FlowResult(type=FlowResultType.CREATE_ENTRY), ), ( { @@ -510,7 +523,7 @@ async def test_config_invalid( CONF_TCP_PORT: 343, CONF_VERSION: "2.3", }, - FlowResult(type="create_entry"), + FlowResult(type=FlowResultType.CREATE_ENTRY), ), ( { @@ -527,7 +540,7 @@ async def test_config_invalid( CONF_TCP_PORT: 343, CONF_VERSION: "2.3", }, - FlowResult(type="form", errors={"base": "already_configured"}), + FlowResult(type=FlowResultType.FORM, errors={"base": "already_configured"}), ), ( { @@ -544,7 +557,7 @@ async def test_config_invalid( CONF_TCP_PORT: 5003, CONF_VERSION: "2.3", }, - FlowResult(type="create_entry"), + FlowResult(type=FlowResultType.CREATE_ENTRY), ), ( { @@ -559,7 +572,7 @@ async def test_config_invalid( CONF_TCP_PORT: 5003, CONF_VERSION: "2.3", }, - FlowResult(type="create_entry"), + FlowResult(type=FlowResultType.CREATE_ENTRY), ), ( { @@ -574,7 +587,7 @@ async def test_config_invalid( CONF_VERSION: "2.3", CONF_PERSISTENCE_FILE: "different2.json", }, - FlowResult(type="form", errors={"base": "already_configured"}), + FlowResult(type=FlowResultType.FORM, errors={"base": "already_configured"}), ), ( { @@ -588,7 +601,7 @@ async def test_config_invalid( CONF_DEVICE: "COM5", CONF_VERSION: "2.3", }, - FlowResult(type="create_entry"), + FlowResult(type=FlowResultType.CREATE_ENTRY), ), ( { @@ -605,7 +618,7 @@ async def test_config_invalid( CONF_VERSION: "2.3", CONF_PERSISTENCE_FILE: "different2.json", }, - FlowResult(type="form", errors={"base": "already_configured"}), + FlowResult(type=FlowResultType.FORM, errors={"base": "already_configured"}), ), ( { @@ -623,7 +636,8 @@ async def test_config_invalid( CONF_PERSISTENCE_FILE: "same.json", }, FlowResult( - type="form", errors={"persistence_file": "duplicate_persistence_file"} + type=FlowResultType.FORM, + errors={"persistence_file": "duplicate_persistence_file"}, ), ), ( @@ -640,7 +654,7 @@ async def test_config_invalid( CONF_BAUD_RATE: 115200, CONF_VERSION: "1.4", }, - FlowResult(type="create_entry"), + FlowResult(type=FlowResultType.CREATE_ENTRY), ), ], ) diff --git a/tests/components/mysensors/test_cover.py b/tests/components/mysensors/test_cover.py new file mode 100644 index 00000000000..494800f388f --- /dev/null +++ b/tests/components/mysensors/test_cover.py @@ -0,0 +1,280 @@ +"""Provide tests for mysensors cover platform.""" +from __future__ import annotations + +from collections.abc import Callable +from unittest.mock import MagicMock, call + +from mysensors.sensor import Sensor + +from homeassistant.components.cover import ( + ATTR_CURRENT_POSITION, + ATTR_POSITION, + DOMAIN as COVER_DOMAIN, + SERVICE_CLOSE_COVER, + SERVICE_OPEN_COVER, + SERVICE_SET_COVER_POSITION, + SERVICE_STOP_COVER, + STATE_CLOSED, + STATE_CLOSING, + STATE_OPEN, + STATE_OPENING, +) +from homeassistant.const import ATTR_ENTITY_ID +from homeassistant.core import HomeAssistant + + +async def test_cover_node_percentage( + hass: HomeAssistant, + cover_node_percentage: Sensor, + receive_message: Callable[[str], None], + transport_write: MagicMock, +) -> None: + """Test a cover percentage node.""" + entity_id = "cover.cover_node_1_1" + + state = hass.states.get(entity_id) + + assert state + assert state.state == STATE_CLOSED + assert state.attributes[ATTR_CURRENT_POSITION] == 0 + + await hass.services.async_call( + COVER_DOMAIN, + SERVICE_OPEN_COVER, + {ATTR_ENTITY_ID: entity_id}, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;29;1\n") + + receive_message("1;1;1;0;29;1\n") + receive_message("1;1;1;0;3;50\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == STATE_OPENING + assert state.attributes[ATTR_CURRENT_POSITION] == 50 + + transport_write.reset_mock() + + await hass.services.async_call( + COVER_DOMAIN, + SERVICE_STOP_COVER, + {ATTR_ENTITY_ID: entity_id}, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;31;1\n") + + receive_message("1;1;1;0;31;1\n") + receive_message("1;1;1;0;3;50\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == STATE_OPEN + assert state.attributes[ATTR_CURRENT_POSITION] == 50 + + transport_write.reset_mock() + + await hass.services.async_call( + COVER_DOMAIN, + SERVICE_OPEN_COVER, + {ATTR_ENTITY_ID: entity_id}, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;29;1\n") + + receive_message("1;1;1;0;31;0\n") + receive_message("1;1;1;0;29;1\n") + receive_message("1;1;1;0;3;75\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == STATE_OPENING + assert state.attributes[ATTR_CURRENT_POSITION] == 75 + + receive_message("1;1;1;0;29;0\n") + receive_message("1;1;1;0;3;100\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == STATE_OPEN + assert state.attributes[ATTR_CURRENT_POSITION] == 100 + + transport_write.reset_mock() + + await hass.services.async_call( + COVER_DOMAIN, + SERVICE_CLOSE_COVER, + {ATTR_ENTITY_ID: entity_id}, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;30;1\n") + + receive_message("1;1;1;0;30;1\n") + receive_message("1;1;1;0;3;50\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == STATE_CLOSING + assert state.attributes[ATTR_CURRENT_POSITION] == 50 + + receive_message("1;1;1;0;30;0\n") + receive_message("1;1;1;0;3;0\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == STATE_CLOSED + assert state.attributes[ATTR_CURRENT_POSITION] == 0 + + transport_write.reset_mock() + + await hass.services.async_call( + COVER_DOMAIN, + SERVICE_SET_COVER_POSITION, + {ATTR_ENTITY_ID: entity_id, ATTR_POSITION: 25}, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;3;25\n") + + receive_message("1;1;1;0;3;25\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == STATE_OPEN + assert state.attributes[ATTR_CURRENT_POSITION] == 25 + + +async def test_cover_node_binary( + hass: HomeAssistant, + cover_node_binary: Sensor, + receive_message: Callable[[str], None], + transport_write: MagicMock, +) -> None: + """Test a cover binary node.""" + entity_id = "cover.cover_node_1_1" + + state = hass.states.get(entity_id) + + assert state + assert state.state == STATE_CLOSED + + await hass.services.async_call( + COVER_DOMAIN, + SERVICE_OPEN_COVER, + {ATTR_ENTITY_ID: entity_id}, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;29;1\n") + + receive_message("1;1;1;0;29;1\n") + receive_message("1;1;1;0;2;1\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == STATE_OPENING + + transport_write.reset_mock() + + await hass.services.async_call( + COVER_DOMAIN, + SERVICE_STOP_COVER, + {ATTR_ENTITY_ID: entity_id}, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;31;1\n") + + receive_message("1;1;1;0;31;1\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == STATE_OPEN + + transport_write.reset_mock() + + await hass.services.async_call( + COVER_DOMAIN, + SERVICE_OPEN_COVER, + {ATTR_ENTITY_ID: entity_id}, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;29;1\n") + + receive_message("1;1;1;0;31;0\n") + receive_message("1;1;1;0;29;1\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == STATE_OPENING + + receive_message("1;1;1;0;29;0\n") + receive_message("1;1;1;0;2;1\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == STATE_OPEN + + transport_write.reset_mock() + + await hass.services.async_call( + COVER_DOMAIN, + SERVICE_CLOSE_COVER, + {ATTR_ENTITY_ID: entity_id}, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;30;1\n") + + receive_message("1;1;1;0;30;1\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == STATE_CLOSING + + receive_message("1;1;1;0;30;0\n") + receive_message("1;1;1;0;2;0\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == STATE_CLOSED diff --git a/tests/components/mysensors/test_device_tracker.py b/tests/components/mysensors/test_device_tracker.py new file mode 100644 index 00000000000..63c9ed7b1da --- /dev/null +++ b/tests/components/mysensors/test_device_tracker.py @@ -0,0 +1,49 @@ +"""Provide tests for mysensors device tracker platform.""" +from __future__ import annotations + +from collections.abc import Callable + +from mysensors.sensor import Sensor + +from homeassistant.components.device_tracker import ATTR_SOURCE_TYPE, SourceType +from homeassistant.const import ATTR_LATITUDE, ATTR_LONGITUDE, STATE_NOT_HOME +from homeassistant.core import HomeAssistant + + +async def test_gps_sensor( + hass: HomeAssistant, + gps_sensor: Sensor, + receive_message: Callable[[str], None], +) -> None: + """Test a gps sensor.""" + entity_id = "device_tracker.gps_sensor_1_1" + altitude = 0 + latitude = "40.742" + longitude = "-73.989" + message_string = f"1;1;1;0;49;{latitude},{longitude},{altitude}\n" + + receive_message(message_string) + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == STATE_NOT_HOME + assert state.attributes[ATTR_SOURCE_TYPE] == SourceType.GPS + assert state.attributes[ATTR_LATITUDE] == float(latitude) + assert state.attributes[ATTR_LONGITUDE] == float(longitude) + + latitude = "40.782" + longitude = "-73.965" + message_string = f"1;1;1;0;49;{latitude},{longitude},{altitude}\n" + + receive_message(message_string) + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == STATE_NOT_HOME + assert state.attributes[ATTR_SOURCE_TYPE] == SourceType.GPS + assert state.attributes[ATTR_LATITUDE] == float(latitude) + assert state.attributes[ATTR_LONGITUDE] == float(longitude) diff --git a/tests/components/mysensors/test_light.py b/tests/components/mysensors/test_light.py new file mode 100644 index 00000000000..8d4ce445779 --- /dev/null +++ b/tests/components/mysensors/test_light.py @@ -0,0 +1,315 @@ +"""Provide tests for mysensors light platform.""" +from __future__ import annotations + +from collections.abc import Callable +from unittest.mock import MagicMock, call + +from mysensors.sensor import Sensor + +from homeassistant.components.light import ( + ATTR_BRIGHTNESS, + ATTR_RGB_COLOR, + ATTR_RGBW_COLOR, + DOMAIN as LIGHT_DOMAIN, +) +from homeassistant.core import HomeAssistant + + +async def test_dimmer_node( + hass: HomeAssistant, + dimmer_node: Sensor, + receive_message: Callable[[str], None], + transport_write: MagicMock, +) -> None: + """Test a dimmer node.""" + entity_id = "light.dimmer_node_1_1" + + state = hass.states.get(entity_id) + + assert state + assert state.state == "off" + + # Test turn on + await hass.services.async_call( + LIGHT_DOMAIN, + "turn_on", + {"entity_id": entity_id}, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;2;1\n") + + receive_message("1;1;1;0;2;1\n") + receive_message("1;1;1;0;3;100\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == "on" + assert state.attributes[ATTR_BRIGHTNESS] == 255 + + transport_write.reset_mock() + + # Test turn on brightness + await hass.services.async_call( + LIGHT_DOMAIN, + "turn_on", + {"entity_id": entity_id, "brightness": 128}, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;3;50\n") + + receive_message("1;1;1;0;2;1\n") + receive_message("1;1;1;0;3;50\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == "on" + assert state.attributes[ATTR_BRIGHTNESS] == 128 + + transport_write.reset_mock() + + # Test turn off + await hass.services.async_call( + LIGHT_DOMAIN, + "turn_off", + {"entity_id": entity_id}, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;2;0\n") + + receive_message("1;1;1;0;2;0\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == "off" + + +async def test_rgb_node( + hass: HomeAssistant, + rgb_node: Sensor, + receive_message: Callable[[str], None], + transport_write: MagicMock, +) -> None: + """Test a rgb node.""" + entity_id = "light.rgb_node_1_1" + + state = hass.states.get(entity_id) + + assert state + assert state.state == "off" + + # Test turn on + await hass.services.async_call( + LIGHT_DOMAIN, + "turn_on", + {"entity_id": entity_id}, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;2;1\n") + + receive_message("1;1;1;0;2;1\n") + receive_message("1;1;1;0;3;100\n") + receive_message("1;1;1;0;40;ffffff\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == "on" + assert state.attributes[ATTR_BRIGHTNESS] == 255 + assert state.attributes[ATTR_RGB_COLOR] == (255, 255, 255) + + transport_write.reset_mock() + + # Test turn on brightness + await hass.services.async_call( + LIGHT_DOMAIN, + "turn_on", + {"entity_id": entity_id, "brightness": 128}, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;3;50\n") + + receive_message("1;1;1;0;2;1\n") + receive_message("1;1;1;0;3;50\n") + receive_message("1;1;1;0;40;ffffff\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == "on" + assert state.attributes[ATTR_BRIGHTNESS] == 128 + assert state.attributes[ATTR_RGB_COLOR] == (255, 255, 255) + + transport_write.reset_mock() + + # Test turn off + await hass.services.async_call( + LIGHT_DOMAIN, + "turn_off", + {"entity_id": entity_id}, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;2;0\n") + + receive_message("1;1;1;0;2;0\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == "off" + + transport_write.reset_mock() + + # Test turn on rgb + await hass.services.async_call( + LIGHT_DOMAIN, + "turn_on", + {"entity_id": entity_id, ATTR_RGB_COLOR: (255, 0, 0)}, + blocking=True, + ) + + assert transport_write.call_count == 2 + assert transport_write.call_args_list[0] == call("1;1;1;1;2;1\n") + assert transport_write.call_args_list[1] == call("1;1;1;1;40;ff0000\n") + + receive_message("1;1;1;0;2;1\n") + receive_message("1;1;1;0;3;50\n") + receive_message("1;1;1;0;40;ff0000\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == "on" + assert state.attributes[ATTR_BRIGHTNESS] == 128 + assert state.attributes[ATTR_RGB_COLOR] == (255, 0, 0) + + +async def test_rgbw_node( + hass: HomeAssistant, + rgbw_node: Sensor, + receive_message: Callable[[str], None], + transport_write: MagicMock, +) -> None: + """Test a rgbw node.""" + entity_id = "light.rgbw_node_1_1" + + state = hass.states.get(entity_id) + + assert state + assert state.state == "off" + + # Test turn on + await hass.services.async_call( + LIGHT_DOMAIN, + "turn_on", + {"entity_id": entity_id}, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;2;1\n") + + receive_message("1;1;1;0;2;1\n") + receive_message("1;1;1;0;3;100\n") + receive_message("1;1;1;0;41;ffffffff\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == "on" + assert state.attributes[ATTR_BRIGHTNESS] == 255 + assert state.attributes[ATTR_RGBW_COLOR] == (255, 255, 255, 255) + + transport_write.reset_mock() + + # Test turn on brightness + await hass.services.async_call( + LIGHT_DOMAIN, + "turn_on", + {"entity_id": entity_id, "brightness": 128}, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;3;50\n") + + receive_message("1;1;1;0;2;1\n") + receive_message("1;1;1;0;3;50\n") + receive_message("1;1;1;0;41;ffffffff\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == "on" + assert state.attributes[ATTR_BRIGHTNESS] == 128 + assert state.attributes[ATTR_RGBW_COLOR] == (255, 255, 255, 255) + + transport_write.reset_mock() + + # Test turn off + await hass.services.async_call( + LIGHT_DOMAIN, + "turn_off", + {"entity_id": entity_id}, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;2;0\n") + + receive_message("1;1;1;0;2;0\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == "off" + + transport_write.reset_mock() + + # Test turn on rgbw + await hass.services.async_call( + LIGHT_DOMAIN, + "turn_on", + {"entity_id": entity_id, ATTR_RGBW_COLOR: (255, 0, 0, 0)}, + blocking=True, + ) + + assert transport_write.call_count == 2 + assert transport_write.call_args_list[0] == call("1;1;1;1;2;1\n") + assert transport_write.call_args_list[1] == call("1;1;1;1;41;ff000000\n") + + receive_message("1;1;1;0;2;1\n") + receive_message("1;1;1;0;3;50\n") + receive_message("1;1;1;0;41;ff000000\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == "on" + assert state.attributes[ATTR_BRIGHTNESS] == 128 + assert state.attributes[ATTR_RGBW_COLOR] == (255, 0, 0, 0) diff --git a/tests/components/mysensors/test_sensor.py b/tests/components/mysensors/test_sensor.py index 3a1b7b56872..7b5854b9efe 100644 --- a/tests/components/mysensors/test_sensor.py +++ b/tests/components/mysensors/test_sensor.py @@ -48,9 +48,6 @@ async def test_gps_sensor( message_string = f"1;1;1;0;49;{new_coords},{altitude}\n" receive_message(message_string) - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) @@ -105,7 +102,7 @@ async def test_sound_sensor( assert state assert state.state == "10" - assert state.attributes[ATTR_ICON] == "mdi:volume-high" + assert state.attributes[ATTR_DEVICE_CLASS] == SensorDeviceClass.SOUND_PRESSURE assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == "dB" @@ -144,9 +141,6 @@ async def test_temperature_sensor( message_string = f"1;1;1;0;0;{temperature}\n" receive_message(message_string) - # the integration adds multiple jobs to do the update currently - await hass.async_block_till_done() - await hass.async_block_till_done() await hass.async_block_till_done() state = hass.states.get(entity_id) diff --git a/tests/components/mysensors/test_switch.py b/tests/components/mysensors/test_switch.py new file mode 100644 index 00000000000..b77d540d543 --- /dev/null +++ b/tests/components/mysensors/test_switch.py @@ -0,0 +1,142 @@ +"""Provide tests for mysensors switch platform.""" +from __future__ import annotations + +from collections.abc import Callable +from unittest.mock import MagicMock, call + +from mysensors.sensor import Sensor + +from homeassistant.components.mysensors.const import DOMAIN +from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN +from homeassistant.core import HomeAssistant + + +async def test_relay_node( + hass: HomeAssistant, + relay_node: Sensor, + receive_message: Callable[[str], None], + transport_write: MagicMock, +) -> None: + """Test a relay node.""" + entity_id = "switch.relay_node_1_1" + + state = hass.states.get(entity_id) + + assert state + assert state.state == "off" + + await hass.services.async_call( + SWITCH_DOMAIN, + "turn_on", + {"entity_id": entity_id}, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;2;1\n") + + receive_message("1;1;1;0;2;1\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == "on" + + transport_write.reset_mock() + + await hass.services.async_call( + SWITCH_DOMAIN, + "turn_off", + {"entity_id": entity_id}, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;2;0\n") + + receive_message("1;1;1;0;2;0\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == "off" + + +async def test_ir_transceiver( + hass: HomeAssistant, + ir_transceiver: Sensor, + receive_message: Callable[[str], None], + transport_write: MagicMock, +) -> None: + """Test an ir transceiver.""" + entity_id = "switch.ir_transceiver_1_1" + + state = hass.states.get(entity_id) + + assert state + assert state.state == "off" + + await hass.services.async_call( + SWITCH_DOMAIN, + "turn_on", + {"entity_id": entity_id}, + blocking=True, + ) + + assert transport_write.call_count == 2 + assert transport_write.call_args_list[0] == call("1;1;1;0;32;test_code\n") + assert transport_write.call_args_list[1] == call("1;1;1;1;2;1\n") + + receive_message("1;1;1;0;2;1\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == "on" + assert state.attributes["V_IR_SEND"] == "test_code" + + transport_write.reset_mock() + + await hass.services.async_call( + SWITCH_DOMAIN, + "turn_off", + {"entity_id": entity_id}, + blocking=True, + ) + + assert transport_write.call_count == 1 + assert transport_write.call_args == call("1;1;1;1;2;0\n") + + receive_message("1;1;1;0;2;0\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == "off" + + transport_write.reset_mock() + + await hass.services.async_call( + DOMAIN, + "send_ir_code", + {"entity_id": entity_id, "V_IR_SEND": "new_code"}, + blocking=True, + ) + + assert transport_write.call_count == 2 + assert transport_write.call_args_list[0] == call("1;1;1;0;32;new_code\n") + assert transport_write.call_args_list[1] == call("1;1;1;1;2;1\n") + + receive_message("1;1;1;0;32;new_code\n") + receive_message("1;1;1;0;2;1\n") + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + + assert state + assert state.state == "on" + assert state.attributes["V_IR_SEND"] == "new_code" diff --git a/tests/components/nam/test_sensor.py b/tests/components/nam/test_sensor.py index dc9e9a76d76..271f8a7cb85 100644 --- a/tests/components/nam/test_sensor.py +++ b/tests/components/nam/test_sensor.py @@ -6,6 +6,7 @@ from nettigo_air_monitor import ApiError from homeassistant.components.nam.const import DOMAIN from homeassistant.components.sensor import ( + ATTR_OPTIONS, ATTR_STATE_CLASS, DOMAIN as SENSOR_DOMAIN, SensorDeviceClass, @@ -231,12 +232,20 @@ async def test_sensor(hass): state = hass.states.get("sensor.nettigo_air_monitor_pmsx003_caqi_level") assert state assert state.state == "very low" - assert state.attributes.get(ATTR_DEVICE_CLASS) == "nam__caqi_level" + assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.ENUM + assert state.attributes.get(ATTR_OPTIONS) == [ + "very low", + "low", + "medium", + "high", + "very high", + ] assert state.attributes.get(ATTR_ICON) == "mdi:air-filter" entry = registry.async_get("sensor.nettigo_air_monitor_pmsx003_caqi_level") assert entry assert entry.unique_id == "aa:bb:cc:dd:ee:ff-pms_caqi_level" + assert entry.translation_key == "caqi_level" state = hass.states.get("sensor.nettigo_air_monitor_pmsx003_caqi") assert state @@ -323,12 +332,20 @@ async def test_sensor(hass): state = hass.states.get("sensor.nettigo_air_monitor_sds011_caqi_level") assert state assert state.state == "very low" - assert state.attributes.get(ATTR_DEVICE_CLASS) == "nam__caqi_level" + assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.ENUM + assert state.attributes.get(ATTR_OPTIONS) == [ + "very low", + "low", + "medium", + "high", + "very high", + ] assert state.attributes.get(ATTR_ICON) == "mdi:air-filter" entry = registry.async_get("sensor.nettigo_air_monitor_sds011_caqi_level") assert entry assert entry.unique_id == "aa:bb:cc:dd:ee:ff-sds011_caqi_level" + assert entry.translation_key == "caqi_level" state = hass.states.get("sensor.nettigo_air_monitor_sds011_particulate_matter_2_5") assert state @@ -358,12 +375,20 @@ async def test_sensor(hass): state = hass.states.get("sensor.nettigo_air_monitor_sps30_caqi_level") assert state assert state.state == "medium" - assert state.attributes.get(ATTR_DEVICE_CLASS) == "nam__caqi_level" + assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.ENUM + assert state.attributes.get(ATTR_OPTIONS) == [ + "very low", + "low", + "medium", + "high", + "very high", + ] assert state.attributes.get(ATTR_ICON) == "mdi:air-filter" entry = registry.async_get("sensor.nettigo_air_monitor_sps30_caqi_level") assert entry assert entry.unique_id == "aa:bb:cc:dd:ee:ff-sps30_caqi_level" + assert entry.translation_key == "caqi_level" state = hass.states.get("sensor.nettigo_air_monitor_sps30_particulate_matter_1_0") assert state diff --git a/tests/components/nina/__init__.py b/tests/components/nina/__init__.py index da09b0ba17b..d8a70a180dd 100644 --- a/tests/components/nina/__init__.py +++ b/tests/components/nina/__init__.py @@ -19,9 +19,16 @@ def mocked_request_function(url: str) -> dict[str, Any]: load_fixture("sample_regions.json", "nina") ) + dummy_response_labels: dict[str, Any] = json.loads( + load_fixture("sample_labels.json", "nina") + ) + if "https://warnung.bund.de/api31/dashboard/" in url: return dummy_response + if "https://warnung.bund.de/api/appdata/gsb/labels/de_labels.json" in url: + return dummy_response_labels + if ( url == "https://www.xrepository.de/api/xrepository/urn:de:bund:destatis:bevoelkerungsstatistik:schluessel:rs_2021-07-31/download/Regionalschl_ssel_2021-07-31.json" diff --git a/tests/components/nina/fixtures/sample_labels.json b/tests/components/nina/fixtures/sample_labels.json new file mode 100644 index 00000000000..80ee31cc79f --- /dev/null +++ b/tests/components/nina/fixtures/sample_labels.json @@ -0,0 +1,225 @@ +{ + "BBK-ISC-050": "Halten Sie Abflüsse und Schächte frei, damit das Wasser abfließen kann.", + "BBK-ISC-052": "Halten Sie Abstand zu Bäumen und Gebäuden, von denen sich Dachlawinen lösen können.", + "BBK-ISC-051": "Halten Sie Abstand zu beschädigten Gebäuden und Stromleitungen.", + "BBK-ISC-058": "Schützen Sie sich vor der Sonne.", + "BBK-ISC-057": "Nutzen Sie öffentliche Verkehrsmittel.", + "BBK-ISC-059": "Schützen Sie sich vor direkter Sonneneinstrahlung (durch Aufenthalt im Schatten, Bekleidung, Kopfbedeckung etc.).", + "BBK-ISC-054": "Begeben Sie sich wenn möglich in sichere Räume des Gebäudes auf der hangabgewandten Seite.", + "BBK-ISC-053": "Halten Sie Abstand zu Bäumen und Stromleitungen. Sie können unter der Schneelast abbrechen, entwurzeln bzw. abknicken.", + "BBK-ISC-056": "Achten Sie auf ausströmendes Gas. Ein Hinweis darauf können Zischgeräusche oder ein gastypischer Geruch sein. Benutzen Sie keine Streichhölzer, Feuerzeuge oder Ähnliches: Offenes Feuer kann in Kombination mit ausströmendem Gas zu Explosionen und Bränden führen.", + "BBK-ISC-055": "Achten Sie auf zusätzliche Gefahren, z.B. Elektrizität. Vor allem im Zusammenhang mit beschädigten Wasserrohren werden elektrische Leitungen zur Gefahr, weil das austretende Wasser den Strom leiten kann.", + "BBK-ISC-061": "Vermeiden Sie längere Aufenthalte und Aktivitäten im Freien.", + "BBK-ISC-060": "Halten Sie sich nach Möglichkeit in kühlen Räumen und Gebäuden auf.", + "BBK-ISC-063": "Trinken Sie viel Wasser und achten Sie auf leichtes Essen.", + "BBK-ISC-062": "Tragen Sie im Freien eine Sonnenbrille mit 100% UV-Schutz.", + "BBK-ISC-069": "Vermeiden Sie jeden Hautkontakt mit Leitungswasser. Stellen Sie die Wasserzufuhr zu Ihrem Haus ab.", + "BBK-ISC-068": "Trinken Sie nur Mineralwasser aus der Flasche.", + "BBK-ISC-065": "Suchen Sie höher liegende Gebiete auf und warten Sie ab. Flutwellen können noch über Stunden hinweg auftreten.", + "BBK-ISC-064": "Verlassen Sie das betroffene Gebiet sofort und suchen Sie höher liegende Gebiete auf.", + "version": "11", + "BBK-ISC-067": "Trinken Sie kein Leitungswasser.", + "BBK-ISC-066": "Kochen Sie das Wasser ab, bevor Sie es zum Trinken oder in der Küche verwenden.", + "BBK-ISC-072": "Suchen Sie Deckung, zum Beispiel unter einem stabilen Tisch. Warten Sie ab, bis das Erdbeben vorüber ist.", + "BBK-ISC-071": "Suchen Sie Deckung und warten Sie ab, bis das Erdbeben vorüber ist.", + "BBK-ISC-074": "Verlassen Sie sofort beschädigte Gebäude. Vorsicht vor herunterfallenden Objekten.", + "BBK-ISC-073": "Halten Sie sich von Glasflächen wie Fenstern und Glastüren fern. Es besteht Verletzungsgefahr durch Glassplitter.", + "BBK-ISC-070": "Nehmen Sie gefährdete Personen vorübergehend bei sich auf.", + "BBK-ISC-079": "Bereiten Sie sich auf einen möglichen Stromausfall vor: Überprüfen Sie Ihre Vorräte an Wasser, Lebensmitteln, Bargeld, Batterien und Medikamenten.", + "BBK-ISC-076": "Berühren Sie keine Trümmerteile.", + "BBK-ISC-075": "Benutzen Sie keine Fahrstühle.", + "BBK-ISC-078": "Seien Sie auf Nachbeben gefasst.", + "BBK-ISC-077": "Halten Sie Abstand von Erdspalten und Abhängen.", + "BBK-ISC-083": "Halten Sie sämtliche Zugangswege zur Brandstelle frei.", + "BBK-ISC-082": "Es besteht keine Gefahr.", + "BBK-ISC-085": "Suchen Sie Schutz in einem Gebäude.", + "BBK-ISC-084": "Betreten Sie keine verqualmten Räume. Dort können sich tödliche Gase bilden.", + "BBK-ISC-081": "Bleiben Sie im Gebäude und warten Sie auf weitere Informationen.", + "BBK-ISC-080": "Nutzen Sie Fahrrad- oder Motorradhelme, um Ihren Kopf vor herabfallendem Gestein zu schützen.", + "BBK-ISC-087": "Schließen Sie Fenster und Türen und schalten Sie Lüftungen und Klimaanlagen ab.", + "BBK-ISC-086": "Suchen Sie sofort Schutz in einem Gebäude.", + "BBK-ISC-089": "Werfen Sie keine brennenden Zigaretten weg.", + "BBK-ISC-088": "Bedecken Sie Mund und Nase mit einem improvisierten Atemschutz (Stofftuch, Kleidungsstück, OP-Maske).", + "BBK-ISC-014": "Wir informieren Sie, wenn die Gefahr vorüber ist.", + "BBK-ISC-135": "Vermeiden Sie Aktivitäten in der Gruppe wie Teamsportarten.", + "BBK-ISC-013": "Warnen Sie andere Personen, um den Zutritt zum Gefahrenbereich zu verhindern.", + "BBK-ISC-134": "Vermeiden Sie Körperkontakt mit anderen Personen wie Begrüßungsküsse und Händeschütteln.", + "BBK-ISC-016": "Folgen Sie den Anweisungen der Einsatzkräfte.", + "BBK-ISC-015": "Achten Sie auf Durchsagen von Polizei und Feuerwehr.", + "BBK-ISC-136": "Es besteht kein Handlungsbedarf.", + "BBK-ISC-010": "Falls möglich, informieren Sie sich in den Medien, zum Beispiel im Lokalradio.", + "BBK-ISC-131": "Berühren Sie keine toten Tiere. Melden Sie Funde von toten Wildtieren den Behörden.", + "BBK-ISC-130": "Halten Sie Abstand zu betroffenen Landwirtschaftsbetrieben und beachten Sie Absperrmaßnahmen der Behörden.", + "BBK-ISC-012": "Informieren Sie Ihre Nachbarn.", + "BBK-ISC-133": "Halten Sie mindestens einen Meter Abstand zu Gesprächspartnern.", + "BBK-ISC-011": "Hören Sie regionale Radiosender.", + "BBK-ISC-132": "Waschen Sie sich regelmäßig und gründlich die Hände.", + "BBK-ISC-007": "Suchen Sie Schutz. Vermeiden Sie Autofahrten.", + "BBK-ISC-128": "Halten Sie ausreichend Abstand zum Gebäude, um sich vor herabfallenden Trümmerteilen und Staubwolken zu schützen.", + "BBK-ISC-006": "Vermeiden Sie Autofahrten.", + "BBK-ISC-127": "Nehmen Sie JETZT die Jodtabletten gemäß Packungsbeilage ein.", + "BBK-ISC-009": "Informieren Sie sich in den Medien, zum Beispiel im Lokalradio.", + "BBK-ISC-008": "Schalten Sie das Autoradio ein und achten Sie auf weitere Informationen.", + "BBK-ISC-129": "Wenn möglich, desinfizieren Sie Ihre Hände immer nach Kontakt mit möglichen Überträgern.", + "BBK-ISC-025": "Schließen Sie alle Fenster und Türen.", + "BBK-ISC-024": "Sollten Sie Hilfe beim Verlassen Ihrer Wohnung benötigen, nehmen Sie Kontakt mit Ihrer Stadt unter der angegebenen Telefonnummer auf.", + "BBK-ISC-027": "Schließen Sie die Fenster, Roll- oder Fensterläden und halten Sie sich von ungeschützten Öffnungen fern.", + "BBK-ISC-026": "Schalten Sie die Belüftung aus und schließen Sie die Fenster.", + "BBK-ISC-021": "Helfen Sie Kindern und anderen hilfsbedürftigen Personen, aber ohne sich selbst zu gefährden.", + "BBK-ISC-020": "Leisten Sie bei Bedarf Erste Hilfe, jedoch ohne sich selbst in Gefahr zu begeben.", + "BBK-ISC-023": "Nehmen Sie nur das Notwendigste mit, insbesondere Ausweise und Bargeld.", + "BBK-ISC-022": "Bereiten Sie sich auf eine Evakuierung vor.", + "BBK-ISC-018": "Telefonieren Sie nur im äußersten Notfall, damit die Leitungen nicht zusammenbrechen.", + "BBK-ISC-017": "Wählen Sie nur in Notfällen den Notruf 110 (Polizei) und 112 (Feuerwehr).", + "BBK-ISC-019": "Falls Sie relevante Beobachtungen gemacht haben, informieren Sie die Polizei (110).", + "BBK-EVC-007": "Biologische Gefahr", + "BBK-EVC-005": "Ausfall Notruf", + "BBK-ISC-030": "Meiden Sie den Aufenthalt im Freien. Halten Sie Abstand zu Bäumen, Türmen und Masten. Halten Sie mindesten 20 m Abstand zu Hochspannungsleitungen. Achten Sie auf herumfliegende Gegenstände.", + "BBK-EVC-006": "Ausfall Telefonleitung", + "BBK-EVC-009": "Bomben-/Munitionsfund", + "BBK-ISC-036": "Vermeiden Sie alle Gegenstände mit Metallteilen wie Regenschirme oder Fahrräder.", + "BBK-ISC-035": "Nehmen Sie empfindliche Geräte vom Netz.", + "BBK-ISC-038": "Lassen Sie Haus- und Nutztiere nicht ins Freie.", + "BBK-ISC-037": "Baden und duschen Sie während eines Gewitters nicht. Baden und duschen kann lebensgefährlich sein.", + "BBK-EVC-003": "Ausfall Gasversorgung", + "BBK-ISC-032": "Meiden Sie sehr große Räume wie zum Beispiel Hallen, in denen das Dach nicht durch Säulen gestützt wird.", + "BBK-EVC-004": "Ausfall IT-Systeme", + "BBK-ISC-031": "Meiden Sie Räume unmittelbar unter dem Dachstuhl.", + "BBK-EVC-001": "Angriff auf IT-Systeme", + "BBK-ISC-034": "Wenn Sie sich in offenem Gelände aufhalten, gehen Sie mit eng zusammenstehenden Füßen, möglichst in einer Mulde, auf den Fußballen in die Hocke.", + "BBK-EVC-002": "Angriff mit Kernwaffen", + "BBK-ISC-033": "Wenn es nirgendwo Schutz gibt, legen Sie sich mit dem Gesicht auf den Boden und schützen Sie Kopf und Nacken mit den Händen.", + "BBK-ISC-029": "Suchen Sie geschützte Orte auf, an denen Sie nicht von Hagel getroffen werden können.", + "BBK-ISC-028": "Bleiben Sie im Gebäude und warten Sie auf Anweisungen.", + "BBK-EVC-018": "Erdbeben", + "BBK-EVC-019": "Erdrutsch", + "BBK-EVC-016": "Deichbruch", + "BBK-ISC-041": "Gehen Sie bei Überschwemmungsgefahr nicht in Keller oder Tiefgaragen.", + "BBK-EVC-017": "Demonstration", + "BBK-ISC-040": "Begeben Sie sich in höher liegende Gebäudeteile.", + "BBK-EVC-010": "Brandgase", + "BBK-ISC-047": "Gehen Sie nicht an Gewässer, die Hochwasser führen. Flutwellen können Sie überraschen und das Ufer kann einbrechen.", + "BBK-EVC-011": "Brandgefahr", + "BBK-ISC-046": "Schalten Sie Strom, Gas und Heizungen in gefährdeten Räumen ab. Eine Stromschlaggefahr besteht bereits bei Kondenswasser! Liegt der Stromkasten im überfluteten Raum, betreten Sie diesen nicht, sondern informieren Sie die Feuerwehr (112).", + "BBK-ISC-049": "Fahren Sie nicht durch überflutete Straßen. Schon eine geringe Wasserhöhe kann die Steuerung behindern.", + "BBK-ISC-048": "Schwimmen Sie nicht in überschwemmten Straßen! Auch das Durchschreiten von überschwemmten Unterführungen ist lebensgefährlich! Durch den Druck im Kanal können Schachtabdeckungen hochgedrückt werden. Dabei entsteht ein Sog, durch den eine Person angesaugt werden kann.", + "BBK-EVC-014": "Chemische Gefahr", + "BBK-ISC-043": "Trennen Sie Gas-, Wasser- und Stromleitungen von der Versorgung.", + "BBK-EVC-015": "Dammbruch", + "BBK-ISC-042": "Bringen Sie persönliche Wertgegenstände in höher liegende Gebäudeteile.", + "BBK-EVC-012": "Chemieunfall", + "BBK-ISC-045": "Schalten Sie Strom und Heizungen in gefährdeten Räumen ab. Eine Stromschlaggefahr besteht bereits bei Kondenswasser! Liegt der Stromkasten im überfluteten Raum, betreten Sie diesen nicht, sondern informieren Sie die Feuerwehr (112).", + "BBK-ISC-044": "Schließen Sie alle Gasleitungen.", + "BBK-ISC-039": "Suchen Sie sofort höher liegende Gebiete auf.", + "BBK-EVC-029": "Gefahrgutunfall", + "BBK-EVC-027": "Gasaustritt", + "BBK-EVC-028": "Gefahr durch Waffen", + "BBK-EVC-021": "Extrem heftiger Starkregen", + "BBK-EVC-022": "Extrem starke Schneeverwehungen", + "BBK-EVC-020": "Explosionsgefahr", + "BBK-EVC-025": "Flugzeugabsturz", + "BBK-EVC-026": "Frost", + "BBK-EVC-023": "Extreme Orkanböen", + "BBK-EVC-024": "Extremes Glatteis", + "BBK-EVC-038": "Hochwasser", + "BBK-EVC-039": "Hohe UV-Strahlung", + "BBK-EVC-032": "Glatteis", + "BBK-ISC-102": "Erkundigen Sie sich über den Standort der Informationsstellen, die von den Behörden eingerichtet wurden.", + "BBK-EVC-033": "Gletscherabbruch", + "BBK-ISC-101": "Überprüfen und ergänzen Sie Ihre Ausrüstung und Vorräte an Wasser, Lebensmittel, Medikamente, Bargeld und Batterien.", + "BBK-EVC-030": "Geruchsbelästigung", + "BBK-ISC-104": "Warnen Sie andere Personen. Fordern Sie diese zur Flucht auf.", + "BBK-EVC-031": "Gewitter", + "BBK-ISC-103": "Wir melden, wenn die Gefahr vorüber ist.", + "BBK-EVC-036": "Hagel", + "BBK-EVC-037": "Hitze", + "BBK-EVC-034": "Großbrand", + "BBK-ISC-100": "Besorgen Sie sich Batterien für Taschenlampen und Radios.", + "BBK-EVC-035": "Großveranstaltung", + "BBK-EVC-049": "Nebel", + "BBK-EVC-043": "Lawinengefahr", + "BBK-ISC-113": "Sofern Sie keine Möglichkeit haben, ein Gebäude zu erreichen, legen Sie sich flach auf den Boden und schützen Sie den Kopf mit den Händen.", + "BBK-EVC-044": "Lebensbedrohliche Lage", + "BBK-ISC-112": "Legen Sie sich flach auf den Boden und schützen Sie Ihren Kopf mit den Händen.", + "BBK-EVC-041": "Insektenplage", + "BBK-ISC-115": "Sofern Sie sich in einem Fahrzeug aufhalten: Halten Sie auf dem Seitenstreifen bzw. am Fahrbahnrand an. Wenn sich ein Gebäude in der Nähe befindet, suchen Sie in diesem Gebäude Schutz.", + "BBK-EVC-042": "Lärmbelästigung", + "BBK-ISC-114": "Halten Sie auf dem Seitenstreifen bzw. am Fahrbahnrand an. Verlassen Sie das Fahrzeug: Das Benzin im Tank könnte durch die Auswirkungen einer Explosion entzündet werden.", + "BBK-EVC-047": "Luftverschmutzung", + "BBK-EVC-048": "Meteoriteneinschlag", + "BBK-EVC-045": "Lebensmittelwarnung", + "BBK-ISC-111": "Legen Sie sich auf den Boden, entfernt von Fenstern und Türen.", + "BBK-EVC-046": "Luftangriff", + "BBK-ISC-110": "Gehen Sie sofort in einen unterirdischen oder einen innenliegenden Raum mit möglichst wenig Außenwänden, Fenstern und Türen. Meiden Sie Gebäudeteile mit großen Glasflächen. Nutzen Sie keine Aufzüge.", + "BBK-ISC-109": "Gehen Sie sofort in einen innenliegenden Raum mit möglichst wenig Außenwänden, Fenstern und Türen. Meiden Sie Gebäudeteile mit Glasflächen.", + "BBK-ISC-106": "Suchen Sie Schutz, wenn Sie das Gebiet nicht sofort verlassen können.", + "BBK-EVC-040": "Infektionsgefahr", + "BBK-ISC-105": "Verstecken Sie sich.", + "BBK-ISC-108": "Gehen Sie schnell in Innenräume.", + "BBK-ISC-107": "Gehen Sie auf Polizeikräfte ruhig und besonnen zu. Halten Sie dabei die Hände hoch.", + "BBK-EVC-054": "Schädlingsbefall", + "BBK-ISC-003": "Verlassen Sie sofort das betroffene Gebiet.", + "BBK-ISC-124": "Schwangere und Kinder bis 16 sollen sich in geschlossenen Räumen aufhalten.", + "BBK-EVC-055": "Schiffshavarie", + "BBK-ISC-002": "Meiden Sie den Gefährdungsbereich.", + "BBK-ISC-123": "Bedecken Sie Mund und Nase mit einem improvisierten Atemschutz (Stofftuch, Kleidungsstück, OP-Maske). Bewegen Sie sich möglichst quer zur Windrichtung, da Sie so am schnellsten den Gefahrenbereich mit einer möglichen Gefahrstoffwolke verlassen.", + "BBK-EVC-052": "Raketenangriff", + "BBK-ISC-005": "Passen Sie Ihr Verhalten im Straßenverkehr den Verhältnissen an.", + "BBK-ISC-126": "Halten Sie Jodtabletten bereit. Nehmen Sie die Jodtabletten jetzt NOCH NICHT ein. Wenn es erforderlich sein sollte, werden wir Sie rechtzeitig informieren.", + "BBK-EVC-053": "Satellitenabsturz", + "BBK-ISC-004": "Umfahren Sie das betroffene Gebiet weiträumig.", + "BBK-ISC-125": "Ziehen Sie vor Betreten des Gebäudes Ihre Oberbekleidung und die Schuhe aus und lassen Sie diese außerhalb des Gebäudes zurück. Es könnte sein, dass Sie bereits mit einem Gefahrstoff in Kontakt gekommen sind. Waschen Sie sich zuerst die Hände, dann Gesicht und Haare sowie die Nase und die Ohren.", + "BBK-EVC-058": "Schneeverwehungen", + "BBK-ISC-120": "Informieren Sie die Einsatzkräfte über Schäden und Trümmerteile.", + "BBK-EVC-059": "Sicherheitswarnung", + "BBK-EVC-056": "Schlammlawine", + "BBK-ISC-001": "Meiden Sie das betroffene Gebiet.", + "BBK-ISC-122": "Suchen Sie einen Kellerraum oder innenliegende Räume in unteren Stockwerken auf.", + "BBK-EVC-057": "Schneefall", + "BBK-ISC-121": "Informieren Sie die Einsatzkräfte über Schäden und Trümmerteile. Trümmerteile können zusätzliche Gefahren wie Brände und Explosionen auslösen.", + "BBK-EVC-050": "Probewarnung", + "BBK-ISC-117": "Falls Sie das Fahrzeug nicht verlassen können, senken Sie den Kopf und schützen Sie diesen mit den Händen. Schalten Sie das Autoradio ein und achten Sie auf weitere Informationen.", + "BBK-EVC-051": "Radiologische Gefahr", + "BBK-ISC-116": "Halten Sie auf dem Seitenstreifen bzw. am Fahrbahnrand an. Verlassen Sie das Fahrzeug: Das Benzin im Tank könnte durch die Auswirkungen einer Explosion entzündet werden. Wenn sich ein Gebäude in der Nähe befindet, suchen Sie in diesem Gebäude Schutz.", + "BBK-ISC-119": "Berühren Sie keine Gegenstände, die Ihnen verdächtig vorkommen: Es besteht die Gefahr weiterer Explosionen. Verlassen Sie umgehend den Bereich und teilen Sie die Position verdächtiger Gegenstände den Einsatzkräften mit.", + "BBK-ISC-118": "Verlassen Sie sofort den Ort des Einschlags und bedecken Sie Mund und Nase mit einem improvisierten Atemschutz (Stofftuch, Kleidungsstück, OP-Maske). Dies schützt Sie vor Stäuben, allerdings nicht vor gasförmigen Gefahrstoffen. Suchen Sie ein Gebäude auf. Bewegen Sie sich möglichst quer zur Windrichtung, da Sie so am schnellsten den Gefahrenbereich mit einer möglichen Gefahrstoffwolke verlassen.", + "BBK-ISC-094": "Feuern Sie Feuerwerkskörper nur mit Bewilligung der Gemeinde ab, halten Sie einen Sicherheitsabstand zum Wald ein und halten Sie Löschwasser bereit.", + "BBK-ISC-093": "Zünden Sie keine Feuerwerkskörper.", + "BBK-ISC-096": "Die Stromversorgung wird so schnell wie möglich wieder hergestellt.", + "BBK-ISC-095": "Die Störung wird so schnell wie möglich behoben.", + "BBK-ISC-090": "Werfen Sie keine brennenden Zigaretten und Streichhölzer weg.", + "BBK-ISC-092": "Verwenden Sie beim Grillen fest eingerichtete Feuerstellen. Versichern Sie sich, dass Ihr Feuer vollständig gelöscht ist, bevor Sie den Ort verlassen.", + "BBK-ISC-091": "Machen Sie im Freien kein Feuer.", + "BBK-EVC-065": "Sturmflut", + "BBK-EVC-066": "Sturzflut", + "BBK-EVC-063": "Stromausfall", + "BBK-EVC-064": "Sturm", + "BBK-EVC-069": "Trinkwasserverschmutzung", + "BBK-ISC-098": "Schalten Sie alle netzbetriebenen Geräte aus.", + "BBK-ISC-097": "Reduzieren Sie Ihren Stromverbrauch über Akkus und Batterien auf das Nötigste.", + "BBK-EVC-067": "Tierseuche", + "BBK-EVC-068": "Tornado", + "BBK-ISC-099": "Reduzieren Sie Ihren Wasserverbrauch auf das Nötigste.", + "BBK-EVC-061": "Sonnensturm", + "BBK-EVC-062": "Starkregen", + "BBK-EVC-060": "Sirenentest", + "BBK-EVC-999": "Ohne Benennung", + "BBK-EVC-076": "Vulkanausbruch", + "BBK-EVC-077": "Waldbrand", + "BBK-EVC-074": "Verkehrsunfall", + "BBK-EVC-075": "Verkehrswarnung", + "BBK-EVC-078": "Warnung", + "BBK-EVC-079": "Weltkriegsbombe", + "BBK-EVC-072": "Unfall Kernkraftwerk", + "BBK-EVC-073": "Ungeklärtes Abwasser", + "BBK-EVC-070": "Tsunami", + "BBK-EVC-071": "Überschwemmung", + "BBK-EVC-087": "Extreme Wettersituationen, bei denen verbreitet mit Leiterseilschwingungen zu rechnen ist (keine Medienalarmierung)", + "BBK-EVC-085": "Schweres Gewitter mit extrem großem Hagel", + "BBK-EVC-086": "Schweres Gewitter mit extremen Orkanböen", + "BBK-EVC-080": "Zugunfall", + "BBK-EVC-083": "Extrem ergiebiger Dauerregen", + "BBK-EVC-084": "Extrem starker Schneefall", + "BBK-EVC-081": "Gesundheitsgefährdung", + "BBK-EVC-082": "Schweres Gewitter mit extrem heftigem Starkregen" +} diff --git a/tests/components/nina/fixtures/sample_warning_details.json b/tests/components/nina/fixtures/sample_warning_details.json index aa176b2199e..612885e9aba 100644 --- a/tests/components/nina/fixtures/sample_warning_details.json +++ b/tests/components/nina/fixtures/sample_warning_details.json @@ -43,7 +43,7 @@ }, { "valueName": "instructionCode", - "value": "BBK-ISC-132" + "value": "BBK-ISC-082" }, { "valueName": "sender_langname", @@ -158,6 +158,88 @@ } ] }, + "mow.DE-NW-BN-SE030-20201014-30-011": { + "identifier": "mow.DE-NW-BN-SE030-20201014-30-011", + "sender": "opendata@dwd.de", + "sent": "2021-10-11T05:21:00+01:00", + "status": "Actual", + "msgType": "Alert", + "source": "PVW", + "scope": "Public", + "code": [ + "DVN:2", + "id:2.49.0.0.276.0.DWD.PVW.1645004040000.5a168da8-ac20-4b6d-86be-d616526a7914" + ], + "info": [ + { + "language": "de-DE", + "category": ["Met"], + "event": "STURMBÖEN", + "responseType": ["Prepare"], + "urgency": "Immediate", + "severity": "Moderate", + "certainty": "Likely", + "eventCode": [ + { + "valueName": "PROFILE_VERSION", + "value": "2.1.11" + }, + { + "valueName": "LICENSE", + "value": "© GeoBasis-DE / BKG 2019 (Daten modifiziert)" + }, + { + "valueName": "II", + "value": "52" + }, + { + "valueName": "GROUP", + "value": "WIND" + }, + { + "valueName": "AREA_COLOR", + "value": "251 140 0" + } + ], + "effective": "2021-11-01T03:20:00+01:00", + "onset": "2021-11-01T05:20:00+01:00", + "expires": "3021-11-22T05:19:00+01:00", + "senderName": "Deutscher Wetterdienst", + "headline": "Ausfall Notruf 112", + "description": "Es treten Sturmböen mit Geschwindigkeiten zwischen 70 km/h (20m/s, 38kn, Bft 8) und 85 km/h (24m/s, 47kn, Bft 9) aus westlicher Richtung auf. In Schauernähe sowie in exponierten Lagen muss mit schweren Sturmböen bis 90 km/h (25m/s, 48kn, Bft 10) gerechnet werden.", + "instruction": "ACHTUNG! Hinweis auf mögliche Gefahren: Es können zum Beispiel einzelne Äste herabstürzen. Achten Sie besonders auf herabfallende Gegenstände.", + "web": "https://www.wettergefahren.de", + "contact": "Deutscher Wetterdienst", + "parameter": [ + { + "valueName": "gusts", + "value": "70-85 [km/h]" + }, + { + "valueName": "exposed gusts", + "value": "<90 [km/h]" + }, + { + "valueName": "wind direction", + "value": "west" + }, + { + "valueName": "PHGEM", + "value": "3243+168,3413+1,3424+52,3478+1,3495+2,3499,3639+2527,6168+1,6175+22,6199+36,6238,6241+7,6256,9956+184,10142,10154,10164+7,10173,10176+6,10186+1,10195+2,10199,10201+6,10214+4,10220,10249+117,10368,10373+2,10425+9,10436+1,10440+8,10450+1,10453+7,10462+1,10467+5,10474+2,10484+5,10773+68,10843+2,10847+9,10858,10867+8,10878+1,10882+68,10952+7,10961+2,11046,11056+1" + }, + { + "valueName": "ZGEM", + "value": "3243+168,3413+1,3424+52,3478+1,3495+2,3499,3639+2527,6168+1,6175+22,6199+36,6238,6241+7,6256,9956+184,10142,10154,10164+7,10173,10176+6,10186+1,10195+2,10199,10201+6,10214+4,10220,10249+117,10368,10373+2,10425+9,10436+1,10440+8,10450+1,10453+7,10462+1,10467+5,10474+2,10484+5,10773+68,10843+2,10847+9,10858,10867+8,10878+1,10882+68,10952+7,10961+2,11046,11056+1" + } + ], + "area": [ + { + "areaDesc": "Gemeinde Oberreichenbach, Gemeinde Neuweiler, Stadt Nagold, Stadt Neubulach, Gemeinde Schömberg, Gemeinde Simmersfeld, Gemeinde Simmozheim, Gemeinde Rohrdorf, Gemeinde Ostelsheim, Gemeinde Ebhausen, Gemeinde Egenhausen, Gemeinde Dobel, Stadt Bad Liebenzell, Stadt Solingen, Stadt Haiterbach, Stadt Bad Herrenalb, Gemeinde Höfen an der Enz, Gemeinde Gechingen, Gemeinde Enzklösterle, Gemeinde Gutach (Schwarzwaldbahn) und 3392 weitere." + } + ] + } + ] + }, "biw.BIWAPP-69634": { "identifier": "biw.BIWAPP-69634", "sender": "CAP@biwapp.de", diff --git a/tests/components/nina/fixtures/sample_warnings.json b/tests/components/nina/fixtures/sample_warnings.json index 12d78b03cce..83759a5e0e8 100644 --- a/tests/components/nina/fixtures/sample_warnings.json +++ b/tests/components/nina/fixtures/sample_warnings.json @@ -41,6 +41,27 @@ "sent": "2021-10-11T05:20:00+01:00", "expires": "3021-11-22T05:19:00+01:00" }, + { + "id": "mow.DE-NW-BN-SE030-20201014-30-011", + "payload": { + "version": 1, + "type": "ALERT", + "id": "mow.DE-NW-BN-SE030-20201014-30-011", + "hash": "551db820a43be7e4f39283e1dfb71b212cd520c3ee478d44f43519e9c42fde1c", + "data": { + "headline": "Ausfall Notruf 112", + "provider": "MOWAS", + "severity": "Minor", + "msgType": "Update", + "transKeys": { "event": "BBK-EVC-040" }, + "area": { "type": "ZGEM", "data": "1+11057,100001" } + } + }, + "i18nTitle": { "de": "Ausfall Notruf 112" }, + "onset": "2021-11-01T05:20:00+01:00", + "sent": "2021-10-11T05:21:00+01:00", + "expires": "3021-11-22T05:19:00+01:00" + }, { "id": "biw.BIWAPP-69634", "payload": { diff --git a/tests/components/nina/test_binary_sensor.py b/tests/components/nina/test_binary_sensor.py index 210ebc66a60..855b733ef46 100644 --- a/tests/components/nina/test_binary_sensor.py +++ b/tests/components/nina/test_binary_sensor.py @@ -10,6 +10,7 @@ from homeassistant.components.nina.const import ( ATTR_EXPIRES, ATTR_HEADLINE, ATTR_ID, + ATTR_RECOMMENDED_ACTIONS, ATTR_SENDER, ATTR_SENT, ATTR_SEVERITY, @@ -69,6 +70,7 @@ async def test_sensors(hass: HomeAssistant) -> None: ) assert state_w1.attributes.get(ATTR_SENDER) == "Deutscher Wetterdienst" assert state_w1.attributes.get(ATTR_SEVERITY) == "Minor" + assert state_w1.attributes.get(ATTR_RECOMMENDED_ACTIONS) == "" assert state_w1.attributes.get(ATTR_ID) == "mow.DE-NW-BN-SE030-20201014-30-000" assert state_w1.attributes.get(ATTR_SENT) == "2021-10-11T05:20:00+01:00" assert state_w1.attributes.get(ATTR_START) == "2021-11-01T05:20:00+01:00" @@ -85,6 +87,7 @@ async def test_sensors(hass: HomeAssistant) -> None: assert state_w2.attributes.get(ATTR_DESCRIPTION) is None assert state_w2.attributes.get(ATTR_SENDER) is None assert state_w2.attributes.get(ATTR_SEVERITY) is None + assert state_w2.attributes.get(ATTR_RECOMMENDED_ACTIONS) is None assert state_w2.attributes.get(ATTR_ID) is None assert state_w2.attributes.get(ATTR_SENT) is None assert state_w2.attributes.get(ATTR_START) is None @@ -101,6 +104,7 @@ async def test_sensors(hass: HomeAssistant) -> None: assert state_w3.attributes.get(ATTR_DESCRIPTION) is None assert state_w3.attributes.get(ATTR_SENDER) is None assert state_w3.attributes.get(ATTR_SEVERITY) is None + assert state_w3.attributes.get(ATTR_RECOMMENDED_ACTIONS) is None assert state_w3.attributes.get(ATTR_ID) is None assert state_w3.attributes.get(ATTR_SENT) is None assert state_w3.attributes.get(ATTR_START) is None @@ -117,6 +121,7 @@ async def test_sensors(hass: HomeAssistant) -> None: assert state_w4.attributes.get(ATTR_DESCRIPTION) is None assert state_w4.attributes.get(ATTR_SENDER) is None assert state_w4.attributes.get(ATTR_SEVERITY) is None + assert state_w4.attributes.get(ATTR_RECOMMENDED_ACTIONS) is None assert state_w4.attributes.get(ATTR_ID) is None assert state_w4.attributes.get(ATTR_SENT) is None assert state_w4.attributes.get(ATTR_START) is None @@ -133,6 +138,7 @@ async def test_sensors(hass: HomeAssistant) -> None: assert state_w5.attributes.get(ATTR_DESCRIPTION) is None assert state_w5.attributes.get(ATTR_SENDER) is None assert state_w5.attributes.get(ATTR_SEVERITY) is None + assert state_w5.attributes.get(ATTR_RECOMMENDED_ACTIONS) is None assert state_w5.attributes.get(ATTR_ID) is None assert state_w5.attributes.get(ATTR_SENT) is None assert state_w5.attributes.get(ATTR_START) is None @@ -176,6 +182,10 @@ async def test_sensors_without_corona_filter(hass: HomeAssistant) -> None: ) assert state_w1.attributes.get(ATTR_SENDER) == "" assert state_w1.attributes.get(ATTR_SEVERITY) == "Minor" + assert ( + state_w1.attributes.get(ATTR_RECOMMENDED_ACTIONS) + == "Es besteht keine Gefahr." + ) assert state_w1.attributes.get(ATTR_ID) == "mow.DE-BW-S-SE018-20211102-18-001" assert state_w1.attributes.get(ATTR_SENT) == "2021-11-02T20:07:16+01:00" assert state_w1.attributes.get(ATTR_START) == "" @@ -195,6 +205,7 @@ async def test_sensors_without_corona_filter(hass: HomeAssistant) -> None: ) assert state_w2.attributes.get(ATTR_SENDER) == "Deutscher Wetterdienst" assert state_w2.attributes.get(ATTR_SEVERITY) == "Minor" + assert state_w2.attributes.get(ATTR_RECOMMENDED_ACTIONS) == "" assert state_w2.attributes.get(ATTR_ID) == "mow.DE-NW-BN-SE030-20201014-30-000" assert state_w2.attributes.get(ATTR_SENT) == "2021-10-11T05:20:00+01:00" assert state_w2.attributes.get(ATTR_START) == "2021-11-01T05:20:00+01:00" @@ -211,6 +222,7 @@ async def test_sensors_without_corona_filter(hass: HomeAssistant) -> None: assert state_w3.attributes.get(ATTR_DESCRIPTION) is None assert state_w3.attributes.get(ATTR_SENDER) is None assert state_w3.attributes.get(ATTR_SEVERITY) is None + assert state_w3.attributes.get(ATTR_RECOMMENDED_ACTIONS) is None assert state_w3.attributes.get(ATTR_ID) is None assert state_w3.attributes.get(ATTR_SENT) is None assert state_w3.attributes.get(ATTR_START) is None @@ -227,6 +239,7 @@ async def test_sensors_without_corona_filter(hass: HomeAssistant) -> None: assert state_w4.attributes.get(ATTR_DESCRIPTION) is None assert state_w4.attributes.get(ATTR_SENDER) is None assert state_w4.attributes.get(ATTR_SEVERITY) is None + assert state_w4.attributes.get(ATTR_RECOMMENDED_ACTIONS) is None assert state_w4.attributes.get(ATTR_ID) is None assert state_w4.attributes.get(ATTR_SENT) is None assert state_w4.attributes.get(ATTR_START) is None @@ -243,6 +256,7 @@ async def test_sensors_without_corona_filter(hass: HomeAssistant) -> None: assert state_w5.attributes.get(ATTR_DESCRIPTION) is None assert state_w5.attributes.get(ATTR_SENDER) is None assert state_w5.attributes.get(ATTR_SEVERITY) is None + assert state_w5.attributes.get(ATTR_RECOMMENDED_ACTIONS) is None assert state_w5.attributes.get(ATTR_ID) is None assert state_w5.attributes.get(ATTR_SENT) is None assert state_w5.attributes.get(ATTR_START) is None diff --git a/tests/components/nina/test_config_flow.py b/tests/components/nina/test_config_flow.py index 9d4a0e9376d..bfea7781780 100644 --- a/tests/components/nina/test_config_flow.py +++ b/tests/components/nina/test_config_flow.py @@ -303,7 +303,9 @@ async def test_options_flow_entity_removal(hass: HomeAssistant) -> None: with patch( "pynina.baseApi.BaseAPI._makeRequest", wraps=mocked_request_function, - ): + ), patch( + "homeassistant.components.nina._async_update_listener" + ) as mock_update_listener: await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() @@ -330,3 +332,4 @@ async def test_options_flow_entity_removal(hass: HomeAssistant) -> None: ) assert len(entries) == 2 + assert len(mock_update_listener.mock_calls) == 1 diff --git a/tests/components/notion/conftest.py b/tests/components/notion/conftest.py index 7d87b9adc64..e29ea83ef2a 100644 --- a/tests/components/notion/conftest.py +++ b/tests/components/notion/conftest.py @@ -56,12 +56,20 @@ def data_task_fixture(): return json.loads(load_fixture("task_data.json", "notion")) +@pytest.fixture(name="get_client") +def get_client_fixture(client): + """Define a fixture to mock the async_get_client method.""" + return AsyncMock(return_value=client) + + @pytest.fixture(name="setup_notion") -async def setup_notion_fixture(hass, client, config): +async def setup_notion_fixture(hass, config, get_client): """Define a fixture to set up Notion.""" - with patch("homeassistant.components.notion.config_flow.async_get_client"), patch( + with patch( + "homeassistant.components.notion.config_flow.async_get_client", get_client + ), patch("homeassistant.components.notion.async_get_client", get_client), patch( "homeassistant.components.notion.PLATFORMS", [] - ), patch("homeassistant.components.notion.async_get_client", return_value=client): + ): assert await async_setup_component(hass, DOMAIN, config) await hass.async_block_till_done() yield diff --git a/tests/components/notion/test_config_flow.py b/tests/components/notion/test_config_flow.py index 92e285ba899..0eff3890274 100644 --- a/tests/components/notion/test_config_flow.py +++ b/tests/components/notion/test_config_flow.py @@ -1,5 +1,5 @@ """Define tests for the Notion config flow.""" -from unittest.mock import patch +from unittest.mock import AsyncMock, patch from aionotion.errors import InvalidCredentialsError, NotionError import pytest @@ -10,6 +10,46 @@ from homeassistant.config_entries import SOURCE_REAUTH, SOURCE_USER from homeassistant.const import CONF_PASSWORD, CONF_USERNAME +@pytest.mark.parametrize( + "get_client_with_exception,errors", + [ + (AsyncMock(side_effect=Exception), {"base": "unknown"}), + (AsyncMock(side_effect=InvalidCredentialsError), {"base": "invalid_auth"}), + (AsyncMock(side_effect=NotionError), {"base": "unknown"}), + ], +) +async def test_create_entry( + hass, client, config, errors, get_client_with_exception, setup_notion +): + """Test creating an etry (including recovery from errors).""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_USER} + ) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "user" + + # Test errors that can arise when getting a Notion API client: + with patch( + "homeassistant.components.notion.config_flow.async_get_client", + get_client_with_exception, + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_USER}, data=config + ) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["errors"] == errors + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input=config + ) + assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY + assert result["title"] == "user@host.com" + assert result["data"] == { + CONF_USERNAME: "user@host.com", + CONF_PASSWORD: "password123", + } + + async def test_duplicate_error(hass, config, config_entry): """Test that errors are shown when duplicates are added.""" result = await hass.config_entries.flow.async_init( @@ -20,62 +60,42 @@ async def test_duplicate_error(hass, config, config_entry): @pytest.mark.parametrize( - "exc,error", + "get_client_with_exception,errors", [ - (NotionError, "unknown"), - (InvalidCredentialsError, "invalid_auth"), + (AsyncMock(side_effect=Exception), {"base": "unknown"}), + (AsyncMock(side_effect=InvalidCredentialsError), {"base": "invalid_auth"}), + (AsyncMock(side_effect=NotionError), {"base": "unknown"}), ], ) -async def test_erros(hass, config, error, exc): - """Test that exceptions show the correct error.""" - with patch( - "homeassistant.components.notion.config_flow.async_get_client", side_effect=exc - ): - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_USER}, data=config - ) - assert result["errors"] == {"base": error} - - -async def test_step_reauth(hass, config, config_entry, setup_notion): - """Test that the reauth step works.""" +async def test_reauth( + hass, config, config_entry, errors, get_client_with_exception, setup_notion +): + """Test that re-auth works.""" result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_REAUTH}, data=config + DOMAIN, + context={ + "source": SOURCE_REAUTH, + "entry_id": config_entry.entry_id, + "unique_id": config_entry.unique_id, + }, + data=config, ) assert result["step_id"] == "reauth_confirm" - result = await hass.config_entries.flow.async_configure(result["flow_id"]) - assert result["type"] == data_entry_flow.FlowResultType.FORM - assert result["step_id"] == "reauth_confirm" - - with patch("homeassistant.components.notion.async_setup_entry", return_value=True): + # Test errors that can arise when getting a Notion API client: + with patch( + "homeassistant.components.notion.config_flow.async_get_client", + get_client_with_exception, + ): result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={CONF_PASSWORD: "password"} ) - await hass.async_block_till_done() + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["errors"] == errors + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={CONF_PASSWORD: "password"} + ) assert result["type"] == data_entry_flow.FlowResultType.ABORT assert result["reason"] == "reauth_successful" assert len(hass.config_entries.async_entries()) == 1 - - -async def test_show_form(hass): - """Test that the form is served with no input.""" - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_USER} - ) - assert result["type"] == data_entry_flow.FlowResultType.FORM - assert result["step_id"] == "user" - - -async def test_step_user(hass, config, setup_notion): - """Test that the user step works.""" - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_USER}, data=config - ) - assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY - assert result["title"] == "user@host.com" - assert result["data"] == { - CONF_USERNAME: "user@host.com", - CONF_PASSWORD: "password123", - } diff --git a/tests/components/number/test_init.py b/tests/components/number/test_init.py index 630506623de..f6b2c615123 100644 --- a/tests/components/number/test_init.py +++ b/tests/components/number/test_init.py @@ -857,6 +857,7 @@ def test_device_classes_aligned(): non_numeric_device_classes = { SensorDeviceClass.DATE, SensorDeviceClass.DURATION, + SensorDeviceClass.ENUM, SensorDeviceClass.TIMESTAMP, } diff --git a/tests/components/nws/test_sensor.py b/tests/components/nws/test_sensor.py index 9597618ccc8..a42dba542d1 100644 --- a/tests/components/nws/test_sensor.py +++ b/tests/components/nws/test_sensor.py @@ -1,7 +1,8 @@ """Sensors for National Weather Service (NWS).""" import pytest -from homeassistant.components.nws.const import ATTRIBUTION, DOMAIN, SENSOR_TYPES +from homeassistant.components.nws.const import ATTRIBUTION, DOMAIN +from homeassistant.components.nws.sensor import SENSOR_TYPES from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN from homeassistant.const import ATTR_ATTRIBUTION, STATE_UNKNOWN from homeassistant.helpers import entity_registry as er diff --git a/tests/components/nzbget/test_sensor.py b/tests/components/nzbget/test_sensor.py index 9cedf29c82e..796c72804aa 100644 --- a/tests/components/nzbget/test_sensor.py +++ b/tests/components/nzbget/test_sensor.py @@ -25,26 +25,51 @@ async def test_sensors(hass, nzbget_api) -> None: uptime = now - timedelta(seconds=600) sensors = { - "article_cache": ("ArticleCacheMB", "64", DATA_MEGABYTES, None), + "article_cache": ( + "ArticleCacheMB", + "64", + DATA_MEGABYTES, + SensorDeviceClass.DATA_SIZE, + ), "average_speed": ( "AverageDownloadRate", "1.19", DATA_RATE_MEGABYTES_PER_SECOND, - None, + SensorDeviceClass.DATA_RATE, ), "download_paused": ("DownloadPaused", "False", None, None), - "speed": ("DownloadRate", "2.38", DATA_RATE_MEGABYTES_PER_SECOND, None), - "size": ("DownloadedSizeMB", "256", DATA_MEGABYTES, None), - "disk_free": ("FreeDiskSpaceMB", "1024", DATA_MEGABYTES, None), + "speed": ( + "DownloadRate", + "2.38", + DATA_RATE_MEGABYTES_PER_SECOND, + SensorDeviceClass.DATA_RATE, + ), + "size": ( + "DownloadedSizeMB", + "256", + DATA_MEGABYTES, + SensorDeviceClass.DATA_SIZE, + ), + "disk_free": ( + "FreeDiskSpaceMB", + "1024", + DATA_MEGABYTES, + SensorDeviceClass.DATA_SIZE, + ), "post_processing_jobs": ("PostJobCount", "2", "Jobs", None), "post_processing_paused": ("PostPaused", "False", None, None), - "queue_size": ("RemainingSizeMB", "512", DATA_MEGABYTES, None), + "queue_size": ( + "RemainingSizeMB", + "512", + DATA_MEGABYTES, + SensorDeviceClass.DATA_SIZE, + ), "uptime": ("UpTimeSec", uptime.isoformat(), None, SensorDeviceClass.TIMESTAMP), "speed_limit": ( "DownloadLimit", "0.95", DATA_RATE_MEGABYTES_PER_SECOND, - None, + SensorDeviceClass.DATA_RATE, ), } diff --git a/tests/components/ovo_energy/test_config_flow.py b/tests/components/ovo_energy/test_config_flow.py index 5fbd4586d12..1e9a0672473 100644 --- a/tests/components/ovo_energy/test_config_flow.py +++ b/tests/components/ovo_energy/test_config_flow.py @@ -84,6 +84,9 @@ async def test_full_flow_implementation(hass: HomeAssistant) -> None: with patch( "homeassistant.components.ovo_energy.config_flow.OVOEnergy.authenticate", return_value=True, + ), patch( + "homeassistant.components.ovo_energy.config_flow.OVOEnergy.username", + "some_name", ), patch( "homeassistant.components.ovo_energy.async_setup_entry", return_value=True, diff --git a/tests/components/prometheus/test_init.py b/tests/components/prometheus/test_init.py index febbc72ec31..e49e0eb779d 100644 --- a/tests/components/prometheus/test_init.py +++ b/tests/components/prometheus/test_init.py @@ -11,6 +11,7 @@ from homeassistant.components import ( binary_sensor, climate, counter, + cover, device_tracker, humidifier, input_boolean, @@ -44,11 +45,15 @@ from homeassistant.const import ( ENERGY_KILO_WATT_HOUR, EVENT_STATE_CHANGED, PERCENTAGE, + STATE_CLOSED, + STATE_CLOSING, STATE_HOME, STATE_LOCKED, STATE_NOT_HOME, STATE_OFF, STATE_ON, + STATE_OPEN, + STATE_OPENING, STATE_UNLOCKED, TEMP_CELSIUS, TEMP_FAHRENHEIT, @@ -445,6 +450,65 @@ async def test_lock(client, lock_entities): ) +@pytest.mark.parametrize("namespace", [""]) +async def test_cover(client, cover_entities): + """Test prometheus metrics for cover.""" + data = {**cover_entities} + body = await generate_latest_metrics(client) + + open_covers = ["cover_open", "cover_position", "cover_tilt_position"] + for testcover in data: + open_metric = ( + f'cover_state{{domain="cover",' + f'entity="{cover_entities[testcover].entity_id}",' + f'friendly_name="{cover_entities[testcover].original_name}",' + f'state="open"}} {1.0 if cover_entities[testcover].unique_id in open_covers else 0.0}' + ) + assert open_metric in body + + closed_metric = ( + f'cover_state{{domain="cover",' + f'entity="{cover_entities[testcover].entity_id}",' + f'friendly_name="{cover_entities[testcover].original_name}",' + f'state="closed"}} {1.0 if cover_entities[testcover].unique_id == "cover_closed" else 0.0}' + ) + assert closed_metric in body + + opening_metric = ( + f'cover_state{{domain="cover",' + f'entity="{cover_entities[testcover].entity_id}",' + f'friendly_name="{cover_entities[testcover].original_name}",' + f'state="opening"}} {1.0 if cover_entities[testcover].unique_id == "cover_opening" else 0.0}' + ) + assert opening_metric in body + + closing_metric = ( + f'cover_state{{domain="cover",' + f'entity="{cover_entities[testcover].entity_id}",' + f'friendly_name="{cover_entities[testcover].original_name}",' + f'state="closing"}} {1.0 if cover_entities[testcover].unique_id == "cover_closing" else 0.0}' + ) + assert closing_metric in body + + if testcover == "cover_position": + position_metric = ( + f'cover_position{{domain="cover",' + f'entity="{cover_entities[testcover].entity_id}",' + f'friendly_name="{cover_entities[testcover].original_name}"' + f"}} 50.0" + ) + assert position_metric in body + + if testcover == "cover_tilt_position": + tilt_position_metric = ( + f'cover_tilt_position{{domain="cover",' + f'entity="{cover_entities[testcover].entity_id}",' + f'friendly_name="{cover_entities[testcover].original_name}"' + f"}} 50.0" + ) + assert tilt_position_metric in body + + @pytest.mark.parametrize("namespace", [""]) async def test_counter(client, counter_entities): """Test prometheus metrics for counter.""" @@ -1110,6 +1174,78 @@ async def lock_fixture(hass, registry): return data +@pytest.fixture(name="cover_entities") +async def cover_fixture(hass, registry): + """Simulate cover entities.""" + data = {} + cover_open = registry.async_get_or_create( + domain=cover.DOMAIN, + platform="test", + unique_id="cover_open", + suggested_object_id="open_shade", + original_name="Open Shade", + ) + set_state_with_entry(hass, cover_open, STATE_OPEN) + data["cover_open"] = cover_open + + cover_closed = registry.async_get_or_create( + domain=cover.DOMAIN, + platform="test", + unique_id="cover_closed", + suggested_object_id="closed_shade", + original_name="Closed Shade", + ) + set_state_with_entry(hass, cover_closed, STATE_CLOSED) + data["cover_closed"] = cover_closed + + cover_closing = registry.async_get_or_create( + domain=cover.DOMAIN, + platform="test", + unique_id="cover_closing", + suggested_object_id="closing_shade", + original_name="Closing Shade", + ) + set_state_with_entry(hass, cover_closing, STATE_CLOSING) + data["cover_closing"] = cover_closing + + cover_opening = registry.async_get_or_create( + domain=cover.DOMAIN, + platform="test", + unique_id="cover_opening", + suggested_object_id="opening_shade", + original_name="Opening Shade", + ) + set_state_with_entry(hass, cover_opening, STATE_OPENING) + data["cover_opening"] = cover_opening + + cover_position = registry.async_get_or_create( + domain=cover.DOMAIN, + platform="test", + unique_id="cover_position", + suggested_object_id="position_shade", + original_name="Position Shade", + ) + cover_position_attributes = {cover.ATTR_POSITION: 50} + set_state_with_entry(hass, cover_position, STATE_OPEN, cover_position_attributes) + data["cover_position"] = cover_position + + cover_tilt_position = registry.async_get_or_create( + domain=cover.DOMAIN, + platform="test", + unique_id="cover_tilt_position", + suggested_object_id="tilt_position_shade", + original_name="Tilt Position Shade", + ) + cover_tilt_position_attributes = {cover.ATTR_TILT_POSITION: 50} + set_state_with_entry( + hass, cover_tilt_position, STATE_OPEN, cover_tilt_position_attributes + ) + data["cover_tilt_position"] = cover_tilt_position + + await hass.async_block_till_done() + return data + + @pytest.fixture(name="input_number_entities") async def input_number_fixture(hass, registry): """Simulate input_number entities.""" diff --git a/tests/components/prusalink/test_sensor.py b/tests/components/prusalink/test_sensor.py index 3e08b2b8b53..f5e4b801d30 100644 --- a/tests/components/prusalink/test_sensor.py +++ b/tests/components/prusalink/test_sensor.py @@ -6,6 +6,7 @@ from unittest.mock import PropertyMock, patch import pytest from homeassistant.components.sensor import ( + ATTR_OPTIONS, ATTR_STATE_CLASS, SensorDeviceClass, SensorStateClass, @@ -39,6 +40,14 @@ async def test_sensors_no_job(hass: HomeAssistant, mock_config_entry, mock_api): state = hass.states.get("sensor.mock_title") assert state is not None assert state.state == "idle" + assert state.attributes[ATTR_DEVICE_CLASS] == SensorDeviceClass.ENUM + assert state.attributes[ATTR_OPTIONS] == [ + "cancelling", + "idle", + "paused", + "pausing", + "printing", + ] state = hass.states.get("sensor.mock_title_heatbed") assert state is not None diff --git a/tests/components/purpleair/__init__.py b/tests/components/purpleair/__init__.py new file mode 100644 index 00000000000..67883dabbe8 --- /dev/null +++ b/tests/components/purpleair/__init__.py @@ -0,0 +1 @@ +"""Tests for the PurpleAir integration.""" diff --git a/tests/components/purpleair/conftest.py b/tests/components/purpleair/conftest.py new file mode 100644 index 00000000000..c19ff62fdb7 --- /dev/null +++ b/tests/components/purpleair/conftest.py @@ -0,0 +1,92 @@ +"""Define fixtures for PurpleAir tests.""" +from unittest.mock import AsyncMock, Mock, patch + +from aiopurpleair.endpoints.sensors import NearbySensorResult +from aiopurpleair.models.sensors import GetSensorsResponse +import pytest + +from homeassistant.components.purpleair import DOMAIN +from homeassistant.setup import async_setup_component + +from tests.common import MockConfigEntry, load_fixture + + +@pytest.fixture(name="api") +def api_fixture(check_api_key, get_nearby_sensors, get_sensors): + """Define a fixture to return a mocked aiopurple API object.""" + api = Mock(async_check_api_key=check_api_key) + api.sensors.async_get_nearby_sensors = get_nearby_sensors + api.sensors.async_get_sensors = get_sensors + return api + + +@pytest.fixture(name="check_api_key") +def check_api_key_fixture(): + """Define a fixture to mock the method to check an API key's validity.""" + return AsyncMock() + + +@pytest.fixture(name="config_entry") +def config_entry_fixture(hass, config_entry_data, config_entry_options): + """Define a config entry fixture.""" + entry = MockConfigEntry( + domain=DOMAIN, + title="abcde", + unique_id="abcde12345", + data=config_entry_data, + options=config_entry_options, + ) + entry.add_to_hass(hass) + return entry + + +@pytest.fixture(name="config_entry_data") +def config_entry_data_fixture(): + """Define a config entry data fixture.""" + return { + "api_key": "abcde12345", + } + + +@pytest.fixture(name="config_entry_options") +def config_entry_options_fixture(): + """Define a config entry options fixture.""" + return { + "sensor_indices": [123456], + } + + +@pytest.fixture(name="get_nearby_sensors") +def get_nearby_sensors_fixture(get_sensors_response): + """Define a mocked API.sensors.async_get_nearby_sensors.""" + return AsyncMock( + return_value=[ + NearbySensorResult(sensor=sensor, distance=1.0) + for sensor in get_sensors_response.data.values() + ] + ) + + +@pytest.fixture(name="get_sensors") +def get_sensors_fixture(get_sensors_response): + """Define a mocked API.sensors.async_get_sensors.""" + return AsyncMock(return_value=get_sensors_response) + + +@pytest.fixture(name="get_sensors_response", scope="package") +def get_sensors_response_fixture(): + """Define a fixture to mock an aiopurpleair GetSensorsResponse object.""" + return GetSensorsResponse.parse_raw( + load_fixture("get_sensors_response.json", "purpleair") + ) + + +@pytest.fixture(name="setup_purpleair") +async def setup_purpleair_fixture(hass, api, config_entry_data): + """Define a fixture to set up PurpleAir.""" + with patch( + "homeassistant.components.purpleair.config_flow.API", return_value=api + ), patch("homeassistant.components.purpleair.coordinator.API", return_value=api): + assert await async_setup_component(hass, DOMAIN, config_entry_data) + await hass.async_block_till_done() + yield diff --git a/tests/components/purpleair/fixtures/__init__.py b/tests/components/purpleair/fixtures/__init__.py new file mode 100644 index 00000000000..08cb2cb5102 --- /dev/null +++ b/tests/components/purpleair/fixtures/__init__.py @@ -0,0 +1 @@ +"""Define data fixtures.""" diff --git a/tests/components/purpleair/fixtures/get_sensors_response.json b/tests/components/purpleair/fixtures/get_sensors_response.json new file mode 100644 index 00000000000..6949ca6ca04 --- /dev/null +++ b/tests/components/purpleair/fixtures/get_sensors_response.json @@ -0,0 +1,88 @@ +{ + "api_version": "V1.0.11-0.0.41", + "time_stamp": 1668985817, + "data_time_stamp": 1668985800, + "max_age": 604800, + "firmware_default_version": "7.02", + "fields": [ + "sensor_index", + "name", + "location_type", + "model", + "hardware", + "firmware_version", + "rssi", + "uptime", + "latitude", + "longitude", + "altitude", + "humidity", + "temperature", + "pressure", + "voc", + "pm1.0", + "pm2.5", + "pm10.0", + "0.3_um_count", + "0.5_um_count", + "1.0_um_count", + "2.5_um_count", + "5.0_um_count", + "10.0_um_count" + ], + "location_types": ["outside", "inside"], + "data": [ + [ + 123456, + "Test Sensor", + 0, + "PA-II", + "2.0+BME280+PMSX003-B+PMSX003-A", + "7.02", + -69, + 13788, + 51.5285582, + -0.2416796, + 569, + 13, + 82, + 1000.74, + null, + 0.0, + 0.0, + 0.0, + 76, + 68, + 0, + 0, + 0, + 0 + ], + [ + 567890, + "Test Sensor 2", + 0, + "PA-II", + "2.0+BME280+PMSX003-B+PMSX003-A", + "7.02", + -69, + 13788, + 51.5285582, + -0.2416796, + 569, + 13, + 82, + 1000.74, + null, + 0.0, + 0.0, + 0.0, + 76, + 68, + 0, + 0, + 0, + 0 + ] + ] +} diff --git a/tests/components/purpleair/test_config_flow.py b/tests/components/purpleair/test_config_flow.py new file mode 100644 index 00000000000..2f4af57a3c5 --- /dev/null +++ b/tests/components/purpleair/test_config_flow.py @@ -0,0 +1,270 @@ +"""Define tests for the PurpleAir config flow.""" +from unittest.mock import AsyncMock, patch + +from aiopurpleair.errors import InvalidApiKeyError, PurpleAirError +import pytest + +from homeassistant import data_entry_flow +from homeassistant.components.purpleair import DOMAIN +from homeassistant.config_entries import SOURCE_REAUTH, SOURCE_USER +from homeassistant.helpers import device_registry as dr + + +async def test_duplicate_error(hass, config_entry, setup_purpleair): + """Test that the proper error is shown when adding a duplicate config entry.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_USER}, data={"api_key": "abcde12345"} + ) + assert result["type"] == data_entry_flow.FlowResultType.ABORT + assert result["reason"] == "already_configured" + + +@pytest.mark.parametrize( + "check_api_key_mock,check_api_key_errors", + [ + (AsyncMock(side_effect=Exception), {"base": "unknown"}), + (AsyncMock(side_effect=InvalidApiKeyError), {"base": "invalid_api_key"}), + (AsyncMock(side_effect=PurpleAirError), {"base": "unknown"}), + ], +) +@pytest.mark.parametrize( + "get_nearby_sensors_mock,get_nearby_sensors_errors", + [ + (AsyncMock(return_value=[]), {"base": "no_sensors_near_coordinates"}), + (AsyncMock(side_effect=Exception), {"base": "unknown"}), + (AsyncMock(side_effect=PurpleAirError), {"base": "unknown"}), + ], +) +async def test_create_entry_by_coordinates( + hass, + api, + check_api_key_errors, + check_api_key_mock, + get_nearby_sensors_errors, + get_nearby_sensors_mock, + setup_purpleair, +): + """Test creating an entry by entering a latitude/longitude (including errors).""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_USER} + ) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "user" + + # Test errors that can arise when checking the API key: + with patch.object(api, "async_check_api_key", check_api_key_mock): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={"api_key": "abcde12345"} + ) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["errors"] == check_api_key_errors + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={"api_key": "abcde12345"} + ) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "by_coordinates" + + # Test errors that can arise when searching for nearby sensors: + with patch.object(api.sensors, "async_get_nearby_sensors", get_nearby_sensors_mock): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={ + "latitude": 51.5285582, + "longitude": -0.2416796, + "distance": 5, + }, + ) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["errors"] == get_nearby_sensors_errors + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={ + "latitude": 51.5285582, + "longitude": -0.2416796, + "distance": 5, + }, + ) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "choose_sensor" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={ + "sensor_index": "123456", + }, + ) + assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY + assert result["title"] == "abcde" + assert result["data"] == { + "api_key": "abcde12345", + } + assert result["options"] == { + "sensor_indices": [123456], + } + + +@pytest.mark.parametrize( + "check_api_key_mock,check_api_key_errors", + [ + (AsyncMock(side_effect=Exception), {"base": "unknown"}), + (AsyncMock(side_effect=InvalidApiKeyError), {"base": "invalid_api_key"}), + (AsyncMock(side_effect=PurpleAirError), {"base": "unknown"}), + ], +) +async def test_reauth( + hass, api, check_api_key_errors, check_api_key_mock, config_entry, setup_purpleair +): + """Test re-auth (including errors).""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={ + "source": SOURCE_REAUTH, + "entry_id": config_entry.entry_id, + "unique_id": config_entry.unique_id, + }, + data={"api_key": "abcde12345"}, + ) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "reauth_confirm" + + # Test errors that can arise when checking the API key: + with patch.object(api, "async_check_api_key", check_api_key_mock): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={"api_key": "new_api_key"} + ) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["errors"] == check_api_key_errors + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={"api_key": "new_api_key"}, + ) + assert result["type"] == data_entry_flow.FlowResultType.ABORT + assert result["reason"] == "reauth_successful" + assert len(hass.config_entries.async_entries()) == 1 + + +@pytest.mark.parametrize( + "get_nearby_sensors_mock,get_nearby_sensors_errors", + [ + (AsyncMock(return_value=[]), {"base": "no_sensors_near_coordinates"}), + (AsyncMock(side_effect=Exception), {"base": "unknown"}), + (AsyncMock(side_effect=PurpleAirError), {"base": "unknown"}), + ], +) +async def test_options_add_sensor( + hass, + api, + config_entry, + get_nearby_sensors_errors, + get_nearby_sensors_mock, + setup_purpleair, +): + """Test adding a sensor via the options flow (including errors).""" + result = await hass.config_entries.options.async_init(config_entry.entry_id) + assert result["type"] == data_entry_flow.FlowResultType.MENU + assert result["step_id"] == "init" + + result = await hass.config_entries.options.async_configure( + result["flow_id"], user_input={"next_step_id": "add_sensor"} + ) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "add_sensor" + + # Test errors that can arise when searching for nearby sensors: + with patch.object(api.sensors, "async_get_nearby_sensors", get_nearby_sensors_mock): + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + "latitude": 51.5285582, + "longitude": -0.2416796, + "distance": 5, + }, + ) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "add_sensor" + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + "latitude": 51.5285582, + "longitude": -0.2416796, + "distance": 5, + }, + ) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "choose_sensor" + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + "sensor_index": "567890", + }, + ) + assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY + assert result["data"] == { + "sensor_indices": [123456, 567890], + } + + assert config_entry.options["sensor_indices"] == [123456, 567890] + + +async def test_options_add_sensor_duplicate(hass, config_entry, setup_purpleair): + """Test adding a duplicate sensor via the options flow.""" + result = await hass.config_entries.options.async_init(config_entry.entry_id) + assert result["type"] == data_entry_flow.FlowResultType.MENU + assert result["step_id"] == "init" + + result = await hass.config_entries.options.async_configure( + result["flow_id"], user_input={"next_step_id": "add_sensor"} + ) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "add_sensor" + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + "latitude": 51.5285582, + "longitude": -0.2416796, + "distance": 5, + }, + ) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "choose_sensor" + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + "sensor_index": "123456", + }, + ) + assert result["type"] == data_entry_flow.FlowResultType.ABORT + assert result["reason"] == "already_configured" + + +async def test_options_remove_sensor(hass, config_entry, setup_purpleair): + """Test removing a sensor via the options flow.""" + result = await hass.config_entries.options.async_init(config_entry.entry_id) + assert result["type"] == data_entry_flow.FlowResultType.MENU + assert result["step_id"] == "init" + + result = await hass.config_entries.options.async_configure( + result["flow_id"], user_input={"next_step_id": "remove_sensor"} + ) + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "remove_sensor" + + device_registry = dr.async_get(hass) + device_entry = device_registry.async_get_device({(DOMAIN, "123456")}) + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={"sensor_device_id": device_entry.id}, + ) + assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY + assert result["data"] == { + "sensor_indices": [], + } + + assert config_entry.options["sensor_indices"] == [] diff --git a/tests/components/purpleair/test_diagnostics.py b/tests/components/purpleair/test_diagnostics.py new file mode 100644 index 00000000000..ee17a2889b8 --- /dev/null +++ b/tests/components/purpleair/test_diagnostics.py @@ -0,0 +1,332 @@ +"""Test PurpleAir diagnostics.""" +from homeassistant.components.diagnostics import REDACTED + +from tests.components.diagnostics import get_diagnostics_for_config_entry + + +async def test_entry_diagnostics(hass, config_entry, hass_client, setup_purpleair): + """Test config entry diagnostics.""" + assert await get_diagnostics_for_config_entry(hass, hass_client, config_entry) == { + "entry": { + "entry_id": config_entry.entry_id, + "version": 1, + "domain": "purpleair", + "title": REDACTED, + "data": { + "api_key": REDACTED, + }, + "options": { + "sensor_indices": [ + 123456, + ], + }, + "pref_disable_new_entities": False, + "pref_disable_polling": False, + "source": "user", + "unique_id": REDACTED, + "disabled_by": None, + }, + "data": { + "fields": [ + "sensor_index", + "name", + "location_type", + "model", + "hardware", + "firmware_version", + "rssi", + "uptime", + "latitude", + "longitude", + "altitude", + "humidity", + "temperature", + "pressure", + "voc", + "pm1.0", + "pm2.5", + "pm10.0", + "0.3_um_count", + "0.5_um_count", + "1.0_um_count", + "2.5_um_count", + "5.0_um_count", + "10.0_um_count", + ], + "data": { + "123456": { + "sensor_index": 123456, + "altitude": 569.0, + "analog_input": None, + "channel_flags": None, + "channel_flags_auto": None, + "channel_flags_manual": None, + "channel_state": None, + "confidence": None, + "confidence_auto": None, + "confidence_manual": None, + "date_created_utc": None, + "deciviews": None, + "deciviews_a": None, + "deciviews_b": None, + "firmware_upgrade": None, + "firmware_version": "7.02", + "hardware": "2.0+BME280+PMSX003-B+PMSX003-A", + "humidity": 13.0, + "humidity_a": None, + "humidity_b": None, + "icon": None, + "is_owner": None, + "last_modified_utc": None, + "last_seen_utc": None, + "latitude": REDACTED, + "led_brightness": None, + "location_type": { + "__type": "", + "repr": "", + }, + "longitude": REDACTED, + "memory": None, + "model": "PA-II", + "name": "Test Sensor", + "ozone1": None, + "pa_latency": None, + "pm0_3_um_count": 76.0, + "pm0_3_um_count_a": None, + "pm0_3_um_count_b": None, + "pm0_5_um_count": 68.0, + "pm0_5_um_count_a": None, + "pm0_5_um_count_b": None, + "pm10_0": 0.0, + "pm10_0_a": None, + "pm10_0_atm": None, + "pm10_0_atm_a": None, + "pm10_0_atm_b": None, + "pm10_0_b": None, + "pm10_0_cf_1": None, + "pm10_0_cf_1_a": None, + "pm10_0_cf_1_b": None, + "pm10_0_um_count": 0.0, + "pm10_0_um_count_a": None, + "pm10_0_um_count_b": None, + "pm1_0": 0.0, + "pm1_0_a": None, + "pm1_0_atm": None, + "pm1_0_atm_a": None, + "pm1_0_atm_b": None, + "pm1_0_b": None, + "pm1_0_cf_1": None, + "pm1_0_cf_1_a": None, + "pm1_0_cf_1_b": None, + "pm1_0_um_count": 0.0, + "pm1_0_um_count_a": None, + "pm1_0_um_count_b": None, + "pm2_5": 0.0, + "pm2_5_10minute": None, + "pm2_5_10minute_a": None, + "pm2_5_10minute_b": None, + "pm2_5_1week": None, + "pm2_5_1week_a": None, + "pm2_5_1week_b": None, + "pm2_5_24hour": None, + "pm2_5_24hour_a": None, + "pm2_5_24hour_b": None, + "pm2_5_30minute": None, + "pm2_5_30minute_a": None, + "pm2_5_30minute_b": None, + "pm2_5_60minute": None, + "pm2_5_60minute_a": None, + "pm2_5_60minute_b": None, + "pm2_5_6hour": None, + "pm2_5_6hour_a": None, + "pm2_5_6hour_b": None, + "pm2_5_a": None, + "pm2_5_alt": None, + "pm2_5_alt_a": None, + "pm2_5_alt_b": None, + "pm2_5_atm": None, + "pm2_5_atm_a": None, + "pm2_5_atm_b": None, + "pm2_5_b": None, + "pm2_5_cf_1": None, + "pm2_5_cf_1_a": None, + "pm2_5_cf_1_b": None, + "pm2_5_um_count": 0.0, + "pm2_5_um_count_a": None, + "pm2_5_um_count_b": None, + "pm5_0_um_count": 0.0, + "pm5_0_um_count_a": None, + "pm5_0_um_count_b": None, + "position_rating": None, + "pressure": 1000.74, + "pressure_a": None, + "pressure_b": None, + "primary_id_a": None, + "primary_id_b": None, + "primary_key_a": None, + "primary_key_b": None, + "private": None, + "rssi": -69, + "scattering_coefficient": None, + "scattering_coefficient_a": None, + "scattering_coefficient_b": None, + "secondary_id_a": None, + "secondary_id_b": None, + "secondary_key_a": None, + "secondary_key_b": None, + "stats": None, + "stats_a": None, + "stats_b": None, + "temperature": 82.0, + "temperature_a": None, + "temperature_b": None, + "uptime": 13788, + "visual_range": None, + "visual_range_a": None, + "visual_range_b": None, + "voc": None, + "voc_a": None, + "voc_b": None, + }, + "567890": { + "sensor_index": 567890, + "altitude": 569.0, + "analog_input": None, + "channel_flags": None, + "channel_flags_auto": None, + "channel_flags_manual": None, + "channel_state": None, + "confidence": None, + "confidence_auto": None, + "confidence_manual": None, + "date_created_utc": None, + "deciviews": None, + "deciviews_a": None, + "deciviews_b": None, + "firmware_upgrade": None, + "firmware_version": "7.02", + "hardware": "2.0+BME280+PMSX003-B+PMSX003-A", + "humidity": 13.0, + "humidity_a": None, + "humidity_b": None, + "icon": None, + "is_owner": None, + "last_modified_utc": None, + "last_seen_utc": None, + "latitude": REDACTED, + "led_brightness": None, + "location_type": { + "__type": "", + "repr": "", + }, + "longitude": REDACTED, + "memory": None, + "model": "PA-II", + "name": "Test Sensor 2", + "ozone1": None, + "pa_latency": None, + "pm0_3_um_count": 76.0, + "pm0_3_um_count_a": None, + "pm0_3_um_count_b": None, + "pm0_5_um_count": 68.0, + "pm0_5_um_count_a": None, + "pm0_5_um_count_b": None, + "pm10_0": 0.0, + "pm10_0_a": None, + "pm10_0_atm": None, + "pm10_0_atm_a": None, + "pm10_0_atm_b": None, + "pm10_0_b": None, + "pm10_0_cf_1": None, + "pm10_0_cf_1_a": None, + "pm10_0_cf_1_b": None, + "pm10_0_um_count": 0.0, + "pm10_0_um_count_a": None, + "pm10_0_um_count_b": None, + "pm1_0": 0.0, + "pm1_0_a": None, + "pm1_0_atm": None, + "pm1_0_atm_a": None, + "pm1_0_atm_b": None, + "pm1_0_b": None, + "pm1_0_cf_1": None, + "pm1_0_cf_1_a": None, + "pm1_0_cf_1_b": None, + "pm1_0_um_count": 0.0, + "pm1_0_um_count_a": None, + "pm1_0_um_count_b": None, + "pm2_5": 0.0, + "pm2_5_10minute": None, + "pm2_5_10minute_a": None, + "pm2_5_10minute_b": None, + "pm2_5_1week": None, + "pm2_5_1week_a": None, + "pm2_5_1week_b": None, + "pm2_5_24hour": None, + "pm2_5_24hour_a": None, + "pm2_5_24hour_b": None, + "pm2_5_30minute": None, + "pm2_5_30minute_a": None, + "pm2_5_30minute_b": None, + "pm2_5_60minute": None, + "pm2_5_60minute_a": None, + "pm2_5_60minute_b": None, + "pm2_5_6hour": None, + "pm2_5_6hour_a": None, + "pm2_5_6hour_b": None, + "pm2_5_a": None, + "pm2_5_alt": None, + "pm2_5_alt_a": None, + "pm2_5_alt_b": None, + "pm2_5_atm": None, + "pm2_5_atm_a": None, + "pm2_5_atm_b": None, + "pm2_5_b": None, + "pm2_5_cf_1": None, + "pm2_5_cf_1_a": None, + "pm2_5_cf_1_b": None, + "pm2_5_um_count": 0.0, + "pm2_5_um_count_a": None, + "pm2_5_um_count_b": None, + "pm5_0_um_count": 0.0, + "pm5_0_um_count_a": None, + "pm5_0_um_count_b": None, + "position_rating": None, + "pressure": 1000.74, + "pressure_a": None, + "pressure_b": None, + "primary_id_a": None, + "primary_id_b": None, + "primary_key_a": None, + "primary_key_b": None, + "private": None, + "rssi": -69, + "scattering_coefficient": None, + "scattering_coefficient_a": None, + "scattering_coefficient_b": None, + "secondary_id_a": None, + "secondary_id_b": None, + "secondary_key_a": None, + "secondary_key_b": None, + "stats": None, + "stats_a": None, + "stats_b": None, + "temperature": 82.0, + "temperature_a": None, + "temperature_b": None, + "uptime": 13788, + "visual_range": None, + "visual_range_a": None, + "visual_range_b": None, + "voc": None, + "voc_a": None, + "voc_b": None, + }, + }, + "api_version": "V1.0.11-0.0.41", + "firmware_default_version": "7.02", + "max_age": 604800, + "data_timestamp_utc": "2022-11-20T23:10:00", + "timestamp_utc": "2022-11-20T23:10:17", + }, + } diff --git a/tests/components/rainbird/__init__.py b/tests/components/rainbird/__init__.py new file mode 100644 index 00000000000..f2d3f91e098 --- /dev/null +++ b/tests/components/rainbird/__init__.py @@ -0,0 +1 @@ +"""Tests for the rainbird integration.""" diff --git a/tests/components/rainbird/conftest.py b/tests/components/rainbird/conftest.py new file mode 100644 index 00000000000..660307f1c60 --- /dev/null +++ b/tests/components/rainbird/conftest.py @@ -0,0 +1,116 @@ +"""Test fixtures for rainbird.""" + +from __future__ import annotations + +from collections.abc import Awaitable, Callable, Generator +from typing import Any +from unittest.mock import patch + +from pyrainbird import encryption +import pytest + +from homeassistant.components.rainbird import DOMAIN +from homeassistant.const import Platform +from homeassistant.core import HomeAssistant +from homeassistant.setup import async_setup_component + +from tests.test_util.aiohttp import AiohttpClientMocker, AiohttpClientMockResponse + +ComponentSetup = Callable[[], Awaitable[bool]] + +HOST = "example.com" +URL = "http://example.com/stick" +PASSWORD = "password" + +# +# Response payloads below come from pyrainbird test cases. +# + +# Get serial number Command 0x85. Serial is 0x12635436566 +SERIAL_RESPONSE = "850000012635436566" +# Get available stations command 0x83 +AVAILABLE_STATIONS_RESPONSE = "83017F000000" # Mask for 7 zones +EMPTY_STATIONS_RESPONSE = "830000000000" +# Get zone state command 0xBF. +ZONE_3_ON_RESPONSE = "BF0004000000" # Zone 3 is on +ZONE_5_ON_RESPONSE = "BF0010000000" # Zone 5 is on +ZONE_OFF_RESPONSE = "BF0000000000" # All zones off +ZONE_STATE_OFF_RESPONSE = "BF0000000000" +# Get rain sensor state command 0XBE +RAIN_SENSOR_OFF = "BE00" +RAIN_SENSOR_ON = "BE01" +# Get rain delay command 0xB6 +RAIN_DELAY = "B60010" # 0x10 is 16 +RAIN_DELAY_OFF = "B60000" +# ACK command 0x10, Echo 0x06 +ACK_ECHO = "0106" + +CONFIG = { + DOMAIN: { + "host": HOST, + "password": PASSWORD, + "trigger_time": 360, + } +} + + +@pytest.fixture +def platforms() -> list[Platform]: + """Fixture to specify platforms to test.""" + return [] + + +@pytest.fixture +def yaml_config() -> dict[str, Any]: + """Fixture for configuration.yaml.""" + return CONFIG + + +@pytest.fixture +async def setup_integration( + hass: HomeAssistant, + platforms: list[str], + yaml_config: dict[str, Any], +) -> Generator[ComponentSetup, None, None]: + """Fixture for setting up the component.""" + + with patch(f"homeassistant.components.{DOMAIN}.PLATFORMS", platforms): + + async def func() -> bool: + result = await async_setup_component(hass, DOMAIN, yaml_config) + await hass.async_block_till_done() + return result + + yield func + + +def rainbird_response(data: str) -> bytes: + """Create a fake API response.""" + return encryption.encrypt( + '{"jsonrpc": "2.0", "result": {"data":"%s"}, "id": 1} ' % data, + PASSWORD, + ) + + +def mock_response(data: str) -> AiohttpClientMockResponse: + """Create a fake AiohttpClientMockResponse.""" + return AiohttpClientMockResponse("POST", URL, response=rainbird_response(data)) + + +@pytest.fixture(name="responses") +def mock_responses() -> list[AiohttpClientMockResponse]: + """Fixture to set up a list of fake API responsees for tests to extend.""" + return [mock_response(SERIAL_RESPONSE)] + + +@pytest.fixture(autouse=True) +def handle_responses( + aioclient_mock: AiohttpClientMocker, + responses: list[AiohttpClientMockResponse], +) -> None: + """Fixture for command mocking for fake responses to the API url.""" + + async def handle(method, url, data) -> AiohttpClientMockResponse: + return responses.pop(0) + + aioclient_mock.post(URL, side_effect=handle) diff --git a/tests/components/rainbird/test_binary_sensor.py b/tests/components/rainbird/test_binary_sensor.py new file mode 100644 index 00000000000..7ed6f2d1a29 --- /dev/null +++ b/tests/components/rainbird/test_binary_sensor.py @@ -0,0 +1,78 @@ +"""Tests for rainbird sensor platform.""" + + +import pytest + +from homeassistant.const import Platform +from homeassistant.core import HomeAssistant + +from .conftest import ( + RAIN_DELAY, + RAIN_DELAY_OFF, + RAIN_SENSOR_OFF, + RAIN_SENSOR_ON, + ComponentSetup, + mock_response, +) + +from tests.test_util.aiohttp import AiohttpClientMockResponse + + +@pytest.fixture +def platforms() -> list[Platform]: + """Fixture to specify platforms to test.""" + return [Platform.BINARY_SENSOR] + + +@pytest.mark.parametrize( + "sensor_payload,expected_state", + [(RAIN_SENSOR_OFF, "off"), (RAIN_SENSOR_ON, "on")], +) +async def test_rainsensor( + hass: HomeAssistant, + setup_integration: ComponentSetup, + responses: list[AiohttpClientMockResponse], + sensor_payload: str, + expected_state: bool, +) -> None: + """Test rainsensor binary sensor.""" + + responses.extend( + [ + mock_response(sensor_payload), + mock_response(RAIN_DELAY), + ] + ) + + assert await setup_integration() + + rainsensor = hass.states.get("binary_sensor.rainsensor") + assert rainsensor is not None + assert rainsensor.state == expected_state + + +@pytest.mark.parametrize( + "sensor_payload,expected_state", + [(RAIN_DELAY_OFF, "off"), (RAIN_DELAY, "on")], +) +async def test_raindelay( + hass: HomeAssistant, + setup_integration: ComponentSetup, + responses: list[AiohttpClientMockResponse], + sensor_payload: str, + expected_state: bool, +) -> None: + """Test raindelay binary sensor.""" + + responses.extend( + [ + mock_response(RAIN_SENSOR_OFF), + mock_response(sensor_payload), + ] + ) + + assert await setup_integration() + + raindelay = hass.states.get("binary_sensor.raindelay") + assert raindelay is not None + assert raindelay.state == expected_state diff --git a/tests/components/rainbird/test_init.py b/tests/components/rainbird/test_init.py new file mode 100644 index 00000000000..acf6a92d4a5 --- /dev/null +++ b/tests/components/rainbird/test_init.py @@ -0,0 +1,34 @@ +"""Tests for rainbird initialization.""" + +from http import HTTPStatus + +from homeassistant.core import HomeAssistant + +from .conftest import URL, ComponentSetup + +from tests.test_util.aiohttp import AiohttpClientMocker, AiohttpClientMockResponse + + +async def test_setup_success( + hass: HomeAssistant, + setup_integration: ComponentSetup, +) -> None: + """Test successful setup and unload.""" + + assert await setup_integration() + + +async def test_setup_communication_failure( + hass: HomeAssistant, + setup_integration: ComponentSetup, + responses: list[AiohttpClientMockResponse], + aioclient_mock: AiohttpClientMocker, +) -> None: + """Test unable to talk to server on startup, which permanently fails setup.""" + + responses.clear() + responses.append( + AiohttpClientMockResponse("POST", URL, status=HTTPStatus.SERVICE_UNAVAILABLE) + ) + + assert not await setup_integration() diff --git a/tests/components/rainbird/test_sensor.py b/tests/components/rainbird/test_sensor.py new file mode 100644 index 00000000000..b80e014b236 --- /dev/null +++ b/tests/components/rainbird/test_sensor.py @@ -0,0 +1,49 @@ +"""Tests for rainbird sensor platform.""" + + +import pytest + +from homeassistant.const import Platform +from homeassistant.core import HomeAssistant + +from .conftest import ( + RAIN_DELAY, + RAIN_SENSOR_OFF, + RAIN_SENSOR_ON, + ComponentSetup, + mock_response, +) + +from tests.test_util.aiohttp import AiohttpClientMockResponse + + +@pytest.fixture +def platforms() -> list[str]: + """Fixture to specify platforms to test.""" + return [Platform.SENSOR] + + +@pytest.mark.parametrize( + "sensor_payload,expected_state", + [(RAIN_SENSOR_OFF, "False"), (RAIN_SENSOR_ON, "True")], +) +async def test_sensors( + hass: HomeAssistant, + setup_integration: ComponentSetup, + responses: list[AiohttpClientMockResponse], + sensor_payload: str, + expected_state: bool, +) -> None: + """Test sensor platform.""" + + responses.extend([mock_response(sensor_payload), mock_response(RAIN_DELAY)]) + + assert await setup_integration() + + rainsensor = hass.states.get("sensor.rainsensor") + assert rainsensor is not None + assert rainsensor.state == expected_state + + raindelay = hass.states.get("sensor.raindelay") + assert raindelay is not None + assert raindelay.state == "16" diff --git a/tests/components/rainbird/test_switch.py b/tests/components/rainbird/test_switch.py new file mode 100644 index 00000000000..d6e89c58527 --- /dev/null +++ b/tests/components/rainbird/test_switch.py @@ -0,0 +1,301 @@ +"""Tests for rainbird sensor platform.""" + + +from http import HTTPStatus +import logging + +import pytest + +from homeassistant.components.rainbird import DOMAIN +from homeassistant.const import ATTR_ENTITY_ID, Platform +from homeassistant.core import HomeAssistant + +from .conftest import ( + ACK_ECHO, + AVAILABLE_STATIONS_RESPONSE, + EMPTY_STATIONS_RESPONSE, + HOST, + PASSWORD, + URL, + ZONE_3_ON_RESPONSE, + ZONE_5_ON_RESPONSE, + ZONE_OFF_RESPONSE, + ComponentSetup, + mock_response, +) + +from tests.components.switch import common as switch_common +from tests.test_util.aiohttp import AiohttpClientMocker, AiohttpClientMockResponse + + +@pytest.fixture +def platforms() -> list[str]: + """Fixture to specify platforms to test.""" + return [Platform.SWITCH] + + +async def test_no_zones( + hass: HomeAssistant, + setup_integration: ComponentSetup, + responses: list[AiohttpClientMockResponse], +) -> None: + """Test case where listing stations returns no stations.""" + + responses.append(mock_response(EMPTY_STATIONS_RESPONSE)) + assert await setup_integration() + + zone = hass.states.get("switch.sprinkler_1") + assert zone is None + + +async def test_zones( + hass: HomeAssistant, + setup_integration: ComponentSetup, + responses: list[AiohttpClientMockResponse], +) -> None: + """Test switch platform with fake data that creates 7 zones with one enabled.""" + + responses.extend( + [mock_response(AVAILABLE_STATIONS_RESPONSE), mock_response(ZONE_5_ON_RESPONSE)] + ) + + assert await setup_integration() + + zone = hass.states.get("switch.sprinkler_1") + assert zone is not None + assert zone.state == "off" + + zone = hass.states.get("switch.sprinkler_2") + assert zone is not None + assert zone.state == "off" + + zone = hass.states.get("switch.sprinkler_3") + assert zone is not None + assert zone.state == "off" + + zone = hass.states.get("switch.sprinkler_4") + assert zone is not None + assert zone.state == "off" + + zone = hass.states.get("switch.sprinkler_5") + assert zone is not None + assert zone.state == "on" + + zone = hass.states.get("switch.sprinkler_6") + assert zone is not None + assert zone.state == "off" + + zone = hass.states.get("switch.sprinkler_7") + assert zone is not None + assert zone.state == "off" + + assert not hass.states.get("switch.sprinkler_8") + + +async def test_switch_on( + hass: HomeAssistant, + setup_integration: ComponentSetup, + aioclient_mock: AiohttpClientMocker, + responses: list[AiohttpClientMockResponse], +) -> None: + """Test turning on irrigation switch.""" + + responses.extend( + [mock_response(AVAILABLE_STATIONS_RESPONSE), mock_response(ZONE_OFF_RESPONSE)] + ) + assert await setup_integration() + + # Initially all zones are off. Pick zone3 as an arbitrary to assert + # state, then update below as a switch. + zone = hass.states.get("switch.sprinkler_3") + assert zone is not None + assert zone.state == "off" + + aioclient_mock.mock_calls.clear() + responses.extend( + [ + mock_response(ACK_ECHO), # Switch on response + mock_response(ZONE_3_ON_RESPONSE), # Updated zone state + ] + ) + await switch_common.async_turn_on(hass, "switch.sprinkler_3") + await hass.async_block_till_done() + assert len(aioclient_mock.mock_calls) == 2 + aioclient_mock.mock_calls.clear() + + # Verify switch state is updated + zone = hass.states.get("switch.sprinkler_3") + assert zone is not None + assert zone.state == "on" + + +async def test_switch_off( + hass: HomeAssistant, + setup_integration: ComponentSetup, + aioclient_mock: AiohttpClientMocker, + responses: list[AiohttpClientMockResponse], +) -> None: + """Test turning off irrigation switch.""" + + responses.extend( + [mock_response(AVAILABLE_STATIONS_RESPONSE), mock_response(ZONE_3_ON_RESPONSE)] + ) + assert await setup_integration() + + # Initially the test zone is on + zone = hass.states.get("switch.sprinkler_3") + assert zone is not None + assert zone.state == "on" + + aioclient_mock.mock_calls.clear() + responses.extend( + [ + mock_response(ACK_ECHO), # Switch off response + mock_response(ZONE_OFF_RESPONSE), # Updated zone state + ] + ) + await switch_common.async_turn_off(hass, "switch.sprinkler_3") + await hass.async_block_till_done() + + # One call to change the service and one to refresh state + assert len(aioclient_mock.mock_calls) == 2 + + # Verify switch state is updated + zone = hass.states.get("switch.sprinkler_3") + assert zone is not None + assert zone.state == "off" + + +async def test_irrigation_service( + hass: HomeAssistant, + setup_integration: ComponentSetup, + aioclient_mock: AiohttpClientMocker, + responses: list[AiohttpClientMockResponse], +) -> None: + """Test calling the irrigation service.""" + + responses.extend( + [mock_response(AVAILABLE_STATIONS_RESPONSE), mock_response(ZONE_3_ON_RESPONSE)] + ) + assert await setup_integration() + + aioclient_mock.mock_calls.clear() + responses.extend([mock_response(ACK_ECHO), mock_response(ZONE_OFF_RESPONSE)]) + + await hass.services.async_call( + DOMAIN, + "start_irrigation", + {ATTR_ENTITY_ID: "switch.sprinkler_5", "duration": 30}, + blocking=True, + ) + + # One call to change the service and one to refresh state + assert len(aioclient_mock.mock_calls) == 2 + + +async def test_rain_delay_service( + hass: HomeAssistant, + setup_integration: ComponentSetup, + aioclient_mock: AiohttpClientMocker, + responses: list[AiohttpClientMockResponse], +) -> None: + """Test calling the rain delay service.""" + + responses.extend( + [mock_response(AVAILABLE_STATIONS_RESPONSE), mock_response(ZONE_3_ON_RESPONSE)] + ) + assert await setup_integration() + + aioclient_mock.mock_calls.clear() + responses.extend( + [ + mock_response(ACK_ECHO), + ] + ) + + await hass.services.async_call( + DOMAIN, "set_rain_delay", {"duration": 30}, blocking=True + ) + + assert len(aioclient_mock.mock_calls) == 1 + + +async def test_platform_unavailable( + hass: HomeAssistant, + setup_integration: ComponentSetup, + responses: list[AiohttpClientMockResponse], + caplog: pytest.LogCaptureFixture, +) -> None: + """Test failure while listing the stations when setting up the platform.""" + + responses.append( + AiohttpClientMockResponse("POST", URL, status=HTTPStatus.SERVICE_UNAVAILABLE) + ) + + with caplog.at_level(logging.WARNING): + assert await setup_integration() + + assert "Failed to get stations" in caplog.text + + +async def test_coordinator_unavailable( + hass: HomeAssistant, + setup_integration: ComponentSetup, + responses: list[AiohttpClientMockResponse], + caplog: pytest.LogCaptureFixture, +) -> None: + """Test failure to refresh the update coordinator.""" + + responses.extend( + [ + mock_response(AVAILABLE_STATIONS_RESPONSE), + AiohttpClientMockResponse( + "POST", URL, status=HTTPStatus.SERVICE_UNAVAILABLE + ), + ], + ) + + with caplog.at_level(logging.WARNING): + assert await setup_integration() + + assert "Failed to load zone state" in caplog.text + + +@pytest.mark.parametrize( + "yaml_config", + [ + { + DOMAIN: { + "host": HOST, + "password": PASSWORD, + "trigger_time": 360, + "zones": { + 1: { + "friendly_name": "Garden Sprinkler", + }, + 2: { + "friendly_name": "Back Yard", + }, + }, + } + }, + ], +) +async def test_yaml_config( + hass: HomeAssistant, + setup_integration: ComponentSetup, + responses: list[AiohttpClientMockResponse], +) -> None: + """Test switch platform with fake data that creates 7 zones with one enabled.""" + + responses.extend( + [mock_response(AVAILABLE_STATIONS_RESPONSE), mock_response(ZONE_5_ON_RESPONSE)] + ) + + assert await setup_integration() + + assert hass.states.get("switch.garden_sprinkler") + assert not hass.states.get("switch.sprinkler_1") + assert hass.states.get("switch.back_yard") + assert not hass.states.get("switch.sprinkler_2") + assert hass.states.get("switch.sprinkler_3") diff --git a/tests/components/renault/const.py b/tests/components/renault/const.py index 97499d19bea..96cdeed2493 100644 --- a/tests/components/renault/const.py +++ b/tests/components/renault/const.py @@ -3,9 +3,6 @@ from homeassistant.components.binary_sensor import BinarySensorDeviceClass from homeassistant.components.renault.const import ( CONF_KAMEREON_ACCOUNT_ID, CONF_LOCALE, - DEVICE_CLASS_CHARGE_MODE, - DEVICE_CLASS_CHARGE_STATE, - DEVICE_CLASS_PLUG_STATE, DOMAIN, ) from homeassistant.components.select import ATTR_OPTIONS @@ -121,7 +118,6 @@ MOCK_VEHICLES = { Platform.DEVICE_TRACKER: [], Platform.SELECT: [ { - ATTR_DEVICE_CLASS: DEVICE_CLASS_CHARGE_MODE, ATTR_ENTITY_ID: "select.reg_number_charge_mode", ATTR_ICON: "mdi:calendar-remove", ATTR_OPTIONS: ["always", "always_charging", "schedule_mode"], @@ -171,9 +167,19 @@ MOCK_VEHICLES = { ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS, }, { - ATTR_DEVICE_CLASS: DEVICE_CLASS_CHARGE_STATE, + ATTR_DEVICE_CLASS: SensorDeviceClass.ENUM, ATTR_ENTITY_ID: "sensor.reg_number_charge_state", ATTR_ICON: "mdi:flash", + ATTR_OPTIONS: [ + "not_in_charge", + "waiting_for_a_planned_charge", + "charge_ended", + "waiting_for_current_charge", + "energy_flap_opened", + "charge_in_progress", + "charge_error", + "unavailable", + ], ATTR_STATE: "charge_in_progress", ATTR_UNIQUE_ID: "vf1aaaaa555777999_charge_state", }, @@ -224,9 +230,10 @@ MOCK_VEHICLES = { ATTR_UNIQUE_ID: "vf1aaaaa555777999_hvac_last_activity", }, { - ATTR_DEVICE_CLASS: DEVICE_CLASS_PLUG_STATE, + ATTR_DEVICE_CLASS: SensorDeviceClass.ENUM, ATTR_ENTITY_ID: "sensor.reg_number_plug_state", ATTR_ICON: "mdi:power-plug", + ATTR_OPTIONS: ["unplugged", "plugged", "plug_error", "plug_unknown"], ATTR_STATE: "plugged", ATTR_UNIQUE_ID: "vf1aaaaa555777999_plug_state", }, @@ -340,7 +347,6 @@ MOCK_VEHICLES = { ], Platform.SELECT: [ { - ATTR_DEVICE_CLASS: DEVICE_CLASS_CHARGE_MODE, ATTR_ENTITY_ID: "select.reg_number_charge_mode", ATTR_ICON: "mdi:calendar-clock", ATTR_OPTIONS: ["always", "always_charging", "schedule_mode"], @@ -390,9 +396,19 @@ MOCK_VEHICLES = { ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS, }, { - ATTR_DEVICE_CLASS: DEVICE_CLASS_CHARGE_STATE, + ATTR_DEVICE_CLASS: SensorDeviceClass.ENUM, ATTR_ENTITY_ID: "sensor.reg_number_charge_state", ATTR_ICON: "mdi:flash-off", + ATTR_OPTIONS: [ + "not_in_charge", + "waiting_for_a_planned_charge", + "charge_ended", + "waiting_for_current_charge", + "energy_flap_opened", + "charge_in_progress", + "charge_error", + "unavailable", + ], ATTR_STATE: "charge_error", ATTR_UNIQUE_ID: "vf1aaaaa555777999_charge_state", }, @@ -443,9 +459,10 @@ MOCK_VEHICLES = { ATTR_UNIQUE_ID: "vf1aaaaa555777999_hvac_last_activity", }, { - ATTR_DEVICE_CLASS: DEVICE_CLASS_PLUG_STATE, + ATTR_DEVICE_CLASS: SensorDeviceClass.ENUM, ATTR_ENTITY_ID: "sensor.reg_number_plug_state", ATTR_ICON: "mdi:power-plug-off", + ATTR_OPTIONS: ["unplugged", "plugged", "plug_error", "plug_unknown"], ATTR_STATE: "unplugged", ATTR_UNIQUE_ID: "vf1aaaaa555777999_plug_state", }, @@ -559,7 +576,6 @@ MOCK_VEHICLES = { ], Platform.SELECT: [ { - ATTR_DEVICE_CLASS: DEVICE_CLASS_CHARGE_MODE, ATTR_ENTITY_ID: "select.reg_number_charge_mode", ATTR_ICON: "mdi:calendar-remove", ATTR_OPTIONS: ["always", "always_charging", "schedule_mode"], @@ -609,9 +625,19 @@ MOCK_VEHICLES = { ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS, }, { - ATTR_DEVICE_CLASS: DEVICE_CLASS_CHARGE_STATE, + ATTR_DEVICE_CLASS: SensorDeviceClass.ENUM, ATTR_ENTITY_ID: "sensor.reg_number_charge_state", ATTR_ICON: "mdi:flash", + ATTR_OPTIONS: [ + "not_in_charge", + "waiting_for_a_planned_charge", + "charge_ended", + "waiting_for_current_charge", + "energy_flap_opened", + "charge_in_progress", + "charge_error", + "unavailable", + ], ATTR_STATE: "charge_in_progress", ATTR_UNIQUE_ID: "vf1aaaaa555777123_charge_state", }, @@ -659,9 +685,10 @@ MOCK_VEHICLES = { ATTR_UNIT_OF_MEASUREMENT: LENGTH_KILOMETERS, }, { - ATTR_DEVICE_CLASS: DEVICE_CLASS_PLUG_STATE, + ATTR_DEVICE_CLASS: SensorDeviceClass.ENUM, ATTR_ENTITY_ID: "sensor.reg_number_plug_state", ATTR_ICON: "mdi:power-plug", + ATTR_OPTIONS: ["unplugged", "plugged", "plug_error", "plug_unknown"], ATTR_STATE: "plugged", ATTR_UNIQUE_ID: "vf1aaaaa555777123_plug_state", }, diff --git a/tests/components/reolink/__init__.py b/tests/components/reolink/__init__.py new file mode 100644 index 00000000000..45bcb2fab8c --- /dev/null +++ b/tests/components/reolink/__init__.py @@ -0,0 +1 @@ +"""Tests for the Reolink integration.""" diff --git a/tests/components/reolink/test_config_flow.py b/tests/components/reolink/test_config_flow.py new file mode 100644 index 00000000000..b69fab9797f --- /dev/null +++ b/tests/components/reolink/test_config_flow.py @@ -0,0 +1,263 @@ +"""Test the Reolink config flow.""" +import json +from unittest.mock import AsyncMock, Mock, patch + +import pytest +from reolink_aio.exceptions import ApiError, CredentialsInvalidError + +from homeassistant import config_entries, data_entry_flow +from homeassistant.components.reolink import const +from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT, CONF_USERNAME +from homeassistant.helpers.device_registry import format_mac + +from tests.common import MockConfigEntry + +TEST_HOST = "1.2.3.4" +TEST_HOST2 = "4.5.6.7" +TEST_USERNAME = "admin" +TEST_USERNAME2 = "username" +TEST_PASSWORD = "password" +TEST_PASSWORD2 = "new_password" +TEST_MAC = "ab:cd:ef:gh:ij:kl" +TEST_PORT = 1234 +TEST_NVR_NAME = "test_reolink_name" +TEST_USE_HTTPS = True + + +def get_mock_info(error=None, host_data_return=True): + """Return a mock gateway info instance.""" + host_mock = Mock() + if error is None: + host_mock.get_host_data = AsyncMock(return_value=host_data_return) + else: + host_mock.get_host_data = AsyncMock(side_effect=error) + host_mock.unsubscribe_all = AsyncMock(return_value=True) + host_mock.logout = AsyncMock(return_value=True) + host_mock.mac_address = TEST_MAC + host_mock.onvif_enabled = True + host_mock.rtmp_enabled = True + host_mock.rtsp_enabled = True + host_mock.nvr_name = TEST_NVR_NAME + host_mock.port = TEST_PORT + host_mock.use_https = TEST_USE_HTTPS + return host_mock + + +@pytest.fixture(name="reolink_connect", autouse=True) +def reolink_connect_fixture(mock_get_source_ip): + """Mock reolink connection and entry setup.""" + with patch( + "homeassistant.components.reolink.async_setup_entry", return_value=True + ), patch( + "homeassistant.components.reolink.host.Host", return_value=get_mock_info() + ): + yield + + +async def test_config_flow_manual_success(hass): + """Successful flow manually initialized by the user.""" + result = await hass.config_entries.flow.async_init( + const.DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + assert result["type"] is data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "user" + assert result["errors"] == {} + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_USERNAME: TEST_USERNAME, + CONF_PASSWORD: TEST_PASSWORD, + CONF_HOST: TEST_HOST, + }, + ) + + assert result["type"] is data_entry_flow.FlowResultType.CREATE_ENTRY + assert result["title"] == TEST_NVR_NAME + assert result["data"] == { + CONF_HOST: TEST_HOST, + CONF_USERNAME: TEST_USERNAME, + CONF_PASSWORD: TEST_PASSWORD, + CONF_PORT: TEST_PORT, + const.CONF_USE_HTTPS: TEST_USE_HTTPS, + } + assert result["options"] == { + const.CONF_PROTOCOL: const.DEFAULT_PROTOCOL, + } + + +async def test_config_flow_errors(hass): + """Successful flow manually initialized by the user after some errors.""" + result = await hass.config_entries.flow.async_init( + const.DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + assert result["type"] is data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "user" + assert result["errors"] == {} + + host_mock = get_mock_info(host_data_return=False) + with patch("homeassistant.components.reolink.host.Host", return_value=host_mock): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_USERNAME: TEST_USERNAME, + CONF_PASSWORD: TEST_PASSWORD, + CONF_HOST: TEST_HOST, + }, + ) + + assert result["type"] is data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "user" + assert result["errors"] == {"host": "cannot_connect"} + + host_mock = get_mock_info(error=json.JSONDecodeError("test_error", "test", 1)) + with patch("homeassistant.components.reolink.host.Host", return_value=host_mock): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_USERNAME: TEST_USERNAME, + CONF_PASSWORD: TEST_PASSWORD, + CONF_HOST: TEST_HOST, + }, + ) + + assert result["type"] is data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "user" + assert result["errors"] == {"host": "unknown"} + + host_mock = get_mock_info(error=CredentialsInvalidError("Test error")) + with patch("homeassistant.components.reolink.host.Host", return_value=host_mock): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_USERNAME: TEST_USERNAME, + CONF_PASSWORD: TEST_PASSWORD, + CONF_HOST: TEST_HOST, + }, + ) + + assert result["type"] is data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "user" + assert result["errors"] == {"host": "invalid_auth"} + + host_mock = get_mock_info(error=ApiError("Test error")) + with patch("homeassistant.components.reolink.host.Host", return_value=host_mock): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_USERNAME: TEST_USERNAME, + CONF_PASSWORD: TEST_PASSWORD, + CONF_HOST: TEST_HOST, + }, + ) + + assert result["type"] is data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "user" + assert result["errors"] == {"host": "api_error"} + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_USERNAME: TEST_USERNAME, + CONF_PASSWORD: TEST_PASSWORD, + CONF_HOST: TEST_HOST, + CONF_PORT: TEST_PORT, + const.CONF_USE_HTTPS: TEST_USE_HTTPS, + }, + ) + + assert result["type"] is data_entry_flow.FlowResultType.CREATE_ENTRY + assert result["title"] == TEST_NVR_NAME + assert result["data"] == { + CONF_HOST: TEST_HOST, + CONF_USERNAME: TEST_USERNAME, + CONF_PASSWORD: TEST_PASSWORD, + CONF_PORT: TEST_PORT, + const.CONF_USE_HTTPS: TEST_USE_HTTPS, + } + assert result["options"] == { + const.CONF_PROTOCOL: const.DEFAULT_PROTOCOL, + } + + +async def test_options_flow(hass): + """Test specifying non default settings using options flow.""" + config_entry = MockConfigEntry( + domain=const.DOMAIN, + unique_id=format_mac(TEST_MAC), + data={ + CONF_HOST: TEST_HOST, + CONF_USERNAME: TEST_USERNAME, + CONF_PASSWORD: TEST_PASSWORD, + CONF_PORT: TEST_PORT, + const.CONF_USE_HTTPS: TEST_USE_HTTPS, + }, + options={ + const.CONF_PROTOCOL: "rtsp", + }, + title=TEST_NVR_NAME, + ) + config_entry.add_to_hass(hass) + + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + result = await hass.config_entries.options.async_init(config_entry.entry_id) + + assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "init" + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={const.CONF_PROTOCOL: "rtmp"}, + ) + + assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY + assert config_entry.options == { + const.CONF_PROTOCOL: "rtmp", + } + + +async def test_change_connection_settings(hass): + """Test changing connection settings by issuing a second user config flow.""" + config_entry = MockConfigEntry( + domain=const.DOMAIN, + unique_id=format_mac(TEST_MAC), + data={ + CONF_HOST: TEST_HOST, + CONF_USERNAME: TEST_USERNAME, + CONF_PASSWORD: TEST_PASSWORD, + CONF_PORT: TEST_PORT, + const.CONF_USE_HTTPS: TEST_USE_HTTPS, + }, + options={ + const.CONF_PROTOCOL: const.DEFAULT_PROTOCOL, + }, + title=TEST_NVR_NAME, + ) + config_entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + const.DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + assert result["type"] is data_entry_flow.FlowResultType.FORM + assert result["step_id"] == "user" + assert result["errors"] == {} + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_HOST: TEST_HOST2, + CONF_USERNAME: TEST_USERNAME2, + CONF_PASSWORD: TEST_PASSWORD2, + }, + ) + + assert result["type"] is data_entry_flow.FlowResultType.ABORT + assert result["reason"] == "already_configured" + assert config_entry.data[CONF_HOST] == TEST_HOST2 + assert config_entry.data[CONF_USERNAME] == TEST_USERNAME2 + assert config_entry.data[CONF_PASSWORD] == TEST_PASSWORD2 diff --git a/tests/components/rest/test_sensor.py b/tests/components/rest/test_sensor.py index 49ad69b1caa..db55df4ff16 100644 --- a/tests/components/rest/test_sensor.py +++ b/tests/components/rest/test_sensor.py @@ -243,7 +243,6 @@ async def test_setup_timestamp( "method": "GET", "value_template": "{{ value_json.key }}", "device_class": SensorDeviceClass.TIMESTAMP, - "state_class": SensorStateClass.MEASUREMENT, } }, ) @@ -255,7 +254,6 @@ async def test_setup_timestamp( state = hass.states.get("sensor.rest_sensor") assert state.state == "2021-11-11T11:39:00+00:00" assert state.attributes[ATTR_DEVICE_CLASS] == SensorDeviceClass.TIMESTAMP - assert state.attributes[ATTR_STATE_CLASS] is SensorStateClass.MEASUREMENT assert "sensor.rest_sensor rendered invalid timestamp" not in caplog.text assert "sensor.rest_sensor rendered timestamp without timezone" not in caplog.text @@ -904,7 +902,7 @@ async def test_entity_config(hass: HomeAssistant) -> None: "name": "{{'REST' + ' ' + 'Sensor'}}", "state_class": "measurement", "unique_id": "very_unique", - "unit_of_measurement": "beardsecond", + "unit_of_measurement": "°C", }, } @@ -923,5 +921,5 @@ async def test_entity_config(hass: HomeAssistant) -> None: "friendly_name": "REST Sensor", "icon": "mdi:one_two_three", "state_class": "measurement", - "unit_of_measurement": "beardsecond", + "unit_of_measurement": "°C", } diff --git a/tests/components/rfxtrx/test_device_action.py b/tests/components/rfxtrx/test_device_action.py index 7dda64bbe62..69ba983cd5d 100644 --- a/tests/components/rfxtrx/test_device_action.py +++ b/tests/components/rfxtrx/test_device_action.py @@ -169,7 +169,7 @@ async def test_action( rfxtrx.transport.send.assert_called_once_with(bytearray.fromhex(expected)) -async def test_invalid_action(hass, device_reg: DeviceRegistry): +async def test_invalid_action(hass, device_reg: DeviceRegistry, caplog): """Test for invalid actions.""" device = DEVICE_LIGHTING_1 @@ -201,8 +201,4 @@ async def test_invalid_action(hass, device_reg: DeviceRegistry): ) await hass.async_block_till_done() - assert len(notifications := hass.states.async_all("persistent_notification")) == 1 - assert ( - "The following integrations and platforms could not be set up" - in notifications[0].attributes["message"] - ) + assert "Subtype invalid not found in device commands" in caplog.text diff --git a/tests/components/rfxtrx/test_device_trigger.py b/tests/components/rfxtrx/test_device_trigger.py index b783d10ed27..57d604ce64f 100644 --- a/tests/components/rfxtrx/test_device_trigger.py +++ b/tests/components/rfxtrx/test_device_trigger.py @@ -155,7 +155,7 @@ async def test_firing_event(hass, device_reg: DeviceRegistry, rfxtrx, event): assert calls[0].data["some"] == "device" -async def test_invalid_trigger(hass, device_reg: DeviceRegistry): +async def test_invalid_trigger(hass, device_reg: DeviceRegistry, caplog): """Test for invalid actions.""" event = EVENT_LIGHTING_1 @@ -188,8 +188,4 @@ async def test_invalid_trigger(hass, device_reg: DeviceRegistry): ) await hass.async_block_till_done() - assert len(notifications := hass.states.async_all("persistent_notification")) == 1 - assert ( - "The following integrations and platforms could not be set up" - in notifications[0].attributes["message"] - ) + assert "Subtype invalid not found in device triggers" in caplog.text diff --git a/tests/components/rituals_perfume_genie/test_sensor.py b/tests/components/rituals_perfume_genie/test_sensor.py index e7b8daec27f..5b15febced3 100644 --- a/tests/components/rituals_perfume_genie/test_sensor.py +++ b/tests/components/rituals_perfume_genie/test_sensor.py @@ -64,7 +64,7 @@ async def test_sensors_diffuser_v1_battery_cartridge(hass: HomeAssistant) -> Non state = hass.states.get("sensor.genie_wifi") assert state assert state.state == str(diffuser.wifi_percentage) - assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.SIGNAL_STRENGTH + assert state.attributes.get(ATTR_DEVICE_CLASS) is None assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == PERCENTAGE entry = registry.async_get("sensor.genie_wifi") diff --git a/tests/components/roon/test_config_flow.py b/tests/components/roon/test_config_flow.py index 3e5bce8c1c2..32810d22789 100644 --- a/tests/components/roon/test_config_flow.py +++ b/tests/components/roon/test_config_flow.py @@ -144,7 +144,6 @@ async def test_unsuccessful_discovery_user_form_and_auth(hass): "host": "1.1.1.1", "api_key": "good_token", "port": 9331, - "api_key": "good_token", "roon_server_id": "core_id", "roon_server_name": "Roon Core", } diff --git a/tests/components/scrape/__init__.py b/tests/components/scrape/__init__.py index b13cc4a7326..1bf3040513f 100644 --- a/tests/components/scrape/__init__.py +++ b/tests/components/scrape/__init__.py @@ -103,6 +103,9 @@ class MockRestData: "

Current Version: 2021.12.10

Released: January 17, 2022" "" "" + "
" + "

Current Time:

2022-12-22T13:15:30Z" + "
" ) if self.payload == "test_scrape_sensor2": self.data = ( diff --git a/tests/components/scrape/test_sensor.py b/tests/components/scrape/test_sensor.py index 83607f1b993..e9b66049e61 100644 --- a/tests/components/scrape/test_sensor.py +++ b/tests/components/scrape/test_sensor.py @@ -338,6 +338,119 @@ async def test_scrape_sensor_attribute_and_tag(hass: HomeAssistant) -> None: assert state2.state == "Trying to get" +async def test_scrape_sensor_device_date(hass: HomeAssistant) -> None: + """Test Scrape sensor with a device of type DATE.""" + config = { + DOMAIN: [ + return_integration_config( + sensors=[ + { + "select": ".release-date", + "name": "HA Date", + "device_class": "date", + "value_template": "{{ strptime(value, '%B %d, %Y').strftime('%Y-%m-%d') }}", + } + ], + ), + ] + } + + mocker = MockRestData("test_scrape_sensor") + with patch( + "homeassistant.components.rest.RestData", + return_value=mocker, + ): + assert await async_setup_component(hass, DOMAIN, config) + await hass.async_block_till_done() + + state = hass.states.get("sensor.ha_date") + assert state.state == "2022-01-17" + + +async def test_scrape_sensor_device_date_errors(hass: HomeAssistant) -> None: + """Test Scrape sensor with a device of type DATE.""" + config = { + DOMAIN: [ + return_integration_config( + sensors=[ + { + "select": ".current-version h1", + "name": "HA Date", + "device_class": "date", + } + ], + ), + ] + } + + mocker = MockRestData("test_scrape_sensor") + with patch( + "homeassistant.components.rest.RestData", + return_value=mocker, + ): + assert await async_setup_component(hass, DOMAIN, config) + await hass.async_block_till_done() + + state = hass.states.get("sensor.ha_date") + assert state.state == STATE_UNKNOWN + + +async def test_scrape_sensor_device_timestamp(hass: HomeAssistant) -> None: + """Test Scrape sensor with a device of type TIMESTAMP.""" + config = { + DOMAIN: [ + return_integration_config( + sensors=[ + { + "select": ".utc-time", + "name": "HA Timestamp", + "device_class": "timestamp", + } + ], + ), + ] + } + + mocker = MockRestData("test_scrape_sensor") + with patch( + "homeassistant.components.rest.RestData", + return_value=mocker, + ): + assert await async_setup_component(hass, DOMAIN, config) + await hass.async_block_till_done() + + state = hass.states.get("sensor.ha_timestamp") + assert state.state == "2022-12-22T13:15:30+00:00" + + +async def test_scrape_sensor_device_timestamp_error(hass: HomeAssistant) -> None: + """Test Scrape sensor with a device of type TIMESTAMP.""" + config = { + DOMAIN: [ + return_integration_config( + sensors=[ + { + "select": ".current-time", + "name": "HA Timestamp", + "device_class": "timestamp", + } + ], + ), + ] + } + + mocker = MockRestData("test_scrape_sensor") + with patch( + "homeassistant.components.rest.RestData", + return_value=mocker, + ): + assert await async_setup_component(hass, DOMAIN, config) + await hass.async_block_till_done() + + state = hass.states.get("sensor.ha_timestamp") + assert state.state == STATE_UNKNOWN + + async def test_scrape_sensor_errors(hass: HomeAssistant) -> None: """Test Scrape sensor handle errors.""" config = { diff --git a/tests/components/script/test_init.py b/tests/components/script/test_init.py index 09d2c3c70b1..7fbb1f6bfe4 100644 --- a/tests/components/script/test_init.py +++ b/tests/components/script/test_init.py @@ -38,6 +38,7 @@ from homeassistant.helpers.script import ( ) from homeassistant.helpers.service import async_get_all_descriptions from homeassistant.setup import async_setup_component +from homeassistant.util import yaml import homeassistant.util.dt as dt_util from tests.common import async_fire_time_changed, async_mock_service, mock_restore_cache @@ -166,6 +167,67 @@ async def test_setup_with_invalid_configs(hass, value): assert len(hass.states.async_entity_ids("script")) == 0 +@pytest.mark.parametrize( + "object_id, broken_config, problem, details", + ( + ( + "Bad Script", + {}, + "has invalid object id", + "invalid slug Bad Script", + ), + ( + "bad_script", + {}, + "could not be validated", + "required key not provided @ data['sequence']", + ), + ( + "bad_script", + { + "sequence": { + "condition": "state", + # The UUID will fail being resolved to en entity_id + "entity_id": "abcdabcdabcdabcdabcdabcdabcdabcd", + "state": "blah", + }, + }, + "failed to setup actions", + "Unknown entity registry entry abcdabcdabcdabcdabcdabcdabcdabcd.", + ), + ), +) +async def test_bad_config_validation( + hass: HomeAssistant, caplog, object_id, broken_config, problem, details +): + """Test bad script configuration which can be detected during validation.""" + assert await async_setup_component( + hass, + script.DOMAIN, + { + script.DOMAIN: { + object_id: {"alias": "bad_script", **broken_config}, + "good_script": { + "alias": "good_script", + "sequence": { + "service": "test.automation", + "entity_id": "hello.world", + }, + }, + } + }, + ) + + # Check we get the expected error message + assert ( + f"Script with alias 'bad_script' {problem} and has been disabled: {details}" + in caplog.text + ) + + # Make sure one bad automation does not prevent other automations from setting up + assert hass.states.async_entity_ids("script") == ["script.good_script"] + + @pytest.mark.parametrize("running", ["no", "same", "different"]) async def test_reload_service(hass, running): """Verify the reload service.""" @@ -1248,3 +1310,81 @@ async def test_script_service_changed_entity_id(hass: HomeAssistant) -> None: assert len(calls) == 2 assert calls[1].data["entity_id"] == "script.custom_entity_id_2" + + +@pytest.mark.parametrize( + "blueprint_inputs, problem, details", + ( + ( + # No input + {}, + "Failed to generate script from blueprint", + "Missing input service_to_call", + ), + ( + # Missing input + {"a_number": 5}, + "Failed to generate script from blueprint", + "Missing input service_to_call", + ), + ( + # Wrong input + { + "trigger_event": "blueprint_event", + "service_to_call": {"dict": "not allowed"}, + "a_number": 5, + }, + "Blueprint 'Call service' generated invalid script", + "value should be a string for dictionary value @ data['sequence'][0]['service']", + ), + ), +) +async def test_blueprint_script_bad_config( + hass, caplog, blueprint_inputs, problem, details +): + """Test blueprint script with bad inputs.""" + assert await async_setup_component( + hass, + script.DOMAIN, + { + script.DOMAIN: { + "test_script": { + "use_blueprint": { + "path": "test_service.yaml", + "input": blueprint_inputs, + } + } + } + }, + ) + assert problem in caplog.text + assert details in caplog.text + + +async def test_blueprint_script_fails_substitution(hass, caplog): + """Test blueprint script with bad inputs.""" + with patch( + "homeassistant.components.blueprint.models.BlueprintInputs.async_substitute", + side_effect=yaml.UndefinedSubstitution("blah"), + ): + assert await async_setup_component( + hass, + script.DOMAIN, + { + script.DOMAIN: { + "test_script": { + "use_blueprint": { + "path": "test_service.yaml", + "input": { + "service_to_call": "test.automation", + }, + } + } + } + }, + ) + assert ( + "Blueprint 'Call service' failed to generate script with inputs " + "{'service_to_call': 'test.automation'}: No substitution found for input blah" + in caplog.text + ) diff --git a/tests/components/season/test_sensor.py b/tests/components/season/test_sensor.py index 0c2470edb7b..e2f4e6d4dcc 100644 --- a/tests/components/season/test_sensor.py +++ b/tests/components/season/test_sensor.py @@ -15,7 +15,8 @@ from homeassistant.components.season.sensor import ( STATE_SUMMER, STATE_WINTER, ) -from homeassistant.const import CONF_TYPE, STATE_UNKNOWN +from homeassistant.components.sensor import ATTR_OPTIONS, SensorDeviceClass +from homeassistant.const import ATTR_DEVICE_CLASS, CONF_TYPE, STATE_UNKNOWN from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry as dr, entity_registry as er @@ -92,11 +93,14 @@ async def test_season_northern_hemisphere( state = hass.states.get("sensor.season") assert state assert state.state == expected + assert state.attributes[ATTR_DEVICE_CLASS] == SensorDeviceClass.ENUM + assert state.attributes[ATTR_OPTIONS] == ["spring", "summer", "autumn", "winter"] entity_registry = er.async_get(hass) entry = entity_registry.async_get("sensor.season") assert entry assert entry.unique_id == mock_config_entry.entry_id + assert entry.translation_key == "season" @pytest.mark.parametrize("type,day,expected", SOUTHERN_PARAMETERS, ids=idfn) @@ -121,11 +125,14 @@ async def test_season_southern_hemisphere( state = hass.states.get("sensor.season") assert state assert state.state == expected + assert state.attributes[ATTR_DEVICE_CLASS] == SensorDeviceClass.ENUM + assert state.attributes[ATTR_OPTIONS] == ["spring", "summer", "autumn", "winter"] entity_registry = er.async_get(hass) entry = entity_registry.async_get("sensor.season") assert entry assert entry.unique_id == mock_config_entry.entry_id + assert entry.translation_key == "season" device_registry = dr.async_get(hass) assert entry.device_id diff --git a/tests/components/select/test_device_trigger.py b/tests/components/select/test_device_trigger.py index c0166d02840..09471068429 100644 --- a/tests/components/select/test_device_trigger.py +++ b/tests/components/select/test_device_trigger.py @@ -258,7 +258,6 @@ async def test_get_trigger_capabilities(hass: HomeAssistant) -> None: "name": "for", "optional": True, "type": "positive_time_period_dict", - "optional": True, }, ] @@ -288,6 +287,5 @@ async def test_get_trigger_capabilities(hass: HomeAssistant) -> None: "name": "for", "optional": True, "type": "positive_time_period_dict", - "optional": True, }, ] diff --git a/tests/components/sensor/test_init.py b/tests/components/sensor/test_init.py index c9c29d6c99a..d0027a6a07c 100644 --- a/tests/components/sensor/test_init.py +++ b/tests/components/sensor/test_init.py @@ -6,7 +6,7 @@ import pytest from pytest import approx from homeassistant.components.number import NumberDeviceClass -from homeassistant.components.sensor import SensorDeviceClass +from homeassistant.components.sensor import SensorDeviceClass, SensorStateClass from homeassistant.const import ( ATTR_UNIT_OF_MEASUREMENT, LENGTH_CENTIMETERS, @@ -32,8 +32,9 @@ from homeassistant.const import ( VOLUME_CUBIC_METERS, VOLUME_FLUID_OUNCE, VOLUME_LITERS, + UnitOfTemperature, ) -from homeassistant.core import State +from homeassistant.core import HomeAssistant, State from homeassistant.helpers import entity_registry as er from homeassistant.helpers.restore_state import STORAGE_KEY as RESTORE_STATE_KEY from homeassistant.setup import async_setup_component @@ -888,6 +889,14 @@ async def test_unit_conversion_priority_suggested_unit_change( 621, SensorDeviceClass.DISTANCE, ), + ( + US_CUSTOMARY_SYSTEM, + LENGTH_METERS, + LENGTH_MILES, + 1000000, + 621.371, + SensorDeviceClass.DISTANCE, + ), ], ) async def test_unit_conversion_priority_legacy_conversion_removed( @@ -936,3 +945,214 @@ def test_device_classes_aligned(): for device_class in NumberDeviceClass: assert hasattr(SensorDeviceClass, device_class.name) assert getattr(SensorDeviceClass, device_class.name).value == device_class.value + + +async def test_value_unknown_in_enumeration( + hass: HomeAssistant, + caplog: pytest.LogCaptureFixture, + enable_custom_integrations: None, +): + """Test warning on invalid enum value.""" + platform = getattr(hass.components, "test.sensor") + platform.init(empty=True) + platform.ENTITIES["0"] = platform.MockSensor( + name="Test", + native_value="invalid_option", + device_class=SensorDeviceClass.ENUM, + options=["option1", "option2"], + ) + + assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}}) + await hass.async_block_till_done() + + assert ( + "Sensor sensor.test provides state value 'invalid_option', " + "which is not in the list of options provided" + ) in caplog.text + + +async def test_invalid_enumeration_entity_with_device_class( + hass: HomeAssistant, + caplog: pytest.LogCaptureFixture, + enable_custom_integrations: None, +): + """Test warning on entities that provide an enum with a device class.""" + platform = getattr(hass.components, "test.sensor") + platform.init(empty=True) + platform.ENTITIES["0"] = platform.MockSensor( + name="Test", + native_value=21, + device_class=SensorDeviceClass.POWER, + options=["option1", "option2"], + ) + + assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}}) + await hass.async_block_till_done() + + assert ( + "Sensor sensor.test is providing enum options, but has device class 'power' " + "instead of 'enum'" + ) in caplog.text + + +async def test_invalid_enumeration_entity_without_device_class( + hass: HomeAssistant, + caplog: pytest.LogCaptureFixture, + enable_custom_integrations: None, +): + """Test warning on entities that provide an enum without a device class.""" + platform = getattr(hass.components, "test.sensor") + platform.init(empty=True) + platform.ENTITIES["0"] = platform.MockSensor( + name="Test", + native_value=21, + options=["option1", "option2"], + ) + + assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}}) + await hass.async_block_till_done() + + assert ( + "Sensor sensor.test is providing enum options, but is missing " + "the enum device class" + ) in caplog.text + + +@pytest.mark.parametrize( + "device_class", + ( + SensorDeviceClass.DATE, + SensorDeviceClass.ENUM, + SensorDeviceClass.TIMESTAMP, + ), +) +async def test_non_numeric_device_class_with_state_class( + hass: HomeAssistant, + caplog: pytest.LogCaptureFixture, + enable_custom_integrations: None, + device_class: SensorDeviceClass, +): + """Test error on numeric entities that provide an state class.""" + platform = getattr(hass.components, "test.sensor") + platform.init(empty=True) + platform.ENTITIES["0"] = platform.MockSensor( + name="Test", + native_value=None, + device_class=device_class, + state_class=SensorStateClass.MEASUREMENT, + options=["option1", "option2"], + ) + + assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}}) + await hass.async_block_till_done() + + assert ( + "Sensor sensor.test has a state class and thus indicating it has a numeric " + f"value; however, it has the non-numeric device class: {device_class}" + ) in caplog.text + + +@pytest.mark.parametrize( + "device_class", + ( + SensorDeviceClass.DATE, + SensorDeviceClass.ENUM, + SensorDeviceClass.TIMESTAMP, + ), +) +async def test_non_numeric_device_class_with_unit_of_measurement( + hass: HomeAssistant, + caplog: pytest.LogCaptureFixture, + enable_custom_integrations: None, + device_class: SensorDeviceClass, +): + """Test error on numeric entities that provide an unit of measurement.""" + platform = getattr(hass.components, "test.sensor") + platform.init(empty=True) + platform.ENTITIES["0"] = platform.MockSensor( + name="Test", + native_value=None, + device_class=device_class, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + options=["option1", "option2"], + ) + + assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}}) + await hass.async_block_till_done() + + assert ( + "Sensor sensor.test has a unit of measurement and thus indicating it has " + f"a numeric value; however, it has the non-numeric device class: {device_class}" + ) in caplog.text + + +@pytest.mark.parametrize( + "device_class", + ( + SensorDeviceClass.APPARENT_POWER, + SensorDeviceClass.AQI, + SensorDeviceClass.ATMOSPHERIC_PRESSURE, + SensorDeviceClass.BATTERY, + SensorDeviceClass.CO, + SensorDeviceClass.CO2, + SensorDeviceClass.CURRENT, + SensorDeviceClass.DATA_RATE, + SensorDeviceClass.DATA_SIZE, + SensorDeviceClass.DISTANCE, + SensorDeviceClass.DURATION, + SensorDeviceClass.ENERGY, + SensorDeviceClass.FREQUENCY, + SensorDeviceClass.GAS, + SensorDeviceClass.HUMIDITY, + SensorDeviceClass.ILLUMINANCE, + SensorDeviceClass.IRRADIANCE, + SensorDeviceClass.MOISTURE, + SensorDeviceClass.NITROGEN_DIOXIDE, + SensorDeviceClass.NITROGEN_MONOXIDE, + SensorDeviceClass.NITROUS_OXIDE, + SensorDeviceClass.OZONE, + SensorDeviceClass.PM1, + SensorDeviceClass.PM10, + SensorDeviceClass.PM25, + SensorDeviceClass.POWER_FACTOR, + SensorDeviceClass.POWER, + SensorDeviceClass.PRECIPITATION_INTENSITY, + SensorDeviceClass.PRECIPITATION, + SensorDeviceClass.PRESSURE, + SensorDeviceClass.REACTIVE_POWER, + SensorDeviceClass.SIGNAL_STRENGTH, + SensorDeviceClass.SOUND_PRESSURE, + SensorDeviceClass.SPEED, + SensorDeviceClass.SULPHUR_DIOXIDE, + SensorDeviceClass.TEMPERATURE, + SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS, + SensorDeviceClass.VOLTAGE, + SensorDeviceClass.VOLUME, + SensorDeviceClass.WATER, + SensorDeviceClass.WEIGHT, + SensorDeviceClass.WIND_SPEED, + ), +) +async def test_device_classes_with_invalid_unit_of_measurement( + hass: HomeAssistant, + caplog: pytest.LogCaptureFixture, + enable_custom_integrations: None, + device_class: SensorDeviceClass, +): + """Test error when unit of measurement is not valid for used device class.""" + platform = getattr(hass.components, "test.sensor") + platform.init(empty=True) + platform.ENTITIES["0"] = platform.MockSensor( + name="Test", + native_value="1.0", + device_class=device_class, + native_unit_of_measurement="INVALID!", + ) + + assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}}) + await hass.async_block_till_done() + + assert ( + "is using native unit of measurement 'INVALID!' which is not a valid " + f"unit for the device class ('{device_class}') it is using" + ) in caplog.text diff --git a/tests/components/sensor/test_recorder.py b/tests/components/sensor/test_recorder.py index 1e129b7af92..a252a85b8d7 100644 --- a/tests/components/sensor/test_recorder.py +++ b/tests/components/sensor/test_recorder.py @@ -10,7 +10,11 @@ from pytest import approx from homeassistant import loader from homeassistant.components.recorder import DOMAIN as RECORDER_DOMAIN, history -from homeassistant.components.recorder.db_schema import StatisticsMeta +from homeassistant.components.recorder.db_schema import ( + StateAttributes, + States, + StatisticsMeta, +) from homeassistant.components.recorder.models import ( StatisticData, StatisticMetaData, @@ -22,11 +26,14 @@ from homeassistant.components.recorder.statistics import ( list_statistic_ids, ) from homeassistant.components.recorder.util import get_instance, session_scope -from homeassistant.const import STATE_UNAVAILABLE +from homeassistant.components.sensor import ATTR_OPTIONS, DOMAIN +from homeassistant.const import ATTR_FRIENDLY_NAME, STATE_UNAVAILABLE +from homeassistant.core import HomeAssistant, State from homeassistant.setup import async_setup_component, setup_component import homeassistant.util.dt as dt_util from homeassistant.util.unit_system import METRIC_SYSTEM, US_CUSTOMARY_SYSTEM +from tests.common import async_fire_time_changed from tests.components.recorder.common import ( async_recorder_block_till_done, async_wait_recording_done, @@ -4050,7 +4057,7 @@ async def test_validate_statistics_unit_change_equivalent_units( @pytest.mark.parametrize( "attributes, unit1, unit2, supported_unit", [ - (NONE_SENSOR_ATTRIBUTES, "m³", "m3", "L, fl. oz., ft³, gal, mL, m³"), + (NONE_SENSOR_ATTRIBUTES, "m³", "m3", "CCF, L, fl. oz., ft³, gal, mL, m³"), ], ) async def test_validate_statistics_unit_change_equivalent_units_2( @@ -4320,3 +4327,27 @@ def record_states_partially_unavailable(hass, zero, entity_id, attributes): ) return four, states + + +async def test_exclude_attributes(recorder_mock: None, hass: HomeAssistant) -> None: + """Test sensor attributes to be excluded.""" + await async_setup_component(hass, DOMAIN, {DOMAIN: {"platform": "demo"}}) + await hass.async_block_till_done() + async_fire_time_changed(hass, dt_util.utcnow() + timedelta(minutes=5)) + await hass.async_block_till_done() + await async_wait_recording_done(hass) + + def _fetch_states() -> list[State]: + with session_scope(hass=hass) as session: + native_states = [] + for db_state, db_state_attributes in session.query(States, StateAttributes): + state = db_state.to_native() + state.attributes = db_state_attributes.to_native() + native_states.append(state) + return native_states + + states: list[State] = await hass.async_add_executor_job(_fetch_states) + assert len(states) > 1 + for state in states: + assert ATTR_OPTIONS not in state.attributes + assert ATTR_FRIENDLY_NAME in state.attributes diff --git a/tests/components/shelly/conftest.py b/tests/components/shelly/conftest.py index 80e53ac6796..e2ba5fc767a 100644 --- a/tests/components/shelly/conftest.py +++ b/tests/components/shelly/conftest.py @@ -124,6 +124,7 @@ MOCK_BLOCKS = [ MOCK_CONFIG = { "input:0": {"id": 0, "type": "button"}, + "light:0": {"name": "test light_0"}, "switch:0": {"name": "test switch_0"}, "cover:0": {"name": "test cover_0"}, "sys": { @@ -169,6 +170,7 @@ MOCK_STATUS_COAP = { MOCK_STATUS_RPC = { "switch:0": {"output": True}, + "light:0": {"output": True, "brightness": 53.0}, "cloud": {"connected": False}, "cover:0": { "state": "stopped", @@ -299,8 +301,14 @@ async def mock_rpc_device(): {}, UpdateType.EVENT ) + def disconnected(): + rpc_device_mock.return_value.subscribe_updates.call_args[0][0]( + {}, UpdateType.DISCONNECTED + ) + device = _mock_rpc_device("0.12.0") rpc_device_mock.return_value = device + rpc_device_mock.return_value.mock_disconnected = Mock(side_effect=disconnected) rpc_device_mock.return_value.mock_update = Mock(side_effect=update) rpc_device_mock.return_value.mock_event = Mock(side_effect=event) diff --git a/tests/components/shelly/test_config_flow.py b/tests/components/shelly/test_config_flow.py index 1a1acea16a3..6795049a207 100644 --- a/tests/components/shelly/test_config_flow.py +++ b/tests/components/shelly/test_config_flow.py @@ -37,6 +37,15 @@ DISCOVERY_INFO = zeroconf.ZeroconfServiceInfo( properties={zeroconf.ATTR_PROPERTIES_ID: "shelly1pm-12345"}, type="mock_type", ) +DISCOVERY_INFO_WITH_MAC = zeroconf.ZeroconfServiceInfo( + host="1.1.1.1", + addresses=["1.1.1.1"], + hostname="mock_hostname", + name="shelly1pm-AABBCCDDEEFF", + port=None, + properties={zeroconf.ATTR_PROPERTIES_ID: "shelly1pm-AABBCCDDEEFF"}, + type="mock_type", +) MOCK_CONFIG = { "sys": { "device": {"name": "Test name"}, @@ -1064,3 +1073,67 @@ async def test_options_flow_pre_ble_device(hass, mock_pre_ble_rpc_device): assert result["reason"] == "ble_unsupported" await hass.config_entries.async_unload(entry.entry_id) + + +async def test_zeroconf_already_configured_triggers_refresh_mac_in_name( + hass, mock_rpc_device, monkeypatch +): + """Test zeroconf discovery triggers refresh when the mac is in the device name.""" + entry = MockConfigEntry( + domain="shelly", + unique_id="AABBCCDDEEFF", + data={"host": "1.1.1.1", "gen": 2, "sleep_period": 0, "model": "SHSW-1"}, + ) + entry.add_to_hass(hass) + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + assert len(mock_rpc_device.initialize.mock_calls) == 1 + + with patch( + "aioshelly.common.get_info", + return_value={"mac": "", "type": "SHSW-1", "auth": False}, + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, + data=DISCOVERY_INFO_WITH_MAC, + context={"source": config_entries.SOURCE_ZEROCONF}, + ) + assert result["type"] == data_entry_flow.FlowResultType.ABORT + assert result["reason"] == "already_configured" + + monkeypatch.setattr(mock_rpc_device, "connected", False) + mock_rpc_device.mock_disconnected() + await hass.async_block_till_done() + assert len(mock_rpc_device.initialize.mock_calls) == 2 + + +async def test_zeroconf_already_configured_triggers_refresh( + hass, mock_rpc_device, monkeypatch +): + """Test zeroconf discovery triggers refresh when the mac is obtained via get_info.""" + entry = MockConfigEntry( + domain="shelly", + unique_id="AABBCCDDEEFF", + data={"host": "1.1.1.1", "gen": 2, "sleep_period": 0, "model": "SHSW-1"}, + ) + entry.add_to_hass(hass) + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + assert len(mock_rpc_device.initialize.mock_calls) == 1 + + with patch( + "aioshelly.common.get_info", + return_value={"mac": "AABBCCDDEEFF", "type": "SHSW-1", "auth": False}, + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, + data=DISCOVERY_INFO, + context={"source": config_entries.SOURCE_ZEROCONF}, + ) + assert result["type"] == data_entry_flow.FlowResultType.ABORT + assert result["reason"] == "already_configured" + + monkeypatch.setattr(mock_rpc_device, "connected", False) + mock_rpc_device.mock_disconnected() + await hass.async_block_till_done() + assert len(mock_rpc_device.initialize.mock_calls) == 2 diff --git a/tests/components/shelly/test_device_trigger.py b/tests/components/shelly/test_device_trigger.py index ec74745da15..13ccf3f843e 100644 --- a/tests/components/shelly/test_device_trigger.py +++ b/tests/components/shelly/test_device_trigger.py @@ -324,7 +324,7 @@ async def test_validate_trigger_rpc_device_not_ready( assert calls[0].data["some"] == "test_trigger_single_push" -async def test_validate_trigger_invalid_triggers(hass, mock_block_device): +async def test_validate_trigger_invalid_triggers(hass, mock_block_device, caplog): """Test for click_event with invalid triggers.""" entry = await init_integration(hass, 1) dev_reg = async_get_dev_reg(hass) @@ -352,8 +352,4 @@ async def test_validate_trigger_invalid_triggers(hass, mock_block_device): }, ) - assert len(notifications := hass.states.async_all("persistent_notification")) == 1 - assert ( - "The following integrations and platforms could not be set up" - in notifications[0].attributes["message"] - ) + assert "Invalid (type,subtype): ('single', 'button3')" in caplog.text diff --git a/tests/components/shelly/test_diagnostics.py b/tests/components/shelly/test_diagnostics.py index ccac9bcc1b0..fb78cdd5316 100644 --- a/tests/components/shelly/test_diagnostics.py +++ b/tests/components/shelly/test_diagnostics.py @@ -1,12 +1,19 @@ """Tests for Shelly diagnostics platform.""" +from unittest.mock import ANY + from aiohttp import ClientSession +from aioshelly.ble.const import BLE_SCAN_RESULT_EVENT from homeassistant.components.diagnostics import REDACTED -from homeassistant.components.shelly.const import DOMAIN +from homeassistant.components.shelly.const import ( + CONF_BLE_SCANNER_MODE, + DOMAIN, + BLEScannerMode, +) from homeassistant.components.shelly.diagnostics import TO_REDACT from homeassistant.core import HomeAssistant -from . import init_integration +from . import init_integration, inject_rpc_device_event from .conftest import MOCK_STATUS_COAP from tests.components.diagnostics import get_diagnostics_for_config_entry @@ -30,6 +37,7 @@ async def test_block_config_entry_diagnostics( assert result == { "entry": entry_dict, + "bluetooth": "not initialized", "device_info": { "name": "Test name", "model": "SHSW-25", @@ -44,9 +52,35 @@ async def test_rpc_config_entry_diagnostics( hass: HomeAssistant, hass_client: ClientSession, mock_rpc_device, + monkeypatch, ): """Test config entry diagnostics for rpc device.""" - await init_integration(hass, 2) + await init_integration( + hass, 2, options={CONF_BLE_SCANNER_MODE: BLEScannerMode.ACTIVE} + ) + + inject_rpc_device_event( + monkeypatch, + mock_rpc_device, + { + "events": [ + { + "component": "script:1", + "data": [ + 1, + "aa:bb:cc:dd:ee:ff", + -62, + "AgEGCf9ZANH7O3TIkA==", + "EQcbxdWlAgC4n+YRTSIADaLLBhYADUgQYQ==", + ], + "event": BLE_SCAN_RESULT_EVENT, + "id": 1, + "ts": 1668522399.2, + } + ], + "ts": 1668522399.2, + }, + ) entry = hass.config_entries.async_entries(DOMAIN)[0] entry_dict = entry.as_dict() @@ -58,6 +92,48 @@ async def test_rpc_config_entry_diagnostics( assert result == { "entry": entry_dict, + "bluetooth": { + "scanner": { + "connectable": False, + "discovered_device_timestamps": {"AA:BB:CC:DD:EE:FF": ANY}, + "discovered_devices_and_advertisement_data": [ + { + "address": "AA:BB:CC:DD:EE:FF", + "advertisement_data": [ + None, + { + "89": { + "__type": "", + "repr": "b'\\xd1\\xfb;t\\xc8\\x90'", + } + }, + { + "00000d00-0000-1000-8000-00805f9b34fb": { + "__type": "", + "repr": "b'H\\x10a'", + } + }, + ["cba20d00-224d-11e6-9fb8-0002a5d5c51b"], + -127, + -62, + [], + ], + "details": {"source": "12:34:56:78:9A:BC"}, + "name": None, + "rssi": -62, + } + ], + "last_detection": ANY, + "monotonic_time": ANY, + "name": "Mock Title (12:34:56:78:9A:BC)", + "scanning": True, + "start_time": ANY, + "source": "12:34:56:78:9A:BC", + "storage": None, + "time_since_last_device_detection": {"AA:BB:CC:DD:EE:FF": ANY}, + "type": "ShellyBLEScanner", + } + }, "device_info": { "name": "Test name", "model": "SHSW-25", diff --git a/tests/components/shelly/test_init.py b/tests/components/shelly/test_init.py index 48ea978e5d9..0697c6fb613 100644 --- a/tests/components/shelly/test_init.py +++ b/tests/components/shelly/test_init.py @@ -211,3 +211,30 @@ async def test_entry_unload_not_connected(hass, mock_rpc_device, monkeypatch): assert not mock_stop_scanner.call_count assert entry.state is ConfigEntryState.LOADED + + +async def test_entry_unload_not_connected_but_we_with_we_are( + hass, mock_rpc_device, monkeypatch +): + """Test entry unload when not connected but we think we are still connected.""" + with patch( + "homeassistant.components.shelly.coordinator.async_stop_scanner", + side_effect=DeviceConnectionError, + ) as mock_stop_scanner: + + entry = await init_integration( + hass, 2, options={CONF_BLE_SCANNER_MODE: BLEScannerMode.ACTIVE} + ) + entity_id = "switch.test_switch_0" + + assert entry.state is ConfigEntryState.LOADED + assert hass.states.get(entity_id).state is STATE_ON + assert not mock_stop_scanner.call_count + + monkeypatch.setattr(mock_rpc_device, "connected", False) + + await hass.config_entries.async_reload(entry.entry_id) + await hass.async_block_till_done() + + assert not mock_stop_scanner.call_count + assert entry.state is ConfigEntryState.LOADED diff --git a/tests/components/shelly/test_light.py b/tests/components/shelly/test_light.py index 5f8d49fa8aa..7b7376548c8 100644 --- a/tests/components/shelly/test_light.py +++ b/tests/components/shelly/test_light.py @@ -383,3 +383,60 @@ async def test_rpc_device_switch_type_lights_mode(hass, mock_rpc_device, monkeyp ) mock_rpc_device.mock_update() assert hass.states.get("light.test_switch_0").state == STATE_OFF + + +async def test_rpc_light(hass, mock_rpc_device, monkeypatch): + """Test RPC light.""" + entity_id = f"{LIGHT_DOMAIN}.test_light_0" + monkeypatch.delitem(mock_rpc_device.status, "switch:0") + await init_integration(hass, 2) + + # Turn on + await hass.services.async_call( + LIGHT_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: entity_id}, + blocking=True, + ) + + mock_rpc_device.call_rpc.assert_called_once_with("Light.Set", {"id": 0, "on": True}) + state = hass.states.get(entity_id) + assert state.state == STATE_ON + assert state.attributes[ATTR_BRIGHTNESS] == 135 + + # Turn off + mock_rpc_device.call_rpc.reset_mock() + mutate_rpc_device_status(monkeypatch, mock_rpc_device, "light:0", "output", False) + await hass.services.async_call( + LIGHT_DOMAIN, + SERVICE_TURN_OFF, + {ATTR_ENTITY_ID: entity_id}, + blocking=True, + ) + + mock_rpc_device.mock_update() + mock_rpc_device.call_rpc.assert_called_once_with( + "Light.Set", {"id": 0, "on": False} + ) + state = hass.states.get(entity_id) + assert state.state == STATE_OFF + + # Turn on, brightness = 33 + mock_rpc_device.call_rpc.reset_mock() + await hass.services.async_call( + LIGHT_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: entity_id, ATTR_BRIGHTNESS: 33}, + blocking=True, + ) + + mutate_rpc_device_status(monkeypatch, mock_rpc_device, "light:0", "output", True) + mutate_rpc_device_status(monkeypatch, mock_rpc_device, "light:0", "brightness", 13) + mock_rpc_device.mock_update() + + mock_rpc_device.call_rpc.assert_called_once_with( + "Light.Set", {"id": 0, "on": True, "brightness": 13} + ) + state = hass.states.get(entity_id) + assert state.state == STATE_ON + assert state.attributes[ATTR_BRIGHTNESS] == 33 diff --git a/tests/components/shopping_list/test_init.py b/tests/components/shopping_list/test_init.py index 515d0818460..018a6470e8f 100644 --- a/tests/components/shopping_list/test_init.py +++ b/tests/components/shopping_list/test_init.py @@ -1,12 +1,16 @@ """Test shopping list component.""" from http import HTTPStatus +import pytest + +from homeassistant.components.shopping_list import NoMatchingShoppingListItem from homeassistant.components.shopping_list.const import ( DOMAIN, EVENT_SHOPPING_LIST_UPDATED, SERVICE_ADD_ITEM, SERVICE_CLEAR_COMPLETED_ITEMS, SERVICE_COMPLETE_ITEM, + SERVICE_REMOVE_ITEM, ) from homeassistant.components.websocket_api.const import ( ERR_INVALID_FORMAT, @@ -29,6 +33,32 @@ async def test_add_item(hass, sl_setup): assert response.speech["plain"]["speech"] == "I've added beer to your shopping list" +async def test_remove_item(hass, sl_setup): + """Test removiung list items.""" + await intent.async_handle( + hass, "test", "HassShoppingListAddItem", {"item": {"value": "beer"}} + ) + + await intent.async_handle( + hass, "test", "HassShoppingListAddItem", {"item": {"value": "cheese"}} + ) + + assert len(hass.data[DOMAIN].items) == 2 + + # Remove a single item + item_id = hass.data[DOMAIN].items[0]["id"] + await hass.data[DOMAIN].async_remove(item_id) + + assert len(hass.data[DOMAIN].items) == 1 + + item = hass.data[DOMAIN].items[0] + assert item["name"] == "cheese" + + # Trying to remove the same item twice should fail + with pytest.raises(NoMatchingShoppingListItem): + await hass.data[DOMAIN].async_remove(item_id) + + async def test_update_list(hass, sl_setup): """Test updating all list items.""" await intent.async_handle( @@ -414,6 +444,47 @@ async def test_ws_add_item_fail(hass, hass_ws_client, sl_setup): assert len(hass.data["shopping_list"].items) == 0 +async def test_ws_remove_item(hass, hass_ws_client, sl_setup): + """Test removing shopping_list item websocket command.""" + client = await hass_ws_client(hass) + events = async_capture_events(hass, EVENT_SHOPPING_LIST_UPDATED) + await client.send_json({"id": 5, "type": "shopping_list/items/add", "name": "soda"}) + msg = await client.receive_json() + first_item_id = msg["result"]["id"] + await client.send_json( + {"id": 6, "type": "shopping_list/items/add", "name": "cheese"} + ) + msg = await client.receive_json() + assert len(events) == 2 + + items = hass.data["shopping_list"].items + assert len(items) == 2 + + await client.send_json( + {"id": 7, "type": "shopping_list/items/remove", "item_id": first_item_id} + ) + msg = await client.receive_json() + assert len(events) == 3 + assert msg["success"] is True + + items = hass.data["shopping_list"].items + assert len(items) == 1 + assert items[0]["name"] == "cheese" + + +async def test_ws_remove_item_fail(hass, hass_ws_client, sl_setup): + """Test removing shopping_list item failure websocket command.""" + client = await hass_ws_client(hass) + events = async_capture_events(hass, EVENT_SHOPPING_LIST_UPDATED) + await client.send_json({"id": 5, "type": "shopping_list/items/add", "name": "soda"}) + msg = await client.receive_json() + await client.send_json({"id": 6, "type": "shopping_list/items/remove"}) + msg = await client.receive_json() + assert msg["success"] is False + assert len(events) == 1 + assert len(hass.data["shopping_list"].items) == 1 + + async def test_ws_reorder_items(hass, hass_ws_client, sl_setup): """Test reordering shopping_list items websocket command.""" await intent.async_handle( @@ -558,6 +629,40 @@ async def test_add_item_service(hass, sl_setup): assert len(events) == 1 +async def test_remove_item_service(hass, sl_setup): + """Test removing shopping_list item service.""" + events = async_capture_events(hass, EVENT_SHOPPING_LIST_UPDATED) + await hass.services.async_call( + DOMAIN, + SERVICE_ADD_ITEM, + {ATTR_NAME: "beer"}, + blocking=True, + ) + await hass.async_block_till_done() + await hass.services.async_call( + DOMAIN, + SERVICE_ADD_ITEM, + {ATTR_NAME: "cheese"}, + blocking=True, + ) + await hass.async_block_till_done() + + assert len(hass.data[DOMAIN].items) == 2 + assert len(events) == 2 + + await hass.services.async_call( + DOMAIN, + SERVICE_REMOVE_ITEM, + {ATTR_NAME: "beer"}, + blocking=True, + ) + await hass.async_block_till_done() + + assert len(hass.data[DOMAIN].items) == 1 + assert hass.data[DOMAIN].items[0]["name"] == "cheese" + assert len(events) == 3 + + async def test_clear_completed_items_service(hass, sl_setup): """Test clearing completed shopping_list items service.""" events = async_capture_events(hass, EVENT_SHOPPING_LIST_UPDATED) diff --git a/tests/components/snmp/test_sensor.py b/tests/components/snmp/test_sensor.py index 9f3a555c2d9..965bc0d3ae9 100644 --- a/tests/components/snmp/test_sensor.py +++ b/tests/components/snmp/test_sensor.py @@ -1,5 +1,6 @@ """SNMP sensor tests.""" +import asyncio from unittest.mock import MagicMock, Mock, patch import pytest @@ -15,9 +16,11 @@ def hlapi_mock(): """Mock out 3rd party API.""" mock_data = MagicMock() mock_data.prettyPrint = Mock(return_value="hello") + future = asyncio.get_event_loop().create_future() + future.set_result((None, None, None, [[mock_data]])) with patch( "homeassistant.components.snmp.sensor.getCmd", - return_value=(None, None, None, [[mock_data]]), + return_value=future, ): yield @@ -57,7 +60,7 @@ async def test_entity_config(hass: HomeAssistant) -> None: "name": "{{'SNMP' + ' ' + 'Sensor'}}", "state_class": "measurement", "unique_id": "very_unique", - "unit_of_measurement": "beardsecond", + "unit_of_measurement": "°C", }, } @@ -75,5 +78,5 @@ async def test_entity_config(hass: HomeAssistant) -> None: "friendly_name": "SNMP Sensor", "icon": "mdi:one_two_three", "state_class": "measurement", - "unit_of_measurement": "beardsecond", + "unit_of_measurement": "°C", } diff --git a/tests/components/speedtestdotnet/test_config_flow.py b/tests/components/speedtestdotnet/test_config_flow.py index 0344c09631b..00269c55ec3 100644 --- a/tests/components/speedtestdotnet/test_config_flow.py +++ b/tests/components/speedtestdotnet/test_config_flow.py @@ -1,17 +1,15 @@ """Tests for SpeedTest config flow.""" -from datetime import timedelta from unittest.mock import MagicMock -from homeassistant import config_entries, data_entry_flow +from homeassistant import config_entries from homeassistant.components import speedtestdotnet from homeassistant.components.speedtestdotnet.const import ( - CONF_MANUAL, CONF_SERVER_ID, CONF_SERVER_NAME, DOMAIN, ) -from homeassistant.const import CONF_SCAN_INTERVAL from homeassistant.core import HomeAssistant +from homeassistant.data_entry_flow import FlowResultType from tests.common import MockConfigEntry @@ -21,13 +19,13 @@ async def test_flow_works(hass: HomeAssistant) -> None: result = await hass.config_entries.flow.async_init( speedtestdotnet.DOMAIN, context={"source": config_entries.SOURCE_USER} ) - assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["type"] == FlowResultType.FORM assert result["step_id"] == "user" result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={} ) - assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY + assert result["type"] == FlowResultType.CREATE_ENTRY async def test_options(hass: HomeAssistant, mock_api: MagicMock) -> None: @@ -42,68 +40,41 @@ async def test_options(hass: HomeAssistant, mock_api: MagicMock) -> None: await hass.async_block_till_done() result = await hass.config_entries.options.async_init(entry.entry_id) - assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["type"] == FlowResultType.FORM assert result["step_id"] == "init" result = await hass.config_entries.options.async_configure( result["flow_id"], user_input={ CONF_SERVER_NAME: "Country1 - Sponsor1 - Server1", - CONF_SCAN_INTERVAL: 30, - CONF_MANUAL: True, }, ) - assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY + assert result["type"] == FlowResultType.CREATE_ENTRY assert result["data"] == { CONF_SERVER_NAME: "Country1 - Sponsor1 - Server1", CONF_SERVER_ID: "1", - CONF_SCAN_INTERVAL: 30, - CONF_MANUAL: True, } await hass.async_block_till_done() - assert hass.data[DOMAIN].update_interval is None - # test setting server name to "*Auto Detect" result = await hass.config_entries.options.async_init(entry.entry_id) - assert result["type"] == data_entry_flow.FlowResultType.FORM + assert result["type"] == FlowResultType.FORM assert result["step_id"] == "init" result = await hass.config_entries.options.async_configure( result["flow_id"], user_input={ CONF_SERVER_NAME: "*Auto Detect", - CONF_SCAN_INTERVAL: 30, - CONF_MANUAL: True, }, ) - assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY + assert result["type"] == FlowResultType.CREATE_ENTRY assert result["data"] == { CONF_SERVER_NAME: "*Auto Detect", CONF_SERVER_ID: None, - CONF_SCAN_INTERVAL: 30, - CONF_MANUAL: True, } - # test setting the option to update periodically - result2 = await hass.config_entries.options.async_init(entry.entry_id) - assert result2["type"] == data_entry_flow.FlowResultType.FORM - assert result2["step_id"] == "init" - - result2 = await hass.config_entries.options.async_configure( - result2["flow_id"], - user_input={ - CONF_SERVER_NAME: "Country1 - Sponsor1 - Server1", - CONF_SCAN_INTERVAL: 30, - CONF_MANUAL: False, - }, - ) - await hass.async_block_till_done() - - assert hass.data[DOMAIN].update_interval == timedelta(minutes=30) - async def test_integration_already_configured(hass: HomeAssistant) -> None: """Test integration is already configured.""" @@ -114,5 +85,5 @@ async def test_integration_already_configured(hass: HomeAssistant) -> None: result = await hass.config_entries.flow.async_init( speedtestdotnet.DOMAIN, context={"source": config_entries.SOURCE_USER} ) - assert result["type"] == data_entry_flow.FlowResultType.ABORT + assert result["type"] == FlowResultType.ABORT assert result["reason"] == "single_instance_allowed" diff --git a/tests/components/speedtestdotnet/test_init.py b/tests/components/speedtestdotnet/test_init.py index 4b8071ad1ea..da19fd85dd3 100644 --- a/tests/components/speedtestdotnet/test_init.py +++ b/tests/components/speedtestdotnet/test_init.py @@ -6,13 +6,12 @@ from unittest.mock import MagicMock import speedtest from homeassistant.components.speedtestdotnet.const import ( - CONF_MANUAL, CONF_SERVER_ID, CONF_SERVER_NAME, DOMAIN, ) from homeassistant.config_entries import ConfigEntryState -from homeassistant.const import CONF_SCAN_INTERVAL, STATE_UNAVAILABLE +from homeassistant.const import STATE_UNAVAILABLE from homeassistant.core import HomeAssistant import homeassistant.util.dt as dt_util @@ -28,8 +27,6 @@ async def test_successful_config_entry(hass: HomeAssistant) -> None: options={ CONF_SERVER_NAME: "Country1 - Sponsor1 - Server1", CONF_SERVER_ID: "1", - CONF_SCAN_INTERVAL: 30, - CONF_MANUAL: False, }, ) entry.add_to_hass(hass) @@ -75,10 +72,7 @@ async def test_server_not_found(hass: HomeAssistant, mock_api: MagicMock) -> Non entry = MockConfigEntry( domain=DOMAIN, - options={ - CONF_MANUAL: False, - CONF_SCAN_INTERVAL: 60, - }, + options={}, ) entry.add_to_hass(hass) @@ -89,7 +83,7 @@ async def test_server_not_found(hass: HomeAssistant, mock_api: MagicMock) -> Non mock_api.return_value.get_servers.side_effect = speedtest.NoMatchedServers async_fire_time_changed( hass, - dt_util.utcnow() + timedelta(minutes=entry.options[CONF_SCAN_INTERVAL] + 1), + dt_util.utcnow() + timedelta(minutes=61), ) await hass.async_block_till_done() state = hass.states.get("sensor.speedtest_ping") diff --git a/tests/components/speedtestdotnet/test_sensor.py b/tests/components/speedtestdotnet/test_sensor.py index 06802a6cae7..68f14c64a69 100644 --- a/tests/components/speedtestdotnet/test_sensor.py +++ b/tests/components/speedtestdotnet/test_sensor.py @@ -1,13 +1,10 @@ """Tests for SpeedTest sensors.""" from unittest.mock import MagicMock -from homeassistant.components import speedtestdotnet from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN -from homeassistant.components.speedtestdotnet.const import ( - CONF_MANUAL, - DEFAULT_NAME, - SENSOR_TYPES, -) +from homeassistant.components.speedtestdotnet import DOMAIN +from homeassistant.components.speedtestdotnet.const import DEFAULT_NAME +from homeassistant.components.speedtestdotnet.sensor import SENSOR_TYPES from homeassistant.core import HomeAssistant, State from . import MOCK_RESULTS, MOCK_SERVERS, MOCK_STATES @@ -19,7 +16,7 @@ async def test_speedtestdotnet_sensors( hass: HomeAssistant, mock_api: MagicMock ) -> None: """Test sensors created for speedtestdotnet integration.""" - entry = MockConfigEntry(domain=speedtestdotnet.DOMAIN, data={}) + entry = MockConfigEntry(domain=DOMAIN, data={}) entry.add_to_hass(hass) mock_api.return_value.get_best_server.return_value = MOCK_SERVERS[1][0] @@ -45,9 +42,7 @@ async def test_restore_last_state(hass: HomeAssistant, mock_api: MagicMock) -> N for sensor, state in MOCK_STATES.items() ], ) - entry = MockConfigEntry( - domain=speedtestdotnet.DOMAIN, data={}, options={CONF_MANUAL: True} - ) + entry = MockConfigEntry(domain=DOMAIN) entry.add_to_hass(hass) await hass.config_entries.async_setup(entry.entry_id) diff --git a/tests/components/ssdp/test_init.py b/tests/components/ssdp/test_init.py index 4076ef4685d..959a35dba78 100644 --- a/tests/components/ssdp/test_init.py +++ b/tests/components/ssdp/test_init.py @@ -11,7 +11,6 @@ from async_upnp_client.ssdp_listener import SsdpListener from async_upnp_client.utils import CaseInsensitiveDict import pytest -import homeassistant from homeassistant import config_entries from homeassistant.components import ssdp from homeassistant.const import ( @@ -19,6 +18,7 @@ from homeassistant.const import ( EVENT_HOMEASSISTANT_STOP, MATCH_ALL, ) +from homeassistant.core import HomeAssistant from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util @@ -31,7 +31,7 @@ def _ssdp_headers(headers): return ssdp_headers -async def init_ssdp_component(hass: homeassistant) -> SsdpListener: +async def init_ssdp_component(hass: HomeAssistant) -> SsdpListener: """Initialize ssdp component and get SsdpListener.""" await async_setup_component(hass, ssdp.DOMAIN, {ssdp.DOMAIN: {}}) await hass.async_block_till_done() @@ -55,7 +55,7 @@ async def test_ssdp_flow_dispatched_on_st(mock_get_ssdp, hass, caplog, mock_flow } ) ssdp_listener = await init_ssdp_component(hass) - await ssdp_listener._on_search(mock_ssdp_search_response) + ssdp_listener._on_search(mock_ssdp_search_response) await hass.async_block_till_done() hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED) await hass.async_block_till_done() @@ -98,7 +98,7 @@ async def test_ssdp_flow_dispatched_on_manufacturer_url( } ) ssdp_listener = await init_ssdp_component(hass) - await ssdp_listener._on_search(mock_ssdp_search_response) + ssdp_listener._on_search(mock_ssdp_search_response) await hass.async_block_till_done() hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED) await hass.async_block_till_done() @@ -148,7 +148,7 @@ async def test_scan_match_upnp_devicedesc_manufacturer( } ) ssdp_listener = await init_ssdp_component(hass) - await ssdp_listener._on_search(mock_ssdp_search_response) + ssdp_listener._on_search(mock_ssdp_search_response) await hass.async_block_till_done() hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED) await hass.async_block_till_done() @@ -189,7 +189,7 @@ async def test_scan_match_upnp_devicedesc_devicetype( } ) ssdp_listener = await init_ssdp_component(hass) - await ssdp_listener._on_search(mock_ssdp_search_response) + ssdp_listener._on_search(mock_ssdp_search_response) await hass.async_block_till_done() hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED) @@ -237,7 +237,7 @@ async def test_scan_not_all_present( } ) ssdp_listener = await init_ssdp_component(hass) - await ssdp_listener._on_search(mock_ssdp_search_response) + ssdp_listener._on_search(mock_ssdp_search_response) await hass.async_block_till_done() hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED) await hass.async_block_till_done() @@ -278,7 +278,7 @@ async def test_scan_not_all_match(mock_get_ssdp, hass, aioclient_mock, mock_flow } ) ssdp_listener = await init_ssdp_component(hass) - await ssdp_listener._on_search(mock_ssdp_search_response) + ssdp_listener._on_search(mock_ssdp_search_response) await hass.async_block_till_done() hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED) await hass.async_block_till_done() @@ -317,7 +317,7 @@ async def test_flow_start_only_alive( "usn": "uuid:mock-udn::mock-st", } ) - await ssdp_listener._on_search(mock_ssdp_search_response) + ssdp_listener._on_search(mock_ssdp_search_response) await hass.async_block_till_done() mock_flow_init.assert_awaited_once_with( @@ -334,7 +334,7 @@ async def test_flow_start_only_alive( "nts": "ssdp:alive", } ) - await ssdp_listener._on_alive(mock_ssdp_advertisement) + ssdp_listener._on_alive(mock_ssdp_advertisement) await hass.async_block_till_done() mock_flow_init.assert_awaited_once_with( "mock-domain", context={"source": config_entries.SOURCE_SSDP}, data=ANY @@ -343,14 +343,14 @@ async def test_flow_start_only_alive( # ssdp:byebye advertisement should not start a flow mock_flow_init.reset_mock() mock_ssdp_advertisement["nts"] = "ssdp:byebye" - await ssdp_listener._on_byebye(mock_ssdp_advertisement) + ssdp_listener._on_byebye(mock_ssdp_advertisement) await hass.async_block_till_done() mock_flow_init.assert_not_called() # ssdp:update advertisement should start a flow mock_flow_init.reset_mock() mock_ssdp_advertisement["nts"] = "ssdp:update" - await ssdp_listener._on_update(mock_ssdp_advertisement) + ssdp_listener._on_update(mock_ssdp_advertisement) await hass.async_block_till_done() mock_flow_init.assert_awaited_once_with( "mock-domain", context={"source": config_entries.SOURCE_SSDP}, data=ANY @@ -388,7 +388,7 @@ async def test_discovery_from_advertisement_sets_ssdp_st( "usn": "uuid:mock-udn::mock-st", } ) - await ssdp_listener._on_alive(mock_ssdp_advertisement) + ssdp_listener._on_alive(mock_ssdp_advertisement) await hass.async_block_till_done() discovery_info = await ssdp.async_get_discovery_info_by_udn(hass, "uuid:mock-udn") @@ -469,34 +469,35 @@ async def test_scan_with_registered_callback( hass, async_integration_callback, {"st": "mock-st"} ) - async_integration_match_all_callback1 = AsyncMock() + async_integration_match_all_callback = AsyncMock() await ssdp.async_register_callback( - hass, async_integration_match_all_callback1, {"x-rincon-bootseq": MATCH_ALL} + hass, async_integration_match_all_callback, {"x-rincon-bootseq": MATCH_ALL} ) - async_integration_match_all_not_present_callback1 = AsyncMock() + async_integration_match_all_not_present_callback = AsyncMock() await ssdp.async_register_callback( hass, - async_integration_match_all_not_present_callback1, + async_integration_match_all_not_present_callback, {"x-not-there": MATCH_ALL}, ) - async_not_matching_integration_callback1 = AsyncMock() + async_not_matching_integration_callback = AsyncMock() await ssdp.async_register_callback( - hass, async_not_matching_integration_callback1, {"st": "not-match-mock-st"} + hass, async_not_matching_integration_callback, {"st": "not-match-mock-st"} ) - async_match_any_callback1 = AsyncMock() - await ssdp.async_register_callback(hass, async_match_any_callback1) + async_match_any_callback = AsyncMock() + await ssdp.async_register_callback(hass, async_match_any_callback) await hass.async_block_till_done() - await ssdp_listener._on_search(mock_ssdp_search_response) + ssdp_listener._on_search(mock_ssdp_search_response) + await hass.async_block_till_done() assert async_integration_callback.call_count == 1 - assert async_integration_match_all_callback1.call_count == 1 - assert async_integration_match_all_not_present_callback1.call_count == 0 - assert async_match_any_callback1.call_count == 1 - assert async_not_matching_integration_callback1.call_count == 0 + assert async_integration_match_all_callback.call_count == 1 + assert async_integration_match_all_not_present_callback.call_count == 0 + assert async_match_any_callback.call_count == 1 + assert async_not_matching_integration_callback.call_count == 0 assert async_integration_callback.call_args[0][1] == ssdp.SsdpChange.ALIVE mock_call_data: ssdp.SsdpServiceInfo = async_integration_callback.call_args[0][0] assert mock_call_data.ssdp_ext == "" @@ -552,7 +553,7 @@ async def test_getting_existing_headers( } ) ssdp_listener = await init_ssdp_component(hass) - await ssdp_listener._on_search(mock_ssdp_search_response) + ssdp_listener._on_search(mock_ssdp_search_response) discovery_info_by_st = await ssdp.async_get_discovery_info_by_st(hass, "mock-st") discovery_info_by_st = discovery_info_by_st[0] diff --git a/tests/components/switchbee/test_config_flow.py b/tests/components/switchbee/test_config_flow.py index 8b9637a7695..1745d6c5d40 100644 --- a/tests/components/switchbee/test_config_flow.py +++ b/tests/components/switchbee/test_config_flow.py @@ -25,15 +25,15 @@ async def test_form(hass): assert result["errors"] == {} with patch( - "switchbee.api.CentralUnitAPI.get_configuration", + "switchbee.api.polling.CentralUnitPolling.get_configuration", return_value=coordinator_data, ), patch( "homeassistant.components.switchbee.async_setup_entry", return_value=True, ), patch( - "switchbee.api.CentralUnitAPI.fetch_states", return_value=None + "switchbee.api.polling.CentralUnitPolling.fetch_states", return_value=None ), patch( - "switchbee.api.CentralUnitAPI._login", return_value=None + "switchbee.api.polling.CentralUnitPolling._login", return_value=None ): result2 = await hass.config_entries.flow.async_configure( @@ -62,7 +62,7 @@ async def test_form_invalid_auth(hass: HomeAssistant) -> None: ) with patch( - "switchbee.api.CentralUnitAPI._login", + "switchbee.api.polling.CentralUnitPolling._login", side_effect=SwitchBeeError(MOCK_FAILED_TO_LOGIN_MSG), ): result2 = await hass.config_entries.flow.async_configure( @@ -86,7 +86,7 @@ async def test_form_cannot_connect(hass: HomeAssistant) -> None: ) with patch( - "switchbee.api.CentralUnitAPI._login", + "switchbee.api.polling.CentralUnitPolling._login", side_effect=SwitchBeeError(MOCK_INVALID_TOKEN_MGS), ): result2 = await hass.config_entries.flow.async_configure( @@ -109,7 +109,7 @@ async def test_form_unknown_error(hass): ) with patch( - "switchbee.api.CentralUnitAPI._login", + "switchbee.api.polling.CentralUnitPolling._login", side_effect=Exception, ): form_result = await hass.config_entries.flow.async_configure( @@ -144,14 +144,16 @@ async def test_form_entry_exists(hass): DOMAIN, context={"source": config_entries.SOURCE_USER} ) - with patch("switchbee.api.CentralUnitAPI._login", return_value=None), patch( + with patch( + "switchbee.api.polling.CentralUnitPolling._login", return_value=None + ), patch( "homeassistant.components.switchbee.async_setup_entry", return_value=True, ), patch( - "switchbee.api.CentralUnitAPI.get_configuration", + "switchbee.api.polling.CentralUnitPolling.get_configuration", return_value=coordinator_data, ), patch( - "switchbee.api.CentralUnitAPI.fetch_states", return_value=None + "switchbee.api.polling.CentralUnitPolling.fetch_states", return_value=None ): form_result = await hass.config_entries.flow.async_configure( result["flow_id"], diff --git a/tests/components/switchbot/__init__.py b/tests/components/switchbot/__init__.py index d5216cf6262..ce39579915f 100644 --- a/tests/components/switchbot/__init__.py +++ b/tests/components/switchbot/__init__.py @@ -168,6 +168,26 @@ WOSENSORTH_SERVICE_INFO = BluetoothServiceInfoBleak( connectable=False, ) + +WOLOCK_SERVICE_INFO = BluetoothServiceInfoBleak( + name="WoLock", + manufacturer_data={2409: b"\xf1\t\x9fE\x1a]\xda\x83\x00 "}, + service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b"o\x80d"}, + service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"], + address="aa:bb:cc:dd:ee:ff", + rssi=-60, + source="local", + advertisement=generate_advertisement_data( + local_name="WoLock", + manufacturer_data={2409: b"\xf1\t\x9fE\x1a]\xda\x83\x00 "}, + service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b"o\x80d"}, + service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"], + ), + device=BLEDevice("aa:bb:cc:dd:ee:ff", "WoLock"), + time=0, + connectable=True, +) + NOT_SWITCHBOT_INFO = BluetoothServiceInfoBleak( name="unknown", service_uuids=[], diff --git a/tests/components/switchbot/test_config_flow.py b/tests/components/switchbot/test_config_flow.py index 66d0874809f..1a3db48f192 100644 --- a/tests/components/switchbot/test_config_flow.py +++ b/tests/components/switchbot/test_config_flow.py @@ -2,9 +2,21 @@ from unittest.mock import patch -from homeassistant.components.switchbot.const import CONF_RETRY_COUNT +from switchbot import SwitchbotAccountConnectionError, SwitchbotAuthenticationError + +from homeassistant.components.switchbot.const import ( + CONF_ENCRYPTION_KEY, + CONF_KEY_ID, + CONF_RETRY_COUNT, +) from homeassistant.config_entries import SOURCE_BLUETOOTH, SOURCE_USER -from homeassistant.const import CONF_ADDRESS, CONF_NAME, CONF_PASSWORD, CONF_SENSOR_TYPE +from homeassistant.const import ( + CONF_ADDRESS, + CONF_NAME, + CONF_PASSWORD, + CONF_SENSOR_TYPE, + CONF_USERNAME, +) from homeassistant.data_entry_flow import FlowResultType from . import ( @@ -15,6 +27,7 @@ from . import ( WOHAND_SERVICE_ALT_ADDRESS_INFO, WOHAND_SERVICE_INFO, WOHAND_SERVICE_INFO_NOT_CONNECTABLE, + WOLOCK_SERVICE_INFO, WOSENSORTH_SERVICE_INFO, init_integration, patch_async_setup_entry, @@ -80,6 +93,66 @@ async def test_bluetooth_discovery_requires_password(hass): assert len(mock_setup_entry.mock_calls) == 1 +async def test_bluetooth_discovery_lock_key(hass): + """Test discovery via bluetooth with a lock.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_BLUETOOTH}, + data=WOLOCK_SERVICE_INFO, + ) + assert result["type"] == FlowResultType.MENU + assert result["step_id"] == "lock_choose_method" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={"next_step_id": "lock_key"} + ) + await hass.async_block_till_done() + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "lock_key" + assert result["errors"] == {} + + with patch( + "homeassistant.components.switchbot.config_flow.SwitchbotLock.verify_encryption_key", + return_value=False, + ): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_KEY_ID: "", + CONF_ENCRYPTION_KEY: "", + }, + ) + await hass.async_block_till_done() + + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "lock_key" + assert result["errors"] == {"base": "encryption_key_invalid"} + + with patch_async_setup_entry() as mock_setup_entry, patch( + "homeassistant.components.switchbot.config_flow.SwitchbotLock.verify_encryption_key", + return_value=True, + ): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_KEY_ID: "ff", + CONF_ENCRYPTION_KEY: "ffffffffffffffffffffffffffffffff", + }, + ) + await hass.async_block_till_done() + + assert result["type"] == FlowResultType.CREATE_ENTRY + assert result["title"] == "Lock EEFF" + assert result["data"] == { + CONF_ADDRESS: "aa:bb:cc:dd:ee:ff", + CONF_KEY_ID: "ff", + CONF_ENCRYPTION_KEY: "ffffffffffffffffffffffffffffffff", + CONF_SENSOR_TYPE: "lock", + } + + assert len(mock_setup_entry.mock_calls) == 1 + + async def test_bluetooth_discovery_already_setup(hass): """Test discovery via bluetooth with a valid device when already setup.""" entry = MockConfigEntry( @@ -322,6 +395,232 @@ async def test_user_setup_single_bot_with_password(hass): assert len(mock_setup_entry.mock_calls) == 1 +async def test_user_setup_wolock_key(hass): + """Test the user initiated form for a lock.""" + + with patch( + "homeassistant.components.switchbot.config_flow.async_discovered_service_info", + return_value=[WOLOCK_SERVICE_INFO], + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_USER} + ) + assert result["type"] == FlowResultType.MENU + assert result["step_id"] == "lock_choose_method" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={"next_step_id": "lock_key"} + ) + await hass.async_block_till_done() + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "lock_key" + assert result["errors"] == {} + + with patch( + "homeassistant.components.switchbot.config_flow.SwitchbotLock.verify_encryption_key", + return_value=False, + ): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_KEY_ID: "", + CONF_ENCRYPTION_KEY: "", + }, + ) + await hass.async_block_till_done() + + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "lock_key" + assert result["errors"] == {"base": "encryption_key_invalid"} + + with patch_async_setup_entry() as mock_setup_entry, patch( + "homeassistant.components.switchbot.config_flow.SwitchbotLock.verify_encryption_key", + return_value=True, + ): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_KEY_ID: "ff", + CONF_ENCRYPTION_KEY: "ffffffffffffffffffffffffffffffff", + }, + ) + await hass.async_block_till_done() + + assert result["type"] == FlowResultType.CREATE_ENTRY + assert result["title"] == "Lock EEFF" + assert result["data"] == { + CONF_ADDRESS: "aa:bb:cc:dd:ee:ff", + CONF_KEY_ID: "ff", + CONF_ENCRYPTION_KEY: "ffffffffffffffffffffffffffffffff", + CONF_SENSOR_TYPE: "lock", + } + + assert len(mock_setup_entry.mock_calls) == 1 + + +async def test_user_setup_wolock_auth(hass): + """Test the user initiated form for a lock.""" + + with patch( + "homeassistant.components.switchbot.config_flow.async_discovered_service_info", + return_value=[WOLOCK_SERVICE_INFO], + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_USER} + ) + assert result["type"] == FlowResultType.MENU + assert result["step_id"] == "lock_choose_method" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={"next_step_id": "lock_auth"} + ) + await hass.async_block_till_done() + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "lock_auth" + assert result["errors"] == {} + + with patch( + "homeassistant.components.switchbot.config_flow.SwitchbotLock.retrieve_encryption_key", + side_effect=SwitchbotAuthenticationError, + ): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_USERNAME: "", + CONF_PASSWORD: "", + }, + ) + await hass.async_block_till_done() + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "lock_auth" + assert result["errors"] == {"base": "auth_failed"} + + with patch_async_setup_entry() as mock_setup_entry, patch( + "homeassistant.components.switchbot.config_flow.SwitchbotLock.verify_encryption_key", + return_value=True, + ), patch( + "homeassistant.components.switchbot.config_flow.SwitchbotLock.retrieve_encryption_key", + return_value={ + CONF_KEY_ID: "ff", + CONF_ENCRYPTION_KEY: "ffffffffffffffffffffffffffffffff", + }, + ): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_USERNAME: "username", + CONF_PASSWORD: "password", + }, + ) + await hass.async_block_till_done() + + assert result["type"] == FlowResultType.CREATE_ENTRY + assert result["title"] == "Lock EEFF" + assert result["data"] == { + CONF_ADDRESS: "aa:bb:cc:dd:ee:ff", + CONF_KEY_ID: "ff", + CONF_ENCRYPTION_KEY: "ffffffffffffffffffffffffffffffff", + CONF_SENSOR_TYPE: "lock", + } + + assert len(mock_setup_entry.mock_calls) == 1 + + +async def test_user_setup_wolock_auth_switchbot_api_down(hass): + """Test the user initiated form for a lock when the switchbot api is down.""" + + with patch( + "homeassistant.components.switchbot.config_flow.async_discovered_service_info", + return_value=[WOLOCK_SERVICE_INFO], + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_USER} + ) + assert result["type"] == FlowResultType.MENU + assert result["step_id"] == "lock_choose_method" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={"next_step_id": "lock_auth"} + ) + await hass.async_block_till_done() + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "lock_auth" + assert result["errors"] == {} + + with patch( + "homeassistant.components.switchbot.config_flow.SwitchbotLock.retrieve_encryption_key", + side_effect=SwitchbotAccountConnectionError, + ): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_USERNAME: "", + CONF_PASSWORD: "", + }, + ) + await hass.async_block_till_done() + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "cannot_connect" + + +async def test_user_setup_wolock_or_bot(hass): + """Test the user initiated form for a lock.""" + + with patch( + "homeassistant.components.switchbot.config_flow.async_discovered_service_info", + return_value=[ + WOLOCK_SERVICE_INFO, + WOHAND_SERVICE_ALT_ADDRESS_INFO, + ], + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_USER} + ) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "user" + assert result["errors"] == {} + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + USER_INPUT, + ) + await hass.async_block_till_done() + assert result["type"] == FlowResultType.MENU + assert result["step_id"] == "lock_choose_method" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={"next_step_id": "lock_key"} + ) + await hass.async_block_till_done() + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "lock_key" + assert result["errors"] == {} + + with patch_async_setup_entry() as mock_setup_entry, patch( + "homeassistant.components.switchbot.config_flow.SwitchbotLock.verify_encryption_key", + return_value=True, + ): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_KEY_ID: "ff", + CONF_ENCRYPTION_KEY: "ffffffffffffffffffffffffffffffff", + }, + ) + await hass.async_block_till_done() + + assert result["type"] == FlowResultType.CREATE_ENTRY + assert result["title"] == "Lock EEFF" + assert result["data"] == { + CONF_ADDRESS: "aa:bb:cc:dd:ee:ff", + CONF_KEY_ID: "ff", + CONF_ENCRYPTION_KEY: "ffffffffffffffffffffffffffffffff", + CONF_SENSOR_TYPE: "lock", + } + + assert len(mock_setup_entry.mock_calls) == 1 + + async def test_user_setup_wosensor(hass): """Test the user initiated form with password and valid mac.""" with patch( diff --git a/tests/components/tag/test_trigger.py b/tests/components/tag/test_trigger.py index 3976c3193d1..379319be502 100644 --- a/tests/components/tag/test_trigger.py +++ b/tests/components/tag/test_trigger.py @@ -101,7 +101,7 @@ async def test_exception_bad_trigger(hass, calls, caplog): }, ) await hass.async_block_till_done() - assert "Invalid config for [automation]" in caplog.text + assert "Unnamed automation could not be validated" in caplog.text async def test_multiple_tags_and_devices_trigger(hass, tag_setup, calls): diff --git a/tests/components/tankerkoenig/test_config_flow.py b/tests/components/tankerkoenig/test_config_flow.py index 600bfd98c73..266f9b67376 100644 --- a/tests/components/tankerkoenig/test_config_flow.py +++ b/tests/components/tankerkoenig/test_config_flow.py @@ -8,7 +8,7 @@ from homeassistant.components.tankerkoenig.const import ( CONF_STATIONS, DOMAIN, ) -from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_REAUTH, SOURCE_USER +from homeassistant.config_entries import SOURCE_REAUTH, SOURCE_USER from homeassistant.const import ( CONF_API_KEY, CONF_LATITUDE, @@ -47,18 +47,6 @@ MOCK_OPTIONS_DATA = { ], } -MOCK_IMPORT_DATA = { - CONF_API_KEY: "269534f6-xxxx-xxxx-xxxx-yyyyzzzzxxxx", - CONF_FUEL_TYPES: ["e5"], - CONF_LOCATION: {CONF_LATITUDE: 51.0, CONF_LONGITUDE: 13.0}, - CONF_RADIUS: 2.0, - CONF_STATIONS: [ - "3bcd61da-yyyy-yyyy-yyyy-19d5523a7ae8", - "36b4b812-yyyy-yyyy-yyyy-c51735325858", - ], - CONF_SHOW_ON_MAP: True, -} - MOCK_NEARVY_STATIONS_OK = { "ok": True, "stations": [ @@ -187,37 +175,6 @@ async def test_user_no_stations(hass: HomeAssistant): assert result["errors"][CONF_RADIUS] == "no_stations" -async def test_import(hass: HomeAssistant): - """Test starting a flow by import.""" - with patch( - "homeassistant.components.tankerkoenig.async_setup_entry", return_value=True - ) as mock_setup_entry, patch( - "homeassistant.components.tankerkoenig.config_flow.getNearbyStations", - return_value=MOCK_NEARVY_STATIONS_OK, - ): - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_IMPORT}, data=MOCK_IMPORT_DATA - ) - assert result["type"] == FlowResultType.CREATE_ENTRY - assert result["type"] == FlowResultType.CREATE_ENTRY - assert result["data"][CONF_NAME] == "Home" - assert result["data"][CONF_API_KEY] == "269534f6-xxxx-xxxx-xxxx-yyyyzzzzxxxx" - assert result["data"][CONF_FUEL_TYPES] == ["e5"] - assert result["data"][CONF_LOCATION] == {"latitude": 51.0, "longitude": 13.0} - assert result["data"][CONF_RADIUS] == 2.0 - assert result["data"][CONF_STATIONS] == [ - "3bcd61da-xxxx-xxxx-xxxx-19d5523a7ae8", - "36b4b812-xxxx-xxxx-xxxx-c51735325858", - "3bcd61da-yyyy-yyyy-yyyy-19d5523a7ae8", - "36b4b812-yyyy-yyyy-yyyy-c51735325858", - ] - assert result["options"][CONF_SHOW_ON_MAP] - - await hass.async_block_till_done() - - assert mock_setup_entry.called - - async def test_reauth(hass: HomeAssistant): """Test starting a flow by user to re-auth.""" diff --git a/tests/components/tasmota/test_fan.py b/tests/components/tasmota/test_fan.py index 8c54e913f7c..2d2cb93406b 100644 --- a/tests/components/tasmota/test_fan.py +++ b/tests/components/tasmota/test_fan.py @@ -154,6 +154,33 @@ async def test_sending_mqtt_commands(hass, mqtt_mock, setup_tasmota): "tasmota_49A3BC/cmnd/FanSpeed", "3", 0, False ) + # Test the last known fan speed is restored + # First, get a fan speed update + async_fire_mqtt_message(hass, "tasmota_49A3BC/tele/STATE", '{"FanSpeed":3}') + state = hass.states.get("fan.tasmota") + assert state.state == STATE_ON + assert state.attributes["percentage"] == 100 + mqtt_mock.async_publish.reset_mock() + + # Then turn the fan off and get a fan state update + await common.async_turn_off(hass, "fan.tasmota") + mqtt_mock.async_publish.assert_called_once_with( + "tasmota_49A3BC/cmnd/FanSpeed", "0", 0, False + ) + mqtt_mock.async_publish.reset_mock() + async_fire_mqtt_message(hass, "tasmota_49A3BC/stat/RESULT", '{"FanSpeed":0}') + state = hass.states.get("fan.tasmota") + assert state.state == STATE_OFF + assert state.attributes["percentage"] == 0 + mqtt_mock.async_publish.reset_mock() + + # Finally, turn the fan on again and verify MQTT message is sent with last known speed + await common.async_turn_on(hass, "fan.tasmota") + mqtt_mock.async_publish.assert_called_once_with( + "tasmota_49A3BC/cmnd/FanSpeed", "3", 0, False + ) + mqtt_mock.async_publish.reset_mock() + async def test_invalid_fan_speed_percentage(hass, mqtt_mock, setup_tasmota): """Test the sending MQTT commands.""" diff --git a/tests/components/template/test_sensor.py b/tests/components/template/test_sensor.py index 44fa96cfc6a..19d43f08d2b 100644 --- a/tests/components/template/test_sensor.py +++ b/tests/components/template/test_sensor.py @@ -292,6 +292,7 @@ async def test_template_attribute_missing(hass, start_ha): "sensors": { "test1": { "value_template": "{{ states.sensor.test_sensor.state }}", + "unit_of_measurement": "°C", "device_class": "temperature", }, "test2": { diff --git a/tests/components/tibber/test_diagnostics.py b/tests/components/tibber/test_diagnostics.py index 38b5eb91a2f..78c0b6e321f 100644 --- a/tests/components/tibber/test_diagnostics.py +++ b/tests/components/tibber/test_diagnostics.py @@ -25,7 +25,7 @@ async def test_entry_diagnostics(recorder_mock, hass, hass_client, config_entry) result = await get_diagnostics_for_config_entry(hass, hass_client, config_entry) assert result == { - "homes": {}, + "homes": [], } with patch( @@ -35,13 +35,13 @@ async def test_entry_diagnostics(recorder_mock, hass, hass_client, config_entry) result = await get_diagnostics_for_config_entry(hass, hass_client, config_entry) assert result == { - "homes": { - "home_id": { + "homes": [ + { "last_data_timestamp": "2016-01-01T12:48:57", "has_active_subscription": True, "has_real_time_consumption": False, "last_cons_data_timestamp": "2016-01-01T12:44:57", "country": "NO", } - }, + ], } diff --git a/tests/components/todoist/test_calendar.py b/tests/components/todoist/test_calendar.py index 3458a7bf5d3..b08d96a6b92 100644 --- a/tests/components/todoist/test_calendar.py +++ b/tests/components/todoist/test_calendar.py @@ -57,7 +57,7 @@ def mock_state() -> dict[str, Any]: return { "collaborators": [], "labels": [{"name": "label1", "id": 1}], - "projects": [{"id": 12345, "name": "Name"}], + "projects": [{"id": "12345", "name": "Name"}], } @@ -80,7 +80,7 @@ async def test_calendar_entity_unique_id(todoist_api, hass, state): registry = entity_registry.async_get(hass) entity = registry.async_get("calendar.name") - assert 12345 == entity.unique_id + assert "12345" == entity.unique_id @patch("homeassistant.components.todoist.calendar.TodoistAPI") diff --git a/tests/components/tomorrowio/test_sensor.py b/tests/components/tomorrowio/test_sensor.py index 7721e5d36ac..a93a551ae03 100644 --- a/tests/components/tomorrowio/test_sensor.py +++ b/tests/components/tomorrowio/test_sensor.py @@ -192,7 +192,7 @@ async def test_v4_sensor_imperial(hass: HomeAssistant) -> None: check_sensor_state(hass, TREE_POLLEN, "none") check_sensor_state(hass, FEELS_LIKE, "214.3") check_sensor_state(hass, DEW_POINT, "163.08") - check_sensor_state(hass, PRESSURE_SURFACE_LEVEL, "29.47") + check_sensor_state(hass, PRESSURE_SURFACE_LEVEL, "0.427") check_sensor_state(hass, GHI, "0.0") check_sensor_state(hass, CLOUD_BASE, "0.46") check_sensor_state(hass, CLOUD_COVER, "100") diff --git a/tests/components/tradfri/test_sensor.py b/tests/components/tradfri/test_sensor.py index 1cd7a3672bc..6408613f4e3 100644 --- a/tests/components/tradfri/test_sensor.py +++ b/tests/components/tradfri/test_sensor.py @@ -71,6 +71,7 @@ async def test_cover_battery_sensor(hass, mock_gateway, mock_api_factory): assert sensor_1.state == "42" assert sensor_1.attributes["unit_of_measurement"] == "%" assert sensor_1.attributes["device_class"] == "battery" + assert sensor_1.attributes["state_class"] == "measurement" async def test_air_quality_sensor(hass, mock_gateway, mock_api_factory): @@ -91,6 +92,7 @@ async def test_air_quality_sensor(hass, mock_gateway, mock_api_factory): assert sensor_1.state == "42" assert sensor_1.attributes["unit_of_measurement"] == "µg/m³" assert sensor_1.attributes["device_class"] == "aqi" + assert sensor_1.attributes["state_class"] == "measurement" async def test_filter_time_left_sensor(hass, mock_gateway, mock_api_factory): diff --git a/tests/components/transmission/test_config_flow.py b/tests/components/transmission/test_config_flow.py index 44edc4b28a9..7b1a1a29696 100644 --- a/tests/components/transmission/test_config_flow.py +++ b/tests/components/transmission/test_config_flow.py @@ -2,7 +2,7 @@ from unittest.mock import MagicMock, patch import pytest -from transmissionrpc.error import TransmissionError +from transmission_rpc.error import TransmissionError from homeassistant import config_entries from homeassistant.components import transmission @@ -18,7 +18,7 @@ from tests.common import MockConfigEntry @pytest.fixture(autouse=True) def mock_api(): """Mock an api.""" - with patch("transmissionrpc.Client") as api: + with patch("transmission_rpc.Client") as api: yield api diff --git a/tests/components/transmission/test_init.py b/tests/components/transmission/test_init.py index 60e2d67d75c..da5e6859544 100644 --- a/tests/components/transmission/test_init.py +++ b/tests/components/transmission/test_init.py @@ -3,7 +3,7 @@ from unittest.mock import MagicMock, patch import pytest -from transmissionrpc.error import TransmissionError +from transmission_rpc.error import TransmissionError from homeassistant.components.transmission.const import DOMAIN from homeassistant.config_entries import ConfigEntryState @@ -17,7 +17,7 @@ from tests.common import MockConfigEntry @pytest.fixture(autouse=True) def mock_api(): """Mock an api.""" - with patch("transmissionrpc.Client") as api: + with patch("transmission_rpc.Client") as api: yield api diff --git a/tests/components/unifi/test_sensor.py b/tests/components/unifi/test_sensor.py index 100918a93da..ebdea40fe73 100644 --- a/tests/components/unifi/test_sensor.py +++ b/tests/components/unifi/test_sensor.py @@ -13,10 +13,8 @@ from homeassistant.components.unifi.const import ( CONF_ALLOW_UPTIME_SENSORS, CONF_TRACK_CLIENTS, CONF_TRACK_DEVICES, - DOMAIN as UNIFI_DOMAIN, ) from homeassistant.helpers import entity_registry as er -from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.entity import EntityCategory import homeassistant.util.dt as dt_util @@ -106,37 +104,6 @@ async def test_bandwidth_sensors(hass, aioclient_mock, mock_unifi_websocket): assert hass.states.get("sensor.wired_client_rx") is None assert hass.states.get("sensor.wired_client_tx") is None - # Enable option - - options[CONF_ALLOW_BANDWIDTH_SENSORS] = True - hass.config_entries.async_update_entry(config_entry, options=options.copy()) - await hass.async_block_till_done() - - assert len(hass.states.async_all()) == 5 - assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 4 - assert hass.states.get("sensor.wireless_client_rx") - assert hass.states.get("sensor.wireless_client_tx") - assert hass.states.get("sensor.wired_client_rx") - assert hass.states.get("sensor.wired_client_tx") - - # Try to add the sensors again, using a signal - - clients_connected = {wired_client["mac"], wireless_client["mac"]} - devices_connected = set() - - controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id] - - async_dispatcher_send( - hass, - controller.signal_update, - clients_connected, - devices_connected, - ) - await hass.async_block_till_done() - - assert len(hass.states.async_all()) == 5 - assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 4 - @pytest.mark.parametrize( "initial_uptime,event_uptime,new_uptime", @@ -220,35 +187,6 @@ async def test_uptime_sensors( assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 0 assert hass.states.get("sensor.client1_uptime") is None - # Enable option - - options[CONF_ALLOW_UPTIME_SENSORS] = True - with patch("homeassistant.util.dt.now", return_value=now): - hass.config_entries.async_update_entry(config_entry, options=options.copy()) - await hass.async_block_till_done() - - assert len(hass.states.async_all()) == 2 - assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 1 - assert hass.states.get("sensor.client1_uptime") - - # Try to add the sensors again, using a signal - - clients_connected = {uptime_client["mac"]} - devices_connected = set() - - controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id] - - async_dispatcher_send( - hass, - controller.signal_update, - clients_connected, - devices_connected, - ) - await hass.async_block_till_done() - - assert len(hass.states.async_all()) == 2 - assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 1 - async def test_remove_sensors(hass, aioclient_mock, mock_unifi_websocket): """Verify removing of clients work as expected.""" diff --git a/tests/components/unifiprotect/test_repairs.py b/tests/components/unifiprotect/test_repairs.py index 3ffd2ea4a43..1d1d7315aac 100644 --- a/tests/components/unifiprotect/test_repairs.py +++ b/tests/components/unifiprotect/test_repairs.py @@ -4,10 +4,11 @@ from __future__ import annotations from copy import copy from http import HTTPStatus -from unittest.mock import Mock +from unittest.mock import Mock, patch -from pyunifiprotect.data import Version +from pyunifiprotect.data import Camera, Version +from homeassistant.components.automation import DOMAIN as AUTOMATION_DOMAIN from homeassistant.components.repairs.issue_handler import ( async_process_repairs_platforms, ) @@ -15,8 +16,12 @@ from homeassistant.components.repairs.websocket_api import ( RepairsFlowIndexView, RepairsFlowResourceView, ) +from homeassistant.components.script import DOMAIN as SCRIPT_DOMAIN from homeassistant.components.unifiprotect.const import DOMAIN +from homeassistant.const import SERVICE_RELOAD, Platform from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er +from homeassistant.setup import async_setup_component from .utils import MockUFPFixture, init_entry @@ -124,3 +129,198 @@ async def test_ea_warning_fix( data = await resp.json() assert data["type"] == "create_entry" + + +async def test_deprecate_smart_default( + hass: HomeAssistant, ufp: MockUFPFixture, hass_ws_client, doorbell: Camera +): + """Test Deprecate Sensor repair does not exist by default (new installs).""" + + await init_entry(hass, ufp, [doorbell]) + + await async_process_repairs_platforms(hass) + ws_client = await hass_ws_client(hass) + + await ws_client.send_json({"id": 1, "type": "repairs/list_issues"}) + msg = await ws_client.receive_json() + + assert msg["success"] + issue = None + for i in msg["result"]["issues"]: + if i["issue_id"] == "deprecate_smart_sensor": + issue = i + assert issue is None + + +async def test_deprecate_smart_no_automations( + hass: HomeAssistant, ufp: MockUFPFixture, hass_ws_client, doorbell: Camera +): + """Test Deprecate Sensor repair exists for existing installs.""" + + registry = er.async_get(hass) + registry.async_get_or_create( + Platform.SENSOR, + DOMAIN, + f"{doorbell.mac}_detected_object", + config_entry=ufp.entry, + ) + + await init_entry(hass, ufp, [doorbell]) + + await async_process_repairs_platforms(hass) + ws_client = await hass_ws_client(hass) + + await ws_client.send_json({"id": 1, "type": "repairs/list_issues"}) + msg = await ws_client.receive_json() + + assert msg["success"] + issue = None + for i in msg["result"]["issues"]: + if i["issue_id"] == "deprecate_smart_sensor": + issue = i + assert issue is None + + +async def _load_automation(hass: HomeAssistant, entity_id: str): + assert await async_setup_component( + hass, + AUTOMATION_DOMAIN, + { + AUTOMATION_DOMAIN: [ + { + "alias": "test1", + "trigger": [ + {"platform": "state", "entity_id": entity_id}, + { + "platform": "event", + "event_type": "state_changed", + "event_data": {"entity_id": entity_id}, + }, + ], + "condition": { + "condition": "state", + "entity_id": entity_id, + "state": "on", + }, + "action": [ + { + "service": "test.script", + "data": {"entity_id": entity_id}, + }, + ], + }, + ] + }, + ) + + +async def test_deprecate_smart_automation( + hass: HomeAssistant, ufp: MockUFPFixture, hass_ws_client, doorbell: Camera +): + """Test Deprecate Sensor repair exists for existing installs.""" + + registry = er.async_get(hass) + entry = registry.async_get_or_create( + Platform.SENSOR, + DOMAIN, + f"{doorbell.mac}_detected_object", + config_entry=ufp.entry, + ) + await _load_automation(hass, entry.entity_id) + await init_entry(hass, ufp, [doorbell]) + + await async_process_repairs_platforms(hass) + ws_client = await hass_ws_client(hass) + + await ws_client.send_json({"id": 1, "type": "repairs/list_issues"}) + msg = await ws_client.receive_json() + + assert msg["success"] + issue = None + for i in msg["result"]["issues"]: + if i["issue_id"] == "deprecate_smart_sensor": + issue = i + assert issue is not None + + with patch( + "homeassistant.config.load_yaml_config_file", + autospec=True, + return_value={AUTOMATION_DOMAIN: []}, + ): + await hass.services.async_call(AUTOMATION_DOMAIN, SERVICE_RELOAD, blocking=True) + await hass.async_block_till_done() + + await ws_client.send_json({"id": 2, "type": "repairs/list_issues"}) + msg = await ws_client.receive_json() + + assert msg["success"] + issue = None + for i in msg["result"]["issues"]: + if i["issue_id"] == "deprecate_smart_sensor": + issue = i + assert issue is None + + +async def _load_script(hass: HomeAssistant, entity_id: str): + assert await async_setup_component( + hass, + SCRIPT_DOMAIN, + { + SCRIPT_DOMAIN: { + "test": { + "sequence": { + "service": "test.script", + "data": {"entity_id": entity_id}, + } + } + }, + }, + ) + + +async def test_deprecate_smart_script( + hass: HomeAssistant, ufp: MockUFPFixture, hass_ws_client, doorbell: Camera +): + """Test Deprecate Sensor repair exists for existing installs.""" + + registry = er.async_get(hass) + entry = registry.async_get_or_create( + Platform.SENSOR, + DOMAIN, + f"{doorbell.mac}_detected_object", + config_entry=ufp.entry, + ) + await _load_script(hass, entry.entity_id) + await init_entry(hass, ufp, [doorbell]) + + await async_process_repairs_platforms(hass) + ws_client = await hass_ws_client(hass) + + await ws_client.send_json({"id": 1, "type": "repairs/list_issues"}) + msg = await ws_client.receive_json() + + assert msg["success"] + issue = None + for i in msg["result"]["issues"]: + if i["issue_id"] == "deprecate_smart_sensor": + issue = i + assert issue is not None + + with patch( + "homeassistant.config.load_yaml_config_file", + autospec=True, + return_value={SCRIPT_DOMAIN: {}}, + ): + await hass.services.async_call(SCRIPT_DOMAIN, SERVICE_RELOAD, blocking=True) + await hass.config_entries.async_reload(ufp.entry.entry_id) + await hass.async_block_till_done() + + await ws_client.send_json({"id": 2, "type": "repairs/list_issues"}) + msg = await ws_client.receive_json() + + assert msg["success"] + issue = None + for i in msg["result"]["issues"]: + if i["issue_id"] == "deprecate_smart_sensor": + issue = i + assert issue is None diff --git a/tests/components/unifiprotect/test_text.py b/tests/components/unifiprotect/test_text.py new file mode 100644 index 00000000000..17fe3ee7bc2 --- /dev/null +++ b/tests/components/unifiprotect/test_text.py @@ -0,0 +1,92 @@ +"""Test the UniFi Protect text platform.""" +# pylint: disable=protected-access +from __future__ import annotations + +from unittest.mock import AsyncMock, Mock + +from pyunifiprotect.data import Camera, DoorbellMessageType, LCDMessage + +from homeassistant.components.unifiprotect.const import DEFAULT_ATTRIBUTION +from homeassistant.components.unifiprotect.text import CAMERA +from homeassistant.const import ATTR_ATTRIBUTION, ATTR_ENTITY_ID, Platform +from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er + +from .utils import ( + MockUFPFixture, + adopt_devices, + assert_entity_counts, + ids_from_device_description, + init_entry, + remove_entities, +) + + +async def test_text_camera_remove( + hass: HomeAssistant, ufp: MockUFPFixture, doorbell: Camera, unadopted_camera: Camera +): + """Test removing and re-adding a camera device.""" + + ufp.api.bootstrap.nvr.system_info.ustorage = None + await init_entry(hass, ufp, [doorbell, unadopted_camera]) + assert_entity_counts(hass, Platform.TEXT, 1, 1) + await remove_entities(hass, ufp, [doorbell, unadopted_camera]) + assert_entity_counts(hass, Platform.TEXT, 0, 0) + await adopt_devices(hass, ufp, [doorbell, unadopted_camera]) + assert_entity_counts(hass, Platform.TEXT, 1, 1) + + +async def test_text_camera_setup( + hass: HomeAssistant, ufp: MockUFPFixture, doorbell: Camera +): + """Test text entity setup for camera devices.""" + + doorbell.lcd_message = LCDMessage( + type=DoorbellMessageType.CUSTOM_MESSAGE, text="Test" + ) + await init_entry(hass, ufp, [doorbell]) + assert_entity_counts(hass, Platform.TEXT, 1, 1) + + entity_registry = er.async_get(hass) + + description = CAMERA[0] + unique_id, entity_id = ids_from_device_description( + Platform.TEXT, doorbell, description + ) + + entity = entity_registry.async_get(entity_id) + assert entity + assert entity.unique_id == unique_id + + state = hass.states.get(entity_id) + assert state + assert state.state == "Test" + assert state.attributes[ATTR_ATTRIBUTION] == DEFAULT_ATTRIBUTION + + +async def test_text_camera_set( + hass: HomeAssistant, ufp: MockUFPFixture, doorbell: Camera +): + """Test text entity setting value camera devices.""" + + await init_entry(hass, ufp, [doorbell]) + assert_entity_counts(hass, Platform.TEXT, 1, 1) + + description = CAMERA[0] + unique_id, entity_id = ids_from_device_description( + Platform.TEXT, doorbell, description + ) + + doorbell.__fields__["set_lcd_text"] = Mock(final=False) + doorbell.set_lcd_text = AsyncMock() + + await hass.services.async_call( + "text", + "set_value", + {ATTR_ENTITY_ID: entity_id, "value": "Test test"}, + blocking=True, + ) + + doorbell.set_lcd_text.assert_called_once_with( + DoorbellMessageType.CUSTOM_MESSAGE, text="Test test" + ) diff --git a/tests/components/uptimerobot/test_sensor.py b/tests/components/uptimerobot/test_sensor.py index 3e833af9bd4..68b64f70e5a 100644 --- a/tests/components/uptimerobot/test_sensor.py +++ b/tests/components/uptimerobot/test_sensor.py @@ -4,6 +4,7 @@ from unittest.mock import patch from pyuptimerobot import UptimeRobotAuthenticationException +from homeassistant.components.sensor import SensorDeviceClass from homeassistant.components.uptimerobot.const import COORDINATOR_UPDATE_INTERVAL from homeassistant.const import STATE_UNAVAILABLE from homeassistant.core import HomeAssistant @@ -30,6 +31,14 @@ async def test_presentation(hass: HomeAssistant) -> None: assert entity.state == STATE_UP assert entity.attributes["icon"] == SENSOR_ICON assert entity.attributes["target"] == MOCK_UPTIMEROBOT_MONITOR["url"] + assert entity.attributes["device_class"] == SensorDeviceClass.ENUM + assert entity.attributes["options"] == [ + "down", + "not_checked_yet", + "pause", + "seems_down", + "up", + ] async def test_unaviable_on_update_failure(hass: HomeAssistant) -> None: diff --git a/tests/components/vallox/conftest.py b/tests/components/vallox/conftest.py index 0c14f359b5f..60d8bfc0562 100644 --- a/tests/components/vallox/conftest.py +++ b/tests/components/vallox/conftest.py @@ -39,13 +39,36 @@ def patch_metrics(metrics: dict[str, Any]): ) +def patch_profile(profile: PROFILE): + """Patch the Vallox metrics response.""" + return patch( + "homeassistant.components.vallox.Vallox.get_profile", + return_value=profile, + ) + + +def patch_profile_set(): + """Patch the Vallox metrics set values.""" + return patch("homeassistant.components.vallox.Vallox.set_profile") + + def patch_metrics_set(): """Patch the Vallox metrics set values.""" return patch("homeassistant.components.vallox.Vallox.set_values") @pytest.fixture(autouse=True) -def patch_profile_home(): +def patch_empty_metrics(): + """Patch the Vallox profile response.""" + with patch( + "homeassistant.components.vallox.Vallox.fetch_metrics", + return_value={}, + ): + yield + + +@pytest.fixture(autouse=True) +def patch_default_profile(): """Patch the Vallox profile response.""" with patch( "homeassistant.components.vallox.Vallox.get_profile", diff --git a/tests/components/vallox/test_config_flow.py b/tests/components/vallox/test_config_flow.py index b0c951383b8..39de026bdbb 100644 --- a/tests/components/vallox/test_config_flow.py +++ b/tests/components/vallox/test_config_flow.py @@ -1,7 +1,7 @@ """Test the Vallox integration config flow.""" from unittest.mock import patch -from vallox_websocket_api.exceptions import ValloxApiException +from vallox_websocket_api import ValloxApiException, ValloxWebsocketException from homeassistant.components.vallox.const import DOMAIN from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_USER @@ -95,7 +95,7 @@ async def test_form_os_error_cannot_connect(hass: HomeAssistant) -> None: with patch( "homeassistant.components.vallox.config_flow.Vallox.get_info", - side_effect=OSError, + side_effect=ValloxWebsocketException, ): result = await hass.config_entries.flow.async_configure( init["flow_id"], @@ -243,7 +243,7 @@ async def test_import_cannot_connect_os_error(hass: HomeAssistant) -> None: with patch( "homeassistant.components.vallox.config_flow.Vallox.get_info", - side_effect=OSError, + side_effect=ValloxWebsocketException, ): result = await hass.config_entries.flow.async_init( DOMAIN, diff --git a/tests/components/vallox/test_fan.py b/tests/components/vallox/test_fan.py new file mode 100644 index 00000000000..beb9a2647ef --- /dev/null +++ b/tests/components/vallox/test_fan.py @@ -0,0 +1,259 @@ +"""Tests for Vallox fan platform.""" +from unittest.mock import call + +import pytest +from vallox_websocket_api import PROFILE, ValloxApiException + +from homeassistant.components.fan import ( + ATTR_PERCENTAGE, + ATTR_PRESET_MODE, + DOMAIN as FAN_DOMAIN, + SERVICE_SET_PERCENTAGE, + SERVICE_SET_PRESET_MODE, +) +from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError + +from .conftest import patch_metrics, patch_metrics_set, patch_profile, patch_profile_set + +from tests.common import MockConfigEntry + + +@pytest.mark.parametrize( + "metrics, expected_state", [({"A_CYC_MODE": 0}, "on"), ({"A_CYC_MODE": 5}, "off")] +) +async def test_fan_state( + metrics: dict[str, int], + expected_state: str, + mock_entry: MockConfigEntry, + hass: HomeAssistant, +) -> None: + """Test fan on/off state.""" + + # Act + with patch_metrics(metrics=metrics): + await hass.config_entries.async_setup(mock_entry.entry_id) + await hass.async_block_till_done() + + # Assert + sensor = hass.states.get("fan.vallox") + assert sensor + assert sensor.state == expected_state + + +@pytest.mark.parametrize( + "profile, expected_preset", + [ + (PROFILE.HOME, "Home"), + (PROFILE.AWAY, "Away"), + (PROFILE.BOOST, "Boost"), + (PROFILE.FIREPLACE, "Fireplace"), + ], +) +async def test_fan_profile( + profile: PROFILE, + expected_preset: str, + mock_entry: MockConfigEntry, + hass: HomeAssistant, +) -> None: + """Test fan profile.""" + + # Act + with patch_profile(profile): + await hass.config_entries.async_setup(mock_entry.entry_id) + await hass.async_block_till_done() + + # Assert + sensor = hass.states.get("fan.vallox") + assert sensor + assert sensor.attributes["preset_mode"] == expected_preset + + +@pytest.mark.parametrize( + "service, initial_metrics, expected_called_with", + [ + (SERVICE_TURN_ON, {"A_CYC_MODE": 5}, {"A_CYC_MODE": 0}), + (SERVICE_TURN_OFF, {"A_CYC_MODE": 0}, {"A_CYC_MODE": 5}), + ], +) +async def test_turn_on_off( + service: str, + initial_metrics: dict[str, int], + expected_called_with: dict[str, int], + mock_entry: MockConfigEntry, + hass: HomeAssistant, +) -> None: + """Test turn on/off.""" + with patch_metrics(metrics=initial_metrics), patch_metrics_set() as metrics_set: + await hass.config_entries.async_setup(mock_entry.entry_id) + await hass.async_block_till_done() + await hass.services.async_call( + FAN_DOMAIN, + service, + service_data={ATTR_ENTITY_ID: "fan.vallox"}, + blocking=True, + ) + metrics_set.assert_called_once_with(expected_called_with) + + +@pytest.mark.parametrize( + "initial_metrics, expected_call_args_list", + [ + ( + {"A_CYC_MODE": 5}, + [ + call({"A_CYC_MODE": 0}), + call({"A_CYC_AWAY_SPEED_SETTING": 15}), + ], + ), + ( + {"A_CYC_MODE": 0}, + [ + call({"A_CYC_AWAY_SPEED_SETTING": 15}), + ], + ), + ], +) +async def test_turn_on_with_parameters( + initial_metrics: dict[str, int], + expected_call_args_list: list[tuple], + mock_entry: MockConfigEntry, + hass: HomeAssistant, +) -> None: + """Test turn on/off.""" + with patch_metrics( + metrics=initial_metrics + ), patch_metrics_set() as metrics_set, patch_profile_set() as profile_set: + await hass.config_entries.async_setup(mock_entry.entry_id) + await hass.async_block_till_done() + await hass.services.async_call( + FAN_DOMAIN, + SERVICE_TURN_ON, + service_data={ + ATTR_ENTITY_ID: "fan.vallox", + ATTR_PERCENTAGE: "15", + ATTR_PRESET_MODE: "Away", + }, + blocking=True, + ) + assert metrics_set.call_args_list == expected_call_args_list + profile_set.assert_called_once_with(PROFILE.AWAY) + + +@pytest.mark.parametrize( + "preset, initial_profile, expected_call_args_list", + [ + ("Home", PROFILE.AWAY, [call(PROFILE.HOME)]), + ("Away", PROFILE.HOME, [call(PROFILE.AWAY)]), + ("Boost", PROFILE.HOME, [call(PROFILE.BOOST)]), + ("Fireplace", PROFILE.HOME, [call(PROFILE.FIREPLACE)]), + ("Home", PROFILE.HOME, []), + ], +) +async def test_set_preset_mode( + preset: str, + initial_profile: PROFILE, + expected_call_args_list: list[tuple], + mock_entry: MockConfigEntry, + hass: HomeAssistant, +) -> None: + """Test set preset mode.""" + with patch_profile(initial_profile), patch_profile_set() as profile_set: + await hass.config_entries.async_setup(mock_entry.entry_id) + await hass.async_block_till_done() + await hass.services.async_call( + FAN_DOMAIN, + SERVICE_SET_PRESET_MODE, + service_data={ATTR_ENTITY_ID: "fan.vallox", ATTR_PRESET_MODE: preset}, + blocking=True, + ) + assert profile_set.call_args_list == expected_call_args_list + + +async def test_set_invalid_preset_mode( + mock_entry: MockConfigEntry, + hass: HomeAssistant, +) -> None: + """Test set preset mode.""" + await hass.config_entries.async_setup(mock_entry.entry_id) + await hass.async_block_till_done() + with pytest.raises(ValueError): + await hass.services.async_call( + FAN_DOMAIN, + SERVICE_SET_PRESET_MODE, + service_data={ + ATTR_ENTITY_ID: "fan.vallox", + ATTR_PRESET_MODE: "Invalid", + }, + blocking=True, + ) + + +async def test_set_preset_mode_exception( + mock_entry: MockConfigEntry, + hass: HomeAssistant, +) -> None: + """Test set preset mode.""" + with patch_profile_set() as profile_set: + profile_set.side_effect = ValloxApiException("Fake exception") + await hass.config_entries.async_setup(mock_entry.entry_id) + await hass.async_block_till_done() + with pytest.raises(HomeAssistantError): + await hass.services.async_call( + FAN_DOMAIN, + SERVICE_SET_PRESET_MODE, + service_data={ATTR_ENTITY_ID: "fan.vallox", ATTR_PRESET_MODE: "Away"}, + blocking=True, + ) + + +@pytest.mark.parametrize( + "profile, percentage, expected_call_args_list", + [ + (PROFILE.HOME, 40, [call({"A_CYC_HOME_SPEED_SETTING": 40})]), + (PROFILE.AWAY, 30, [call({"A_CYC_AWAY_SPEED_SETTING": 30})]), + (PROFILE.BOOST, 60, [call({"A_CYC_BOOST_SPEED_SETTING": 60})]), + (PROFILE.HOME, 0, [call({"A_CYC_MODE": 5})]), + ], +) +async def test_set_fan_speed( + profile: PROFILE, + percentage: int, + expected_call_args_list: list[tuple], + mock_entry: MockConfigEntry, + hass: HomeAssistant, +) -> None: + """Test set fan speed percentage.""" + with patch_profile(profile), patch_metrics_set() as metrics_set, patch_metrics( + {"A_CYC_MODE": 0} + ): + await hass.config_entries.async_setup(mock_entry.entry_id) + await hass.async_block_till_done() + await hass.services.async_call( + FAN_DOMAIN, + SERVICE_SET_PERCENTAGE, + service_data={ATTR_ENTITY_ID: "fan.vallox", ATTR_PERCENTAGE: percentage}, + blocking=True, + ) + assert metrics_set.call_args_list == expected_call_args_list + + +async def test_set_fan_speed_exception( + mock_entry: MockConfigEntry, + hass: HomeAssistant, +) -> None: + """Test set fan speed percentage.""" + with patch_metrics_set() as metrics_set, patch_metrics( + {"A_CYC_MODE": 0, "A_CYC_HOME_SPEED_SETTING": 30} + ): + metrics_set.side_effect = ValloxApiException("Fake failure") + await hass.config_entries.async_setup(mock_entry.entry_id) + await hass.async_block_till_done() + with pytest.raises(HomeAssistantError): + await hass.services.async_call( + FAN_DOMAIN, + SERVICE_SET_PERCENTAGE, + service_data={ATTR_ENTITY_ID: "fan.vallox", ATTR_PERCENTAGE: 5}, + blocking=True, + ) diff --git a/tests/components/venstar/__init__.py b/tests/components/venstar/__init__.py index c7b4815c5bb..fa35dd88379 100644 --- a/tests/components/venstar/__init__.py +++ b/tests/components/venstar/__init__.py @@ -43,6 +43,7 @@ class VenstarColorTouchMock: def update_info(self): """Mock update_info.""" + self.name = "username" return True def broken_update_info(self): diff --git a/tests/components/withings/common.py b/tests/components/withings/common.py index 598e7ab8c33..96a35d10c40 100644 --- a/tests/components/withings/common.py +++ b/tests/components/withings/common.py @@ -24,7 +24,9 @@ from homeassistant.components.withings import async_unload_entry from homeassistant.components.withings.common import ( ConfigEntryWithingsApi, DataManager, + WithingsEntityDescription, get_all_data_managers, + get_attribute_unique_id, ) import homeassistant.components.withings.const as const from homeassistant.config import async_process_ha_core_config @@ -37,7 +39,7 @@ from homeassistant.const import ( CONF_UNIT_SYSTEM_METRIC, ) from homeassistant.core import HomeAssistant -from homeassistant.helpers import config_entry_oauth2_flow +from homeassistant.helpers import config_entry_oauth2_flow, entity_registry as er from homeassistant.helpers.config_entry_oauth2_flow import AUTH_CALLBACK_PATH from homeassistant.setup import async_setup_component from homeassistant.util import dt as dt_util @@ -320,3 +322,16 @@ def get_data_manager_by_user_id( ), None, ) + + +async def async_get_entity_id( + hass: HomeAssistant, + description: WithingsEntityDescription, + user_id: int, + platform: str, +) -> str | None: + """Get an entity id for a user's attribute.""" + entity_registry = er.async_get(hass) + unique_id = get_attribute_unique_id(description, user_id) + + return entity_registry.async_get_entity_id(platform, const.DOMAIN, unique_id) diff --git a/tests/components/withings/test_binary_sensor.py b/tests/components/withings/test_binary_sensor.py index 9f93e00f4ef..e3205ffa2db 100644 --- a/tests/components/withings/test_binary_sensor.py +++ b/tests/components/withings/test_binary_sensor.py @@ -1,17 +1,20 @@ """Tests for the Withings component.""" from withings_api.common import NotifyAppli -from homeassistant.components.withings.common import ( - WITHINGS_MEASUREMENTS_MAP, - async_get_entity_id, -) +from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN +from homeassistant.components.withings.binary_sensor import BINARY_SENSORS +from homeassistant.components.withings.common import WithingsEntityDescription from homeassistant.components.withings.const import Measurement from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNAVAILABLE from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er from homeassistant.helpers.entity_registry import EntityRegistry -from .common import ComponentFactory, new_profile_config +from .common import ComponentFactory, async_get_entity_id, new_profile_config + +WITHINGS_MEASUREMENTS_MAP: dict[Measurement, WithingsEntityDescription] = { + attr.measurement: attr for attr in BINARY_SENSORS +} async def test_binary_sensor( @@ -25,15 +28,23 @@ async def test_binary_sensor( entity_registry: EntityRegistry = er.async_get(hass) await component_factory.configure_component(profile_configs=(person0, person1)) - assert not await async_get_entity_id(hass, in_bed_attribute, person0.user_id) - assert not await async_get_entity_id(hass, in_bed_attribute, person1.user_id) + assert not await async_get_entity_id( + hass, in_bed_attribute, person0.user_id, BINARY_SENSOR_DOMAIN + ) + assert not await async_get_entity_id( + hass, in_bed_attribute, person1.user_id, BINARY_SENSOR_DOMAIN + ) # person 0 await component_factory.setup_profile(person0.user_id) await component_factory.setup_profile(person1.user_id) - entity_id0 = await async_get_entity_id(hass, in_bed_attribute, person0.user_id) - entity_id1 = await async_get_entity_id(hass, in_bed_attribute, person1.user_id) + entity_id0 = await async_get_entity_id( + hass, in_bed_attribute, person0.user_id, BINARY_SENSOR_DOMAIN + ) + entity_id1 = await async_get_entity_id( + hass, in_bed_attribute, person1.user_id, BINARY_SENSOR_DOMAIN + ) assert entity_id0 assert entity_id1 diff --git a/tests/components/withings/test_sensor.py b/tests/components/withings/test_sensor.py index fbe9c6304bb..a22ba36e59a 100644 --- a/tests/components/withings/test_sensor.py +++ b/tests/components/withings/test_sensor.py @@ -17,20 +17,20 @@ from withings_api.common import ( SleepModel, ) -from homeassistant.components.withings.common import ( - WITHINGS_MEASUREMENTS_MAP, - WithingsAttribute, - async_get_entity_id, - get_platform_attributes, -) +from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN +from homeassistant.components.withings.common import WithingsEntityDescription from homeassistant.components.withings.const import Measurement -from homeassistant.const import Platform +from homeassistant.components.withings.sensor import SENSORS from homeassistant.core import HomeAssistant, State from homeassistant.helpers import entity_registry as er from homeassistant.helpers.entity_registry import EntityRegistry from homeassistant.util import dt as dt_util -from .common import ComponentFactory, new_profile_config +from .common import ComponentFactory, async_get_entity_id, new_profile_config + +WITHINGS_MEASUREMENTS_MAP: dict[Measurement, WithingsEntityDescription] = { + attr.measurement: attr for attr in SENSORS +} PERSON0 = new_profile_config( "person0", @@ -290,14 +290,17 @@ EXPECTED_DATA = ( def async_assert_state_equals( - entity_id: str, state_obj: State, expected: Any, attribute: WithingsAttribute + entity_id: str, + state_obj: State, + expected: Any, + description: WithingsEntityDescription, ) -> None: """Assert at given state matches what is expected.""" assert state_obj, f"Expected entity {entity_id} to exist but it did not" assert state_obj.state == str(expected), ( f"Expected {expected} but was {state_obj.state} " - f"for measure {attribute.measurement}, {entity_id}" + f"for measure {description.measurement}, {entity_id}" ) @@ -310,15 +313,19 @@ async def test_sensor_default_enabled_entities( await component_factory.configure_component(profile_configs=(PERSON0,)) # Assert entities should not exist yet. - for attribute in get_platform_attributes(Platform.SENSOR): - assert not await async_get_entity_id(hass, attribute, PERSON0.user_id) + for attribute in SENSORS: + assert not await async_get_entity_id( + hass, attribute, PERSON0.user_id, SENSOR_DOMAIN + ) # person 0 await component_factory.setup_profile(PERSON0.user_id) # Assert entities should exist. - for attribute in get_platform_attributes(Platform.SENSOR): - entity_id = await async_get_entity_id(hass, attribute, PERSON0.user_id) + for attribute in SENSORS: + entity_id = await async_get_entity_id( + hass, attribute, PERSON0.user_id, SENSOR_DOMAIN + ) assert entity_id assert entity_registry.async_is_registered(entity_id) @@ -330,10 +337,12 @@ async def test_sensor_default_enabled_entities( for person, measurement, expected in EXPECTED_DATA: attribute = WITHINGS_MEASUREMENTS_MAP[measurement] - entity_id = await async_get_entity_id(hass, attribute, person.user_id) + entity_id = await async_get_entity_id( + hass, attribute, person.user_id, SENSOR_DOMAIN + ) state_obj = hass.states.get(entity_id) - if attribute.enabled_by_default: + if attribute.entity_registry_enabled_default: async_assert_state_equals(entity_id, state_obj, expected, attribute) else: assert state_obj is None @@ -356,15 +365,19 @@ async def test_all_entities( await component_factory.configure_component(profile_configs=(PERSON0,)) # Assert entities should not exist yet. - for attribute in get_platform_attributes(Platform.SENSOR): - assert not await async_get_entity_id(hass, attribute, PERSON0.user_id) + for attribute in SENSORS: + assert not await async_get_entity_id( + hass, attribute, PERSON0.user_id, SENSOR_DOMAIN + ) # person 0 await component_factory.setup_profile(PERSON0.user_id) # Assert entities should exist. - for attribute in get_platform_attributes(Platform.SENSOR): - entity_id = await async_get_entity_id(hass, attribute, PERSON0.user_id) + for attribute in SENSORS: + entity_id = await async_get_entity_id( + hass, attribute, PERSON0.user_id, SENSOR_DOMAIN + ) assert entity_id assert entity_registry.async_is_registered(entity_id) @@ -376,7 +389,9 @@ async def test_all_entities( for person, measurement, expected in EXPECTED_DATA: attribute = WITHINGS_MEASUREMENTS_MAP[measurement] - entity_id = await async_get_entity_id(hass, attribute, person.user_id) + entity_id = await async_get_entity_id( + hass, attribute, person.user_id, SENSOR_DOMAIN + ) state_obj = hass.states.get(entity_id) async_assert_state_equals(entity_id, state_obj, expected, attribute) diff --git a/tests/components/yeelight/__init__.py b/tests/components/yeelight/__init__.py index ea3c8f4656c..64f6bcdadde 100644 --- a/tests/components/yeelight/__init__.py +++ b/tests/components/yeelight/__init__.py @@ -1,5 +1,4 @@ """Tests for the Yeelight integration.""" -import asyncio from datetime import timedelta from unittest.mock import AsyncMock, MagicMock, patch @@ -164,12 +163,12 @@ def _patched_ssdp_listener(info: CaseInsensitiveDict, *args, **kwargs): async def _async_callback(*_): if kwargs["source"][0] == FAIL_TO_BIND_IP: raise OSError - await listener.async_connect_callback() + listener.connect_callback() @callback def _async_search(*_): if info: - asyncio.create_task(listener.async_callback(info)) + listener.callback(info) listener.async_start = _async_callback listener.async_search = _async_search diff --git a/tests/components/zamg/__init__.py b/tests/components/zamg/__init__.py index 9c6415d7f84..33a9acaddba 100644 --- a/tests/components/zamg/__init__.py +++ b/tests/components/zamg/__init__.py @@ -1 +1,18 @@ """Tests for the ZAMG component.""" + +from homeassistant import config_entries +from homeassistant.components.zamg.const import CONF_STATION_ID, DOMAIN as ZAMG_DOMAIN + +from .conftest import TEST_STATION_ID, TEST_STATION_NAME + +FIXTURE_CONFIG_ENTRY = { + "entry_id": "1", + "domain": ZAMG_DOMAIN, + "title": TEST_STATION_NAME, + "data": { + CONF_STATION_ID: TEST_STATION_ID, + }, + "options": None, + "source": config_entries.SOURCE_USER, + "unique_id": TEST_STATION_ID, +} diff --git a/tests/components/zamg/conftest.py b/tests/components/zamg/conftest.py index 62ef191cb48..e3d3d384e85 100644 --- a/tests/components/zamg/conftest.py +++ b/tests/components/zamg/conftest.py @@ -14,6 +14,9 @@ from tests.common import MockConfigEntry, load_fixture TEST_STATION_ID = "11240" TEST_STATION_NAME = "Graz/Flughafen" +TEST_STATION_ID_2 = "11035" +TEST_STATION_NAME_2 = "WIEN/HOHE WARTE" + @pytest.fixture def mock_config_entry() -> MockConfigEntry: @@ -67,6 +70,27 @@ def mock_zamg(request: pytest.FixtureRequest) -> Generator[None, MagicMock, None yield zamg +@pytest.fixture +def mock_zamg_coordinator( + request: pytest.FixtureRequest, +) -> Generator[None, MagicMock, None]: + """Return a mocked Zamg client.""" + + with patch( + "homeassistant.components.zamg.coordinator.ZamgDevice", autospec=True + ) as zamg_mock: + zamg = zamg_mock.return_value + zamg.update.return_value = {TEST_STATION_ID: {"Name": TEST_STATION_NAME}} + zamg.zamg_stations.return_value = { + TEST_STATION_ID: (46.99305556, 15.43916667, TEST_STATION_NAME), + "11244": (46.8722229, 15.90361118, "BAD GLEICHENBERG"), + } + zamg.closest_station.return_value = TEST_STATION_ID + zamg.get_data.return_value = TEST_STATION_ID + zamg.get_station_name = TEST_STATION_NAME + yield zamg + + @pytest.fixture def mock_zamg_stations( request: pytest.FixtureRequest, diff --git a/tests/components/zamg/test_config_flow.py b/tests/components/zamg/test_config_flow.py index dc2eb62f1b9..26939c07f0c 100644 --- a/tests/components/zamg/test_config_flow.py +++ b/tests/components/zamg/test_config_flow.py @@ -1,6 +1,8 @@ """Tests for the Zamg config flow.""" from unittest.mock import MagicMock +from zamg.exceptions import ZamgApiError, ZamgStationNotFoundError + from homeassistant.components.zamg.const import CONF_STATION_ID, DOMAIN, LOGGER from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_USER from homeassistant.const import CONF_NAME @@ -27,7 +29,7 @@ async def test_full_user_flow_implementation( assert "flow_id" in result result = await hass.config_entries.flow.async_configure( result["flow_id"], - user_input={CONF_STATION_ID: int(TEST_STATION_ID)}, + user_input={CONF_STATION_ID: TEST_STATION_ID}, ) assert result.get("type") == FlowResultType.CREATE_ENTRY assert "data" in result @@ -36,6 +38,21 @@ async def test_full_user_flow_implementation( assert result["result"].unique_id == TEST_STATION_ID +async def test_error_closest_station( + hass: HomeAssistant, + mock_zamg: MagicMock, + mock_setup_entry: None, +) -> None: + """Test with error of reading from Zamg.""" + mock_zamg.closest_station.side_effect = ZamgApiError + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_USER}, + ) + assert result.get("type") == FlowResultType.ABORT + assert result.get("reason") == "cannot_connect" + + async def test_error_update( hass: HomeAssistant, mock_zamg: MagicMock, @@ -50,11 +67,11 @@ async def test_error_update( assert result.get("type") == FlowResultType.FORM LOGGER.debug(result) assert result.get("data_schema") != "" - mock_zamg.update.side_effect = ValueError + mock_zamg.update.side_effect = ZamgApiError assert "flow_id" in result result = await hass.config_entries.flow.async_configure( result["flow_id"], - user_input={CONF_STATION_ID: int(TEST_STATION_ID)}, + user_input={CONF_STATION_ID: TEST_STATION_ID}, ) assert result.get("type") == FlowResultType.ABORT assert result.get("reason") == "cannot_connect" @@ -91,7 +108,7 @@ async def test_user_flow_duplicate( assert "flow_id" in result result = await hass.config_entries.flow.async_configure( result["flow_id"], - user_input={CONF_STATION_ID: int(TEST_STATION_ID)}, + user_input={CONF_STATION_ID: TEST_STATION_ID}, ) assert result.get("type") == FlowResultType.CREATE_ENTRY assert "data" in result @@ -107,7 +124,7 @@ async def test_user_flow_duplicate( assert result.get("type") == FlowResultType.FORM result = await hass.config_entries.flow.async_configure( result["flow_id"], - user_input={CONF_STATION_ID: int(TEST_STATION_ID)}, + user_input={CONF_STATION_ID: TEST_STATION_ID}, ) assert result.get("type") == FlowResultType.ABORT assert result.get("reason") == "already_configured" @@ -129,7 +146,7 @@ async def test_import_flow_duplicate( assert "flow_id" in result result = await hass.config_entries.flow.async_configure( result["flow_id"], - user_input={CONF_STATION_ID: int(TEST_STATION_ID)}, + user_input={CONF_STATION_ID: TEST_STATION_ID}, ) assert result.get("type") == FlowResultType.CREATE_ENTRY assert "data" in result @@ -162,7 +179,7 @@ async def test_import_flow_duplicate_after_position( assert "flow_id" in result result = await hass.config_entries.flow.async_configure( result["flow_id"], - user_input={CONF_STATION_ID: int(TEST_STATION_ID)}, + user_input={CONF_STATION_ID: TEST_STATION_ID}, ) assert result.get("type") == FlowResultType.CREATE_ENTRY assert "data" in result @@ -184,7 +201,7 @@ async def test_import_flow_no_name( mock_zamg: MagicMock, mock_setup_entry: None, ) -> None: - """Test the full import flow from start to finish.""" + """Test import flow without any name.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_IMPORT}, @@ -192,3 +209,35 @@ async def test_import_flow_no_name( ) assert result.get("type") == FlowResultType.CREATE_ENTRY assert result.get("data") == {CONF_STATION_ID: TEST_STATION_ID} + + +async def test_import_flow_invalid_station( + hass: HomeAssistant, + mock_zamg: MagicMock, + mock_setup_entry: None, +) -> None: + """Test import flow with invalid station.""" + mock_zamg.closest_station.side_effect = ZamgStationNotFoundError + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_IMPORT}, + data={CONF_STATION_ID: ""}, + ) + assert result.get("type") == FlowResultType.ABORT + assert result.get("reason") == "station_not_found" + + +async def test_import_flow_zamg_error( + hass: HomeAssistant, + mock_zamg: MagicMock, + mock_setup_entry: None, +) -> None: + """Test import flow with error on getting zamg stations.""" + mock_zamg.zamg_stations.side_effect = ZamgApiError + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_IMPORT}, + data={CONF_STATION_ID: ""}, + ) + assert result.get("type") == FlowResultType.ABORT + assert result.get("reason") == "cannot_connect" diff --git a/tests/components/zamg/test_init.py b/tests/components/zamg/test_init.py new file mode 100644 index 00000000000..f70d406f2be --- /dev/null +++ b/tests/components/zamg/test_init.py @@ -0,0 +1,203 @@ +"""Test Zamg component init.""" +from unittest.mock import MagicMock + +import pytest + +from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN +from homeassistant.components.weather import DOMAIN as WEATHER_DOMAIN +from homeassistant.components.zamg.const import CONF_STATION_ID, DOMAIN as ZAMG_DOMAIN +from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er + +from . import FIXTURE_CONFIG_ENTRY +from .conftest import ( + TEST_STATION_ID, + TEST_STATION_ID_2, + TEST_STATION_NAME, + TEST_STATION_NAME_2, +) + +from tests.common import MockConfigEntry + + +@pytest.mark.parametrize( + "entitydata,old_unique_id,new_unique_id,station_id", + [ + ( + { + "domain": WEATHER_DOMAIN, + "platform": ZAMG_DOMAIN, + "unique_id": f"{TEST_STATION_NAME}_{TEST_STATION_ID}", + "suggested_object_id": f"Zamg {TEST_STATION_NAME}", + "disabled_by": None, + }, + f"{TEST_STATION_NAME}_{TEST_STATION_ID}", + TEST_STATION_ID, + TEST_STATION_ID, + ), + ( + { + "domain": WEATHER_DOMAIN, + "platform": ZAMG_DOMAIN, + "unique_id": f"{TEST_STATION_NAME_2}_{TEST_STATION_ID_2}", + "suggested_object_id": f"Zamg {TEST_STATION_NAME_2}", + "disabled_by": None, + }, + f"{TEST_STATION_NAME_2}_{TEST_STATION_ID_2}", + TEST_STATION_ID_2, + TEST_STATION_ID_2, + ), + ( + { + "domain": SENSOR_DOMAIN, + "platform": ZAMG_DOMAIN, + "unique_id": f"{TEST_STATION_NAME_2}_{TEST_STATION_ID_2}_temperature", + "suggested_object_id": f"Zamg {TEST_STATION_NAME_2}", + "disabled_by": None, + }, + f"{TEST_STATION_NAME_2}_{TEST_STATION_ID_2}_temperature", + f"{TEST_STATION_NAME_2}_{TEST_STATION_ID_2}_temperature", + TEST_STATION_ID_2, + ), + ], +) +async def test_migrate_unique_ids( + hass: HomeAssistant, + mock_zamg_coordinator: MagicMock, + entitydata: dict, + old_unique_id: str, + new_unique_id: str, + station_id: str, +) -> None: + """Test successful migration of entity unique_ids.""" + FIXTURE_CONFIG_ENTRY["data"][CONF_STATION_ID] = station_id + mock_config_entry = MockConfigEntry(**FIXTURE_CONFIG_ENTRY) + mock_config_entry.add_to_hass(hass) + + entity_registry = er.async_get(hass) + entity: er.RegistryEntry = entity_registry.async_get_or_create( + **entitydata, + config_entry=mock_config_entry, + ) + + assert entity.unique_id == old_unique_id + + assert await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() + + entity_migrated = entity_registry.async_get(entity.entity_id) + assert entity_migrated + assert entity_migrated.unique_id == new_unique_id + + +@pytest.mark.parametrize( + "entitydata,old_unique_id,new_unique_id,station_id", + [ + ( + { + "domain": WEATHER_DOMAIN, + "platform": ZAMG_DOMAIN, + "unique_id": f"{TEST_STATION_NAME}_{TEST_STATION_ID}", + "suggested_object_id": f"Zamg {TEST_STATION_NAME}", + "disabled_by": None, + }, + f"{TEST_STATION_NAME}_{TEST_STATION_ID}", + TEST_STATION_ID, + TEST_STATION_ID, + ), + ], +) +async def test_dont_migrate_unique_ids( + hass: HomeAssistant, + mock_zamg_coordinator: MagicMock, + entitydata: dict, + old_unique_id: str, + new_unique_id: str, + station_id: str, +) -> None: + """Test successful migration of entity unique_ids.""" + FIXTURE_CONFIG_ENTRY["data"][CONF_STATION_ID] = station_id + mock_config_entry = MockConfigEntry(**FIXTURE_CONFIG_ENTRY) + mock_config_entry.add_to_hass(hass) + + entity_registry = er.async_get(hass) + + # create existing entry with new_unique_id + existing_entity = entity_registry.async_get_or_create( + WEATHER_DOMAIN, + ZAMG_DOMAIN, + unique_id=TEST_STATION_ID, + suggested_object_id=f"Zamg {TEST_STATION_NAME}", + config_entry=mock_config_entry, + ) + + entity: er.RegistryEntry = entity_registry.async_get_or_create( + **entitydata, + config_entry=mock_config_entry, + ) + + assert entity.unique_id == old_unique_id + + assert await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() + + entity_migrated = entity_registry.async_get(entity.entity_id) + assert entity_migrated + assert entity_migrated.unique_id == old_unique_id + + entity_not_changed = entity_registry.async_get(existing_entity.entity_id) + assert entity_not_changed + assert entity_not_changed.unique_id == new_unique_id + + assert entity_migrated != entity_not_changed + + +@pytest.mark.parametrize( + "entitydata,unique_id", + [ + ( + { + "domain": WEATHER_DOMAIN, + "platform": ZAMG_DOMAIN, + "unique_id": TEST_STATION_ID, + "suggested_object_id": f"Zamg {TEST_STATION_NAME}", + "disabled_by": None, + }, + TEST_STATION_ID, + ), + ], +) +async def test_unload_entry( + hass: HomeAssistant, + mock_zamg_coordinator: MagicMock, + entitydata: dict, + unique_id: str, +) -> None: + """Test unload entity unique_ids.""" + mock_config_entry = MockConfigEntry(**FIXTURE_CONFIG_ENTRY) + mock_config_entry.add_to_hass(hass) + + entity_registry = er.async_get(hass) + + entity_registry.async_get_or_create( + WEATHER_DOMAIN, + ZAMG_DOMAIN, + unique_id=TEST_STATION_ID, + suggested_object_id=f"Zamg {TEST_STATION_NAME}", + config_entry=mock_config_entry, + ) + + entity: er.RegistryEntry = entity_registry.async_get_or_create( + **entitydata, + config_entry=mock_config_entry, + ) + + assert entity.unique_id == unique_id + + assert await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() + + assert await hass.config_entries.async_remove(mock_config_entry.entry_id) + await hass.async_block_till_done() + + assert hass.config_entries.async_get_entry(unique_id) is None diff --git a/tests/components/zha/test_device_trigger.py b/tests/components/zha/test_device_trigger.py index 49eeacc0e42..0081ba04d16 100644 --- a/tests/components/zha/test_device_trigger.py +++ b/tests/components/zha/test_device_trigger.py @@ -327,7 +327,7 @@ async def test_exception_no_triggers(hass, mock_devices, calls, caplog): }, ) await hass.async_block_till_done() - assert "Invalid config for [automation]" in caplog.text + assert "Invalid trigger configuration" in caplog.text async def test_exception_bad_trigger(hass, mock_devices, calls, caplog): @@ -369,7 +369,7 @@ async def test_exception_bad_trigger(hass, mock_devices, calls, caplog): }, ) await hass.async_block_till_done() - assert "Invalid config for [automation]" in caplog.text + assert "Invalid trigger configuration" in caplog.text @pytest.mark.skip(reason="Temporarily disabled until automation validation is improved") @@ -408,4 +408,4 @@ async def test_exception_no_device(hass, mock_devices, calls, caplog): }, ) await hass.async_block_till_done() - assert "Invalid config for [automation]" in caplog.text + assert "Invalid trigger configuration" in caplog.text diff --git a/tests/components/zha/test_sensor.py b/tests/components/zha/test_sensor.py index 55ea9833caa..dec065936a1 100644 --- a/tests/components/zha/test_sensor.py +++ b/tests/components/zha/test_sensor.py @@ -129,7 +129,7 @@ async def async_test_pressure(hass, cluster, entity_id): async def async_test_illuminance(hass, cluster, entity_id): """Test illuminance sensor.""" await send_attributes_report(hass, cluster, {1: 1, 0: 10, 2: 20}) - assert_state(hass, entity_id, "1.0", LIGHT_LUX) + assert_state(hass, entity_id, "1", LIGHT_LUX) async def async_test_metering(hass, cluster, entity_id): diff --git a/tests/components/zodiac/test_sensor.py b/tests/components/zodiac/test_sensor.py index 90b19e73b04..b0894dfde7d 100644 --- a/tests/components/zodiac/test_sensor.py +++ b/tests/components/zodiac/test_sensor.py @@ -4,6 +4,7 @@ from unittest.mock import patch import pytest +from homeassistant.components.sensor import ATTR_OPTIONS, SensorDeviceClass from homeassistant.components.zodiac.const import ( ATTR_ELEMENT, ATTR_MODALITY, @@ -17,6 +18,8 @@ from homeassistant.components.zodiac.const import ( SIGN_SCORPIO, SIGN_TAURUS, ) +from homeassistant.const import ATTR_DEVICE_CLASS +from homeassistant.helpers import entity_registry as er from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util @@ -48,3 +51,24 @@ async def test_zodiac_day(hass, now, sign, element, modality): assert state.attributes assert state.attributes[ATTR_ELEMENT] == element assert state.attributes[ATTR_MODALITY] == modality + assert state.attributes[ATTR_DEVICE_CLASS] == SensorDeviceClass.ENUM + assert state.attributes[ATTR_OPTIONS] == [ + "aquarius", + "aries", + "cancer", + "capricorn", + "gemini", + "leo", + "libra", + "pisces", + "sagittarius", + "scorpio", + "taurus", + "virgo", + ] + + entity_registry = er.async_get(hass) + entry = entity_registry.async_get("sensor.zodiac") + assert entry + assert entry.unique_id == "zodiac" + assert entry.translation_key == "sign" diff --git a/tests/components/zwave_js/test_api.py b/tests/components/zwave_js/test_api.py index caea283e25c..4ea0669f0cb 100644 --- a/tests/components/zwave_js/test_api.py +++ b/tests/components/zwave_js/test_api.py @@ -87,6 +87,26 @@ def get_device(hass, node): return dev_reg.async_get_device({device_id}) +async def test_no_driver( + hass, client, multisensor_6, controller_state, integration, hass_ws_client +): + """Test driver missing results in error.""" + entry = integration + ws_client = await hass_ws_client(hass) + client.driver = None + + # Try API call with entry ID + await ws_client.send_json( + { + ID: 1, + TYPE: "zwave_js/network_status", + ENTRY_ID: entry.entry_id, + } + ) + msg = await ws_client.receive_json() + assert not msg["success"] + + async def test_network_status( hass, multisensor_6, controller_state, integration, hass_ws_client ): diff --git a/tests/components/zwave_js/test_cover.py b/tests/components/zwave_js/test_cover.py index 0ca2e36d853..d2c8fc32a73 100644 --- a/tests/components/zwave_js/test_cover.py +++ b/tests/components/zwave_js/test_cover.py @@ -267,6 +267,31 @@ async def test_fibaro_FGR222_shutter_cover( } assert args["value"] == 0 + # Test some tilt + event = Event( + type="value updated", + data={ + "source": "node", + "event": "value updated", + "nodeId": 42, + "args": { + "commandClassName": "Manufacturer Proprietary", + "commandClass": 145, + "endpoint": 0, + "property": "fibaro", + "propertyKey": "venetianBlindsTilt", + "newValue": 99, + "prevValue": 0, + "propertyName": "fibaro", + "propertyKeyName": "venetianBlindsTilt", + }, + }, + ) + fibaro_fgr222_shutter.receive_event(event) + state = hass.states.get(FIBARO_SHUTTER_COVER_ENTITY) + assert state + assert state.attributes[ATTR_CURRENT_TILT_POSITION] == 100 + async def test_aeotec_nano_shutter_cover( hass, client, aeotec_nano_shutter, integration diff --git a/tests/components/zwave_js/test_device_trigger.py b/tests/components/zwave_js/test_device_trigger.py index e9bc319fe4d..336923d83f7 100644 --- a/tests/components/zwave_js/test_device_trigger.py +++ b/tests/components/zwave_js/test_device_trigger.py @@ -788,7 +788,6 @@ async def test_if_central_scene_value_notification_fires( "property": "scene", "propertyName": "scene", "propertyKey": "001", - "propertyKey": "001", "value": 0, "metadata": { "type": "number", diff --git a/tests/components/zwave_js/test_number.py b/tests/components/zwave_js/test_number.py index e36bd081b18..ff551ce554b 100644 --- a/tests/components/zwave_js/test_number.py +++ b/tests/components/zwave_js/test_number.py @@ -1,11 +1,17 @@ """Test the Z-Wave JS number platform.""" +from unittest.mock import patch + +import pytest from zwave_js_server.event import Event from homeassistant.const import STATE_UNKNOWN +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_registry as er from .common import BASIC_NUMBER_ENTITY +from tests.common import MockConfigEntry + NUMBER_ENTITY = "number.thermostat_hvac_valve_control" VOLUME_NUMBER_ENTITY = "number.indoor_siren_6_default_volume_2" @@ -63,6 +69,66 @@ async def test_number(hass, client, aeotec_radiator_thermostat, integration): assert state.state == "99.0" +@pytest.fixture(name="no_target_value") +def mock_client_fixture(): + """Mock no target_value.""" + + with patch( + "homeassistant.components.zwave_js.number.ZwaveNumberEntity.get_zwave_value", + return_value=None, + ): + yield + + +async def test_number_no_target_value( + hass, client, no_target_value, aeotec_radiator_thermostat, integration +): + """Test the number entity with no target value.""" + # Test turn on setting value fails + with pytest.raises(HomeAssistantError): + await hass.services.async_call( + "number", + "set_value", + {"entity_id": NUMBER_ENTITY, "value": 30}, + blocking=True, + ) + + +async def test_number_writeable(hass, client, aeotec_radiator_thermostat): + """Test the number entity where current value is writeable.""" + aeotec_radiator_thermostat.values["4-38-0-currentValue"].metadata.data[ + "writeable" + ] = True + aeotec_radiator_thermostat.values.pop("4-38-0-targetValue") + + # set up config entry + entry = MockConfigEntry(domain="zwave_js", data={"url": "ws://test.org"}) + entry.add_to_hass(hass) + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + # Test turn on setting value + await hass.services.async_call( + "number", + "set_value", + {"entity_id": NUMBER_ENTITY, "value": 30}, + blocking=True, + ) + + assert len(client.async_send_command.call_args_list) == 1 + args = client.async_send_command.call_args[0][0] + assert args["command"] == "node.set_value" + assert args["nodeId"] == 4 + assert args["valueId"] == { + "commandClass": 38, + "endpoint": 0, + "property": "currentValue", + } + assert args["value"] == 30.0 + + client.async_send_command.reset_mock() + + async def test_volume_number(hass, client, aeotec_zw164_siren, integration): """Test the volume number entity.""" node = aeotec_zw164_siren diff --git a/tests/components/zwave_js/test_sensor.py b/tests/components/zwave_js/test_sensor.py index a32537b1d0d..59ca814a197 100644 --- a/tests/components/zwave_js/test_sensor.py +++ b/tests/components/zwave_js/test_sensor.py @@ -24,12 +24,13 @@ from homeassistant.const import ( ATTR_ENTITY_ID, ATTR_ICON, ATTR_UNIT_OF_MEASUREMENT, - ELECTRIC_CURRENT_AMPERE, - ELECTRIC_POTENTIAL_VOLT, - ENERGY_KILO_WATT_HOUR, - POWER_WATT, + PERCENTAGE, STATE_UNAVAILABLE, - TEMP_CELSIUS, + UnitOfElectricCurrent, + UnitOfElectricPotential, + UnitOfEnergy, + UnitOfPower, + UnitOfTemperature, ) from homeassistant.helpers import entity_registry as er from homeassistant.helpers.entity import EntityCategory @@ -49,21 +50,25 @@ from .common import ( ) -async def test_numeric_sensor(hass, multisensor_6, integration): +async def test_numeric_sensor( + hass, multisensor_6, express_controls_ezmultipli, integration +): """Test the numeric sensor.""" state = hass.states.get(AIR_TEMPERATURE_SENSOR) assert state assert state.state == "9.0" - assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == TEMP_CELSIUS + assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == UnitOfTemperature.CELSIUS assert state.attributes[ATTR_DEVICE_CLASS] == SensorDeviceClass.TEMPERATURE + assert state.attributes[ATTR_STATE_CLASS] == SensorStateClass.MEASUREMENT state = hass.states.get(BATTERY_SENSOR) assert state assert state.state == "100.0" - assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == "%" + assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == PERCENTAGE assert state.attributes[ATTR_DEVICE_CLASS] == SensorDeviceClass.BATTERY + assert state.attributes[ATTR_STATE_CLASS] == SensorStateClass.MEASUREMENT ent_reg = er.async_get(hass) entity_entry = ent_reg.async_get(BATTERY_SENSOR) @@ -74,8 +79,27 @@ async def test_numeric_sensor(hass, multisensor_6, integration): assert state assert state.state == "65.0" - assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == "%" + assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == PERCENTAGE assert state.attributes[ATTR_DEVICE_CLASS] == SensorDeviceClass.HUMIDITY + assert state.attributes[ATTR_STATE_CLASS] == SensorStateClass.MEASUREMENT + + state = hass.states.get("sensor.multisensor_6_ultraviolet") + + assert state + assert state.state == "0.0" + # TODO: Add UV_INDEX unit of measurement to this sensor + assert ATTR_UNIT_OF_MEASUREMENT not in state.attributes + assert ATTR_DEVICE_CLASS not in state.attributes + # TODO: Add measurement state class to this sensor + assert ATTR_STATE_CLASS not in state.attributes + + state = hass.states.get("sensor.hsm200_illuminance") + + assert state + assert state.state == "61.0" + assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == PERCENTAGE + assert ATTR_DEVICE_CLASS not in state.attributes + assert state.attributes[ATTR_STATE_CLASS] == SensorStateClass.MEASUREMENT async def test_energy_sensors(hass, hank_binary_switch, integration): @@ -84,7 +108,7 @@ async def test_energy_sensors(hass, hank_binary_switch, integration): assert state assert state.state == "0.0" - assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == POWER_WATT + assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == UnitOfPower.WATT assert state.attributes[ATTR_DEVICE_CLASS] == SensorDeviceClass.POWER assert state.attributes[ATTR_STATE_CLASS] is SensorStateClass.MEASUREMENT @@ -92,7 +116,7 @@ async def test_energy_sensors(hass, hank_binary_switch, integration): assert state assert state.state == "0.16" - assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == ENERGY_KILO_WATT_HOUR + assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == UnitOfEnergy.KILO_WATT_HOUR assert state.attributes[ATTR_DEVICE_CLASS] == SensorDeviceClass.ENERGY assert state.attributes[ATTR_STATE_CLASS] is SensorStateClass.TOTAL_INCREASING @@ -100,14 +124,14 @@ async def test_energy_sensors(hass, hank_binary_switch, integration): assert state assert state.state == "122.96" - assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == ELECTRIC_POTENTIAL_VOLT + assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == UnitOfElectricPotential.VOLT assert state.attributes[ATTR_DEVICE_CLASS] == SensorDeviceClass.VOLTAGE state = hass.states.get(CURRENT_SENSOR) assert state assert state.state == "0.0" - assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == ELECTRIC_CURRENT_AMPERE + assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == UnitOfElectricCurrent.AMPERE assert state.attributes[ATTR_DEVICE_CLASS] == SensorDeviceClass.CURRENT @@ -401,7 +425,8 @@ async def test_unit_change(hass, zp3111, client, integration): state = hass.states.get(entity_id) assert state assert state.state == "21.98" - assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == TEMP_CELSIUS + assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == UnitOfTemperature.CELSIUS + assert state.attributes[ATTR_DEVICE_CLASS] == SensorDeviceClass.TEMPERATURE event = Event( "metadata updated", { @@ -431,7 +456,8 @@ async def test_unit_change(hass, zp3111, client, integration): state = hass.states.get(entity_id) assert state assert state.state == "21.98" - assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == TEMP_CELSIUS + assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == UnitOfTemperature.CELSIUS + assert state.attributes[ATTR_DEVICE_CLASS] == SensorDeviceClass.TEMPERATURE event = Event( "value updated", { @@ -454,4 +480,5 @@ async def test_unit_change(hass, zp3111, client, integration): state = hass.states.get(entity_id) assert state assert state.state == "100.0" - assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == TEMP_CELSIUS + assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == UnitOfTemperature.CELSIUS + assert state.attributes[ATTR_DEVICE_CLASS] == SensorDeviceClass.TEMPERATURE diff --git a/tests/conftest.py b/tests/conftest.py index 4dc988db8fc..072ce9e112b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -7,14 +7,13 @@ from contextlib import asynccontextmanager import functools import gc import itertools -from json import JSONDecoder, loads +from json import JSONDecoder import logging import sqlite3 import ssl import threading from typing import Any from unittest.mock import AsyncMock, MagicMock, Mock, patch -import warnings from aiohttp import client from aiohttp.pytest_plugin import AiohttpClient @@ -46,6 +45,7 @@ from homeassistant.components.websocket_api.http import URL from homeassistant.const import HASSIO_USER_NAME from homeassistant.core import CoreState, HomeAssistant from homeassistant.helpers import config_entry_oauth2_flow, recorder as recorder_helper +from homeassistant.helpers.json import json_loads from homeassistant.helpers.typing import ConfigType from homeassistant.setup import async_setup_component from homeassistant.util import dt as dt_util, location @@ -212,14 +212,14 @@ def verify_cleanup(event_loop: asyncio.AbstractEventLoop): # before moving on to the next test. tasks = asyncio.all_tasks(event_loop) - tasks_before for task in tasks: - warnings.warn(f"Linger task after test {task}") + _LOGGER.warning("Linger task after test %r", task) task.cancel() if tasks: event_loop.run_until_complete(asyncio.wait(tasks)) for handle in event_loop._scheduled: # pylint: disable=protected-access if not handle.cancelled(): - warnings.warn(f"Lingering timer after test {handle}") + _LOGGER.warning("Lingering timer after test %r", handle) handle.cancel() # Make sure garbage collect run in same test as allocation @@ -272,7 +272,7 @@ class CoalescingResponse(client.ClientWebSocketResponse): async def receive_json( self, *, - loads: JSONDecoder = loads, + loads: JSONDecoder = json_loads, timeout: float | None = None, ) -> Any: """receive_json or from buffer.""" @@ -1093,6 +1093,9 @@ async def mock_enable_bluetooth( entry.add_to_hass(hass) await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() + yield + await hass.config_entries.async_unload(entry.entry_id) + await hass.async_block_till_done() @pytest.fixture(name="mock_bluetooth_adapters") diff --git a/tests/fixtures/homematicip_cloud.json b/tests/fixtures/homematicip_cloud.json index 3e3536a2f42..c54327069a2 100644 --- a/tests/fixtures/homematicip_cloud.json +++ b/tests/fixtures/homematicip_cloud.json @@ -6977,6 +6977,148 @@ "permanentlyReachable": true, "supported": true, "type": "EXTERNAL" + }, + "3014F711A000DIN_RAIL_DIMMER3": { + "availableFirmwareVersion": "1.2.0", + "connectionType": "HMIP_RF", + "firmwareVersion": "1.2.0", + "firmwareVersionInteger": 66048, + "functionalChannels": { + "0": { + "busConfigMismatch": null, + "coProFaulty": false, + "coProRestartNeeded": false, + "coProUpdateFailure": false, + "configPending": false, + "deviceId": "3014F711A000DIN_RAIL_DIMMER3", + "deviceOverheated": false, + "deviceOverloaded": false, + "devicePowerFailureDetected": false, + "deviceUndervoltage": false, + "dutyCycle": false, + "functionalChannelType": "DEVICE_BASE", + "groupIndex": 0, + "groups": ["49bf77e1-a07b-4a3c-8151-e0a7aca331bb"], + "index": 0, + "label": "", + "lowBat": null, + "mountingOrientation": null, + "multicastRoutingEnabled": false, + "particulateMatterSensorCommunicationError": null, + "particulateMatterSensorError": null, + "powerShortCircuit": null, + "profilePeriodLimitReached": null, + "routerModuleEnabled": false, + "routerModuleSupported": false, + "rssiDeviceValue": -64, + "rssiPeerValue": -66, + "shortCircuitDataLine": null, + "supportedOptionalFeatures": { + "IFeatureBusConfigMismatch": false, + "IFeatureDeviceCoProError": false, + "IFeatureDeviceCoProRestart": false, + "IFeatureDeviceCoProUpdate": false, + "IFeatureDeviceIdentify": true, + "IFeatureDeviceOverheated": true, + "IFeatureDeviceOverloaded": false, + "IFeatureDeviceParticulateMatterSensorCommunicationError": false, + "IFeatureDeviceParticulateMatterSensorError": false, + "IFeatureDevicePowerFailure": true, + "IFeatureDeviceTemperatureHumiditySensorCommunicationError": false, + "IFeatureDeviceTemperatureHumiditySensorError": false, + "IFeatureDeviceTemperatureOutOfRange": false, + "IFeatureDeviceUndervoltage": false, + "IFeatureMulticastRouter": false, + "IFeaturePowerShortCircuit": false, + "IFeatureProfilePeriodLimit": true, + "IFeatureRssiValue": true, + "IFeatureShortCircuitDataLine": false, + "IOptionalFeatureDutyCycle": true, + "IOptionalFeatureLowBat": false, + "IOptionalFeatureMountingOrientation": false + }, + "temperatureHumiditySensorCommunicationError": null, + "temperatureHumiditySensorError": null, + "temperatureOutOfRange": false, + "unreach": false + }, + "1": { + "binaryBehaviorType": "NORMALLY_CLOSE", + "coProFaulty": true, + "coProRestartNeeded": false, + "deviceId": "3014F711A000DIN_RAIL_DIMMER3", + "deviceOverheated": false, + "deviceOverloaded": false, + "dimLevel": 0.1, + "functionalChannelType": "MULTI_MODE_INPUT_DIMMER_CHANNEL", + "groupIndex": 1, + "groups": [], + "index": 1, + "label": "", + "multiModeInputMode": "KEY_BEHAVIOR", + "on": true, + "profileMode": "AUTOMATIC", + "supportedOptionalFeatures": { + "IFeatureDeviceOverloaded": true + }, + "userDesiredProfileMode": "AUTOMATIC" + }, + "2": { + "binaryBehaviorType": "NORMALLY_CLOSE", + "coProFaulty": true, + "coProRestartNeeded": false, + "deviceId": "3014F711A000DIN_RAIL_DIMMER3", + "deviceOverheated": false, + "deviceOverloaded": false, + "dimLevel": 0.2, + "functionalChannelType": "MULTI_MODE_INPUT_DIMMER_CHANNEL", + "groupIndex": 2, + "groups": [], + "index": 2, + "label": "", + "multiModeInputMode": "KEY_BEHAVIOR", + "on": true, + "profileMode": "AUTOMATIC", + "supportedOptionalFeatures": { + "IFeatureDeviceOverloaded": true + }, + "userDesiredProfileMode": "AUTOMATIC" + }, + "3": { + "binaryBehaviorType": "NORMALLY_CLOSE", + "coProFaulty": false, + "coProRestartNeeded": false, + "deviceId": "3014F711A000DIN_RAIL_DIMMER3", + "deviceOverheated": false, + "deviceOverloaded": false, + "dimLevel": 0.3, + "functionalChannelType": "MULTI_MODE_INPUT_DIMMER_CHANNEL", + "groupIndex": 3, + "groups": [], + "index": 3, + "label": "Esstisch", + "multiModeInputMode": "KEY_BEHAVIOR", + "on": true, + "profileMode": "AUTOMATIC", + "supportedOptionalFeatures": { + "IFeatureDeviceOverloaded": true + }, + "userDesiredProfileMode": "AUTOMATIC" + } + }, + "homeId": "00000000-0000-0000-0000-000000000001", + "id": "3014F711A000DIN_RAIL_DIMMER3", + "label": "3-Dimmer", + "lastStatusUpdate": 1618223872902, + "liveUpdateState": "LIVE_UPDATE_NOT_SUPPORTED", + "manufacturerCode": 1, + "modelId": 407, + "modelType": "HmIP-DRDI3", + "oem": "eQ-3", + "permanentlyReachable": true, + "serializedGlobalTradeItemNumber": "3014F711A000DIN_RAIL_DIMMER3", + "type": "DIN_RAIL_DIMMER_3", + "updateState": "UP_TO_DATE" } }, "groups": { diff --git a/tests/hassfest/test_requirements.py b/tests/hassfest/test_requirements.py index a529c0769d6..fc366db74fa 100644 --- a/tests/hassfest/test_requirements.py +++ b/tests/hassfest/test_requirements.py @@ -12,7 +12,7 @@ def integration(): """Fixture for hassfest integration model.""" integration = Integration( path=Path("homeassistant/components/test"), - manifest={ + _manifest={ "domain": "test", "documentation": "https://example.com", "name": "test", diff --git a/tests/helpers/test_area_registry.py b/tests/helpers/test_area_registry.py index 7dca029987e..ea42417aef9 100644 --- a/tests/helpers/test_area_registry.py +++ b/tests/helpers/test_area_registry.py @@ -4,7 +4,7 @@ import pytest from homeassistant.core import callback from homeassistant.helpers import area_registry -from tests.common import flush_store, mock_area_registry +from tests.common import ANY, flush_store, mock_area_registry @pytest.fixture @@ -38,17 +38,39 @@ async def test_list_areas(registry): async def test_create_area(hass, registry, update_events): """Make sure that we can create an area.""" + # Create area with only mandatory parameters area = registry.async_create("mock") - assert area.id == "mock" - assert area.name == "mock" + assert area == area_registry.AreaEntry( + name="mock", normalized_name=ANY, aliases=set(), id=ANY, picture=None + ) assert len(registry.areas) == 1 await hass.async_block_till_done() assert len(update_events) == 1 - assert update_events[0]["action"] == "create" - assert update_events[0]["area_id"] == area.id + assert update_events[-1]["action"] == "create" + assert update_events[-1]["area_id"] == area.id + + # Create area with all parameters + area = registry.async_create( + "mock 2", aliases={"alias_1", "alias_2"}, picture="/image/example.png" + ) + + assert area == area_registry.AreaEntry( + name="mock 2", + normalized_name=ANY, + aliases={"alias_1", "alias_2"}, + id=ANY, + picture="/image/example.png", + ) + assert len(registry.areas) == 2 + + await hass.async_block_till_done() + + assert len(update_events) == 2 + assert update_events[-1]["action"] == "create" + assert update_events[-1]["area_id"] == area.id async def test_create_area_with_name_already_in_use(hass, registry, update_events): @@ -70,7 +92,7 @@ async def test_create_area_with_id_already_in_use(registry): """Make sure that we can't create an area with a name already in use.""" area1 = registry.async_create("mock") - updated_area1 = registry.async_update(area1.id, "New Name") + updated_area1 = registry.async_update(area1.id, name="New Name") assert updated_area1.id == area1.id area2 = registry.async_create("mock") @@ -108,10 +130,21 @@ async def test_update_area(hass, registry, update_events): """Make sure that we can read areas.""" area = registry.async_create("mock") - updated_area = registry.async_update(area.id, name="mock1") + updated_area = registry.async_update( + area.id, + aliases={"alias_1", "alias_2"}, + name="mock1", + picture="/image/example.png", + ) assert updated_area != area - assert updated_area.name == "mock1" + assert updated_area == area_registry.AreaEntry( + name="mock1", + normalized_name=ANY, + aliases={"alias_1", "alias_2"}, + id=ANY, + picture="/image/example.png", + ) assert len(registry.areas) == 1 await hass.async_block_till_done() @@ -196,8 +229,18 @@ async def test_load_area(hass, registry): async def test_loading_area_from_storage(hass, hass_storage): """Test loading stored areas on start.""" hass_storage[area_registry.STORAGE_KEY] = { - "version": area_registry.STORAGE_VERSION, - "data": {"areas": [{"id": "12345A", "name": "mock"}]}, + "version": area_registry.STORAGE_VERSION_MAJOR, + "minor_version": area_registry.STORAGE_VERSION_MINOR, + "data": { + "areas": [ + { + "aliases": ["alias_1", "alias_2"], + "id": "12345A", + "name": "mock", + "picture": "blah", + } + ] + }, } await area_registry.async_load(hass) @@ -206,6 +249,33 @@ async def test_loading_area_from_storage(hass, hass_storage): assert len(registry.areas) == 1 +@pytest.mark.parametrize("load_registries", [False]) +async def test_migration_from_1_1(hass, hass_storage): + """Test migration from version 1.1.""" + hass_storage[area_registry.STORAGE_KEY] = { + "version": 1, + "data": {"areas": [{"id": "12345A", "name": "mock"}]}, + } + + await area_registry.async_load(hass) + registry = area_registry.async_get(hass) + + # Test data was loaded + entry = registry.async_get_or_create("mock") + assert entry.id == "12345A" + + # Check we store migrated data + await flush_store(registry._store) + assert hass_storage[area_registry.STORAGE_KEY] == { + "version": area_registry.STORAGE_VERSION_MAJOR, + "minor_version": area_registry.STORAGE_VERSION_MINOR, + "key": area_registry.STORAGE_KEY, + "data": { + "areas": [{"aliases": [], "id": "12345A", "name": "mock", "picture": None}] + }, + } + + async def test_async_get_or_create(hass, registry): """Make sure we can get the area by name.""" area = registry.async_get_or_create("Mock1") diff --git a/tests/helpers/test_entity.py b/tests/helpers/test_entity.py index 698d3cfe98a..d359efd6325 100644 --- a/tests/helpers/test_entity.py +++ b/tests/helpers/test_entity.py @@ -934,3 +934,23 @@ async def test_friendly_name( assert len(hass.states.async_entity_ids()) == 1 state = hass.states.async_all()[0] assert state.attributes.get(ATTR_FRIENDLY_NAME) == expected_friendly_name + + +async def test_translation_key(hass): + """Test translation key property.""" + mock_entity1 = entity.Entity() + mock_entity1.hass = hass + mock_entity1.entity_description = entity.EntityDescription( + key="abc", translation_key="from_entity_description" + ) + mock_entity1.entity_id = "hello.world" + mock_entity1._attr_translation_key = "from_attr" + assert mock_entity1.translation_key == "from_attr" + + mock_entity2 = entity.Entity() + mock_entity2.hass = hass + mock_entity2.entity_description = entity.EntityDescription( + key="abc", translation_key="from_entity_description" + ) + mock_entity2.entity_id = "hello.world" + assert mock_entity2.translation_key == "from_entity_description" diff --git a/tests/helpers/test_entity_platform.py b/tests/helpers/test_entity_platform.py index b2af85ca631..c7721f7f78c 100644 --- a/tests/helpers/test_entity_platform.py +++ b/tests/helpers/test_entity_platform.py @@ -1227,6 +1227,7 @@ async def test_entity_info_added_to_entity_registry(hass): icon="nice:icon", name="best name", supported_features=5, + translation_key="my_translation_key", unique_id="default", unit_of_measurement=PERCENTAGE, ) @@ -1251,6 +1252,7 @@ async def test_entity_info_added_to_entity_registry(hass): original_icon="nice:icon", original_name="best name", supported_features=5, + translation_key="my_translation_key", unit_of_measurement=PERCENTAGE, ) diff --git a/tests/helpers/test_entity_registry.py b/tests/helpers/test_entity_registry.py index 5538950260c..b8e66255c48 100644 --- a/tests/helpers/test_entity_registry.py +++ b/tests/helpers/test_entity_registry.py @@ -84,6 +84,7 @@ def test_get_or_create_updates_data(registry): original_icon="initial-original_icon", original_name="initial-original_name", supported_features=5, + translation_key="initial-translation_key", unit_of_measurement="initial-unit_of_measurement", ) @@ -106,6 +107,7 @@ def test_get_or_create_updates_data(registry): original_icon="initial-original_icon", original_name="initial-original_name", supported_features=5, + translation_key="initial-translation_key", unit_of_measurement="initial-unit_of_measurement", ) @@ -126,6 +128,7 @@ def test_get_or_create_updates_data(registry): original_icon="updated-original_icon", original_name="updated-original_name", supported_features=10, + translation_key="updated-translation_key", unit_of_measurement="updated-unit_of_measurement", ) @@ -133,6 +136,7 @@ def test_get_or_create_updates_data(registry): "light.hue_5678", "5678", "hue", + aliases=set(), area_id=None, capabilities={"new-max": 150}, config_entry_id=new_config_entry.entry_id, @@ -149,6 +153,7 @@ def test_get_or_create_updates_data(registry): original_icon="updated-original_icon", original_name="updated-original_name", supported_features=10, + translation_key="updated-translation_key", unit_of_measurement="updated-unit_of_measurement", ) @@ -167,6 +172,7 @@ def test_get_or_create_updates_data(registry): original_icon=None, original_name=None, supported_features=None, + translation_key=None, unit_of_measurement=None, ) @@ -174,6 +180,7 @@ def test_get_or_create_updates_data(registry): "light.hue_5678", "5678", "hue", + aliases=set(), area_id=None, capabilities=None, config_entry_id=None, @@ -190,6 +197,7 @@ def test_get_or_create_updates_data(registry): original_icon=None, original_name=None, supported_features=0, # supported_features is stored as an int + translation_key=None, unit_of_measurement=None, ) @@ -242,10 +250,12 @@ async def test_loading_saving_data(hass, registry): original_icon="hass:original-icon", original_name="Original Name", supported_features=5, + translation_key="initial-translation_key", unit_of_measurement="initial-unit_of_measurement", ) registry.async_update_entity( orig_entry2.entity_id, + aliases={"initial_alias_1", "initial_alias_2"}, area_id="mock-area-id", device_class="user-class", name="User Name", @@ -287,6 +297,7 @@ async def test_loading_saving_data(hass, registry): assert new_entry2.original_icon == "hass:original-icon" assert new_entry2.original_name == "Original Name" assert new_entry2.supported_features == 5 + assert new_entry2.translation_key == "initial-translation_key" assert new_entry2.unit_of_measurement == "initial-unit_of_measurement" @@ -655,9 +666,10 @@ async def test_update_entity(registry): ) for attr_name, new_value in ( - ("name", "new name"), - ("icon", "new icon"), + ("aliases", {"alias_1", "alias_2"}), ("disabled_by", er.RegistryEntryDisabler.USER), + ("icon", "new icon"), + ("name", "new name"), ): changes = {attr_name: new_value} updated_entry = registry.async_update_entity(entry.entity_id, **changes) diff --git a/tests/helpers/test_schema_config_entry_flow.py b/tests/helpers/test_schema_config_entry_flow.py index b01da8d3d2a..00935677a21 100644 --- a/tests/helpers/test_schema_config_entry_flow.py +++ b/tests/helpers/test_schema_config_entry_flow.py @@ -1,6 +1,7 @@ """Tests for the schema based data entry flows.""" from __future__ import annotations +from collections.abc import Mapping from typing import Any from unittest.mock import patch @@ -8,7 +9,7 @@ import pytest import voluptuous as vol from homeassistant import config_entries, data_entry_flow -from homeassistant.core import HomeAssistant +from homeassistant.core import HomeAssistant, callback from homeassistant.data_entry_flow import FlowResultType from homeassistant.helpers import entity_registry as er from homeassistant.helpers.schema_config_entry_flow import ( @@ -27,6 +28,17 @@ from tests.common import MockConfigEntry, mock_platform TEST_DOMAIN = "test" +class TestSchemaConfigFlowHandler(SchemaConfigFlowHandler): + """Bare minimum SchemaConfigFlowHandler.""" + + config_flow = {} + + @callback + def async_config_entry_title(self, options: Mapping[str, Any]) -> str: + """Return config entry title.""" + return "title" + + @pytest.fixture(name="manager") def manager_fixture(): """Return a flow manager.""" @@ -116,7 +128,7 @@ async def test_config_flow_advanced_option( } @manager.mock_reg_handler("test") - class TestFlow(SchemaConfigFlowHandler): + class TestFlow(TestSchemaConfigFlowHandler): config_flow = CONFIG_FLOW # Start flow in basic mode @@ -210,7 +222,7 @@ async def test_options_flow_advanced_option( "init": SchemaFlowFormStep(OPTIONS_SCHEMA) } - class TestFlow(SchemaConfigFlowHandler, domain="test"): + class TestFlow(TestSchemaConfigFlowHandler, domain="test"): config_flow = {} options_flow = OPTIONS_FLOW @@ -314,7 +326,7 @@ async def test_menu_step(hass: HomeAssistant) -> None: "option4": SchemaFlowFormStep(vol.Schema({})), } - class TestConfigFlow(SchemaConfigFlowHandler, domain=TEST_DOMAIN): + class TestConfigFlow(TestSchemaConfigFlowHandler, domain=TEST_DOMAIN): """Handle a config or options flow for Derivative.""" config_flow = CONFIG_FLOW @@ -363,7 +375,7 @@ async def test_schema_none(hass: HomeAssistant) -> None: "option3": SchemaFlowFormStep(vol.Schema({})), } - class TestConfigFlow(SchemaConfigFlowHandler, domain=TEST_DOMAIN): + class TestConfigFlow(TestSchemaConfigFlowHandler, domain=TEST_DOMAIN): """Handle a config or options flow for Derivative.""" config_flow = CONFIG_FLOW @@ -397,7 +409,7 @@ async def test_last_step(hass: HomeAssistant) -> None: "step3": SchemaFlowFormStep(vol.Schema({}), next_step=None), } - class TestConfigFlow(SchemaConfigFlowHandler, domain=TEST_DOMAIN): + class TestConfigFlow(TestSchemaConfigFlowHandler, domain=TEST_DOMAIN): """Handle a config or options flow for Derivative.""" config_flow = CONFIG_FLOW @@ -440,7 +452,7 @@ async def test_next_step_function(hass: HomeAssistant) -> None: "step2": SchemaFlowFormStep(vol.Schema({}), next_step=_step2_next_step), } - class TestConfigFlow(SchemaConfigFlowHandler, domain=TEST_DOMAIN): + class TestConfigFlow(TestSchemaConfigFlowHandler, domain=TEST_DOMAIN): """Handle a config or options flow for Derivative.""" config_flow = CONFIG_FLOW @@ -497,7 +509,7 @@ async def test_suggested_values( ), } - class TestFlow(SchemaConfigFlowHandler, domain="test"): + class TestFlow(TestSchemaConfigFlowHandler, domain="test"): config_flow = {} options_flow = OPTIONS_FLOW @@ -608,7 +620,7 @@ async def test_options_flow_state(hass: HomeAssistant) -> None: ), } - class TestFlow(SchemaConfigFlowHandler, domain="test"): + class TestFlow(TestSchemaConfigFlowHandler, domain="test"): config_flow = {} options_flow = OPTIONS_FLOW diff --git a/tests/pylint/test_enforce_type_hints.py b/tests/pylint/test_enforce_type_hints.py index 9abd2c89a74..665a4d6594c 100644 --- a/tests/pylint/test_enforce_type_hints.py +++ b/tests/pylint/test_enforce_type_hints.py @@ -898,7 +898,7 @@ def test_invalid_device_class( pylint.testutils.MessageTest( msg_id="hass-return-type", node=prop_node, - args=(["CoverDeviceClass", "str", None], "device_class"), + args=(["CoverDeviceClass", None], "device_class"), line=12, col_offset=4, end_line=12, diff --git a/tests/test_config_entries.py b/tests/test_config_entries.py index b1e3fd760d5..994c220adc4 100644 --- a/tests/test_config_entries.py +++ b/tests/test_config_entries.py @@ -903,7 +903,7 @@ async def test_setup_raise_not_ready(hass, caplog): p_hass, p_wait_time, p_setup = mock_call.mock_calls[0][1] assert p_hass is hass - assert p_wait_time == 5 + assert 5 <= p_wait_time <= 5.5 assert entry.state is config_entries.ConfigEntryState.SETUP_RETRY assert entry.reason == "The internet connection is offline" diff --git a/tests/testing_config/custom_components/test/sensor.py b/tests/testing_config/custom_components/test/sensor.py index 9584e47ba0b..9b3911313a6 100644 --- a/tests/testing_config/custom_components/test/sensor.py +++ b/tests/testing_config/custom_components/test/sensor.py @@ -13,6 +13,7 @@ from homeassistant.const import ( CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, CONCENTRATION_PARTS_PER_MILLION, FREQUENCY_GIGAHERTZ, + LIGHT_LUX, PERCENTAGE, POWER_VOLT_AMPERE, POWER_VOLT_AMPERE_REACTIVE, @@ -31,7 +32,7 @@ UNITS_OF_MEASUREMENT = { SensorDeviceClass.CO: CONCENTRATION_PARTS_PER_MILLION, # ppm of CO concentration SensorDeviceClass.CO2: CONCENTRATION_PARTS_PER_MILLION, # ppm of CO2 concentration SensorDeviceClass.HUMIDITY: PERCENTAGE, # % of humidity in the air - SensorDeviceClass.ILLUMINANCE: "lm", # current light level (lx/lm) + SensorDeviceClass.ILLUMINANCE: LIGHT_LUX, # current light level lx SensorDeviceClass.MOISTURE: PERCENTAGE, # % of water in a substance SensorDeviceClass.NITROGEN_DIOXIDE: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, # µg/m³ of nitrogen dioxide SensorDeviceClass.NITROGEN_MONOXIDE: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, # µg/m³ of nitrogen monoxide @@ -70,7 +71,7 @@ def init(empty=False): name=f"{device_class} sensor", unique_id=f"unique_{device_class}", device_class=device_class, - unit_of_measurement=UNITS_OF_MEASUREMENT.get(device_class), + native_unit_of_measurement=UNITS_OF_MEASUREMENT.get(device_class), ) for device_class in DEVICE_CLASSES } @@ -107,6 +108,11 @@ class MockSensor(MockEntity, SensorEntity): """Return the native value of this sensor.""" return self._handle("native_value") + @property + def options(self): + """Return the options for this sensor.""" + return self._handle("options") + @property def state_class(self): """Return the state class of this sensor.""" diff --git a/tests/util/test_unit_conversion.py b/tests/util/test_unit_conversion.py index b2b99d6f8c0..83aaf6224b5 100644 --- a/tests/util/test_unit_conversion.py +++ b/tests/util/test_unit_conversion.py @@ -2,7 +2,10 @@ import pytest from homeassistant.const import ( + UnitOfDataRate, + UnitOfElectricCurrent, UnitOfEnergy, + UnitOfInformation, UnitOfLength, UnitOfMass, UnitOfPower, @@ -15,8 +18,11 @@ from homeassistant.const import ( from homeassistant.exceptions import HomeAssistantError from homeassistant.util.unit_conversion import ( BaseUnitConverter, + DataRateConverter, DistanceConverter, + ElectricCurrentConverter, EnergyConverter, + InformationConverter, MassConverter, PowerConverter, PressureConverter, @@ -31,6 +37,7 @@ INVALID_SYMBOL = "bob" @pytest.mark.parametrize( "converter,valid_unit", [ + (DataRateConverter, UnitOfDataRate.GIBIBYTES_PER_SECOND), (DistanceConverter, UnitOfLength.KILOMETERS), (DistanceConverter, UnitOfLength.METERS), (DistanceConverter, UnitOfLength.CENTIMETERS), @@ -39,10 +46,13 @@ INVALID_SYMBOL = "bob" (DistanceConverter, UnitOfLength.YARDS), (DistanceConverter, UnitOfLength.FEET), (DistanceConverter, UnitOfLength.INCHES), + (ElectricCurrentConverter, UnitOfElectricCurrent.AMPERE), + (ElectricCurrentConverter, UnitOfElectricCurrent.MILLIAMPERE), (EnergyConverter, UnitOfEnergy.WATT_HOUR), (EnergyConverter, UnitOfEnergy.KILO_WATT_HOUR), (EnergyConverter, UnitOfEnergy.MEGA_WATT_HOUR), (EnergyConverter, UnitOfEnergy.GIGA_JOULE), + (InformationConverter, UnitOfInformation.GIGABYTES), (MassConverter, UnitOfMass.GRAMS), (MassConverter, UnitOfMass.KILOGRAMS), (MassConverter, UnitOfMass.MICROGRAMS), @@ -85,8 +95,11 @@ def test_convert_same_unit(converter: type[BaseUnitConverter], valid_unit: str) @pytest.mark.parametrize( "converter,valid_unit", [ + (DataRateConverter, UnitOfDataRate.GIBIBYTES_PER_SECOND), (DistanceConverter, UnitOfLength.KILOMETERS), + (ElectricCurrentConverter, UnitOfElectricCurrent.AMPERE), (EnergyConverter, UnitOfEnergy.KILO_WATT_HOUR), + (InformationConverter, UnitOfInformation.GIBIBYTES), (MassConverter, UnitOfMass.GRAMS), (PowerConverter, UnitOfPower.WATT), (PressureConverter, UnitOfPressure.PA), @@ -111,8 +124,18 @@ def test_convert_invalid_unit( @pytest.mark.parametrize( "converter,from_unit,to_unit", [ + ( + DataRateConverter, + UnitOfDataRate.BYTES_PER_SECOND, + UnitOfDataRate.BITS_PER_SECOND, + ), (DistanceConverter, UnitOfLength.KILOMETERS, UnitOfLength.METERS), (EnergyConverter, UnitOfEnergy.WATT_HOUR, UnitOfEnergy.KILO_WATT_HOUR), + ( + InformationConverter, + UnitOfInformation.GIBIBYTES, + UnitOfInformation.GIGABYTES, + ), (MassConverter, UnitOfMass.GRAMS, UnitOfMass.KILOGRAMS), (PowerConverter, UnitOfPower.WATT, UnitOfPower.KILO_WATT), (PressureConverter, UnitOfPressure.HPA, UnitOfPressure.INHG), @@ -132,8 +155,21 @@ def test_convert_nonnumeric_value( @pytest.mark.parametrize( "converter,from_unit,to_unit,expected", [ + ( + DataRateConverter, + UnitOfDataRate.BITS_PER_SECOND, + UnitOfDataRate.BYTES_PER_SECOND, + 8, + ), (DistanceConverter, UnitOfLength.KILOMETERS, UnitOfLength.METERS, 1 / 1000), + ( + ElectricCurrentConverter, + UnitOfElectricCurrent.AMPERE, + UnitOfElectricCurrent.MILLIAMPERE, + 1 / 1000, + ), (EnergyConverter, UnitOfEnergy.WATT_HOUR, UnitOfEnergy.KILO_WATT_HOUR, 1000), + (InformationConverter, UnitOfInformation.BITS, UnitOfInformation.BYTES, 8), (PowerConverter, UnitOfPower.WATT, UnitOfPower.KILO_WATT, 1000), ( PressureConverter, @@ -168,6 +204,65 @@ def test_get_unit_ratio( assert converter.get_unit_ratio(from_unit, to_unit) == expected +@pytest.mark.parametrize( + "value,from_unit,expected,to_unit", + [ + (8e3, UnitOfDataRate.BITS_PER_SECOND, 8, UnitOfDataRate.KILOBITS_PER_SECOND), + (8e6, UnitOfDataRate.BITS_PER_SECOND, 8, UnitOfDataRate.MEGABITS_PER_SECOND), + (8e9, UnitOfDataRate.BITS_PER_SECOND, 8, UnitOfDataRate.GIGABITS_PER_SECOND), + (8, UnitOfDataRate.BITS_PER_SECOND, 1, UnitOfDataRate.BYTES_PER_SECOND), + (8e3, UnitOfDataRate.BITS_PER_SECOND, 1, UnitOfDataRate.KILOBYTES_PER_SECOND), + (8e6, UnitOfDataRate.BITS_PER_SECOND, 1, UnitOfDataRate.MEGABYTES_PER_SECOND), + (8e9, UnitOfDataRate.BITS_PER_SECOND, 1, UnitOfDataRate.GIGABYTES_PER_SECOND), + ( + 8 * 2**10, + UnitOfDataRate.BITS_PER_SECOND, + 1, + UnitOfDataRate.KIBIBYTES_PER_SECOND, + ), + ( + 8 * 2**20, + UnitOfDataRate.BITS_PER_SECOND, + 1, + UnitOfDataRate.MEBIBYTES_PER_SECOND, + ), + ( + 8 * 2**30, + UnitOfDataRate.BITS_PER_SECOND, + 1, + UnitOfDataRate.GIBIBYTES_PER_SECOND, + ), + ], +) +def test_data_rate_convert( + value: float, + from_unit: str, + expected: float, + to_unit: str, +) -> None: + """Test conversion to other units.""" + assert DataRateConverter.convert(value, from_unit, to_unit) == pytest.approx( + expected + ) + + +@pytest.mark.parametrize( + "value,from_unit,expected,to_unit", + [ + (5, UnitOfElectricCurrent.AMPERE, 5000, UnitOfElectricCurrent.MILLIAMPERE), + (5, UnitOfElectricCurrent.MILLIAMPERE, 0.005, UnitOfElectricCurrent.AMPERE), + ], +) +def test_electric_current_convert( + value: float, + from_unit: str, + expected: float, + to_unit: str, +) -> None: + """Test conversion to other units.""" + assert ElectricCurrentConverter.convert(value, from_unit, to_unit) == expected + + @pytest.mark.parametrize( "value,from_unit,expected,to_unit", [ @@ -307,6 +402,43 @@ def test_energy_convert( assert EnergyConverter.convert(value, from_unit, to_unit) == expected +@pytest.mark.parametrize( + "value,from_unit,expected,to_unit", + [ + (8e3, UnitOfInformation.BITS, 8, UnitOfInformation.KILOBITS), + (8e6, UnitOfInformation.BITS, 8, UnitOfInformation.MEGABITS), + (8e9, UnitOfInformation.BITS, 8, UnitOfInformation.GIGABITS), + (8, UnitOfInformation.BITS, 1, UnitOfInformation.BYTES), + (8e3, UnitOfInformation.BITS, 1, UnitOfInformation.KILOBYTES), + (8e6, UnitOfInformation.BITS, 1, UnitOfInformation.MEGABYTES), + (8e9, UnitOfInformation.BITS, 1, UnitOfInformation.GIGABYTES), + (8e12, UnitOfInformation.BITS, 1, UnitOfInformation.TERABYTES), + (8e15, UnitOfInformation.BITS, 1, UnitOfInformation.PETABYTES), + (8e18, UnitOfInformation.BITS, 1, UnitOfInformation.EXABYTES), + (8e21, UnitOfInformation.BITS, 1, UnitOfInformation.ZETTABYTES), + (8e24, UnitOfInformation.BITS, 1, UnitOfInformation.YOTTABYTES), + (8 * 2**10, UnitOfInformation.BITS, 1, UnitOfInformation.KIBIBYTES), + (8 * 2**20, UnitOfInformation.BITS, 1, UnitOfInformation.MEBIBYTES), + (8 * 2**30, UnitOfInformation.BITS, 1, UnitOfInformation.GIBIBYTES), + (8 * 2**40, UnitOfInformation.BITS, 1, UnitOfInformation.TEBIBYTES), + (8 * 2**50, UnitOfInformation.BITS, 1, UnitOfInformation.PEBIBYTES), + (8 * 2**60, UnitOfInformation.BITS, 1, UnitOfInformation.EXBIBYTES), + (8 * 2**70, UnitOfInformation.BITS, 1, UnitOfInformation.ZEBIBYTES), + (8 * 2**80, UnitOfInformation.BITS, 1, UnitOfInformation.YOBIBYTES), + ], +) +def test_information_convert( + value: float, + from_unit: str, + expected: float, + to_unit: str, +) -> None: + """Test conversion to other units.""" + assert InformationConverter.convert(value, from_unit, to_unit) == pytest.approx( + expected + ) + + @pytest.mark.parametrize( "value,from_unit,expected,to_unit", [ @@ -345,6 +477,11 @@ def test_energy_convert( (16, UnitOfMass.OUNCES, 453592.37, UnitOfMass.MILLIGRAMS), (16, UnitOfMass.OUNCES, 453592370, UnitOfMass.MICROGRAMS), (16, UnitOfMass.OUNCES, 1, UnitOfMass.POUNDS), + (1, UnitOfMass.STONES, pytest.approx(6.350293), UnitOfMass.KILOGRAMS), + (1, UnitOfMass.STONES, pytest.approx(6350.293), UnitOfMass.GRAMS), + (1, UnitOfMass.STONES, pytest.approx(6350293), UnitOfMass.MILLIGRAMS), + (1, UnitOfMass.STONES, pytest.approx(14), UnitOfMass.POUNDS), + (1, UnitOfMass.STONES, pytest.approx(224), UnitOfMass.OUNCES), ], ) def test_mass_convert( @@ -559,110 +696,45 @@ def test_temperature_convert_with_interval( @pytest.mark.parametrize( "value,from_unit,expected,to_unit", [ - (5, UnitOfVolume.LITERS, pytest.approx(1.32086), UnitOfVolume.GALLONS), - (5, UnitOfVolume.GALLONS, pytest.approx(18.92706), UnitOfVolume.LITERS), - ( - 5, - UnitOfVolume.CUBIC_METERS, - pytest.approx(176.5733335), - UnitOfVolume.CUBIC_FEET, - ), - ( - 500, - UnitOfVolume.CUBIC_FEET, - pytest.approx(14.1584233), - UnitOfVolume.CUBIC_METERS, - ), - ( - 500, - UnitOfVolume.CUBIC_FEET, - pytest.approx(14.1584233), - UnitOfVolume.CUBIC_METERS, - ), - ( - 500, - UnitOfVolume.CUBIC_FEET, - pytest.approx(478753.2467), - UnitOfVolume.FLUID_OUNCES, - ), - (500, UnitOfVolume.CUBIC_FEET, pytest.approx(3740.25974), UnitOfVolume.GALLONS), - ( - 500, - UnitOfVolume.CUBIC_FEET, - pytest.approx(14158.42329599), - UnitOfVolume.LITERS, - ), - ( - 500, - UnitOfVolume.CUBIC_FEET, - pytest.approx(14158423.29599), - UnitOfVolume.MILLILITERS, - ), + (5, UnitOfVolume.LITERS, 1.32086, UnitOfVolume.GALLONS), + (5, UnitOfVolume.GALLONS, 18.92706, UnitOfVolume.LITERS), + (5, UnitOfVolume.CUBIC_METERS, 176.5733335, UnitOfVolume.CUBIC_FEET), + (500, UnitOfVolume.CUBIC_FEET, 14.1584233, UnitOfVolume.CUBIC_METERS), + (500, UnitOfVolume.CUBIC_FEET, 14.1584233, UnitOfVolume.CUBIC_METERS), + (500, UnitOfVolume.CUBIC_FEET, 478753.2467, UnitOfVolume.FLUID_OUNCES), + (500, UnitOfVolume.CUBIC_FEET, 3740.25974, UnitOfVolume.GALLONS), + (500, UnitOfVolume.CUBIC_FEET, 14158.42329599, UnitOfVolume.LITERS), + (500, UnitOfVolume.CUBIC_FEET, 14158423.29599, UnitOfVolume.MILLILITERS), (500, UnitOfVolume.CUBIC_METERS, 500, UnitOfVolume.CUBIC_METERS), - ( - 500, - UnitOfVolume.CUBIC_METERS, - pytest.approx(16907011.35), - UnitOfVolume.FLUID_OUNCES, - ), - ( - 500, - UnitOfVolume.CUBIC_METERS, - pytest.approx(132086.02617), - UnitOfVolume.GALLONS, - ), + (500, UnitOfVolume.CUBIC_METERS, 16907011.35, UnitOfVolume.FLUID_OUNCES), + (500, UnitOfVolume.CUBIC_METERS, 132086.02617, UnitOfVolume.GALLONS), (500, UnitOfVolume.CUBIC_METERS, 500000, UnitOfVolume.LITERS), (500, UnitOfVolume.CUBIC_METERS, 500000000, UnitOfVolume.MILLILITERS), - ( - 500, - UnitOfVolume.FLUID_OUNCES, - pytest.approx(0.52218967), - UnitOfVolume.CUBIC_FEET, - ), - ( - 500, - UnitOfVolume.FLUID_OUNCES, - pytest.approx(0.014786764), - UnitOfVolume.CUBIC_METERS, - ), + (500, UnitOfVolume.FLUID_OUNCES, 0.52218967, UnitOfVolume.CUBIC_FEET), + (500, UnitOfVolume.FLUID_OUNCES, 0.014786764, UnitOfVolume.CUBIC_METERS), (500, UnitOfVolume.FLUID_OUNCES, 3.90625, UnitOfVolume.GALLONS), - (500, UnitOfVolume.FLUID_OUNCES, pytest.approx(14.786764), UnitOfVolume.LITERS), - ( - 500, - UnitOfVolume.FLUID_OUNCES, - pytest.approx(14786.764), - UnitOfVolume.MILLILITERS, - ), - (500, UnitOfVolume.GALLONS, pytest.approx(66.84027), UnitOfVolume.CUBIC_FEET), - (500, UnitOfVolume.GALLONS, pytest.approx(1.892706), UnitOfVolume.CUBIC_METERS), + (500, UnitOfVolume.FLUID_OUNCES, 14.786764, UnitOfVolume.LITERS), + (500, UnitOfVolume.FLUID_OUNCES, 14786.764, UnitOfVolume.MILLILITERS), + (500, UnitOfVolume.GALLONS, 66.84027, UnitOfVolume.CUBIC_FEET), + (500, UnitOfVolume.GALLONS, 1.892706, UnitOfVolume.CUBIC_METERS), (500, UnitOfVolume.GALLONS, 64000, UnitOfVolume.FLUID_OUNCES), - (500, UnitOfVolume.GALLONS, pytest.approx(1892.70589), UnitOfVolume.LITERS), - ( - 500, - UnitOfVolume.GALLONS, - pytest.approx(1892705.89), - UnitOfVolume.MILLILITERS, - ), - (500, UnitOfVolume.LITERS, pytest.approx(17.65733), UnitOfVolume.CUBIC_FEET), + (500, UnitOfVolume.GALLONS, 1892.70589, UnitOfVolume.LITERS), + (500, UnitOfVolume.GALLONS, 1892705.89, UnitOfVolume.MILLILITERS), + (500, UnitOfVolume.LITERS, 17.65733, UnitOfVolume.CUBIC_FEET), (500, UnitOfVolume.LITERS, 0.5, UnitOfVolume.CUBIC_METERS), - (500, UnitOfVolume.LITERS, pytest.approx(16907.011), UnitOfVolume.FLUID_OUNCES), - (500, UnitOfVolume.LITERS, pytest.approx(132.086), UnitOfVolume.GALLONS), + (500, UnitOfVolume.LITERS, 16907.011, UnitOfVolume.FLUID_OUNCES), + (500, UnitOfVolume.LITERS, 132.086, UnitOfVolume.GALLONS), (500, UnitOfVolume.LITERS, 500000, UnitOfVolume.MILLILITERS), - ( - 500, - UnitOfVolume.MILLILITERS, - pytest.approx(0.01765733), - UnitOfVolume.CUBIC_FEET, - ), + (500, UnitOfVolume.MILLILITERS, 0.01765733, UnitOfVolume.CUBIC_FEET), (500, UnitOfVolume.MILLILITERS, 0.0005, UnitOfVolume.CUBIC_METERS), - ( - 500, - UnitOfVolume.MILLILITERS, - pytest.approx(16.907), - UnitOfVolume.FLUID_OUNCES, - ), - (500, UnitOfVolume.MILLILITERS, pytest.approx(0.132086), UnitOfVolume.GALLONS), + (500, UnitOfVolume.MILLILITERS, 16.907, UnitOfVolume.FLUID_OUNCES), + (500, UnitOfVolume.MILLILITERS, 0.132086, UnitOfVolume.GALLONS), (500, UnitOfVolume.MILLILITERS, 0.5, UnitOfVolume.LITERS), + (5, UnitOfVolume.CENTUM_CUBIC_FEET, 500, UnitOfVolume.CUBIC_FEET), + (5, UnitOfVolume.CENTUM_CUBIC_FEET, 14.15842, UnitOfVolume.CUBIC_METERS), + (5, UnitOfVolume.CENTUM_CUBIC_FEET, 478753.24, UnitOfVolume.FLUID_OUNCES), + (5, UnitOfVolume.CENTUM_CUBIC_FEET, 3740.26, UnitOfVolume.GALLONS), + (5, UnitOfVolume.CENTUM_CUBIC_FEET, 14158.42, UnitOfVolume.LITERS), ], ) def test_volume_convert( @@ -672,4 +744,4 @@ def test_volume_convert( to_unit: str, ) -> None: """Test conversion to other units.""" - assert VolumeConverter.convert(value, from_unit, to_unit) == expected + assert VolumeConverter.convert(value, from_unit, to_unit) == pytest.approx(expected) diff --git a/tests/util/test_unit_system.py b/tests/util/test_unit_system.py index 03385196cab..4813bc34a3a 100644 --- a/tests/util/test_unit_system.py +++ b/tests/util/test_unit_system.py @@ -380,6 +380,24 @@ def test_get_unit_system_invalid(key: str) -> None: @pytest.mark.parametrize( "device_class, original_unit, state_unit", ( + # Test atmospheric pressure + ( + SensorDeviceClass.ATMOSPHERIC_PRESSURE, + UnitOfPressure.PSI, + UnitOfPressure.HPA, + ), + ( + SensorDeviceClass.ATMOSPHERIC_PRESSURE, + UnitOfPressure.BAR, + UnitOfPressure.HPA, + ), + ( + SensorDeviceClass.ATMOSPHERIC_PRESSURE, + UnitOfPressure.INHG, + UnitOfPressure.HPA, + ), + (SensorDeviceClass.ATMOSPHERIC_PRESSURE, UnitOfPressure.HPA, None), + (SensorDeviceClass.ATMOSPHERIC_PRESSURE, "very_much", None), # Test distance conversion (SensorDeviceClass.DISTANCE, UnitOfLength.FEET, UnitOfLength.METERS), (SensorDeviceClass.DISTANCE, UnitOfLength.INCHES, UnitOfLength.MILLIMETERS), @@ -391,6 +409,10 @@ def test_get_unit_system_invalid(key: str) -> None: (SensorDeviceClass.GAS, UnitOfVolume.CUBIC_FEET, UnitOfVolume.CUBIC_METERS), (SensorDeviceClass.GAS, UnitOfVolume.CUBIC_METERS, None), (SensorDeviceClass.GAS, "very_much", None), + # Test pressure conversion + (SensorDeviceClass.PRESSURE, UnitOfPressure.PSI, UnitOfPressure.KPA), + (SensorDeviceClass.PRESSURE, UnitOfPressure.BAR, None), + (SensorDeviceClass.PRESSURE, "very_much", None), # Test speed conversion ( SensorDeviceClass.SPEED, @@ -435,6 +457,24 @@ def test_get_metric_converted_unit_( @pytest.mark.parametrize( "device_class, original_unit, state_unit", ( + # Test atmospheric pressure + ( + SensorDeviceClass.ATMOSPHERIC_PRESSURE, + UnitOfPressure.PSI, + UnitOfPressure.INHG, + ), + ( + SensorDeviceClass.ATMOSPHERIC_PRESSURE, + UnitOfPressure.BAR, + UnitOfPressure.INHG, + ), + ( + SensorDeviceClass.ATMOSPHERIC_PRESSURE, + UnitOfPressure.HPA, + UnitOfPressure.INHG, + ), + (SensorDeviceClass.ATMOSPHERIC_PRESSURE, UnitOfPressure.INHG, None), + (SensorDeviceClass.ATMOSPHERIC_PRESSURE, "very_much", None), # Test distance conversion (SensorDeviceClass.DISTANCE, UnitOfLength.CENTIMETERS, UnitOfLength.INCHES), (SensorDeviceClass.DISTANCE, UnitOfLength.KILOMETERS, UnitOfLength.MILES), @@ -446,6 +486,10 @@ def test_get_metric_converted_unit_( (SensorDeviceClass.GAS, UnitOfVolume.CUBIC_METERS, UnitOfVolume.CUBIC_FEET), (SensorDeviceClass.GAS, UnitOfVolume.CUBIC_FEET, None), (SensorDeviceClass.GAS, "very_much", None), + # Test pressure conversion + (SensorDeviceClass.PRESSURE, UnitOfPressure.BAR, UnitOfPressure.PSI), + (SensorDeviceClass.PRESSURE, UnitOfPressure.PSI, None), + (SensorDeviceClass.PRESSURE, "very_much", None), # Test speed conversion ( SensorDeviceClass.SPEED,