mirror of
https://github.com/home-assistant/core.git
synced 2025-07-30 08:47:09 +00:00
Merge pull request #50123 from home-assistant/rc
This commit is contained in:
commit
ccf92e4721
41
.coveragerc
41
.coveragerc
@ -145,7 +145,6 @@ omit =
|
||||
homeassistant/components/clickatell/notify.py
|
||||
homeassistant/components/clicksend/notify.py
|
||||
homeassistant/components/clicksend_tts/notify.py
|
||||
homeassistant/components/climacell/weather.py
|
||||
homeassistant/components/cmus/media_player.py
|
||||
homeassistant/components/co2signal/*
|
||||
homeassistant/components/coinbase/*
|
||||
@ -174,6 +173,7 @@ omit =
|
||||
homeassistant/components/deluge/sensor.py
|
||||
homeassistant/components/deluge/switch.py
|
||||
homeassistant/components/denon/media_player.py
|
||||
homeassistant/components/denonavr/__init__.py
|
||||
homeassistant/components/denonavr/media_player.py
|
||||
homeassistant/components/denonavr/receiver.py
|
||||
homeassistant/components/deutsche_bahn/sensor.py
|
||||
@ -237,6 +237,8 @@ omit =
|
||||
homeassistant/components/emby/media_player.py
|
||||
homeassistant/components/emoncms/sensor.py
|
||||
homeassistant/components/emoncms_history/*
|
||||
homeassistant/components/emonitor/__init__.py
|
||||
homeassistant/components/emonitor/sensor.py
|
||||
homeassistant/components/enigma2/media_player.py
|
||||
homeassistant/components/enocean/__init__.py
|
||||
homeassistant/components/enocean/binary_sensor.py
|
||||
@ -246,12 +248,14 @@ omit =
|
||||
homeassistant/components/enocean/light.py
|
||||
homeassistant/components/enocean/sensor.py
|
||||
homeassistant/components/enocean/switch.py
|
||||
homeassistant/components/enphase_envoy/__init__.py
|
||||
homeassistant/components/enphase_envoy/sensor.py
|
||||
homeassistant/components/entur_public_transport/*
|
||||
homeassistant/components/environment_canada/*
|
||||
homeassistant/components/envirophat/sensor.py
|
||||
homeassistant/components/envisalink/*
|
||||
homeassistant/components/ephember/climate.py
|
||||
homeassistant/components/epson/__init__.py
|
||||
homeassistant/components/epson/const.py
|
||||
homeassistant/components/epson/media_player.py
|
||||
homeassistant/components/epsonworkforce/sensor.py
|
||||
@ -271,7 +275,13 @@ omit =
|
||||
homeassistant/components/eufy/*
|
||||
homeassistant/components/everlights/light.py
|
||||
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/faa_delays/__init__.py
|
||||
homeassistant/components/faa_delays/binary_sensor.py
|
||||
@ -319,6 +329,9 @@ omit =
|
||||
homeassistant/components/freebox/router.py
|
||||
homeassistant/components/freebox/sensor.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/fritzbox_callmonitor/__init__.py
|
||||
homeassistant/components/fritzbox_callmonitor/const.py
|
||||
@ -335,7 +348,6 @@ omit =
|
||||
homeassistant/components/garmin_connect/alarm_util.py
|
||||
homeassistant/components/gc100/*
|
||||
homeassistant/components/geniushub/*
|
||||
homeassistant/components/geizhals/sensor.py
|
||||
homeassistant/components/github/sensor.py
|
||||
homeassistant/components/gitlab_ci/sensor.py
|
||||
homeassistant/components/gitter/sensor.py
|
||||
@ -349,6 +361,8 @@ omit =
|
||||
homeassistant/components/google/*
|
||||
homeassistant/components/google_cloud/tts.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/gpmdp/media_player.py
|
||||
homeassistant/components/gpsd/sensor.py
|
||||
@ -421,6 +435,7 @@ omit =
|
||||
homeassistant/components/hvv_departures/sensor.py
|
||||
homeassistant/components/hvv_departures/__init__.py
|
||||
homeassistant/components/hydrawise/*
|
||||
homeassistant/components/ialarm/alarm_control_panel.py
|
||||
homeassistant/components/iammeter/sensor.py
|
||||
homeassistant/components/iaqualink/binary_sensor.py
|
||||
homeassistant/components/iaqualink/climate.py
|
||||
@ -502,6 +517,10 @@ omit =
|
||||
homeassistant/components/kodi/media_player.py
|
||||
homeassistant/components/kodi/notify.py
|
||||
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/lacrosse/sensor.py
|
||||
homeassistant/components/lametric/*
|
||||
@ -573,6 +592,8 @@ omit =
|
||||
homeassistant/components/melcloud/water_heater.py
|
||||
homeassistant/components/message_bird/notify.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/const.py
|
||||
homeassistant/components/meteo_france/sensor.py
|
||||
@ -600,7 +621,6 @@ omit =
|
||||
homeassistant/components/modbus/cover.py
|
||||
homeassistant/components/modbus/modbus.py
|
||||
homeassistant/components/modbus/switch.py
|
||||
homeassistant/components/modbus/sensor.py
|
||||
homeassistant/components/modem_callerid/sensor.py
|
||||
homeassistant/components/motion_blinds/__init__.py
|
||||
homeassistant/components/motion_blinds/const.py
|
||||
@ -612,6 +632,8 @@ omit =
|
||||
homeassistant/components/msteams/notify.py
|
||||
homeassistant/components/mullvad/__init__.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/mvglive/sensor.py
|
||||
homeassistant/components/mychevy/*
|
||||
@ -667,6 +689,7 @@ omit =
|
||||
homeassistant/components/nsw_fuel_station/sensor.py
|
||||
homeassistant/components/nuki/__init__.py
|
||||
homeassistant/components/nuki/const.py
|
||||
homeassistant/components/nuki/binary_sensor.py
|
||||
homeassistant/components/nuki/lock.py
|
||||
homeassistant/components/nut/sensor.py
|
||||
homeassistant/components/nx584/alarm_control_panel.py
|
||||
@ -759,6 +782,7 @@ omit =
|
||||
homeassistant/components/poolsense/__init__.py
|
||||
homeassistant/components/poolsense/sensor.py
|
||||
homeassistant/components/poolsense/binary_sensor.py
|
||||
homeassistant/components/powerwall/__init__.py
|
||||
homeassistant/components/proliphix/climate.py
|
||||
homeassistant/components/progettihwsw/__init__.py
|
||||
homeassistant/components/progettihwsw/binary_sensor.py
|
||||
@ -807,9 +831,13 @@ omit =
|
||||
homeassistant/components/rest/switch.py
|
||||
homeassistant/components/ring/camera.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/__init__.py
|
||||
homeassistant/components/rocketchat/notify.py
|
||||
homeassistant/components/roomba/__init__.py
|
||||
homeassistant/components/roomba/binary_sensor.py
|
||||
homeassistant/components/roomba/braava.py
|
||||
homeassistant/components/roomba/irobot_base.py
|
||||
@ -841,6 +869,7 @@ omit =
|
||||
homeassistant/components/screenlogic/binary_sensor.py
|
||||
homeassistant/components/screenlogic/climate.py
|
||||
homeassistant/components/screenlogic/sensor.py
|
||||
homeassistant/components/screenlogic/services.py
|
||||
homeassistant/components/screenlogic/switch.py
|
||||
homeassistant/components/scsgate/*
|
||||
homeassistant/components/scsgate/cover.py
|
||||
@ -878,6 +907,7 @@ omit =
|
||||
homeassistant/components/slack/notify.py
|
||||
homeassistant/components/sinch/*
|
||||
homeassistant/components/slide/*
|
||||
homeassistant/components/sma/__init__.py
|
||||
homeassistant/components/sma/sensor.py
|
||||
homeassistant/components/smappee/__init__.py
|
||||
homeassistant/components/smappee/api.py
|
||||
@ -893,7 +923,6 @@ omit =
|
||||
homeassistant/components/snapcast/*
|
||||
homeassistant/components/snmp/*
|
||||
homeassistant/components/sochain/sensor.py
|
||||
homeassistant/components/socialblade/sensor.py
|
||||
homeassistant/components/solaredge/__init__.py
|
||||
homeassistant/components/solaredge/sensor.py
|
||||
homeassistant/components/solaredge_local/sensor.py
|
||||
@ -1097,6 +1126,8 @@ omit =
|
||||
homeassistant/components/waterfurnace/*
|
||||
homeassistant/components/watson_iot/*
|
||||
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/webostv/*
|
||||
homeassistant/components/whois/sensor.py
|
||||
|
11
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
11
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -1,7 +1,5 @@
|
||||
name: Report an issue with Home Assistant Core
|
||||
description: Report an issue with Home Assistant Core.
|
||||
title: ""
|
||||
issue_body: true
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
@ -85,13 +83,10 @@ body:
|
||||
label: Anything in the logs that might be useful for us?
|
||||
description: For example, error message, or stack traces.
|
||||
render: txt
|
||||
- type: markdown
|
||||
- type: textarea
|
||||
attributes:
|
||||
value: |
|
||||
## Additional information
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: >
|
||||
label: Additional information
|
||||
description: >
|
||||
If you have any additional information for us, use the field below.
|
||||
Please note, you can attach screenshots or screen recordings here, by
|
||||
dragging and dropping files in the field below.
|
||||
|
311
.github/workflows/builder.yml
vendored
Normal file
311
.github/workflows/builder.yml
vendored
Normal 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
|
81
.github/workflows/ci.yaml
vendored
81
.github/workflows/ci.yaml
vendored
@ -13,6 +13,7 @@ env:
|
||||
CACHE_VERSION: 1
|
||||
DEFAULT_PYTHON: 3.8
|
||||
PRE_COMMIT_CACHE: ~/.cache/pre-commit
|
||||
SQLALCHEMY_WARN_20: 1
|
||||
|
||||
jobs:
|
||||
# Separate job to pre-populate the base dependency cache
|
||||
@ -28,7 +29,7 @@ jobs:
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
id: python
|
||||
uses: actions/setup-python@v2.2.1
|
||||
uses: actions/setup-python@v2.2.2
|
||||
with:
|
||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||
- name: Generate partial Python venv restore key
|
||||
@ -40,7 +41,7 @@ jobs:
|
||||
hashFiles('homeassistant/package_constraints.txt') }}"
|
||||
- name: Restore base Python virtual environment
|
||||
id: cache-venv
|
||||
uses: actions/cache@v2.1.4
|
||||
uses: actions/cache@v2.1.5
|
||||
with:
|
||||
path: venv
|
||||
key: >-
|
||||
@ -64,7 +65,7 @@ jobs:
|
||||
hashFiles('.pre-commit-config.yaml') }}"
|
||||
- name: Restore pre-commit environment from cache
|
||||
id: cache-precommit
|
||||
uses: actions/cache@v2.1.4
|
||||
uses: actions/cache@v2.1.5
|
||||
with:
|
||||
path: ${{ env.PRE_COMMIT_CACHE }}
|
||||
key: >-
|
||||
@ -85,13 +86,13 @@ jobs:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
uses: actions/setup-python@v2.2.1
|
||||
uses: actions/setup-python@v2.2.2
|
||||
id: python
|
||||
with:
|
||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||
- name: Restore base Python virtual environment
|
||||
id: cache-venv
|
||||
uses: actions/cache@v2.1.4
|
||||
uses: actions/cache@v2.1.5
|
||||
with:
|
||||
path: venv
|
||||
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
|
||||
@ -103,7 +104,7 @@ jobs:
|
||||
exit 1
|
||||
- name: Restore pre-commit environment from cache
|
||||
id: cache-precommit
|
||||
uses: actions/cache@v2.1.4
|
||||
uses: actions/cache@v2.1.5
|
||||
with:
|
||||
path: ${{ env.PRE_COMMIT_CACHE }}
|
||||
key: ${{ runner.os }}-${{ needs.prepare-base.outputs.pre-commit-key }}
|
||||
@ -125,13 +126,13 @@ jobs:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
uses: actions/setup-python@v2.2.1
|
||||
uses: actions/setup-python@v2.2.2
|
||||
id: python
|
||||
with:
|
||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||
- name: Restore base Python virtual environment
|
||||
id: cache-venv
|
||||
uses: actions/cache@v2.1.4
|
||||
uses: actions/cache@v2.1.5
|
||||
with:
|
||||
path: venv
|
||||
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
|
||||
@ -143,7 +144,7 @@ jobs:
|
||||
exit 1
|
||||
- name: Restore pre-commit environment from cache
|
||||
id: cache-precommit
|
||||
uses: actions/cache@v2.1.4
|
||||
uses: actions/cache@v2.1.5
|
||||
with:
|
||||
path: ${{ env.PRE_COMMIT_CACHE }}
|
||||
key: ${{ runner.os }}-${{ needs.prepare-base.outputs.pre-commit-key }}
|
||||
@ -165,13 +166,13 @@ jobs:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
uses: actions/setup-python@v2.2.1
|
||||
uses: actions/setup-python@v2.2.2
|
||||
id: python
|
||||
with:
|
||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||
- name: Restore base Python virtual environment
|
||||
id: cache-venv
|
||||
uses: actions/cache@v2.1.4
|
||||
uses: actions/cache@v2.1.5
|
||||
with:
|
||||
path: venv
|
||||
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
|
||||
@ -183,7 +184,7 @@ jobs:
|
||||
exit 1
|
||||
- name: Restore pre-commit environment from cache
|
||||
id: cache-precommit
|
||||
uses: actions/cache@v2.1.4
|
||||
uses: actions/cache@v2.1.5
|
||||
with:
|
||||
path: ${{ env.PRE_COMMIT_CACHE }}
|
||||
key: ${{ runner.os }}-${{ needs.prepare-base.outputs.pre-commit-key }}
|
||||
@ -227,13 +228,13 @@ jobs:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
uses: actions/setup-python@v2.2.1
|
||||
uses: actions/setup-python@v2.2.2
|
||||
id: python
|
||||
with:
|
||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||
- name: Restore base Python virtual environment
|
||||
id: cache-venv
|
||||
uses: actions/cache@v2.1.4
|
||||
uses: actions/cache@v2.1.5
|
||||
with:
|
||||
path: venv
|
||||
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
|
||||
@ -245,7 +246,7 @@ jobs:
|
||||
exit 1
|
||||
- name: Restore pre-commit environment from cache
|
||||
id: cache-precommit
|
||||
uses: actions/cache@v2.1.4
|
||||
uses: actions/cache@v2.1.5
|
||||
with:
|
||||
path: ${{ env.PRE_COMMIT_CACHE }}
|
||||
key: ${{ runner.os }}-${{ needs.prepare-base.outputs.pre-commit-key }}
|
||||
@ -270,13 +271,13 @@ jobs:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
uses: actions/setup-python@v2.2.1
|
||||
uses: actions/setup-python@v2.2.2
|
||||
id: python
|
||||
with:
|
||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||
- name: Restore base Python virtual environment
|
||||
id: cache-venv
|
||||
uses: actions/cache@v2.1.4
|
||||
uses: actions/cache@v2.1.5
|
||||
with:
|
||||
path: venv
|
||||
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
|
||||
@ -288,7 +289,7 @@ jobs:
|
||||
exit 1
|
||||
- name: Restore pre-commit environment from cache
|
||||
id: cache-precommit
|
||||
uses: actions/cache@v2.1.4
|
||||
uses: actions/cache@v2.1.5
|
||||
with:
|
||||
path: ${{ env.PRE_COMMIT_CACHE }}
|
||||
key: ${{ runner.os }}-${{ needs.prepare-base.outputs.pre-commit-key }}
|
||||
@ -313,13 +314,13 @@ jobs:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
uses: actions/setup-python@v2.2.1
|
||||
uses: actions/setup-python@v2.2.2
|
||||
id: python
|
||||
with:
|
||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||
- name: Restore base Python virtual environment
|
||||
id: cache-venv
|
||||
uses: actions/cache@v2.1.4
|
||||
uses: actions/cache@v2.1.5
|
||||
with:
|
||||
path: venv
|
||||
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
|
||||
@ -331,7 +332,7 @@ jobs:
|
||||
exit 1
|
||||
- name: Restore pre-commit environment from cache
|
||||
id: cache-precommit
|
||||
uses: actions/cache@v2.1.4
|
||||
uses: actions/cache@v2.1.5
|
||||
with:
|
||||
path: ${{ env.PRE_COMMIT_CACHE }}
|
||||
key: ${{ runner.os }}-${{ needs.prepare-base.outputs.pre-commit-key }}
|
||||
@ -353,13 +354,13 @@ jobs:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
uses: actions/setup-python@v2.2.1
|
||||
uses: actions/setup-python@v2.2.2
|
||||
id: python
|
||||
with:
|
||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||
- name: Restore base Python virtual environment
|
||||
id: cache-venv
|
||||
uses: actions/cache@v2.1.4
|
||||
uses: actions/cache@v2.1.5
|
||||
with:
|
||||
path: venv
|
||||
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
|
||||
@ -371,7 +372,7 @@ jobs:
|
||||
exit 1
|
||||
- name: Restore pre-commit environment from cache
|
||||
id: cache-precommit
|
||||
uses: actions/cache@v2.1.4
|
||||
uses: actions/cache@v2.1.5
|
||||
with:
|
||||
path: ${{ env.PRE_COMMIT_CACHE }}
|
||||
key: ${{ runner.os }}-${{ needs.prepare-base.outputs.pre-commit-key }}
|
||||
@ -396,13 +397,13 @@ jobs:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
uses: actions/setup-python@v2.2.1
|
||||
uses: actions/setup-python@v2.2.2
|
||||
id: python
|
||||
with:
|
||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||
- name: Restore base Python virtual environment
|
||||
id: cache-venv
|
||||
uses: actions/cache@v2.1.4
|
||||
uses: actions/cache@v2.1.5
|
||||
with:
|
||||
path: venv
|
||||
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
|
||||
@ -414,7 +415,7 @@ jobs:
|
||||
exit 1
|
||||
- name: Restore pre-commit environment from cache
|
||||
id: cache-precommit
|
||||
uses: actions/cache@v2.1.4
|
||||
uses: actions/cache@v2.1.5
|
||||
with:
|
||||
path: ${{ env.PRE_COMMIT_CACHE }}
|
||||
key: ${{ runner.os }}-${{ needs.prepare-base.outputs.pre-commit-key }}
|
||||
@ -447,13 +448,13 @@ jobs:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
uses: actions/setup-python@v2.2.1
|
||||
uses: actions/setup-python@v2.2.2
|
||||
id: python
|
||||
with:
|
||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||
- name: Restore base Python virtual environment
|
||||
id: cache-venv
|
||||
uses: actions/cache@v2.1.4
|
||||
uses: actions/cache@v2.1.5
|
||||
with:
|
||||
path: venv
|
||||
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
|
||||
@ -465,7 +466,7 @@ jobs:
|
||||
exit 1
|
||||
- name: Restore pre-commit environment from cache
|
||||
id: cache-precommit
|
||||
uses: actions/cache@v2.1.4
|
||||
uses: actions/cache@v2.1.5
|
||||
with:
|
||||
path: ${{ env.PRE_COMMIT_CACHE }}
|
||||
key: ${{ runner.os }}-${{ needs.prepare-base.outputs.pre-commit-key }}
|
||||
@ -495,7 +496,7 @@ jobs:
|
||||
uses: actions/checkout@v2
|
||||
- name: Restore full Python ${{ matrix.python-version }} virtual environment
|
||||
id: cache-venv
|
||||
uses: actions/cache@v2.1.4
|
||||
uses: actions/cache@v2.1.5
|
||||
with:
|
||||
path: venv
|
||||
key: ${{ runner.os }}-${{ matrix.python-version }}-${{
|
||||
@ -518,13 +519,13 @@ jobs:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
uses: actions/setup-python@v2.2.1
|
||||
uses: actions/setup-python@v2.2.2
|
||||
id: python
|
||||
with:
|
||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||
- name: Restore base Python virtual environment
|
||||
id: cache-venv
|
||||
uses: actions/cache@v2.1.4
|
||||
uses: actions/cache@v2.1.5
|
||||
with:
|
||||
path: venv
|
||||
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
|
||||
@ -560,7 +561,7 @@ jobs:
|
||||
hashFiles('homeassistant/package_constraints.txt') }}"
|
||||
- name: Restore full Python ${{ matrix.python-version }} virtual environment
|
||||
id: cache-venv
|
||||
uses: actions/cache@v2.1.4
|
||||
uses: actions/cache@v2.1.5
|
||||
with:
|
||||
path: venv
|
||||
key: >-
|
||||
@ -597,7 +598,7 @@ jobs:
|
||||
uses: actions/checkout@v2
|
||||
- name: Restore full Python ${{ matrix.python-version }} virtual environment
|
||||
id: cache-venv
|
||||
uses: actions/cache@v2.1.4
|
||||
uses: actions/cache@v2.1.5
|
||||
with:
|
||||
path: venv
|
||||
key: ${{ runner.os }}-${{ matrix.python-version }}-${{
|
||||
@ -628,7 +629,7 @@ jobs:
|
||||
uses: actions/checkout@v2
|
||||
- name: Restore full Python ${{ matrix.python-version }} virtual environment
|
||||
id: cache-venv
|
||||
uses: actions/cache@v2.1.4
|
||||
uses: actions/cache@v2.1.5
|
||||
with:
|
||||
path: venv
|
||||
key: ${{ runner.os }}-${{ matrix.python-version }}-${{
|
||||
@ -662,7 +663,7 @@ jobs:
|
||||
uses: actions/checkout@v2
|
||||
- name: Restore full Python ${{ matrix.python-version }} virtual environment
|
||||
id: cache-venv
|
||||
uses: actions/cache@v2.1.4
|
||||
uses: actions/cache@v2.1.5
|
||||
with:
|
||||
path: venv
|
||||
key: ${{ runner.os }}-${{ matrix.python-version }}-${{
|
||||
@ -699,7 +700,7 @@ jobs:
|
||||
-p no:sugar \
|
||||
tests
|
||||
- name: Upload coverage artifact
|
||||
uses: actions/upload-artifact@v2.2.2
|
||||
uses: actions/upload-artifact@v2.2.3
|
||||
with:
|
||||
name: coverage-${{ matrix.python-version }}-group${{ matrix.group }}
|
||||
path: .coverage
|
||||
@ -720,7 +721,7 @@ jobs:
|
||||
uses: actions/checkout@v2
|
||||
- name: Restore full Python ${{ matrix.python-version }} virtual environment
|
||||
id: cache-venv
|
||||
uses: actions/cache@v2.1.4
|
||||
uses: actions/cache@v2.1.5
|
||||
with:
|
||||
path: venv
|
||||
key: ${{ runner.os }}-${{ matrix.python-version }}-${{
|
||||
@ -739,4 +740,4 @@ jobs:
|
||||
coverage report --fail-under=94
|
||||
coverage xml
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v1.3.1
|
||||
uses: codecov/codecov-action@v1.4.1
|
||||
|
@ -1,11 +1,11 @@
|
||||
repos:
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v2.11.0
|
||||
rev: v2.12.0
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: [--py38-plus]
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 20.8b1
|
||||
rev: 21.4b0
|
||||
hooks:
|
||||
- id: black
|
||||
args:
|
||||
@ -23,7 +23,7 @@ repos:
|
||||
exclude_types: [csv, json]
|
||||
exclude: ^tests/fixtures/
|
||||
- repo: https://gitlab.com/pycqa/flake8
|
||||
rev: 3.9.0
|
||||
rev: 3.9.1
|
||||
hooks:
|
||||
- id: flake8
|
||||
additional_dependencies:
|
||||
@ -33,6 +33,7 @@ repos:
|
||||
- pydocstyle==6.0.0
|
||||
- flake8-comprehensions==3.4.0
|
||||
- flake8-noqa==1.1.0
|
||||
- mccabe==0.6.1
|
||||
files: ^(homeassistant|script|tests)/.+\.py$
|
||||
- repo: https://github.com/PyCQA/bandit
|
||||
rev: 1.7.0
|
||||
@ -44,7 +45,7 @@ repos:
|
||||
- --configfile=tests/bandit.yaml
|
||||
files: ^(homeassistant|script|tests)/.+\.py$
|
||||
- repo: https://github.com/PyCQA/isort
|
||||
rev: 5.7.0
|
||||
rev: 5.8.0
|
||||
hooks:
|
||||
- id: isort
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
@ -69,7 +70,7 @@ repos:
|
||||
- id: prettier
|
||||
stages: [manual]
|
||||
- repo: https://github.com/cdce8p/python-typing-update
|
||||
rev: v0.3.2
|
||||
rev: v0.3.3
|
||||
hooks:
|
||||
# Run `python-typing-update` hook manually from time to time
|
||||
# to update python typing syntax.
|
||||
|
47
.strict-typing
Normal file
47
.strict-typing
Normal 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
13
.vscode/launch.json
vendored
@ -9,11 +9,8 @@
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"module": "homeassistant",
|
||||
"args": [
|
||||
"--debug",
|
||||
"-c",
|
||||
"config"
|
||||
]
|
||||
"justMyCode": false,
|
||||
"args": ["--debug", "-c", "config"]
|
||||
},
|
||||
{
|
||||
// Debug by attaching to local Home Asistant server using Remote Python Debugger.
|
||||
@ -28,7 +25,7 @@
|
||||
"localRoot": "${workspaceFolder}",
|
||||
"remoteRoot": "."
|
||||
}
|
||||
],
|
||||
]
|
||||
},
|
||||
{
|
||||
// Debug by attaching to remote Home Asistant server using Remote Python Debugger.
|
||||
@ -43,7 +40,7 @@
|
||||
"localRoot": "${workspaceFolder}",
|
||||
"remoteRoot": "/usr/src/homeassistant"
|
||||
}
|
||||
],
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
19
CODEOWNERS
19
CODEOWNERS
@ -35,7 +35,6 @@ homeassistant/components/almond/* @gcampax @balloob
|
||||
homeassistant/components/alpha_vantage/* @fabaff
|
||||
homeassistant/components/ambiclimate/* @danielhiversen
|
||||
homeassistant/components/ambient_station/* @bachya
|
||||
homeassistant/components/amcrest/* @pnbruckner
|
||||
homeassistant/components/analytics/* @home-assistant/core @ludeeus
|
||||
homeassistant/components/androidtv/* @JeffLIrion
|
||||
homeassistant/components/apache_kafka/* @bachya
|
||||
@ -89,6 +88,7 @@ homeassistant/components/cloud/* @home-assistant/cloud
|
||||
homeassistant/components/cloudflare/* @ludeeus @ctalkington
|
||||
homeassistant/components/color_extractor/* @GenericStudent
|
||||
homeassistant/components/comfoconnect/* @michaelarnauts
|
||||
homeassistant/components/compensation/* @Petro31
|
||||
homeassistant/components/config/* @home-assistant/core
|
||||
homeassistant/components/configurator/* @home-assistant/core
|
||||
homeassistant/components/control4/* @lawtancool
|
||||
@ -133,6 +133,7 @@ homeassistant/components/elkm1/* @gwww @bdraco
|
||||
homeassistant/components/elv/* @majuss
|
||||
homeassistant/components/emby/* @mezz64
|
||||
homeassistant/components/emoncms/* @borpin
|
||||
homeassistant/components/emonitor/* @bdraco
|
||||
homeassistant/components/emulated_kasa/* @kbickar
|
||||
homeassistant/components/enigma2/* @fbradyirl
|
||||
homeassistant/components/enocean/* @bdurrer
|
||||
@ -146,7 +147,7 @@ homeassistant/components/eq3btsmart/* @rytilahti
|
||||
homeassistant/components/esphome/* @OttoWinter
|
||||
homeassistant/components/essent/* @TheLastProject
|
||||
homeassistant/components/evohome/* @zxdavb
|
||||
homeassistant/components/ezviz/* @baqs
|
||||
homeassistant/components/ezviz/* @RenierM26 @baqs
|
||||
homeassistant/components/faa_delays/* @ntilley905
|
||||
homeassistant/components/fastdotcom/* @rohankapoorcom
|
||||
homeassistant/components/file/* @fabaff
|
||||
@ -163,6 +164,8 @@ homeassistant/components/forked_daapd/* @uvjustin
|
||||
homeassistant/components/fortios/* @kimfrellsen
|
||||
homeassistant/components/foscam/* @skgsergio
|
||||
homeassistant/components/freebox/* @hacf-fr @Quentame
|
||||
homeassistant/components/fritz/* @mammuth @AaronDavidSchneider @chemelli74
|
||||
homeassistant/components/fritzbox/* @mib1185
|
||||
homeassistant/components/fronius/* @nielstron
|
||||
homeassistant/components/frontend/* @home-assistant/frontend
|
||||
homeassistant/components/garmin_connect/* @cyberjunky
|
||||
@ -182,7 +185,7 @@ homeassistant/components/gpsd/* @fabaff
|
||||
homeassistant/components/gree/* @cmroche
|
||||
homeassistant/components/greeneye_monitor/* @jkeljo
|
||||
homeassistant/components/group/* @home-assistant/core
|
||||
homeassistant/components/growatt_server/* @indykoning
|
||||
homeassistant/components/growatt_server/* @indykoning @muppet3000
|
||||
homeassistant/components/guardian/* @bachya
|
||||
homeassistant/components/habitica/* @ASMfreaK @leikoilja
|
||||
homeassistant/components/harmony/* @ehendrix23 @bramkragten @bdraco @mkeesey
|
||||
@ -212,6 +215,7 @@ homeassistant/components/hunterdouglas_powerview/* @bdraco
|
||||
homeassistant/components/hvv_departures/* @vigonotion
|
||||
homeassistant/components/hydrawise/* @ptcryan
|
||||
homeassistant/components/hyperion/* @dermotduffy
|
||||
homeassistant/components/ialarm/* @RyuzakiKK
|
||||
homeassistant/components/iammeter/* @lewei50
|
||||
homeassistant/components/iaqualink/* @flz
|
||||
homeassistant/components/icloud/* @Quentame @nzapponi
|
||||
@ -248,6 +252,7 @@ homeassistant/components/kmtronic/* @dgomes
|
||||
homeassistant/components/knx/* @Julius2342 @farmio @marvin-w
|
||||
homeassistant/components/kodi/* @OnFreund @cgtobi
|
||||
homeassistant/components/konnected/* @heythisisnate @kit-klein
|
||||
homeassistant/components/kostal_plenticore/* @stegm
|
||||
homeassistant/components/kulersky/* @emlove
|
||||
homeassistant/components/lametric/* @robbiet480
|
||||
homeassistant/components/launch_library/* @ludeeus
|
||||
@ -276,6 +281,7 @@ homeassistant/components/mediaroom/* @dgomes
|
||||
homeassistant/components/melcloud/* @vilppuvuorinen
|
||||
homeassistant/components/melissa/* @kennedyshead
|
||||
homeassistant/components/met/* @danielhiversen @thimic
|
||||
homeassistant/components/met_eireann/* @DylanGore
|
||||
homeassistant/components/meteo_france/* @hacf-fr @oncleben31 @Quentame
|
||||
homeassistant/components/meteoalarm/* @rolfberkenbosch
|
||||
homeassistant/components/metoffice/* @MrHarcombe
|
||||
@ -290,10 +296,12 @@ homeassistant/components/modbus/* @adamchengtkc @janiversen @vzahradnik
|
||||
homeassistant/components/monoprice/* @etsinko @OnFreund
|
||||
homeassistant/components/moon/* @fabaff
|
||||
homeassistant/components/motion_blinds/* @starkillerOG
|
||||
homeassistant/components/motioneye/* @dermotduffy
|
||||
homeassistant/components/mpd/* @fabaff
|
||||
homeassistant/components/mqtt/* @emontnemery
|
||||
homeassistant/components/msteams/* @peroyvind
|
||||
homeassistant/components/mullvad/* @meichthys
|
||||
homeassistant/components/mutesync/* @currentoor
|
||||
homeassistant/components/my/* @home-assistant/core
|
||||
homeassistant/components/myq/* @bdraco
|
||||
homeassistant/components/mysensors/* @MartinHjelmare @functionpointer
|
||||
@ -352,6 +360,7 @@ homeassistant/components/persistent_notification/* @home-assistant/core
|
||||
homeassistant/components/philips_js/* @elupus
|
||||
homeassistant/components/pi4ioe5v9xxxx/* @antonverburg
|
||||
homeassistant/components/pi_hole/* @fabaff @johnluetke @shenxn
|
||||
homeassistant/components/picnic/* @corneyl
|
||||
homeassistant/components/pilight/* @trekky12
|
||||
homeassistant/components/plaato/* @JohNan
|
||||
homeassistant/components/plex/* @jjlawren
|
||||
@ -363,7 +372,7 @@ homeassistant/components/powerwall/* @bdraco @jrester
|
||||
homeassistant/components/profiler/* @bdraco
|
||||
homeassistant/components/progettihwsw/* @ardaseremet
|
||||
homeassistant/components/prometheus/* @knyar
|
||||
homeassistant/components/proxmoxve/* @k4ds3 @jhollowe
|
||||
homeassistant/components/proxmoxve/* @k4ds3 @jhollowe @Corbeno
|
||||
homeassistant/components/ps4/* @ktnrg45
|
||||
homeassistant/components/push/* @dgomes
|
||||
homeassistant/components/pvoutput/* @fabaff
|
||||
@ -424,7 +433,7 @@ homeassistant/components/sisyphus/* @jkeljo
|
||||
homeassistant/components/sky_hub/* @rogerselwyn
|
||||
homeassistant/components/slack/* @bachya
|
||||
homeassistant/components/slide/* @ualex73
|
||||
homeassistant/components/sma/* @kellerza
|
||||
homeassistant/components/sma/* @kellerza @rklomp
|
||||
homeassistant/components/smappee/* @bsmappee
|
||||
homeassistant/components/smart_meter_texas/* @grahamwetzler
|
||||
homeassistant/components/smarthab/* @outadoc
|
||||
|
@ -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')
|
20
build.json
20
build.json
@ -1,14 +1,22 @@
|
||||
{
|
||||
"image": "homeassistant/{arch}-homeassistant",
|
||||
"shadow_repository": "ghcr.io/home-assistant",
|
||||
"build_from": {
|
||||
"aarch64": "homeassistant/aarch64-homeassistant-base:2021.02.0",
|
||||
"armhf": "homeassistant/armhf-homeassistant-base:2021.02.0",
|
||||
"armv7": "homeassistant/armv7-homeassistant-base:2021.02.0",
|
||||
"amd64": "homeassistant/amd64-homeassistant-base:2021.02.0",
|
||||
"i386": "homeassistant/i386-homeassistant-base:2021.02.0"
|
||||
"aarch64": "ghcr.io/home-assistant/aarch64-homeassistant-base:2021.04.3",
|
||||
"armhf": "ghcr.io/home-assistant/armhf-homeassistant-base:2021.04.3",
|
||||
"armv7": "ghcr.io/home-assistant/armv7-homeassistant-base:2021.04.3",
|
||||
"amd64": "ghcr.io/home-assistant/amd64-homeassistant-base:2021.04.3",
|
||||
"i386": "ghcr.io/home-assistant/i386-homeassistant-base:2021.04.3"
|
||||
},
|
||||
"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
|
||||
}
|
||||
|
@ -145,6 +145,7 @@ def daemonize() -> None:
|
||||
sys.exit(0)
|
||||
|
||||
# redirect standard file descriptors to devnull
|
||||
# pylint: disable=consider-using-with
|
||||
infd = open(os.devnull)
|
||||
outfd = open(os.devnull, "a+")
|
||||
sys.stdout.flush()
|
||||
|
@ -4,13 +4,14 @@ from __future__ import annotations
|
||||
import asyncio
|
||||
from collections import OrderedDict
|
||||
from datetime import timedelta
|
||||
from typing import Any, Dict, Optional, Tuple, cast
|
||||
from typing import Any, Dict, Mapping, Optional, Tuple, cast
|
||||
|
||||
import jwt
|
||||
|
||||
from homeassistant import data_entry_flow
|
||||
from homeassistant.auth.const import ACCESS_TOKEN_EXPIRATION
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from . import auth_store, models
|
||||
@ -97,8 +98,8 @@ class AuthManagerFlowManager(data_entry_flow.FlowManager):
|
||||
return await auth_provider.async_login_flow(context)
|
||||
|
||||
async def async_finish_flow(
|
||||
self, flow: data_entry_flow.FlowHandler, result: dict[str, Any]
|
||||
) -> dict[str, Any]:
|
||||
self, flow: data_entry_flow.FlowHandler, result: FlowResult
|
||||
) -> FlowResult:
|
||||
"""Return a user as result of login flow."""
|
||||
flow = cast(LoginFlow, flow)
|
||||
|
||||
@ -115,7 +116,7 @@ class AuthManagerFlowManager(data_entry_flow.FlowManager):
|
||||
raise KeyError(f"Unknown auth provider {result['handler']}")
|
||||
|
||||
credentials = await auth_provider.async_get_or_create_credentials(
|
||||
result["data"]
|
||||
cast(Mapping[str, str], result["data"]),
|
||||
)
|
||||
|
||||
if flow.context.get("credential_only"):
|
||||
|
@ -12,6 +12,7 @@ from voluptuous.humanize import humanize_error
|
||||
from homeassistant import data_entry_flow, requirements
|
||||
from homeassistant.const import CONF_ID, CONF_NAME, CONF_TYPE
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.util.decorator import Registry
|
||||
|
||||
@ -105,7 +106,7 @@ class SetupFlow(data_entry_flow.FlowHandler):
|
||||
|
||||
async def async_step_init(
|
||||
self, user_input: dict[str, str] | None = None
|
||||
) -> dict[str, Any]:
|
||||
) -> FlowResult:
|
||||
"""Handle the first step of setup flow.
|
||||
|
||||
Return self.async_show_form(step_id='init') if user_input is None.
|
||||
|
@ -14,6 +14,7 @@ import voluptuous as vol
|
||||
|
||||
from homeassistant.const import CONF_EXCLUDE, CONF_INCLUDE
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
from homeassistant.exceptions import ServiceNotFound
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
|
||||
@ -292,7 +293,7 @@ class NotifySetupFlow(SetupFlow):
|
||||
|
||||
async def async_step_init(
|
||||
self, user_input: dict[str, str] | None = None
|
||||
) -> dict[str, Any]:
|
||||
) -> FlowResult:
|
||||
"""Let user select available notify services."""
|
||||
errors: dict[str, str] = {}
|
||||
|
||||
@ -318,7 +319,7 @@ class NotifySetupFlow(SetupFlow):
|
||||
|
||||
async def async_step_setup(
|
||||
self, user_input: dict[str, str] | None = None
|
||||
) -> dict[str, Any]:
|
||||
) -> FlowResult:
|
||||
"""Verify user can receive one-time password."""
|
||||
errors: dict[str, str] = {}
|
||||
|
||||
|
@ -9,6 +9,7 @@ import voluptuous as vol
|
||||
|
||||
from homeassistant.auth.models import User
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
|
||||
from . import (
|
||||
MULTI_FACTOR_AUTH_MODULE_SCHEMA,
|
||||
@ -189,7 +190,7 @@ class TotpSetupFlow(SetupFlow):
|
||||
|
||||
async def async_step_init(
|
||||
self, user_input: dict[str, str] | None = None
|
||||
) -> dict[str, Any]:
|
||||
) -> FlowResult:
|
||||
"""Handle the first step of setup flow.
|
||||
|
||||
Return self.async_show_form(step_id='init') if user_input is None.
|
||||
|
@ -1,6 +1,7 @@
|
||||
"""Auth providers for Home Assistant."""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Mapping
|
||||
import importlib
|
||||
import logging
|
||||
import types
|
||||
@ -12,6 +13,7 @@ from voluptuous.humanize import humanize_error
|
||||
from homeassistant import data_entry_flow, requirements
|
||||
from homeassistant.const import CONF_ID, CONF_NAME, CONF_TYPE
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.util import dt as dt_util
|
||||
from homeassistant.util.decorator import Registry
|
||||
@ -102,7 +104,7 @@ class AuthProvider:
|
||||
raise NotImplementedError
|
||||
|
||||
async def async_get_or_create_credentials(
|
||||
self, flow_result: dict[str, str]
|
||||
self, flow_result: Mapping[str, str]
|
||||
) -> Credentials:
|
||||
"""Get credentials based on the flow result."""
|
||||
raise NotImplementedError
|
||||
@ -198,7 +200,7 @@ class LoginFlow(data_entry_flow.FlowHandler):
|
||||
|
||||
async def async_step_init(
|
||||
self, user_input: dict[str, str] | None = None
|
||||
) -> dict[str, Any]:
|
||||
) -> FlowResult:
|
||||
"""Handle the first step of login flow.
|
||||
|
||||
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(
|
||||
self, user_input: dict[str, str] | None = None
|
||||
) -> dict[str, Any]:
|
||||
) -> FlowResult:
|
||||
"""Handle the step of select mfa module."""
|
||||
errors = {}
|
||||
|
||||
@ -233,7 +235,7 @@ class LoginFlow(data_entry_flow.FlowHandler):
|
||||
|
||||
async def async_step_mfa(
|
||||
self, user_input: dict[str, str] | None = None
|
||||
) -> dict[str, Any]:
|
||||
) -> FlowResult:
|
||||
"""Handle the step of mfa validation."""
|
||||
assert self.credential
|
||||
assert self.user
|
||||
@ -285,6 +287,6 @@ class LoginFlow(data_entry_flow.FlowHandler):
|
||||
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."""
|
||||
return self.async_create_entry(title=self._auth_provider.name, data=flow_result)
|
||||
|
@ -3,6 +3,7 @@ from __future__ import annotations
|
||||
|
||||
import asyncio.subprocess
|
||||
import collections
|
||||
from collections.abc import Mapping
|
||||
import logging
|
||||
import os
|
||||
from typing import Any, cast
|
||||
@ -10,6 +11,7 @@ from typing import Any, cast
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.const import CONF_COMMAND
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
|
||||
from . import AUTH_PROVIDER_SCHEMA, AUTH_PROVIDERS, AuthProvider, LoginFlow
|
||||
@ -100,7 +102,7 @@ class CommandLineAuthProvider(AuthProvider):
|
||||
self._user_meta[username] = meta
|
||||
|
||||
async def async_get_or_create_credentials(
|
||||
self, flow_result: dict[str, str]
|
||||
self, flow_result: Mapping[str, str]
|
||||
) -> Credentials:
|
||||
"""Get credentials based on the flow result."""
|
||||
username = flow_result["username"]
|
||||
@ -127,7 +129,7 @@ class CommandLineLoginFlow(LoginFlow):
|
||||
|
||||
async def async_step_init(
|
||||
self, user_input: dict[str, str] | None = None
|
||||
) -> dict[str, Any]:
|
||||
) -> FlowResult:
|
||||
"""Handle the step of the form."""
|
||||
errors = {}
|
||||
|
||||
|
@ -4,6 +4,7 @@ from __future__ import annotations
|
||||
import asyncio
|
||||
import base64
|
||||
from collections import OrderedDict
|
||||
from collections.abc import Mapping
|
||||
import logging
|
||||
from typing import Any, cast
|
||||
|
||||
@ -12,6 +13,7 @@ import voluptuous as vol
|
||||
|
||||
from homeassistant.const import CONF_ID
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
|
||||
from . import AUTH_PROVIDER_SCHEMA, AUTH_PROVIDERS, AuthProvider, LoginFlow
|
||||
@ -277,7 +279,7 @@ class HassAuthProvider(AuthProvider):
|
||||
await self.data.async_save()
|
||||
|
||||
async def async_get_or_create_credentials(
|
||||
self, flow_result: dict[str, str]
|
||||
self, flow_result: Mapping[str, str]
|
||||
) -> Credentials:
|
||||
"""Get credentials based on the flow result."""
|
||||
if self.data is None:
|
||||
@ -319,7 +321,7 @@ class HassLoginFlow(LoginFlow):
|
||||
|
||||
async def async_step_init(
|
||||
self, user_input: dict[str, str] | None = None
|
||||
) -> dict[str, Any]:
|
||||
) -> FlowResult:
|
||||
"""Handle the step of the form."""
|
||||
errors = {}
|
||||
|
||||
|
@ -2,12 +2,14 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections import OrderedDict
|
||||
from collections.abc import Mapping
|
||||
import hmac
|
||||
from typing import Any, cast
|
||||
from typing import cast
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
|
||||
from . import AUTH_PROVIDER_SCHEMA, AUTH_PROVIDERS, AuthProvider, LoginFlow
|
||||
@ -62,7 +64,7 @@ class ExampleAuthProvider(AuthProvider):
|
||||
raise InvalidAuthError
|
||||
|
||||
async def async_get_or_create_credentials(
|
||||
self, flow_result: dict[str, str]
|
||||
self, flow_result: Mapping[str, str]
|
||||
) -> Credentials:
|
||||
"""Get credentials based on the flow result."""
|
||||
username = flow_result["username"]
|
||||
@ -97,7 +99,7 @@ class ExampleLoginFlow(LoginFlow):
|
||||
|
||||
async def async_step_init(
|
||||
self, user_input: dict[str, str] | None = None
|
||||
) -> dict[str, Any]:
|
||||
) -> FlowResult:
|
||||
"""Handle the step of the form."""
|
||||
errors = {}
|
||||
|
||||
|
@ -5,12 +5,14 @@ It will be removed when auth system production ready
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Mapping
|
||||
import hmac
|
||||
from typing import Any, cast
|
||||
from typing import cast
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
@ -57,7 +59,7 @@ class LegacyApiPasswordAuthProvider(AuthProvider):
|
||||
raise InvalidAuthError
|
||||
|
||||
async def async_get_or_create_credentials(
|
||||
self, flow_result: dict[str, str]
|
||||
self, flow_result: Mapping[str, str]
|
||||
) -> Credentials:
|
||||
"""Return credentials for this login."""
|
||||
credentials = await self.async_credentials()
|
||||
@ -82,7 +84,7 @@ class LegacyLoginFlow(LoginFlow):
|
||||
|
||||
async def async_step_init(
|
||||
self, user_input: dict[str, str] | None = None
|
||||
) -> dict[str, Any]:
|
||||
) -> FlowResult:
|
||||
"""Handle the step of the form."""
|
||||
errors = {}
|
||||
|
||||
|
@ -5,6 +5,7 @@ Abort login flow if not access from trusted network.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Mapping
|
||||
from ipaddress import (
|
||||
IPv4Address,
|
||||
IPv4Network,
|
||||
@ -18,6 +19,7 @@ from typing import Any, Dict, List, Union, cast
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
@ -127,7 +129,7 @@ class TrustedNetworksAuthProvider(AuthProvider):
|
||||
)
|
||||
|
||||
async def async_get_or_create_credentials(
|
||||
self, flow_result: dict[str, str]
|
||||
self, flow_result: Mapping[str, str]
|
||||
) -> Credentials:
|
||||
"""Get credentials based on the flow result."""
|
||||
user_id = flow_result["user"]
|
||||
@ -199,7 +201,7 @@ class TrustedNetworksLoginFlow(LoginFlow):
|
||||
|
||||
async def async_step_init(
|
||||
self, user_input: dict[str, str] | None = None
|
||||
) -> dict[str, Any]:
|
||||
) -> FlowResult:
|
||||
"""Handle the step of the form."""
|
||||
try:
|
||||
cast(
|
||||
|
@ -20,14 +20,17 @@ from homeassistant.components import http
|
||||
from homeassistant.const import REQUIRED_NEXT_PYTHON_DATE, REQUIRED_NEXT_PYTHON_VER
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
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.setup import (
|
||||
DATA_SETUP,
|
||||
DATA_SETUP_STARTED,
|
||||
DATA_SETUP_TIME,
|
||||
async_set_domains_to_be_loaded,
|
||||
async_setup_component,
|
||||
)
|
||||
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.package import async_get_user_site, is_virtual_env
|
||||
|
||||
@ -42,6 +45,8 @@ ERROR_LOG_FILENAME = "home-assistant.log"
|
||||
DATA_LOGGING = "logging"
|
||||
|
||||
LOG_SLOW_STARTUP_INTERVAL = 60
|
||||
SLOW_STARTUP_CHECK_INTERVAL = 1
|
||||
SIGNAL_BOOTSTRAP_INTEGRATONS = "bootstrap_integrations"
|
||||
|
||||
STAGE_1_TIMEOUT = 120
|
||||
STAGE_2_TIMEOUT = 300
|
||||
@ -380,19 +385,32 @@ def _get_domains(hass: core.HomeAssistant, config: dict[str, Any]) -> set[str]:
|
||||
return domains
|
||||
|
||||
|
||||
async def _async_log_pending_setups(
|
||||
hass: core.HomeAssistant, domains: set[str], setup_started: dict[str, datetime]
|
||||
) -> None:
|
||||
async def _async_watch_pending_setups(hass: core.HomeAssistant) -> None:
|
||||
"""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:
|
||||
await asyncio.sleep(LOG_SLOW_STARTUP_INTERVAL)
|
||||
remaining = [domain for domain in domains if domain in setup_started]
|
||||
now = dt_util.utcnow()
|
||||
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(
|
||||
"Waiting on integrations to complete setup: %s",
|
||||
", ".join(remaining),
|
||||
", ".join(setup_started),
|
||||
)
|
||||
loop_count = 0
|
||||
_LOGGER.debug("Running timeout Zones: %s", hass.timeout.zones)
|
||||
|
||||
|
||||
@ -400,18 +418,13 @@ async def async_setup_multi_components(
|
||||
hass: core.HomeAssistant,
|
||||
domains: set[str],
|
||||
config: dict[str, Any],
|
||||
setup_started: dict[str, datetime],
|
||||
) -> None:
|
||||
"""Set up multiple domains. Log on failure."""
|
||||
futures = {
|
||||
domain: hass.async_create_task(async_setup_component(hass, domain, config))
|
||||
for domain in domains
|
||||
}
|
||||
log_task = asyncio.create_task(
|
||||
_async_log_pending_setups(hass, domains, setup_started)
|
||||
)
|
||||
await asyncio.wait(futures.values())
|
||||
log_task.cancel()
|
||||
errors = [domain for domain in domains if futures[domain].exception()]
|
||||
for domain in errors:
|
||||
exception = futures[domain].exception()
|
||||
@ -427,7 +440,11 @@ async def _async_set_up_integrations(
|
||||
hass: core.HomeAssistant, config: dict[str, Any]
|
||||
) -> None:
|
||||
"""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)
|
||||
|
||||
# Resolve all dependencies so we know all integrations
|
||||
@ -476,14 +493,14 @@ async def _async_set_up_integrations(
|
||||
# Load logging as soon as possible
|
||||
if 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.
|
||||
debuggers = domains_to_setup & DEBUGGER_INTEGRATIONS
|
||||
|
||||
if 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
|
||||
stage_1_domains = set()
|
||||
@ -524,9 +541,7 @@ async def _async_set_up_integrations(
|
||||
async with hass.timeout.async_timeout(
|
||||
STAGE_1_TIMEOUT, cool_down=COOLDOWN_TIME
|
||||
):
|
||||
await async_setup_multi_components(
|
||||
hass, stage_1_domains, config, setup_started
|
||||
)
|
||||
await async_setup_multi_components(hass, stage_1_domains, config)
|
||||
except asyncio.TimeoutError:
|
||||
_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(
|
||||
STAGE_2_TIMEOUT, cool_down=COOLDOWN_TIME
|
||||
):
|
||||
await async_setup_multi_components(
|
||||
hass, stage_2_domains, config, setup_started
|
||||
)
|
||||
await async_setup_multi_components(hass, stage_2_domains, config)
|
||||
except asyncio.TimeoutError:
|
||||
_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
|
||||
_LOGGER.debug("Waiting for startup to wrap up")
|
||||
try:
|
||||
|
@ -7,16 +7,16 @@ Component design guidelines:
|
||||
format "<DOMAIN>.<OBJECT_ID>".
|
||||
- Each component should publish services only under its own domain.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from homeassistant.core import split_entity_id
|
||||
|
||||
# mypy: allow-untyped-defs
|
||||
from homeassistant.core import HomeAssistant, split_entity_id
|
||||
|
||||
_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.
|
||||
|
||||
If there is no entity id given we will check all.
|
||||
|
@ -1,5 +1,4 @@
|
||||
"""Support for the Abode Security System."""
|
||||
from asyncio import gather
|
||||
from copy import deepcopy
|
||||
from functools import partial
|
||||
|
||||
@ -9,7 +8,7 @@ import abodepy.helpers.timeline as TIMELINE
|
||||
from requests.exceptions import ConnectTimeout, HTTPError
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_REAUTH
|
||||
from homeassistant.config_entries import SOURCE_IMPORT
|
||||
from homeassistant.const import (
|
||||
ATTR_ATTRIBUTION,
|
||||
ATTR_DATE,
|
||||
@ -20,7 +19,7 @@ from homeassistant.const import (
|
||||
CONF_USERNAME,
|
||||
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.dispatcher import dispatcher_send
|
||||
from homeassistant.helpers.entity import Entity
|
||||
@ -124,24 +123,14 @@ async def async_setup_entry(hass, config_entry):
|
||||
)
|
||||
|
||||
except AbodeAuthenticationException as ex:
|
||||
LOGGER.error("Invalid credentials: %s", ex)
|
||||
await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_REAUTH},
|
||||
data=config_entry.data,
|
||||
)
|
||||
return False
|
||||
raise ConfigEntryAuthFailed(f"Invalid credentials: {ex}") from ex
|
||||
|
||||
except (AbodeException, ConnectTimeout, HTTPError) as ex:
|
||||
LOGGER.error("Unable to connect to Abode: %s", ex)
|
||||
raise ConfigEntryNotReady from ex
|
||||
raise ConfigEntryNotReady(f"Unable to connect to Abode: {ex}") from ex
|
||||
|
||||
hass.data[DOMAIN] = AbodeSystem(abode, polling)
|
||||
|
||||
for platform in PLATFORMS:
|
||||
hass.async_create_task(
|
||||
hass.config_entries.async_forward_entry_setup(config_entry, platform)
|
||||
)
|
||||
hass.config_entries.async_setup_platforms(config_entry, PLATFORMS)
|
||||
|
||||
await setup_hass_events(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_TRIGGER_AUTOMATION)
|
||||
|
||||
tasks = []
|
||||
|
||||
for platform in PLATFORMS:
|
||||
tasks.append(
|
||||
hass.config_entries.async_forward_entry_unload(config_entry, platform)
|
||||
)
|
||||
|
||||
await gather(*tasks)
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(
|
||||
config_entry, PLATFORMS
|
||||
)
|
||||
|
||||
await hass.async_add_executor_job(hass.data[DOMAIN].abode.events.stop)
|
||||
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.pop(DOMAIN)
|
||||
|
||||
return True
|
||||
return unload_ok
|
||||
|
||||
|
||||
def setup_hass_services(hass):
|
||||
|
@ -7,5 +7,6 @@
|
||||
"codeowners": ["@shred86"],
|
||||
"homekit": {
|
||||
"models": ["Abode", "Iota"]
|
||||
}
|
||||
},
|
||||
"iot_class": "cloud_push"
|
||||
}
|
||||
|
@ -3,7 +3,22 @@
|
||||
"abort": {
|
||||
"single_instance_allowed": "Solo se permite una \u00fanica configuraci\u00f3n de Abode."
|
||||
},
|
||||
"error": {
|
||||
"invalid_mfa_code": "C\u00f3digo MFA no v\u00e1lido"
|
||||
},
|
||||
"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": {
|
||||
"data": {
|
||||
"password": "Contrase\u00f1a",
|
||||
|
7
homeassistant/components/abode/translations/ro.json
Normal file
7
homeassistant/components/abode/translations/ro.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"reauth_successful": "Re-autentificare efectuata cu succes"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
"""The AccuWeather component."""
|
||||
import asyncio
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
@ -46,23 +45,15 @@ async def async_setup_entry(hass, config_entry) -> bool:
|
||||
UNDO_UPDATE_LISTENER: undo_listener,
|
||||
}
|
||||
|
||||
for platform in PLATFORMS:
|
||||
hass.async_create_task(
|
||||
hass.config_entries.async_forward_entry_setup(config_entry, platform)
|
||||
)
|
||||
hass.config_entries.async_setup_platforms(config_entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass, config_entry):
|
||||
"""Unload a config entry."""
|
||||
unload_ok = all(
|
||||
await asyncio.gather(
|
||||
*[
|
||||
hass.config_entries.async_forward_entry_unload(config_entry, platform)
|
||||
for platform in PLATFORMS
|
||||
]
|
||||
)
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(
|
||||
config_entry, PLATFORMS
|
||||
)
|
||||
|
||||
hass.data[DOMAIN][config_entry.entry_id][UNDO_UPDATE_LISTENER]()
|
||||
|
@ -5,5 +5,6 @@
|
||||
"requirements": ["accuweather==0.1.1"],
|
||||
"codeowners": ["@bieniu"],
|
||||
"config_flow": true,
|
||||
"quality_scale": "platinum"
|
||||
"quality_scale": "platinum",
|
||||
"iot_class": "cloud_polling"
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
"longitude": "L\u00e4ngengrad",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
@ -25,7 +26,9 @@
|
||||
"user": {
|
||||
"data": {
|
||||
"forecast": "Wettervorhersage"
|
||||
}
|
||||
},
|
||||
"description": "Aufgrund der Einschr\u00e4nkungen der kostenlosen Version des AccuWeather-API-Schl\u00fcssels werden bei aktivierter Wettervorhersage Datenaktualisierungen alle 80 Minuten statt alle 40 Minuten durchgef\u00fchrt.",
|
||||
"title": "AccuWeather Optionen"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -16,8 +16,14 @@
|
||||
"data": {
|
||||
"forecast": "Pron\u00f3stico del tiempo"
|
||||
},
|
||||
"description": "Debido a las limitaciones de la versi\u00f3n gratuita de la clave API de AccuWeather, cuando habilita el pron\u00f3stico del tiempo, las actualizaciones de datos se realizar\u00e1n cada 64 minutos en lugar de cada 32 minutos."
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
{
|
||||
"state": {
|
||||
"accuweather__pressure_tendency": {
|
||||
"falling": "Descendente",
|
||||
"rising": "Creciente",
|
||||
"steady": "Firme"
|
||||
}
|
||||
}
|
||||
}
|
@ -3,5 +3,6 @@
|
||||
"name": "Acer Projector",
|
||||
"documentation": "https://www.home-assistant.io/integrations/acer_projector",
|
||||
"requirements": ["pyserial==3.5"],
|
||||
"codeowners": []
|
||||
"codeowners": [],
|
||||
"iot_class": "local_polling"
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
"""The Rollease Acmeda Automate integration."""
|
||||
import asyncio
|
||||
|
||||
from homeassistant import config_entries, core
|
||||
|
||||
@ -23,10 +22,7 @@ async def async_setup_entry(
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
hass.data[DOMAIN][config_entry.entry_id] = hub
|
||||
|
||||
for platform in PLATFORMS:
|
||||
hass.async_create_task(
|
||||
hass.config_entries.async_forward_entry_setup(config_entry, platform)
|
||||
)
|
||||
hass.config_entries.async_setup_platforms(config_entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
@ -37,14 +33,10 @@ async def async_unload_entry(
|
||||
"""Unload a config entry."""
|
||||
hub = hass.data[DOMAIN][config_entry.entry_id]
|
||||
|
||||
unload_ok = all(
|
||||
await asyncio.gather(
|
||||
*[
|
||||
hass.config_entries.async_forward_entry_unload(config_entry, platform)
|
||||
for platform in PLATFORMS
|
||||
]
|
||||
)
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(
|
||||
config_entry, PLATFORMS
|
||||
)
|
||||
|
||||
if not await hub.async_reset():
|
||||
return False
|
||||
|
||||
|
@ -4,7 +4,6 @@
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/acmeda",
|
||||
"requirements": ["aiopulse==0.4.2"],
|
||||
"codeowners": [
|
||||
"@atmurray"
|
||||
]
|
||||
}
|
||||
"codeowners": ["@atmurray"],
|
||||
"iot_class": "local_push"
|
||||
}
|
||||
|
@ -2,5 +2,6 @@
|
||||
"domain": "actiontec",
|
||||
"name": "Actiontec",
|
||||
"documentation": "https://www.home-assistant.io/integrations/actiontec",
|
||||
"codeowners": []
|
||||
"codeowners": [],
|
||||
"iot_class": "local_polling"
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import voluptuous as vol
|
||||
from homeassistant.components.adguard.const import (
|
||||
CONF_FORCE,
|
||||
DATA_ADGUARD_CLIENT,
|
||||
DATA_ADGUARD_VERION,
|
||||
DATA_ADGUARD_VERSION,
|
||||
DOMAIN,
|
||||
SERVICE_ADD_URL,
|
||||
SERVICE_DISABLE_URL,
|
||||
@ -61,17 +61,14 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
session=session,
|
||||
)
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})[DATA_ADGUARD_CLIENT] = adguard
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = {DATA_ADGUARD_CLIENT: adguard}
|
||||
|
||||
try:
|
||||
await adguard.version()
|
||||
except AdGuardHomeConnectionError as exception:
|
||||
raise ConfigEntryNotReady from exception
|
||||
|
||||
for platform in PLATFORMS:
|
||||
hass.async_create_task(
|
||||
hass.config_entries.async_forward_entry_setup(entry, platform)
|
||||
)
|
||||
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
|
||||
|
||||
async def add_url(call) -> None:
|
||||
"""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_REFRESH)
|
||||
|
||||
for platform in PLATFORMS:
|
||||
await hass.config_entries.async_forward_entry_unload(entry, platform)
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
if unload_ok:
|
||||
del hass.data[DOMAIN]
|
||||
|
||||
del hass.data[DOMAIN]
|
||||
|
||||
return True
|
||||
return unload_ok
|
||||
|
||||
|
||||
class AdGuardHomeEntity(Entity):
|
||||
"""Defines a base AdGuard Home entity."""
|
||||
|
||||
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:
|
||||
"""Initialize the AdGuard Home entity."""
|
||||
self._available = True
|
||||
self._enabled_default = enabled_default
|
||||
self._icon = icon
|
||||
self._name = name
|
||||
self._entry = entry
|
||||
self.adguard = adguard
|
||||
|
||||
@property
|
||||
@ -200,6 +202,8 @@ class AdGuardHomeDeviceEntity(AdGuardHomeEntity):
|
||||
},
|
||||
"name": "AdGuard Home",
|
||||
"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",
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ from homeassistant.const import (
|
||||
CONF_USERNAME,
|
||||
CONF_VERIFY_SSL,
|
||||
)
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
from .const import DOMAIN
|
||||
@ -31,7 +32,7 @@ class AdGuardHomeFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
async def _show_setup_form(
|
||||
self, errors: dict[str, str] | None = None
|
||||
) -> dict[str, Any]:
|
||||
) -> FlowResult:
|
||||
"""Show the setup form to the user."""
|
||||
return self.async_show_form(
|
||||
step_id="user",
|
||||
@ -50,7 +51,7 @@ class AdGuardHomeFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
async def _show_hassio_form(
|
||||
self, errors: dict[str, str] | None = None
|
||||
) -> dict[str, Any]:
|
||||
) -> FlowResult:
|
||||
"""Show the Hass.io confirmation form to the user."""
|
||||
return self.async_show_form(
|
||||
step_id="hassio_confirm",
|
||||
@ -61,14 +62,19 @@ class AdGuardHomeFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> dict[str, Any]:
|
||||
) -> FlowResult:
|
||||
"""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:
|
||||
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 = {}
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
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")
|
||||
self._hassio_discovery = discovery_info
|
||||
return await self.async_step_hassio_confirm()
|
||||
|
||||
async def async_step_hassio_confirm(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> dict[str, Any]:
|
||||
"""Confirm Hass.io discovery."""
|
||||
) -> FlowResult:
|
||||
"""Confirm Supervisor discovery."""
|
||||
if user_input is None:
|
||||
return await self._show_hassio_form()
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
DOMAIN = "adguard"
|
||||
|
||||
DATA_ADGUARD_CLIENT = "adguard_client"
|
||||
DATA_ADGUARD_VERION = "adguard_version"
|
||||
DATA_ADGUARD_VERSION = "adguard_version"
|
||||
|
||||
CONF_FORCE = "force"
|
||||
|
||||
|
@ -4,5 +4,6 @@
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/adguard",
|
||||
"requirements": ["adguardhome==0.5.0"],
|
||||
"codeowners": ["@frenck"]
|
||||
"codeowners": ["@frenck"],
|
||||
"iot_class": "local_polling"
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ from homeassistant.exceptions import PlatformNotReady
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
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)
|
||||
PARALLEL_UPDATES = 4
|
||||
@ -26,24 +26,24 @@ async def async_setup_entry(
|
||||
async_add_entities: Callable[[list[Entity], bool], None],
|
||||
) -> None:
|
||||
"""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:
|
||||
version = await adguard.version()
|
||||
except AdGuardHomeConnectionError as exception:
|
||||
raise PlatformNotReady from exception
|
||||
|
||||
hass.data[DOMAIN][DATA_ADGUARD_VERION] = version
|
||||
hass.data[DOMAIN][entry.entry_id][DATA_ADGUARD_VERSION] = version
|
||||
|
||||
sensors = [
|
||||
AdGuardHomeDNSQueriesSensor(adguard),
|
||||
AdGuardHomeBlockedFilteringSensor(adguard),
|
||||
AdGuardHomePercentageBlockedSensor(adguard),
|
||||
AdGuardHomeReplacedParentalSensor(adguard),
|
||||
AdGuardHomeReplacedSafeBrowsingSensor(adguard),
|
||||
AdGuardHomeReplacedSafeSearchSensor(adguard),
|
||||
AdGuardHomeAverageProcessingTimeSensor(adguard),
|
||||
AdGuardHomeRulesCountSensor(adguard),
|
||||
AdGuardHomeDNSQueriesSensor(adguard, entry),
|
||||
AdGuardHomeBlockedFilteringSensor(adguard, entry),
|
||||
AdGuardHomePercentageBlockedSensor(adguard, entry),
|
||||
AdGuardHomeReplacedParentalSensor(adguard, entry),
|
||||
AdGuardHomeReplacedSafeBrowsingSensor(adguard, entry),
|
||||
AdGuardHomeReplacedSafeSearchSensor(adguard, entry),
|
||||
AdGuardHomeAverageProcessingTimeSensor(adguard, entry),
|
||||
AdGuardHomeRulesCountSensor(adguard, entry),
|
||||
]
|
||||
|
||||
async_add_entities(sensors, True)
|
||||
@ -55,6 +55,7 @@ class AdGuardHomeSensor(AdGuardHomeDeviceEntity, SensorEntity):
|
||||
def __init__(
|
||||
self,
|
||||
adguard: AdGuardHome,
|
||||
entry: ConfigEntry,
|
||||
name: str,
|
||||
icon: str,
|
||||
measurement: str,
|
||||
@ -66,7 +67,7 @@ class AdGuardHomeSensor(AdGuardHomeDeviceEntity, SensorEntity):
|
||||
self._unit_of_measurement = unit_of_measurement
|
||||
self.measurement = measurement
|
||||
|
||||
super().__init__(adguard, name, icon, enabled_default)
|
||||
super().__init__(adguard, entry, name, icon, enabled_default)
|
||||
|
||||
@property
|
||||
def unique_id(self) -> str:
|
||||
@ -95,10 +96,15 @@ class AdGuardHomeSensor(AdGuardHomeDeviceEntity, SensorEntity):
|
||||
class AdGuardHomeDNSQueriesSensor(AdGuardHomeSensor):
|
||||
"""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."""
|
||||
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:
|
||||
@ -109,10 +115,11 @@ class AdGuardHomeDNSQueriesSensor(AdGuardHomeSensor):
|
||||
class AdGuardHomeBlockedFilteringSensor(AdGuardHomeSensor):
|
||||
"""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."""
|
||||
super().__init__(
|
||||
adguard,
|
||||
entry,
|
||||
"AdGuard DNS Queries Blocked",
|
||||
"mdi:magnify-close",
|
||||
"blocked_filtering",
|
||||
@ -128,10 +135,11 @@ class AdGuardHomeBlockedFilteringSensor(AdGuardHomeSensor):
|
||||
class AdGuardHomePercentageBlockedSensor(AdGuardHomeSensor):
|
||||
"""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."""
|
||||
super().__init__(
|
||||
adguard,
|
||||
entry,
|
||||
"AdGuard DNS Queries Blocked Ratio",
|
||||
"mdi:magnify-close",
|
||||
"blocked_percentage",
|
||||
@ -147,10 +155,11 @@ class AdGuardHomePercentageBlockedSensor(AdGuardHomeSensor):
|
||||
class AdGuardHomeReplacedParentalSensor(AdGuardHomeSensor):
|
||||
"""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."""
|
||||
super().__init__(
|
||||
adguard,
|
||||
entry,
|
||||
"AdGuard Parental Control Blocked",
|
||||
"mdi:human-male-girl",
|
||||
"blocked_parental",
|
||||
@ -165,10 +174,11 @@ class AdGuardHomeReplacedParentalSensor(AdGuardHomeSensor):
|
||||
class AdGuardHomeReplacedSafeBrowsingSensor(AdGuardHomeSensor):
|
||||
"""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."""
|
||||
super().__init__(
|
||||
adguard,
|
||||
entry,
|
||||
"AdGuard Safe Browsing Blocked",
|
||||
"mdi:shield-half-full",
|
||||
"blocked_safebrowsing",
|
||||
@ -183,10 +193,11 @@ class AdGuardHomeReplacedSafeBrowsingSensor(AdGuardHomeSensor):
|
||||
class AdGuardHomeReplacedSafeSearchSensor(AdGuardHomeSensor):
|
||||
"""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."""
|
||||
super().__init__(
|
||||
adguard,
|
||||
entry,
|
||||
"AdGuard Safe Searches Enforced",
|
||||
"mdi:shield-search",
|
||||
"enforced_safesearch",
|
||||
@ -201,10 +212,11 @@ class AdGuardHomeReplacedSafeSearchSensor(AdGuardHomeSensor):
|
||||
class AdGuardHomeAverageProcessingTimeSensor(AdGuardHomeSensor):
|
||||
"""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."""
|
||||
super().__init__(
|
||||
adguard,
|
||||
entry,
|
||||
"AdGuard Average Processing Speed",
|
||||
"mdi:speedometer",
|
||||
"average_speed",
|
||||
@ -220,10 +232,11 @@ class AdGuardHomeAverageProcessingTimeSensor(AdGuardHomeSensor):
|
||||
class AdGuardHomeRulesCountSensor(AdGuardHomeSensor):
|
||||
"""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."""
|
||||
super().__init__(
|
||||
adguard,
|
||||
entry,
|
||||
"AdGuard Rules Count",
|
||||
"mdi:counter",
|
||||
"rules_count",
|
||||
|
@ -22,7 +22,7 @@
|
||||
},
|
||||
"abort": {
|
||||
"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%]"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ from homeassistant.exceptions import PlatformNotReady
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
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__)
|
||||
|
||||
@ -28,22 +28,22 @@ async def async_setup_entry(
|
||||
async_add_entities: Callable[[list[Entity], bool], None],
|
||||
) -> None:
|
||||
"""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:
|
||||
version = await adguard.version()
|
||||
except AdGuardHomeConnectionError as exception:
|
||||
raise PlatformNotReady from exception
|
||||
|
||||
hass.data[DOMAIN][DATA_ADGUARD_VERION] = version
|
||||
hass.data[DOMAIN][entry.entry_id][DATA_ADGUARD_VERSION] = version
|
||||
|
||||
switches = [
|
||||
AdGuardHomeProtectionSwitch(adguard),
|
||||
AdGuardHomeFilteringSwitch(adguard),
|
||||
AdGuardHomeParentalSwitch(adguard),
|
||||
AdGuardHomeSafeBrowsingSwitch(adguard),
|
||||
AdGuardHomeSafeSearchSwitch(adguard),
|
||||
AdGuardHomeQueryLogSwitch(adguard),
|
||||
AdGuardHomeProtectionSwitch(adguard, entry),
|
||||
AdGuardHomeFilteringSwitch(adguard, entry),
|
||||
AdGuardHomeParentalSwitch(adguard, entry),
|
||||
AdGuardHomeSafeBrowsingSwitch(adguard, entry),
|
||||
AdGuardHomeSafeSearchSwitch(adguard, entry),
|
||||
AdGuardHomeQueryLogSwitch(adguard, entry),
|
||||
]
|
||||
async_add_entities(switches, True)
|
||||
|
||||
@ -54,6 +54,7 @@ class AdGuardHomeSwitch(AdGuardHomeDeviceEntity, SwitchEntity):
|
||||
def __init__(
|
||||
self,
|
||||
adguard: AdGuardHome,
|
||||
entry: ConfigEntry,
|
||||
name: str,
|
||||
icon: str,
|
||||
key: str,
|
||||
@ -62,7 +63,7 @@ class AdGuardHomeSwitch(AdGuardHomeDeviceEntity, SwitchEntity):
|
||||
"""Initialize AdGuard Home switch."""
|
||||
self._state = False
|
||||
self._key = key
|
||||
super().__init__(adguard, name, icon, enabled_default)
|
||||
super().__init__(adguard, entry, name, icon, enabled_default)
|
||||
|
||||
@property
|
||||
def unique_id(self) -> str:
|
||||
@ -104,10 +105,10 @@ class AdGuardHomeSwitch(AdGuardHomeDeviceEntity, SwitchEntity):
|
||||
class AdGuardHomeProtectionSwitch(AdGuardHomeSwitch):
|
||||
"""Defines a AdGuard Home protection switch."""
|
||||
|
||||
def __init__(self, adguard: AdGuardHome) -> None:
|
||||
def __init__(self, adguard: AdGuardHome, entry: ConfigEntry) -> None:
|
||||
"""Initialize AdGuard Home switch."""
|
||||
super().__init__(
|
||||
adguard, "AdGuard Protection", "mdi:shield-check", "protection"
|
||||
adguard, entry, "AdGuard Protection", "mdi:shield-check", "protection"
|
||||
)
|
||||
|
||||
async def _adguard_turn_off(self) -> None:
|
||||
@ -126,10 +127,10 @@ class AdGuardHomeProtectionSwitch(AdGuardHomeSwitch):
|
||||
class AdGuardHomeParentalSwitch(AdGuardHomeSwitch):
|
||||
"""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."""
|
||||
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:
|
||||
@ -148,10 +149,10 @@ class AdGuardHomeParentalSwitch(AdGuardHomeSwitch):
|
||||
class AdGuardHomeSafeSearchSwitch(AdGuardHomeSwitch):
|
||||
"""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."""
|
||||
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:
|
||||
@ -170,10 +171,10 @@ class AdGuardHomeSafeSearchSwitch(AdGuardHomeSwitch):
|
||||
class AdGuardHomeSafeBrowsingSwitch(AdGuardHomeSwitch):
|
||||
"""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."""
|
||||
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:
|
||||
@ -192,9 +193,11 @@ class AdGuardHomeSafeBrowsingSwitch(AdGuardHomeSwitch):
|
||||
class AdGuardHomeFilteringSwitch(AdGuardHomeSwitch):
|
||||
"""Defines a AdGuard Home filtering switch."""
|
||||
|
||||
def __init__(self, adguard: AdGuardHome) -> None:
|
||||
def __init__(self, adguard: AdGuardHome, entry: ConfigEntry) -> None:
|
||||
"""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:
|
||||
"""Turn off the switch."""
|
||||
@ -212,10 +215,11 @@ class AdGuardHomeFilteringSwitch(AdGuardHomeSwitch):
|
||||
class AdGuardHomeQueryLogSwitch(AdGuardHomeSwitch):
|
||||
"""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."""
|
||||
super().__init__(
|
||||
adguard,
|
||||
entry,
|
||||
"AdGuard Query Log",
|
||||
"mdi:shield-check",
|
||||
"querylog",
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "El servei ja est\u00e0 configurat",
|
||||
"existing_instance_updated": "S'ha actualitzat la configuraci\u00f3 existent.",
|
||||
"single_instance_allowed": "Ja configurat. Nom\u00e9s \u00e9s possible una sola configuraci\u00f3."
|
||||
},
|
||||
@ -9,8 +10,8 @@
|
||||
},
|
||||
"step": {
|
||||
"hassio_confirm": {
|
||||
"description": "Vols configurar Home Assistant perqu\u00e8 es connecti amb l'AdGuard Home proporcionat pel complement de Hass.io: {addon}?",
|
||||
"title": "AdGuard Home via complement de Hass.io"
|
||||
"description": "Vols configurar Home Assistant perqu\u00e8 es connecti amb l'AdGuard Home proporcionat pel complement: {addon}?",
|
||||
"title": "AdGuard Home via complement de Home Assistant"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Slu\u017eba je ji\u017e nastavena",
|
||||
"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."
|
||||
},
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Service is already configured",
|
||||
"existing_instance_updated": "Updated existing configuration.",
|
||||
"single_instance_allowed": "Already configured. Only a single configuration possible."
|
||||
},
|
||||
@ -9,8 +10,8 @@
|
||||
},
|
||||
"step": {
|
||||
"hassio_confirm": {
|
||||
"description": "Do you want to configure Home Assistant to connect to the AdGuard Home provided by the Hass.io add-on: {addon}?",
|
||||
"title": "AdGuard Home via Hass.io add-on"
|
||||
"description": "Do you want to configure Home Assistant to connect to the AdGuard Home provided by the add-on: {addon}?",
|
||||
"title": "AdGuard Home via Home Assistant add-on"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "El servicio ya est\u00e1 configurado",
|
||||
"existing_instance_updated": "Se ha actualizado la configuraci\u00f3n existente.",
|
||||
"single_instance_allowed": "S\u00f3lo se permite una \u00fanica configuraci\u00f3n de AdGuard Home."
|
||||
},
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Teenus on juba seadistatud",
|
||||
"existing_instance_updated": "Olemasolevad seaded v\u00e4rskendatud.",
|
||||
"single_instance_allowed": "Juba seadistatud. V\u00f5imalik on ainult \u00fcks seadistamine."
|
||||
},
|
||||
@ -9,8 +10,8 @@
|
||||
},
|
||||
"step": {
|
||||
"hassio_confirm": {
|
||||
"description": "Kas soovid seadistada Home Assistant-i \u00fchenduse AdGuard Home'iga mida pakub Hass.io lisandmoodul: {addon} ?",
|
||||
"title": "AdGuard Home Hass.io lisandmooduli abil"
|
||||
"description": "Kas soovid seadistada Home Assistant-i \u00fchenduse AdGuard Home'iga mida pakub lisandmoodul: {addon} ?",
|
||||
"title": "AdGuard Home Home Assistanti lisandmooduli abil"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Il servizio \u00e8 gi\u00e0 configurato",
|
||||
"existing_instance_updated": "Configurazione esistente aggiornata.",
|
||||
"single_instance_allowed": "Gi\u00e0 configurato. \u00c8 possibile una sola configurazione."
|
||||
},
|
||||
@ -9,8 +10,8 @@
|
||||
},
|
||||
"step": {
|
||||
"hassio_confirm": {
|
||||
"description": "Vuoi configurare Home Assistant per connettersi ad AdGuard Home fornito dal componente aggiuntivo di Hass.io: {addon}?",
|
||||
"title": "AdGuard Home tramite il componente aggiuntivo di Hass.io"
|
||||
"description": "Vuoi configurare Home Assistant per connettersi ad AdGuard Home fornito dal componente aggiuntivo: {addon}?",
|
||||
"title": "AdGuard Home tramite il componente aggiuntivo di Home Assistant"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"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.",
|
||||
"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": {
|
||||
"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?",
|
||||
"title": "Hass.io \uc560\ub4dc\uc628\uc758 AdGuard Home"
|
||||
"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": "Home Assistant \uc560\ub4dc\uc628\uc758 AdGuard Home"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Service is al geconfigureerd",
|
||||
"existing_instance_updated": "Bestaande configuratie bijgewerkt.",
|
||||
"single_instance_allowed": "Slechts \u00e9\u00e9n configuratie van AdGuard Home is toegestaan."
|
||||
},
|
||||
@ -9,8 +10,8 @@
|
||||
},
|
||||
"step": {
|
||||
"hassio_confirm": {
|
||||
"description": "Wilt u Home Assistant configureren om verbinding te maken met AdGuard Home van de Supervisor-add-on: {addon}?",
|
||||
"title": "AdGuard Home via Supervisor add-on"
|
||||
"description": "Wilt u Home Assistant configureren om verbinding te maken met AdGuard Home van de Home Assistant add-on: {addon}?",
|
||||
"title": "AdGuard Home via Home Assistant add-on"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Tjenesten er allerede konfigurert",
|
||||
"existing_instance_updated": "Oppdatert eksisterende konfigurasjon.",
|
||||
"single_instance_allowed": "Allerede konfigurert. Bare \u00e9n enkelt konfigurasjon er mulig."
|
||||
},
|
||||
@ -9,8 +10,8 @@
|
||||
},
|
||||
"step": {
|
||||
"hassio_confirm": {
|
||||
"description": "Vil du konfigurere Home Assistant for \u00e5 koble til AdGuard Home levert av Hass.io-tillegget: {addon} ?",
|
||||
"title": "AdGuard Home via Hass.io-tillegg"
|
||||
"description": "Vil du konfigurere Home Assistant til \u00e5 koble til AdGuard Home levert av tillegget: {addon} ?",
|
||||
"title": "AdGuard Home via Home Assistant-tillegg"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Us\u0142uga jest ju\u017c skonfigurowana",
|
||||
"existing_instance_updated": "Zaktualizowano istniej\u0105c\u0105 konfiguracj\u0119",
|
||||
"single_instance_allowed": "Ju\u017c skonfigurowano. Mo\u017cliwa jest tylko jedna konfiguracja."
|
||||
},
|
||||
@ -9,8 +10,8 @@
|
||||
},
|
||||
"step": {
|
||||
"hassio_confirm": {
|
||||
"description": "Czy chcesz skonfigurowa\u0107 Home Assistanta, aby po\u0142\u0105czy\u0142 si\u0119 z AdGuard Home przez dodatek Hass.io: {addon}?",
|
||||
"title": "AdGuard Home przez dodatek Hass.io"
|
||||
"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 Home Assistant"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"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.",
|
||||
"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": {
|
||||
"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)"
|
||||
},
|
||||
"user": {
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "\u670d\u52d9\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210",
|
||||
"existing_instance_updated": "\u5df2\u66f4\u65b0\u73fe\u6709\u8a2d\u5b9a\u3002",
|
||||
"single_instance_allowed": "\u50c5\u80fd\u8a2d\u5b9a\u4e00\u7d44\u88dd\u7f6e\u3002"
|
||||
},
|
||||
@ -9,8 +10,8 @@
|
||||
},
|
||||
"step": {
|
||||
"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",
|
||||
"title": "\u4f7f\u7528 Hass.io \u9644\u52a0\u5143\u4ef6 AdGuard Home"
|
||||
"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 Home Assistant \u9644\u52a0\u5143\u4ef6 AdGuard Home"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
|
@ -3,5 +3,6 @@
|
||||
"name": "ADS",
|
||||
"documentation": "https://www.home-assistant.io/integrations/ads",
|
||||
"requirements": ["pyads==3.2.2"],
|
||||
"codeowners": []
|
||||
"codeowners": [],
|
||||
"iot_class": "local_push"
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
"""Advantage Air climate integration."""
|
||||
|
||||
import asyncio
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
@ -58,24 +57,14 @@ async def async_setup_entry(hass, entry):
|
||||
"async_change": async_change,
|
||||
}
|
||||
|
||||
for platform in PLATFORMS:
|
||||
hass.async_create_task(
|
||||
hass.config_entries.async_forward_entry_setup(entry, platform)
|
||||
)
|
||||
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass, entry):
|
||||
"""Unload Advantage Air Config."""
|
||||
unload_ok = all(
|
||||
await asyncio.gather(
|
||||
*[
|
||||
hass.config_entries.async_forward_entry_unload(entry, platform)
|
||||
for platform in PLATFORMS
|
||||
]
|
||||
)
|
||||
)
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
if unload_ok:
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
@ -24,6 +24,9 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
# Only add motion sensor when motion is enabled
|
||||
if zone["motionConfig"] >= 2:
|
||||
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)
|
||||
|
||||
|
||||
@ -73,3 +76,27 @@ class AdvantageAirZoneMotion(AdvantageAirEntity, BinarySensorEntity):
|
||||
def is_on(self):
|
||||
"""Return if motion is detect."""
|
||||
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
|
||||
|
@ -15,6 +15,7 @@ from homeassistant.components.climate.const import (
|
||||
SUPPORT_TARGET_TEMPERATURE,
|
||||
)
|
||||
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, TEMP_CELSIUS
|
||||
from homeassistant.helpers import entity_platform
|
||||
|
||||
from .const import (
|
||||
ADVANTAGE_AIR_STATE_CLOSE,
|
||||
@ -49,6 +50,7 @@ AC_HVAC_MODES = [
|
||||
HVAC_MODE_FAN_ONLY,
|
||||
HVAC_MODE_DRY,
|
||||
]
|
||||
ADVANTAGE_AIR_SERVICE_SET_MYZONE = "set_myzone"
|
||||
ZONE_HVAC_MODES = [HVAC_MODE_OFF, HVAC_MODE_FAN_ONLY]
|
||||
|
||||
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))
|
||||
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):
|
||||
"""AdvantageAir Climate class."""
|
||||
@ -233,3 +242,9 @@ class AdvantageAirZone(AdvantageAirClimateEntity):
|
||||
await self.async_change(
|
||||
{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"]}}}
|
||||
)
|
||||
|
@ -5,5 +5,6 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/advantage_air",
|
||||
"codeowners": ["@Bre77"],
|
||||
"requirements": ["advantage_air==0.2.1"],
|
||||
"quality_scale": "platinum"
|
||||
"quality_scale": "platinum",
|
||||
"iot_class": "local_polling"
|
||||
}
|
||||
|
@ -1,9 +1,18 @@
|
||||
set_time_to:
|
||||
name: Set Time To
|
||||
description: Control timers to turn the system on or off after a set number of minutes
|
||||
target:
|
||||
entity:
|
||||
integration: advantage_air
|
||||
domain: sensor
|
||||
fields:
|
||||
entity_id:
|
||||
description: Time To sensor entity
|
||||
example: "sensor.ac_time_to_on"
|
||||
minutes:
|
||||
description: Minutes until action
|
||||
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
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "\u88dd\u7f6e\u7d93\u8a2d\u5b9a\u5b8c\u6210"
|
||||
"already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u9023\u7dda\u5931\u6557"
|
||||
|
@ -1,5 +1,4 @@
|
||||
"""The AEMET OpenData component."""
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
for platform in PLATFORMS:
|
||||
hass.async_create_task(
|
||||
hass.config_entries.async_forward_entry_setup(config_entry, platform)
|
||||
)
|
||||
hass.config_entries.async_setup_platforms(config_entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry):
|
||||
"""Unload a config entry."""
|
||||
unload_ok = all(
|
||||
await asyncio.gather(
|
||||
*[
|
||||
hass.config_entries.async_forward_entry_unload(config_entry, platform)
|
||||
for platform in PLATFORMS
|
||||
]
|
||||
)
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(
|
||||
config_entry, PLATFORMS
|
||||
)
|
||||
|
||||
if unload_ok:
|
||||
hass.data[DOMAIN].pop(config_entry.entry_id)
|
||||
|
||||
|
@ -4,5 +4,6 @@
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/aemet",
|
||||
"requirements": ["AEMET-OpenData==0.1.8"],
|
||||
"codeowners": ["@noltari"]
|
||||
"codeowners": ["@noltari"],
|
||||
"iot_class": "cloud_polling"
|
||||
}
|
||||
|
12
homeassistant/components/aemet/translations/es-419.json
Normal file
12
homeassistant/components/aemet/translations/es-419.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"name": "Nombre de la integraci\u00f3n"
|
||||
},
|
||||
"title": "AEMET OpenData"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -239,7 +239,7 @@ class WeatherUpdateCoordinator(DataUpdateCoordinator):
|
||||
return None
|
||||
|
||||
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_utc = dt_util.utcnow()
|
||||
|
@ -3,5 +3,6 @@
|
||||
"name": "AfterShip",
|
||||
"documentation": "https://www.home-assistant.io/integrations/aftership",
|
||||
"requirements": ["pyaftership==0.1.2"],
|
||||
"codeowners": []
|
||||
"codeowners": [],
|
||||
"iot_class": "cloud_polling"
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
"""Support for Agent."""
|
||||
import asyncio
|
||||
|
||||
from agent import AgentError
|
||||
from agent.a import Agent
|
||||
@ -47,24 +46,14 @@ async def async_setup_entry(hass, config_entry):
|
||||
sw_version=agent_client.version,
|
||||
)
|
||||
|
||||
for forward in FORWARDS:
|
||||
hass.async_create_task(
|
||||
hass.config_entries.async_forward_entry_setup(config_entry, forward)
|
||||
)
|
||||
hass.config_entries.async_setup_platforms(config_entry, FORWARDS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass, config_entry):
|
||||
"""Unload a config entry."""
|
||||
unload_ok = all(
|
||||
await asyncio.gather(
|
||||
*[
|
||||
hass.config_entries.async_forward_entry_unload(config_entry, forward)
|
||||
for forward in FORWARDS
|
||||
]
|
||||
)
|
||||
)
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(config_entry, FORWARDS)
|
||||
|
||||
await hass.data[AGENT_DOMAIN][config_entry.entry_id][CONNECTION].close()
|
||||
|
||||
|
@ -4,5 +4,6 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/agent_dvr/",
|
||||
"requirements": ["agent-py==0.0.23"],
|
||||
"config_flow": true,
|
||||
"codeowners": ["@ispysoftware"]
|
||||
"codeowners": ["@ispysoftware"],
|
||||
"iot_class": "local_polling"
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "\u88dd\u7f6e\u7d93\u8a2d\u5b9a\u5b8c\u6210"
|
||||
"already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210"
|
||||
},
|
||||
"error": {
|
||||
"already_in_progress": "\u8a2d\u5b9a\u5df2\u7d93\u9032\u884c\u4e2d",
|
||||
|
@ -2,5 +2,6 @@
|
||||
"domain": "air_quality",
|
||||
"name": "Air Quality",
|
||||
"documentation": "https://www.home-assistant.io/integrations/air_quality",
|
||||
"codeowners": []
|
||||
"codeowners": [],
|
||||
"quality_scale": "internal"
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
"""The Airly integration."""
|
||||
import asyncio
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
from math import ceil
|
||||
@ -12,6 +11,7 @@ import async_timeout
|
||||
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from .const import (
|
||||
ATTR_API_ADVICE,
|
||||
@ -20,7 +20,8 @@ from .const import (
|
||||
ATTR_API_CAQI_LEVEL,
|
||||
CONF_USE_NEAREST,
|
||||
DOMAIN,
|
||||
MAX_REQUESTS_PER_DAY,
|
||||
MAX_UPDATE_INTERVAL,
|
||||
MIN_UPDATE_INTERVAL,
|
||||
NO_AIRLY_SENSORS,
|
||||
)
|
||||
|
||||
@ -29,15 +30,30 @@ PLATFORMS = ["air_quality", "sensor"]
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def set_update_interval(hass, instances):
|
||||
"""Set update_interval to another configured Airly instances."""
|
||||
# We check how many Airly configured instances are and calculate interval to not
|
||||
# exceed allowed numbers of requests.
|
||||
interval = timedelta(minutes=ceil(24 * 60 / MAX_REQUESTS_PER_DAY) * instances)
|
||||
def set_update_interval(instances, requests_remaining):
|
||||
"""
|
||||
Return data update interval.
|
||||
|
||||
if hass.data.get(DOMAIN):
|
||||
for instance in hass.data[DOMAIN].values():
|
||||
instance.update_interval = interval
|
||||
The number of requests is reset at midnight UTC so we calculate the update
|
||||
interval based on number of minutes until midnight, the number of Airly instances
|
||||
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
|
||||
|
||||
@ -56,10 +72,8 @@ async def async_setup_entry(hass, config_entry):
|
||||
)
|
||||
|
||||
websession = async_get_clientsession(hass)
|
||||
# Change update_interval for other Airly instances
|
||||
update_interval = set_update_interval(
|
||||
hass, len(hass.config_entries.async_entries(DOMAIN))
|
||||
)
|
||||
|
||||
update_interval = timedelta(minutes=MIN_UPDATE_INTERVAL)
|
||||
|
||||
coordinator = AirlyDataUpdateCoordinator(
|
||||
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[DOMAIN][config_entry.entry_id] = coordinator
|
||||
|
||||
for platform in PLATFORMS:
|
||||
hass.async_create_task(
|
||||
hass.config_entries.async_forward_entry_setup(config_entry, platform)
|
||||
)
|
||||
hass.config_entries.async_setup_platforms(config_entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass, config_entry):
|
||||
"""Unload a config entry."""
|
||||
unload_ok = all(
|
||||
await asyncio.gather(
|
||||
*[
|
||||
hass.config_entries.async_forward_entry_unload(config_entry, platform)
|
||||
for platform in PLATFORMS
|
||||
]
|
||||
)
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(
|
||||
config_entry, PLATFORMS
|
||||
)
|
||||
|
||||
if unload_ok:
|
||||
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
|
||||
|
||||
|
||||
@ -140,6 +144,14 @@ class AirlyDataUpdateCoordinator(DataUpdateCoordinator):
|
||||
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"]
|
||||
index = measurements.current["indexes"][0]
|
||||
standards = measurements.current["standards"]
|
||||
|
@ -24,5 +24,6 @@ DEFAULT_NAME = "Airly"
|
||||
DOMAIN = "airly"
|
||||
LABEL_ADVICE = "advice"
|
||||
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."
|
||||
|
@ -5,5 +5,6 @@
|
||||
"codeowners": ["@bieniu"],
|
||||
"requirements": ["airly==1.1.0"],
|
||||
"config_flow": true,
|
||||
"quality_scale": "platinum"
|
||||
"quality_scale": "platinum",
|
||||
"iot_class": "cloud_polling"
|
||||
}
|
||||
|
@ -18,5 +18,11 @@
|
||||
"title": "Airly"
|
||||
}
|
||||
}
|
||||
},
|
||||
"system_health": {
|
||||
"info": {
|
||||
"requests_per_day": "Solicitudes permitidas por d\u00eda",
|
||||
"requests_remaining": "Solicitudes permitidas restantes"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
"""The AirNow integration."""
|
||||
import asyncio
|
||||
import datetime
|
||||
import logging
|
||||
|
||||
@ -60,24 +59,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
hass.data[DOMAIN][entry.entry_id] = coordinator
|
||||
|
||||
for platform in PLATFORMS:
|
||||
hass.async_create_task(
|
||||
hass.config_entries.async_forward_entry_setup(entry, platform)
|
||||
)
|
||||
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||
"""Unload a config entry."""
|
||||
unload_ok = all(
|
||||
await asyncio.gather(
|
||||
*[
|
||||
hass.config_entries.async_forward_entry_unload(entry, platform)
|
||||
for platform in PLATFORMS
|
||||
]
|
||||
)
|
||||
)
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
if unload_ok:
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
|
@ -3,10 +3,7 @@
|
||||
"name": "AirNow",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/airnow",
|
||||
"requirements": [
|
||||
"pyairnow==1.1.0"
|
||||
],
|
||||
"codeowners": [
|
||||
"@asymworks"
|
||||
]
|
||||
"requirements": ["pyairnow==1.1.0"],
|
||||
"codeowners": ["@asymworks"],
|
||||
"iot_class": "cloud_polling"
|
||||
}
|
||||
|
17
homeassistant/components/airnow/translations/es-419.json
Normal file
17
homeassistant/components/airnow/translations/es-419.json
Normal 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"
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "\u88dd\u7f6e\u7d93\u8a2d\u5b9a\u5b8c\u6210"
|
||||
"already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u9023\u7dda\u5931\u6557",
|
||||
|
@ -1,5 +1,4 @@
|
||||
"""The airvisual component."""
|
||||
import asyncio
|
||||
from datetime import timedelta
|
||||
from math import ceil
|
||||
|
||||
@ -11,7 +10,6 @@ from pyairvisual.errors import (
|
||||
NodeProError,
|
||||
)
|
||||
|
||||
from homeassistant.config_entries import SOURCE_REAUTH
|
||||
from homeassistant.const import (
|
||||
ATTR_ATTRIBUTION,
|
||||
CONF_API_KEY,
|
||||
@ -23,6 +21,7 @@ from homeassistant.const import (
|
||||
CONF_STATE,
|
||||
)
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed
|
||||
from homeassistant.helpers import aiohttp_client, config_validation as cv
|
||||
from homeassistant.helpers.update_coordinator import (
|
||||
CoordinatorEntity,
|
||||
@ -206,27 +205,8 @@ async def async_setup_entry(hass, config_entry):
|
||||
|
||||
try:
|
||||
return await api_coro
|
||||
except (InvalidKeyError, KeyExpiredError):
|
||||
matching_flows = [
|
||||
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 (InvalidKeyError, KeyExpiredError) as ex:
|
||||
raise ConfigEntryAuthFailed from ex
|
||||
except AirVisualError as 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]
|
||||
)
|
||||
|
||||
for platform in PLATFORMS:
|
||||
hass.async_create_task(
|
||||
hass.config_entries.async_forward_entry_setup(config_entry, platform)
|
||||
)
|
||||
hass.config_entries.async_setup_platforms(config_entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
@ -329,14 +306,10 @@ async def async_migrate_entry(hass, config_entry):
|
||||
|
||||
async def async_unload_entry(hass, config_entry):
|
||||
"""Unload an AirVisual config entry."""
|
||||
unload_ok = all(
|
||||
await asyncio.gather(
|
||||
*[
|
||||
hass.config_entries.async_forward_entry_unload(config_entry, platform)
|
||||
for platform in PLATFORMS
|
||||
]
|
||||
)
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(
|
||||
config_entry, PLATFORMS
|
||||
)
|
||||
|
||||
if unload_ok:
|
||||
hass.data[DOMAIN][DATA_COORDINATOR].pop(config_entry.entry_id)
|
||||
remove_listener = hass.data[DOMAIN][DATA_LISTENER].pop(config_entry.entry_id)
|
||||
|
@ -3,6 +3,7 @@
|
||||
"name": "AirVisual",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/airvisual",
|
||||
"requirements": ["pyairvisual==5.0.4"],
|
||||
"codeowners": ["@bachya"]
|
||||
"requirements": ["pyairvisual==5.0.8"],
|
||||
"codeowners": ["@bachya"],
|
||||
"iot_class": "cloud_polling"
|
||||
}
|
||||
|
@ -5,7 +5,8 @@
|
||||
},
|
||||
"error": {
|
||||
"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": {
|
||||
"geography": {
|
||||
@ -17,6 +18,10 @@
|
||||
"description": "Use la API de AirVisual para monitorear una ubicaci\u00f3n geogr\u00e1fica.",
|
||||
"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": {
|
||||
"data": {
|
||||
"ip_address": "Direcci\u00f3n IP/nombre de host de la unidad",
|
||||
|
@ -23,7 +23,8 @@
|
||||
"geography_by_name": {
|
||||
"data": {
|
||||
"city": "Stad",
|
||||
"country": "Land"
|
||||
"country": "Land",
|
||||
"state": "Kanton"
|
||||
}
|
||||
},
|
||||
"node_pro": {
|
||||
|
@ -5,12 +5,6 @@
|
||||
"invalid_api_key": "Ogiltig API-nyckel"
|
||||
},
|
||||
"step": {
|
||||
"geography": {
|
||||
"data": {
|
||||
"latitude": "Latitud",
|
||||
"longitude": "Longitud"
|
||||
}
|
||||
},
|
||||
"node_pro": {
|
||||
"data": {
|
||||
"ip_address": "Enhets IP-adress / v\u00e4rdnamn",
|
||||
|
@ -3,5 +3,6 @@
|
||||
"name": "Aladdin Connect",
|
||||
"documentation": "https://www.home-assistant.io/integrations/aladdin_connect",
|
||||
"requirements": ["aladdin_connect==0.3"],
|
||||
"codeowners": []
|
||||
"codeowners": [],
|
||||
"iot_class": "cloud_polling"
|
||||
}
|
||||
|
@ -2,8 +2,9 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from collections.abc import Iterable
|
||||
import logging
|
||||
from typing import Any, Iterable
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
|
@ -1,5 +1,4 @@
|
||||
"""Support for AlarmDecoder devices."""
|
||||
import asyncio
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
@ -14,7 +13,7 @@ from homeassistant.const import (
|
||||
CONF_PROTOCOL,
|
||||
EVENT_HOMEASSISTANT_STOP,
|
||||
)
|
||||
from homeassistant.helpers.typing import HomeAssistantType
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from .const import (
|
||||
@ -39,7 +38,7 @@ _LOGGER = logging.getLogger(__name__)
|
||||
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."""
|
||||
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()
|
||||
|
||||
for platform in PLATFORMS:
|
||||
hass.async_create_task(
|
||||
hass.config_entries.async_forward_entry_setup(entry, platform)
|
||||
)
|
||||
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistantType, entry: ConfigEntry):
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||
"""Unload a AlarmDecoder entry."""
|
||||
hass.data[DOMAIN][entry.entry_id][DATA_RESTART] = False
|
||||
|
||||
unload_ok = all(
|
||||
await asyncio.gather(
|
||||
*[
|
||||
hass.config_entries.async_forward_entry_unload(entry, platform)
|
||||
for platform in PLATFORMS
|
||||
]
|
||||
)
|
||||
)
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
if not unload_ok:
|
||||
return False
|
||||
@ -160,7 +150,7 @@ async def async_unload_entry(hass: HomeAssistantType, entry: ConfigEntry):
|
||||
return True
|
||||
|
||||
|
||||
async def _update_listener(hass: HomeAssistantType, entry: ConfigEntry):
|
||||
async def _update_listener(hass: HomeAssistant, entry: ConfigEntry):
|
||||
"""Handle options update."""
|
||||
_LOGGER.debug("AlarmDecoder options updated: %s", entry.as_dict()["options"])
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
|
@ -19,9 +19,9 @@ from homeassistant.const import (
|
||||
STATE_ALARM_DISARMED,
|
||||
STATE_ALARM_TRIGGERED,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_platform
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.typing import HomeAssistantType
|
||||
|
||||
from .const import (
|
||||
CONF_ALT_NIGHT_MODE,
|
||||
@ -41,7 +41,7 @@ ATTR_KEYPRESS = "keypress"
|
||||
|
||||
|
||||
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."""
|
||||
options = entry.options
|
||||
|
@ -3,7 +3,7 @@ import logging
|
||||
|
||||
from homeassistant.components.binary_sensor import BinarySensorEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.helpers.typing import HomeAssistantType
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import (
|
||||
CONF_RELAY_ADDR,
|
||||
@ -34,7 +34,7 @@ ATTR_RF_LOOP1 = "rf_loop1"
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistantType, entry: ConfigEntry, async_add_entities
|
||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities
|
||||
):
|
||||
"""Set up for AlarmDecoder sensor."""
|
||||
|
||||
|
@ -4,5 +4,6 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/alarmdecoder",
|
||||
"requirements": ["adext==0.4.1"],
|
||||
"codeowners": ["@ajschmidt8"],
|
||||
"config_flow": true
|
||||
"config_flow": true,
|
||||
"iot_class": "local_push"
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
"""Support for AlarmDecoder sensors (Shows Panel Display)."""
|
||||
from homeassistant.components.sensor import SensorEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.helpers.typing import HomeAssistantType
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import SIGNAL_PANEL_MESSAGE
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistantType, entry: ConfigEntry, async_add_entities
|
||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities
|
||||
):
|
||||
"""Set up for AlarmDecoder sensor."""
|
||||
|
||||
|
@ -14,7 +14,8 @@
|
||||
"user": {
|
||||
"data": {
|
||||
"protocol": "Protocolo"
|
||||
}
|
||||
},
|
||||
"title": "Elija el protocolo AlarmDecoder"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "\u88dd\u7f6e\u7d93\u8a2d\u5b9a\u5b8c\u6210"
|
||||
"already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210"
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "\u6210\u529f\u9023\u7dda\u81f3 AlarmDecoder\u3002"
|
||||
|
@ -4,5 +4,6 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/alert",
|
||||
"after_dependencies": ["notify"],
|
||||
"codeowners": [],
|
||||
"quality_scale": "internal"
|
||||
"quality_scale": "internal",
|
||||
"iot_class": "local_push"
|
||||
}
|
||||
|
@ -2,8 +2,9 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from collections.abc import Iterable
|
||||
import logging
|
||||
from typing import Any, Iterable
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
|
@ -504,12 +504,12 @@ class LightCapabilities(AlexaEntity):
|
||||
"""Yield the supported interfaces."""
|
||||
yield AlexaPowerController(self.entity)
|
||||
|
||||
supported = self.entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
|
||||
if supported & light.SUPPORT_BRIGHTNESS:
|
||||
color_modes = self.entity.attributes.get(light.ATTR_SUPPORTED_COLOR_MODES)
|
||||
if light.brightness_supported(color_modes):
|
||||
yield AlexaBrightnessController(self.entity)
|
||||
if supported & light.SUPPORT_COLOR:
|
||||
if light.color_supported(color_modes):
|
||||
yield AlexaColorController(self.entity)
|
||||
if supported & light.SUPPORT_COLOR_TEMP:
|
||||
if light.color_temp_supported(color_modes):
|
||||
yield AlexaColorTemperatureController(self.entity)
|
||||
|
||||
yield AlexaEndpointHealth(self.hass, self.entity)
|
||||
|
@ -1374,10 +1374,7 @@ async def async_api_seek(hass, config, directive, context):
|
||||
msg = f"{entity} did not return the current media position."
|
||||
raise AlexaVideoActionNotPermittedForContentError(msg)
|
||||
|
||||
seek_position = int(current_position) + int(position_delta / 1000)
|
||||
|
||||
if seek_position < 0:
|
||||
seek_position = 0
|
||||
seek_position = max(int(current_position) + int(position_delta / 1000), 0)
|
||||
|
||||
media_duration = entity.attributes.get(media_player.ATTR_MEDIA_DURATION)
|
||||
if media_duration and 0 < int(media_duration) < seek_position:
|
||||
|
@ -17,10 +17,10 @@ def async_describe_events(hass, async_describe_event):
|
||||
if entity_id:
|
||||
state = hass.states.get(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:
|
||||
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}
|
||||
|
@ -2,14 +2,8 @@
|
||||
"domain": "alexa",
|
||||
"name": "Amazon Alexa",
|
||||
"documentation": "https://www.home-assistant.io/integrations/alexa",
|
||||
"dependencies": [
|
||||
"http"
|
||||
],
|
||||
"after_dependencies": [
|
||||
"camera"
|
||||
],
|
||||
"codeowners": [
|
||||
"@home-assistant/cloud",
|
||||
"@ochlocracy"
|
||||
]
|
||||
"dependencies": ["http"],
|
||||
"after_dependencies": ["camera"],
|
||||
"codeowners": ["@home-assistant/cloud", "@ochlocracy"],
|
||||
"iot_class": "cloud_push"
|
||||
}
|
||||
|
@ -5,5 +5,6 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/almond",
|
||||
"dependencies": ["http", "conversation"],
|
||||
"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
Loading…
x
Reference in New Issue
Block a user