Merge pull request #50123 from home-assistant/rc

This commit is contained in:
Franck Nijhof 2021-05-05 19:00:33 +02:00 committed by GitHub
commit ccf92e4721
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3352 changed files with 63692 additions and 19952 deletions

View File

@ -145,7 +145,6 @@ omit =
homeassistant/components/clickatell/notify.py homeassistant/components/clickatell/notify.py
homeassistant/components/clicksend/notify.py homeassistant/components/clicksend/notify.py
homeassistant/components/clicksend_tts/notify.py homeassistant/components/clicksend_tts/notify.py
homeassistant/components/climacell/weather.py
homeassistant/components/cmus/media_player.py homeassistant/components/cmus/media_player.py
homeassistant/components/co2signal/* homeassistant/components/co2signal/*
homeassistant/components/coinbase/* homeassistant/components/coinbase/*
@ -174,6 +173,7 @@ omit =
homeassistant/components/deluge/sensor.py homeassistant/components/deluge/sensor.py
homeassistant/components/deluge/switch.py homeassistant/components/deluge/switch.py
homeassistant/components/denon/media_player.py homeassistant/components/denon/media_player.py
homeassistant/components/denonavr/__init__.py
homeassistant/components/denonavr/media_player.py homeassistant/components/denonavr/media_player.py
homeassistant/components/denonavr/receiver.py homeassistant/components/denonavr/receiver.py
homeassistant/components/deutsche_bahn/sensor.py homeassistant/components/deutsche_bahn/sensor.py
@ -237,6 +237,8 @@ omit =
homeassistant/components/emby/media_player.py homeassistant/components/emby/media_player.py
homeassistant/components/emoncms/sensor.py homeassistant/components/emoncms/sensor.py
homeassistant/components/emoncms_history/* homeassistant/components/emoncms_history/*
homeassistant/components/emonitor/__init__.py
homeassistant/components/emonitor/sensor.py
homeassistant/components/enigma2/media_player.py homeassistant/components/enigma2/media_player.py
homeassistant/components/enocean/__init__.py homeassistant/components/enocean/__init__.py
homeassistant/components/enocean/binary_sensor.py homeassistant/components/enocean/binary_sensor.py
@ -246,12 +248,14 @@ omit =
homeassistant/components/enocean/light.py homeassistant/components/enocean/light.py
homeassistant/components/enocean/sensor.py homeassistant/components/enocean/sensor.py
homeassistant/components/enocean/switch.py homeassistant/components/enocean/switch.py
homeassistant/components/enphase_envoy/__init__.py
homeassistant/components/enphase_envoy/sensor.py homeassistant/components/enphase_envoy/sensor.py
homeassistant/components/entur_public_transport/* homeassistant/components/entur_public_transport/*
homeassistant/components/environment_canada/* homeassistant/components/environment_canada/*
homeassistant/components/envirophat/sensor.py homeassistant/components/envirophat/sensor.py
homeassistant/components/envisalink/* homeassistant/components/envisalink/*
homeassistant/components/ephember/climate.py homeassistant/components/ephember/climate.py
homeassistant/components/epson/__init__.py
homeassistant/components/epson/const.py homeassistant/components/epson/const.py
homeassistant/components/epson/media_player.py homeassistant/components/epson/media_player.py
homeassistant/components/epsonworkforce/sensor.py homeassistant/components/epsonworkforce/sensor.py
@ -271,7 +275,13 @@ omit =
homeassistant/components/eufy/* homeassistant/components/eufy/*
homeassistant/components/everlights/light.py homeassistant/components/everlights/light.py
homeassistant/components/evohome/* homeassistant/components/evohome/*
homeassistant/components/ezviz/* homeassistant/components/ezviz/__init__.py
homeassistant/components/ezviz/camera.py
homeassistant/components/ezviz/coordinator.py
homeassistant/components/ezviz/const.py
homeassistant/components/ezviz/binary_sensor.py
homeassistant/components/ezviz/sensor.py
homeassistant/components/ezviz/switch.py
homeassistant/components/familyhub/camera.py homeassistant/components/familyhub/camera.py
homeassistant/components/faa_delays/__init__.py homeassistant/components/faa_delays/__init__.py
homeassistant/components/faa_delays/binary_sensor.py homeassistant/components/faa_delays/binary_sensor.py
@ -319,6 +329,9 @@ omit =
homeassistant/components/freebox/router.py homeassistant/components/freebox/router.py
homeassistant/components/freebox/sensor.py homeassistant/components/freebox/sensor.py
homeassistant/components/freebox/switch.py homeassistant/components/freebox/switch.py
homeassistant/components/fritz/__init__.py
homeassistant/components/fritz/common.py
homeassistant/components/fritz/const.py
homeassistant/components/fritz/device_tracker.py homeassistant/components/fritz/device_tracker.py
homeassistant/components/fritzbox_callmonitor/__init__.py homeassistant/components/fritzbox_callmonitor/__init__.py
homeassistant/components/fritzbox_callmonitor/const.py homeassistant/components/fritzbox_callmonitor/const.py
@ -335,7 +348,6 @@ omit =
homeassistant/components/garmin_connect/alarm_util.py homeassistant/components/garmin_connect/alarm_util.py
homeassistant/components/gc100/* homeassistant/components/gc100/*
homeassistant/components/geniushub/* homeassistant/components/geniushub/*
homeassistant/components/geizhals/sensor.py
homeassistant/components/github/sensor.py homeassistant/components/github/sensor.py
homeassistant/components/gitlab_ci/sensor.py homeassistant/components/gitlab_ci/sensor.py
homeassistant/components/gitter/sensor.py homeassistant/components/gitter/sensor.py
@ -349,6 +361,8 @@ omit =
homeassistant/components/google/* homeassistant/components/google/*
homeassistant/components/google_cloud/tts.py homeassistant/components/google_cloud/tts.py
homeassistant/components/google_maps/device_tracker.py homeassistant/components/google_maps/device_tracker.py
homeassistant/components/google_travel_time/__init__.py
homeassistant/components/google_travel_time/helpers.py
homeassistant/components/google_travel_time/sensor.py homeassistant/components/google_travel_time/sensor.py
homeassistant/components/gpmdp/media_player.py homeassistant/components/gpmdp/media_player.py
homeassistant/components/gpsd/sensor.py homeassistant/components/gpsd/sensor.py
@ -421,6 +435,7 @@ omit =
homeassistant/components/hvv_departures/sensor.py homeassistant/components/hvv_departures/sensor.py
homeassistant/components/hvv_departures/__init__.py homeassistant/components/hvv_departures/__init__.py
homeassistant/components/hydrawise/* homeassistant/components/hydrawise/*
homeassistant/components/ialarm/alarm_control_panel.py
homeassistant/components/iammeter/sensor.py homeassistant/components/iammeter/sensor.py
homeassistant/components/iaqualink/binary_sensor.py homeassistant/components/iaqualink/binary_sensor.py
homeassistant/components/iaqualink/climate.py homeassistant/components/iaqualink/climate.py
@ -502,6 +517,10 @@ omit =
homeassistant/components/kodi/media_player.py homeassistant/components/kodi/media_player.py
homeassistant/components/kodi/notify.py homeassistant/components/kodi/notify.py
homeassistant/components/konnected/* homeassistant/components/konnected/*
homeassistant/components/kostal_plenticore/__init__.py
homeassistant/components/kostal_plenticore/const.py
homeassistant/components/kostal_plenticore/helper.py
homeassistant/components/kostal_plenticore/sensor.py
homeassistant/components/kwb/sensor.py homeassistant/components/kwb/sensor.py
homeassistant/components/lacrosse/sensor.py homeassistant/components/lacrosse/sensor.py
homeassistant/components/lametric/* homeassistant/components/lametric/*
@ -573,6 +592,8 @@ omit =
homeassistant/components/melcloud/water_heater.py homeassistant/components/melcloud/water_heater.py
homeassistant/components/message_bird/notify.py homeassistant/components/message_bird/notify.py
homeassistant/components/met/weather.py homeassistant/components/met/weather.py
homeassistant/components/met_eireann/__init__.py
homeassistant/components/met_eireann/weather.py
homeassistant/components/meteo_france/__init__.py homeassistant/components/meteo_france/__init__.py
homeassistant/components/meteo_france/const.py homeassistant/components/meteo_france/const.py
homeassistant/components/meteo_france/sensor.py homeassistant/components/meteo_france/sensor.py
@ -600,7 +621,6 @@ omit =
homeassistant/components/modbus/cover.py homeassistant/components/modbus/cover.py
homeassistant/components/modbus/modbus.py homeassistant/components/modbus/modbus.py
homeassistant/components/modbus/switch.py homeassistant/components/modbus/switch.py
homeassistant/components/modbus/sensor.py
homeassistant/components/modem_callerid/sensor.py homeassistant/components/modem_callerid/sensor.py
homeassistant/components/motion_blinds/__init__.py homeassistant/components/motion_blinds/__init__.py
homeassistant/components/motion_blinds/const.py homeassistant/components/motion_blinds/const.py
@ -612,6 +632,8 @@ omit =
homeassistant/components/msteams/notify.py homeassistant/components/msteams/notify.py
homeassistant/components/mullvad/__init__.py homeassistant/components/mullvad/__init__.py
homeassistant/components/mullvad/binary_sensor.py homeassistant/components/mullvad/binary_sensor.py
homeassistant/components/mutesync/__init__.py
homeassistant/components/mutesync/binary_sensor.py
homeassistant/components/nest/const.py homeassistant/components/nest/const.py
homeassistant/components/mvglive/sensor.py homeassistant/components/mvglive/sensor.py
homeassistant/components/mychevy/* homeassistant/components/mychevy/*
@ -667,6 +689,7 @@ omit =
homeassistant/components/nsw_fuel_station/sensor.py homeassistant/components/nsw_fuel_station/sensor.py
homeassistant/components/nuki/__init__.py homeassistant/components/nuki/__init__.py
homeassistant/components/nuki/const.py homeassistant/components/nuki/const.py
homeassistant/components/nuki/binary_sensor.py
homeassistant/components/nuki/lock.py homeassistant/components/nuki/lock.py
homeassistant/components/nut/sensor.py homeassistant/components/nut/sensor.py
homeassistant/components/nx584/alarm_control_panel.py homeassistant/components/nx584/alarm_control_panel.py
@ -759,6 +782,7 @@ omit =
homeassistant/components/poolsense/__init__.py homeassistant/components/poolsense/__init__.py
homeassistant/components/poolsense/sensor.py homeassistant/components/poolsense/sensor.py
homeassistant/components/poolsense/binary_sensor.py homeassistant/components/poolsense/binary_sensor.py
homeassistant/components/powerwall/__init__.py
homeassistant/components/proliphix/climate.py homeassistant/components/proliphix/climate.py
homeassistant/components/progettihwsw/__init__.py homeassistant/components/progettihwsw/__init__.py
homeassistant/components/progettihwsw/binary_sensor.py homeassistant/components/progettihwsw/binary_sensor.py
@ -807,9 +831,13 @@ omit =
homeassistant/components/rest/switch.py homeassistant/components/rest/switch.py
homeassistant/components/ring/camera.py homeassistant/components/ring/camera.py
homeassistant/components/ripple/sensor.py homeassistant/components/ripple/sensor.py
homeassistant/components/rituals_perfume_genie/binary_sensor.py
homeassistant/components/rituals_perfume_genie/entity.py
homeassistant/components/rituals_perfume_genie/sensor.py
homeassistant/components/rituals_perfume_genie/switch.py homeassistant/components/rituals_perfume_genie/switch.py
homeassistant/components/rituals_perfume_genie/__init__.py homeassistant/components/rituals_perfume_genie/__init__.py
homeassistant/components/rocketchat/notify.py homeassistant/components/rocketchat/notify.py
homeassistant/components/roomba/__init__.py
homeassistant/components/roomba/binary_sensor.py homeassistant/components/roomba/binary_sensor.py
homeassistant/components/roomba/braava.py homeassistant/components/roomba/braava.py
homeassistant/components/roomba/irobot_base.py homeassistant/components/roomba/irobot_base.py
@ -841,6 +869,7 @@ omit =
homeassistant/components/screenlogic/binary_sensor.py homeassistant/components/screenlogic/binary_sensor.py
homeassistant/components/screenlogic/climate.py homeassistant/components/screenlogic/climate.py
homeassistant/components/screenlogic/sensor.py homeassistant/components/screenlogic/sensor.py
homeassistant/components/screenlogic/services.py
homeassistant/components/screenlogic/switch.py homeassistant/components/screenlogic/switch.py
homeassistant/components/scsgate/* homeassistant/components/scsgate/*
homeassistant/components/scsgate/cover.py homeassistant/components/scsgate/cover.py
@ -878,6 +907,7 @@ omit =
homeassistant/components/slack/notify.py homeassistant/components/slack/notify.py
homeassistant/components/sinch/* homeassistant/components/sinch/*
homeassistant/components/slide/* homeassistant/components/slide/*
homeassistant/components/sma/__init__.py
homeassistant/components/sma/sensor.py homeassistant/components/sma/sensor.py
homeassistant/components/smappee/__init__.py homeassistant/components/smappee/__init__.py
homeassistant/components/smappee/api.py homeassistant/components/smappee/api.py
@ -893,7 +923,6 @@ omit =
homeassistant/components/snapcast/* homeassistant/components/snapcast/*
homeassistant/components/snmp/* homeassistant/components/snmp/*
homeassistant/components/sochain/sensor.py homeassistant/components/sochain/sensor.py
homeassistant/components/socialblade/sensor.py
homeassistant/components/solaredge/__init__.py homeassistant/components/solaredge/__init__.py
homeassistant/components/solaredge/sensor.py homeassistant/components/solaredge/sensor.py
homeassistant/components/solaredge_local/sensor.py homeassistant/components/solaredge_local/sensor.py
@ -1097,6 +1126,8 @@ omit =
homeassistant/components/waterfurnace/* homeassistant/components/waterfurnace/*
homeassistant/components/watson_iot/* homeassistant/components/watson_iot/*
homeassistant/components/watson_tts/tts.py homeassistant/components/watson_tts/tts.py
homeassistant/components/waze_travel_time/__init__.py
homeassistant/components/waze_travel_time/helpers.py
homeassistant/components/waze_travel_time/sensor.py homeassistant/components/waze_travel_time/sensor.py
homeassistant/components/webostv/* homeassistant/components/webostv/*
homeassistant/components/whois/sensor.py homeassistant/components/whois/sensor.py

View File

@ -1,7 +1,5 @@
name: Report an issue with Home Assistant Core name: Report an issue with Home Assistant Core
description: Report an issue with Home Assistant Core. description: Report an issue with Home Assistant Core.
title: ""
issue_body: true
body: body:
- type: markdown - type: markdown
attributes: attributes:
@ -85,13 +83,10 @@ body:
label: Anything in the logs that might be useful for us? label: Anything in the logs that might be useful for us?
description: For example, error message, or stack traces. description: For example, error message, or stack traces.
render: txt render: txt
- type: markdown - type: textarea
attributes: attributes:
value: | label: Additional information
## Additional information description: >
- type: markdown
attributes:
value: >
If you have any additional information for us, use the field below. If you have any additional information for us, use the field below.
Please note, you can attach screenshots or screen recordings here, by Please note, you can attach screenshots or screen recordings here, by
dragging and dropping files in the field below. dragging and dropping files in the field below.

311
.github/workflows/builder.yml vendored Normal file
View File

@ -0,0 +1,311 @@
name: Build images
# yamllint disable-line rule:truthy
on:
workflow_dispatch:
release:
types: ["published"]
schedule:
- cron: "0 2 * * *"
env:
BUILD_TYPE: core
DEFAULT_PYTHON: 3.8
jobs:
init:
name: Initialize build
runs-on: ubuntu-latest
outputs:
architectures: ${{ steps.info.outputs.architectures }}
version: ${{ steps.version.outputs.version }}
channel: ${{ steps.version.outputs.channel }}
publish: ${{ steps.version.outputs.publish }}
steps:
- name: Checkout the repository
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.2.2
with:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Get information
id: info
uses: home-assistant/actions/helpers/info@master
- name: Get version
id: version
uses: home-assistant/actions/helpers/version@master
with:
type: ${{ env.BUILD_TYPE }}
- name: Verify version
uses: home-assistant/actions/helpers/verify-version@master
with:
ignore-dev: true
build_python:
name: Build PyPi package
needs: init
runs-on: ubuntu-latest
if: needs.init.outputs.publish == 'true'
steps:
- name: Checkout the repository
uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.2.2
with:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Build package
shell: bash
run: |
pip install twine wheel
python setup.py sdist bdist_wheel
- name: Upload package
shell: bash
run: |
export TWINE_USERNAME="__token__"
export TWINE_PASSWORD="${{ secrets.TWINE_TOKEN }}"
twine upload dist/* --skip-existing
build_base:
name: Build ${{ matrix.arch }} base core image
needs: init
runs-on: ubuntu-latest
strategy:
matrix:
arch: ${{ fromJson(needs.init.outputs.architectures) }}
steps:
- name: Checkout the repository
uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
if: needs.init.outputs.channel == 'dev'
uses: actions/setup-python@v2.2.2
with:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Adjust nightly version
if: needs.init.outputs.channel == 'dev'
shell: bash
run: |
python3 -m pip install packaging
python3 -m pip install .
python3 script/version_bump.py nightly
version="$(python setup.py -V)"
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build base image
uses: home-assistant/builder@2021.04.2
with:
args: |
$BUILD_ARGS \
--${{ matrix.arch }} \
--target /data \
--with-codenotary "${{ secrets.VCN_USER }}" "${{ secrets.VCN_PASSWORD }}" "${{ secrets.VCN_ORG }}" \
--validate-from "${{ secrets.VCN_ORG }}" \
--generic ${{ needs.init.outputs.version }}
build_machine:
name: Build ${{ matrix.machine }} machine core image
needs: ["init", "build_base"]
runs-on: ubuntu-latest
strategy:
matrix:
machine:
- generic-x86-64
- intel-nuc
- odroid-c2
- odroid-c4
- odroid-n2
- odroid-xu
- qemuarm
- qemuarm-64
- qemux86
- qemux86-64
- raspberrypi
- raspberrypi2
- raspberrypi3
- raspberrypi3-64
- raspberrypi4
- raspberrypi4-64
- tinker
steps:
- name: Checkout the repository
uses: actions/checkout@v2
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build base image
uses: home-assistant/builder@2021.04.2
with:
args: |
$BUILD_ARGS \
--target /data/machine \
--with-codenotary "${{ secrets.VCN_USER }}" "${{ secrets.VCN_PASSWORD }}" "${{ secrets.VCN_ORG }}" \
--validate-from "${{ secrets.VCN_ORG }}" \
--machine "${{ needs.init.outputs.version }}=${{ matrix.machine }}"
publish_ha:
name: Publish version files
needs: ["init", "build_machine"]
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@v2
- name: Initialize git
uses: home-assistant/actions/helpers/git-init@master
with:
name: ${{ secrets.GIT_NAME }}
email: ${{ secrets.GIT_EMAIL }}
token: ${{ secrets.GIT_TOKEN }}
- name: Update version file
uses: home-assistant/actions/helpers/version-push@master
with:
key: "homeassistant[]"
key-description: "Home Assistant Core"
version: ${{ needs.init.outputs.version }}
channel: ${{ needs.init.outputs.channel }}
- name: Update version file (stable -> beta)
if: needs.init.outputs.channel == 'stable'
uses: home-assistant/actions/helpers/version-push@master
with:
key: "homeassistant[]"
key-description: "Home Assistant Core"
version: ${{ needs.init.outputs.version }}
channel: beta
publish_container:
name: Publish meta container
needs: ["init", "build_base"]
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@v2
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build Meta Image
shell: bash
run: |
bash <(curl https://getvcn.codenotary.com -L)
export DOCKER_CLI_EXPERIMENTAL=enabled
function create_manifest() {
local docker_reg=${1}
local tag_l=${2}
local tag_r=${3}
docker manifest create "${docker_reg}/home-assistant:${tag_l}" \
"${docker_reg}/amd64-homeassistant:${tag_r}" \
"${docker_reg}/i386-homeassistant:${tag_r}" \
"${docker_reg}/armhf-homeassistant:${tag_r}" \
"${docker_reg}/armv7-homeassistant:${tag_r}" \
"${docker_reg}/aarch64-homeassistant:${tag_r}"
docker manifest annotate "${docker_reg}/home-assistant:${tag_l}" \
"${docker_reg}/amd64-homeassistant:${tag_r}" \
--os linux --arch amd64
docker manifest annotate "${docker_reg}/home-assistant:${tag_l}" \
"${docker_reg}/i386-homeassistant:${tag_r}" \
--os linux --arch 386
docker manifest annotate "${docker_reg}/home-assistant:${tag_l}" \
"${docker_reg}/armhf-homeassistant:${tag_r}" \
--os linux --arch arm --variant=v6
docker manifest annotate "${docker_reg}/home-assistant:${tag_l}" \
"${docker_reg}/armv7-homeassistant:${tag_r}" \
--os linux --arch arm --variant=v7
docker manifest annotate "${docker_reg}/home-assistant:${tag_l}" \
"${docker_reg}/aarch64-homeassistant:${tag_r}" \
--os linux --arch arm64 --variant=v8
docker manifest push --purge "${docker_reg}/home-assistant:${tag_l}"
}
function validate_image() {
local image=${1}
state="$(vcn authenticate --org home-assistant.io --output json docker://${image} | jq '.verification.status // 2')"
if [[ "${state}" != "0" ]]; then
echo "Invalid signature!"
exit 1
fi
}
for docker_reg in "homeassistant" "ghcr.io/home-assistant"; do
docker pull "${docker_reg}/amd64-homeassistant:${{ needs.init.outputs.version }}"
docker pull "${docker_reg}/i386-homeassistant:${{ needs.init.outputs.version }}"
docker pull "${docker_reg}/armhf-homeassistant:${{ needs.init.outputs.version }}"
docker pull "${docker_reg}/armv7-homeassistant:${{ needs.init.outputs.version }}"
docker pull "${docker_reg}/aarch64-homeassistant:${{ needs.init.outputs.version }}"
validate_image "${docker_reg}/amd64-homeassistant:${{ needs.init.outputs.version }}"
validate_image "${docker_reg}/i386-homeassistant:${{ needs.init.outputs.version }}"
validate_image "${docker_reg}/armhf-homeassistant:${{ needs.init.outputs.version }}"
validate_image "${docker_reg}/armv7-homeassistant:${{ needs.init.outputs.version }}"
validate_image "${docker_reg}/aarch64-homeassistant:${{ needs.init.outputs.version }}"
# Create version tag
create_manifest "${docker_reg}" "${{ needs.init.outputs.version }}" "${{ needs.init.outputs.version }}"
# Create general tags
if [[ "${{ needs.init.outputs.version }}" =~ d ]]; then
create_manifest "${docker_reg}" "dev" "${{ needs.init.outputs.version }}"
elif [[ "${{ needs.init.outputs.version }}" =~ b ]]; then
create_manifest "${docker_reg}" "beta" "${{ needs.init.outputs.version }}"
create_manifest "${docker_reg}" "rc" "${{ needs.init.outputs.version }}"
else
create_manifest "${docker_reg}" "stable" "${{ needs.init.outputs.version }}"
create_manifest "${docker_reg}" "latest" "${{ needs.init.outputs.version }}"
create_manifest "${docker_reg}" "beta" "${{ needs.init.outputs.version }}"
create_manifest "${docker_reg}" "rc" "${{ needs.init.outputs.version }}"
fi
done

View File

@ -13,6 +13,7 @@ env:
CACHE_VERSION: 1 CACHE_VERSION: 1
DEFAULT_PYTHON: 3.8 DEFAULT_PYTHON: 3.8
PRE_COMMIT_CACHE: ~/.cache/pre-commit PRE_COMMIT_CACHE: ~/.cache/pre-commit
SQLALCHEMY_WARN_20: 1
jobs: jobs:
# Separate job to pre-populate the base dependency cache # Separate job to pre-populate the base dependency cache
@ -28,7 +29,7 @@ jobs:
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }} - name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python id: python
uses: actions/setup-python@v2.2.1 uses: actions/setup-python@v2.2.2
with: with:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
- name: Generate partial Python venv restore key - name: Generate partial Python venv restore key
@ -40,7 +41,7 @@ jobs:
hashFiles('homeassistant/package_constraints.txt') }}" hashFiles('homeassistant/package_constraints.txt') }}"
- name: Restore base Python virtual environment - name: Restore base Python virtual environment
id: cache-venv id: cache-venv
uses: actions/cache@v2.1.4 uses: actions/cache@v2.1.5
with: with:
path: venv path: venv
key: >- key: >-
@ -64,7 +65,7 @@ jobs:
hashFiles('.pre-commit-config.yaml') }}" hashFiles('.pre-commit-config.yaml') }}"
- name: Restore pre-commit environment from cache - name: Restore pre-commit environment from cache
id: cache-precommit id: cache-precommit
uses: actions/cache@v2.1.4 uses: actions/cache@v2.1.5
with: with:
path: ${{ env.PRE_COMMIT_CACHE }} path: ${{ env.PRE_COMMIT_CACHE }}
key: >- key: >-
@ -85,13 +86,13 @@ jobs:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }} - name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.2.1 uses: actions/setup-python@v2.2.2
id: python id: python
with: with:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore base Python virtual environment - name: Restore base Python virtual environment
id: cache-venv id: cache-venv
uses: actions/cache@v2.1.4 uses: actions/cache@v2.1.5
with: with:
path: venv path: venv
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{ key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
@ -103,7 +104,7 @@ 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@v2.1.4 uses: actions/cache@v2.1.5
with: with:
path: ${{ env.PRE_COMMIT_CACHE }} path: ${{ env.PRE_COMMIT_CACHE }}
key: ${{ runner.os }}-${{ needs.prepare-base.outputs.pre-commit-key }} key: ${{ runner.os }}-${{ needs.prepare-base.outputs.pre-commit-key }}
@ -125,13 +126,13 @@ jobs:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }} - name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.2.1 uses: actions/setup-python@v2.2.2
id: python id: python
with: with:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore base Python virtual environment - name: Restore base Python virtual environment
id: cache-venv id: cache-venv
uses: actions/cache@v2.1.4 uses: actions/cache@v2.1.5
with: with:
path: venv path: venv
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{ key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
@ -143,7 +144,7 @@ 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@v2.1.4 uses: actions/cache@v2.1.5
with: with:
path: ${{ env.PRE_COMMIT_CACHE }} path: ${{ env.PRE_COMMIT_CACHE }}
key: ${{ runner.os }}-${{ needs.prepare-base.outputs.pre-commit-key }} key: ${{ runner.os }}-${{ needs.prepare-base.outputs.pre-commit-key }}
@ -165,13 +166,13 @@ jobs:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }} - name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.2.1 uses: actions/setup-python@v2.2.2
id: python id: python
with: with:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore base Python virtual environment - name: Restore base Python virtual environment
id: cache-venv id: cache-venv
uses: actions/cache@v2.1.4 uses: actions/cache@v2.1.5
with: with:
path: venv path: venv
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{ key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
@ -183,7 +184,7 @@ 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@v2.1.4 uses: actions/cache@v2.1.5
with: with:
path: ${{ env.PRE_COMMIT_CACHE }} path: ${{ env.PRE_COMMIT_CACHE }}
key: ${{ runner.os }}-${{ needs.prepare-base.outputs.pre-commit-key }} key: ${{ runner.os }}-${{ needs.prepare-base.outputs.pre-commit-key }}
@ -227,13 +228,13 @@ jobs:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }} - name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.2.1 uses: actions/setup-python@v2.2.2
id: python id: python
with: with:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore base Python virtual environment - name: Restore base Python virtual environment
id: cache-venv id: cache-venv
uses: actions/cache@v2.1.4 uses: actions/cache@v2.1.5
with: with:
path: venv path: venv
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{ key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
@ -245,7 +246,7 @@ 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@v2.1.4 uses: actions/cache@v2.1.5
with: with:
path: ${{ env.PRE_COMMIT_CACHE }} path: ${{ env.PRE_COMMIT_CACHE }}
key: ${{ runner.os }}-${{ needs.prepare-base.outputs.pre-commit-key }} key: ${{ runner.os }}-${{ needs.prepare-base.outputs.pre-commit-key }}
@ -270,13 +271,13 @@ jobs:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }} - name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.2.1 uses: actions/setup-python@v2.2.2
id: python id: python
with: with:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore base Python virtual environment - name: Restore base Python virtual environment
id: cache-venv id: cache-venv
uses: actions/cache@v2.1.4 uses: actions/cache@v2.1.5
with: with:
path: venv path: venv
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{ key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
@ -288,7 +289,7 @@ 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@v2.1.4 uses: actions/cache@v2.1.5
with: with:
path: ${{ env.PRE_COMMIT_CACHE }} path: ${{ env.PRE_COMMIT_CACHE }}
key: ${{ runner.os }}-${{ needs.prepare-base.outputs.pre-commit-key }} key: ${{ runner.os }}-${{ needs.prepare-base.outputs.pre-commit-key }}
@ -313,13 +314,13 @@ jobs:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }} - name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.2.1 uses: actions/setup-python@v2.2.2
id: python id: python
with: with:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore base Python virtual environment - name: Restore base Python virtual environment
id: cache-venv id: cache-venv
uses: actions/cache@v2.1.4 uses: actions/cache@v2.1.5
with: with:
path: venv path: venv
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{ key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
@ -331,7 +332,7 @@ 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@v2.1.4 uses: actions/cache@v2.1.5
with: with:
path: ${{ env.PRE_COMMIT_CACHE }} path: ${{ env.PRE_COMMIT_CACHE }}
key: ${{ runner.os }}-${{ needs.prepare-base.outputs.pre-commit-key }} key: ${{ runner.os }}-${{ needs.prepare-base.outputs.pre-commit-key }}
@ -353,13 +354,13 @@ jobs:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }} - name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.2.1 uses: actions/setup-python@v2.2.2
id: python id: python
with: with:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore base Python virtual environment - name: Restore base Python virtual environment
id: cache-venv id: cache-venv
uses: actions/cache@v2.1.4 uses: actions/cache@v2.1.5
with: with:
path: venv path: venv
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{ key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
@ -371,7 +372,7 @@ 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@v2.1.4 uses: actions/cache@v2.1.5
with: with:
path: ${{ env.PRE_COMMIT_CACHE }} path: ${{ env.PRE_COMMIT_CACHE }}
key: ${{ runner.os }}-${{ needs.prepare-base.outputs.pre-commit-key }} key: ${{ runner.os }}-${{ needs.prepare-base.outputs.pre-commit-key }}
@ -396,13 +397,13 @@ jobs:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }} - name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.2.1 uses: actions/setup-python@v2.2.2
id: python id: python
with: with:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore base Python virtual environment - name: Restore base Python virtual environment
id: cache-venv id: cache-venv
uses: actions/cache@v2.1.4 uses: actions/cache@v2.1.5
with: with:
path: venv path: venv
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{ key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
@ -414,7 +415,7 @@ 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@v2.1.4 uses: actions/cache@v2.1.5
with: with:
path: ${{ env.PRE_COMMIT_CACHE }} path: ${{ env.PRE_COMMIT_CACHE }}
key: ${{ runner.os }}-${{ needs.prepare-base.outputs.pre-commit-key }} key: ${{ runner.os }}-${{ needs.prepare-base.outputs.pre-commit-key }}
@ -447,13 +448,13 @@ jobs:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }} - name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.2.1 uses: actions/setup-python@v2.2.2
id: python id: python
with: with:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore base Python virtual environment - name: Restore base Python virtual environment
id: cache-venv id: cache-venv
uses: actions/cache@v2.1.4 uses: actions/cache@v2.1.5
with: with:
path: venv path: venv
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{ key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
@ -465,7 +466,7 @@ 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@v2.1.4 uses: actions/cache@v2.1.5
with: with:
path: ${{ env.PRE_COMMIT_CACHE }} path: ${{ env.PRE_COMMIT_CACHE }}
key: ${{ runner.os }}-${{ needs.prepare-base.outputs.pre-commit-key }} key: ${{ runner.os }}-${{ needs.prepare-base.outputs.pre-commit-key }}
@ -495,7 +496,7 @@ jobs:
uses: actions/checkout@v2 uses: actions/checkout@v2
- 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@v2.1.4 uses: actions/cache@v2.1.5
with: with:
path: venv path: venv
key: ${{ runner.os }}-${{ matrix.python-version }}-${{ key: ${{ runner.os }}-${{ matrix.python-version }}-${{
@ -518,13 +519,13 @@ jobs:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Set up Python ${{ env.DEFAULT_PYTHON }} - name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.2.1 uses: actions/setup-python@v2.2.2
id: python id: python
with: with:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore base Python virtual environment - name: Restore base Python virtual environment
id: cache-venv id: cache-venv
uses: actions/cache@v2.1.4 uses: actions/cache@v2.1.5
with: with:
path: venv path: venv
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{ key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
@ -560,7 +561,7 @@ jobs:
hashFiles('homeassistant/package_constraints.txt') }}" hashFiles('homeassistant/package_constraints.txt') }}"
- 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@v2.1.4 uses: actions/cache@v2.1.5
with: with:
path: venv path: venv
key: >- key: >-
@ -597,7 +598,7 @@ jobs:
uses: actions/checkout@v2 uses: actions/checkout@v2
- 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@v2.1.4 uses: actions/cache@v2.1.5
with: with:
path: venv path: venv
key: ${{ runner.os }}-${{ matrix.python-version }}-${{ key: ${{ runner.os }}-${{ matrix.python-version }}-${{
@ -628,7 +629,7 @@ jobs:
uses: actions/checkout@v2 uses: actions/checkout@v2
- 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@v2.1.4 uses: actions/cache@v2.1.5
with: with:
path: venv path: venv
key: ${{ runner.os }}-${{ matrix.python-version }}-${{ key: ${{ runner.os }}-${{ matrix.python-version }}-${{
@ -662,7 +663,7 @@ jobs:
uses: actions/checkout@v2 uses: actions/checkout@v2
- 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@v2.1.4 uses: actions/cache@v2.1.5
with: with:
path: venv path: venv
key: ${{ runner.os }}-${{ matrix.python-version }}-${{ key: ${{ runner.os }}-${{ matrix.python-version }}-${{
@ -699,7 +700,7 @@ jobs:
-p no:sugar \ -p no:sugar \
tests tests
- name: Upload coverage artifact - name: Upload coverage artifact
uses: actions/upload-artifact@v2.2.2 uses: actions/upload-artifact@v2.2.3
with: with:
name: coverage-${{ matrix.python-version }}-group${{ matrix.group }} name: coverage-${{ matrix.python-version }}-group${{ matrix.group }}
path: .coverage path: .coverage
@ -720,7 +721,7 @@ jobs:
uses: actions/checkout@v2 uses: actions/checkout@v2
- 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@v2.1.4 uses: actions/cache@v2.1.5
with: with:
path: venv path: venv
key: ${{ runner.os }}-${{ matrix.python-version }}-${{ key: ${{ runner.os }}-${{ matrix.python-version }}-${{
@ -739,4 +740,4 @@ jobs:
coverage report --fail-under=94 coverage report --fail-under=94
coverage xml coverage xml
- name: Upload coverage to Codecov - name: Upload coverage to Codecov
uses: codecov/codecov-action@v1.3.1 uses: codecov/codecov-action@v1.4.1

View File

@ -1,11 +1,11 @@
repos: repos:
- repo: https://github.com/asottile/pyupgrade - repo: https://github.com/asottile/pyupgrade
rev: v2.11.0 rev: v2.12.0
hooks: hooks:
- id: pyupgrade - id: pyupgrade
args: [--py38-plus] args: [--py38-plus]
- repo: https://github.com/psf/black - repo: https://github.com/psf/black
rev: 20.8b1 rev: 21.4b0
hooks: hooks:
- id: black - id: black
args: args:
@ -23,7 +23,7 @@ repos:
exclude_types: [csv, json] exclude_types: [csv, json]
exclude: ^tests/fixtures/ exclude: ^tests/fixtures/
- repo: https://gitlab.com/pycqa/flake8 - repo: https://gitlab.com/pycqa/flake8
rev: 3.9.0 rev: 3.9.1
hooks: hooks:
- id: flake8 - id: flake8
additional_dependencies: additional_dependencies:
@ -33,6 +33,7 @@ repos:
- pydocstyle==6.0.0 - pydocstyle==6.0.0
- flake8-comprehensions==3.4.0 - flake8-comprehensions==3.4.0
- flake8-noqa==1.1.0 - flake8-noqa==1.1.0
- mccabe==0.6.1
files: ^(homeassistant|script|tests)/.+\.py$ files: ^(homeassistant|script|tests)/.+\.py$
- repo: https://github.com/PyCQA/bandit - repo: https://github.com/PyCQA/bandit
rev: 1.7.0 rev: 1.7.0
@ -44,7 +45,7 @@ repos:
- --configfile=tests/bandit.yaml - --configfile=tests/bandit.yaml
files: ^(homeassistant|script|tests)/.+\.py$ files: ^(homeassistant|script|tests)/.+\.py$
- repo: https://github.com/PyCQA/isort - repo: https://github.com/PyCQA/isort
rev: 5.7.0 rev: 5.8.0
hooks: hooks:
- id: isort - id: isort
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
@ -69,7 +70,7 @@ repos:
- id: prettier - id: prettier
stages: [manual] stages: [manual]
- repo: https://github.com/cdce8p/python-typing-update - repo: https://github.com/cdce8p/python-typing-update
rev: v0.3.2 rev: v0.3.3
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.

47
.strict-typing Normal file
View File

@ -0,0 +1,47 @@
# Used by hassfest for generating mypy.ini.
# If component is fully covered with type annotations, please add it here
# to enable strict mypy checks.
homeassistant.components
homeassistant.components.automation.*
homeassistant.components.binary_sensor.*
homeassistant.components.bond.*
homeassistant.components.calendar.*
homeassistant.components.cover.*
homeassistant.components.device_automation.*
homeassistant.components.frontend.*
homeassistant.components.geo_location.*
homeassistant.components.group.*
homeassistant.components.history.*
homeassistant.components.http.*
homeassistant.components.huawei_lte.*
homeassistant.components.hyperion.*
homeassistant.components.image_processing.*
homeassistant.components.integration.*
homeassistant.components.knx.*
homeassistant.components.light.*
homeassistant.components.lock.*
homeassistant.components.mailbox.*
homeassistant.components.media_player.*
homeassistant.components.notify.*
homeassistant.components.number.*
homeassistant.components.persistent_notification.*
homeassistant.components.proximity.*
homeassistant.components.recorder.purge
homeassistant.components.recorder.repack
homeassistant.components.remote.*
homeassistant.components.scene.*
homeassistant.components.sensor.*
homeassistant.components.slack.*
homeassistant.components.sonos.media_player
homeassistant.components.sun.*
homeassistant.components.switch.*
homeassistant.components.systemmonitor.*
homeassistant.components.tts.*
homeassistant.components.vacuum.*
homeassistant.components.water_heater.*
homeassistant.components.weather.*
homeassistant.components.websocket_api.*
homeassistant.components.zeroconf.*
homeassistant.components.zone.*
homeassistant.components.zwave_js.*

13
.vscode/launch.json vendored
View File

@ -9,11 +9,8 @@
"type": "python", "type": "python",
"request": "launch", "request": "launch",
"module": "homeassistant", "module": "homeassistant",
"args": [ "justMyCode": false,
"--debug", "args": ["--debug", "-c", "config"]
"-c",
"config"
]
}, },
{ {
// Debug by attaching to local Home Asistant server using Remote Python Debugger. // Debug by attaching to local Home Asistant server using Remote Python Debugger.
@ -28,7 +25,7 @@
"localRoot": "${workspaceFolder}", "localRoot": "${workspaceFolder}",
"remoteRoot": "." "remoteRoot": "."
} }
], ]
}, },
{ {
// Debug by attaching to remote Home Asistant server using Remote Python Debugger. // Debug by attaching to remote Home Asistant server using Remote Python Debugger.
@ -43,7 +40,7 @@
"localRoot": "${workspaceFolder}", "localRoot": "${workspaceFolder}",
"remoteRoot": "/usr/src/homeassistant" "remoteRoot": "/usr/src/homeassistant"
} }
], ]
} }
] ]
} }

View File

@ -35,7 +35,6 @@ homeassistant/components/almond/* @gcampax @balloob
homeassistant/components/alpha_vantage/* @fabaff homeassistant/components/alpha_vantage/* @fabaff
homeassistant/components/ambiclimate/* @danielhiversen homeassistant/components/ambiclimate/* @danielhiversen
homeassistant/components/ambient_station/* @bachya homeassistant/components/ambient_station/* @bachya
homeassistant/components/amcrest/* @pnbruckner
homeassistant/components/analytics/* @home-assistant/core @ludeeus homeassistant/components/analytics/* @home-assistant/core @ludeeus
homeassistant/components/androidtv/* @JeffLIrion homeassistant/components/androidtv/* @JeffLIrion
homeassistant/components/apache_kafka/* @bachya homeassistant/components/apache_kafka/* @bachya
@ -89,6 +88,7 @@ homeassistant/components/cloud/* @home-assistant/cloud
homeassistant/components/cloudflare/* @ludeeus @ctalkington homeassistant/components/cloudflare/* @ludeeus @ctalkington
homeassistant/components/color_extractor/* @GenericStudent homeassistant/components/color_extractor/* @GenericStudent
homeassistant/components/comfoconnect/* @michaelarnauts homeassistant/components/comfoconnect/* @michaelarnauts
homeassistant/components/compensation/* @Petro31
homeassistant/components/config/* @home-assistant/core homeassistant/components/config/* @home-assistant/core
homeassistant/components/configurator/* @home-assistant/core homeassistant/components/configurator/* @home-assistant/core
homeassistant/components/control4/* @lawtancool homeassistant/components/control4/* @lawtancool
@ -133,6 +133,7 @@ homeassistant/components/elkm1/* @gwww @bdraco
homeassistant/components/elv/* @majuss homeassistant/components/elv/* @majuss
homeassistant/components/emby/* @mezz64 homeassistant/components/emby/* @mezz64
homeassistant/components/emoncms/* @borpin homeassistant/components/emoncms/* @borpin
homeassistant/components/emonitor/* @bdraco
homeassistant/components/emulated_kasa/* @kbickar homeassistant/components/emulated_kasa/* @kbickar
homeassistant/components/enigma2/* @fbradyirl homeassistant/components/enigma2/* @fbradyirl
homeassistant/components/enocean/* @bdurrer homeassistant/components/enocean/* @bdurrer
@ -146,7 +147,7 @@ homeassistant/components/eq3btsmart/* @rytilahti
homeassistant/components/esphome/* @OttoWinter homeassistant/components/esphome/* @OttoWinter
homeassistant/components/essent/* @TheLastProject homeassistant/components/essent/* @TheLastProject
homeassistant/components/evohome/* @zxdavb homeassistant/components/evohome/* @zxdavb
homeassistant/components/ezviz/* @baqs homeassistant/components/ezviz/* @RenierM26 @baqs
homeassistant/components/faa_delays/* @ntilley905 homeassistant/components/faa_delays/* @ntilley905
homeassistant/components/fastdotcom/* @rohankapoorcom homeassistant/components/fastdotcom/* @rohankapoorcom
homeassistant/components/file/* @fabaff homeassistant/components/file/* @fabaff
@ -163,6 +164,8 @@ homeassistant/components/forked_daapd/* @uvjustin
homeassistant/components/fortios/* @kimfrellsen homeassistant/components/fortios/* @kimfrellsen
homeassistant/components/foscam/* @skgsergio homeassistant/components/foscam/* @skgsergio
homeassistant/components/freebox/* @hacf-fr @Quentame homeassistant/components/freebox/* @hacf-fr @Quentame
homeassistant/components/fritz/* @mammuth @AaronDavidSchneider @chemelli74
homeassistant/components/fritzbox/* @mib1185
homeassistant/components/fronius/* @nielstron homeassistant/components/fronius/* @nielstron
homeassistant/components/frontend/* @home-assistant/frontend homeassistant/components/frontend/* @home-assistant/frontend
homeassistant/components/garmin_connect/* @cyberjunky homeassistant/components/garmin_connect/* @cyberjunky
@ -182,7 +185,7 @@ homeassistant/components/gpsd/* @fabaff
homeassistant/components/gree/* @cmroche homeassistant/components/gree/* @cmroche
homeassistant/components/greeneye_monitor/* @jkeljo homeassistant/components/greeneye_monitor/* @jkeljo
homeassistant/components/group/* @home-assistant/core homeassistant/components/group/* @home-assistant/core
homeassistant/components/growatt_server/* @indykoning homeassistant/components/growatt_server/* @indykoning @muppet3000
homeassistant/components/guardian/* @bachya homeassistant/components/guardian/* @bachya
homeassistant/components/habitica/* @ASMfreaK @leikoilja homeassistant/components/habitica/* @ASMfreaK @leikoilja
homeassistant/components/harmony/* @ehendrix23 @bramkragten @bdraco @mkeesey homeassistant/components/harmony/* @ehendrix23 @bramkragten @bdraco @mkeesey
@ -212,6 +215,7 @@ homeassistant/components/hunterdouglas_powerview/* @bdraco
homeassistant/components/hvv_departures/* @vigonotion homeassistant/components/hvv_departures/* @vigonotion
homeassistant/components/hydrawise/* @ptcryan homeassistant/components/hydrawise/* @ptcryan
homeassistant/components/hyperion/* @dermotduffy homeassistant/components/hyperion/* @dermotduffy
homeassistant/components/ialarm/* @RyuzakiKK
homeassistant/components/iammeter/* @lewei50 homeassistant/components/iammeter/* @lewei50
homeassistant/components/iaqualink/* @flz homeassistant/components/iaqualink/* @flz
homeassistant/components/icloud/* @Quentame @nzapponi homeassistant/components/icloud/* @Quentame @nzapponi
@ -248,6 +252,7 @@ homeassistant/components/kmtronic/* @dgomes
homeassistant/components/knx/* @Julius2342 @farmio @marvin-w homeassistant/components/knx/* @Julius2342 @farmio @marvin-w
homeassistant/components/kodi/* @OnFreund @cgtobi homeassistant/components/kodi/* @OnFreund @cgtobi
homeassistant/components/konnected/* @heythisisnate @kit-klein homeassistant/components/konnected/* @heythisisnate @kit-klein
homeassistant/components/kostal_plenticore/* @stegm
homeassistant/components/kulersky/* @emlove homeassistant/components/kulersky/* @emlove
homeassistant/components/lametric/* @robbiet480 homeassistant/components/lametric/* @robbiet480
homeassistant/components/launch_library/* @ludeeus homeassistant/components/launch_library/* @ludeeus
@ -276,6 +281,7 @@ homeassistant/components/mediaroom/* @dgomes
homeassistant/components/melcloud/* @vilppuvuorinen homeassistant/components/melcloud/* @vilppuvuorinen
homeassistant/components/melissa/* @kennedyshead homeassistant/components/melissa/* @kennedyshead
homeassistant/components/met/* @danielhiversen @thimic homeassistant/components/met/* @danielhiversen @thimic
homeassistant/components/met_eireann/* @DylanGore
homeassistant/components/meteo_france/* @hacf-fr @oncleben31 @Quentame homeassistant/components/meteo_france/* @hacf-fr @oncleben31 @Quentame
homeassistant/components/meteoalarm/* @rolfberkenbosch homeassistant/components/meteoalarm/* @rolfberkenbosch
homeassistant/components/metoffice/* @MrHarcombe homeassistant/components/metoffice/* @MrHarcombe
@ -290,10 +296,12 @@ homeassistant/components/modbus/* @adamchengtkc @janiversen @vzahradnik
homeassistant/components/monoprice/* @etsinko @OnFreund homeassistant/components/monoprice/* @etsinko @OnFreund
homeassistant/components/moon/* @fabaff homeassistant/components/moon/* @fabaff
homeassistant/components/motion_blinds/* @starkillerOG homeassistant/components/motion_blinds/* @starkillerOG
homeassistant/components/motioneye/* @dermotduffy
homeassistant/components/mpd/* @fabaff homeassistant/components/mpd/* @fabaff
homeassistant/components/mqtt/* @emontnemery homeassistant/components/mqtt/* @emontnemery
homeassistant/components/msteams/* @peroyvind homeassistant/components/msteams/* @peroyvind
homeassistant/components/mullvad/* @meichthys homeassistant/components/mullvad/* @meichthys
homeassistant/components/mutesync/* @currentoor
homeassistant/components/my/* @home-assistant/core homeassistant/components/my/* @home-assistant/core
homeassistant/components/myq/* @bdraco homeassistant/components/myq/* @bdraco
homeassistant/components/mysensors/* @MartinHjelmare @functionpointer homeassistant/components/mysensors/* @MartinHjelmare @functionpointer
@ -352,6 +360,7 @@ homeassistant/components/persistent_notification/* @home-assistant/core
homeassistant/components/philips_js/* @elupus homeassistant/components/philips_js/* @elupus
homeassistant/components/pi4ioe5v9xxxx/* @antonverburg homeassistant/components/pi4ioe5v9xxxx/* @antonverburg
homeassistant/components/pi_hole/* @fabaff @johnluetke @shenxn homeassistant/components/pi_hole/* @fabaff @johnluetke @shenxn
homeassistant/components/picnic/* @corneyl
homeassistant/components/pilight/* @trekky12 homeassistant/components/pilight/* @trekky12
homeassistant/components/plaato/* @JohNan homeassistant/components/plaato/* @JohNan
homeassistant/components/plex/* @jjlawren homeassistant/components/plex/* @jjlawren
@ -363,7 +372,7 @@ homeassistant/components/powerwall/* @bdraco @jrester
homeassistant/components/profiler/* @bdraco homeassistant/components/profiler/* @bdraco
homeassistant/components/progettihwsw/* @ardaseremet homeassistant/components/progettihwsw/* @ardaseremet
homeassistant/components/prometheus/* @knyar homeassistant/components/prometheus/* @knyar
homeassistant/components/proxmoxve/* @k4ds3 @jhollowe homeassistant/components/proxmoxve/* @k4ds3 @jhollowe @Corbeno
homeassistant/components/ps4/* @ktnrg45 homeassistant/components/ps4/* @ktnrg45
homeassistant/components/push/* @dgomes homeassistant/components/push/* @dgomes
homeassistant/components/pvoutput/* @fabaff homeassistant/components/pvoutput/* @fabaff
@ -424,7 +433,7 @@ homeassistant/components/sisyphus/* @jkeljo
homeassistant/components/sky_hub/* @rogerselwyn homeassistant/components/sky_hub/* @rogerselwyn
homeassistant/components/slack/* @bachya homeassistant/components/slack/* @bachya
homeassistant/components/slide/* @ualex73 homeassistant/components/slide/* @ualex73
homeassistant/components/sma/* @kellerza homeassistant/components/sma/* @kellerza @rklomp
homeassistant/components/smappee/* @bsmappee homeassistant/components/smappee/* @bsmappee
homeassistant/components/smart_meter_texas/* @grahamwetzler homeassistant/components/smart_meter_texas/* @grahamwetzler
homeassistant/components/smarthab/* @outadoc homeassistant/components/smarthab/* @outadoc

View File

@ -1,323 +0,0 @@
# https://dev.azure.com/home-assistant
trigger:
tags:
include:
- '*'
pr: none
schedules:
- cron: "0 1 * * *"
displayName: "nightly builds"
branches:
include:
- dev
always: true
variables:
- name: versionBuilder
value: '2021.02.0'
- group: docker
- group: github
- group: twine
resources:
repositories:
- repository: azure
type: github
name: 'home-assistant/ci-azure'
endpoint: 'home-assistant'
stages:
- stage: 'Validate'
jobs:
- template: templates/azp-job-version.yaml@azure
parameters:
ignoreDev: true
- job: 'Permission'
pool:
vmImage: 'ubuntu-latest'
steps:
- script: |
sudo apt-get install -y --no-install-recommends \
jq curl
release="$(Build.SourceBranchName)"
created_by="$(curl -s https://api.github.com/repos/home-assistant/core/releases/tags/${release} | jq --raw-output '.author.login')"
if [[ "${created_by}" =~ ^(balloob|pvizeli|fabaff|robbiet480|bramkragten|frenck)$ ]]; then
exit 0
fi
echo "${created_by} is not allowed to create an release!"
exit 1
displayName: 'Check rights'
condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags'))
- stage: 'Build'
jobs:
- job: 'ReleasePython'
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags')
pool:
vmImage: 'ubuntu-latest'
steps:
- task: UsePythonVersion@0
displayName: 'Use Python 3.8'
inputs:
versionSpec: '3.8'
- script: pip install twine wheel
displayName: 'Install tools'
- script: python setup.py sdist bdist_wheel
displayName: 'Build package'
- script: |
export TWINE_USERNAME="$(twineUser)"
export TWINE_PASSWORD="$(twinePassword)"
twine upload dist/* --skip-existing
displayName: 'Upload pypi'
- job: 'ReleaseDocker'
timeoutInMinutes: 240
pool:
vmImage: 'ubuntu-latest'
strategy:
maxParallel: 5
matrix:
amd64:
buildArch: 'amd64'
i386:
buildArch: 'i386'
armhf:
buildArch: 'armhf'
armv7:
buildArch: 'armv7'
aarch64:
buildArch: 'aarch64'
steps:
- template: templates/azp-step-ha-version.yaml@azure
- script: |
docker login -u $(dockerUser) -p $(dockerPassword)
displayName: 'Docker hub login'
- script: docker pull homeassistant/amd64-builder:$(versionBuilder)
displayName: 'Install Builder'
- script: |
set -e
docker run --rm --privileged \
-v ~/.docker:/root/.docker:rw \
-v /run/docker.sock:/run/docker.sock:rw \
-v $(pwd):/data:ro \
homeassistant/amd64-builder:$(versionBuilder) \
--generic $(homeassistantRelease) "--$(buildArch)" -t /data \
displayName: 'Build Release'
- job: 'ReleaseMachine'
dependsOn:
- ReleaseDocker
timeoutInMinutes: 240
pool:
vmImage: 'ubuntu-latest'
strategy:
maxParallel: 17
matrix:
qemux86-64:
buildMachine: 'qemux86-64'
generic-x86-64:
buildMachine: 'generic-x86-64'
intel-nuc:
buildMachine: 'intel-nuc'
qemux86:
buildMachine: 'qemux86'
qemuarm:
buildMachine: 'qemuarm'
raspberrypi:
buildMachine: 'raspberrypi'
raspberrypi2:
buildMachine: 'raspberrypi2'
raspberrypi3:
buildMachine: 'raspberrypi3'
raspberrypi4:
buildMachine: 'raspberrypi4'
odroid-xu:
buildMachine: 'odroid-xu'
tinker:
buildMachine: 'tinker'
qemuarm-64:
buildMachine: 'qemuarm-64'
raspberrypi3-64:
buildMachine: 'raspberrypi3-64'
raspberrypi4-64:
buildMachine: 'raspberrypi4-64'
odroid-c2:
buildMachine: 'odroid-c2'
odroid-c4:
buildMachine: 'odroid-c4'
odroid-n2:
buildMachine: 'odroid-n2'
steps:
- template: templates/azp-step-ha-version.yaml@azure
- script: |
docker login -u $(dockerUser) -p $(dockerPassword)
displayName: 'Docker hub login'
- script: docker pull homeassistant/amd64-builder:$(versionBuilder)
displayName: 'Install Builder'
- script: |
set -e
docker run --rm --privileged \
-v ~/.docker:/root/.docker \
-v /run/docker.sock:/run/docker.sock:rw \
-v $(pwd):/data:ro \
homeassistant/amd64-builder:$(versionBuilder) \
--homeassistant-machine "$(homeassistantRelease)=$(buildMachine)" \
-t /data/machine --docker-hub homeassistant
displayName: 'Build Machine'
- stage: 'Publish'
jobs:
- job: 'ReleaseHassio'
pool:
vmImage: 'ubuntu-latest'
steps:
- template: templates/azp-step-ha-version.yaml@azure
- script: |
sudo apt-get install -y --no-install-recommends \
git jq curl
git config --global user.name "Pascal Vizeli"
git config --global user.email "pvizeli@syshack.ch"
git config --global credential.helper store
echo "https://$(githubToken):x-oauth-basic@github.com" > $HOME/.git-credentials
displayName: 'Install requirements'
- script: |
set -e
version="$(homeassistantRelease)"
git clone https://github.com/home-assistant/version
cd version
dev_version="$(jq --raw-output '.homeassistant.default' dev.json)"
beta_version="$(jq --raw-output '.homeassistant.default' beta.json)"
stable_version="$(jq --raw-output '.homeassistant.default' stable.json)"
if [[ "$version" =~ d ]]; then
sed -i "s|$dev_version|$version|g" dev.json
elif [[ "$version" =~ b ]]; then
sed -i "s|$beta_version|$version|g" beta.json
else
sed -i "s|$beta_version|$version|g" beta.json
sed -i "s|$stable_version|$version|g" stable.json
fi
git commit -am "Bump Home Assistant $version"
git push
displayName: "Update version files"
- job: 'ReleaseDocker'
pool:
vmImage: 'ubuntu-latest'
steps:
- template: templates/azp-step-ha-version.yaml@azure
- script: |
docker login -u $(dockerUser) -p $(dockerPassword)
displayName: 'Docker login'
- script: |
set -e
export DOCKER_CLI_EXPERIMENTAL=enabled
function create_manifest() {
local tag_l=$1
local tag_r=$2
docker manifest create homeassistant/home-assistant:${tag_l} \
homeassistant/amd64-homeassistant:${tag_r} \
homeassistant/i386-homeassistant:${tag_r} \
homeassistant/armhf-homeassistant:${tag_r} \
homeassistant/armv7-homeassistant:${tag_r} \
homeassistant/aarch64-homeassistant:${tag_r}
docker manifest annotate homeassistant/home-assistant:${tag_l} \
homeassistant/amd64-homeassistant:${tag_r} \
--os linux --arch amd64
docker manifest annotate homeassistant/home-assistant:${tag_l} \
homeassistant/i386-homeassistant:${tag_r} \
--os linux --arch 386
docker manifest annotate homeassistant/home-assistant:${tag_l} \
homeassistant/armhf-homeassistant:${tag_r} \
--os linux --arch arm --variant=v6
docker manifest annotate homeassistant/home-assistant:${tag_l} \
homeassistant/armv7-homeassistant:${tag_r} \
--os linux --arch arm --variant=v7
docker manifest annotate homeassistant/home-assistant:${tag_l} \
homeassistant/aarch64-homeassistant:${tag_r} \
--os linux --arch arm64 --variant=v8
docker manifest push --purge homeassistant/home-assistant:${tag_l}
}
docker pull homeassistant/amd64-homeassistant:$(homeassistantRelease)
docker pull homeassistant/i386-homeassistant:$(homeassistantRelease)
docker pull homeassistant/armhf-homeassistant:$(homeassistantRelease)
docker pull homeassistant/armv7-homeassistant:$(homeassistantRelease)
docker pull homeassistant/aarch64-homeassistant:$(homeassistantRelease)
# Create version tag
create_manifest "$(homeassistantRelease)" "$(homeassistantRelease)"
# Create general tags
if [[ "$(homeassistantRelease)" =~ d ]]; then
create_manifest "dev" "$(homeassistantRelease)"
elif [[ "$(homeassistantRelease)" =~ b ]]; then
create_manifest "beta" "$(homeassistantRelease)"
create_manifest "rc" "$(homeassistantRelease)"
else
create_manifest "stable" "$(homeassistantRelease)"
create_manifest "latest" "$(homeassistantRelease)"
create_manifest "beta" "$(homeassistantRelease)"
create_manifest "rc" "$(homeassistantRelease)"
fi
displayName: 'Create Meta-Image'
- stage: 'Addidional'
jobs:
- job: 'Updater'
pool:
vmImage: 'ubuntu-latest'
variables:
- group: gcloud
steps:
- template: templates/azp-step-ha-version.yaml@azure
- script: |
set -e
export CLOUDSDK_CORE_DISABLE_PROMPTS=1
curl -o google-cloud-sdk.tar.gz https://dl.google.com/dl/cloudsdk/release/google-cloud-sdk.tar.gz
tar -C . -xvf google-cloud-sdk.tar.gz
rm -f google-cloud-sdk.tar.gz
./google-cloud-sdk/install.sh
displayName: 'Setup gCloud'
condition: eq(variables['homeassistantReleaseStable'], 'true')
- script: |
set -e
export CLOUDSDK_CORE_DISABLE_PROMPTS=1
echo "$(gcloudAnalytic)" > gcloud_auth.json
./google-cloud-sdk/bin/gcloud auth activate-service-account --key-file gcloud_auth.json
rm -f gcloud_auth.json
displayName: 'Auth gCloud'
condition: eq(variables['homeassistantReleaseStable'], 'true')
- script: |
set -e
export CLOUDSDK_CORE_DISABLE_PROMPTS=1
./google-cloud-sdk/bin/gcloud functions deploy Analytics-Receiver \
--project home-assistant-analytics \
--update-env-vars VERSION=$(homeassistantRelease) \
--source gs://analytics-src/function-source.zip
displayName: 'Push details to updater'
condition: eq(variables['homeassistantReleaseStable'], 'true')

View File

@ -1,14 +1,22 @@
{ {
"image": "homeassistant/{arch}-homeassistant", "image": "homeassistant/{arch}-homeassistant",
"shadow_repository": "ghcr.io/home-assistant",
"build_from": { "build_from": {
"aarch64": "homeassistant/aarch64-homeassistant-base:2021.02.0", "aarch64": "ghcr.io/home-assistant/aarch64-homeassistant-base:2021.04.3",
"armhf": "homeassistant/armhf-homeassistant-base:2021.02.0", "armhf": "ghcr.io/home-assistant/armhf-homeassistant-base:2021.04.3",
"armv7": "homeassistant/armv7-homeassistant-base:2021.02.0", "armv7": "ghcr.io/home-assistant/armv7-homeassistant-base:2021.04.3",
"amd64": "homeassistant/amd64-homeassistant-base:2021.02.0", "amd64": "ghcr.io/home-assistant/amd64-homeassistant-base:2021.04.3",
"i386": "homeassistant/i386-homeassistant-base:2021.02.0" "i386": "ghcr.io/home-assistant/i386-homeassistant-base:2021.04.3"
}, },
"labels": { "labels": {
"io.hass.type": "core" "io.hass.type": "core",
"org.opencontainers.image.title": "Home Assistant",
"org.opencontainers.image.description": "Open-source home automation platform running on Python 3",
"org.opencontainers.image.source": "https://github.com/home-assistant/core",
"org.opencontainers.image.authors": "The Home Assistant Authors",
"org.opencontainers.image.url": "https://www.home-assistant.io/",
"org.opencontainers.image.documentation": "https://www.home-assistant.io/docs/",
"org.opencontainers.image.licenses": "Apache License 2.0"
}, },
"version_tag": true "version_tag": true
} }

View File

@ -145,6 +145,7 @@ def daemonize() -> None:
sys.exit(0) sys.exit(0)
# redirect standard file descriptors to devnull # redirect standard file descriptors to devnull
# pylint: disable=consider-using-with
infd = open(os.devnull) infd = open(os.devnull)
outfd = open(os.devnull, "a+") outfd = open(os.devnull, "a+")
sys.stdout.flush() sys.stdout.flush()

View File

@ -4,13 +4,14 @@ from __future__ import annotations
import asyncio import asyncio
from collections import OrderedDict from collections import OrderedDict
from datetime import timedelta from datetime import timedelta
from typing import Any, Dict, Optional, Tuple, cast from typing import Any, Dict, Mapping, Optional, Tuple, cast
import jwt import jwt
from homeassistant import data_entry_flow from homeassistant import data_entry_flow
from homeassistant.auth.const import ACCESS_TOKEN_EXPIRATION from homeassistant.auth.const import ACCESS_TOKEN_EXPIRATION
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.data_entry_flow import FlowResult
from homeassistant.util import dt as dt_util from homeassistant.util import dt as dt_util
from . import auth_store, models from . import auth_store, models
@ -97,8 +98,8 @@ class AuthManagerFlowManager(data_entry_flow.FlowManager):
return await auth_provider.async_login_flow(context) return await auth_provider.async_login_flow(context)
async def async_finish_flow( async def async_finish_flow(
self, flow: data_entry_flow.FlowHandler, result: dict[str, Any] self, flow: data_entry_flow.FlowHandler, result: FlowResult
) -> dict[str, Any]: ) -> FlowResult:
"""Return a user as result of login flow.""" """Return a user as result of login flow."""
flow = cast(LoginFlow, flow) flow = cast(LoginFlow, flow)
@ -115,7 +116,7 @@ class AuthManagerFlowManager(data_entry_flow.FlowManager):
raise KeyError(f"Unknown auth provider {result['handler']}") raise KeyError(f"Unknown auth provider {result['handler']}")
credentials = await auth_provider.async_get_or_create_credentials( credentials = await auth_provider.async_get_or_create_credentials(
result["data"] cast(Mapping[str, str], result["data"]),
) )
if flow.context.get("credential_only"): if flow.context.get("credential_only"):

View File

@ -12,6 +12,7 @@ from voluptuous.humanize import humanize_error
from homeassistant import data_entry_flow, requirements from homeassistant import data_entry_flow, requirements
from homeassistant.const import CONF_ID, CONF_NAME, CONF_TYPE from homeassistant.const import CONF_ID, CONF_NAME, CONF_TYPE
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResult
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.util.decorator import Registry from homeassistant.util.decorator import Registry
@ -105,7 +106,7 @@ class SetupFlow(data_entry_flow.FlowHandler):
async def async_step_init( async def async_step_init(
self, user_input: dict[str, str] | None = None self, user_input: dict[str, str] | None = None
) -> dict[str, Any]: ) -> FlowResult:
"""Handle the first step of setup flow. """Handle the first step of setup flow.
Return self.async_show_form(step_id='init') if user_input is None. Return self.async_show_form(step_id='init') if user_input is None.

View File

@ -14,6 +14,7 @@ import voluptuous as vol
from homeassistant.const import CONF_EXCLUDE, CONF_INCLUDE from homeassistant.const import CONF_EXCLUDE, CONF_INCLUDE
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.data_entry_flow import FlowResult
from homeassistant.exceptions import ServiceNotFound from homeassistant.exceptions import ServiceNotFound
from homeassistant.helpers import config_validation as cv from homeassistant.helpers import config_validation as cv
@ -292,7 +293,7 @@ class NotifySetupFlow(SetupFlow):
async def async_step_init( async def async_step_init(
self, user_input: dict[str, str] | None = None self, user_input: dict[str, str] | None = None
) -> dict[str, Any]: ) -> FlowResult:
"""Let user select available notify services.""" """Let user select available notify services."""
errors: dict[str, str] = {} errors: dict[str, str] = {}
@ -318,7 +319,7 @@ class NotifySetupFlow(SetupFlow):
async def async_step_setup( async def async_step_setup(
self, user_input: dict[str, str] | None = None self, user_input: dict[str, str] | None = None
) -> dict[str, Any]: ) -> FlowResult:
"""Verify user can receive one-time password.""" """Verify user can receive one-time password."""
errors: dict[str, str] = {} errors: dict[str, str] = {}

View File

@ -9,6 +9,7 @@ import voluptuous as vol
from homeassistant.auth.models import User from homeassistant.auth.models import User
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResult
from . import ( from . import (
MULTI_FACTOR_AUTH_MODULE_SCHEMA, MULTI_FACTOR_AUTH_MODULE_SCHEMA,
@ -189,7 +190,7 @@ class TotpSetupFlow(SetupFlow):
async def async_step_init( async def async_step_init(
self, user_input: dict[str, str] | None = None self, user_input: dict[str, str] | None = None
) -> dict[str, Any]: ) -> FlowResult:
"""Handle the first step of setup flow. """Handle the first step of setup flow.
Return self.async_show_form(step_id='init') if user_input is None. Return self.async_show_form(step_id='init') if user_input is None.

View File

@ -1,6 +1,7 @@
"""Auth providers for Home Assistant.""" """Auth providers for Home Assistant."""
from __future__ import annotations from __future__ import annotations
from collections.abc import Mapping
import importlib import importlib
import logging import logging
import types import types
@ -12,6 +13,7 @@ from voluptuous.humanize import humanize_error
from homeassistant import data_entry_flow, requirements from homeassistant import data_entry_flow, requirements
from homeassistant.const import CONF_ID, CONF_NAME, CONF_TYPE from homeassistant.const import CONF_ID, CONF_NAME, CONF_TYPE
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.data_entry_flow import FlowResult
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.util import dt as dt_util from homeassistant.util import dt as dt_util
from homeassistant.util.decorator import Registry from homeassistant.util.decorator import Registry
@ -102,7 +104,7 @@ class AuthProvider:
raise NotImplementedError raise NotImplementedError
async def async_get_or_create_credentials( async def async_get_or_create_credentials(
self, flow_result: dict[str, str] self, flow_result: Mapping[str, str]
) -> Credentials: ) -> Credentials:
"""Get credentials based on the flow result.""" """Get credentials based on the flow result."""
raise NotImplementedError raise NotImplementedError
@ -198,7 +200,7 @@ class LoginFlow(data_entry_flow.FlowHandler):
async def async_step_init( async def async_step_init(
self, user_input: dict[str, str] | None = None self, user_input: dict[str, str] | None = None
) -> dict[str, Any]: ) -> FlowResult:
"""Handle the first step of login flow. """Handle the first step of login flow.
Return self.async_show_form(step_id='init') if user_input is None. Return self.async_show_form(step_id='init') if user_input is None.
@ -208,7 +210,7 @@ class LoginFlow(data_entry_flow.FlowHandler):
async def async_step_select_mfa_module( async def async_step_select_mfa_module(
self, user_input: dict[str, str] | None = None self, user_input: dict[str, str] | None = None
) -> dict[str, Any]: ) -> FlowResult:
"""Handle the step of select mfa module.""" """Handle the step of select mfa module."""
errors = {} errors = {}
@ -233,7 +235,7 @@ class LoginFlow(data_entry_flow.FlowHandler):
async def async_step_mfa( async def async_step_mfa(
self, user_input: dict[str, str] | None = None self, user_input: dict[str, str] | None = None
) -> dict[str, Any]: ) -> FlowResult:
"""Handle the step of mfa validation.""" """Handle the step of mfa validation."""
assert self.credential assert self.credential
assert self.user assert self.user
@ -285,6 +287,6 @@ class LoginFlow(data_entry_flow.FlowHandler):
errors=errors, errors=errors,
) )
async def async_finish(self, flow_result: Any) -> dict: async def async_finish(self, flow_result: Any) -> FlowResult:
"""Handle the pass of login flow.""" """Handle the pass of login flow."""
return self.async_create_entry(title=self._auth_provider.name, data=flow_result) return self.async_create_entry(title=self._auth_provider.name, data=flow_result)

View File

@ -3,6 +3,7 @@ from __future__ import annotations
import asyncio.subprocess import asyncio.subprocess
import collections import collections
from collections.abc import Mapping
import logging import logging
import os import os
from typing import Any, cast from typing import Any, cast
@ -10,6 +11,7 @@ from typing import Any, cast
import voluptuous as vol import voluptuous as vol
from homeassistant.const import CONF_COMMAND from homeassistant.const import CONF_COMMAND
from homeassistant.data_entry_flow import FlowResult
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from . import AUTH_PROVIDER_SCHEMA, AUTH_PROVIDERS, AuthProvider, LoginFlow from . import AUTH_PROVIDER_SCHEMA, AUTH_PROVIDERS, AuthProvider, LoginFlow
@ -100,7 +102,7 @@ class CommandLineAuthProvider(AuthProvider):
self._user_meta[username] = meta self._user_meta[username] = meta
async def async_get_or_create_credentials( async def async_get_or_create_credentials(
self, flow_result: dict[str, str] self, flow_result: Mapping[str, str]
) -> Credentials: ) -> Credentials:
"""Get credentials based on the flow result.""" """Get credentials based on the flow result."""
username = flow_result["username"] username = flow_result["username"]
@ -127,7 +129,7 @@ class CommandLineLoginFlow(LoginFlow):
async def async_step_init( async def async_step_init(
self, user_input: dict[str, str] | None = None self, user_input: dict[str, str] | None = None
) -> dict[str, Any]: ) -> FlowResult:
"""Handle the step of the form.""" """Handle the step of the form."""
errors = {} errors = {}

View File

@ -4,6 +4,7 @@ from __future__ import annotations
import asyncio import asyncio
import base64 import base64
from collections import OrderedDict from collections import OrderedDict
from collections.abc import Mapping
import logging import logging
from typing import Any, cast from typing import Any, cast
@ -12,6 +13,7 @@ import voluptuous as vol
from homeassistant.const import CONF_ID from homeassistant.const import CONF_ID
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.data_entry_flow import FlowResult
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from . import AUTH_PROVIDER_SCHEMA, AUTH_PROVIDERS, AuthProvider, LoginFlow from . import AUTH_PROVIDER_SCHEMA, AUTH_PROVIDERS, AuthProvider, LoginFlow
@ -277,7 +279,7 @@ class HassAuthProvider(AuthProvider):
await self.data.async_save() await self.data.async_save()
async def async_get_or_create_credentials( async def async_get_or_create_credentials(
self, flow_result: dict[str, str] self, flow_result: Mapping[str, str]
) -> Credentials: ) -> Credentials:
"""Get credentials based on the flow result.""" """Get credentials based on the flow result."""
if self.data is None: if self.data is None:
@ -319,7 +321,7 @@ class HassLoginFlow(LoginFlow):
async def async_step_init( async def async_step_init(
self, user_input: dict[str, str] | None = None self, user_input: dict[str, str] | None = None
) -> dict[str, Any]: ) -> FlowResult:
"""Handle the step of the form.""" """Handle the step of the form."""
errors = {} errors = {}

View File

@ -2,12 +2,14 @@
from __future__ import annotations from __future__ import annotations
from collections import OrderedDict from collections import OrderedDict
from collections.abc import Mapping
import hmac import hmac
from typing import Any, cast from typing import cast
import voluptuous as vol import voluptuous as vol
from homeassistant.core import callback from homeassistant.core import callback
from homeassistant.data_entry_flow import FlowResult
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from . import AUTH_PROVIDER_SCHEMA, AUTH_PROVIDERS, AuthProvider, LoginFlow from . import AUTH_PROVIDER_SCHEMA, AUTH_PROVIDERS, AuthProvider, LoginFlow
@ -62,7 +64,7 @@ class ExampleAuthProvider(AuthProvider):
raise InvalidAuthError raise InvalidAuthError
async def async_get_or_create_credentials( async def async_get_or_create_credentials(
self, flow_result: dict[str, str] self, flow_result: Mapping[str, str]
) -> Credentials: ) -> Credentials:
"""Get credentials based on the flow result.""" """Get credentials based on the flow result."""
username = flow_result["username"] username = flow_result["username"]
@ -97,7 +99,7 @@ class ExampleLoginFlow(LoginFlow):
async def async_step_init( async def async_step_init(
self, user_input: dict[str, str] | None = None self, user_input: dict[str, str] | None = None
) -> dict[str, Any]: ) -> FlowResult:
"""Handle the step of the form.""" """Handle the step of the form."""
errors = {} errors = {}

View File

@ -5,12 +5,14 @@ It will be removed when auth system production ready
""" """
from __future__ import annotations from __future__ import annotations
from collections.abc import Mapping
import hmac import hmac
from typing import Any, cast from typing import cast
import voluptuous as vol import voluptuous as vol
from homeassistant.core import callback from homeassistant.core import callback
from homeassistant.data_entry_flow import FlowResult
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
@ -57,7 +59,7 @@ class LegacyApiPasswordAuthProvider(AuthProvider):
raise InvalidAuthError raise InvalidAuthError
async def async_get_or_create_credentials( async def async_get_or_create_credentials(
self, flow_result: dict[str, str] self, flow_result: Mapping[str, str]
) -> Credentials: ) -> Credentials:
"""Return credentials for this login.""" """Return credentials for this login."""
credentials = await self.async_credentials() credentials = await self.async_credentials()
@ -82,7 +84,7 @@ class LegacyLoginFlow(LoginFlow):
async def async_step_init( async def async_step_init(
self, user_input: dict[str, str] | None = None self, user_input: dict[str, str] | None = None
) -> dict[str, Any]: ) -> FlowResult:
"""Handle the step of the form.""" """Handle the step of the form."""
errors = {} errors = {}

View File

@ -5,6 +5,7 @@ Abort login flow if not access from trusted network.
""" """
from __future__ import annotations from __future__ import annotations
from collections.abc import Mapping
from ipaddress import ( from ipaddress import (
IPv4Address, IPv4Address,
IPv4Network, IPv4Network,
@ -18,6 +19,7 @@ from typing import Any, Dict, List, Union, cast
import voluptuous as vol import voluptuous as vol
from homeassistant.core import callback from homeassistant.core import callback
from homeassistant.data_entry_flow import FlowResult
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
@ -127,7 +129,7 @@ class TrustedNetworksAuthProvider(AuthProvider):
) )
async def async_get_or_create_credentials( async def async_get_or_create_credentials(
self, flow_result: dict[str, str] self, flow_result: Mapping[str, str]
) -> Credentials: ) -> Credentials:
"""Get credentials based on the flow result.""" """Get credentials based on the flow result."""
user_id = flow_result["user"] user_id = flow_result["user"]
@ -199,7 +201,7 @@ class TrustedNetworksLoginFlow(LoginFlow):
async def async_step_init( async def async_step_init(
self, user_input: dict[str, str] | None = None self, user_input: dict[str, str] | None = None
) -> dict[str, Any]: ) -> FlowResult:
"""Handle the step of the form.""" """Handle the step of the form."""
try: try:
cast( cast(

View File

@ -20,14 +20,17 @@ from homeassistant.components import http
from homeassistant.const import REQUIRED_NEXT_PYTHON_DATE, REQUIRED_NEXT_PYTHON_VER from homeassistant.const import REQUIRED_NEXT_PYTHON_DATE, REQUIRED_NEXT_PYTHON_VER
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import area_registry, device_registry, entity_registry from homeassistant.helpers import area_registry, device_registry, entity_registry
from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
from homeassistant.setup import ( from homeassistant.setup import (
DATA_SETUP, DATA_SETUP,
DATA_SETUP_STARTED, DATA_SETUP_STARTED,
DATA_SETUP_TIME,
async_set_domains_to_be_loaded, async_set_domains_to_be_loaded,
async_setup_component, async_setup_component,
) )
from homeassistant.util.async_ import gather_with_concurrency from homeassistant.util.async_ import gather_with_concurrency
import homeassistant.util.dt as dt_util
from homeassistant.util.logging import async_activate_log_queue_handler from homeassistant.util.logging import async_activate_log_queue_handler
from homeassistant.util.package import async_get_user_site, is_virtual_env from homeassistant.util.package import async_get_user_site, is_virtual_env
@ -42,6 +45,8 @@ ERROR_LOG_FILENAME = "home-assistant.log"
DATA_LOGGING = "logging" DATA_LOGGING = "logging"
LOG_SLOW_STARTUP_INTERVAL = 60 LOG_SLOW_STARTUP_INTERVAL = 60
SLOW_STARTUP_CHECK_INTERVAL = 1
SIGNAL_BOOTSTRAP_INTEGRATONS = "bootstrap_integrations"
STAGE_1_TIMEOUT = 120 STAGE_1_TIMEOUT = 120
STAGE_2_TIMEOUT = 300 STAGE_2_TIMEOUT = 300
@ -380,19 +385,32 @@ def _get_domains(hass: core.HomeAssistant, config: dict[str, Any]) -> set[str]:
return domains return domains
async def _async_log_pending_setups( async def _async_watch_pending_setups(hass: core.HomeAssistant) -> None:
hass: core.HomeAssistant, domains: set[str], setup_started: dict[str, datetime]
) -> None:
"""Periodic log of setups that are pending for longer than LOG_SLOW_STARTUP_INTERVAL.""" """Periodic log of setups that are pending for longer than LOG_SLOW_STARTUP_INTERVAL."""
loop_count = 0
setup_started: dict[str, datetime] = hass.data[DATA_SETUP_STARTED]
previous_was_empty = True
while True: while True:
await asyncio.sleep(LOG_SLOW_STARTUP_INTERVAL) now = dt_util.utcnow()
remaining = [domain for domain in domains if domain in setup_started] remaining_with_setup_started = {
domain: (now - setup_started[domain]).total_seconds()
for domain in setup_started
}
_LOGGER.debug("Integration remaining: %s", remaining_with_setup_started)
if remaining_with_setup_started or not previous_was_empty:
async_dispatcher_send(
hass, SIGNAL_BOOTSTRAP_INTEGRATONS, remaining_with_setup_started
)
previous_was_empty = not remaining_with_setup_started
await asyncio.sleep(SLOW_STARTUP_CHECK_INTERVAL)
loop_count += SLOW_STARTUP_CHECK_INTERVAL
if remaining: if loop_count >= LOG_SLOW_STARTUP_INTERVAL and setup_started:
_LOGGER.warning( _LOGGER.warning(
"Waiting on integrations to complete setup: %s", "Waiting on integrations to complete setup: %s",
", ".join(remaining), ", ".join(setup_started),
) )
loop_count = 0
_LOGGER.debug("Running timeout Zones: %s", hass.timeout.zones) _LOGGER.debug("Running timeout Zones: %s", hass.timeout.zones)
@ -400,18 +418,13 @@ async def async_setup_multi_components(
hass: core.HomeAssistant, hass: core.HomeAssistant,
domains: set[str], domains: set[str],
config: dict[str, Any], config: dict[str, Any],
setup_started: dict[str, datetime],
) -> None: ) -> None:
"""Set up multiple domains. Log on failure.""" """Set up multiple domains. Log on failure."""
futures = { futures = {
domain: hass.async_create_task(async_setup_component(hass, domain, config)) domain: hass.async_create_task(async_setup_component(hass, domain, config))
for domain in domains for domain in domains
} }
log_task = asyncio.create_task(
_async_log_pending_setups(hass, domains, setup_started)
)
await asyncio.wait(futures.values()) await asyncio.wait(futures.values())
log_task.cancel()
errors = [domain for domain in domains if futures[domain].exception()] errors = [domain for domain in domains if futures[domain].exception()]
for domain in errors: for domain in errors:
exception = futures[domain].exception() exception = futures[domain].exception()
@ -427,7 +440,11 @@ async def _async_set_up_integrations(
hass: core.HomeAssistant, config: dict[str, Any] hass: core.HomeAssistant, config: dict[str, Any]
) -> None: ) -> None:
"""Set up all the integrations.""" """Set up all the integrations."""
setup_started = hass.data[DATA_SETUP_STARTED] = {} hass.data[DATA_SETUP_STARTED] = {}
setup_time = hass.data[DATA_SETUP_TIME] = {}
watch_task = asyncio.create_task(_async_watch_pending_setups(hass))
domains_to_setup = _get_domains(hass, config) domains_to_setup = _get_domains(hass, config)
# Resolve all dependencies so we know all integrations # Resolve all dependencies so we know all integrations
@ -476,14 +493,14 @@ async def _async_set_up_integrations(
# Load logging as soon as possible # Load logging as soon as possible
if logging_domains: if logging_domains:
_LOGGER.info("Setting up logging: %s", logging_domains) _LOGGER.info("Setting up logging: %s", logging_domains)
await async_setup_multi_components(hass, logging_domains, config, setup_started) await async_setup_multi_components(hass, logging_domains, config)
# Start up debuggers. Start these first in case they want to wait. # Start up debuggers. Start these first in case they want to wait.
debuggers = domains_to_setup & DEBUGGER_INTEGRATIONS debuggers = domains_to_setup & DEBUGGER_INTEGRATIONS
if debuggers: if debuggers:
_LOGGER.debug("Setting up debuggers: %s", debuggers) _LOGGER.debug("Setting up debuggers: %s", debuggers)
await async_setup_multi_components(hass, debuggers, config, setup_started) await async_setup_multi_components(hass, debuggers, config)
# calculate what components to setup in what stage # calculate what components to setup in what stage
stage_1_domains = set() stage_1_domains = set()
@ -524,9 +541,7 @@ async def _async_set_up_integrations(
async with hass.timeout.async_timeout( async with hass.timeout.async_timeout(
STAGE_1_TIMEOUT, cool_down=COOLDOWN_TIME STAGE_1_TIMEOUT, cool_down=COOLDOWN_TIME
): ):
await async_setup_multi_components( await async_setup_multi_components(hass, stage_1_domains, config)
hass, stage_1_domains, config, setup_started
)
except asyncio.TimeoutError: except asyncio.TimeoutError:
_LOGGER.warning("Setup timed out for stage 1 - moving forward") _LOGGER.warning("Setup timed out for stage 1 - moving forward")
@ -539,12 +554,23 @@ async def _async_set_up_integrations(
async with hass.timeout.async_timeout( async with hass.timeout.async_timeout(
STAGE_2_TIMEOUT, cool_down=COOLDOWN_TIME STAGE_2_TIMEOUT, cool_down=COOLDOWN_TIME
): ):
await async_setup_multi_components( await async_setup_multi_components(hass, stage_2_domains, config)
hass, stage_2_domains, config, setup_started
)
except asyncio.TimeoutError: except asyncio.TimeoutError:
_LOGGER.warning("Setup timed out for stage 2 - moving forward") _LOGGER.warning("Setup timed out for stage 2 - moving forward")
watch_task.cancel()
async_dispatcher_send(hass, SIGNAL_BOOTSTRAP_INTEGRATONS, {})
_LOGGER.debug(
"Integration setup times: %s",
{
integration: timedelta.total_seconds()
for integration, timedelta in sorted(
setup_time.items(), key=lambda item: item[1].total_seconds() # type: ignore
)
},
)
# Wrap up startup # Wrap up startup
_LOGGER.debug("Waiting for startup to wrap up") _LOGGER.debug("Waiting for startup to wrap up")
try: try:

View File

@ -7,16 +7,16 @@ Component design guidelines:
format "<DOMAIN>.<OBJECT_ID>". format "<DOMAIN>.<OBJECT_ID>".
- Each component should publish services only under its own domain. - Each component should publish services only under its own domain.
""" """
from __future__ import annotations
import logging import logging
from homeassistant.core import split_entity_id from homeassistant.core import HomeAssistant, split_entity_id
# mypy: allow-untyped-defs
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
def is_on(hass, entity_id=None): def is_on(hass: HomeAssistant, entity_id: str | None = None) -> bool:
"""Load up the module to call the is_on method. """Load up the module to call the is_on method.
If there is no entity id given we will check all. If there is no entity id given we will check all.

View File

@ -1,5 +1,4 @@
"""Support for the Abode Security System.""" """Support for the Abode Security System."""
from asyncio import gather
from copy import deepcopy from copy import deepcopy
from functools import partial from functools import partial
@ -9,7 +8,7 @@ import abodepy.helpers.timeline as TIMELINE
from requests.exceptions import ConnectTimeout, HTTPError from requests.exceptions import ConnectTimeout, HTTPError
import voluptuous as vol import voluptuous as vol
from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_REAUTH from homeassistant.config_entries import SOURCE_IMPORT
from homeassistant.const import ( from homeassistant.const import (
ATTR_ATTRIBUTION, ATTR_ATTRIBUTION,
ATTR_DATE, ATTR_DATE,
@ -20,7 +19,7 @@ from homeassistant.const import (
CONF_USERNAME, CONF_USERNAME,
EVENT_HOMEASSISTANT_STOP, EVENT_HOMEASSISTANT_STOP,
) )
from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers import config_validation as cv from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.dispatcher import dispatcher_send from homeassistant.helpers.dispatcher import dispatcher_send
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
@ -124,24 +123,14 @@ async def async_setup_entry(hass, config_entry):
) )
except AbodeAuthenticationException as ex: except AbodeAuthenticationException as ex:
LOGGER.error("Invalid credentials: %s", ex) raise ConfigEntryAuthFailed(f"Invalid credentials: {ex}") from ex
await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_REAUTH},
data=config_entry.data,
)
return False
except (AbodeException, ConnectTimeout, HTTPError) as ex: except (AbodeException, ConnectTimeout, HTTPError) as ex:
LOGGER.error("Unable to connect to Abode: %s", ex) raise ConfigEntryNotReady(f"Unable to connect to Abode: {ex}") from ex
raise ConfigEntryNotReady from ex
hass.data[DOMAIN] = AbodeSystem(abode, polling) hass.data[DOMAIN] = AbodeSystem(abode, polling)
for platform in PLATFORMS: hass.config_entries.async_setup_platforms(config_entry, PLATFORMS)
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(config_entry, platform)
)
await setup_hass_events(hass) await setup_hass_events(hass)
await hass.async_add_executor_job(setup_hass_services, hass) await hass.async_add_executor_job(setup_hass_services, hass)
@ -156,14 +145,9 @@ async def async_unload_entry(hass, config_entry):
hass.services.async_remove(DOMAIN, SERVICE_CAPTURE_IMAGE) hass.services.async_remove(DOMAIN, SERVICE_CAPTURE_IMAGE)
hass.services.async_remove(DOMAIN, SERVICE_TRIGGER_AUTOMATION) hass.services.async_remove(DOMAIN, SERVICE_TRIGGER_AUTOMATION)
tasks = [] unload_ok = await hass.config_entries.async_unload_platforms(
config_entry, PLATFORMS
for platform in PLATFORMS: )
tasks.append(
hass.config_entries.async_forward_entry_unload(config_entry, platform)
)
await gather(*tasks)
await hass.async_add_executor_job(hass.data[DOMAIN].abode.events.stop) await hass.async_add_executor_job(hass.data[DOMAIN].abode.events.stop)
await hass.async_add_executor_job(hass.data[DOMAIN].abode.logout) await hass.async_add_executor_job(hass.data[DOMAIN].abode.logout)
@ -171,7 +155,7 @@ async def async_unload_entry(hass, config_entry):
hass.data[DOMAIN].logout_listener() hass.data[DOMAIN].logout_listener()
hass.data.pop(DOMAIN) hass.data.pop(DOMAIN)
return True return unload_ok
def setup_hass_services(hass): def setup_hass_services(hass):

View File

@ -7,5 +7,6 @@
"codeowners": ["@shred86"], "codeowners": ["@shred86"],
"homekit": { "homekit": {
"models": ["Abode", "Iota"] "models": ["Abode", "Iota"]
} },
"iot_class": "cloud_push"
} }

View File

@ -3,7 +3,22 @@
"abort": { "abort": {
"single_instance_allowed": "Solo se permite una \u00fanica configuraci\u00f3n de Abode." "single_instance_allowed": "Solo se permite una \u00fanica configuraci\u00f3n de Abode."
}, },
"error": {
"invalid_mfa_code": "C\u00f3digo MFA no v\u00e1lido"
},
"step": { "step": {
"mfa": {
"data": {
"mfa_code": "C\u00f3digo MFA (6 d\u00edgitos)"
},
"title": "Ingrese su c\u00f3digo MFA para Abode"
},
"reauth_confirm": {
"data": {
"password": "Contrase\u00f1a"
},
"title": "Complete su informaci\u00f3n de inicio de sesi\u00f3n de Abode"
},
"user": { "user": {
"data": { "data": {
"password": "Contrase\u00f1a", "password": "Contrase\u00f1a",

View File

@ -0,0 +1,7 @@
{
"config": {
"abort": {
"reauth_successful": "Re-autentificare efectuata cu succes"
}
}
}

View File

@ -1,5 +1,4 @@
"""The AccuWeather component.""" """The AccuWeather component."""
import asyncio
from datetime import timedelta from datetime import timedelta
import logging import logging
@ -46,23 +45,15 @@ async def async_setup_entry(hass, config_entry) -> bool:
UNDO_UPDATE_LISTENER: undo_listener, UNDO_UPDATE_LISTENER: undo_listener,
} }
for platform in PLATFORMS: hass.config_entries.async_setup_platforms(config_entry, PLATFORMS)
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(config_entry, platform)
)
return True return True
async def async_unload_entry(hass, config_entry): async def async_unload_entry(hass, config_entry):
"""Unload a config entry.""" """Unload a config entry."""
unload_ok = all( unload_ok = await hass.config_entries.async_unload_platforms(
await asyncio.gather( config_entry, PLATFORMS
*[
hass.config_entries.async_forward_entry_unload(config_entry, platform)
for platform in PLATFORMS
]
)
) )
hass.data[DOMAIN][config_entry.entry_id][UNDO_UPDATE_LISTENER]() hass.data[DOMAIN][config_entry.entry_id][UNDO_UPDATE_LISTENER]()

View File

@ -5,5 +5,6 @@
"requirements": ["accuweather==0.1.1"], "requirements": ["accuweather==0.1.1"],
"codeowners": ["@bieniu"], "codeowners": ["@bieniu"],
"config_flow": true, "config_flow": true,
"quality_scale": "platinum" "quality_scale": "platinum",
"iot_class": "cloud_polling"
} }

View File

@ -16,6 +16,7 @@
"longitude": "L\u00e4ngengrad", "longitude": "L\u00e4ngengrad",
"name": "Name" "name": "Name"
}, },
"description": "Wenn du Hilfe bei der Konfiguration ben\u00f6tigst, schaue hier nach: https://www.home-assistant.io/integrations/accuweather/\n\nEinige Sensoren sind standardm\u00e4\u00dfig nicht aktiviert. Du kannst sie in der Entit\u00e4tsregister nach der Integrationskonfiguration aktivieren.\nDie Wettervorhersage ist nicht standardm\u00e4\u00dfig aktiviert. Du kannst sie in den Integrationsoptionen aktivieren.",
"title": "AccuWeather" "title": "AccuWeather"
} }
} }
@ -25,7 +26,9 @@
"user": { "user": {
"data": { "data": {
"forecast": "Wettervorhersage" "forecast": "Wettervorhersage"
} },
"description": "Aufgrund der Einschr\u00e4nkungen der kostenlosen Version des AccuWeather-API-Schl\u00fcssels werden bei aktivierter Wettervorhersage Datenaktualisierungen alle 80 Minuten statt alle 40 Minuten durchgef\u00fchrt.",
"title": "AccuWeather Optionen"
} }
} }
}, },

View File

@ -16,8 +16,14 @@
"data": { "data": {
"forecast": "Pron\u00f3stico del tiempo" "forecast": "Pron\u00f3stico del tiempo"
}, },
"description": "Debido a las limitaciones de la versi\u00f3n gratuita de la clave API de AccuWeather, cuando habilita el pron\u00f3stico del tiempo, las actualizaciones de datos se realizar\u00e1n cada 64 minutos en lugar de cada 32 minutos." "description": "Debido a las limitaciones de la versi\u00f3n gratuita de la clave API de AccuWeather, cuando habilita el pron\u00f3stico del tiempo, las actualizaciones de datos se realizar\u00e1n cada 64 minutos en lugar de cada 32 minutos.",
"title": "Opciones de AccuWeather"
} }
} }
},
"system_health": {
"info": {
"remaining_requests": "Solicitudes permitidas restantes"
}
} }
} }

View File

@ -0,0 +1,9 @@
{
"state": {
"accuweather__pressure_tendency": {
"falling": "Descendente",
"rising": "Creciente",
"steady": "Firme"
}
}
}

View File

@ -3,5 +3,6 @@
"name": "Acer Projector", "name": "Acer Projector",
"documentation": "https://www.home-assistant.io/integrations/acer_projector", "documentation": "https://www.home-assistant.io/integrations/acer_projector",
"requirements": ["pyserial==3.5"], "requirements": ["pyserial==3.5"],
"codeowners": [] "codeowners": [],
"iot_class": "local_polling"
} }

View File

@ -1,5 +1,4 @@
"""The Rollease Acmeda Automate integration.""" """The Rollease Acmeda Automate integration."""
import asyncio
from homeassistant import config_entries, core from homeassistant import config_entries, core
@ -23,10 +22,7 @@ async def async_setup_entry(
hass.data.setdefault(DOMAIN, {}) hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][config_entry.entry_id] = hub hass.data[DOMAIN][config_entry.entry_id] = hub
for platform in PLATFORMS: hass.config_entries.async_setup_platforms(config_entry, PLATFORMS)
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(config_entry, platform)
)
return True return True
@ -37,14 +33,10 @@ async def async_unload_entry(
"""Unload a config entry.""" """Unload a config entry."""
hub = hass.data[DOMAIN][config_entry.entry_id] hub = hass.data[DOMAIN][config_entry.entry_id]
unload_ok = all( unload_ok = await hass.config_entries.async_unload_platforms(
await asyncio.gather( config_entry, PLATFORMS
*[
hass.config_entries.async_forward_entry_unload(config_entry, platform)
for platform in PLATFORMS
]
)
) )
if not await hub.async_reset(): if not await hub.async_reset():
return False return False

View File

@ -4,7 +4,6 @@
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/acmeda", "documentation": "https://www.home-assistant.io/integrations/acmeda",
"requirements": ["aiopulse==0.4.2"], "requirements": ["aiopulse==0.4.2"],
"codeowners": [ "codeowners": ["@atmurray"],
"@atmurray" "iot_class": "local_push"
] }
}

View File

@ -2,5 +2,6 @@
"domain": "actiontec", "domain": "actiontec",
"name": "Actiontec", "name": "Actiontec",
"documentation": "https://www.home-assistant.io/integrations/actiontec", "documentation": "https://www.home-assistant.io/integrations/actiontec",
"codeowners": [] "codeowners": [],
"iot_class": "local_polling"
} }

View File

@ -10,7 +10,7 @@ import voluptuous as vol
from homeassistant.components.adguard.const import ( from homeassistant.components.adguard.const import (
CONF_FORCE, CONF_FORCE,
DATA_ADGUARD_CLIENT, DATA_ADGUARD_CLIENT,
DATA_ADGUARD_VERION, DATA_ADGUARD_VERSION,
DOMAIN, DOMAIN,
SERVICE_ADD_URL, SERVICE_ADD_URL,
SERVICE_DISABLE_URL, SERVICE_DISABLE_URL,
@ -61,17 +61,14 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
session=session, session=session,
) )
hass.data.setdefault(DOMAIN, {})[DATA_ADGUARD_CLIENT] = adguard hass.data.setdefault(DOMAIN, {})[entry.entry_id] = {DATA_ADGUARD_CLIENT: adguard}
try: try:
await adguard.version() await adguard.version()
except AdGuardHomeConnectionError as exception: except AdGuardHomeConnectionError as exception:
raise ConfigEntryNotReady from exception raise ConfigEntryNotReady from exception
for platform in PLATFORMS: hass.config_entries.async_setup_platforms(entry, PLATFORMS)
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, platform)
)
async def add_url(call) -> None: async def add_url(call) -> None:
"""Service call to add a new filter subscription to AdGuard Home.""" """Service call to add a new filter subscription to AdGuard Home."""
@ -126,25 +123,30 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
hass.services.async_remove(DOMAIN, SERVICE_DISABLE_URL) hass.services.async_remove(DOMAIN, SERVICE_DISABLE_URL)
hass.services.async_remove(DOMAIN, SERVICE_REFRESH) hass.services.async_remove(DOMAIN, SERVICE_REFRESH)
for platform in PLATFORMS: unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
await hass.config_entries.async_forward_entry_unload(entry, platform) if unload_ok:
del hass.data[DOMAIN]
del hass.data[DOMAIN] return unload_ok
return True
class AdGuardHomeEntity(Entity): class AdGuardHomeEntity(Entity):
"""Defines a base AdGuard Home entity.""" """Defines a base AdGuard Home entity."""
def __init__( def __init__(
self, adguard, name: str, icon: str, enabled_default: bool = True self,
adguard: AdGuardHome,
entry: ConfigEntry,
name: str,
icon: str,
enabled_default: bool = True,
) -> None: ) -> None:
"""Initialize the AdGuard Home entity.""" """Initialize the AdGuard Home entity."""
self._available = True self._available = True
self._enabled_default = enabled_default self._enabled_default = enabled_default
self._icon = icon self._icon = icon
self._name = name self._name = name
self._entry = entry
self.adguard = adguard self.adguard = adguard
@property @property
@ -200,6 +202,8 @@ class AdGuardHomeDeviceEntity(AdGuardHomeEntity):
}, },
"name": "AdGuard Home", "name": "AdGuard Home",
"manufacturer": "AdGuard Team", "manufacturer": "AdGuard Team",
"sw_version": self.hass.data[DOMAIN].get(DATA_ADGUARD_VERION), "sw_version": self.hass.data[DOMAIN][self._entry.entry_id].get(
DATA_ADGUARD_VERSION
),
"entry_type": "service", "entry_type": "service",
} }

View File

@ -16,6 +16,7 @@ from homeassistant.const import (
CONF_USERNAME, CONF_USERNAME,
CONF_VERIFY_SSL, CONF_VERIFY_SSL,
) )
from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import DOMAIN from .const import DOMAIN
@ -31,7 +32,7 @@ class AdGuardHomeFlowHandler(ConfigFlow, domain=DOMAIN):
async def _show_setup_form( async def _show_setup_form(
self, errors: dict[str, str] | None = None self, errors: dict[str, str] | None = None
) -> dict[str, Any]: ) -> FlowResult:
"""Show the setup form to the user.""" """Show the setup form to the user."""
return self.async_show_form( return self.async_show_form(
step_id="user", step_id="user",
@ -50,7 +51,7 @@ class AdGuardHomeFlowHandler(ConfigFlow, domain=DOMAIN):
async def _show_hassio_form( async def _show_hassio_form(
self, errors: dict[str, str] | None = None self, errors: dict[str, str] | None = None
) -> dict[str, Any]: ) -> FlowResult:
"""Show the Hass.io confirmation form to the user.""" """Show the Hass.io confirmation form to the user."""
return self.async_show_form( return self.async_show_form(
step_id="hassio_confirm", step_id="hassio_confirm",
@ -61,14 +62,19 @@ class AdGuardHomeFlowHandler(ConfigFlow, domain=DOMAIN):
async def async_step_user( async def async_step_user(
self, user_input: dict[str, Any] | None = None self, user_input: dict[str, Any] | None = None
) -> dict[str, Any]: ) -> FlowResult:
"""Handle a flow initiated by the user.""" """Handle a flow initiated by the user."""
if self._async_current_entries():
return self.async_abort(reason="single_instance_allowed")
if user_input is None: if user_input is None:
return await self._show_setup_form(user_input) return await self._show_setup_form(user_input)
entries = self._async_current_entries()
for entry in entries:
if (
entry.data[CONF_HOST] == user_input[CONF_HOST]
and entry.data[CONF_PORT] == user_input[CONF_PORT]
):
return self.async_abort(reason="already_configured")
errors = {} errors = {}
session = async_get_clientsession(self.hass, user_input[CONF_VERIFY_SSL]) session = async_get_clientsession(self.hass, user_input[CONF_VERIFY_SSL])
@ -101,49 +107,20 @@ class AdGuardHomeFlowHandler(ConfigFlow, domain=DOMAIN):
}, },
) )
async def async_step_hassio(self, discovery_info: dict[str, Any]) -> dict[str, Any]: async def async_step_hassio(self, discovery_info: dict[str, Any]) -> FlowResult:
"""Prepare configuration for a Hass.io AdGuard Home add-on. """Prepare configuration for a Hass.io AdGuard Home add-on.
This flow is triggered by the discovery component. This flow is triggered by the discovery component.
""" """
entries = self._async_current_entries() await self._async_handle_discovery_without_unique_id()
if not entries: self._hassio_discovery = discovery_info
self._hassio_discovery = discovery_info return await self.async_step_hassio_confirm()
await self._async_handle_discovery_without_unique_id()
return await self.async_step_hassio_confirm()
cur_entry = entries[0]
if (
cur_entry.data[CONF_HOST] == discovery_info[CONF_HOST]
and cur_entry.data[CONF_PORT] == discovery_info[CONF_PORT]
):
return self.async_abort(reason="single_instance_allowed")
is_loaded = cur_entry.state == config_entries.ENTRY_STATE_LOADED
if is_loaded:
await self.hass.config_entries.async_unload(cur_entry.entry_id)
self.hass.config_entries.async_update_entry(
cur_entry,
data={
**cur_entry.data,
CONF_HOST: discovery_info[CONF_HOST],
CONF_PORT: discovery_info[CONF_PORT],
},
)
if is_loaded:
await self.hass.config_entries.async_setup(cur_entry.entry_id)
return self.async_abort(reason="existing_instance_updated")
async def async_step_hassio_confirm( async def async_step_hassio_confirm(
self, user_input: dict[str, Any] | None = None self, user_input: dict[str, Any] | None = None
) -> dict[str, Any]: ) -> FlowResult:
"""Confirm Hass.io discovery.""" """Confirm Supervisor discovery."""
if user_input is None: if user_input is None:
return await self._show_hassio_form() return await self._show_hassio_form()

View File

@ -3,7 +3,7 @@
DOMAIN = "adguard" DOMAIN = "adguard"
DATA_ADGUARD_CLIENT = "adguard_client" DATA_ADGUARD_CLIENT = "adguard_client"
DATA_ADGUARD_VERION = "adguard_version" DATA_ADGUARD_VERSION = "adguard_version"
CONF_FORCE = "force" CONF_FORCE = "force"

View File

@ -4,5 +4,6 @@
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/adguard", "documentation": "https://www.home-assistant.io/integrations/adguard",
"requirements": ["adguardhome==0.5.0"], "requirements": ["adguardhome==0.5.0"],
"codeowners": ["@frenck"] "codeowners": ["@frenck"],
"iot_class": "local_polling"
} }

View File

@ -14,7 +14,7 @@ from homeassistant.exceptions import PlatformNotReady
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
from . import AdGuardHomeDeviceEntity from . import AdGuardHomeDeviceEntity
from .const import DATA_ADGUARD_CLIENT, DATA_ADGUARD_VERION, DOMAIN from .const import DATA_ADGUARD_CLIENT, DATA_ADGUARD_VERSION, DOMAIN
SCAN_INTERVAL = timedelta(seconds=300) SCAN_INTERVAL = timedelta(seconds=300)
PARALLEL_UPDATES = 4 PARALLEL_UPDATES = 4
@ -26,24 +26,24 @@ async def async_setup_entry(
async_add_entities: Callable[[list[Entity], bool], None], async_add_entities: Callable[[list[Entity], bool], None],
) -> None: ) -> None:
"""Set up AdGuard Home sensor based on a config entry.""" """Set up AdGuard Home sensor based on a config entry."""
adguard = hass.data[DOMAIN][DATA_ADGUARD_CLIENT] adguard = hass.data[DOMAIN][entry.entry_id][DATA_ADGUARD_CLIENT]
try: try:
version = await adguard.version() version = await adguard.version()
except AdGuardHomeConnectionError as exception: except AdGuardHomeConnectionError as exception:
raise PlatformNotReady from exception raise PlatformNotReady from exception
hass.data[DOMAIN][DATA_ADGUARD_VERION] = version hass.data[DOMAIN][entry.entry_id][DATA_ADGUARD_VERSION] = version
sensors = [ sensors = [
AdGuardHomeDNSQueriesSensor(adguard), AdGuardHomeDNSQueriesSensor(adguard, entry),
AdGuardHomeBlockedFilteringSensor(adguard), AdGuardHomeBlockedFilteringSensor(adguard, entry),
AdGuardHomePercentageBlockedSensor(adguard), AdGuardHomePercentageBlockedSensor(adguard, entry),
AdGuardHomeReplacedParentalSensor(adguard), AdGuardHomeReplacedParentalSensor(adguard, entry),
AdGuardHomeReplacedSafeBrowsingSensor(adguard), AdGuardHomeReplacedSafeBrowsingSensor(adguard, entry),
AdGuardHomeReplacedSafeSearchSensor(adguard), AdGuardHomeReplacedSafeSearchSensor(adguard, entry),
AdGuardHomeAverageProcessingTimeSensor(adguard), AdGuardHomeAverageProcessingTimeSensor(adguard, entry),
AdGuardHomeRulesCountSensor(adguard), AdGuardHomeRulesCountSensor(adguard, entry),
] ]
async_add_entities(sensors, True) async_add_entities(sensors, True)
@ -55,6 +55,7 @@ class AdGuardHomeSensor(AdGuardHomeDeviceEntity, SensorEntity):
def __init__( def __init__(
self, self,
adguard: AdGuardHome, adguard: AdGuardHome,
entry: ConfigEntry,
name: str, name: str,
icon: str, icon: str,
measurement: str, measurement: str,
@ -66,7 +67,7 @@ class AdGuardHomeSensor(AdGuardHomeDeviceEntity, SensorEntity):
self._unit_of_measurement = unit_of_measurement self._unit_of_measurement = unit_of_measurement
self.measurement = measurement self.measurement = measurement
super().__init__(adguard, name, icon, enabled_default) super().__init__(adguard, entry, name, icon, enabled_default)
@property @property
def unique_id(self) -> str: def unique_id(self) -> str:
@ -95,10 +96,15 @@ class AdGuardHomeSensor(AdGuardHomeDeviceEntity, SensorEntity):
class AdGuardHomeDNSQueriesSensor(AdGuardHomeSensor): class AdGuardHomeDNSQueriesSensor(AdGuardHomeSensor):
"""Defines a AdGuard Home DNS Queries sensor.""" """Defines a AdGuard Home DNS Queries sensor."""
def __init__(self, adguard: AdGuardHome) -> None: def __init__(self, adguard: AdGuardHome, entry: ConfigEntry) -> None:
"""Initialize AdGuard Home sensor.""" """Initialize AdGuard Home sensor."""
super().__init__( super().__init__(
adguard, "AdGuard DNS Queries", "mdi:magnify", "dns_queries", "queries" adguard,
entry,
"AdGuard DNS Queries",
"mdi:magnify",
"dns_queries",
"queries",
) )
async def _adguard_update(self) -> None: async def _adguard_update(self) -> None:
@ -109,10 +115,11 @@ class AdGuardHomeDNSQueriesSensor(AdGuardHomeSensor):
class AdGuardHomeBlockedFilteringSensor(AdGuardHomeSensor): class AdGuardHomeBlockedFilteringSensor(AdGuardHomeSensor):
"""Defines a AdGuard Home blocked by filtering sensor.""" """Defines a AdGuard Home blocked by filtering sensor."""
def __init__(self, adguard: AdGuardHome) -> None: def __init__(self, adguard: AdGuardHome, entry: ConfigEntry) -> None:
"""Initialize AdGuard Home sensor.""" """Initialize AdGuard Home sensor."""
super().__init__( super().__init__(
adguard, adguard,
entry,
"AdGuard DNS Queries Blocked", "AdGuard DNS Queries Blocked",
"mdi:magnify-close", "mdi:magnify-close",
"blocked_filtering", "blocked_filtering",
@ -128,10 +135,11 @@ class AdGuardHomeBlockedFilteringSensor(AdGuardHomeSensor):
class AdGuardHomePercentageBlockedSensor(AdGuardHomeSensor): class AdGuardHomePercentageBlockedSensor(AdGuardHomeSensor):
"""Defines a AdGuard Home blocked percentage sensor.""" """Defines a AdGuard Home blocked percentage sensor."""
def __init__(self, adguard: AdGuardHome) -> None: def __init__(self, adguard: AdGuardHome, entry: ConfigEntry) -> None:
"""Initialize AdGuard Home sensor.""" """Initialize AdGuard Home sensor."""
super().__init__( super().__init__(
adguard, adguard,
entry,
"AdGuard DNS Queries Blocked Ratio", "AdGuard DNS Queries Blocked Ratio",
"mdi:magnify-close", "mdi:magnify-close",
"blocked_percentage", "blocked_percentage",
@ -147,10 +155,11 @@ class AdGuardHomePercentageBlockedSensor(AdGuardHomeSensor):
class AdGuardHomeReplacedParentalSensor(AdGuardHomeSensor): class AdGuardHomeReplacedParentalSensor(AdGuardHomeSensor):
"""Defines a AdGuard Home replaced by parental control sensor.""" """Defines a AdGuard Home replaced by parental control sensor."""
def __init__(self, adguard: AdGuardHome) -> None: def __init__(self, adguard: AdGuardHome, entry: ConfigEntry) -> None:
"""Initialize AdGuard Home sensor.""" """Initialize AdGuard Home sensor."""
super().__init__( super().__init__(
adguard, adguard,
entry,
"AdGuard Parental Control Blocked", "AdGuard Parental Control Blocked",
"mdi:human-male-girl", "mdi:human-male-girl",
"blocked_parental", "blocked_parental",
@ -165,10 +174,11 @@ class AdGuardHomeReplacedParentalSensor(AdGuardHomeSensor):
class AdGuardHomeReplacedSafeBrowsingSensor(AdGuardHomeSensor): class AdGuardHomeReplacedSafeBrowsingSensor(AdGuardHomeSensor):
"""Defines a AdGuard Home replaced by safe browsing sensor.""" """Defines a AdGuard Home replaced by safe browsing sensor."""
def __init__(self, adguard: AdGuardHome) -> None: def __init__(self, adguard: AdGuardHome, entry: ConfigEntry) -> None:
"""Initialize AdGuard Home sensor.""" """Initialize AdGuard Home sensor."""
super().__init__( super().__init__(
adguard, adguard,
entry,
"AdGuard Safe Browsing Blocked", "AdGuard Safe Browsing Blocked",
"mdi:shield-half-full", "mdi:shield-half-full",
"blocked_safebrowsing", "blocked_safebrowsing",
@ -183,10 +193,11 @@ class AdGuardHomeReplacedSafeBrowsingSensor(AdGuardHomeSensor):
class AdGuardHomeReplacedSafeSearchSensor(AdGuardHomeSensor): class AdGuardHomeReplacedSafeSearchSensor(AdGuardHomeSensor):
"""Defines a AdGuard Home replaced by safe search sensor.""" """Defines a AdGuard Home replaced by safe search sensor."""
def __init__(self, adguard: AdGuardHome) -> None: def __init__(self, adguard: AdGuardHome, entry: ConfigEntry) -> None:
"""Initialize AdGuard Home sensor.""" """Initialize AdGuard Home sensor."""
super().__init__( super().__init__(
adguard, adguard,
entry,
"AdGuard Safe Searches Enforced", "AdGuard Safe Searches Enforced",
"mdi:shield-search", "mdi:shield-search",
"enforced_safesearch", "enforced_safesearch",
@ -201,10 +212,11 @@ class AdGuardHomeReplacedSafeSearchSensor(AdGuardHomeSensor):
class AdGuardHomeAverageProcessingTimeSensor(AdGuardHomeSensor): class AdGuardHomeAverageProcessingTimeSensor(AdGuardHomeSensor):
"""Defines a AdGuard Home average processing time sensor.""" """Defines a AdGuard Home average processing time sensor."""
def __init__(self, adguard: AdGuardHome) -> None: def __init__(self, adguard: AdGuardHome, entry: ConfigEntry) -> None:
"""Initialize AdGuard Home sensor.""" """Initialize AdGuard Home sensor."""
super().__init__( super().__init__(
adguard, adguard,
entry,
"AdGuard Average Processing Speed", "AdGuard Average Processing Speed",
"mdi:speedometer", "mdi:speedometer",
"average_speed", "average_speed",
@ -220,10 +232,11 @@ class AdGuardHomeAverageProcessingTimeSensor(AdGuardHomeSensor):
class AdGuardHomeRulesCountSensor(AdGuardHomeSensor): class AdGuardHomeRulesCountSensor(AdGuardHomeSensor):
"""Defines a AdGuard Home rules count sensor.""" """Defines a AdGuard Home rules count sensor."""
def __init__(self, adguard: AdGuardHome) -> None: def __init__(self, adguard: AdGuardHome, entry: ConfigEntry) -> None:
"""Initialize AdGuard Home sensor.""" """Initialize AdGuard Home sensor."""
super().__init__( super().__init__(
adguard, adguard,
entry,
"AdGuard Rules Count", "AdGuard Rules Count",
"mdi:counter", "mdi:counter",
"rules_count", "rules_count",

View File

@ -22,7 +22,7 @@
}, },
"abort": { "abort": {
"existing_instance_updated": "Updated existing configuration.", "existing_instance_updated": "Updated existing configuration.",
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]" "already_configured": "[%key:common::config_flow::abort::already_configured_service%]"
} }
} }
} }

View File

@ -14,7 +14,7 @@ from homeassistant.exceptions import PlatformNotReady
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
from . import AdGuardHomeDeviceEntity from . import AdGuardHomeDeviceEntity
from .const import DATA_ADGUARD_CLIENT, DATA_ADGUARD_VERION, DOMAIN from .const import DATA_ADGUARD_CLIENT, DATA_ADGUARD_VERSION, DOMAIN
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -28,22 +28,22 @@ async def async_setup_entry(
async_add_entities: Callable[[list[Entity], bool], None], async_add_entities: Callable[[list[Entity], bool], None],
) -> None: ) -> None:
"""Set up AdGuard Home switch based on a config entry.""" """Set up AdGuard Home switch based on a config entry."""
adguard = hass.data[DOMAIN][DATA_ADGUARD_CLIENT] adguard = hass.data[DOMAIN][entry.entry_id][DATA_ADGUARD_CLIENT]
try: try:
version = await adguard.version() version = await adguard.version()
except AdGuardHomeConnectionError as exception: except AdGuardHomeConnectionError as exception:
raise PlatformNotReady from exception raise PlatformNotReady from exception
hass.data[DOMAIN][DATA_ADGUARD_VERION] = version hass.data[DOMAIN][entry.entry_id][DATA_ADGUARD_VERSION] = version
switches = [ switches = [
AdGuardHomeProtectionSwitch(adguard), AdGuardHomeProtectionSwitch(adguard, entry),
AdGuardHomeFilteringSwitch(adguard), AdGuardHomeFilteringSwitch(adguard, entry),
AdGuardHomeParentalSwitch(adguard), AdGuardHomeParentalSwitch(adguard, entry),
AdGuardHomeSafeBrowsingSwitch(adguard), AdGuardHomeSafeBrowsingSwitch(adguard, entry),
AdGuardHomeSafeSearchSwitch(adguard), AdGuardHomeSafeSearchSwitch(adguard, entry),
AdGuardHomeQueryLogSwitch(adguard), AdGuardHomeQueryLogSwitch(adguard, entry),
] ]
async_add_entities(switches, True) async_add_entities(switches, True)
@ -54,6 +54,7 @@ class AdGuardHomeSwitch(AdGuardHomeDeviceEntity, SwitchEntity):
def __init__( def __init__(
self, self,
adguard: AdGuardHome, adguard: AdGuardHome,
entry: ConfigEntry,
name: str, name: str,
icon: str, icon: str,
key: str, key: str,
@ -62,7 +63,7 @@ class AdGuardHomeSwitch(AdGuardHomeDeviceEntity, SwitchEntity):
"""Initialize AdGuard Home switch.""" """Initialize AdGuard Home switch."""
self._state = False self._state = False
self._key = key self._key = key
super().__init__(adguard, name, icon, enabled_default) super().__init__(adguard, entry, name, icon, enabled_default)
@property @property
def unique_id(self) -> str: def unique_id(self) -> str:
@ -104,10 +105,10 @@ class AdGuardHomeSwitch(AdGuardHomeDeviceEntity, SwitchEntity):
class AdGuardHomeProtectionSwitch(AdGuardHomeSwitch): class AdGuardHomeProtectionSwitch(AdGuardHomeSwitch):
"""Defines a AdGuard Home protection switch.""" """Defines a AdGuard Home protection switch."""
def __init__(self, adguard: AdGuardHome) -> None: def __init__(self, adguard: AdGuardHome, entry: ConfigEntry) -> None:
"""Initialize AdGuard Home switch.""" """Initialize AdGuard Home switch."""
super().__init__( super().__init__(
adguard, "AdGuard Protection", "mdi:shield-check", "protection" adguard, entry, "AdGuard Protection", "mdi:shield-check", "protection"
) )
async def _adguard_turn_off(self) -> None: async def _adguard_turn_off(self) -> None:
@ -126,10 +127,10 @@ class AdGuardHomeProtectionSwitch(AdGuardHomeSwitch):
class AdGuardHomeParentalSwitch(AdGuardHomeSwitch): class AdGuardHomeParentalSwitch(AdGuardHomeSwitch):
"""Defines a AdGuard Home parental control switch.""" """Defines a AdGuard Home parental control switch."""
def __init__(self, adguard: AdGuardHome) -> None: def __init__(self, adguard: AdGuardHome, entry: ConfigEntry) -> None:
"""Initialize AdGuard Home switch.""" """Initialize AdGuard Home switch."""
super().__init__( super().__init__(
adguard, "AdGuard Parental Control", "mdi:shield-check", "parental" adguard, entry, "AdGuard Parental Control", "mdi:shield-check", "parental"
) )
async def _adguard_turn_off(self) -> None: async def _adguard_turn_off(self) -> None:
@ -148,10 +149,10 @@ class AdGuardHomeParentalSwitch(AdGuardHomeSwitch):
class AdGuardHomeSafeSearchSwitch(AdGuardHomeSwitch): class AdGuardHomeSafeSearchSwitch(AdGuardHomeSwitch):
"""Defines a AdGuard Home safe search switch.""" """Defines a AdGuard Home safe search switch."""
def __init__(self, adguard: AdGuardHome) -> None: def __init__(self, adguard: AdGuardHome, entry: ConfigEntry) -> None:
"""Initialize AdGuard Home switch.""" """Initialize AdGuard Home switch."""
super().__init__( super().__init__(
adguard, "AdGuard Safe Search", "mdi:shield-check", "safesearch" adguard, entry, "AdGuard Safe Search", "mdi:shield-check", "safesearch"
) )
async def _adguard_turn_off(self) -> None: async def _adguard_turn_off(self) -> None:
@ -170,10 +171,10 @@ class AdGuardHomeSafeSearchSwitch(AdGuardHomeSwitch):
class AdGuardHomeSafeBrowsingSwitch(AdGuardHomeSwitch): class AdGuardHomeSafeBrowsingSwitch(AdGuardHomeSwitch):
"""Defines a AdGuard Home safe search switch.""" """Defines a AdGuard Home safe search switch."""
def __init__(self, adguard: AdGuardHome) -> None: def __init__(self, adguard: AdGuardHome, entry: ConfigEntry) -> None:
"""Initialize AdGuard Home switch.""" """Initialize AdGuard Home switch."""
super().__init__( super().__init__(
adguard, "AdGuard Safe Browsing", "mdi:shield-check", "safebrowsing" adguard, entry, "AdGuard Safe Browsing", "mdi:shield-check", "safebrowsing"
) )
async def _adguard_turn_off(self) -> None: async def _adguard_turn_off(self) -> None:
@ -192,9 +193,11 @@ class AdGuardHomeSafeBrowsingSwitch(AdGuardHomeSwitch):
class AdGuardHomeFilteringSwitch(AdGuardHomeSwitch): class AdGuardHomeFilteringSwitch(AdGuardHomeSwitch):
"""Defines a AdGuard Home filtering switch.""" """Defines a AdGuard Home filtering switch."""
def __init__(self, adguard: AdGuardHome) -> None: def __init__(self, adguard: AdGuardHome, entry: ConfigEntry) -> None:
"""Initialize AdGuard Home switch.""" """Initialize AdGuard Home switch."""
super().__init__(adguard, "AdGuard Filtering", "mdi:shield-check", "filtering") super().__init__(
adguard, entry, "AdGuard Filtering", "mdi:shield-check", "filtering"
)
async def _adguard_turn_off(self) -> None: async def _adguard_turn_off(self) -> None:
"""Turn off the switch.""" """Turn off the switch."""
@ -212,10 +215,11 @@ class AdGuardHomeFilteringSwitch(AdGuardHomeSwitch):
class AdGuardHomeQueryLogSwitch(AdGuardHomeSwitch): class AdGuardHomeQueryLogSwitch(AdGuardHomeSwitch):
"""Defines a AdGuard Home query log switch.""" """Defines a AdGuard Home query log switch."""
def __init__(self, adguard: AdGuardHome) -> None: def __init__(self, adguard: AdGuardHome, entry: ConfigEntry) -> None:
"""Initialize AdGuard Home switch.""" """Initialize AdGuard Home switch."""
super().__init__( super().__init__(
adguard, adguard,
entry,
"AdGuard Query Log", "AdGuard Query Log",
"mdi:shield-check", "mdi:shield-check",
"querylog", "querylog",

View File

@ -1,6 +1,7 @@
{ {
"config": { "config": {
"abort": { "abort": {
"already_configured": "El servei ja est\u00e0 configurat",
"existing_instance_updated": "S'ha actualitzat la configuraci\u00f3 existent.", "existing_instance_updated": "S'ha actualitzat la configuraci\u00f3 existent.",
"single_instance_allowed": "Ja configurat. Nom\u00e9s \u00e9s possible una sola configuraci\u00f3." "single_instance_allowed": "Ja configurat. Nom\u00e9s \u00e9s possible una sola configuraci\u00f3."
}, },
@ -9,8 +10,8 @@
}, },
"step": { "step": {
"hassio_confirm": { "hassio_confirm": {
"description": "Vols configurar Home Assistant perqu\u00e8 es connecti amb l'AdGuard Home proporcionat pel complement de Hass.io: {addon}?", "description": "Vols configurar Home Assistant perqu\u00e8 es connecti amb l'AdGuard Home proporcionat pel complement: {addon}?",
"title": "AdGuard Home via complement de Hass.io" "title": "AdGuard Home via complement de Home Assistant"
}, },
"user": { "user": {
"data": { "data": {

View File

@ -1,6 +1,7 @@
{ {
"config": { "config": {
"abort": { "abort": {
"already_configured": "Slu\u017eba je ji\u017e nastavena",
"existing_instance_updated": "St\u00e1vaj\u00edc\u00ed nastaven\u00ed aktualizov\u00e1no.", "existing_instance_updated": "St\u00e1vaj\u00edc\u00ed nastaven\u00ed aktualizov\u00e1no.",
"single_instance_allowed": "Ji\u017e nastaveno. Je mo\u017en\u00e1 pouze jedin\u00e1 konfigurace." "single_instance_allowed": "Ji\u017e nastaveno. Je mo\u017en\u00e1 pouze jedin\u00e1 konfigurace."
}, },

View File

@ -1,6 +1,7 @@
{ {
"config": { "config": {
"abort": { "abort": {
"already_configured": "Service is already configured",
"existing_instance_updated": "Updated existing configuration.", "existing_instance_updated": "Updated existing configuration.",
"single_instance_allowed": "Already configured. Only a single configuration possible." "single_instance_allowed": "Already configured. Only a single configuration possible."
}, },
@ -9,8 +10,8 @@
}, },
"step": { "step": {
"hassio_confirm": { "hassio_confirm": {
"description": "Do you want to configure Home Assistant to connect to the AdGuard Home provided by the Hass.io add-on: {addon}?", "description": "Do you want to configure Home Assistant to connect to the AdGuard Home provided by the add-on: {addon}?",
"title": "AdGuard Home via Hass.io add-on" "title": "AdGuard Home via Home Assistant add-on"
}, },
"user": { "user": {
"data": { "data": {

View File

@ -1,6 +1,7 @@
{ {
"config": { "config": {
"abort": { "abort": {
"already_configured": "El servicio ya est\u00e1 configurado",
"existing_instance_updated": "Se ha actualizado la configuraci\u00f3n existente.", "existing_instance_updated": "Se ha actualizado la configuraci\u00f3n existente.",
"single_instance_allowed": "S\u00f3lo se permite una \u00fanica configuraci\u00f3n de AdGuard Home." "single_instance_allowed": "S\u00f3lo se permite una \u00fanica configuraci\u00f3n de AdGuard Home."
}, },

View File

@ -1,6 +1,7 @@
{ {
"config": { "config": {
"abort": { "abort": {
"already_configured": "Teenus on juba seadistatud",
"existing_instance_updated": "Olemasolevad seaded v\u00e4rskendatud.", "existing_instance_updated": "Olemasolevad seaded v\u00e4rskendatud.",
"single_instance_allowed": "Juba seadistatud. V\u00f5imalik on ainult \u00fcks seadistamine." "single_instance_allowed": "Juba seadistatud. V\u00f5imalik on ainult \u00fcks seadistamine."
}, },
@ -9,8 +10,8 @@
}, },
"step": { "step": {
"hassio_confirm": { "hassio_confirm": {
"description": "Kas soovid seadistada Home Assistant-i \u00fchenduse AdGuard Home'iga mida pakub Hass.io lisandmoodul: {addon} ?", "description": "Kas soovid seadistada Home Assistant-i \u00fchenduse AdGuard Home'iga mida pakub lisandmoodul: {addon} ?",
"title": "AdGuard Home Hass.io lisandmooduli abil" "title": "AdGuard Home Home Assistanti lisandmooduli abil"
}, },
"user": { "user": {
"data": { "data": {

View File

@ -1,6 +1,7 @@
{ {
"config": { "config": {
"abort": { "abort": {
"already_configured": "Il servizio \u00e8 gi\u00e0 configurato",
"existing_instance_updated": "Configurazione esistente aggiornata.", "existing_instance_updated": "Configurazione esistente aggiornata.",
"single_instance_allowed": "Gi\u00e0 configurato. \u00c8 possibile una sola configurazione." "single_instance_allowed": "Gi\u00e0 configurato. \u00c8 possibile una sola configurazione."
}, },
@ -9,8 +10,8 @@
}, },
"step": { "step": {
"hassio_confirm": { "hassio_confirm": {
"description": "Vuoi configurare Home Assistant per connettersi ad AdGuard Home fornito dal componente aggiuntivo di Hass.io: {addon}?", "description": "Vuoi configurare Home Assistant per connettersi ad AdGuard Home fornito dal componente aggiuntivo: {addon}?",
"title": "AdGuard Home tramite il componente aggiuntivo di Hass.io" "title": "AdGuard Home tramite il componente aggiuntivo di Home Assistant"
}, },
"user": { "user": {
"data": { "data": {

View File

@ -1,6 +1,7 @@
{ {
"config": { "config": {
"abort": { "abort": {
"already_configured": "\uc11c\ube44\uc2a4\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4",
"existing_instance_updated": "\uae30\uc874 \uad6c\uc131\uc744 \uc5c5\ub370\uc774\ud2b8\ud588\uc2b5\ub2c8\ub2e4.", "existing_instance_updated": "\uae30\uc874 \uad6c\uc131\uc744 \uc5c5\ub370\uc774\ud2b8\ud588\uc2b5\ub2c8\ub2e4.",
"single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4."
}, },
@ -9,8 +10,8 @@
}, },
"step": { "step": {
"hassio_confirm": { "hassio_confirm": {
"description": "Hass.io {addon} \uc560\ub4dc\uc628\uc73c\ub85c AdGuard Home\uc5d0 \uc5f0\uacb0\ud558\ub3c4\ub85d Home Assistant\ub97c \uad6c\uc131\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?", "description": "{addon} \uc560\ub4dc\uc628\uc5d0\uc11c \uc81c\uacf5\ud558\ub294 AdGuard Home\uc5d0 \uc5f0\uacb0\ud558\ub3c4\ub85d Home Assistant\ub97c \uad6c\uc131\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?",
"title": "Hass.io \uc560\ub4dc\uc628\uc758 AdGuard Home" "title": "Home Assistant \uc560\ub4dc\uc628\uc758 AdGuard Home"
}, },
"user": { "user": {
"data": { "data": {

View File

@ -1,6 +1,7 @@
{ {
"config": { "config": {
"abort": { "abort": {
"already_configured": "Service is al geconfigureerd",
"existing_instance_updated": "Bestaande configuratie bijgewerkt.", "existing_instance_updated": "Bestaande configuratie bijgewerkt.",
"single_instance_allowed": "Slechts \u00e9\u00e9n configuratie van AdGuard Home is toegestaan." "single_instance_allowed": "Slechts \u00e9\u00e9n configuratie van AdGuard Home is toegestaan."
}, },
@ -9,8 +10,8 @@
}, },
"step": { "step": {
"hassio_confirm": { "hassio_confirm": {
"description": "Wilt u Home Assistant configureren om verbinding te maken met AdGuard Home van de Supervisor-add-on: {addon}?", "description": "Wilt u Home Assistant configureren om verbinding te maken met AdGuard Home van de Home Assistant add-on: {addon}?",
"title": "AdGuard Home via Supervisor add-on" "title": "AdGuard Home via Home Assistant add-on"
}, },
"user": { "user": {
"data": { "data": {

View File

@ -1,6 +1,7 @@
{ {
"config": { "config": {
"abort": { "abort": {
"already_configured": "Tjenesten er allerede konfigurert",
"existing_instance_updated": "Oppdatert eksisterende konfigurasjon.", "existing_instance_updated": "Oppdatert eksisterende konfigurasjon.",
"single_instance_allowed": "Allerede konfigurert. Bare \u00e9n enkelt konfigurasjon er mulig." "single_instance_allowed": "Allerede konfigurert. Bare \u00e9n enkelt konfigurasjon er mulig."
}, },
@ -9,8 +10,8 @@
}, },
"step": { "step": {
"hassio_confirm": { "hassio_confirm": {
"description": "Vil du konfigurere Home Assistant for \u00e5 koble til AdGuard Home levert av Hass.io-tillegget: {addon} ?", "description": "Vil du konfigurere Home Assistant til \u00e5 koble til AdGuard Home levert av tillegget: {addon} ?",
"title": "AdGuard Home via Hass.io-tillegg" "title": "AdGuard Home via Home Assistant-tillegg"
}, },
"user": { "user": {
"data": { "data": {

View File

@ -1,6 +1,7 @@
{ {
"config": { "config": {
"abort": { "abort": {
"already_configured": "Us\u0142uga jest ju\u017c skonfigurowana",
"existing_instance_updated": "Zaktualizowano istniej\u0105c\u0105 konfiguracj\u0119", "existing_instance_updated": "Zaktualizowano istniej\u0105c\u0105 konfiguracj\u0119",
"single_instance_allowed": "Ju\u017c skonfigurowano. Mo\u017cliwa jest tylko jedna konfiguracja." "single_instance_allowed": "Ju\u017c skonfigurowano. Mo\u017cliwa jest tylko jedna konfiguracja."
}, },
@ -9,8 +10,8 @@
}, },
"step": { "step": {
"hassio_confirm": { "hassio_confirm": {
"description": "Czy chcesz skonfigurowa\u0107 Home Assistanta, aby po\u0142\u0105czy\u0142 si\u0119 z AdGuard Home przez dodatek Hass.io: {addon}?", "description": "Czy chcesz skonfigurowa\u0107 Home Assistanta, aby po\u0142\u0105czy\u0142 si\u0119 z AdGuard Home przez dodatek {addon}?",
"title": "AdGuard Home przez dodatek Hass.io" "title": "AdGuard Home przez dodatek Home Assistant"
}, },
"user": { "user": {
"data": { "data": {

View File

@ -1,6 +1,7 @@
{ {
"config": { "config": {
"abort": { "abort": {
"already_configured": "\u042d\u0442\u0430 \u0441\u043b\u0443\u0436\u0431\u0430 \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 Home Assistant.",
"existing_instance_updated": "\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0430.", "existing_instance_updated": "\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0430.",
"single_instance_allowed": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u0443 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e." "single_instance_allowed": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u0443 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e."
}, },
@ -9,7 +10,7 @@
}, },
"step": { "step": {
"hassio_confirm": { "hassio_confirm": {
"description": "\u0412\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a AdGuard Home (\u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0434\u043b\u044f Home Assistant \"{addon}\")?", "description": "\u0412\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a AdGuard Home (\u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \"{addon}\")?",
"title": "AdGuard Home (\u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0434\u043b\u044f Home Assistant)" "title": "AdGuard Home (\u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0434\u043b\u044f Home Assistant)"
}, },
"user": { "user": {

View File

@ -1,6 +1,7 @@
{ {
"config": { "config": {
"abort": { "abort": {
"already_configured": "\u670d\u52d9\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210",
"existing_instance_updated": "\u5df2\u66f4\u65b0\u73fe\u6709\u8a2d\u5b9a\u3002", "existing_instance_updated": "\u5df2\u66f4\u65b0\u73fe\u6709\u8a2d\u5b9a\u3002",
"single_instance_allowed": "\u50c5\u80fd\u8a2d\u5b9a\u4e00\u7d44\u88dd\u7f6e\u3002" "single_instance_allowed": "\u50c5\u80fd\u8a2d\u5b9a\u4e00\u7d44\u88dd\u7f6e\u3002"
}, },
@ -9,8 +10,8 @@
}, },
"step": { "step": {
"hassio_confirm": { "hassio_confirm": {
"description": "\u662f\u5426\u8981\u8a2d\u5b9a Home Assistant \u4ee5\u4f7f\u7528 Hass.io \u9644\u52a0\u5143\u4ef6\uff1a{addon} \u9023\u7dda\u81f3 AdGuard Home\uff1f", "description": "\u662f\u5426\u8981\u8a2d\u5b9a Home Assistant \u4ee5\u9023\u7dda\u81f3 AdGuard Home\u3002\u9644\u52a0\u5143\u4ef6\u70ba\uff1a{addon} \uff1f",
"title": "\u4f7f\u7528 Hass.io \u9644\u52a0\u5143\u4ef6 AdGuard Home" "title": "\u4f7f\u7528 Home Assistant \u9644\u52a0\u5143\u4ef6 AdGuard Home"
}, },
"user": { "user": {
"data": { "data": {

View File

@ -3,5 +3,6 @@
"name": "ADS", "name": "ADS",
"documentation": "https://www.home-assistant.io/integrations/ads", "documentation": "https://www.home-assistant.io/integrations/ads",
"requirements": ["pyads==3.2.2"], "requirements": ["pyads==3.2.2"],
"codeowners": [] "codeowners": [],
"iot_class": "local_push"
} }

View File

@ -1,6 +1,5 @@
"""Advantage Air climate integration.""" """Advantage Air climate integration."""
import asyncio
from datetime import timedelta from datetime import timedelta
import logging import logging
@ -58,24 +57,14 @@ async def async_setup_entry(hass, entry):
"async_change": async_change, "async_change": async_change,
} }
for platform in PLATFORMS: hass.config_entries.async_setup_platforms(entry, PLATFORMS)
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, platform)
)
return True return True
async def async_unload_entry(hass, entry): async def async_unload_entry(hass, entry):
"""Unload Advantage Air Config.""" """Unload Advantage Air Config."""
unload_ok = all( unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
await asyncio.gather(
*[
hass.config_entries.async_forward_entry_unload(entry, platform)
for platform in PLATFORMS
]
)
)
if unload_ok: if unload_ok:
hass.data[DOMAIN].pop(entry.entry_id) hass.data[DOMAIN].pop(entry.entry_id)

View File

@ -24,6 +24,9 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
# Only add motion sensor when motion is enabled # Only add motion sensor when motion is enabled
if zone["motionConfig"] >= 2: if zone["motionConfig"] >= 2:
entities.append(AdvantageAirZoneMotion(instance, ac_key, zone_key)) entities.append(AdvantageAirZoneMotion(instance, ac_key, zone_key))
# Only add MyZone if it is available
if zone["type"] != 0:
entities.append(AdvantageAirZoneMyZone(instance, ac_key, zone_key))
async_add_entities(entities) async_add_entities(entities)
@ -73,3 +76,27 @@ class AdvantageAirZoneMotion(AdvantageAirEntity, BinarySensorEntity):
def is_on(self): def is_on(self):
"""Return if motion is detect.""" """Return if motion is detect."""
return self._zone["motion"] return self._zone["motion"]
class AdvantageAirZoneMyZone(AdvantageAirEntity, BinarySensorEntity):
"""Advantage Air Zone MyZone."""
@property
def name(self):
"""Return the name."""
return f'{self._zone["name"]} MyZone'
@property
def unique_id(self):
"""Return a unique id."""
return f'{self.coordinator.data["system"]["rid"]}-{self.ac_key}-{self.zone_key}-myzone'
@property
def is_on(self):
"""Return if this zone is the myZone."""
return self._zone["number"] == self._ac["myZone"]
@property
def entity_registry_enabled_default(self):
"""Return false to disable this entity by default."""
return False

View File

@ -15,6 +15,7 @@ from homeassistant.components.climate.const import (
SUPPORT_TARGET_TEMPERATURE, SUPPORT_TARGET_TEMPERATURE,
) )
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, TEMP_CELSIUS from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, TEMP_CELSIUS
from homeassistant.helpers import entity_platform
from .const import ( from .const import (
ADVANTAGE_AIR_STATE_CLOSE, ADVANTAGE_AIR_STATE_CLOSE,
@ -49,6 +50,7 @@ AC_HVAC_MODES = [
HVAC_MODE_FAN_ONLY, HVAC_MODE_FAN_ONLY,
HVAC_MODE_DRY, HVAC_MODE_DRY,
] ]
ADVANTAGE_AIR_SERVICE_SET_MYZONE = "set_myzone"
ZONE_HVAC_MODES = [HVAC_MODE_OFF, HVAC_MODE_FAN_ONLY] ZONE_HVAC_MODES = [HVAC_MODE_OFF, HVAC_MODE_FAN_ONLY]
PARALLEL_UPDATES = 0 PARALLEL_UPDATES = 0
@ -68,6 +70,13 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
entities.append(AdvantageAirZone(instance, ac_key, zone_key)) entities.append(AdvantageAirZone(instance, ac_key, zone_key))
async_add_entities(entities) async_add_entities(entities)
platform = entity_platform.current_platform.get()
platform.async_register_entity_service(
ADVANTAGE_AIR_SERVICE_SET_MYZONE,
{},
"set_myzone",
)
class AdvantageAirClimateEntity(AdvantageAirEntity, ClimateEntity): class AdvantageAirClimateEntity(AdvantageAirEntity, ClimateEntity):
"""AdvantageAir Climate class.""" """AdvantageAir Climate class."""
@ -233,3 +242,9 @@ class AdvantageAirZone(AdvantageAirClimateEntity):
await self.async_change( await self.async_change(
{self.ac_key: {"zones": {self.zone_key: {"setTemp": temp}}}} {self.ac_key: {"zones": {self.zone_key: {"setTemp": temp}}}}
) )
async def set_myzone(self, **kwargs):
"""Set this zone as the 'MyZone'."""
await self.async_change(
{self.ac_key: {"info": {"myZone": self._zone["number"]}}}
)

View File

@ -5,5 +5,6 @@
"documentation": "https://www.home-assistant.io/integrations/advantage_air", "documentation": "https://www.home-assistant.io/integrations/advantage_air",
"codeowners": ["@Bre77"], "codeowners": ["@Bre77"],
"requirements": ["advantage_air==0.2.1"], "requirements": ["advantage_air==0.2.1"],
"quality_scale": "platinum" "quality_scale": "platinum",
"iot_class": "local_polling"
} }

View File

@ -1,9 +1,18 @@
set_time_to: set_time_to:
name: Set Time To
description: Control timers to turn the system on or off after a set number of minutes description: Control timers to turn the system on or off after a set number of minutes
target:
entity:
integration: advantage_air
domain: sensor
fields: fields:
entity_id:
description: Time To sensor entity
example: "sensor.ac_time_to_on"
minutes: minutes:
description: Minutes until action description: Minutes until action
example: "60" example: "60"
set_myzone:
name: Set MyZone
description: Change which zone is set as the reference for temperature control
target:
entity:
integration: advantage_air
domain: climate

View File

@ -1,7 +1,7 @@
{ {
"config": { "config": {
"abort": { "abort": {
"already_configured": "\u88dd\u7f6e\u7d93\u8a2d\u5b9a\u5b8c\u6210" "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210"
}, },
"error": { "error": {
"cannot_connect": "\u9023\u7dda\u5931\u6557" "cannot_connect": "\u9023\u7dda\u5931\u6557"

View File

@ -1,5 +1,4 @@
"""The AEMET OpenData component.""" """The AEMET OpenData component."""
import asyncio
import logging import logging
from aemet_opendata.interface import AEMET from aemet_opendata.interface import AEMET
@ -32,24 +31,17 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry):
ENTRY_WEATHER_COORDINATOR: weather_coordinator, ENTRY_WEATHER_COORDINATOR: weather_coordinator,
} }
for platform in PLATFORMS: hass.config_entries.async_setup_platforms(config_entry, PLATFORMS)
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(config_entry, platform)
)
return True return True
async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry): async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry):
"""Unload a config entry.""" """Unload a config entry."""
unload_ok = all( unload_ok = await hass.config_entries.async_unload_platforms(
await asyncio.gather( config_entry, PLATFORMS
*[
hass.config_entries.async_forward_entry_unload(config_entry, platform)
for platform in PLATFORMS
]
)
) )
if unload_ok: if unload_ok:
hass.data[DOMAIN].pop(config_entry.entry_id) hass.data[DOMAIN].pop(config_entry.entry_id)

View File

@ -4,5 +4,6 @@
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/aemet", "documentation": "https://www.home-assistant.io/integrations/aemet",
"requirements": ["AEMET-OpenData==0.1.8"], "requirements": ["AEMET-OpenData==0.1.8"],
"codeowners": ["@noltari"] "codeowners": ["@noltari"],
"iot_class": "cloud_polling"
} }

View File

@ -0,0 +1,12 @@
{
"config": {
"step": {
"user": {
"data": {
"name": "Nombre de la integraci\u00f3n"
},
"title": "AEMET OpenData"
}
}
}
}

View File

@ -239,7 +239,7 @@ class WeatherUpdateCoordinator(DataUpdateCoordinator):
return None return None
elaborated = dt_util.parse_datetime( elaborated = dt_util.parse_datetime(
weather_response.hourly[ATTR_DATA][0][AEMET_ATTR_ELABORATED] weather_response.hourly[ATTR_DATA][0][AEMET_ATTR_ELABORATED] + "Z"
) )
now = dt_util.now() now = dt_util.now()
now_utc = dt_util.utcnow() now_utc = dt_util.utcnow()

View File

@ -3,5 +3,6 @@
"name": "AfterShip", "name": "AfterShip",
"documentation": "https://www.home-assistant.io/integrations/aftership", "documentation": "https://www.home-assistant.io/integrations/aftership",
"requirements": ["pyaftership==0.1.2"], "requirements": ["pyaftership==0.1.2"],
"codeowners": [] "codeowners": [],
"iot_class": "cloud_polling"
} }

View File

@ -1,5 +1,4 @@
"""Support for Agent.""" """Support for Agent."""
import asyncio
from agent import AgentError from agent import AgentError
from agent.a import Agent from agent.a import Agent
@ -47,24 +46,14 @@ async def async_setup_entry(hass, config_entry):
sw_version=agent_client.version, sw_version=agent_client.version,
) )
for forward in FORWARDS: hass.config_entries.async_setup_platforms(config_entry, FORWARDS)
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(config_entry, forward)
)
return True return True
async def async_unload_entry(hass, config_entry): async def async_unload_entry(hass, config_entry):
"""Unload a config entry.""" """Unload a config entry."""
unload_ok = all( unload_ok = await hass.config_entries.async_unload_platforms(config_entry, FORWARDS)
await asyncio.gather(
*[
hass.config_entries.async_forward_entry_unload(config_entry, forward)
for forward in FORWARDS
]
)
)
await hass.data[AGENT_DOMAIN][config_entry.entry_id][CONNECTION].close() await hass.data[AGENT_DOMAIN][config_entry.entry_id][CONNECTION].close()

View File

@ -4,5 +4,6 @@
"documentation": "https://www.home-assistant.io/integrations/agent_dvr/", "documentation": "https://www.home-assistant.io/integrations/agent_dvr/",
"requirements": ["agent-py==0.0.23"], "requirements": ["agent-py==0.0.23"],
"config_flow": true, "config_flow": true,
"codeowners": ["@ispysoftware"] "codeowners": ["@ispysoftware"],
"iot_class": "local_polling"
} }

View File

@ -1,7 +1,7 @@
{ {
"config": { "config": {
"abort": { "abort": {
"already_configured": "\u88dd\u7f6e\u7d93\u8a2d\u5b9a\u5b8c\u6210" "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210"
}, },
"error": { "error": {
"already_in_progress": "\u8a2d\u5b9a\u5df2\u7d93\u9032\u884c\u4e2d", "already_in_progress": "\u8a2d\u5b9a\u5df2\u7d93\u9032\u884c\u4e2d",

View File

@ -2,5 +2,6 @@
"domain": "air_quality", "domain": "air_quality",
"name": "Air Quality", "name": "Air Quality",
"documentation": "https://www.home-assistant.io/integrations/air_quality", "documentation": "https://www.home-assistant.io/integrations/air_quality",
"codeowners": [] "codeowners": [],
"quality_scale": "internal"
} }

View File

@ -1,5 +1,4 @@
"""The Airly integration.""" """The Airly integration."""
import asyncio
from datetime import timedelta from datetime import timedelta
import logging import logging
from math import ceil from math import ceil
@ -12,6 +11,7 @@ import async_timeout
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from homeassistant.util import dt as dt_util
from .const import ( from .const import (
ATTR_API_ADVICE, ATTR_API_ADVICE,
@ -20,7 +20,8 @@ from .const import (
ATTR_API_CAQI_LEVEL, ATTR_API_CAQI_LEVEL,
CONF_USE_NEAREST, CONF_USE_NEAREST,
DOMAIN, DOMAIN,
MAX_REQUESTS_PER_DAY, MAX_UPDATE_INTERVAL,
MIN_UPDATE_INTERVAL,
NO_AIRLY_SENSORS, NO_AIRLY_SENSORS,
) )
@ -29,15 +30,30 @@ PLATFORMS = ["air_quality", "sensor"]
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
def set_update_interval(hass, instances): def set_update_interval(instances, requests_remaining):
"""Set update_interval to another configured Airly instances.""" """
# We check how many Airly configured instances are and calculate interval to not Return data update interval.
# exceed allowed numbers of requests.
interval = timedelta(minutes=ceil(24 * 60 / MAX_REQUESTS_PER_DAY) * instances)
if hass.data.get(DOMAIN): The number of requests is reset at midnight UTC so we calculate the update
for instance in hass.data[DOMAIN].values(): interval based on number of minutes until midnight, the number of Airly instances
instance.update_interval = interval and the number of remaining requests.
"""
now = dt_util.utcnow()
midnight = dt_util.find_next_time_expression_time(
now, seconds=[0], minutes=[0], hours=[0]
)
minutes_to_midnight = (midnight - now).total_seconds() / 60
interval = timedelta(
minutes=min(
max(
ceil(minutes_to_midnight / requests_remaining * instances),
MIN_UPDATE_INTERVAL,
),
MAX_UPDATE_INTERVAL,
)
)
_LOGGER.debug("Data will be update every %s", interval)
return interval return interval
@ -56,10 +72,8 @@ async def async_setup_entry(hass, config_entry):
) )
websession = async_get_clientsession(hass) websession = async_get_clientsession(hass)
# Change update_interval for other Airly instances
update_interval = set_update_interval( update_interval = timedelta(minutes=MIN_UPDATE_INTERVAL)
hass, len(hass.config_entries.async_entries(DOMAIN))
)
coordinator = AirlyDataUpdateCoordinator( coordinator = AirlyDataUpdateCoordinator(
hass, websession, api_key, latitude, longitude, update_interval, use_nearest hass, websession, api_key, latitude, longitude, update_interval, use_nearest
@ -69,30 +83,20 @@ async def async_setup_entry(hass, config_entry):
hass.data.setdefault(DOMAIN, {}) hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][config_entry.entry_id] = coordinator hass.data[DOMAIN][config_entry.entry_id] = coordinator
for platform in PLATFORMS: hass.config_entries.async_setup_platforms(config_entry, PLATFORMS)
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(config_entry, platform)
)
return True return True
async def async_unload_entry(hass, config_entry): async def async_unload_entry(hass, config_entry):
"""Unload a config entry.""" """Unload a config entry."""
unload_ok = all( unload_ok = await hass.config_entries.async_unload_platforms(
await asyncio.gather( config_entry, PLATFORMS
*[
hass.config_entries.async_forward_entry_unload(config_entry, platform)
for platform in PLATFORMS
]
)
) )
if unload_ok: if unload_ok:
hass.data[DOMAIN].pop(config_entry.entry_id) hass.data[DOMAIN].pop(config_entry.entry_id)
# Change update_interval for other Airly instances
set_update_interval(hass, len(hass.data[DOMAIN]))
return unload_ok return unload_ok
@ -140,6 +144,14 @@ class AirlyDataUpdateCoordinator(DataUpdateCoordinator):
self.airly.requests_per_day, self.airly.requests_per_day,
) )
# Airly API sometimes returns None for requests remaining so we update
# update_interval only if we have valid value.
if self.airly.requests_remaining:
self.update_interval = set_update_interval(
len(self.hass.config_entries.async_entries(DOMAIN)),
self.airly.requests_remaining,
)
values = measurements.current["values"] values = measurements.current["values"]
index = measurements.current["indexes"][0] index = measurements.current["indexes"][0]
standards = measurements.current["standards"] standards = measurements.current["standards"]

View File

@ -24,5 +24,6 @@ DEFAULT_NAME = "Airly"
DOMAIN = "airly" DOMAIN = "airly"
LABEL_ADVICE = "advice" LABEL_ADVICE = "advice"
MANUFACTURER = "Airly sp. z o.o." MANUFACTURER = "Airly sp. z o.o."
MAX_REQUESTS_PER_DAY = 100 MAX_UPDATE_INTERVAL = 90
MIN_UPDATE_INTERVAL = 5
NO_AIRLY_SENSORS = "There are no Airly sensors in this area yet." NO_AIRLY_SENSORS = "There are no Airly sensors in this area yet."

View File

@ -5,5 +5,6 @@
"codeowners": ["@bieniu"], "codeowners": ["@bieniu"],
"requirements": ["airly==1.1.0"], "requirements": ["airly==1.1.0"],
"config_flow": true, "config_flow": true,
"quality_scale": "platinum" "quality_scale": "platinum",
"iot_class": "cloud_polling"
} }

View File

@ -18,5 +18,11 @@
"title": "Airly" "title": "Airly"
} }
} }
},
"system_health": {
"info": {
"requests_per_day": "Solicitudes permitidas por d\u00eda",
"requests_remaining": "Solicitudes permitidas restantes"
}
} }
} }

View File

@ -1,5 +1,4 @@
"""The AirNow integration.""" """The AirNow integration."""
import asyncio
import datetime import datetime
import logging import logging
@ -60,24 +59,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
hass.data.setdefault(DOMAIN, {}) hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][entry.entry_id] = coordinator hass.data[DOMAIN][entry.entry_id] = coordinator
for platform in PLATFORMS: hass.config_entries.async_setup_platforms(entry, PLATFORMS)
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, platform)
)
return True return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry): async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Unload a config entry.""" """Unload a config entry."""
unload_ok = all( unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
await asyncio.gather(
*[
hass.config_entries.async_forward_entry_unload(entry, platform)
for platform in PLATFORMS
]
)
)
if unload_ok: if unload_ok:
hass.data[DOMAIN].pop(entry.entry_id) hass.data[DOMAIN].pop(entry.entry_id)

View File

@ -3,10 +3,7 @@
"name": "AirNow", "name": "AirNow",
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/airnow", "documentation": "https://www.home-assistant.io/integrations/airnow",
"requirements": [ "requirements": ["pyairnow==1.1.0"],
"pyairnow==1.1.0" "codeowners": ["@asymworks"],
], "iot_class": "cloud_polling"
"codeowners": [
"@asymworks"
]
} }

View File

@ -0,0 +1,17 @@
{
"config": {
"error": {
"invalid_location": "No se encontraron resultados para esa ubicaci\u00f3n"
},
"step": {
"user": {
"data": {
"radius": "Radio de la estaci\u00f3n (millas; opcional)"
},
"description": "Configure la integraci\u00f3n de la calidad del aire de AirNow. Para generar la clave de API, vaya a https://docs.airnowapi.org/account/request/",
"title": "AirNow"
}
}
},
"title": "AirNow"
}

View File

@ -1,7 +1,7 @@
{ {
"config": { "config": {
"abort": { "abort": {
"already_configured": "\u88dd\u7f6e\u7d93\u8a2d\u5b9a\u5b8c\u6210" "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210"
}, },
"error": { "error": {
"cannot_connect": "\u9023\u7dda\u5931\u6557", "cannot_connect": "\u9023\u7dda\u5931\u6557",

View File

@ -1,5 +1,4 @@
"""The airvisual component.""" """The airvisual component."""
import asyncio
from datetime import timedelta from datetime import timedelta
from math import ceil from math import ceil
@ -11,7 +10,6 @@ from pyairvisual.errors import (
NodeProError, NodeProError,
) )
from homeassistant.config_entries import SOURCE_REAUTH
from homeassistant.const import ( from homeassistant.const import (
ATTR_ATTRIBUTION, ATTR_ATTRIBUTION,
CONF_API_KEY, CONF_API_KEY,
@ -23,6 +21,7 @@ from homeassistant.const import (
CONF_STATE, CONF_STATE,
) )
from homeassistant.core import callback from homeassistant.core import callback
from homeassistant.exceptions import ConfigEntryAuthFailed
from homeassistant.helpers import aiohttp_client, config_validation as cv from homeassistant.helpers import aiohttp_client, config_validation as cv
from homeassistant.helpers.update_coordinator import ( from homeassistant.helpers.update_coordinator import (
CoordinatorEntity, CoordinatorEntity,
@ -206,27 +205,8 @@ async def async_setup_entry(hass, config_entry):
try: try:
return await api_coro return await api_coro
except (InvalidKeyError, KeyExpiredError): except (InvalidKeyError, KeyExpiredError) as ex:
matching_flows = [ raise ConfigEntryAuthFailed from ex
flow
for flow in hass.config_entries.flow.async_progress()
if flow["context"]["source"] == SOURCE_REAUTH
and flow["context"]["unique_id"] == config_entry.unique_id
]
if not matching_flows:
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN,
context={
"source": SOURCE_REAUTH,
"unique_id": config_entry.unique_id,
},
data=config_entry.data,
)
)
return {}
except AirVisualError as err: except AirVisualError as err:
raise UpdateFailed(f"Error while retrieving data: {err}") from err raise UpdateFailed(f"Error while retrieving data: {err}") from err
@ -277,10 +257,7 @@ async def async_setup_entry(hass, config_entry):
hass, config_entry.data[CONF_API_KEY] hass, config_entry.data[CONF_API_KEY]
) )
for platform in PLATFORMS: hass.config_entries.async_setup_platforms(config_entry, PLATFORMS)
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(config_entry, platform)
)
return True return True
@ -329,14 +306,10 @@ async def async_migrate_entry(hass, config_entry):
async def async_unload_entry(hass, config_entry): async def async_unload_entry(hass, config_entry):
"""Unload an AirVisual config entry.""" """Unload an AirVisual config entry."""
unload_ok = all( unload_ok = await hass.config_entries.async_unload_platforms(
await asyncio.gather( config_entry, PLATFORMS
*[
hass.config_entries.async_forward_entry_unload(config_entry, platform)
for platform in PLATFORMS
]
)
) )
if unload_ok: if unload_ok:
hass.data[DOMAIN][DATA_COORDINATOR].pop(config_entry.entry_id) hass.data[DOMAIN][DATA_COORDINATOR].pop(config_entry.entry_id)
remove_listener = hass.data[DOMAIN][DATA_LISTENER].pop(config_entry.entry_id) remove_listener = hass.data[DOMAIN][DATA_LISTENER].pop(config_entry.entry_id)

View File

@ -3,6 +3,7 @@
"name": "AirVisual", "name": "AirVisual",
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/airvisual", "documentation": "https://www.home-assistant.io/integrations/airvisual",
"requirements": ["pyairvisual==5.0.4"], "requirements": ["pyairvisual==5.0.8"],
"codeowners": ["@bachya"] "codeowners": ["@bachya"],
"iot_class": "cloud_polling"
} }

View File

@ -5,7 +5,8 @@
}, },
"error": { "error": {
"general_error": "Se ha producido un error desconocido.", "general_error": "Se ha producido un error desconocido.",
"invalid_api_key": "Se proporciona una clave de API no v\u00e1lida." "invalid_api_key": "Se proporciona una clave de API no v\u00e1lida.",
"location_not_found": "Ubicaci\u00f3n no encontrada"
}, },
"step": { "step": {
"geography": { "geography": {
@ -17,6 +18,10 @@
"description": "Use la API de AirVisual para monitorear una ubicaci\u00f3n geogr\u00e1fica.", "description": "Use la API de AirVisual para monitorear una ubicaci\u00f3n geogr\u00e1fica.",
"title": "Configurar una geograf\u00eda" "title": "Configurar una geograf\u00eda"
}, },
"geography_by_coords": {
"description": "Utilice la API en la nube de AirVisual para monitorear una latitud / longitud.",
"title": "Configurar una geograf\u00eda"
},
"node_pro": { "node_pro": {
"data": { "data": {
"ip_address": "Direcci\u00f3n IP/nombre de host de la unidad", "ip_address": "Direcci\u00f3n IP/nombre de host de la unidad",

View File

@ -23,7 +23,8 @@
"geography_by_name": { "geography_by_name": {
"data": { "data": {
"city": "Stad", "city": "Stad",
"country": "Land" "country": "Land",
"state": "Kanton"
} }
}, },
"node_pro": { "node_pro": {

View File

@ -5,12 +5,6 @@
"invalid_api_key": "Ogiltig API-nyckel" "invalid_api_key": "Ogiltig API-nyckel"
}, },
"step": { "step": {
"geography": {
"data": {
"latitude": "Latitud",
"longitude": "Longitud"
}
},
"node_pro": { "node_pro": {
"data": { "data": {
"ip_address": "Enhets IP-adress / v\u00e4rdnamn", "ip_address": "Enhets IP-adress / v\u00e4rdnamn",

View File

@ -3,5 +3,6 @@
"name": "Aladdin Connect", "name": "Aladdin Connect",
"documentation": "https://www.home-assistant.io/integrations/aladdin_connect", "documentation": "https://www.home-assistant.io/integrations/aladdin_connect",
"requirements": ["aladdin_connect==0.3"], "requirements": ["aladdin_connect==0.3"],
"codeowners": [] "codeowners": [],
"iot_class": "cloud_polling"
} }

View File

@ -2,8 +2,9 @@
from __future__ import annotations from __future__ import annotations
import asyncio import asyncio
from collections.abc import Iterable
import logging import logging
from typing import Any, Iterable from typing import Any
from homeassistant.const import ( from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_ENTITY_ID,

View File

@ -1,5 +1,4 @@
"""Support for AlarmDecoder devices.""" """Support for AlarmDecoder devices."""
import asyncio
from datetime import timedelta from datetime import timedelta
import logging import logging
@ -14,7 +13,7 @@ from homeassistant.const import (
CONF_PROTOCOL, CONF_PROTOCOL,
EVENT_HOMEASSISTANT_STOP, EVENT_HOMEASSISTANT_STOP,
) )
from homeassistant.helpers.typing import HomeAssistantType from homeassistant.core import HomeAssistant
from homeassistant.util import dt as dt_util from homeassistant.util import dt as dt_util
from .const import ( from .const import (
@ -39,7 +38,7 @@ _LOGGER = logging.getLogger(__name__)
PLATFORMS = ["alarm_control_panel", "sensor", "binary_sensor"] PLATFORMS = ["alarm_control_panel", "sensor", "binary_sensor"]
async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up AlarmDecoder config flow.""" """Set up AlarmDecoder config flow."""
undo_listener = entry.add_update_listener(_update_listener) undo_listener = entry.add_update_listener(_update_listener)
@ -125,25 +124,16 @@ async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry) -> bool
await open_connection() await open_connection()
for platform in PLATFORMS: hass.config_entries.async_setup_platforms(entry, PLATFORMS)
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, platform)
)
return True return True
async def async_unload_entry(hass: HomeAssistantType, entry: ConfigEntry): async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Unload a AlarmDecoder entry.""" """Unload a AlarmDecoder entry."""
hass.data[DOMAIN][entry.entry_id][DATA_RESTART] = False hass.data[DOMAIN][entry.entry_id][DATA_RESTART] = False
unload_ok = all( unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
await asyncio.gather(
*[
hass.config_entries.async_forward_entry_unload(entry, platform)
for platform in PLATFORMS
]
)
)
if not unload_ok: if not unload_ok:
return False return False
@ -160,7 +150,7 @@ async def async_unload_entry(hass: HomeAssistantType, entry: ConfigEntry):
return True return True
async def _update_listener(hass: HomeAssistantType, entry: ConfigEntry): async def _update_listener(hass: HomeAssistant, entry: ConfigEntry):
"""Handle options update.""" """Handle options update."""
_LOGGER.debug("AlarmDecoder options updated: %s", entry.as_dict()["options"]) _LOGGER.debug("AlarmDecoder options updated: %s", entry.as_dict()["options"])
await hass.config_entries.async_reload(entry.entry_id) await hass.config_entries.async_reload(entry.entry_id)

View File

@ -19,9 +19,9 @@ from homeassistant.const import (
STATE_ALARM_DISARMED, STATE_ALARM_DISARMED,
STATE_ALARM_TRIGGERED, STATE_ALARM_TRIGGERED,
) )
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_platform from homeassistant.helpers import entity_platform
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.typing import HomeAssistantType
from .const import ( from .const import (
CONF_ALT_NIGHT_MODE, CONF_ALT_NIGHT_MODE,
@ -41,7 +41,7 @@ ATTR_KEYPRESS = "keypress"
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistantType, entry: ConfigEntry, async_add_entities hass: HomeAssistant, entry: ConfigEntry, async_add_entities
): ):
"""Set up for AlarmDecoder alarm panels.""" """Set up for AlarmDecoder alarm panels."""
options = entry.options options = entry.options

View File

@ -3,7 +3,7 @@ import logging
from homeassistant.components.binary_sensor import BinarySensorEntity from homeassistant.components.binary_sensor import BinarySensorEntity
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers.typing import HomeAssistantType from homeassistant.core import HomeAssistant
from .const import ( from .const import (
CONF_RELAY_ADDR, CONF_RELAY_ADDR,
@ -34,7 +34,7 @@ ATTR_RF_LOOP1 = "rf_loop1"
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistantType, entry: ConfigEntry, async_add_entities hass: HomeAssistant, entry: ConfigEntry, async_add_entities
): ):
"""Set up for AlarmDecoder sensor.""" """Set up for AlarmDecoder sensor."""

View File

@ -4,5 +4,6 @@
"documentation": "https://www.home-assistant.io/integrations/alarmdecoder", "documentation": "https://www.home-assistant.io/integrations/alarmdecoder",
"requirements": ["adext==0.4.1"], "requirements": ["adext==0.4.1"],
"codeowners": ["@ajschmidt8"], "codeowners": ["@ajschmidt8"],
"config_flow": true "config_flow": true,
"iot_class": "local_push"
} }

View File

@ -1,13 +1,13 @@
"""Support for AlarmDecoder sensors (Shows Panel Display).""" """Support for AlarmDecoder sensors (Shows Panel Display)."""
from homeassistant.components.sensor import SensorEntity from homeassistant.components.sensor import SensorEntity
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers.typing import HomeAssistantType from homeassistant.core import HomeAssistant
from .const import SIGNAL_PANEL_MESSAGE from .const import SIGNAL_PANEL_MESSAGE
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistantType, entry: ConfigEntry, async_add_entities hass: HomeAssistant, entry: ConfigEntry, async_add_entities
): ):
"""Set up for AlarmDecoder sensor.""" """Set up for AlarmDecoder sensor."""

View File

@ -14,7 +14,8 @@
"user": { "user": {
"data": { "data": {
"protocol": "Protocolo" "protocol": "Protocolo"
} },
"title": "Elija el protocolo AlarmDecoder"
} }
} }
}, },

View File

@ -1,7 +1,7 @@
{ {
"config": { "config": {
"abort": { "abort": {
"already_configured": "\u88dd\u7f6e\u7d93\u8a2d\u5b9a\u5b8c\u6210" "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210"
}, },
"create_entry": { "create_entry": {
"default": "\u6210\u529f\u9023\u7dda\u81f3 AlarmDecoder\u3002" "default": "\u6210\u529f\u9023\u7dda\u81f3 AlarmDecoder\u3002"

View File

@ -4,5 +4,6 @@
"documentation": "https://www.home-assistant.io/integrations/alert", "documentation": "https://www.home-assistant.io/integrations/alert",
"after_dependencies": ["notify"], "after_dependencies": ["notify"],
"codeowners": [], "codeowners": [],
"quality_scale": "internal" "quality_scale": "internal",
"iot_class": "local_push"
} }

View File

@ -2,8 +2,9 @@
from __future__ import annotations from __future__ import annotations
import asyncio import asyncio
from collections.abc import Iterable
import logging import logging
from typing import Any, Iterable from typing import Any
from homeassistant.const import ( from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_ENTITY_ID,

View File

@ -504,12 +504,12 @@ class LightCapabilities(AlexaEntity):
"""Yield the supported interfaces.""" """Yield the supported interfaces."""
yield AlexaPowerController(self.entity) yield AlexaPowerController(self.entity)
supported = self.entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0) color_modes = self.entity.attributes.get(light.ATTR_SUPPORTED_COLOR_MODES)
if supported & light.SUPPORT_BRIGHTNESS: if light.brightness_supported(color_modes):
yield AlexaBrightnessController(self.entity) yield AlexaBrightnessController(self.entity)
if supported & light.SUPPORT_COLOR: if light.color_supported(color_modes):
yield AlexaColorController(self.entity) yield AlexaColorController(self.entity)
if supported & light.SUPPORT_COLOR_TEMP: if light.color_temp_supported(color_modes):
yield AlexaColorTemperatureController(self.entity) yield AlexaColorTemperatureController(self.entity)
yield AlexaEndpointHealth(self.hass, self.entity) yield AlexaEndpointHealth(self.hass, self.entity)

View File

@ -1374,10 +1374,7 @@ async def async_api_seek(hass, config, directive, context):
msg = f"{entity} did not return the current media position." msg = f"{entity} did not return the current media position."
raise AlexaVideoActionNotPermittedForContentError(msg) raise AlexaVideoActionNotPermittedForContentError(msg)
seek_position = int(current_position) + int(position_delta / 1000) seek_position = max(int(current_position) + int(position_delta / 1000), 0)
if seek_position < 0:
seek_position = 0
media_duration = entity.attributes.get(media_player.ATTR_MEDIA_DURATION) media_duration = entity.attributes.get(media_player.ATTR_MEDIA_DURATION)
if media_duration and 0 < int(media_duration) < seek_position: if media_duration and 0 < int(media_duration) < seek_position:

View File

@ -17,10 +17,10 @@ def async_describe_events(hass, async_describe_event):
if entity_id: if entity_id:
state = hass.states.get(entity_id) state = hass.states.get(entity_id)
name = state.name if state else entity_id name = state.name if state else entity_id
message = f"send command {data['request']['namespace']}/{data['request']['name']} for {name}" message = f"sent command {data['request']['namespace']}/{data['request']['name']} for {name}"
else: else:
message = ( message = (
f"send command {data['request']['namespace']}/{data['request']['name']}" f"sent command {data['request']['namespace']}/{data['request']['name']}"
) )
return {"name": "Amazon Alexa", "message": message, "entity_id": entity_id} return {"name": "Amazon Alexa", "message": message, "entity_id": entity_id}

View File

@ -2,14 +2,8 @@
"domain": "alexa", "domain": "alexa",
"name": "Amazon Alexa", "name": "Amazon Alexa",
"documentation": "https://www.home-assistant.io/integrations/alexa", "documentation": "https://www.home-assistant.io/integrations/alexa",
"dependencies": [ "dependencies": ["http"],
"http" "after_dependencies": ["camera"],
], "codeowners": ["@home-assistant/cloud", "@ochlocracy"],
"after_dependencies": [ "iot_class": "cloud_push"
"camera"
],
"codeowners": [
"@home-assistant/cloud",
"@ochlocracy"
]
} }

View File

@ -5,5 +5,6 @@
"documentation": "https://www.home-assistant.io/integrations/almond", "documentation": "https://www.home-assistant.io/integrations/almond",
"dependencies": ["http", "conversation"], "dependencies": ["http", "conversation"],
"codeowners": ["@gcampax", "@balloob"], "codeowners": ["@gcampax", "@balloob"],
"requirements": ["pyalmond==0.0.2"] "requirements": ["pyalmond==0.0.2"],
"iot_class": "local_polling"
} }

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