This commit is contained in:
Franck Nijhof 2022-11-02 21:47:47 +01:00 committed by GitHub
commit 1f0073f450
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2789 changed files with 71131 additions and 19716 deletions

View File

@ -46,7 +46,6 @@ base_platforms: &base_platforms
# Extra components that trigger the full suite # Extra components that trigger the full suite
components: &components components: &components
- homeassistant/components/alert/**
- homeassistant/components/alexa/** - homeassistant/components/alexa/**
- homeassistant/components/application_credentials/** - homeassistant/components/application_credentials/**
- homeassistant/components/auth/** - homeassistant/components/auth/**

View File

@ -37,6 +37,8 @@ omit =
homeassistant/components/airnow/sensor.py homeassistant/components/airnow/sensor.py
homeassistant/components/airthings/__init__.py homeassistant/components/airthings/__init__.py
homeassistant/components/airthings/sensor.py homeassistant/components/airthings/sensor.py
homeassistant/components/airthings_ble/__init__.py
homeassistant/components/airthings_ble/sensor.py
homeassistant/components/airtouch4/__init__.py homeassistant/components/airtouch4/__init__.py
homeassistant/components/airtouch4/climate.py homeassistant/components/airtouch4/climate.py
homeassistant/components/airtouch4/const.py homeassistant/components/airtouch4/const.py
@ -159,6 +161,8 @@ omit =
homeassistant/components/brunt/const.py homeassistant/components/brunt/const.py
homeassistant/components/brunt/cover.py homeassistant/components/brunt/cover.py
homeassistant/components/bsblan/climate.py homeassistant/components/bsblan/climate.py
homeassistant/components/bsblan/const.py
homeassistant/components/bsblan/entity.py
homeassistant/components/bt_home_hub_5/device_tracker.py homeassistant/components/bt_home_hub_5/device_tracker.py
homeassistant/components/bt_smarthub/device_tracker.py homeassistant/components/bt_smarthub/device_tracker.py
homeassistant/components/buienradar/sensor.py homeassistant/components/buienradar/sensor.py
@ -405,9 +409,11 @@ omit =
homeassistant/components/flick_electric/sensor.py homeassistant/components/flick_electric/sensor.py
homeassistant/components/flock/notify.py homeassistant/components/flock/notify.py
homeassistant/components/flume/__init__.py homeassistant/components/flume/__init__.py
homeassistant/components/flume/binary_sensor.py
homeassistant/components/flume/coordinator.py homeassistant/components/flume/coordinator.py
homeassistant/components/flume/entity.py homeassistant/components/flume/entity.py
homeassistant/components/flume/sensor.py homeassistant/components/flume/sensor.py
homeassistant/components/flume/util.py
homeassistant/components/folder/sensor.py homeassistant/components/folder/sensor.py
homeassistant/components/folder_watcher/* homeassistant/components/folder_watcher/*
homeassistant/components/foobot/sensor.py homeassistant/components/foobot/sensor.py
@ -534,6 +540,7 @@ omit =
homeassistant/components/hunterdouglas_powerview/entity.py homeassistant/components/hunterdouglas_powerview/entity.py
homeassistant/components/hunterdouglas_powerview/model.py homeassistant/components/hunterdouglas_powerview/model.py
homeassistant/components/hunterdouglas_powerview/scene.py homeassistant/components/hunterdouglas_powerview/scene.py
homeassistant/components/hunterdouglas_powerview/select.py
homeassistant/components/hunterdouglas_powerview/sensor.py homeassistant/components/hunterdouglas_powerview/sensor.py
homeassistant/components/hunterdouglas_powerview/shade_data.py homeassistant/components/hunterdouglas_powerview/shade_data.py
homeassistant/components/hunterdouglas_powerview/util.py homeassistant/components/hunterdouglas_powerview/util.py
@ -578,6 +585,7 @@ omit =
homeassistant/components/intellifire/coordinator.py homeassistant/components/intellifire/coordinator.py
homeassistant/components/intellifire/entity.py homeassistant/components/intellifire/entity.py
homeassistant/components/intellifire/fan.py homeassistant/components/intellifire/fan.py
homeassistant/components/intellifire/number.py
homeassistant/components/intellifire/sensor.py homeassistant/components/intellifire/sensor.py
homeassistant/components/intellifire/switch.py homeassistant/components/intellifire/switch.py
homeassistant/components/intesishome/* homeassistant/components/intesishome/*
@ -608,7 +616,6 @@ omit =
homeassistant/components/izone/__init__.py homeassistant/components/izone/__init__.py
homeassistant/components/izone/climate.py homeassistant/components/izone/climate.py
homeassistant/components/izone/discovery.py homeassistant/components/izone/discovery.py
homeassistant/components/jellyfin/__init__.py
homeassistant/components/jellyfin/media_source.py homeassistant/components/jellyfin/media_source.py
homeassistant/components/joaoapps_join/* homeassistant/components/joaoapps_join/*
homeassistant/components/juicenet/__init__.py homeassistant/components/juicenet/__init__.py
@ -657,8 +664,6 @@ omit =
homeassistant/components/kostal_plenticore/switch.py homeassistant/components/kostal_plenticore/switch.py
homeassistant/components/kwb/sensor.py homeassistant/components/kwb/sensor.py
homeassistant/components/lacrosse/sensor.py homeassistant/components/lacrosse/sensor.py
homeassistant/components/lametric/notify.py
homeassistant/components/lametric/number.py
homeassistant/components/lannouncer/notify.py homeassistant/components/lannouncer/notify.py
homeassistant/components/lastfm/sensor.py homeassistant/components/lastfm/sensor.py
homeassistant/components/launch_library/__init__.py homeassistant/components/launch_library/__init__.py
@ -855,6 +860,7 @@ omit =
homeassistant/components/noaa_tides/sensor.py homeassistant/components/noaa_tides/sensor.py
homeassistant/components/nobo_hub/__init__.py homeassistant/components/nobo_hub/__init__.py
homeassistant/components/nobo_hub/climate.py homeassistant/components/nobo_hub/climate.py
homeassistant/components/nobo_hub/sensor.py
homeassistant/components/norway_air/air_quality.py homeassistant/components/norway_air/air_quality.py
homeassistant/components/notify_events/notify.py homeassistant/components/notify_events/notify.py
homeassistant/components/notion/__init__.py homeassistant/components/notion/__init__.py
@ -1101,11 +1107,10 @@ omit =
homeassistant/components/sesame/lock.py homeassistant/components/sesame/lock.py
homeassistant/components/seven_segments/image_processing.py homeassistant/components/seven_segments/image_processing.py
homeassistant/components/seventeentrack/sensor.py homeassistant/components/seventeentrack/sensor.py
homeassistant/components/shelly/__init__.py
homeassistant/components/shelly/binary_sensor.py homeassistant/components/shelly/binary_sensor.py
homeassistant/components/shelly/climate.py homeassistant/components/shelly/climate.py
homeassistant/components/shelly/coordinator.py
homeassistant/components/shelly/entity.py homeassistant/components/shelly/entity.py
homeassistant/components/shelly/light.py
homeassistant/components/shelly/number.py homeassistant/components/shelly/number.py
homeassistant/components/shelly/sensor.py homeassistant/components/shelly/sensor.py
homeassistant/components/shelly/utils.py homeassistant/components/shelly/utils.py
@ -1161,6 +1166,7 @@ omit =
homeassistant/components/smtp/notify.py homeassistant/components/smtp/notify.py
homeassistant/components/snapcast/* homeassistant/components/snapcast/*
homeassistant/components/snmp/* homeassistant/components/snmp/*
homeassistant/components/snooz/__init__.py
homeassistant/components/solaredge/__init__.py homeassistant/components/solaredge/__init__.py
homeassistant/components/solaredge/coordinator.py homeassistant/components/solaredge/coordinator.py
homeassistant/components/solaredge/sensor.py homeassistant/components/solaredge/sensor.py
@ -1230,7 +1236,9 @@ omit =
homeassistant/components/swisscom/device_tracker.py homeassistant/components/swisscom/device_tracker.py
homeassistant/components/switchbee/__init__.py homeassistant/components/switchbee/__init__.py
homeassistant/components/switchbee/button.py homeassistant/components/switchbee/button.py
homeassistant/components/switchbee/climate.py
homeassistant/components/switchbee/coordinator.py homeassistant/components/switchbee/coordinator.py
homeassistant/components/switchbee/cover.py
homeassistant/components/switchbee/entity.py homeassistant/components/switchbee/entity.py
homeassistant/components/switchbee/light.py homeassistant/components/switchbee/light.py
homeassistant/components/switchbee/switch.py homeassistant/components/switchbee/switch.py
@ -1421,6 +1429,7 @@ omit =
homeassistant/components/velbus/const.py homeassistant/components/velbus/const.py
homeassistant/components/velbus/cover.py homeassistant/components/velbus/cover.py
homeassistant/components/velbus/diagnostics.py homeassistant/components/velbus/diagnostics.py
homeassistant/components/velbus/entity.py
homeassistant/components/velbus/light.py homeassistant/components/velbus/light.py
homeassistant/components/velbus/sensor.py homeassistant/components/velbus/sensor.py
homeassistant/components/velbus/switch.py homeassistant/components/velbus/switch.py
@ -1573,6 +1582,9 @@ omit =
homeassistant/components/youless/const.py homeassistant/components/youless/const.py
homeassistant/components/youless/sensor.py homeassistant/components/youless/sensor.py
homeassistant/components/zabbix/* 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/sensor.py
homeassistant/components/zamg/weather.py homeassistant/components/zamg/weather.py
homeassistant/components/zengge/light.py homeassistant/components/zengge/light.py

View File

@ -5,7 +5,7 @@
"postCreateCommand": "script/setup", "postCreateCommand": "script/setup",
"postStartCommand": "script/bootstrap", "postStartCommand": "script/bootstrap",
"containerEnv": { "DEVCONTAINER": "1" }, "containerEnv": { "DEVCONTAINER": "1" },
"appPort": 8123, "appPort": ["8123:8123"],
"runArgs": ["-e", "GIT_EDITOR=code --wait"], "runArgs": ["-e", "GIT_EDITOR=code --wait"],
"extensions": [ "extensions": [
"ms-python.vscode-pylance", "ms-python.vscode-pylance",
@ -17,8 +17,14 @@
// Please keep this file in sync with settings in home-assistant/.vscode/settings.default.json // Please keep this file in sync with settings in home-assistant/.vscode/settings.default.json
"settings": { "settings": {
"python.pythonPath": "/usr/local/bin/python", "python.pythonPath": "/usr/local/bin/python",
"python.linting.pylintEnabled": true,
"python.linting.enabled": true, "python.linting.enabled": true,
"python.linting.pylintEnabled": true,
"python.formatting.blackPath": "/usr/local/bin/black",
"python.linting.flake8Path": "/usr/local/bin/flake8",
"python.linting.pycodestylePath": "/usr/local/bin/pycodestyle",
"python.linting.pydocstylePath": "/usr/local/bin/pydocstyle",
"python.linting.mypyPath": "/usr/local/bin/mypy",
"python.linting.pylintPath": "/usr/local/bin/pylint",
"python.formatting.provider": "black", "python.formatting.provider": "black",
"python.testing.pytestArgs": ["--no-cov"], "python.testing.pytestArgs": ["--no-cov"],
"editor.formatOnPaste": false, "editor.formatOnPaste": false,

View File

@ -46,9 +46,9 @@ body:
attributes: attributes:
label: What type of installation are you running? label: What type of installation are you running?
description: > description: >
Can be found in: [Settings -> About](https://my.home-assistant.io/redirect/info/). Can be found in: [Settings -> System-> Repairs -> Three Dots in Upper Right -> System information](https://my.home-assistant.io/redirect/system_health/).
[![Open your Home Assistant instance and show your Home Assistant version information.](https://my.home-assistant.io/badges/info.svg)](https://my.home-assistant.io/redirect/info/) [![Open your Home Assistant instance and show health information about your system.](https://my.home-assistant.io/badges/system_health.svg)](https://my.home-assistant.io/redirect/system_health/)
options: options:
- Home Assistant OS - Home Assistant OS
- Home Assistant Container - Home Assistant Container

View File

@ -75,18 +75,6 @@ If the code communicates with devices, web services, or third-party tools:
- [ ] For the updated dependencies - a link to the changelog, or at minimum a diff between library versions is added to the PR description. - [ ] For the updated dependencies - a link to the changelog, or at minimum a diff between library versions is added to the PR description.
- [ ] Untested files have been added to `.coveragerc`. - [ ] Untested files have been added to `.coveragerc`.
The integration reached or maintains the following [Integration Quality Scale][quality-scale]:
<!--
The Integration Quality Scale scores an integration on the code quality
and user experience. Each level of the quality scale consists of a list
of requirements. We highly recommend getting your integration scored!
-->
- [ ] No score or internal
- [ ] 🥈 Silver
- [ ] 🥇 Gold
- [ ] 🏆 Platinum
<!-- <!--
This project is very active and we have a high turnover of pull requests. This project is very active and we have a high turnover of pull requests.

View File

@ -24,12 +24,12 @@ jobs:
publish: ${{ steps.version.outputs.publish }} publish: ${{ steps.version.outputs.publish }}
steps: steps:
- name: Checkout the repository - name: Checkout the repository
uses: actions/checkout@v3.0.2 uses: actions/checkout@v3.1.0
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Set up Python ${{ env.DEFAULT_PYTHON }} - name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v4.1.0 uses: actions/setup-python@v4.3.0
with: with:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
@ -67,10 +67,10 @@ jobs:
if: github.repository_owner == 'home-assistant' && needs.init.outputs.publish == 'true' if: github.repository_owner == 'home-assistant' && needs.init.outputs.publish == 'true'
steps: steps:
- name: Checkout the repository - name: Checkout the repository
uses: actions/checkout@v3.0.2 uses: actions/checkout@v3.1.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }} - name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v4.1.0 uses: actions/setup-python@v4.3.0
with: with:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
@ -100,7 +100,7 @@ jobs:
arch: ${{ fromJson(needs.init.outputs.architectures) }} arch: ${{ fromJson(needs.init.outputs.architectures) }}
steps: steps:
- name: Checkout the repository - name: Checkout the repository
uses: actions/checkout@v3.0.2 uses: actions/checkout@v3.1.0
- name: Download nightly wheels of frontend - name: Download nightly wheels of frontend
if: needs.init.outputs.channel == 'dev' if: needs.init.outputs.channel == 'dev'
@ -115,7 +115,7 @@ jobs:
- name: Set up Python ${{ env.DEFAULT_PYTHON }} - name: Set up Python ${{ env.DEFAULT_PYTHON }}
if: needs.init.outputs.channel == 'dev' if: needs.init.outputs.channel == 'dev'
uses: actions/setup-python@v4.1.0 uses: actions/setup-python@v4.3.0
with: with:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
@ -146,13 +146,13 @@ jobs:
echo "${{ github.sha }};${{ github.ref }};${{ github.event_name }};${{ github.actor }}" > rootfs/OFFICIAL_IMAGE echo "${{ github.sha }};${{ github.ref }};${{ github.event_name }};${{ github.actor }}" > rootfs/OFFICIAL_IMAGE
- name: Login to DockerHub - name: Login to DockerHub
uses: docker/login-action@v2.0.0 uses: docker/login-action@v2.1.0
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
uses: docker/login-action@v2.0.0 uses: docker/login-action@v2.1.0
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.repository_owner }} username: ${{ github.repository_owner }}
@ -198,7 +198,7 @@ jobs:
- yellow - yellow
steps: steps:
- name: Checkout the repository - name: Checkout the repository
uses: actions/checkout@v3.0.2 uses: actions/checkout@v3.1.0
- name: Set build additional args - name: Set build additional args
run: | run: |
@ -212,13 +212,13 @@ jobs:
fi fi
- name: Login to DockerHub - name: Login to DockerHub
uses: docker/login-action@v2.0.0 uses: docker/login-action@v2.1.0
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
uses: docker/login-action@v2.0.0 uses: docker/login-action@v2.1.0
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.repository_owner }} username: ${{ github.repository_owner }}
@ -241,7 +241,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout the repository - name: Checkout the repository
uses: actions/checkout@v3.0.2 uses: actions/checkout@v3.1.0
- name: Initialize git - name: Initialize git
uses: home-assistant/actions/helpers/git-init@master uses: home-assistant/actions/helpers/git-init@master
@ -280,18 +280,18 @@ jobs:
- "homeassistant" - "homeassistant"
steps: steps:
- name: Checkout the repository - name: Checkout the repository
uses: actions/checkout@v3.0.2 uses: actions/checkout@v3.1.0
- name: Login to DockerHub - name: Login to DockerHub
if: matrix.registry == 'homeassistant' if: matrix.registry == 'homeassistant'
uses: docker/login-action@v2.0.0 uses: docker/login-action@v2.1.0
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
if: matrix.registry == 'ghcr.io/home-assistant' if: matrix.registry == 'ghcr.io/home-assistant'
uses: docker/login-action@v2.0.0 uses: docker/login-action@v2.1.0
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.repository_owner }} username: ${{ github.repository_owner }}

View File

@ -20,13 +20,11 @@ on:
type: boolean type: boolean
env: env:
CACHE_VERSION: 1 CACHE_VERSION: 3
PIP_CACHE_VERSION: 1 PIP_CACHE_VERSION: 3
HA_SHORT_VERSION: 2022.10 HA_SHORT_VERSION: 2022.11
# Pin latest Python patch versions to avoid issues DEFAULT_PYTHON: 3.9
# with runners using different versions. ALL_PYTHON_VERSIONS: "['3.9', '3.10']"
DEFAULT_PYTHON: 3.9.14
ALL_PYTHON_VERSIONS: "['3.9.14', '3.10.7']"
PRE_COMMIT_CACHE: ~/.cache/pre-commit PRE_COMMIT_CACHE: ~/.cache/pre-commit
PIP_CACHE: /tmp/pip-cache PIP_CACHE: /tmp/pip-cache
SQLALCHEMY_WARN_20: 1 SQLALCHEMY_WARN_20: 1
@ -58,21 +56,21 @@ jobs:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v3.0.2 uses: actions/checkout@v3.1.0
- name: Generate partial Python venv restore key - name: Generate partial Python venv restore key
id: generate_python_cache_key id: generate_python_cache_key
run: >- run: >-
echo "::set-output name=key::venv-${{ env.CACHE_VERSION }}-${{ echo "key=venv-${{ env.CACHE_VERSION }}-${{
hashFiles('requirements_test.txt') }}-${{ hashFiles('requirements_test.txt') }}-${{
hashFiles('requirements_all.txt') }}-${{ hashFiles('requirements_all.txt') }}-${{
hashFiles('homeassistant/package_constraints.txt') }}" hashFiles('homeassistant/package_constraints.txt') }}" >> $GITHUB_OUTPUT
- name: Generate partial pre-commit restore key - name: Generate partial pre-commit restore key
id: generate_pre-commit_cache_key id: generate_pre-commit_cache_key
run: >- run: >-
echo "::set-output name=key::${{ env.CACHE_VERSION }}-${{ env.DEFAULT_PYTHON }}-${{ echo "key=pre-commit-${{ env.CACHE_VERSION }}-${{
hashFiles('.pre-commit-config.yaml') }}" hashFiles('.pre-commit-config.yaml') }}" >> $GITHUB_OUTPUT
- name: Filter for core changes - name: Filter for core changes
uses: dorny/paths-filter@v2.10.2 uses: dorny/paths-filter@v2.11.1
id: core id: core
with: with:
filters: .core_files.yaml filters: .core_files.yaml
@ -87,7 +85,7 @@ jobs:
echo "Result:" echo "Result:"
cat .integration_paths.yaml cat .integration_paths.yaml
- name: Filter for integration changes - name: Filter for integration changes
uses: dorny/paths-filter@v2.10.2 uses: dorny/paths-filter@v2.11.1
id: integrations id: integrations
with: with:
filters: .integration_paths.yaml filters: .integration_paths.yaml
@ -148,19 +146,19 @@ jobs:
# Output & sent to GitHub Actions # Output & sent to GitHub Actions
echo "python_versions: ${ALL_PYTHON_VERSIONS}" echo "python_versions: ${ALL_PYTHON_VERSIONS}"
echo "::set-output name=python_versions::${ALL_PYTHON_VERSIONS}" echo "python_versions=${ALL_PYTHON_VERSIONS}" >> $GITHUB_OUTPUT
echo "test_full_suite: ${test_full_suite}" echo "test_full_suite: ${test_full_suite}"
echo "::set-output name=test_full_suite::${test_full_suite}" echo "test_full_suite=${test_full_suite}" >> $GITHUB_OUTPUT
echo "integrations_glob: ${integrations_glob}" echo "integrations_glob: ${integrations_glob}"
echo "::set-output name=integrations_glob::${integrations_glob}" echo "integrations_glob=${integrations_glob}" >> $GITHUB_OUTPUT
echo "test_group_count: ${test_group_count}" echo "test_group_count: ${test_group_count}"
echo "::set-output name=test_group_count::${test_group_count}" echo "test_group_count=${test_group_count}" >> $GITHUB_OUTPUT
echo "test_groups: ${test_groups}" echo "test_groups: ${test_groups}"
echo "::set-output name=test_groups::${test_groups}" echo "test_groups=${test_groups}" >> $GITHUB_OUTPUT
echo "tests: ${tests}" echo "tests: ${tests}"
echo "::set-output name=tests::${tests}" echo "tests=${tests}" >> $GITHUB_OUTPUT
echo "tests_glob: ${tests_glob}" echo "tests_glob: ${tests_glob}"
echo "::set-output name=tests_glob::${tests_glob}" echo "tests_glob=${tests_glob}" >> $GITHUB_OUTPUT
pre-commit: pre-commit:
name: Prepare pre-commit base name: Prepare pre-commit base
@ -169,18 +167,21 @@ jobs:
- info - info
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v3.0.2 uses: actions/checkout@v3.1.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }} - name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python id: python
uses: actions/setup-python@v4.1.0 uses: actions/setup-python@v4.3.0
with: with:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
check-latest: true
- name: Restore base Python virtual environment - name: Restore base Python virtual environment
id: cache-venv id: cache-venv
uses: actions/cache@v3.0.8 uses: actions/cache@v3.0.11
with: with:
path: venv path: venv
key: ${{ runner.os }}-venv-${{ needs.info.outputs.pre-commit_cache_key }} key: >-
${{ runner.os }}-${{ steps.python.outputs.python-version }}-venv-${{
needs.info.outputs.pre-commit_cache_key }}
- name: Create Python virtual environment - name: Create Python virtual environment
if: steps.cache-venv.outputs.cache-hit != 'true' if: steps.cache-venv.outputs.cache-hit != 'true'
run: | run: |
@ -190,10 +191,12 @@ jobs:
pip install "$(cat requirements_test.txt | grep pre-commit)" pip install "$(cat requirements_test.txt | grep pre-commit)"
- name: Restore pre-commit environment from cache - name: Restore pre-commit environment from cache
id: cache-precommit id: cache-precommit
uses: actions/cache@v3.0.8 uses: actions/cache@v3.0.11
with: with:
path: ${{ env.PRE_COMMIT_CACHE }} path: ${{ env.PRE_COMMIT_CACHE }}
key: ${{ runner.os }}-pre-commit-${{ needs.info.outputs.pre-commit_cache_key }} key: >-
${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
needs.info.outputs.pre-commit_cache_key }}
- name: Install pre-commit dependencies - name: Install pre-commit dependencies
if: steps.cache-precommit.outputs.cache-hit != 'true' if: steps.cache-precommit.outputs.cache-hit != 'true'
run: | run: |
@ -208,18 +211,21 @@ jobs:
- pre-commit - pre-commit
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v3.0.2 uses: actions/checkout@v3.1.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }} - name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v4.1.0 uses: actions/setup-python@v4.3.0
id: python id: python
with: with:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
check-latest: true
- name: Restore base Python virtual environment - name: Restore base Python virtual environment
id: cache-venv id: cache-venv
uses: actions/cache@v3.0.8 uses: actions/cache@v3.0.11
with: with:
path: venv path: venv
key: ${{ runner.os }}-venv-${{ needs.info.outputs.pre-commit_cache_key }} key: >-
${{ runner.os }}-${{ steps.python.outputs.python-version }}-venv-${{
needs.info.outputs.pre-commit_cache_key }}
- name: Fail job if Python cache restore failed - name: Fail job if Python cache restore failed
if: steps.cache-venv.outputs.cache-hit != 'true' if: steps.cache-venv.outputs.cache-hit != 'true'
run: | run: |
@ -227,10 +233,12 @@ jobs:
exit 1 exit 1
- name: Restore pre-commit environment from cache - name: Restore pre-commit environment from cache
id: cache-precommit id: cache-precommit
uses: actions/cache@v3.0.8 uses: actions/cache@v3.0.11
with: with:
path: ${{ env.PRE_COMMIT_CACHE }} path: ${{ env.PRE_COMMIT_CACHE }}
key: ${{ runner.os }}-pre-commit-${{ needs.info.outputs.pre-commit_cache_key }} key: >-
${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
needs.info.outputs.pre-commit_cache_key }}
- name: Fail job if pre-commit cache restore failed - name: Fail job if pre-commit cache restore failed
if: steps.cache-precommit.outputs.cache-hit != 'true' if: steps.cache-precommit.outputs.cache-hit != 'true'
run: | run: |
@ -257,18 +265,21 @@ jobs:
- pre-commit - pre-commit
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v3.0.2 uses: actions/checkout@v3.1.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }} - name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v4.1.0 uses: actions/setup-python@v4.3.0
id: python id: python
with: with:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
check-latest: true
- name: Restore base Python virtual environment - name: Restore base Python virtual environment
id: cache-venv id: cache-venv
uses: actions/cache@v3.0.8 uses: actions/cache@v3.0.11
with: with:
path: venv path: venv
key: ${{ runner.os }}-venv-${{ needs.info.outputs.pre-commit_cache_key }} key: >-
${{ runner.os }}-${{ steps.python.outputs.python-version }}-venv-${{
needs.info.outputs.pre-commit_cache_key }}
- name: Fail job if Python cache restore failed - name: Fail job if Python cache restore failed
if: steps.cache-venv.outputs.cache-hit != 'true' if: steps.cache-venv.outputs.cache-hit != 'true'
run: | run: |
@ -276,10 +287,12 @@ jobs:
exit 1 exit 1
- name: Restore pre-commit environment from cache - name: Restore pre-commit environment from cache
id: cache-precommit id: cache-precommit
uses: actions/cache@v3.0.8 uses: actions/cache@v3.0.11
with: with:
path: ${{ env.PRE_COMMIT_CACHE }} path: ${{ env.PRE_COMMIT_CACHE }}
key: ${{ runner.os }}-pre-commit-${{ needs.info.outputs.pre-commit_cache_key }} key: >-
${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
needs.info.outputs.pre-commit_cache_key }}
- name: Fail job if pre-commit cache restore failed - name: Fail job if pre-commit cache restore failed
if: steps.cache-precommit.outputs.cache-hit != 'true' if: steps.cache-precommit.outputs.cache-hit != 'true'
run: | run: |
@ -309,18 +322,21 @@ jobs:
- pre-commit - pre-commit
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v3.0.2 uses: actions/checkout@v3.1.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }} - name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v4.1.0 uses: actions/setup-python@v4.3.0
id: python id: python
with: with:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
check-latest: true
- name: Restore base Python virtual environment - name: Restore base Python virtual environment
id: cache-venv id: cache-venv
uses: actions/cache@v3.0.8 uses: actions/cache@v3.0.11
with: with:
path: venv path: venv
key: ${{ runner.os }}-venv-${{ needs.info.outputs.pre-commit_cache_key }} key: >-
${{ runner.os }}-${{ steps.python.outputs.python-version }}-venv-${{
needs.info.outputs.pre-commit_cache_key }}
- name: Fail job if Python cache restore failed - name: Fail job if Python cache restore failed
if: steps.cache-venv.outputs.cache-hit != 'true' if: steps.cache-venv.outputs.cache-hit != 'true'
run: | run: |
@ -328,10 +344,12 @@ jobs:
exit 1 exit 1
- name: Restore pre-commit environment from cache - name: Restore pre-commit environment from cache
id: cache-precommit id: cache-precommit
uses: actions/cache@v3.0.8 uses: actions/cache@v3.0.11
with: with:
path: ${{ env.PRE_COMMIT_CACHE }} path: ${{ env.PRE_COMMIT_CACHE }}
key: ${{ runner.os }}-pre-commit-${{ needs.info.outputs.pre-commit_cache_key }} key: >-
${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
needs.info.outputs.pre-commit_cache_key }}
- name: Fail job if pre-commit cache restore failed - name: Fail job if pre-commit cache restore failed
if: steps.cache-precommit.outputs.cache-hit != 'true' if: steps.cache-precommit.outputs.cache-hit != 'true'
run: | run: |
@ -350,18 +368,21 @@ jobs:
- pre-commit - pre-commit
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v3.0.2 uses: actions/checkout@v3.1.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }} - name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v4.1.0 uses: actions/setup-python@v4.3.0
id: python id: python
with: with:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
check-latest: true
- name: Restore base Python virtual environment - name: Restore base Python virtual environment
id: cache-venv id: cache-venv
uses: actions/cache@v3.0.8 uses: actions/cache@v3.0.11
with: with:
path: venv path: venv
key: ${{ runner.os }}-venv-${{ needs.info.outputs.pre-commit_cache_key }} key: >-
${{ runner.os }}-${{ steps.python.outputs.python-version }}-venv-${{
needs.info.outputs.pre-commit_cache_key }}
- name: Fail job if Python cache restore failed - name: Fail job if Python cache restore failed
if: steps.cache-venv.outputs.cache-hit != 'true' if: steps.cache-venv.outputs.cache-hit != 'true'
run: | run: |
@ -369,10 +390,12 @@ jobs:
exit 1 exit 1
- name: Restore pre-commit environment from cache - name: Restore pre-commit environment from cache
id: cache-precommit id: cache-precommit
uses: actions/cache@v3.0.8 uses: actions/cache@v3.0.11
with: with:
path: ${{ env.PRE_COMMIT_CACHE }} path: ${{ env.PRE_COMMIT_CACHE }}
key: ${{ runner.os }}-pre-commit-${{ needs.info.outputs.pre-commit_cache_key }} key: >-
${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
needs.info.outputs.pre-commit_cache_key }}
- name: Fail job if pre-commit cache restore failed - name: Fail job if pre-commit cache restore failed
if: steps.cache-precommit.outputs.cache-hit != 'true' if: steps.cache-precommit.outputs.cache-hit != 'true'
run: | run: |
@ -472,20 +495,21 @@ jobs:
python-version: ${{ fromJSON(needs.info.outputs.python_versions) }} python-version: ${{ fromJSON(needs.info.outputs.python_versions) }}
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v3.0.2 uses: actions/checkout@v3.1.0
- name: Set up Python ${{ matrix.python-version }} - name: Set up Python ${{ matrix.python-version }}
id: python id: python
uses: actions/setup-python@v4.1.0 uses: actions/setup-python@v4.3.0
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
check-latest: true
- name: Generate partial pip restore key - name: Generate partial pip restore key
id: generate-pip-key id: generate-pip-key
run: >- run: >-
echo "::set-output name=key::pip-${{ env.PIP_CACHE_VERSION }}-${{ echo "key=pip-${{ env.PIP_CACHE_VERSION }}-${{
env.HA_SHORT_VERSION }}-$(date -u '+%Y-%m-%dT%H:%M:%s')" env.HA_SHORT_VERSION }}-$(date -u '+%Y-%m-%dT%H:%M:%s')" >> $GITHUB_OUTPUT
- name: Restore base Python virtual environment - name: Restore base Python virtual environment
id: cache-venv id: cache-venv
uses: actions/cache@v3.0.8 uses: actions/cache@v3.0.11
with: with:
path: venv path: venv
key: >- key: >-
@ -493,7 +517,7 @@ jobs:
needs.info.outputs.python_cache_key }} needs.info.outputs.python_cache_key }}
- name: Restore pip wheel cache - name: Restore pip wheel cache
if: steps.cache-venv.outputs.cache-hit != 'true' if: steps.cache-venv.outputs.cache-hit != 'true'
uses: actions/cache@v3.0.8 uses: actions/cache@v3.0.11
with: with:
path: ${{ env.PIP_CACHE }} path: ${{ env.PIP_CACHE }}
key: >- key: >-
@ -522,7 +546,7 @@ jobs:
python -m venv venv python -m venv venv
. venv/bin/activate . venv/bin/activate
python --version python --version
pip install --cache-dir=$PIP_CACHE -U "pip>=21.0,<22.3" setuptools wheel pip install --cache-dir=$PIP_CACHE -U "pip>=21.0,<22.4" setuptools wheel
pip install --cache-dir=$PIP_CACHE -r requirements_all.txt --use-deprecated=legacy-resolver pip install --cache-dir=$PIP_CACHE -r requirements_all.txt --use-deprecated=legacy-resolver
pip install --cache-dir=$PIP_CACHE -r requirements_test.txt --use-deprecated=legacy-resolver pip install --cache-dir=$PIP_CACHE -r requirements_test.txt --use-deprecated=legacy-resolver
pip install -e . pip install -e .
@ -535,15 +559,16 @@ jobs:
- base - base
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v3.0.2 uses: actions/checkout@v3.1.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }} - name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python id: python
uses: actions/setup-python@v4.1.0 uses: actions/setup-python@v4.3.0
with: with:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
check-latest: true
- name: Restore full Python ${{ env.DEFAULT_PYTHON }} virtual environment - name: Restore full Python ${{ env.DEFAULT_PYTHON }} virtual environment
id: cache-venv id: cache-venv
uses: actions/cache@v3.0.8 uses: actions/cache@v3.0.11
with: with:
path: venv path: venv
key: >- key: >-
@ -567,15 +592,16 @@ jobs:
- base - base
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v3.0.2 uses: actions/checkout@v3.1.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }} - name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python id: python
uses: actions/setup-python@v4.1.0 uses: actions/setup-python@v4.3.0
with: with:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
check-latest: true
- name: Restore base Python virtual environment - name: Restore base Python virtual environment
id: cache-venv id: cache-venv
uses: actions/cache@v3.0.8 uses: actions/cache@v3.0.11
with: with:
path: venv path: venv
key: >- key: >-
@ -600,15 +626,16 @@ jobs:
- base - base
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v3.0.2 uses: actions/checkout@v3.1.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }} - name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python id: python
uses: actions/setup-python@v4.1.0 uses: actions/setup-python@v4.3.0
with: with:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
check-latest: true
- name: Restore full Python ${{ env.DEFAULT_PYTHON }} virtual environment - name: Restore full Python ${{ env.DEFAULT_PYTHON }} virtual environment
id: cache-venv id: cache-venv
uses: actions/cache@v3.0.8 uses: actions/cache@v3.0.11
with: with:
path: venv path: venv
key: >- key: >-
@ -644,15 +671,16 @@ jobs:
- base - base
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v3.0.2 uses: actions/checkout@v3.1.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }} - name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python id: python
uses: actions/setup-python@v4.1.0 uses: actions/setup-python@v4.3.0
with: with:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
check-latest: true
- name: Restore full Python ${{ env.DEFAULT_PYTHON }} virtual environment - name: Restore full Python ${{ env.DEFAULT_PYTHON }} virtual environment
id: cache-venv id: cache-venv
uses: actions/cache@v3.0.8 uses: actions/cache@v3.0.11
with: with:
path: venv path: venv
key: >- key: >-
@ -692,15 +720,16 @@ jobs:
name: Run pip check ${{ matrix.python-version }} name: Run pip check ${{ matrix.python-version }}
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v3.0.2 uses: actions/checkout@v3.1.0
- name: Set up Python ${{ matrix.python-version }} - name: Set up Python ${{ matrix.python-version }}
id: python id: python
uses: actions/setup-python@v4.1.0 uses: actions/setup-python@v4.3.0
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
check-latest: true
- name: Restore full Python ${{ matrix.python-version }} virtual environment - name: Restore full Python ${{ matrix.python-version }} virtual environment
id: cache-venv id: cache-venv
uses: actions/cache@v3.0.8 uses: actions/cache@v3.0.11
with: with:
path: venv path: venv
key: >- key: >-
@ -746,15 +775,16 @@ jobs:
bluez \ bluez \
ffmpeg ffmpeg
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v3.0.2 uses: actions/checkout@v3.1.0
- name: Set up Python ${{ matrix.python-version }} - name: Set up Python ${{ matrix.python-version }}
id: python id: python
uses: actions/setup-python@v4.1.0 uses: actions/setup-python@v4.3.0
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
check-latest: true
- name: Restore full Python ${{ matrix.python-version }} virtual environment - name: Restore full Python ${{ matrix.python-version }} virtual environment
id: cache-venv id: cache-venv
uses: actions/cache@v3.0.8 uses: actions/cache@v3.0.11
with: with:
path: venv path: venv
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{ key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
@ -823,7 +853,7 @@ jobs:
-p no:sugar \ -p no:sugar \
tests/components/${{ matrix.group }} tests/components/${{ matrix.group }}
- name: Upload coverage artifact - name: Upload coverage artifact
uses: actions/upload-artifact@v3.1.0 uses: actions/upload-artifact@v3.1.1
with: with:
name: coverage-${{ matrix.python-version }}-${{ matrix.group }} name: coverage-${{ matrix.python-version }}-${{ matrix.group }}
path: coverage.xml path: coverage.xml
@ -831,6 +861,109 @@ jobs:
run: | run: |
./script/check_dirty ./script/check_dirty
pytest-mariadb:
runs-on: ubuntu-20.04
services:
mariadb:
image: mariadb:10.9.3
ports:
- 3306:3306
env:
MYSQL_ROOT_PASSWORD: password
options: --health-cmd="mysqladmin ping -uroot -ppassword" --health-interval=5s --health-timeout=2s --health-retries=3
if: |
(github.event_name != 'push' || github.event.repository.full_name == 'home-assistant/core')
&& github.event.inputs.lint-only != 'true'
&& needs.info.outputs.test_full_suite == 'true'
needs:
- info
- base
- gen-requirements-all
- hassfest
- lint-black
- lint-other
- lint-isort
- mypy
strategy:
fail-fast: false
matrix:
python-version: ${{ fromJson(needs.info.outputs.python_versions) }}
name: >-
Run tests Python ${{ matrix.python-version }} (mariadb)
steps:
- name: Install additional OS dependencies
run: |
sudo apt-get update
sudo apt-get -y install \
bluez \
ffmpeg \
libmariadb-dev-compat
- name: Check out code from GitHub
uses: actions/checkout@v3.1.0
- name: Set up Python ${{ matrix.python-version }}
id: python
uses: actions/setup-python@v4.3.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
with:
path: venv
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
needs.info.outputs.python_cache_key }}
- name: Fail job if Python cache restore failed
if: steps.cache-venv.outputs.cache-hit != 'true'
run: |
echo "Failed to restore Python virtual environment from cache"
exit 1
- name: Register Python problem matcher
run: |
echo "::add-matcher::.github/workflows/matchers/python.json"
- name: Install Pytest Annotation plugin
run: |
. venv/bin/activate
# Ideally this should be part of our dependencies
# However this plugin is fairly new and doesn't run correctly
# on a non-GitHub environment.
pip install pytest-github-actions-annotate-failures==0.1.3
- name: Register pytest slow test problem matcher
run: |
echo "::add-matcher::.github/workflows/matchers/pytest-slow.json"
- name: Install SQL Python libraries
run: |
. venv/bin/activate
pip install mysqlclient sqlalchemy_utils
- name: Run pytest (partially)
timeout-minutes: 10
shell: bash
run: |
. venv/bin/activate
python --version
python3 -X dev -m pytest \
-qq \
--timeout=9 \
-n 1 \
--cov="homeassistant.components.recorder" \
--cov-report=xml \
--cov-report=term-missing \
-o console_output_style=count \
--durations=0 \
--durations-min=10 \
-p no:sugar \
--dburl=mysql://root:password@127.0.0.1/homeassistant-test \
tests/components/recorder
- name: Upload coverage artifact
uses: actions/upload-artifact@v3.1.1
with:
name: coverage-${{ matrix.python-version }}-mariadb
path: coverage.xml
- name: Check dirty
run: |
./script/check_dirty
coverage: coverage:
name: Upload test coverage to Codecov name: Upload test coverage to Codecov
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
@ -839,7 +972,7 @@ jobs:
- pytest - pytest
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v3.0.2 uses: actions/checkout@v3.1.0
- name: Download all coverage artifacts - name: Download all coverage artifacts
uses: actions/download-artifact@v3 uses: actions/download-artifact@v3
- name: Upload coverage to Codecov (full coverage) - name: Upload coverage to Codecov (full coverage)

View File

@ -17,7 +17,7 @@ jobs:
# - No PRs marked as no-stale # - No PRs marked as no-stale
# - No issues marked as no-stale or help-wanted # - No issues marked as no-stale or help-wanted
- name: 90 days stale issues & PRs policy - name: 90 days stale issues & PRs policy
uses: actions/stale@v6.0.0 uses: actions/stale@v6.0.1
with: with:
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 90 days-before-stale: 90
@ -54,7 +54,7 @@ jobs:
# - No PRs marked as no-stale or new-integrations # - No PRs marked as no-stale or new-integrations
# - No issues (-1) # - No issues (-1)
- name: 30 days stale PRs policy - name: 30 days stale PRs policy
uses: actions/stale@v6.0.0 uses: actions/stale@v6.0.1
with: with:
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 30 days-before-stale: 30
@ -79,7 +79,7 @@ jobs:
# - No Issues marked as no-stale or help-wanted # - No Issues marked as no-stale or help-wanted
# - No PRs (-1) # - No PRs (-1)
- name: Needs more information stale issues policy - name: Needs more information stale issues policy
uses: actions/stale@v6.0.0 uses: actions/stale@v6.0.1
with: with:
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
only-labels: "needs-more-information" only-labels: "needs-more-information"

View File

@ -21,10 +21,10 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout the repository - name: Checkout the repository
uses: actions/checkout@v3.0.2 uses: actions/checkout@v3.1.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }} - name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v4.1.0 uses: actions/setup-python@v4.3.0
with: with:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
@ -40,10 +40,10 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout the repository - name: Checkout the repository
uses: actions/checkout@v3.0.2 uses: actions/checkout@v3.1.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }} - name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v4.1.0 uses: actions/setup-python@v4.3.0
with: with:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}

View File

@ -22,7 +22,7 @@ jobs:
architectures: ${{ steps.info.outputs.architectures }} architectures: ${{ steps.info.outputs.architectures }}
steps: steps:
- name: Checkout the repository - name: Checkout the repository
uses: actions/checkout@v3.0.2 uses: actions/checkout@v3.1.0
- name: Get information - name: Get information
id: info id: info
@ -57,13 +57,13 @@ jobs:
) > .env_file ) > .env_file
- name: Upload env_file - name: Upload env_file
uses: actions/upload-artifact@v3.1.0 uses: actions/upload-artifact@v3.1.1
with: with:
name: env_file name: env_file
path: ./.env_file path: ./.env_file
- name: Upload requirements_diff - name: Upload requirements_diff
uses: actions/upload-artifact@v3.1.0 uses: actions/upload-artifact@v3.1.1
with: with:
name: requirements_diff name: requirements_diff
path: ./requirements_diff.txt path: ./requirements_diff.txt
@ -79,7 +79,7 @@ jobs:
arch: ${{ fromJson(needs.init.outputs.architectures) }} arch: ${{ fromJson(needs.init.outputs.architectures) }}
steps: steps:
- name: Checkout the repository - name: Checkout the repository
uses: actions/checkout@v3.0.2 uses: actions/checkout@v3.1.0
- name: Download env_file - name: Download env_file
uses: actions/download-artifact@v3 uses: actions/download-artifact@v3
@ -92,7 +92,7 @@ jobs:
name: requirements_diff name: requirements_diff
- name: Build wheels - name: Build wheels
uses: home-assistant/wheels@2022.06.7 uses: home-assistant/wheels@2022.10.1
with: with:
abi: cp310 abi: cp310
tag: musllinux_1_2 tag: musllinux_1_2
@ -116,7 +116,7 @@ jobs:
arch: ${{ fromJson(needs.init.outputs.architectures) }} arch: ${{ fromJson(needs.init.outputs.architectures) }}
steps: steps:
- name: Checkout the repository - name: Checkout the repository
uses: actions/checkout@v3.0.2 uses: actions/checkout@v3.1.0
- name: Download env_file - name: Download env_file
uses: actions/download-artifact@v3 uses: actions/download-artifact@v3
@ -140,7 +140,6 @@ jobs:
sed -i "s|# pycups|pycups|g" ${requirement_file} sed -i "s|# pycups|pycups|g" ${requirement_file}
sed -i "s|# homekit|homekit|g" ${requirement_file} sed -i "s|# homekit|homekit|g" ${requirement_file}
sed -i "s|# decora_wifi|decora_wifi|g" ${requirement_file} sed -i "s|# decora_wifi|decora_wifi|g" ${requirement_file}
sed -i "s|# face_recognition|face_recognition|g" ${requirement_file}
sed -i "s|# python-gammu|python-gammu|g" ${requirement_file} sed -i "s|# python-gammu|python-gammu|g" ${requirement_file}
sed -i "s|# opencv-python-headless|opencv-python-headless|g" ${requirement_file} sed -i "s|# opencv-python-headless|opencv-python-headless|g" ${requirement_file}
done done
@ -161,7 +160,7 @@ jobs:
sed -i "/numpy/d" homeassistant/package_constraints.txt sed -i "/numpy/d" homeassistant/package_constraints.txt
- name: Build wheels - name: Build wheels
uses: home-assistant/wheels@2022.06.7 uses: home-assistant/wheels@2022.10.1
with: with:
abi: cp310 abi: cp310
tag: musllinux_1_2 tag: musllinux_1_2

View File

@ -1,11 +1,11 @@
repos: repos:
- repo: https://github.com/asottile/pyupgrade - repo: https://github.com/asottile/pyupgrade
rev: v2.38.0 rev: v3.1.0
hooks: hooks:
- id: pyupgrade - id: pyupgrade
args: [--py39-plus] args: [--py39-plus]
- repo: https://github.com/psf/black - repo: https://github.com/psf/black
rev: 22.8.0 rev: 22.10.0
hooks: hooks:
- id: black - id: black
args: args:
@ -61,7 +61,7 @@ repos:
- --branch=master - --branch=master
- --branch=rc - --branch=rc
- repo: https://github.com/adrienverge/yamllint.git - repo: https://github.com/adrienverge/yamllint.git
rev: v1.27.1 rev: v1.28.0
hooks: hooks:
- id: yamllint - id: yamllint
- repo: https://github.com/pre-commit/mirrors-prettier - repo: https://github.com/pre-commit/mirrors-prettier
@ -69,11 +69,12 @@ repos:
hooks: hooks:
- id: prettier - id: prettier
- repo: https://github.com/cdce8p/python-typing-update - repo: https://github.com/cdce8p/python-typing-update
rev: v0.3.5 rev: v0.5.0
hooks: hooks:
# Run `python-typing-update` hook manually from time to time # Run `python-typing-update` hook manually from time to time
# to update python typing syntax. # to update python typing syntax.
# Will require manual work, before submitting changes! # Will require manual work, before submitting changes!
# pre-commit run --hook-stage manual python-typing-update --all-files
- id: python-typing-update - id: python-typing-update
stages: [manual] stages: [manual]
args: args:
@ -113,7 +114,7 @@ repos:
pass_filenames: false pass_filenames: false
language: script language: script
types: [text] types: [text]
files: ^(homeassistant/.+/(manifest|strings)\.json|\.coveragerc|homeassistant/.+/services\.yaml|script/hassfest/(?!metadata|mypy_config).+\.py)$ files: ^(homeassistant/.+/(manifest|strings)\.json|homeassistant/brands/.*\.json|\.coveragerc|homeassistant/.+/services\.yaml|script/hassfest/(?!metadata|mypy_config).+\.py)$
- id: hassfest-metadata - id: hassfest-metadata
name: hassfest-metadata name: hassfest-metadata
entry: script/run-in-env.sh python3 -m script.hassfest -p metadata entry: script/run-in-env.sh python3 -m script.hassfest -p metadata

View File

@ -2,7 +2,7 @@
# If component is fully covered with type annotations, please add it here # If component is fully covered with type annotations, please add it here
# to enable strict mypy checks. # to enable strict mypy checks.
# Stict typing is enabled by default for core files. # Strict typing is enabled by default for core files.
# Add it here to add 'disallow_any_generics'. # Add it here to add 'disallow_any_generics'.
# --- Only for core file! --- # --- Only for core file! ---
homeassistant.exceptions homeassistant.exceptions
@ -57,6 +57,7 @@ homeassistant.components.ambient_station.*
homeassistant.components.amcrest.* homeassistant.components.amcrest.*
homeassistant.components.ampio.* homeassistant.components.ampio.*
homeassistant.components.anthemav.* homeassistant.components.anthemav.*
homeassistant.components.aqualogic.*
homeassistant.components.aseko_pool_live.* homeassistant.components.aseko_pool_live.*
homeassistant.components.asuswrt.* homeassistant.components.asuswrt.*
homeassistant.components.auth.* homeassistant.components.auth.*
@ -64,7 +65,9 @@ homeassistant.components.automation.*
homeassistant.components.awair.* homeassistant.components.awair.*
homeassistant.components.backup.* homeassistant.components.backup.*
homeassistant.components.baf.* homeassistant.components.baf.*
homeassistant.components.bayesian.*
homeassistant.components.binary_sensor.* homeassistant.components.binary_sensor.*
homeassistant.components.blockchain.*
homeassistant.components.bluetooth.* homeassistant.components.bluetooth.*
homeassistant.components.bluetooth_tracker.* homeassistant.components.bluetooth_tracker.*
homeassistant.components.bmw_connected_drive.* homeassistant.components.bmw_connected_drive.*
@ -77,6 +80,8 @@ homeassistant.components.calendar.*
homeassistant.components.camera.* homeassistant.components.camera.*
homeassistant.components.canary.* homeassistant.components.canary.*
homeassistant.components.cover.* homeassistant.components.cover.*
homeassistant.components.clickatell.*
homeassistant.components.clicksend.*
homeassistant.components.cpuspeed.* homeassistant.components.cpuspeed.*
homeassistant.components.crownstone.* homeassistant.components.crownstone.*
homeassistant.components.deconz.* homeassistant.components.deconz.*
@ -116,6 +121,7 @@ homeassistant.components.geocaching.*
homeassistant.components.gios.* homeassistant.components.gios.*
homeassistant.components.goalzero.* homeassistant.components.goalzero.*
homeassistant.components.google.* homeassistant.components.google.*
homeassistant.components.google_sheets.*
homeassistant.components.greeneye_monitor.* homeassistant.components.greeneye_monitor.*
homeassistant.components.group.* homeassistant.components.group.*
homeassistant.components.guardian.* homeassistant.components.guardian.*
@ -160,6 +166,7 @@ homeassistant.components.lacrosse_view.*
homeassistant.components.lametric.* homeassistant.components.lametric.*
homeassistant.components.laundrify.* homeassistant.components.laundrify.*
homeassistant.components.lcn.* homeassistant.components.lcn.*
homeassistant.components.lidarr.*
homeassistant.components.lifx.* homeassistant.components.lifx.*
homeassistant.components.light.* homeassistant.components.light.*
homeassistant.components.litterrobot.* homeassistant.components.litterrobot.*
@ -208,6 +215,7 @@ homeassistant.components.pvoutput.*
homeassistant.components.qnap_qsw.* homeassistant.components.qnap_qsw.*
homeassistant.components.rainmachine.* homeassistant.components.rainmachine.*
homeassistant.components.rdw.* homeassistant.components.rdw.*
homeassistant.components.radarr.*
homeassistant.components.recollect_waste.* homeassistant.components.recollect_waste.*
homeassistant.components.recorder.* homeassistant.components.recorder.*
homeassistant.components.remote.* homeassistant.components.remote.*
@ -230,9 +238,12 @@ homeassistant.components.sensor.*
homeassistant.components.senz.* homeassistant.components.senz.*
homeassistant.components.shelly.* homeassistant.components.shelly.*
homeassistant.components.simplisafe.* homeassistant.components.simplisafe.*
homeassistant.components.skybell.*
homeassistant.components.slack.* homeassistant.components.slack.*
homeassistant.components.sleepiq.* homeassistant.components.sleepiq.*
homeassistant.components.smhi.* homeassistant.components.smhi.*
homeassistant.components.snooz.*
homeassistant.components.sonarr.*
homeassistant.components.ssdp.* homeassistant.components.ssdp.*
homeassistant.components.statistics.* homeassistant.components.statistics.*
homeassistant.components.steamist.* homeassistant.components.steamist.*
@ -249,6 +260,7 @@ homeassistant.components.tag.*
homeassistant.components.tailscale.* homeassistant.components.tailscale.*
homeassistant.components.tautulli.* homeassistant.components.tautulli.*
homeassistant.components.tcp.* homeassistant.components.tcp.*
homeassistant.components.tibber.*
homeassistant.components.tile.* homeassistant.components.tile.*
homeassistant.components.tilt_ble.* homeassistant.components.tilt_ble.*
homeassistant.components.tolo.* homeassistant.components.tolo.*
@ -260,6 +272,7 @@ homeassistant.components.trafikverket_train.*
homeassistant.components.trafikverket_weatherstation.* homeassistant.components.trafikverket_weatherstation.*
homeassistant.components.tts.* homeassistant.components.tts.*
homeassistant.components.twentemilieu.* homeassistant.components.twentemilieu.*
homeassistant.components.unifi.update
homeassistant.components.unifiprotect.* homeassistant.components.unifiprotect.*
homeassistant.components.upcloud.* homeassistant.components.upcloud.*
homeassistant.components.update.* homeassistant.components.update.*
@ -279,6 +292,7 @@ homeassistant.components.websocket_api.*
homeassistant.components.wemo.* homeassistant.components.wemo.*
homeassistant.components.whois.* homeassistant.components.whois.*
homeassistant.components.wiz.* homeassistant.components.wiz.*
homeassistant.components.wled.*
homeassistant.components.worldclock.* homeassistant.components.worldclock.*
homeassistant.components.yale_smart_alarm.* homeassistant.components.yale_smart_alarm.*
homeassistant.components.zeroconf.* homeassistant.components.zeroconf.*

View File

@ -19,6 +19,8 @@ build.json @home-assistant/supervisor
# Other code # Other code
/homeassistant/scripts/check_config.py @kellerza /homeassistant/scripts/check_config.py @kellerza
/homeassistant/const.py @epenet
/homeassistant/util/ @epenet
# Integrations # Integrations
/homeassistant/components/abode/ @shred86 /homeassistant/components/abode/ @shred86
@ -45,6 +47,8 @@ build.json @home-assistant/supervisor
/tests/components/airnow/ @asymworks /tests/components/airnow/ @asymworks
/homeassistant/components/airthings/ @danielhiversen /homeassistant/components/airthings/ @danielhiversen
/tests/components/airthings/ @danielhiversen /tests/components/airthings/ @danielhiversen
/homeassistant/components/airthings_ble/ @vincegio
/tests/components/airthings_ble/ @vincegio
/homeassistant/components/airtouch4/ @LonePurpleWolf /homeassistant/components/airtouch4/ @LonePurpleWolf
/tests/components/airtouch4/ @LonePurpleWolf /tests/components/airtouch4/ @LonePurpleWolf
/homeassistant/components/airvisual/ @bachya /homeassistant/components/airvisual/ @bachya
@ -55,8 +59,8 @@ build.json @home-assistant/supervisor
/tests/components/aladdin_connect/ @mkmer /tests/components/aladdin_connect/ @mkmer
/homeassistant/components/alarm_control_panel/ @home-assistant/core /homeassistant/components/alarm_control_panel/ @home-assistant/core
/tests/components/alarm_control_panel/ @home-assistant/core /tests/components/alarm_control_panel/ @home-assistant/core
/homeassistant/components/alert/ @home-assistant/core /homeassistant/components/alert/ @home-assistant/core @frenck
/tests/components/alert/ @home-assistant/core /tests/components/alert/ @home-assistant/core @frenck
/homeassistant/components/alexa/ @home-assistant/cloud @ochlocracy /homeassistant/components/alexa/ @home-assistant/cloud @ochlocracy
/tests/components/alexa/ @home-assistant/cloud @ochlocracy /tests/components/alexa/ @home-assistant/cloud @ochlocracy
/homeassistant/components/almond/ @gcampax @balloob /homeassistant/components/almond/ @gcampax @balloob
@ -281,7 +285,6 @@ build.json @home-assistant/supervisor
/homeassistant/components/ecovacs/ @OverloadUT @mib1185 /homeassistant/components/ecovacs/ @OverloadUT @mib1185
/homeassistant/components/ecowitt/ @pvizeli /homeassistant/components/ecowitt/ @pvizeli
/tests/components/ecowitt/ @pvizeli /tests/components/ecowitt/ @pvizeli
/homeassistant/components/edl21/ @mtdcr
/homeassistant/components/efergy/ @tkdrob /homeassistant/components/efergy/ @tkdrob
/tests/components/efergy/ @tkdrob /tests/components/efergy/ @tkdrob
/homeassistant/components/egardia/ @jeroenterheerdt /homeassistant/components/egardia/ @jeroenterheerdt
@ -566,8 +569,8 @@ build.json @home-assistant/supervisor
/tests/components/isy994/ @bdraco @shbatm /tests/components/isy994/ @bdraco @shbatm
/homeassistant/components/izone/ @Swamp-Ig /homeassistant/components/izone/ @Swamp-Ig
/tests/components/izone/ @Swamp-Ig /tests/components/izone/ @Swamp-Ig
/homeassistant/components/jellyfin/ @j-stienstra /homeassistant/components/jellyfin/ @j-stienstra @ctalkington
/tests/components/jellyfin/ @j-stienstra /tests/components/jellyfin/ @j-stienstra @ctalkington
/homeassistant/components/jewish_calendar/ @tsvi /homeassistant/components/jewish_calendar/ @tsvi
/tests/components/jewish_calendar/ @tsvi /tests/components/jewish_calendar/ @tsvi
/homeassistant/components/juicenet/ @jesserockz /homeassistant/components/juicenet/ @jesserockz
@ -602,8 +605,8 @@ build.json @home-assistant/supervisor
/tests/components/kulersky/ @emlove /tests/components/kulersky/ @emlove
/homeassistant/components/lacrosse_view/ @IceBotYT /homeassistant/components/lacrosse_view/ @IceBotYT
/tests/components/lacrosse_view/ @IceBotYT /tests/components/lacrosse_view/ @IceBotYT
/homeassistant/components/lametric/ @robbiet480 @frenck /homeassistant/components/lametric/ @robbiet480 @frenck @bachya
/tests/components/lametric/ @robbiet480 @frenck /tests/components/lametric/ @robbiet480 @frenck @bachya
/homeassistant/components/landisgyr_heat_meter/ @vpathuis /homeassistant/components/landisgyr_heat_meter/ @vpathuis
/tests/components/landisgyr_heat_meter/ @vpathuis /tests/components/landisgyr_heat_meter/ @vpathuis
/homeassistant/components/launch_library/ @ludeeus @DurgNomis-drol /homeassistant/components/launch_library/ @ludeeus @DurgNomis-drol
@ -794,8 +797,8 @@ build.json @home-assistant/supervisor
/tests/components/omnilogic/ @oliver84 @djtimca @gentoosu /tests/components/omnilogic/ @oliver84 @djtimca @gentoosu
/homeassistant/components/onboarding/ @home-assistant/core /homeassistant/components/onboarding/ @home-assistant/core
/tests/components/onboarding/ @home-assistant/core /tests/components/onboarding/ @home-assistant/core
/homeassistant/components/oncue/ @bdraco /homeassistant/components/oncue/ @bdraco @peterager
/tests/components/oncue/ @bdraco /tests/components/oncue/ @bdraco @peterager
/homeassistant/components/ondilo_ico/ @JeromeHXP /homeassistant/components/ondilo_ico/ @JeromeHXP
/tests/components/ondilo_ico/ @JeromeHXP /tests/components/ondilo_ico/ @JeromeHXP
/homeassistant/components/onewire/ @garbled1 @epenet /homeassistant/components/onewire/ @garbled1 @epenet
@ -819,6 +822,8 @@ build.json @home-assistant/supervisor
/tests/components/openweathermap/ @fabaff @freekode @nzapponi /tests/components/openweathermap/ @fabaff @freekode @nzapponi
/homeassistant/components/opnsense/ @mtreinish /homeassistant/components/opnsense/ @mtreinish
/tests/components/opnsense/ @mtreinish /tests/components/opnsense/ @mtreinish
/homeassistant/components/oralb/ @bdraco
/tests/components/oralb/ @bdraco
/homeassistant/components/oru/ @bvlaicu /homeassistant/components/oru/ @bvlaicu
/homeassistant/components/overkiz/ @imicknl @vlebourl @tetienne /homeassistant/components/overkiz/ @imicknl @vlebourl @tetienne
/tests/components/overkiz/ @imicknl @vlebourl @tetienne /tests/components/overkiz/ @imicknl @vlebourl @tetienne
@ -930,8 +935,6 @@ build.json @home-assistant/supervisor
/tests/components/rhasspy/ @balloob @synesthesiam /tests/components/rhasspy/ @balloob @synesthesiam
/homeassistant/components/ridwell/ @bachya /homeassistant/components/ridwell/ @bachya
/tests/components/ridwell/ @bachya /tests/components/ridwell/ @bachya
/homeassistant/components/ring/ @balloob
/tests/components/ring/ @balloob
/homeassistant/components/risco/ @OnFreund /homeassistant/components/risco/ @OnFreund
/tests/components/risco/ @OnFreund /tests/components/risco/ @OnFreund
/homeassistant/components/rituals_perfume_genie/ @milanmeu /homeassistant/components/rituals_perfume_genie/ @milanmeu
@ -964,8 +967,8 @@ build.json @home-assistant/supervisor
/homeassistant/components/schedule/ @home-assistant/core /homeassistant/components/schedule/ @home-assistant/core
/tests/components/schedule/ @home-assistant/core /tests/components/schedule/ @home-assistant/core
/homeassistant/components/schluter/ @prairieapps /homeassistant/components/schluter/ @prairieapps
/homeassistant/components/scrape/ @fabaff /homeassistant/components/scrape/ @fabaff @gjohansson-ST @epenet
/tests/components/scrape/ @fabaff /tests/components/scrape/ @fabaff @gjohansson-ST @epenet
/homeassistant/components/screenlogic/ @dieselrabbit @bdraco /homeassistant/components/screenlogic/ @dieselrabbit @bdraco
/tests/components/screenlogic/ @dieselrabbit @bdraco /tests/components/screenlogic/ @dieselrabbit @bdraco
/homeassistant/components/script/ @home-assistant/core /homeassistant/components/script/ @home-assistant/core
@ -1039,6 +1042,8 @@ build.json @home-assistant/supervisor
/homeassistant/components/smhi/ @gjohansson-ST /homeassistant/components/smhi/ @gjohansson-ST
/tests/components/smhi/ @gjohansson-ST /tests/components/smhi/ @gjohansson-ST
/homeassistant/components/sms/ @ocalvo /homeassistant/components/sms/ @ocalvo
/homeassistant/components/snooz/ @AustinBrunkhorst
/tests/components/snooz/ @AustinBrunkhorst
/homeassistant/components/solaredge/ @frenck /homeassistant/components/solaredge/ @frenck
/tests/components/solaredge/ @frenck /tests/components/solaredge/ @frenck
/homeassistant/components/solaredge_local/ @drobtravels @scheric /homeassistant/components/solaredge_local/ @drobtravels @scheric
@ -1198,8 +1203,8 @@ build.json @home-assistant/supervisor
/tests/components/upcloud/ @scop /tests/components/upcloud/ @scop
/homeassistant/components/update/ @home-assistant/core /homeassistant/components/update/ @home-assistant/core
/tests/components/update/ @home-assistant/core /tests/components/update/ @home-assistant/core
/homeassistant/components/upnp/ @StevenLooman @ehendrix23 /homeassistant/components/upnp/ @StevenLooman
/tests/components/upnp/ @StevenLooman @ehendrix23 /tests/components/upnp/ @StevenLooman
/homeassistant/components/uptime/ @frenck /homeassistant/components/uptime/ @frenck
/tests/components/uptime/ @frenck /tests/components/uptime/ @frenck
/homeassistant/components/uptimerobot/ @ludeeus @chemelli74 /homeassistant/components/uptimerobot/ @ludeeus @chemelli74
@ -1293,8 +1298,8 @@ build.json @home-assistant/supervisor
/tests/components/xiaomi_aqara/ @danielhiversen @syssi /tests/components/xiaomi_aqara/ @danielhiversen @syssi
/homeassistant/components/xiaomi_ble/ @Jc2k @Ernst79 /homeassistant/components/xiaomi_ble/ @Jc2k @Ernst79
/tests/components/xiaomi_ble/ @Jc2k @Ernst79 /tests/components/xiaomi_ble/ @Jc2k @Ernst79
/homeassistant/components/xiaomi_miio/ @rytilahti @syssi @starkillerOG @bieniu /homeassistant/components/xiaomi_miio/ @rytilahti @syssi @starkillerOG
/tests/components/xiaomi_miio/ @rytilahti @syssi @starkillerOG @bieniu /tests/components/xiaomi_miio/ @rytilahti @syssi @starkillerOG
/homeassistant/components/xiaomi_tv/ @simse /homeassistant/components/xiaomi_tv/ @simse
/homeassistant/components/xmpp/ @fabaff @flowolf /homeassistant/components/xmpp/ @fabaff @flowolf
/homeassistant/components/yale_smart_alarm/ @gjohansson-ST /homeassistant/components/yale_smart_alarm/ @gjohansson-ST
@ -1313,6 +1318,8 @@ build.json @home-assistant/supervisor
/tests/components/yolink/ @matrixd2 /tests/components/yolink/ @matrixd2
/homeassistant/components/youless/ @gjong /homeassistant/components/youless/ @gjong
/tests/components/youless/ @gjong /tests/components/youless/ @gjong
/homeassistant/components/zamg/ @killer0071234
/tests/components/zamg/ @killer0071234
/homeassistant/components/zengge/ @emontnemery /homeassistant/components/zengge/ @emontnemery
/homeassistant/components/zeroconf/ @bdraco /homeassistant/components/zeroconf/ @bdraco
/tests/components/zeroconf/ @bdraco /tests/components/zeroconf/ @bdraco

View File

@ -2,6 +2,15 @@ FROM mcr.microsoft.com/vscode/devcontainers/python:0-3.9
SHELL ["/bin/bash", "-o", "pipefail", "-c"] SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# Uninstall pre-installed formatting and linting tools
# They would conflict with our pinned versions
RUN pipx uninstall black
RUN pipx uninstall flake8
RUN pipx uninstall pydocstyle
RUN pipx uninstall pycodestyle
RUN pipx uninstall mypy
RUN pipx uninstall pylint
RUN \ RUN \
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \ curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
&& apt-get update \ && apt-get update \

View File

@ -1,11 +1,11 @@
image: homeassistant/{arch}-homeassistant image: homeassistant/{arch}-homeassistant
shadow_repository: ghcr.io/home-assistant shadow_repository: ghcr.io/home-assistant
build_from: build_from:
aarch64: ghcr.io/home-assistant/aarch64-homeassistant-base:2022.07.0 aarch64: ghcr.io/home-assistant/aarch64-homeassistant-base:2022.10.0
armhf: ghcr.io/home-assistant/armhf-homeassistant-base:2022.07.0 armhf: ghcr.io/home-assistant/armhf-homeassistant-base:2022.10.0
armv7: ghcr.io/home-assistant/armv7-homeassistant-base:2022.07.0 armv7: ghcr.io/home-assistant/armv7-homeassistant-base:2022.10.0
amd64: ghcr.io/home-assistant/amd64-homeassistant-base:2022.07.0 amd64: ghcr.io/home-assistant/amd64-homeassistant-base:2022.10.0
i386: ghcr.io/home-assistant/i386-homeassistant-base:2022.07.0 i386: ghcr.io/home-assistant/i386-homeassistant-base:2022.10.0
codenotary: codenotary:
signer: notary@home-assistant.io signer: notary@home-assistant.io
base_image: notary@home-assistant.io base_image: notary@home-assistant.io

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 KiB

After

Width:  |  Height:  |  Size: 118 KiB

View File

@ -21,7 +21,7 @@ from .components import http, persistent_notification
from .const import ( from .const import (
REQUIRED_NEXT_PYTHON_HA_RELEASE, REQUIRED_NEXT_PYTHON_HA_RELEASE,
REQUIRED_NEXT_PYTHON_VER, REQUIRED_NEXT_PYTHON_VER,
SIGNAL_BOOTSTRAP_INTEGRATONS, SIGNAL_BOOTSTRAP_INTEGRATIONS,
) )
from .exceptions import HomeAssistantError from .exceptions import HomeAssistantError
from .helpers import ( from .helpers import (
@ -431,7 +431,7 @@ async def _async_watch_pending_setups(hass: core.HomeAssistant) -> None:
_LOGGER.debug("Integration remaining: %s", remaining_with_setup_started) _LOGGER.debug("Integration remaining: %s", remaining_with_setup_started)
if remaining_with_setup_started or not previous_was_empty: if remaining_with_setup_started or not previous_was_empty:
async_dispatcher_send( async_dispatcher_send(
hass, SIGNAL_BOOTSTRAP_INTEGRATONS, remaining_with_setup_started hass, SIGNAL_BOOTSTRAP_INTEGRATIONS, remaining_with_setup_started
) )
previous_was_empty = not remaining_with_setup_started previous_was_empty = not remaining_with_setup_started
await asyncio.sleep(SLOW_STARTUP_CHECK_INTERVAL) await asyncio.sleep(SLOW_STARTUP_CHECK_INTERVAL)
@ -622,7 +622,7 @@ async def _async_set_up_integrations(
_LOGGER.warning("Setup timed out for bootstrap - moving forward") _LOGGER.warning("Setup timed out for bootstrap - moving forward")
watch_task.cancel() watch_task.cancel()
async_dispatcher_send(hass, SIGNAL_BOOTSTRAP_INTEGRATONS, {}) async_dispatcher_send(hass, SIGNAL_BOOTSTRAP_INTEGRATIONS, {})
_LOGGER.debug( _LOGGER.debug(
"Integration setup times: %s", "Integration setup times: %s",

View File

@ -0,0 +1,5 @@
{
"domain": "airthings",
"name": "Airthings",
"integrations": ["airthings", "airthings_ble"]
}

View File

@ -1,5 +1,6 @@
{ {
"domain": "devolo", "domain": "devolo",
"name": "devolo", "name": "devolo",
"integrations": ["devolo_home_control", "devolo_home_network"] "integrations": ["devolo_home_control", "devolo_home_network"],
"iot_standards": ["zwave"]
} }

View File

@ -0,0 +1,5 @@
{
"domain": "ikea",
"name": "IKEA",
"integrations": ["symfonisk", "tradfri"]
}

View File

@ -1,5 +0,0 @@
{
"domain": "raspberry_pi",
"name": "Raspberry Pi",
"integrations": ["rpi_camera", "rpi_power", "remote_rpi_gpio"]
}

View File

@ -0,0 +1,5 @@
{
"domain": "raspberry_pi",
"name": "Raspberry Pi",
"integrations": ["raspberry_pi", "rpi_camera", "rpi_power", "remote_rpi_gpio"]
}

View File

@ -1,5 +1,5 @@
{ {
"domain": "u_tec", "domain": "u_tec",
"name": "U-tec", "name": "U-tec",
"iot_standards": ["zwave"] "integrations": ["ultraloq"]
} }

View File

@ -0,0 +1,6 @@
{
"domain": "3_day_blinds",
"name": "3 Day Blinds",
"integration_type": "virtual",
"supported_by": "motion_blinds"
}

View File

@ -11,13 +11,13 @@
"reauth_confirm": { "reauth_confirm": {
"data": { "data": {
"password": "\u041f\u0430\u0440\u043e\u043b\u0430", "password": "\u041f\u0430\u0440\u043e\u043b\u0430",
"username": "Email" "username": "\u0418\u043c\u0435\u0439\u043b"
} }
}, },
"user": { "user": {
"data": { "data": {
"password": "\u041f\u0430\u0440\u043e\u043b\u0430", "password": "\u041f\u0430\u0440\u043e\u043b\u0430",
"username": "E-mail \u0430\u0434\u0440\u0435\u0441" "username": "\u0418\u043c\u0435\u0439\u043b"
}, },
"title": "\u041f\u043e\u043f\u044a\u043b\u043d\u0435\u0442\u0435 \u0412\u0430\u0448\u0430\u0442\u0430 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u0437\u0430 \u0432\u0445\u043e\u0434 \u0432 Abode" "title": "\u041f\u043e\u043f\u044a\u043b\u043d\u0435\u0442\u0435 \u0412\u0430\u0448\u0430\u0442\u0430 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u0437\u0430 \u0432\u0445\u043e\u0434 \u0432 Abode"
} }

View File

@ -1,7 +1,7 @@
{ {
"config": { "config": {
"abort": { "abort": {
"reauth_successful": "Godkjenning p\u00e5 nytt var vellykket", "reauth_successful": "Re-autentisering var vellykket",
"single_instance_allowed": "Allerede konfigurert. Bare \u00e9n enkelt konfigurasjon er mulig." "single_instance_allowed": "Allerede konfigurert. Bare \u00e9n enkelt konfigurasjon er mulig."
}, },
"error": { "error": {

View File

@ -17,6 +17,7 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.device_registry import DeviceEntryType from homeassistant.helpers.device_registry import DeviceEntryType
from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from homeassistant.util.unit_system import METRIC_SYSTEM
from .const import ATTR_FORECAST, CONF_FORECAST, DOMAIN, MANUFACTURER from .const import ATTR_FORECAST, CONF_FORECAST, DOMAIN, MANUFACTURER
@ -116,7 +117,7 @@ class AccuWeatherDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
current = await self.accuweather.async_get_current_conditions() current = await self.accuweather.async_get_current_conditions()
forecast = ( forecast = (
await self.accuweather.async_get_forecast( await self.accuweather.async_get_forecast(
metric=self.hass.config.units.is_metric metric=self.hass.config.units is METRIC_SYSTEM
) )
if self.forecast if self.forecast
else {} else {}

View File

@ -7,5 +7,6 @@
"config_flow": true, "config_flow": true,
"quality_scale": "platinum", "quality_scale": "platinum",
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"loggers": ["accuweather"] "loggers": ["accuweather"],
"integration_type": "service"
} }

View File

@ -30,6 +30,7 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType from homeassistant.helpers.typing import StateType
from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.helpers.update_coordinator import CoordinatorEntity
from homeassistant.util.unit_system import METRIC_SYSTEM
from . import AccuWeatherDataUpdateCoordinator from . import AccuWeatherDataUpdateCoordinator
from .const import ( from .const import (
@ -189,6 +190,7 @@ FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = (
), ),
AccuWeatherSensorDescription( AccuWeatherSensorDescription(
key="WindGustDay", key="WindGustDay",
device_class=SensorDeviceClass.WIND_SPEED,
icon="mdi:weather-windy", icon="mdi:weather-windy",
name="Wind gust day", name="Wind gust day",
entity_registry_enabled_default=False, entity_registry_enabled_default=False,
@ -200,6 +202,7 @@ FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = (
), ),
AccuWeatherSensorDescription( AccuWeatherSensorDescription(
key="WindGustNight", key="WindGustNight",
device_class=SensorDeviceClass.WIND_SPEED,
icon="mdi:weather-windy", icon="mdi:weather-windy",
name="Wind gust night", name="Wind gust night",
entity_registry_enabled_default=False, entity_registry_enabled_default=False,
@ -211,6 +214,7 @@ FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = (
), ),
AccuWeatherSensorDescription( AccuWeatherSensorDescription(
key="WindDay", key="WindDay",
device_class=SensorDeviceClass.WIND_SPEED,
icon="mdi:weather-windy", icon="mdi:weather-windy",
name="Wind day", name="Wind day",
unit_fn=lambda metric: SPEED_KILOMETERS_PER_HOUR unit_fn=lambda metric: SPEED_KILOMETERS_PER_HOUR
@ -221,6 +225,7 @@ FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = (
), ),
AccuWeatherSensorDescription( AccuWeatherSensorDescription(
key="WindNight", key="WindNight",
device_class=SensorDeviceClass.WIND_SPEED,
icon="mdi:weather-windy", icon="mdi:weather-windy",
name="Wind night", name="Wind night",
unit_fn=lambda metric: SPEED_KILOMETERS_PER_HOUR unit_fn=lambda metric: SPEED_KILOMETERS_PER_HOUR
@ -243,6 +248,7 @@ SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = (
), ),
AccuWeatherSensorDescription( AccuWeatherSensorDescription(
key="Ceiling", key="Ceiling",
device_class=SensorDeviceClass.DISTANCE,
icon="mdi:weather-fog", icon="mdi:weather-fog",
name="Cloud ceiling", name="Cloud ceiling",
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
@ -329,6 +335,7 @@ SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = (
), ),
AccuWeatherSensorDescription( AccuWeatherSensorDescription(
key="Wind", key="Wind",
device_class=SensorDeviceClass.WIND_SPEED,
icon="mdi:weather-windy", icon="mdi:weather-windy",
name="Wind", name="Wind",
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
@ -339,6 +346,7 @@ SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = (
), ),
AccuWeatherSensorDescription( AccuWeatherSensorDescription(
key="WindGust", key="WindGust",
device_class=SensorDeviceClass.WIND_SPEED,
icon="mdi:weather-windy", icon="mdi:weather-windy",
name="Wind gust", name="Wind gust",
entity_registry_enabled_default=False, entity_registry_enabled_default=False,
@ -405,12 +413,12 @@ class AccuWeatherSensor(
self._attr_unique_id = ( self._attr_unique_id = (
f"{coordinator.location_key}-{description.key}".lower() f"{coordinator.location_key}-{description.key}".lower()
) )
if self.coordinator.hass.config.units.is_metric: if self.coordinator.hass.config.units is METRIC_SYSTEM:
self._unit_system = API_METRIC self._unit_system = API_METRIC
else: else:
self._unit_system = API_IMPERIAL self._unit_system = API_IMPERIAL
self._attr_native_unit_of_measurement = self.entity_description.unit_fn( self._attr_native_unit_of_measurement = self.entity_description.unit_fn(
self.coordinator.hass.config.units.is_metric self.coordinator.hass.config.units is METRIC_SYSTEM
) )
self._attr_device_info = coordinator.device_info self._attr_device_info = coordinator.device_info
if forecast_day is not None: if forecast_day is not None:

View File

@ -4,7 +4,7 @@
"single_instance_allowed": "\u0412\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e. \u0412\u044a\u0437\u043c\u043e\u0436\u043d\u0430 \u0435 \u0441\u0430\u043c\u043e \u0435\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f." "single_instance_allowed": "\u0412\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e. \u0412\u044a\u0437\u043c\u043e\u0436\u043d\u0430 \u0435 \u0441\u0430\u043c\u043e \u0435\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f."
}, },
"error": { "error": {
"cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435",
"invalid_api_key": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u0435\u043d API \u043a\u043b\u044e\u0447" "invalid_api_key": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u0435\u043d API \u043a\u043b\u044e\u0447"
}, },
"step": { "step": {

View File

@ -33,6 +33,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.helpers.update_coordinator import CoordinatorEntity
from homeassistant.util.dt import utc_from_timestamp from homeassistant.util.dt import utc_from_timestamp
from homeassistant.util.unit_system import METRIC_SYSTEM
from . import AccuWeatherDataUpdateCoordinator from . import AccuWeatherDataUpdateCoordinator
from .const import ( from .const import (
@ -70,7 +71,7 @@ class AccuWeatherEntity(
# Coordinator data is used also for sensors which don't have units automatically # Coordinator data is used also for sensors which don't have units automatically
# converted, hence the weather entity's native units follow the configured unit # converted, hence the weather entity's native units follow the configured unit
# system # system
if coordinator.hass.config.units.is_metric: if coordinator.hass.config.units is METRIC_SYSTEM:
self._attr_native_precipitation_unit = LENGTH_MILLIMETERS self._attr_native_precipitation_unit = LENGTH_MILLIMETERS
self._attr_native_pressure_unit = PRESSURE_HPA self._attr_native_pressure_unit = PRESSURE_HPA
self._attr_native_temperature_unit = TEMP_CELSIUS self._attr_native_temperature_unit = TEMP_CELSIUS

View File

@ -3,7 +3,7 @@
"name": "Adax", "name": "Adax",
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/adax", "documentation": "https://www.home-assistant.io/integrations/adax",
"requirements": ["adax==0.2.0", "Adax-local==0.1.4"], "requirements": ["adax==0.2.0", "Adax-local==0.1.5"],
"codeowners": ["@danielhiversen"], "codeowners": ["@danielhiversen"],
"iot_class": "local_polling", "iot_class": "local_polling",
"loggers": ["adax", "adax_local"] "loggers": ["adax", "adax_local"]

View File

@ -6,5 +6,6 @@
"requirements": ["adguardhome==0.5.1"], "requirements": ["adguardhome==0.5.1"],
"codeowners": ["@frenck"], "codeowners": ["@frenck"],
"iot_class": "local_polling", "iot_class": "local_polling",
"integration_type": "service",
"loggers": ["adguardhome"] "loggers": ["adguardhome"]
} }

View File

@ -1,6 +1,7 @@
{ {
"config": { "config": {
"abort": { "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",
"existing_instance_updated": "\u0410\u043a\u0442\u0443\u0430\u043b\u0438\u0437\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u0441\u044a\u0449\u0435\u0441\u0442\u0432\u0443\u0432\u0430\u0449\u0430\u0442\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f." "existing_instance_updated": "\u0410\u043a\u0442\u0443\u0430\u043b\u0438\u0437\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u0441\u044a\u0449\u0435\u0441\u0442\u0432\u0443\u0432\u0430\u0449\u0430\u0442\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f."
}, },
"error": { "error": {

View File

@ -21,11 +21,11 @@ from homeassistant.components.weather import (
from homeassistant.const import ( from homeassistant.const import (
DEGREE, DEGREE,
PERCENTAGE, PERCENTAGE,
PRECIPITATION_MILLIMETERS_PER_HOUR,
PRESSURE_HPA, PRESSURE_HPA,
SPEED_KILOMETERS_PER_HOUR, SPEED_KILOMETERS_PER_HOUR,
TEMP_CELSIUS, TEMP_CELSIUS,
Platform, Platform,
UnitOfVolumetricFlux,
) )
ATTRIBUTION = "Powered by AEMET OpenData" ATTRIBUTION = "Powered by AEMET OpenData"
@ -208,7 +208,8 @@ FORECAST_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
SensorEntityDescription( SensorEntityDescription(
key=ATTR_API_FORECAST_PRECIPITATION, key=ATTR_API_FORECAST_PRECIPITATION,
name="Precipitation", name="Precipitation",
native_unit_of_measurement=PRECIPITATION_MILLIMETERS_PER_HOUR, native_unit_of_measurement=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
device_class=SensorDeviceClass.PRECIPITATION_INTENSITY,
), ),
SensorEntityDescription( SensorEntityDescription(
key=ATTR_API_FORECAST_PRECIPITATION_PROBABILITY, key=ATTR_API_FORECAST_PRECIPITATION_PROBABILITY,
@ -265,7 +266,8 @@ WEATHER_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
SensorEntityDescription( SensorEntityDescription(
key=ATTR_API_RAIN, key=ATTR_API_RAIN,
name="Rain", name="Rain",
native_unit_of_measurement=PRECIPITATION_MILLIMETERS_PER_HOUR, native_unit_of_measurement=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
device_class=SensorDeviceClass.PRECIPITATION_INTENSITY,
), ),
SensorEntityDescription( SensorEntityDescription(
key=ATTR_API_RAIN_PROB, key=ATTR_API_RAIN_PROB,
@ -276,7 +278,8 @@ WEATHER_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
SensorEntityDescription( SensorEntityDescription(
key=ATTR_API_SNOW, key=ATTR_API_SNOW,
name="Snow", name="Snow",
native_unit_of_measurement=PRECIPITATION_MILLIMETERS_PER_HOUR, native_unit_of_measurement=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
device_class=SensorDeviceClass.PRECIPITATION_INTENSITY,
), ),
SensorEntityDescription( SensorEntityDescription(
key=ATTR_API_SNOW_PROB, key=ATTR_API_SNOW_PROB,

View File

@ -84,8 +84,7 @@ async def async_setup_entry(
unique_id = f"{config_entry.unique_id} {mode}" unique_id = f"{config_entry.unique_id} {mode}"
entities.append(AemetWeather(name, unique_id, weather_coordinator, mode)) entities.append(AemetWeather(name, unique_id, weather_coordinator, mode))
if entities: async_add_entities(entities, False)
async_add_entities(entities, False)
class AemetWeather(CoordinatorEntity[WeatherUpdateCoordinator], WeatherEntity): class AemetWeather(CoordinatorEntity[WeatherUpdateCoordinator], WeatherEntity):

View File

@ -7,5 +7,6 @@
"config_flow": true, "config_flow": true,
"quality_scale": "platinum", "quality_scale": "platinum",
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"loggers": ["airly"] "loggers": ["airly"],
"integration_type": "service"
} }

View File

@ -0,0 +1,53 @@
"""Diagnostics support for AirNow."""
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 . import AirNowDataUpdateCoordinator
from .const import DOMAIN
ATTR_LATITUDE_CAP = "Latitude"
ATTR_LONGITUDE_CAP = "Longitude"
ATTR_REPORTING_AREA = "ReportingArea"
ATTR_STATE_CODE = "StateCode"
CONF_TITLE = "title"
TO_REDACT = {
ATTR_LATITUDE_CAP,
ATTR_LONGITUDE_CAP,
ATTR_REPORTING_AREA,
ATTR_STATE_CODE,
CONF_API_KEY,
CONF_LATITUDE,
CONF_LONGITUDE,
# The config entry title has latitude/longitude:
CONF_TITLE,
# The config entry unique ID has latitude/longitude:
CONF_UNIQUE_ID,
}
async def async_get_config_entry_diagnostics(
hass: HomeAssistant, entry: ConfigEntry
) -> dict[str, Any]:
"""Return diagnostics for a config entry."""
coordinator: AirNowDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
return async_redact_data(
{
"entry": entry.as_dict(),
"data": coordinator.data,
},
TO_REDACT,
)

View File

@ -0,0 +1,7 @@
{
"config": {
"error": {
"unknown": "Uventet feil"
}
}
}

View File

@ -0,0 +1,7 @@
{
"config": {
"error": {
"unknown": "Uventet feil"
}
}
}

View File

@ -0,0 +1,74 @@
"""The Airthings BLE integration."""
from __future__ import annotations
from datetime import timedelta
import logging
from airthings_ble import AirthingsBluetoothDeviceData
from homeassistant.components import bluetooth
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from homeassistant.util.unit_system import METRIC_SYSTEM
from .const import DEFAULT_SCAN_INTERVAL, DOMAIN
PLATFORMS: list[Platform] = [Platform.SENSOR]
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Airthings BLE device from a config entry."""
hass.data.setdefault(DOMAIN, {})
address = entry.unique_id
elevation = hass.config.elevation
is_metric = hass.config.units is METRIC_SYSTEM
assert address is not None
ble_device = bluetooth.async_ble_device_from_address(hass, address)
if not ble_device:
raise ConfigEntryNotReady(
f"Could not find Airthings device with address {address}"
)
async def _async_update_method():
"""Get data from Airthings BLE."""
ble_device = bluetooth.async_ble_device_from_address(hass, address)
airthings = AirthingsBluetoothDeviceData(_LOGGER, elevation, is_metric)
try:
data = await airthings.update_device(ble_device)
except Exception as err:
raise UpdateFailed(f"Unable to fetch data: {err}") from err
return data
coordinator = DataUpdateCoordinator(
hass,
_LOGGER,
name=DOMAIN,
update_method=_async_update_method,
update_interval=timedelta(seconds=DEFAULT_SCAN_INTERVAL),
)
await coordinator.async_config_entry_first_refresh()
hass.data[DOMAIN][entry.entry_id] = coordinator
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):
hass.data[DOMAIN].pop(entry.entry_id)
return unload_ok

View File

@ -0,0 +1,169 @@
"""Config flow for Airthings BlE integration."""
from __future__ import annotations
import dataclasses
import logging
from typing import Any
from airthings_ble import AirthingsBluetoothDeviceData, AirthingsDevice
from bleak import BleakError
import voluptuous as vol
from homeassistant.components import bluetooth
from homeassistant.components.bluetooth import (
BluetoothServiceInfo,
async_discovered_service_info,
)
from homeassistant.config_entries import ConfigFlow
from homeassistant.const import CONF_ADDRESS
from homeassistant.data_entry_flow import FlowResult
from .const import DOMAIN, MFCT_ID
_LOGGER = logging.getLogger(__name__)
@dataclasses.dataclass
class Discovery:
"""A discovered bluetooth device."""
name: str
discovery_info: BluetoothServiceInfo
device: AirthingsDevice
def get_name(device: AirthingsDevice) -> str:
"""Generate name with identifier for device."""
return f"{device.name} ({device.identifier})"
class AirthingsDeviceUpdateError(Exception):
"""Custom error class for device updates."""
class AirthingsConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Airthings BLE."""
VERSION = 1
def __init__(self) -> None:
"""Initialize the config flow."""
self._discovered_device: Discovery | None = None
self._discovered_devices: dict[str, Discovery] = {}
async def _get_device_data(
self, discovery_info: BluetoothServiceInfo
) -> AirthingsDevice:
ble_device = bluetooth.async_ble_device_from_address(
self.hass, discovery_info.address
)
if ble_device is None:
_LOGGER.debug("no ble_device in _get_device_data")
raise AirthingsDeviceUpdateError("No ble_device")
airthings = AirthingsBluetoothDeviceData(_LOGGER)
try:
data = await airthings.update_device(ble_device)
except BleakError as err:
_LOGGER.error(
"Error connecting to and getting data from %s: %s",
discovery_info.address,
err,
)
raise AirthingsDeviceUpdateError("Failed getting device data") from err
except Exception as err:
_LOGGER.error(
"Unknown error occurred from %s: %s", discovery_info.address, err
)
raise err
return data
async def async_step_bluetooth(
self, discovery_info: BluetoothServiceInfo
) -> FlowResult:
"""Handle the bluetooth discovery step."""
_LOGGER.debug("Discovered BT device: %s", discovery_info)
await self.async_set_unique_id(discovery_info.address)
self._abort_if_unique_id_configured()
try:
device = await self._get_device_data(discovery_info)
except AirthingsDeviceUpdateError:
return self.async_abort(reason="cannot_connect")
except Exception: # pylint: disable=broad-except
return self.async_abort(reason="unknown")
name = get_name(device)
self.context["title_placeholders"] = {"name": name}
self._discovered_device = Discovery(name, discovery_info, device)
return await self.async_step_bluetooth_confirm()
async def async_step_bluetooth_confirm(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Confirm discovery."""
if user_input is not None:
return self.async_create_entry(
title=self.context["title_placeholders"]["name"], data={}
)
self._set_confirm_only()
return self.async_show_form(
step_id="bluetooth_confirm",
description_placeholders=self.context["title_placeholders"],
)
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Handle the user step to pick discovered device."""
if user_input is not None:
address = user_input[CONF_ADDRESS]
await self.async_set_unique_id(address, raise_on_progress=False)
self._abort_if_unique_id_configured()
discovery = self._discovered_devices[address]
self.context["title_placeholders"] = {
"name": discovery.name,
}
self._discovered_device = discovery
return self.async_create_entry(title=discovery.name, data={})
current_addresses = self._async_current_ids()
for discovery_info in async_discovered_service_info(self.hass):
address = discovery_info.address
if address in current_addresses or address in self._discovered_devices:
continue
if MFCT_ID not in discovery_info.manufacturer_data:
continue
try:
device = await self._get_device_data(discovery_info)
except AirthingsDeviceUpdateError:
return self.async_abort(reason="cannot_connect")
except Exception: # pylint: disable=broad-except
return self.async_abort(reason="unknown")
name = get_name(device)
self._discovered_devices[address] = Discovery(name, discovery_info, device)
if not self._discovered_devices:
return self.async_abort(reason="no_devices_found")
titles = {
address: get_name(discovery.device)
for (address, discovery) in self._discovered_devices.items()
}
return self.async_show_form(
step_id="user",
data_schema=vol.Schema(
{
vol.Required(CONF_ADDRESS): vol.In(titles),
},
),
)

View File

@ -0,0 +1,9 @@
"""Constants for Airthings BLE."""
DOMAIN = "airthings_ble"
MFCT_ID = 820
VOLUME_BECQUEREL = "Bq/m³"
VOLUME_PICOCURIE = "pCi/L"
DEFAULT_SCAN_INTERVAL = 300

View File

@ -0,0 +1,15 @@
{
"domain": "airthings_ble",
"name": "Airthings BLE",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/airthings_ble",
"requirements": ["airthings-ble==0.5.2"],
"dependencies": ["bluetooth"],
"codeowners": ["@vincegio"],
"iot_class": "local_polling",
"bluetooth": [
{
"manufacturer_id": 820
}
]
}

View File

@ -0,0 +1,186 @@
"""Support for airthings ble sensors."""
from __future__ import annotations
import logging
from airthings_ble import AirthingsDevice
from homeassistant import config_entries
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_PARTS_PER_BILLION,
CONCENTRATION_PARTS_PER_MILLION,
LIGHT_LUX,
PERCENTAGE,
PRESSURE_MBAR,
TEMP_CELSIUS,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import CONNECTION_BLUETOOTH
from homeassistant.helpers.entity import DeviceInfo, EntityCategory
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator,
)
from homeassistant.util.unit_system import METRIC_SYSTEM
from .const import DOMAIN, VOLUME_BECQUEREL, VOLUME_PICOCURIE
_LOGGER = logging.getLogger(__name__)
SENSORS_MAPPING_TEMPLATE: dict[str, SensorEntityDescription] = {
"radon_1day_avg": SensorEntityDescription(
key="radon_1day_avg",
native_unit_of_measurement=VOLUME_BECQUEREL,
name="Radon 1-day average",
state_class=SensorStateClass.MEASUREMENT,
icon="mdi:radioactive",
),
"radon_longterm_avg": SensorEntityDescription(
key="radon_longterm_avg",
native_unit_of_measurement=VOLUME_BECQUEREL,
name="Radon longterm average",
state_class=SensorStateClass.MEASUREMENT,
icon="mdi:radioactive",
),
"radon_1day_level": SensorEntityDescription(
key="radon_1day_level",
name="Radon 1-day level",
icon="mdi:radioactive",
),
"radon_longterm_level": SensorEntityDescription(
key="radon_longterm_level",
name="Radon longterm level",
icon="mdi:radioactive",
),
"temperature": SensorEntityDescription(
key="temperature",
device_class=SensorDeviceClass.TEMPERATURE,
native_unit_of_measurement=TEMP_CELSIUS,
name="Temperature",
),
"humidity": SensorEntityDescription(
key="humidity",
device_class=SensorDeviceClass.HUMIDITY,
native_unit_of_measurement=PERCENTAGE,
name="Humidity",
),
"pressure": SensorEntityDescription(
key="pressure",
device_class=SensorDeviceClass.PRESSURE,
native_unit_of_measurement=PRESSURE_MBAR,
name="Pressure",
),
"battery": SensorEntityDescription(
key="battery",
device_class=SensorDeviceClass.BATTERY,
native_unit_of_measurement=PERCENTAGE,
entity_category=EntityCategory.DIAGNOSTIC,
name="Battery",
),
"co2": SensorEntityDescription(
key="co2",
device_class=SensorDeviceClass.CO2,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
name="co2",
),
"voc": SensorEntityDescription(
key="voc",
device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION,
name="VOC",
icon="mdi:cloud",
),
"illuminance": SensorEntityDescription(
key="illuminance",
device_class=SensorDeviceClass.ILLUMINANCE,
native_unit_of_measurement=LIGHT_LUX,
name="Illuminance",
),
}
async def async_setup_entry(
hass: HomeAssistant,
entry: config_entries.ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the Airthings BLE sensors."""
is_metric = hass.config.units is METRIC_SYSTEM
coordinator: DataUpdateCoordinator[AirthingsDevice] = hass.data[DOMAIN][
entry.entry_id
]
# we need to change some units
sensors_mapping = SENSORS_MAPPING_TEMPLATE.copy()
if not is_metric:
for val in sensors_mapping.values():
if val.native_unit_of_measurement is not VOLUME_BECQUEREL:
continue
val.native_unit_of_measurement = VOLUME_PICOCURIE
entities = []
_LOGGER.debug("got sensors: %s", coordinator.data.sensors)
for sensor_type, sensor_value in coordinator.data.sensors.items():
if sensor_type not in sensors_mapping:
_LOGGER.debug(
"Unknown sensor type detected: %s, %s",
sensor_type,
sensor_value,
)
continue
entities.append(
AirthingsSensor(coordinator, coordinator.data, sensors_mapping[sensor_type])
)
async_add_entities(entities)
class AirthingsSensor(
CoordinatorEntity[DataUpdateCoordinator[AirthingsDevice]], SensorEntity
):
"""Airthings BLE sensors for the device."""
_attr_state_class = SensorStateClass.MEASUREMENT
_attr_has_entity_name = True
def __init__(
self,
coordinator: DataUpdateCoordinator,
airthings_device: AirthingsDevice,
entity_description: SensorEntityDescription,
) -> None:
"""Populate the airthings entity with relevant data."""
super().__init__(coordinator)
self.entity_description = entity_description
name = f"{airthings_device.name} {airthings_device.identifier}"
self._attr_unique_id = f"{name}_{entity_description.key}"
self._id = airthings_device.address
self._attr_device_info = DeviceInfo(
connections={
(
CONNECTION_BLUETOOTH,
airthings_device.address,
)
},
name=name,
manufacturer="Airthings",
hw_version=airthings_device.hw_version,
sw_version=airthings_device.sw_version,
)
@property
def native_value(self) -> StateType:
"""Return the value reported by the sensor."""
return self.coordinator.data.sensors[self.entity_description.key]

View File

@ -0,0 +1,23 @@
{
"config": {
"flow_title": "[%key:component::bluetooth::config::flow_title%]",
"step": {
"user": {
"description": "[%key:component::bluetooth::config::step::user::description%]",
"data": {
"address": "[%key:component::bluetooth::config::step::user::data::address%]"
}
},
"bluetooth_confirm": {
"description": "[%key:component::bluetooth::config::step::bluetooth_confirm::description%]"
}
},
"abort": {
"no_devices_found": "[%key:common::config_flow::abort::no_devices_found%]",
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"unknown": "[%key:common::config_flow::error::unknown%]"
}
}
}

View File

@ -0,0 +1,22 @@
{
"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",
"cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435",
"no_devices_found": "\u041d\u044f\u043c\u0430 \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0432 \u043c\u0440\u0435\u0436\u0430\u0442\u0430",
"unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430"
},
"flow_title": "{name}",
"step": {
"bluetooth_confirm": {
"description": "\u0418\u0441\u043a\u0430\u0442\u0435 \u043b\u0438 \u0434\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u0435 {name}?"
},
"user": {
"data": {
"address": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e"
},
"description": "\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0437\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430"
}
}
}
}

View File

@ -0,0 +1,23 @@
{
"config": {
"abort": {
"already_configured": "El dispositiu ja est\u00e0 configurat",
"already_in_progress": "El flux de configuraci\u00f3 ja est\u00e0 en curs",
"cannot_connect": "Ha fallat la connexi\u00f3",
"no_devices_found": "No s'han trobat dispositius a la xarxa",
"unknown": "Error inesperat"
},
"flow_title": "{name}",
"step": {
"bluetooth_confirm": {
"description": "Vols configurar {name}?"
},
"user": {
"data": {
"address": "Dispositiu"
},
"description": "Tria un dispositiu a configurar"
}
}
}
}

View File

@ -0,0 +1,23 @@
{
"config": {
"abort": {
"already_configured": "Ger\u00e4t ist bereits konfiguriert",
"already_in_progress": "Der Konfigurationsablauf wird bereits ausgef\u00fchrt",
"cannot_connect": "Verbindung fehlgeschlagen",
"no_devices_found": "Keine Ger\u00e4te im Netzwerk gefunden",
"unknown": "Unerwarteter Fehler"
},
"flow_title": "{name}",
"step": {
"bluetooth_confirm": {
"description": "M\u00f6chtest du {name} einrichten?"
},
"user": {
"data": {
"address": "Ger\u00e4t"
},
"description": "W\u00e4hle ein Ger\u00e4t zum Einrichten aus"
}
}
}
}

View File

@ -0,0 +1,23 @@
{
"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",
"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",
"no_devices_found": "\u0394\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b1\u03bd \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 \u03c3\u03c4\u03bf \u03b4\u03af\u03ba\u03c4\u03c5\u03bf",
"unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1"
},
"flow_title": "{name}",
"step": {
"bluetooth_confirm": {
"description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf {name};"
},
"user": {
"data": {
"address": "\u03a3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae"
},
"description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03bc\u03b9\u03b1 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b3\u03b9\u03b1 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7"
}
}
}
}

View File

@ -0,0 +1,23 @@
{
"config": {
"abort": {
"already_configured": "Device is already configured",
"already_in_progress": "Configuration flow is already in progress",
"cannot_connect": "Failed to connect",
"no_devices_found": "No devices found on the network",
"unknown": "Unexpected error"
},
"flow_title": "{name}",
"step": {
"bluetooth_confirm": {
"description": "Do you want to setup {name}?"
},
"user": {
"data": {
"address": "Device"
},
"description": "Choose a device to setup"
}
}
}
}

View File

@ -0,0 +1,23 @@
{
"config": {
"abort": {
"already_configured": "El dispositivo ya est\u00e1 configurado",
"already_in_progress": "El flujo de configuraci\u00f3n ya est\u00e1 en curso",
"cannot_connect": "No se pudo conectar",
"no_devices_found": "No se encontraron dispositivos en la red",
"unknown": "Error inesperado"
},
"flow_title": "{name}",
"step": {
"bluetooth_confirm": {
"description": "\u00bfQuieres configurar {name}?"
},
"user": {
"data": {
"address": "Dispositivo"
},
"description": "Elige un dispositivo para configurar"
}
}
}
}

View File

@ -0,0 +1,23 @@
{
"config": {
"abort": {
"already_configured": "Seade on juba h\u00e4\u00e4lestatud",
"already_in_progress": "Seadistamine on juba k\u00e4imas",
"cannot_connect": "\u00dchendamine nurjus",
"no_devices_found": "V\u00f5rgust seadmeid ei leitud",
"unknown": "Ootamatu t\u00f5rge"
},
"flow_title": "{name}",
"step": {
"bluetooth_confirm": {
"description": "Kas seadistada {name}?"
},
"user": {
"data": {
"address": "Seade"
},
"description": "Vali h\u00e4\u00e4lestatav seade"
}
}
}
}

View File

@ -0,0 +1,23 @@
{
"config": {
"abort": {
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9",
"already_in_progress": "La configuration est d\u00e9j\u00e0 en cours",
"cannot_connect": "\u00c9chec de connexion",
"no_devices_found": "Aucun appareil trouv\u00e9 sur le r\u00e9seau",
"unknown": "Erreur inattendue"
},
"flow_title": "{name}",
"step": {
"bluetooth_confirm": {
"description": "Voulez-vous configurer {name}\u00a0?"
},
"user": {
"data": {
"address": "Appareil"
},
"description": "S\u00e9lectionnez l'appareil \u00e0 configurer"
}
}
}
}

View File

@ -0,0 +1,23 @@
{
"config": {
"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",
"cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4",
"no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea",
"unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4"
},
"flow_title": "{name}",
"step": {
"bluetooth_confirm": {
"description": "\u05d4\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05d2\u05d3\u05d9\u05e8 \u05d0\u05ea {name}?"
},
"user": {
"data": {
"address": "\u05d4\u05ea\u05e7\u05df"
},
"description": "\u05d1\u05d7\u05d9\u05e8\u05ea \u05d4\u05ea\u05e7\u05df \u05dc\u05d4\u05d2\u05d3\u05e8\u05d4"
}
}
}
}

View File

@ -0,0 +1,23 @@
{
"config": {
"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",
"cannot_connect": "Sikertelen csatlakoz\u00e1s",
"no_devices_found": "Nem tal\u00e1lhat\u00f3 eszk\u00f6z a h\u00e1l\u00f3zaton",
"unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt"
},
"flow_title": "{name}",
"step": {
"bluetooth_confirm": {
"description": "Szeretn\u00e9 be\u00e1ll\u00edtani: {name}?"
},
"user": {
"data": {
"address": "Eszk\u00f6z"
},
"description": "V\u00e1lasszon egy be\u00e1ll\u00edtand\u00f3 eszk\u00f6zt"
}
}
}
}

View File

@ -0,0 +1,23 @@
{
"config": {
"abort": {
"already_configured": "Perangkat sudah dikonfigurasi",
"already_in_progress": "Alur konfigurasi sedang berlangsung",
"cannot_connect": "Gagal terhubung",
"no_devices_found": "Tidak ada perangkat yang ditemukan di jaringan",
"unknown": "Kesalahan yang tidak diharapkan"
},
"flow_title": "{name}",
"step": {
"bluetooth_confirm": {
"description": "Ingin menyiapkan {name}?"
},
"user": {
"data": {
"address": "Perangkat"
},
"description": "Pilih perangkat untuk disiapkan"
}
}
}
}

View File

@ -0,0 +1,23 @@
{
"config": {
"abort": {
"already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato",
"already_in_progress": "Il flusso di configurazione \u00e8 gi\u00e0 in corso",
"cannot_connect": "Impossibile connettersi",
"no_devices_found": "Nessun dispositivo trovato sulla rete",
"unknown": "Errore imprevisto"
},
"flow_title": "{name}",
"step": {
"bluetooth_confirm": {
"description": "Vuoi configurare {name}?"
},
"user": {
"data": {
"address": "Dispositivo"
},
"description": "Seleziona un dispositivo da configurare"
}
}
}
}

View File

@ -0,0 +1,23 @@
{
"config": {
"abort": {
"already_configured": "\u30c7\u30d0\u30a4\u30b9\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",
"no_devices_found": "\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u4e0a\u306b\u30c7\u30d0\u30a4\u30b9\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093",
"unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc"
},
"flow_title": "{name}",
"step": {
"bluetooth_confirm": {
"description": "{name} \u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u304b\uff1f"
},
"user": {
"data": {
"address": "\u30c7\u30d0\u30a4\u30b9"
},
"description": "\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3059\u308b\u30c7\u30d0\u30a4\u30b9\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044"
}
}
}
}

View File

@ -0,0 +1,9 @@
{
"config": {
"abort": {
"already_configured": "Enheten er allerede konfigurert",
"cannot_connect": "Tilkobling mislyktes",
"unknown": "Uventet feil"
}
}
}

View File

@ -0,0 +1,23 @@
{
"config": {
"abort": {
"already_configured": "Nederlands",
"already_in_progress": "Nederlands",
"cannot_connect": "Nederlands",
"no_devices_found": "Nederlands",
"unknown": "Nederlands"
},
"flow_title": "Nederlands",
"step": {
"bluetooth_confirm": {
"description": "Nederlands"
},
"user": {
"data": {
"address": "Nederlands"
},
"description": "Nederlands"
}
}
}
}

View File

@ -0,0 +1,23 @@
{
"config": {
"abort": {
"already_configured": "Enheten er allerede konfigurert",
"already_in_progress": "Konfigurasjonsflyten p\u00e5g\u00e5r allerede",
"cannot_connect": "Tilkobling mislyktes",
"no_devices_found": "Ingen enheter funnet p\u00e5 nettverket",
"unknown": "Uventet feil"
},
"flow_title": "{name}",
"step": {
"bluetooth_confirm": {
"description": "Vil du konfigurere {name}?"
},
"user": {
"data": {
"address": "Enhet"
},
"description": "Velg en enhet du vil konfigurere"
}
}
}
}

View File

@ -0,0 +1,23 @@
{
"config": {
"abort": {
"already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane",
"already_in_progress": "Konfiguracja jest ju\u017c w toku",
"cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia",
"no_devices_found": "Nie znaleziono urz\u0105dze\u0144 w sieci",
"unknown": "Nieoczekiwany b\u0142\u0105d"
},
"flow_title": "{name}",
"step": {
"bluetooth_confirm": {
"description": "Czy chcesz skonfigurowa\u0107 {name}?"
},
"user": {
"data": {
"address": "Urz\u0105dzenie"
},
"description": "Wybierz urz\u0105dzenie do skonfigurowania"
}
}
}
}

View File

@ -0,0 +1,23 @@
{
"config": {
"abort": {
"already_configured": "Dispositivo j\u00e1 est\u00e1 configurado",
"already_in_progress": "O fluxo de configura\u00e7\u00e3o j\u00e1 est\u00e1 em andamento",
"cannot_connect": "Falha ao conectar",
"no_devices_found": "Nenhum dispositivo encontrado na rede",
"unknown": "Erro inesperado"
},
"flow_title": "{name}",
"step": {
"bluetooth_confirm": {
"description": "Deseja configurar {name}?"
},
"user": {
"data": {
"address": "Dispositivo"
},
"description": "Escolha um dispositivo para configurar"
}
}
}
}

View File

@ -0,0 +1,23 @@
{
"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.",
"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.",
"no_devices_found": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b \u0432 \u0441\u0435\u0442\u0438.",
"unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430."
},
"flow_title": "{name}",
"step": {
"bluetooth_confirm": {
"description": "\u0425\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c {name}?"
},
"user": {
"data": {
"address": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e"
},
"description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0434\u043b\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438"
}
}
}
}

View File

@ -0,0 +1,23 @@
{
"config": {
"abort": {
"already_configured": "Enheten \u00e4r redan konfigurerad",
"already_in_progress": "Konfigurationsfl\u00f6det p\u00e5g\u00e5r redan",
"cannot_connect": "Det gick inte att ansluta.",
"no_devices_found": "Inga enheter hittades i n\u00e4tverket",
"unknown": "Ov\u00e4ntat fel"
},
"flow_title": "{name}",
"step": {
"bluetooth_confirm": {
"description": "Vill du konfigurera {name}?"
},
"user": {
"data": {
"address": "Enhet"
},
"description": "V\u00e4lj en enhet att konfigurera"
}
}
}
}

View File

@ -0,0 +1,23 @@
{
"config": {
"abort": {
"already_configured": "Cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f",
"already_in_progress": "Yap\u0131land\u0131rma ak\u0131\u015f\u0131 zaten devam ediyor",
"cannot_connect": "Ba\u011flanma hatas\u0131",
"no_devices_found": "A\u011fda cihaz bulunamad\u0131",
"unknown": "Beklenmeyen hata"
},
"flow_title": "{name}",
"step": {
"bluetooth_confirm": {
"description": "{name} kurulumunu yapmak istiyor musunuz?"
},
"user": {
"data": {
"address": "Cihaz"
},
"description": "Kurulum i\u00e7in bir cihaz se\u00e7in"
}
}
}
}

View File

@ -0,0 +1,22 @@
{
"config": {
"abort": {
"already_configured": "\u8bbe\u5907\u5df2\u7ecf\u914d\u7f6e",
"already_in_progress": "\u914d\u7f6e\u6d41\u7a0b\u5df2\u5728\u8fdb\u884c\u4e2d",
"cannot_connect": "\u65e0\u6cd5\u8fde\u63a5",
"no_devices_found": "\u5728\u6b64\u7f51\u7edc\u4e0a\u672a\u627e\u5230\u8bbe\u5907",
"unknown": "\u672a\u77e5\u9519\u8bef"
},
"step": {
"bluetooth_confirm": {
"description": "\u60a8\u60f3\u8bbe\u7f6e\u7684\u8bbe\u5907\u662f\u5426\u662f\uff1a {name}?"
},
"user": {
"data": {
"address": "\u8bbe\u5907"
},
"description": "\u9009\u62e9\u4e00\u4e2a\u8bbe\u5907\u4ee5\u914d\u7f6e"
}
}
}
}

View File

@ -0,0 +1,23 @@
{
"config": {
"abort": {
"already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210",
"already_in_progress": "\u8a2d\u5b9a\u5df2\u7d93\u9032\u884c\u4e2d",
"cannot_connect": "\u9023\u7dda\u5931\u6557",
"no_devices_found": "\u7db2\u8def\u4e0a\u627e\u4e0d\u5230\u88dd\u7f6e",
"unknown": "\u672a\u9810\u671f\u932f\u8aa4"
},
"flow_title": "{name}",
"step": {
"bluetooth_confirm": {
"description": "\u662f\u5426\u8981\u8a2d\u5b9a {name}\uff1f"
},
"user": {
"data": {
"address": "\u88dd\u7f6e"
},
"description": "\u9078\u64c7\u6240\u8981\u8a2d\u5b9a\u7684\u88dd\u7f6e"
}
}
}
}

View File

@ -5,13 +5,20 @@ from typing import Any
from homeassistant.components.diagnostics import async_redact_data from homeassistant.components.diagnostics import async_redact_data
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_STATE from homeassistant.const import (
CONF_API_KEY,
CONF_LATITUDE,
CONF_LONGITUDE,
CONF_STATE,
CONF_UNIQUE_ID,
)
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from .const import CONF_CITY, CONF_COUNTRY, DOMAIN from .const import CONF_CITY, CONF_COUNTRY, DOMAIN
CONF_COORDINATES = "coordinates" CONF_COORDINATES = "coordinates"
CONF_TITLE = "title"
TO_REDACT = { TO_REDACT = {
CONF_API_KEY, CONF_API_KEY,
@ -21,6 +28,9 @@ TO_REDACT = {
CONF_LATITUDE, CONF_LATITUDE,
CONF_LONGITUDE, CONF_LONGITUDE,
CONF_STATE, CONF_STATE,
# Config entry title and unique ID may contain sensitive data:
CONF_TITLE,
CONF_UNIQUE_ID,
} }
@ -31,10 +41,6 @@ async def async_get_config_entry_diagnostics(
coordinator: DataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] coordinator: DataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
return { return {
"entry": { "entry": async_redact_data(entry.as_dict(), TO_REDACT),
"title": entry.title,
"data": async_redact_data(entry.data, TO_REDACT),
"options": async_redact_data(entry.options, TO_REDACT),
},
"data": async_redact_data(coordinator.data["data"], TO_REDACT), "data": async_redact_data(coordinator.data["data"], TO_REDACT),
} }

View File

@ -6,5 +6,6 @@
"requirements": ["pyairvisual==2022.07.0"], "requirements": ["pyairvisual==2022.07.0"],
"codeowners": ["@bachya"], "codeowners": ["@bachya"],
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"loggers": ["pyairvisual", "pysmb"] "loggers": ["pyairvisual", "pysmb"],
"integration_type": "device"
} }

View File

@ -0,0 +1,7 @@
{
"config": {
"error": {
"general_error": "Uventet feil"
}
}
}

View File

@ -2,7 +2,7 @@
"config": { "config": {
"abort": { "abort": {
"already_configured": "Plasseringen er allerede konfigurert eller Node / Pro ID er allerede registrert.", "already_configured": "Plasseringen er allerede konfigurert eller Node / Pro ID er allerede registrert.",
"reauth_successful": "Godkjenning p\u00e5 nytt var vellykket" "reauth_successful": "Re-autentisering var vellykket"
}, },
"error": { "error": {
"cannot_connect": "Tilkobling mislyktes", "cannot_connect": "Tilkobling mislyktes",

View File

@ -6,7 +6,8 @@
"airvisual__pollutant_level": { "airvisual__pollutant_level": {
"good": "\u05d8\u05d5\u05d1", "good": "\u05d8\u05d5\u05d1",
"unhealthy": "\u05dc\u05d0 \u05d1\u05e8\u05d9\u05d0", "unhealthy": "\u05dc\u05d0 \u05d1\u05e8\u05d9\u05d0",
"unhealthy_sensitive": "\u05dc\u05d0 \u05d1\u05e8\u05d9\u05d0 \u05dc\u05e7\u05d1\u05d5\u05e6\u05d5\u05ea \u05e8\u05d2\u05d9\u05e9\u05d5\u05ea" "unhealthy_sensitive": "\u05dc\u05d0 \u05d1\u05e8\u05d9\u05d0 \u05dc\u05e7\u05d1\u05d5\u05e6\u05d5\u05ea \u05e8\u05d2\u05d9\u05e9\u05d5\u05ea",
"very_unhealthy": "\u05de\u05d0\u05d5\u05d3 \u05dc\u05d0 \u05d1\u05e8\u05d9\u05d0"
} }
} }
} }

View File

@ -2,7 +2,7 @@
"config": { "config": {
"abort": { "abort": {
"already_configured": "Enheten er allerede konfigurert", "already_configured": "Enheten er allerede konfigurert",
"reauth_successful": "Godkjenning p\u00e5 nytt var vellykket" "reauth_successful": "Re-autentisering var vellykket"
}, },
"error": { "error": {
"cannot_connect": "Tilkobling mislyktes", "cannot_connect": "Tilkobling mislyktes",

View File

@ -1,4 +1,18 @@
{ {
"device_automation": {
"condition_type": {
"is_armed_away": "{entity_name} er \u00e1 ver\u00f0i \u00fati",
"is_armed_home": "{entity_name} er \u00e1 ver\u00f0i heima",
"is_armed_night": "{entity_name} er \u00e1 ver\u00f0i n\u00f3tt",
"is_disarmed": "{entity_name} er ekki \u00e1 ver\u00f0i"
},
"trigger_type": {
"armed_away": "{entity_name} \u00e1 ver\u00f0i \u00fati",
"armed_home": "\u00e1 ver\u00f0i heima",
"armed_night": "\u00e1 ver\u00f0i n\u00f3tt",
"disarmed": "ekki \u00e1 ver\u00f0i"
}
},
"state": { "state": {
"_": { "_": {
"armed": "\u00c1 ver\u00f0i", "armed": "\u00c1 ver\u00f0i",
@ -6,7 +20,7 @@
"armed_home": "\u00c1 ver\u00f0i heima", "armed_home": "\u00c1 ver\u00f0i heima",
"armed_night": "\u00c1 ver\u00f0i n\u00f3tt", "armed_night": "\u00c1 ver\u00f0i n\u00f3tt",
"arming": "Set \u00e1 v\u00f6r\u00f0", "arming": "Set \u00e1 v\u00f6r\u00f0",
"disarmed": "ekki \u00e1 ver\u00f0i", "disarmed": "Ekki \u00e1 ver\u00f0i",
"disarming": "tek af ver\u00f0i", "disarming": "tek af ver\u00f0i",
"pending": "B\u00ed\u00f0ur", "pending": "B\u00ed\u00f0ur",
"triggered": "R\u00e6st" "triggered": "R\u00e6st"

View File

@ -3,8 +3,7 @@ from __future__ import annotations
from collections.abc import Callable from collections.abc import Callable
from datetime import timedelta from datetime import timedelta
import logging from typing import Any
from typing import Any, final
import voluptuous as vol import voluptuous as vol
@ -15,7 +14,6 @@ from homeassistant.components.notify import (
DOMAIN as DOMAIN_NOTIFY, DOMAIN as DOMAIN_NOTIFY,
) )
from homeassistant.const import ( from homeassistant.const import (
ATTR_ENTITY_ID,
CONF_ENTITY_ID, CONF_ENTITY_ID,
CONF_NAME, CONF_NAME,
CONF_REPEAT, CONF_REPEAT,
@ -27,10 +25,10 @@ from homeassistant.const import (
STATE_OFF, STATE_OFF,
STATE_ON, STATE_ON,
) )
from homeassistant.core import Event, HomeAssistant, ServiceCall from homeassistant.core import Event, HomeAssistant
from homeassistant.helpers import service
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import ToggleEntity from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.event import ( from homeassistant.helpers.event import (
async_track_point_in_time, async_track_point_in_time,
async_track_state_change_event, async_track_state_change_event,
@ -39,39 +37,40 @@ from homeassistant.helpers.template import Template
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
from homeassistant.util.dt import now from homeassistant.util.dt import now
_LOGGER = logging.getLogger(__name__) from .const import (
CONF_ALERT_MESSAGE,
DOMAIN = "alert" CONF_CAN_ACK,
CONF_DATA,
CONF_CAN_ACK = "can_acknowledge" CONF_DONE_MESSAGE,
CONF_NOTIFIERS = "notifiers" CONF_NOTIFIERS,
CONF_SKIP_FIRST = "skip_first" CONF_SKIP_FIRST,
CONF_ALERT_MESSAGE = "message" CONF_TITLE,
CONF_DONE_MESSAGE = "done_message" DEFAULT_CAN_ACK,
CONF_TITLE = "title" DEFAULT_SKIP_FIRST,
CONF_DATA = "data" DOMAIN,
LOGGER,
DEFAULT_CAN_ACK = True )
DEFAULT_SKIP_FIRST = False
ALERT_SCHEMA = vol.Schema( ALERT_SCHEMA = vol.Schema(
{ {
vol.Required(CONF_NAME): cv.string, vol.Required(CONF_NAME): cv.string,
vol.Required(CONF_ENTITY_ID): cv.entity_id, vol.Required(CONF_ENTITY_ID): cv.entity_id,
vol.Required(CONF_STATE, default=STATE_ON): cv.string, vol.Optional(CONF_STATE, default=STATE_ON): cv.string,
vol.Required(CONF_REPEAT): vol.All( vol.Required(CONF_REPEAT): vol.All(
cv.ensure_list, cv.ensure_list,
[vol.Coerce(float)], [vol.Coerce(float)],
# Minimum delay is 1 second = 0.016 minutes # Minimum delay is 1 second = 0.016 minutes
[vol.Range(min=0.016)], [vol.Range(min=0.016)],
), ),
vol.Required(CONF_CAN_ACK, default=DEFAULT_CAN_ACK): cv.boolean, vol.Optional(CONF_CAN_ACK, default=DEFAULT_CAN_ACK): cv.boolean,
vol.Required(CONF_SKIP_FIRST, default=DEFAULT_SKIP_FIRST): cv.boolean, vol.Optional(CONF_SKIP_FIRST, default=DEFAULT_SKIP_FIRST): cv.boolean,
vol.Optional(CONF_ALERT_MESSAGE): cv.template, vol.Optional(CONF_ALERT_MESSAGE): cv.template,
vol.Optional(CONF_DONE_MESSAGE): cv.template, vol.Optional(CONF_DONE_MESSAGE): cv.template,
vol.Optional(CONF_TITLE): cv.template, vol.Optional(CONF_TITLE): cv.template,
vol.Optional(CONF_DATA): dict, vol.Optional(CONF_DATA): dict,
vol.Required(CONF_NOTIFIERS): vol.All(cv.ensure_list, [cv.string]), vol.Optional(CONF_NOTIFIERS, default=list): vol.All(
cv.ensure_list, [cv.string]
),
} }
) )
@ -79,16 +78,11 @@ CONFIG_SCHEMA = vol.Schema(
{DOMAIN: cv.schema_with_slug_keys(ALERT_SCHEMA)}, extra=vol.ALLOW_EXTRA {DOMAIN: cv.schema_with_slug_keys(ALERT_SCHEMA)}, extra=vol.ALLOW_EXTRA
) )
ALERT_SERVICE_SCHEMA = vol.Schema({vol.Required(ATTR_ENTITY_ID): cv.entity_ids})
def is_on(hass: HomeAssistant, entity_id: str) -> bool:
"""Return if the alert is firing and not acknowledged."""
return hass.states.is_state(entity_id, STATE_ON)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the Alert component.""" """Set up the Alert component."""
component = EntityComponent[Alert](LOGGER, DOMAIN, hass)
entities: list[Alert] = [] entities: list[Alert] = []
for object_id, cfg in config[DOMAIN].items(): for object_id, cfg in config[DOMAIN].items():
@ -128,50 +122,16 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
if not entities: if not entities:
return False return False
async def async_handle_alert_service(service_call: ServiceCall) -> None: component.async_register_entity_service(SERVICE_TURN_OFF, {}, "async_turn_off")
"""Handle calls to alert services.""" component.async_register_entity_service(SERVICE_TURN_ON, {}, "async_turn_on")
alert_ids = await service.async_extract_entity_ids(hass, service_call) component.async_register_entity_service(SERVICE_TOGGLE, {}, "async_toggle")
for alert_id in alert_ids: await component.async_add_entities(entities)
for alert in entities:
if alert.entity_id != alert_id:
continue
alert.async_set_context(service_call.context)
if service_call.service == SERVICE_TURN_ON:
await alert.async_turn_on()
elif service_call.service == SERVICE_TOGGLE:
await alert.async_toggle()
else:
await alert.async_turn_off()
# Setup service calls
hass.services.async_register(
DOMAIN,
SERVICE_TURN_OFF,
async_handle_alert_service,
schema=ALERT_SERVICE_SCHEMA,
)
hass.services.async_register(
DOMAIN,
SERVICE_TURN_ON,
async_handle_alert_service,
schema=ALERT_SERVICE_SCHEMA,
)
hass.services.async_register(
DOMAIN,
SERVICE_TOGGLE,
async_handle_alert_service,
schema=ALERT_SERVICE_SCHEMA,
)
for alert in entities:
alert.async_write_ha_state()
return True return True
class Alert(ToggleEntity): class Alert(Entity):
"""Representation of an alert.""" """Representation of an alert."""
_attr_should_poll = False _attr_should_poll = False
@ -227,10 +187,8 @@ class Alert(ToggleEntity):
hass, [watched_entity_id], self.watched_entity_change hass, [watched_entity_id], self.watched_entity_change
) )
@final # type: ignore[misc]
@property @property
# pylint: disable=overridden-final-method def state(self) -> str:
def state(self) -> str: # type: ignore[override]
"""Return the alert status.""" """Return the alert status."""
if self._firing: if self._firing:
if self._ack: if self._ack:
@ -242,7 +200,7 @@ class Alert(ToggleEntity):
"""Determine if the alert should start or stop.""" """Determine if the alert should start or stop."""
if (to_state := event.data.get("new_state")) is None: if (to_state := event.data.get("new_state")) is None:
return return
_LOGGER.debug("Watched entity (%s) has changed", event.data.get("entity_id")) LOGGER.debug("Watched entity (%s) has changed", event.data.get("entity_id"))
if to_state.state == self._alert_state and not self._firing: if to_state.state == self._alert_state and not self._firing:
await self.begin_alerting() await self.begin_alerting()
if to_state.state != self._alert_state and self._firing: if to_state.state != self._alert_state and self._firing:
@ -250,7 +208,7 @@ class Alert(ToggleEntity):
async def begin_alerting(self) -> None: async def begin_alerting(self) -> None:
"""Begin the alert procedures.""" """Begin the alert procedures."""
_LOGGER.debug("Beginning Alert: %s", self._attr_name) LOGGER.debug("Beginning Alert: %s", self._attr_name)
self._ack = False self._ack = False
self._firing = True self._firing = True
self._next_delay = 0 self._next_delay = 0
@ -264,7 +222,7 @@ class Alert(ToggleEntity):
async def end_alerting(self) -> None: async def end_alerting(self) -> None:
"""End the alert procedures.""" """End the alert procedures."""
_LOGGER.debug("Ending Alert: %s", self._attr_name) LOGGER.debug("Ending Alert: %s", self._attr_name)
if self._cancel is not None: if self._cancel is not None:
self._cancel() self._cancel()
self._cancel = None self._cancel = None
@ -288,7 +246,7 @@ class Alert(ToggleEntity):
return return
if not self._ack: if not self._ack:
_LOGGER.info("Alerting: %s", self._attr_name) LOGGER.info("Alerting: %s", self._attr_name)
self._send_done_message = True self._send_done_message = True
if self._message_template is not None: if self._message_template is not None:
@ -301,7 +259,7 @@ class Alert(ToggleEntity):
async def _notify_done_message(self) -> None: async def _notify_done_message(self) -> None:
"""Send notification of complete alert.""" """Send notification of complete alert."""
_LOGGER.info("Alerting: %s", self._done_message_template) LOGGER.info("Alerting: %s", self._done_message_template)
self._send_done_message = False self._send_done_message = False
if self._done_message_template is None: if self._done_message_template is None:
@ -313,6 +271,9 @@ class Alert(ToggleEntity):
async def _send_notification_message(self, message: Any) -> None: async def _send_notification_message(self, message: Any) -> None:
if not self._notifiers:
return
msg_payload = {ATTR_MESSAGE: message} msg_payload = {ATTR_MESSAGE: message}
if self._title_template is not None: if self._title_template is not None:
@ -321,7 +282,7 @@ class Alert(ToggleEntity):
if self._data: if self._data:
msg_payload[ATTR_DATA] = self._data msg_payload[ATTR_DATA] = self._data
_LOGGER.debug(msg_payload) LOGGER.debug(msg_payload)
for target in self._notifiers: for target in self._notifiers:
await self.hass.services.async_call( await self.hass.services.async_call(
@ -330,13 +291,13 @@ class Alert(ToggleEntity):
async def async_turn_on(self, **kwargs: Any) -> None: async def async_turn_on(self, **kwargs: Any) -> None:
"""Async Unacknowledge alert.""" """Async Unacknowledge alert."""
_LOGGER.debug("Reset Alert: %s", self._attr_name) LOGGER.debug("Reset Alert: %s", self._attr_name)
self._ack = False self._ack = False
self.async_write_ha_state() self.async_write_ha_state()
async def async_turn_off(self, **kwargs: Any) -> None: async def async_turn_off(self, **kwargs: Any) -> None:
"""Async Acknowledge alert.""" """Async Acknowledge alert."""
_LOGGER.debug("Acknowledged Alert: %s", self._attr_name) LOGGER.debug("Acknowledged Alert: %s", self._attr_name)
self._ack = True self._ack = True
self.async_write_ha_state() self.async_write_ha_state()

View File

@ -0,0 +1,19 @@
"""Constants for the Alert integration."""
import logging
from typing import Final
DOMAIN: Final = "alert"
LOGGER = logging.getLogger(__package__)
CONF_CAN_ACK = "can_acknowledge"
CONF_NOTIFIERS = "notifiers"
CONF_SKIP_FIRST = "skip_first"
CONF_ALERT_MESSAGE = "message"
CONF_DONE_MESSAGE = "done_message"
CONF_TITLE = "title"
CONF_DATA = "data"
DEFAULT_CAN_ACK = True
DEFAULT_SKIP_FIRST = False

View File

@ -3,7 +3,7 @@
"name": "Alert", "name": "Alert",
"documentation": "https://www.home-assistant.io/integrations/alert", "documentation": "https://www.home-assistant.io/integrations/alert",
"after_dependencies": ["notify"], "after_dependencies": ["notify"],
"codeowners": ["@home-assistant/core"], "codeowners": ["@home-assistant/core", "@frenck"],
"quality_scale": "internal", "quality_scale": "internal",
"iot_class": "local_push" "iot_class": "local_push"
} }

View File

@ -3,7 +3,6 @@ from __future__ import annotations
import asyncio import asyncio
from collections.abc import Iterable from collections.abc import Iterable
import logging
from typing import Any from typing import Any
from homeassistant.const import ( from homeassistant.const import (
@ -15,9 +14,7 @@ from homeassistant.const import (
) )
from homeassistant.core import Context, HomeAssistant, State from homeassistant.core import Context, HomeAssistant, State
from . import DOMAIN from .const import DOMAIN, LOGGER
_LOGGER = logging.getLogger(__name__)
VALID_STATES = {STATE_ON, STATE_OFF} VALID_STATES = {STATE_ON, STATE_OFF}
@ -31,11 +28,11 @@ async def _async_reproduce_state(
) -> None: ) -> None:
"""Reproduce a single state.""" """Reproduce a single state."""
if (cur_state := hass.states.get(state.entity_id)) is None: if (cur_state := hass.states.get(state.entity_id)) is None:
_LOGGER.warning("Unable to find entity %s", state.entity_id) LOGGER.warning("Unable to find entity %s", state.entity_id)
return return
if state.state not in VALID_STATES: if state.state not in VALID_STATES:
_LOGGER.warning( LOGGER.warning(
"Invalid state specified for %s: %s", state.entity_id, state.state "Invalid state specified for %s: %s", state.entity_id, state.state
) )
return return

View File

@ -20,7 +20,7 @@ from homeassistant.components.sensor import (
SensorStateClass, SensorStateClass,
) )
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CURRENCY_DOLLAR, ENERGY_KILO_WATT_HOUR from homeassistant.const import CURRENCY_DOLLAR, ENERGY_KILO_WATT_HOUR, PERCENTAGE
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.helpers.update_coordinator import CoordinatorEntity
@ -247,7 +247,7 @@ async def async_setup_entry(
renewables_description = SensorEntityDescription( renewables_description = SensorEntityDescription(
key="renewables", key="renewables",
name=f"{entry.title} - Renewables", name=f"{entry.title} - Renewables",
native_unit_of_measurement="%", native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
icon="mdi:solar-power", icon="mdi:solar-power",
) )

View File

@ -0,0 +1,7 @@
{
"config": {
"error": {
"unknown_error": "Uventet feil"
}
}
}

View File

@ -2,6 +2,7 @@
"config": { "config": {
"error": { "error": {
"invalid_api_token": "Ongeldige API-sleutel", "invalid_api_token": "Ongeldige API-sleutel",
"no_site": "Geen site opgegeven",
"unknown_error": "Onverwachte fout" "unknown_error": "Onverwachte fout"
}, },
"step": { "step": {

View File

@ -1,5 +1,10 @@
{ {
"config": { "config": {
"error": {
"invalid_api_token": "Ogiltig API-nyckel",
"no_site": "Ingen plats har tillhandah\u00e5llits.",
"unknown_error": "Ov\u00e4ntat fel"
},
"step": { "step": {
"site": { "site": {
"data": { "data": {

View File

@ -5,7 +5,7 @@ from typing import Any
from homeassistant.components.diagnostics import async_redact_data from homeassistant.components.diagnostics import async_redact_data
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_API_KEY, CONF_LOCATION from homeassistant.const import CONF_API_KEY, CONF_LOCATION, CONF_UNIQUE_ID
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from . import AmbientStation from . import AmbientStation
@ -16,6 +16,7 @@ CONF_APP_KEY_CAMEL = "appKey"
CONF_DEVICE_ID_CAMEL = "deviceId" CONF_DEVICE_ID_CAMEL = "deviceId"
CONF_MAC_ADDRESS = "mac_address" CONF_MAC_ADDRESS = "mac_address"
CONF_MAC_ADDRESS_CAMEL = "macAddress" CONF_MAC_ADDRESS_CAMEL = "macAddress"
CONF_TITLE = "title"
CONF_TZ = "tz" CONF_TZ = "tz"
TO_REDACT = { TO_REDACT = {
@ -28,6 +29,9 @@ TO_REDACT = {
CONF_MAC_ADDRESS, CONF_MAC_ADDRESS,
CONF_MAC_ADDRESS_CAMEL, CONF_MAC_ADDRESS_CAMEL,
CONF_TZ, CONF_TZ,
# Config entry title and unique ID may contain sensitive data:
CONF_TITLE,
CONF_UNIQUE_ID,
} }
@ -38,9 +42,6 @@ async def async_get_config_entry_diagnostics(
ambient: AmbientStation = hass.data[DOMAIN][entry.entry_id] ambient: AmbientStation = hass.data[DOMAIN][entry.entry_id]
return { return {
"entry": { "entry": async_redact_data(entry.as_dict(), TO_REDACT),
"title": entry.title,
"data": async_redact_data(entry.data, TO_REDACT),
},
"stations": async_redact_data(ambient.stations, TO_REDACT), "stations": async_redact_data(ambient.stations, TO_REDACT),
} }

View File

@ -6,5 +6,6 @@
"requirements": ["aioambient==2021.11.0"], "requirements": ["aioambient==2021.11.0"],
"codeowners": ["@bachya"], "codeowners": ["@bachya"],
"iot_class": "cloud_push", "iot_class": "cloud_push",
"loggers": ["aioambient"] "loggers": ["aioambient"],
"integration_type": "hub"
} }

View File

@ -19,10 +19,10 @@ from homeassistant.const import (
LIGHT_LUX, LIGHT_LUX,
PERCENTAGE, PERCENTAGE,
PRECIPITATION_INCHES, PRECIPITATION_INCHES,
PRECIPITATION_INCHES_PER_HOUR,
PRESSURE_INHG, PRESSURE_INHG,
SPEED_MILES_PER_HOUR, SPEED_MILES_PER_HOUR,
TEMP_FAHRENHEIT, TEMP_FAHRENHEIT,
UnitOfVolumetricFlux,
) )
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity import EntityDescription from homeassistant.helpers.entity import EntityDescription
@ -194,9 +194,9 @@ SENSOR_DESCRIPTIONS = (
SensorEntityDescription( SensorEntityDescription(
key=TYPE_HOURLYRAININ, key=TYPE_HOURLYRAININ,
name="Hourly rain rate", name="Hourly rain rate",
icon="mdi:water", native_unit_of_measurement=UnitOfVolumetricFlux.INCHES_PER_HOUR,
native_unit_of_measurement=PRECIPITATION_INCHES_PER_HOUR,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.PRECIPITATION_INTENSITY,
), ),
SensorEntityDescription( SensorEntityDescription(
key=TYPE_HUMIDITY10, key=TYPE_HUMIDITY10,

View File

@ -0,0 +1,6 @@
{
"domain": "amp_motorization",
"name": "AMP Motorization",
"integration_type": "virtual",
"supported_by": "motion_blinds"
}

View File

@ -1,4 +1,6 @@
"""Send instance and usage analytics.""" """Send instance and usage analytics."""
from typing import Any
import voluptuous as vol import voluptuous as vol
from homeassistant.components import websocket_api from homeassistant.components import websocket_api
@ -41,7 +43,7 @@ async def async_setup(hass: HomeAssistant, _: ConfigType) -> bool:
async def websocket_analytics( async def websocket_analytics(
hass: HomeAssistant, hass: HomeAssistant,
connection: websocket_api.connection.ActiveConnection, connection: websocket_api.connection.ActiveConnection,
msg: dict, msg: dict[str, Any],
) -> None: ) -> None:
"""Return analytics preferences.""" """Return analytics preferences."""
analytics: Analytics = hass.data[DOMAIN] analytics: Analytics = hass.data[DOMAIN]
@ -62,7 +64,7 @@ async def websocket_analytics(
async def websocket_analytics_preferences( async def websocket_analytics_preferences(
hass: HomeAssistant, hass: HomeAssistant,
connection: websocket_api.connection.ActiveConnection, connection: websocket_api.connection.ActiveConnection,
msg: dict, msg: dict[str, Any],
) -> None: ) -> None:
"""Update analytics preferences.""" """Update analytics preferences."""
preferences = msg[ATTR_PREFERENCES] preferences = msg[ATTR_PREFERENCES]

View File

@ -2,37 +2,20 @@
from __future__ import annotations from __future__ import annotations
from pydroid_ipcam import PyDroidIPCam from pydroid_ipcam import PyDroidIPCam
import voluptuous as vol
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ( from homeassistant.const import (
CONF_HOST, CONF_HOST,
CONF_NAME,
CONF_PASSWORD, CONF_PASSWORD,
CONF_PORT, CONF_PORT,
CONF_SCAN_INTERVAL,
CONF_SENSORS,
CONF_SWITCHES,
CONF_TIMEOUT,
CONF_USERNAME, CONF_USERNAME,
Platform, Platform,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
from homeassistant.helpers.typing import ConfigType
from .const import ( from .const import DOMAIN
CONF_MOTION_SENSOR,
DEFAULT_NAME,
DEFAULT_PORT,
DEFAULT_TIMEOUT,
DOMAIN,
SCAN_INTERVAL,
SENSORS,
SWITCHES,
)
from .coordinator import AndroidIPCamDataUpdateCoordinator from .coordinator import AndroidIPCamDataUpdateCoordinator
PLATFORMS: list[Platform] = [ PLATFORMS: list[Platform] = [
@ -43,66 +26,7 @@ PLATFORMS: list[Platform] = [
] ]
CONFIG_SCHEMA = vol.Schema( CONFIG_SCHEMA = cv.removed(DOMAIN, raise_if_present=False)
vol.All(
cv.deprecated(DOMAIN),
{
DOMAIN: vol.All(
cv.ensure_list,
[
vol.Schema(
{
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Required(CONF_HOST): cv.string,
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
vol.Optional(
CONF_TIMEOUT, default=DEFAULT_TIMEOUT
): cv.positive_int,
vol.Optional(
CONF_SCAN_INTERVAL, default=SCAN_INTERVAL
): cv.time_period,
vol.Inclusive(CONF_USERNAME, "authentication"): cv.string,
vol.Inclusive(CONF_PASSWORD, "authentication"): cv.string,
vol.Optional(CONF_SWITCHES): vol.All(
cv.ensure_list, [vol.In(SWITCHES)]
),
vol.Optional(CONF_SENSORS): vol.All(
cv.ensure_list, [vol.In(SENSORS)]
),
vol.Optional(CONF_MOTION_SENSOR): cv.boolean,
}
)
],
)
},
),
extra=vol.ALLOW_EXTRA,
)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the IP Webcam component."""
if DOMAIN not in config:
return True
async_create_issue(
hass,
DOMAIN,
"deprecated_yaml",
breaks_in_ha_version="2022.11.0",
is_fixable=False,
severity=IssueSeverity.WARNING,
translation_key="deprecated_yaml",
)
for entry in config[DOMAIN]:
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_IMPORT}, data=entry
)
)
return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:

View File

@ -8,15 +8,7 @@ from pydroid_ipcam.exceptions import PyDroidIPCamException, Unauthorized
import voluptuous as vol import voluptuous as vol
from homeassistant import config_entries from homeassistant import config_entries
from homeassistant.const import ( from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT, CONF_USERNAME
CONF_HOST,
CONF_NAME,
CONF_PASSWORD,
CONF_PORT,
CONF_SCAN_INTERVAL,
CONF_TIMEOUT,
CONF_USERNAME,
)
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResult from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers import config_validation as cv from homeassistant.helpers import config_validation as cv
@ -75,19 +67,11 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
self._async_abort_entries_match( self._async_abort_entries_match(
{CONF_HOST: user_input[CONF_HOST], CONF_PORT: user_input[CONF_PORT]} {CONF_HOST: user_input[CONF_HOST], CONF_PORT: user_input[CONF_PORT]}
) )
# to be removed when YAML import is removed
title = user_input.get(CONF_NAME) or user_input[CONF_HOST]
if not (errors := await validate_input(self.hass, user_input)): if not (errors := await validate_input(self.hass, user_input)):
return self.async_create_entry(title=title, data=user_input) return self.async_create_entry(title=user_input[CONF_HOST], data=user_input)
return self.async_show_form( return self.async_show_form(
step_id="user", step_id="user",
data_schema=STEP_USER_DATA_SCHEMA, data_schema=STEP_USER_DATA_SCHEMA,
errors=errors, errors=errors,
) )
async def async_step_import(self, import_config: dict[str, Any]) -> FlowResult:
"""Import a config entry from configuration.yaml."""
import_config.pop(CONF_SCAN_INTERVAL)
import_config.pop(CONF_TIMEOUT)
return await self.async_step_user(import_config)

View File

@ -4,38 +4,6 @@ from datetime import timedelta
from typing import Final from typing import Final
DOMAIN: Final = "android_ip_webcam" DOMAIN: Final = "android_ip_webcam"
DEFAULT_NAME: Final = "IP Webcam"
DEFAULT_PORT: Final = 8080 DEFAULT_PORT: Final = 8080
DEFAULT_TIMEOUT: Final = 10
CONF_MOTION_SENSOR: Final = "motion_sensor"
MOTION_ACTIVE: Final = "motion_active" MOTION_ACTIVE: Final = "motion_active"
SCAN_INTERVAL: Final = timedelta(seconds=10) SCAN_INTERVAL: Final = timedelta(seconds=10)
SWITCHES = [
"exposure_lock",
"ffc",
"focus",
"gps_active",
"motion_detect",
"night_vision",
"overlay",
"torch",
"whitebalance_lock",
"video_recording",
]
SENSORS = [
"audio_connections",
"battery_level",
"battery_temp",
"battery_voltage",
"light",
"motion",
"pressure",
"proximity",
"sound",
"video_connections",
]

View File

@ -0,0 +1,21 @@
{
"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",
"invalid_auth": "\u05d0\u05d9\u05de\u05d5\u05ea \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9"
},
"step": {
"user": {
"data": {
"host": "\u05de\u05d0\u05e8\u05d7",
"password": "\u05e1\u05d9\u05e1\u05de\u05d4",
"port": "\u05e4\u05ea\u05d7\u05d4",
"username": "\u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9"
}
}
}
}
}

View File

@ -20,8 +20,8 @@
}, },
"issues": { "issues": {
"deprecated_yaml": { "deprecated_yaml": {
"description": "Proses konfigurasi Android IP Webcam lewat YAML dalam proses penghapusan.\n\nKonfigurasi YAML yang ada telah diimpor ke antarmuka secara otomatis.\n\nHapus konfigurasi YAML Android IP Webcam dari file configuration.yaml dan mulai ulang Home Assistant untuk memperbaiki masalah ini.", "description": "Proses konfigurasi Integrasi Android IP Webcam lewat YAML dalam proses penghapusan.\n\nKonfigurasi YAML yang ada telah diimpor ke antarmuka secara otomatis.\n\nHapus konfigurasi YAML Integrasi Android IP Webcam dari file configuration.yaml dan mulai ulang Home Assistant untuk memperbaiki masalah ini.",
"title": "Konfigurasi YAML Android IP Webcam dalam proses penghapusan" "title": "Konfigurasi YAML Integrasi Android IP Webcam dalam proses penghapusan"
} }
} }
} }

View File

@ -4,7 +4,7 @@
"documentation": "https://www.home-assistant.io/integrations/androidtv", "documentation": "https://www.home-assistant.io/integrations/androidtv",
"requirements": [ "requirements": [
"adb-shell[async]==0.4.3", "adb-shell[async]==0.4.3",
"androidtv[async]==0.0.67", "androidtv[async]==0.0.69",
"pure-python-adb[async]==0.3.0.dev0" "pure-python-adb[async]==0.3.0.dev0"
], ],
"codeowners": ["@JeffLIrion", "@ollo69"], "codeowners": ["@JeffLIrion", "@ollo69"],

View File

@ -0,0 +1,7 @@
{
"config": {
"error": {
"unknown": "Uventet feil"
}
}
}

View File

@ -92,9 +92,3 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
return self.async_show_form( return self.async_show_form(
step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors
) )
async def async_step_import(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Import a config entry from configuration.yaml."""
return await self.async_step_user(user_input)

View File

@ -5,73 +5,24 @@ import logging
from anthemav.connection import Connection from anthemav.connection import Connection
from anthemav.protocol import AVR from anthemav.protocol import AVR
import voluptuous as vol
from homeassistant.components.media_player import ( from homeassistant.components.media_player import (
PLATFORM_SCHEMA,
MediaPlayerDeviceClass, MediaPlayerDeviceClass,
MediaPlayerEntity, MediaPlayerEntity,
MediaPlayerEntityFeature, MediaPlayerEntityFeature,
MediaPlayerState, MediaPlayerState,
) )
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_MAC, CONF_NAME, CONF_PORT from homeassistant.const import CONF_MAC, CONF_NAME
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback 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 .const import ( from .const import ANTHEMAV_UDATE_SIGNAL, CONF_MODEL, DOMAIN, MANUFACTURER
ANTHEMAV_UDATE_SIGNAL,
CONF_MODEL,
DEFAULT_NAME,
DEFAULT_PORT,
DOMAIN,
MANUFACTURER,
)
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
vol.Required(CONF_HOST): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
}
)
async def async_setup_platform(
hass: HomeAssistant,
config: ConfigType,
async_add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up our socket to the AVR."""
async_create_issue(
hass,
DOMAIN,
"deprecated_yaml",
breaks_in_ha_version="2022.10.0",
is_fixable=False,
severity=IssueSeverity.WARNING,
translation_key="deprecated_yaml",
)
_LOGGER.warning(
"Configuration of the Anthem A/V Receivers integration in YAML is "
"deprecated and will be removed in Home Assistant 2022.10; Your "
"existing configuration has been imported into the UI automatically "
"and can be safely removed from your configuration.yaml file"
)
await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_IMPORT},
data=config,
)
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,

View File

@ -15,11 +15,5 @@
"abort": { "abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]" "already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
} }
},
"issues": {
"deprecated_yaml": {
"title": "The Anthem A/V Receivers YAML configuration is being removed",
"description": "Configuring Anthem A/V Receivers using YAML is being removed.\n\nYour existing YAML configuration has been imported into the UI automatically.\n\nRemove the Anthem A/V Receivers YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue."
}
} }
} }

View File

@ -15,11 +15,5 @@
} }
} }
} }
},
"issues": {
"deprecated_yaml": {
"description": "Configuring Anthem A/V Receivers using YAML is being removed.\n\nYour existing YAML configuration has been imported into the UI automatically.\n\nRemove the Anthem A/V Receivers YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue.",
"title": "The Anthem A/V Receivers YAML configuration is being removed"
}
} }
} }

Some files were not shown because too many files have changed in this diff Show More