mirror of
https://github.com/home-assistant/core.git
synced 2025-07-29 08:07:45 +00:00
2023.11.0 (#103164)
This commit is contained in:
commit
0b8d4235c3
@ -45,6 +45,7 @@ base_platforms: &base_platforms
|
||||
- homeassistant/components/switch/**
|
||||
- homeassistant/components/text/**
|
||||
- homeassistant/components/time/**
|
||||
- homeassistant/components/todo/**
|
||||
- homeassistant/components/tts/**
|
||||
- homeassistant/components/update/**
|
||||
- homeassistant/components/vacuum/**
|
||||
@ -96,8 +97,8 @@ components: &components
|
||||
- homeassistant/components/persistent_notification/**
|
||||
- homeassistant/components/person/**
|
||||
- homeassistant/components/recorder/**
|
||||
- homeassistant/components/recovery_mode/**
|
||||
- homeassistant/components/repairs/**
|
||||
- homeassistant/components/safe_mode/**
|
||||
- homeassistant/components/script/**
|
||||
- homeassistant/components/shopping_list/**
|
||||
- homeassistant/components/ssdp/**
|
||||
|
29
.coveragerc
29
.coveragerc
@ -178,6 +178,8 @@ omit =
|
||||
homeassistant/components/comelit/cover.py
|
||||
homeassistant/components/comelit/coordinator.py
|
||||
homeassistant/components/comelit/light.py
|
||||
homeassistant/components/comelit/sensor.py
|
||||
homeassistant/components/comelit/switch.py
|
||||
homeassistant/components/comfoconnect/fan.py
|
||||
homeassistant/components/concord232/alarm_control_panel.py
|
||||
homeassistant/components/concord232/binary_sensor.py
|
||||
@ -284,9 +286,6 @@ omit =
|
||||
homeassistant/components/edl21/__init__.py
|
||||
homeassistant/components/edl21/sensor.py
|
||||
homeassistant/components/egardia/*
|
||||
homeassistant/components/eight_sleep/__init__.py
|
||||
homeassistant/components/eight_sleep/binary_sensor.py
|
||||
homeassistant/components/eight_sleep/sensor.py
|
||||
homeassistant/components/electric_kiwi/__init__.py
|
||||
homeassistant/components/electric_kiwi/api.py
|
||||
homeassistant/components/electric_kiwi/oauth2.py
|
||||
@ -376,6 +375,7 @@ omit =
|
||||
homeassistant/components/fibaro/binary_sensor.py
|
||||
homeassistant/components/fibaro/climate.py
|
||||
homeassistant/components/fibaro/cover.py
|
||||
homeassistant/components/fibaro/event.py
|
||||
homeassistant/components/fibaro/light.py
|
||||
homeassistant/components/fibaro/lock.py
|
||||
homeassistant/components/fibaro/sensor.py
|
||||
@ -541,12 +541,6 @@ omit =
|
||||
homeassistant/components/hvv_departures/__init__.py
|
||||
homeassistant/components/hvv_departures/binary_sensor.py
|
||||
homeassistant/components/hvv_departures/sensor.py
|
||||
homeassistant/components/hydrawise/__init__.py
|
||||
homeassistant/components/hydrawise/binary_sensor.py
|
||||
homeassistant/components/hydrawise/const.py
|
||||
homeassistant/components/hydrawise/coordinator.py
|
||||
homeassistant/components/hydrawise/sensor.py
|
||||
homeassistant/components/hydrawise/switch.py
|
||||
homeassistant/components/ialarm/alarm_control_panel.py
|
||||
homeassistant/components/iammeter/sensor.py
|
||||
homeassistant/components/iaqualink/binary_sensor.py
|
||||
@ -563,7 +557,6 @@ omit =
|
||||
homeassistant/components/ifttt/alarm_control_panel.py
|
||||
homeassistant/components/iglo/light.py
|
||||
homeassistant/components/ihc/*
|
||||
homeassistant/components/imap_email_content/sensor.py
|
||||
homeassistant/components/incomfort/*
|
||||
homeassistant/components/insteon/binary_sensor.py
|
||||
homeassistant/components/insteon/climate.py
|
||||
@ -750,11 +743,6 @@ omit =
|
||||
homeassistant/components/mikrotik/hub.py
|
||||
homeassistant/components/mill/climate.py
|
||||
homeassistant/components/mill/sensor.py
|
||||
homeassistant/components/minecraft_server/__init__.py
|
||||
homeassistant/components/minecraft_server/binary_sensor.py
|
||||
homeassistant/components/minecraft_server/coordinator.py
|
||||
homeassistant/components/minecraft_server/entity.py
|
||||
homeassistant/components/minecraft_server/sensor.py
|
||||
homeassistant/components/minio/minio_helper.py
|
||||
homeassistant/components/mjpeg/camera.py
|
||||
homeassistant/components/mjpeg/util.py
|
||||
@ -795,6 +783,7 @@ omit =
|
||||
homeassistant/components/mystrom/binary_sensor.py
|
||||
homeassistant/components/mystrom/light.py
|
||||
homeassistant/components/mystrom/switch.py
|
||||
homeassistant/components/mystrom/sensor.py
|
||||
homeassistant/components/nad/media_player.py
|
||||
homeassistant/components/nanoleaf/__init__.py
|
||||
homeassistant/components/nanoleaf/button.py
|
||||
@ -835,7 +824,6 @@ omit =
|
||||
homeassistant/components/nibe_heatpump/__init__.py
|
||||
homeassistant/components/nibe_heatpump/climate.py
|
||||
homeassistant/components/nibe_heatpump/binary_sensor.py
|
||||
homeassistant/components/nibe_heatpump/number.py
|
||||
homeassistant/components/nibe_heatpump/select.py
|
||||
homeassistant/components/nibe_heatpump/sensor.py
|
||||
homeassistant/components/nibe_heatpump/switch.py
|
||||
@ -964,6 +952,7 @@ omit =
|
||||
homeassistant/components/ping/__init__.py
|
||||
homeassistant/components/ping/binary_sensor.py
|
||||
homeassistant/components/ping/device_tracker.py
|
||||
homeassistant/components/ping/helpers.py
|
||||
homeassistant/components/pioneer/media_player.py
|
||||
homeassistant/components/plaato/__init__.py
|
||||
homeassistant/components/plaato/binary_sensor.py
|
||||
@ -1000,6 +989,7 @@ omit =
|
||||
homeassistant/components/pushsafer/notify.py
|
||||
homeassistant/components/pyload/sensor.py
|
||||
homeassistant/components/qbittorrent/__init__.py
|
||||
homeassistant/components/qbittorrent/coordinator.py
|
||||
homeassistant/components/qbittorrent/sensor.py
|
||||
homeassistant/components/qnap/__init__.py
|
||||
homeassistant/components/qnap/coordinator.py
|
||||
@ -1122,7 +1112,6 @@ omit =
|
||||
homeassistant/components/sesame/lock.py
|
||||
homeassistant/components/seven_segments/image_processing.py
|
||||
homeassistant/components/seventeentrack/sensor.py
|
||||
homeassistant/components/shiftr/*
|
||||
homeassistant/components/shodan/sensor.py
|
||||
homeassistant/components/sia/__init__.py
|
||||
homeassistant/components/sia/alarm_control_panel.py
|
||||
@ -1276,6 +1265,7 @@ omit =
|
||||
homeassistant/components/switchbot/sensor.py
|
||||
homeassistant/components/switchbot/switch.py
|
||||
homeassistant/components/switchbot/lock.py
|
||||
homeassistant/components/switchbot_cloud/climate.py
|
||||
homeassistant/components/switchbot_cloud/coordinator.py
|
||||
homeassistant/components/switchbot_cloud/entity.py
|
||||
homeassistant/components/switchbot_cloud/switch.py
|
||||
@ -1301,6 +1291,7 @@ omit =
|
||||
homeassistant/components/system_bridge/__init__.py
|
||||
homeassistant/components/system_bridge/binary_sensor.py
|
||||
homeassistant/components/system_bridge/coordinator.py
|
||||
homeassistant/components/system_bridge/media_player.py
|
||||
homeassistant/components/system_bridge/notify.py
|
||||
homeassistant/components/system_bridge/sensor.py
|
||||
homeassistant/components/systemmonitor/sensor.py
|
||||
@ -1400,6 +1391,7 @@ omit =
|
||||
homeassistant/components/trafikverket_weatherstation/coordinator.py
|
||||
homeassistant/components/trafikverket_weatherstation/sensor.py
|
||||
homeassistant/components/transmission/__init__.py
|
||||
homeassistant/components/transmission/coordinator.py
|
||||
homeassistant/components/transmission/sensor.py
|
||||
homeassistant/components/transmission/switch.py
|
||||
homeassistant/components/travisci/sensor.py
|
||||
@ -1474,7 +1466,9 @@ omit =
|
||||
homeassistant/components/vicare/binary_sensor.py
|
||||
homeassistant/components/vicare/button.py
|
||||
homeassistant/components/vicare/climate.py
|
||||
homeassistant/components/vicare/entity.py
|
||||
homeassistant/components/vicare/sensor.py
|
||||
homeassistant/components/vicare/utils.py
|
||||
homeassistant/components/vicare/water_heater.py
|
||||
homeassistant/components/vilfo/__init__.py
|
||||
homeassistant/components/vilfo/sensor.py
|
||||
@ -1516,7 +1510,6 @@ omit =
|
||||
homeassistant/components/wiffi/sensor.py
|
||||
homeassistant/components/wiffi/wiffi_strings.py
|
||||
homeassistant/components/wirelesstag/*
|
||||
homeassistant/components/withings/api.py
|
||||
homeassistant/components/wolflink/__init__.py
|
||||
homeassistant/components/wolflink/sensor.py
|
||||
homeassistant/components/worldtidesinfo/sensor.py
|
||||
|
18
.github/workflows/builder.yml
vendored
18
.github/workflows/builder.yml
vendored
@ -24,12 +24,12 @@ jobs:
|
||||
publish: ${{ steps.version.outputs.publish }}
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v4.1.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
uses: actions/setup-python@v4.7.0
|
||||
uses: actions/setup-python@v4.7.1
|
||||
with:
|
||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||
|
||||
@ -56,10 +56,10 @@ jobs:
|
||||
if: github.repository_owner == 'home-assistant' && needs.init.outputs.publish == 'true'
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v4.1.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
uses: actions/setup-python@v4.7.0
|
||||
uses: actions/setup-python@v4.7.1
|
||||
with:
|
||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||
|
||||
@ -98,7 +98,7 @@ jobs:
|
||||
arch: ${{ fromJson(needs.init.outputs.architectures) }}
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v4.1.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: Download nightly wheels of frontend
|
||||
if: needs.init.outputs.channel == 'dev'
|
||||
@ -124,7 +124,7 @@ jobs:
|
||||
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
if: needs.init.outputs.channel == 'dev'
|
||||
uses: actions/setup-python@v4.7.0
|
||||
uses: actions/setup-python@v4.7.1
|
||||
with:
|
||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||
|
||||
@ -252,7 +252,7 @@ jobs:
|
||||
- green
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v4.1.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: Set build additional args
|
||||
run: |
|
||||
@ -289,7 +289,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v4.1.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: Initialize git
|
||||
uses: home-assistant/actions/helpers/git-init@master
|
||||
@ -327,7 +327,7 @@ jobs:
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v4.1.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@v3.1.2
|
||||
|
60
.github/workflows/ci.yaml
vendored
60
.github/workflows/ci.yaml
vendored
@ -37,18 +37,20 @@ env:
|
||||
PIP_CACHE_VERSION: 4
|
||||
MYPY_CACHE_VERSION: 5
|
||||
BLACK_CACHE_VERSION: 1
|
||||
HA_SHORT_VERSION: "2023.10"
|
||||
HA_SHORT_VERSION: "2023.11"
|
||||
DEFAULT_PYTHON: "3.11"
|
||||
ALL_PYTHON_VERSIONS: "['3.11']"
|
||||
ALL_PYTHON_VERSIONS: "['3.11', '3.12']"
|
||||
# 10.3 is the oldest supported version
|
||||
# - 10.3.32 is the version currently shipped with Synology (as of 17 Feb 2022)
|
||||
# 10.6 is the current long-term-support
|
||||
# - 10.6.10 is the version currently shipped with the Add-on (as of 31 Jan 2023)
|
||||
# 10.10 is the latest short-term-support
|
||||
# - 10.10.3 is the latest (as of 6 Feb 2023)
|
||||
# 10.11 is the latest long-term-support
|
||||
# - 10.11.2 is the version currently shipped with Synology (as of 11 Oct 2023)
|
||||
# mysql 8.0.32 does not always behave the same as MariaDB
|
||||
# and some queries that work on MariaDB do not work on MySQL
|
||||
MARIADB_VERSIONS: "['mariadb:10.3.32','mariadb:10.6.10','mariadb:10.10.3','mysql:8.0.32']"
|
||||
MARIADB_VERSIONS: "['mariadb:10.3.32','mariadb:10.6.10','mariadb:10.10.3','mariadb:10.11.2','mysql:8.0.32']"
|
||||
# 12 is the oldest supported version
|
||||
# - 12.14 is the latest (as of 9 Feb 2023)
|
||||
# 15 is the latest version
|
||||
@ -89,7 +91,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v4.1.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
- name: Generate partial Python venv restore key
|
||||
id: generate_python_cache_key
|
||||
run: >-
|
||||
@ -222,10 +224,10 @@ jobs:
|
||||
- info
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v4.1.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
id: python
|
||||
uses: actions/setup-python@v4.7.0
|
||||
uses: actions/setup-python@v4.7.1
|
||||
with:
|
||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||
check-latest: true
|
||||
@ -267,9 +269,9 @@ jobs:
|
||||
- pre-commit
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v4.1.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
uses: actions/setup-python@v4.7.0
|
||||
uses: actions/setup-python@v4.7.1
|
||||
id: python
|
||||
with:
|
||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||
@ -335,9 +337,9 @@ jobs:
|
||||
- pre-commit
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v4.1.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
uses: actions/setup-python@v4.7.0
|
||||
uses: actions/setup-python@v4.7.1
|
||||
id: python
|
||||
with:
|
||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||
@ -384,9 +386,9 @@ jobs:
|
||||
- pre-commit
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v4.1.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
uses: actions/setup-python@v4.7.0
|
||||
uses: actions/setup-python@v4.7.1
|
||||
id: python
|
||||
with:
|
||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||
@ -478,10 +480,10 @@ jobs:
|
||||
python-version: ${{ fromJSON(needs.info.outputs.python_versions) }}
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v4.1.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
id: python
|
||||
uses: actions/setup-python@v4.7.0
|
||||
uses: actions/setup-python@v4.7.1
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
check-latest: true
|
||||
@ -546,10 +548,10 @@ jobs:
|
||||
- base
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v4.1.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
id: python
|
||||
uses: actions/setup-python@v4.7.0
|
||||
uses: actions/setup-python@v4.7.1
|
||||
with:
|
||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||
check-latest: true
|
||||
@ -578,10 +580,10 @@ jobs:
|
||||
- base
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v4.1.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
id: python
|
||||
uses: actions/setup-python@v4.7.0
|
||||
uses: actions/setup-python@v4.7.1
|
||||
with:
|
||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||
check-latest: true
|
||||
@ -611,10 +613,10 @@ jobs:
|
||||
- base
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v4.1.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
id: python
|
||||
uses: actions/setup-python@v4.7.0
|
||||
uses: actions/setup-python@v4.7.1
|
||||
with:
|
||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||
check-latest: true
|
||||
@ -655,10 +657,10 @@ jobs:
|
||||
- base
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v4.1.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
id: python
|
||||
uses: actions/setup-python@v4.7.0
|
||||
uses: actions/setup-python@v4.7.1
|
||||
with:
|
||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||
check-latest: true
|
||||
@ -737,10 +739,10 @@ jobs:
|
||||
bluez \
|
||||
ffmpeg
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v4.1.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
id: python
|
||||
uses: actions/setup-python@v4.7.0
|
||||
uses: actions/setup-python@v4.7.1
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
check-latest: true
|
||||
@ -889,10 +891,10 @@ jobs:
|
||||
ffmpeg \
|
||||
libmariadb-dev-compat
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v4.1.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
id: python
|
||||
uses: actions/setup-python@v4.7.0
|
||||
uses: actions/setup-python@v4.7.1
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
check-latest: true
|
||||
@ -1013,10 +1015,10 @@ jobs:
|
||||
ffmpeg \
|
||||
postgresql-server-dev-14
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v4.1.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
id: python
|
||||
uses: actions/setup-python@v4.7.0
|
||||
uses: actions/setup-python@v4.7.1
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
check-latest: true
|
||||
@ -1108,7 +1110,7 @@ jobs:
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v4.1.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
- name: Download all coverage artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
- name: Upload coverage to Codecov (full coverage)
|
||||
|
39
.github/workflows/codeql.yml
vendored
Normal file
39
.github/workflows/codeql.yml
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
name: "CodeQL"
|
||||
|
||||
# yamllint disable-line rule:truthy
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
- rc
|
||||
- master
|
||||
schedule:
|
||||
- cron: "30 18 * * 4"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 360
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2.22.4
|
||||
with:
|
||||
languages: python
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2.22.4
|
||||
with:
|
||||
category: "/language:python"
|
4
.github/workflows/translations.yml
vendored
4
.github/workflows/translations.yml
vendored
@ -19,10 +19,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v4.1.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
uses: actions/setup-python@v4.7.0
|
||||
uses: actions/setup-python@v4.7.1
|
||||
with:
|
||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||
|
||||
|
51
.github/workflows/wheels.yml
vendored
51
.github/workflows/wheels.yml
vendored
@ -10,8 +10,10 @@ on:
|
||||
- dev
|
||||
- rc
|
||||
paths:
|
||||
- "requirements.txt"
|
||||
- ".github/workflows/wheels.yml"
|
||||
- "homeassistant/package_constraints.txt"
|
||||
- "requirements_all.txt"
|
||||
- "requirements.txt"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref_name}}
|
||||
@ -26,7 +28,7 @@ jobs:
|
||||
architectures: ${{ steps.info.outputs.architectures }}
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v4.1.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: Get information
|
||||
id: info
|
||||
@ -80,11 +82,11 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
abi: ["cp311"]
|
||||
abi: ["cp311", "cp312"]
|
||||
arch: ${{ fromJson(needs.init.outputs.architectures) }}
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v4.1.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: Download env_file
|
||||
uses: actions/download-artifact@v3
|
||||
@ -97,7 +99,7 @@ jobs:
|
||||
name: requirements_diff
|
||||
|
||||
- name: Build wheels
|
||||
uses: home-assistant/wheels@2023.09.1
|
||||
uses: home-assistant/wheels@2023.10.5
|
||||
with:
|
||||
abi: ${{ matrix.abi }}
|
||||
tag: musllinux_1_2
|
||||
@ -110,7 +112,7 @@ jobs:
|
||||
requirements-diff: "requirements_diff.txt"
|
||||
requirements: "requirements.txt"
|
||||
|
||||
integrations_cp311:
|
||||
integrations:
|
||||
name: Build wheels ${{ matrix.abi }} for ${{ matrix.arch }}
|
||||
if: github.repository_owner == 'home-assistant'
|
||||
needs: init
|
||||
@ -118,11 +120,11 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
abi: ["cp311"]
|
||||
abi: ["cp311", "cp312"]
|
||||
arch: ${{ fromJson(needs.init.outputs.architectures) }}
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v4.1.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: Download env_file
|
||||
uses: actions/download-artifact@v3
|
||||
@ -168,6 +170,18 @@ jobs:
|
||||
|
||||
split -l $(expr $(expr $(cat requirements_all.txt | wc -l) + 1) / 3) requirements_all.txt requirements_all.txt
|
||||
|
||||
- name: Create requirements for cython<3
|
||||
run: |
|
||||
# Some dependencies still require 'cython<3'
|
||||
# and don't yet use isolated build environments.
|
||||
# Build these first.
|
||||
# grpcio: https://github.com/grpc/grpc/issues/33918
|
||||
# pydantic: https://github.com/pydantic/pydantic/issues/7689
|
||||
|
||||
touch requirements_old-cython.txt
|
||||
cat homeassistant/package_constraints.txt | grep 'grpcio==' >> requirements_old-cython.txt
|
||||
cat homeassistant/package_constraints.txt | grep 'pydantic==' >> requirements_old-cython.txt
|
||||
|
||||
- name: Adjust build env
|
||||
run: |
|
||||
if [ "${{ matrix.arch }}" = "i386" ]; then
|
||||
@ -177,8 +191,23 @@ jobs:
|
||||
# Do not pin numpy in wheels building
|
||||
sed -i "/numpy/d" homeassistant/package_constraints.txt
|
||||
|
||||
- name: Build wheels (old cython)
|
||||
uses: home-assistant/wheels@2023.10.5
|
||||
with:
|
||||
abi: ${{ matrix.abi }}
|
||||
tag: musllinux_1_2
|
||||
arch: ${{ matrix.arch }}
|
||||
wheels-key: ${{ secrets.WHEELS_KEY }}
|
||||
env-file: true
|
||||
apk: "bluez-dev;libffi-dev;openssl-dev;glib-dev;eudev-dev;libxml2-dev;libxslt-dev;libpng-dev;libjpeg-turbo-dev;tiff-dev;cups-dev;gmp-dev;mpfr-dev;mpc1-dev;ffmpeg-dev;gammu-dev;yaml-dev;openblas-dev;fftw-dev;lapack-dev;gfortran;blas-dev;eigen-dev;freetype-dev;glew-dev;harfbuzz-dev;hdf5-dev;libdc1394-dev;libtbb-dev;mesa-dev;openexr-dev;openjpeg-dev;uchardet-dev"
|
||||
skip-binary: aiohttp;charset-normalizer;grpcio;SQLAlchemy;protobuf
|
||||
constraints: "homeassistant/package_constraints.txt"
|
||||
requirements-diff: "requirements_diff.txt"
|
||||
requirements: "requirements_old-cython.txt"
|
||||
pip: "'cython<3'"
|
||||
|
||||
- name: Build wheels (part 1)
|
||||
uses: home-assistant/wheels@2023.09.1
|
||||
uses: home-assistant/wheels@2023.10.5
|
||||
with:
|
||||
abi: ${{ matrix.abi }}
|
||||
tag: musllinux_1_2
|
||||
@ -192,7 +221,7 @@ jobs:
|
||||
requirements: "requirements_all.txtaa"
|
||||
|
||||
- name: Build wheels (part 2)
|
||||
uses: home-assistant/wheels@2023.09.1
|
||||
uses: home-assistant/wheels@2023.10.5
|
||||
with:
|
||||
abi: ${{ matrix.abi }}
|
||||
tag: musllinux_1_2
|
||||
@ -206,7 +235,7 @@ jobs:
|
||||
requirements: "requirements_all.txtab"
|
||||
|
||||
- name: Build wheels (part 3)
|
||||
uses: home-assistant/wheels@2023.09.1
|
||||
uses: home-assistant/wheels@2023.10.5
|
||||
with:
|
||||
abi: ${{ matrix.abi }}
|
||||
tag: musllinux_1_2
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -111,9 +111,6 @@ virtualization/vagrant/config
|
||||
!.vscode/tasks.json
|
||||
.env
|
||||
|
||||
# Built docs
|
||||
docs/build
|
||||
|
||||
# Windows Explorer
|
||||
desktop.ini
|
||||
/home-assistant.pyproj
|
||||
|
@ -3,3 +3,4 @@ ignored:
|
||||
- DL3008
|
||||
- DL3013
|
||||
- DL3018
|
||||
- DL3042
|
||||
|
@ -1,12 +1,12 @@
|
||||
repos:
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.0.289
|
||||
rev: v0.1.1
|
||||
hooks:
|
||||
- id: ruff
|
||||
args:
|
||||
- --fix
|
||||
- repo: https://github.com/psf/black-pre-commit-mirror
|
||||
rev: 23.9.1
|
||||
rev: 23.10.0
|
||||
hooks:
|
||||
- id: black
|
||||
args:
|
||||
|
@ -1,7 +1,6 @@
|
||||
*.md
|
||||
.strict-typing
|
||||
azure-*.yml
|
||||
docs/source/_templates/*
|
||||
homeassistant/components/*/translations/*.json
|
||||
homeassistant/generated/*
|
||||
tests/components/lidarr/fixtures/initialize.js
|
||||
|
@ -1,14 +0,0 @@
|
||||
# .readthedocs.yml
|
||||
|
||||
version: 2
|
||||
|
||||
build:
|
||||
os: ubuntu-20.04
|
||||
tools:
|
||||
python: "3.9"
|
||||
|
||||
python:
|
||||
install:
|
||||
- method: setuptools
|
||||
path: .
|
||||
- requirements: requirements_docs.txt
|
@ -103,6 +103,7 @@ homeassistant.components.devolo_home_control.*
|
||||
homeassistant.components.devolo_home_network.*
|
||||
homeassistant.components.dhcp.*
|
||||
homeassistant.components.diagnostics.*
|
||||
homeassistant.components.discovergy.*
|
||||
homeassistant.components.dlna_dmr.*
|
||||
homeassistant.components.dnsip.*
|
||||
homeassistant.components.doorbird.*
|
||||
@ -156,15 +157,7 @@ homeassistant.components.homeassistant_green.*
|
||||
homeassistant.components.homeassistant_hardware.*
|
||||
homeassistant.components.homeassistant_sky_connect.*
|
||||
homeassistant.components.homeassistant_yellow.*
|
||||
homeassistant.components.homekit
|
||||
homeassistant.components.homekit.accessories
|
||||
homeassistant.components.homekit.aidmanager
|
||||
homeassistant.components.homekit.config_flow
|
||||
homeassistant.components.homekit.diagnostics
|
||||
homeassistant.components.homekit.logbook
|
||||
homeassistant.components.homekit.type_locks
|
||||
homeassistant.components.homekit.type_triggers
|
||||
homeassistant.components.homekit.util
|
||||
homeassistant.components.homekit.*
|
||||
homeassistant.components.homekit_controller
|
||||
homeassistant.components.homekit_controller.alarm_control_panel
|
||||
homeassistant.components.homekit_controller.button
|
||||
@ -211,6 +204,7 @@ homeassistant.components.light.*
|
||||
homeassistant.components.litejet.*
|
||||
homeassistant.components.litterrobot.*
|
||||
homeassistant.components.local_ip.*
|
||||
homeassistant.components.local_todo.*
|
||||
homeassistant.components.lock.*
|
||||
homeassistant.components.logbook.*
|
||||
homeassistant.components.logger.*
|
||||
@ -327,6 +321,7 @@ homeassistant.components.synology_dsm.*
|
||||
homeassistant.components.systemmonitor.*
|
||||
homeassistant.components.tag.*
|
||||
homeassistant.components.tailscale.*
|
||||
homeassistant.components.tami4.*
|
||||
homeassistant.components.tautulli.*
|
||||
homeassistant.components.tcp.*
|
||||
homeassistant.components.text.*
|
||||
@ -343,6 +338,7 @@ homeassistant.components.trafikverket_camera.*
|
||||
homeassistant.components.trafikverket_ferry.*
|
||||
homeassistant.components.trafikverket_train.*
|
||||
homeassistant.components.trafikverket_weatherstation.*
|
||||
homeassistant.components.transmission.*
|
||||
homeassistant.components.trend.*
|
||||
homeassistant.components.tts.*
|
||||
homeassistant.components.twentemilieu.*
|
||||
@ -366,6 +362,7 @@ homeassistant.components.webostv.*
|
||||
homeassistant.components.websocket_api.*
|
||||
homeassistant.components.wemo.*
|
||||
homeassistant.components.whois.*
|
||||
homeassistant.components.withings.*
|
||||
homeassistant.components.wiz.*
|
||||
homeassistant.components.wled.*
|
||||
homeassistant.components.worldclock.*
|
||||
|
6
.vscode/tasks.json
vendored
6
.vscode/tasks.json
vendored
@ -16,7 +16,7 @@
|
||||
{
|
||||
"label": "Pytest",
|
||||
"type": "shell",
|
||||
"command": "pytest --timeout=10 tests",
|
||||
"command": "python3 -m pytest --timeout=10 tests",
|
||||
"dependsOn": ["Install all Test Requirements"],
|
||||
"group": {
|
||||
"kind": "test",
|
||||
@ -31,7 +31,7 @@
|
||||
{
|
||||
"label": "Pytest (changed tests only)",
|
||||
"type": "shell",
|
||||
"command": "pytest --timeout=10 --picked",
|
||||
"command": "python3 -m pytest --timeout=10 --picked",
|
||||
"group": {
|
||||
"kind": "test",
|
||||
"isDefault": true
|
||||
@ -75,7 +75,7 @@
|
||||
"label": "Code Coverage",
|
||||
"detail": "Generate code coverage report for a given integration.",
|
||||
"type": "shell",
|
||||
"command": "pytest ./tests/components/${input:integrationName}/ --cov=homeassistant.components.${input:integrationName} --cov-report term-missing --durations-min=1 --durations=0 --numprocesses=auto",
|
||||
"command": "python3 -m pytest ./tests/components/${input:integrationName}/ --cov=homeassistant.components.${input:integrationName} --cov-report term-missing --durations-min=1 --durations=0 --numprocesses=auto",
|
||||
"group": {
|
||||
"kind": "test",
|
||||
"isDefault": true
|
||||
|
58
CODEOWNERS
58
CODEOWNERS
@ -100,8 +100,8 @@ build.json @home-assistant/supervisor
|
||||
/tests/components/apprise/ @caronc
|
||||
/homeassistant/components/aprs/ @PhilRW
|
||||
/tests/components/aprs/ @PhilRW
|
||||
/homeassistant/components/aranet/ @aschmitz
|
||||
/tests/components/aranet/ @aschmitz
|
||||
/homeassistant/components/aranet/ @aschmitz @thecode
|
||||
/tests/components/aranet/ @aschmitz @thecode
|
||||
/homeassistant/components/arcam_fmj/ @elupus
|
||||
/tests/components/arcam_fmj/ @elupus
|
||||
/homeassistant/components/arris_tg2492lg/ @vanbalken
|
||||
@ -233,8 +233,8 @@ build.json @home-assistant/supervisor
|
||||
/tests/components/counter/ @fabaff
|
||||
/homeassistant/components/cover/ @home-assistant/core
|
||||
/tests/components/cover/ @home-assistant/core
|
||||
/homeassistant/components/cpuspeed/ @fabaff @frenck
|
||||
/tests/components/cpuspeed/ @fabaff @frenck
|
||||
/homeassistant/components/cpuspeed/ @fabaff
|
||||
/tests/components/cpuspeed/ @fabaff
|
||||
/homeassistant/components/crownstone/ @Crownstone @RicArch97
|
||||
/tests/components/crownstone/ @Crownstone @RicArch97
|
||||
/homeassistant/components/cups/ @fabaff
|
||||
@ -319,8 +319,6 @@ build.json @home-assistant/supervisor
|
||||
/homeassistant/components/efergy/ @tkdrob
|
||||
/tests/components/efergy/ @tkdrob
|
||||
/homeassistant/components/egardia/ @jeroenterheerdt
|
||||
/homeassistant/components/eight_sleep/ @mezz64 @raman325
|
||||
/tests/components/eight_sleep/ @mezz64 @raman325
|
||||
/homeassistant/components/electrasmart/ @jafar-atili
|
||||
/tests/components/electrasmart/ @jafar-atili
|
||||
/homeassistant/components/electric_kiwi/ @mikey0000
|
||||
@ -423,8 +421,8 @@ build.json @home-assistant/supervisor
|
||||
/tests/components/fritzbox/ @mib1185 @flabbamann
|
||||
/homeassistant/components/fritzbox_callmonitor/ @cdce8p
|
||||
/tests/components/fritzbox_callmonitor/ @cdce8p
|
||||
/homeassistant/components/fronius/ @nielstron @farmio
|
||||
/tests/components/fronius/ @nielstron @farmio
|
||||
/homeassistant/components/fronius/ @farmio
|
||||
/tests/components/fronius/ @farmio
|
||||
/homeassistant/components/frontend/ @home-assistant/frontend
|
||||
/tests/components/frontend/ @home-assistant/frontend
|
||||
/homeassistant/components/frontier_silicon/ @wlcrs
|
||||
@ -479,6 +477,8 @@ build.json @home-assistant/supervisor
|
||||
/tests/components/google_mail/ @tkdrob
|
||||
/homeassistant/components/google_sheets/ @tkdrob
|
||||
/tests/components/google_sheets/ @tkdrob
|
||||
/homeassistant/components/google_tasks/ @allenporter
|
||||
/tests/components/google_tasks/ @allenporter
|
||||
/homeassistant/components/google_travel_time/ @eifinger
|
||||
/tests/components/google_travel_time/ @eifinger
|
||||
/homeassistant/components/govee_ble/ @bdraco @PierreAronnax
|
||||
@ -586,6 +586,8 @@ build.json @home-assistant/supervisor
|
||||
/tests/components/image_upload/ @home-assistant/core
|
||||
/homeassistant/components/imap/ @jbouwh
|
||||
/tests/components/imap/ @jbouwh
|
||||
/homeassistant/components/improv_ble/ @emontnemery
|
||||
/tests/components/improv_ble/ @emontnemery
|
||||
/homeassistant/components/incomfort/ @zxdavb
|
||||
/homeassistant/components/influxdb/ @mdegat01
|
||||
/tests/components/influxdb/ @mdegat01
|
||||
@ -660,8 +662,8 @@ build.json @home-assistant/supervisor
|
||||
/tests/components/kmtronic/ @dgomes
|
||||
/homeassistant/components/knx/ @Julius2342 @farmio @marvin-w
|
||||
/tests/components/knx/ @Julius2342 @farmio @marvin-w
|
||||
/homeassistant/components/kodi/ @OnFreund @cgtobi
|
||||
/tests/components/kodi/ @OnFreund @cgtobi
|
||||
/homeassistant/components/kodi/ @OnFreund
|
||||
/tests/components/kodi/ @OnFreund
|
||||
/homeassistant/components/konnected/ @heythisisnate
|
||||
/tests/components/konnected/ @heythisisnate
|
||||
/homeassistant/components/kostal_plenticore/ @stegm
|
||||
@ -708,6 +710,8 @@ build.json @home-assistant/supervisor
|
||||
/tests/components/local_calendar/ @allenporter
|
||||
/homeassistant/components/local_ip/ @issacg
|
||||
/tests/components/local_ip/ @issacg
|
||||
/homeassistant/components/local_todo/ @allenporter
|
||||
/tests/components/local_todo/ @allenporter
|
||||
/homeassistant/components/lock/ @home-assistant/core
|
||||
/tests/components/lock/ @home-assistant/core
|
||||
/homeassistant/components/logbook/ @home-assistant/core
|
||||
@ -755,8 +759,8 @@ build.json @home-assistant/supervisor
|
||||
/tests/components/melissa/ @kennedyshead
|
||||
/homeassistant/components/melnor/ @vanstinator
|
||||
/tests/components/melnor/ @vanstinator
|
||||
/homeassistant/components/met/ @danielhiversen @thimic
|
||||
/tests/components/met/ @danielhiversen @thimic
|
||||
/homeassistant/components/met/ @danielhiversen
|
||||
/tests/components/met/ @danielhiversen
|
||||
/homeassistant/components/met_eireann/ @DylanGore
|
||||
/tests/components/met_eireann/ @DylanGore
|
||||
/homeassistant/components/meteo_france/ @hacf-fr @oncleben31 @Quentame
|
||||
@ -949,6 +953,8 @@ build.json @home-assistant/supervisor
|
||||
/tests/components/picnic/ @corneyl
|
||||
/homeassistant/components/pilight/ @trekky12
|
||||
/tests/components/pilight/ @trekky12
|
||||
/homeassistant/components/ping/ @jpbede
|
||||
/tests/components/ping/ @jpbede
|
||||
/homeassistant/components/plaato/ @JohNan
|
||||
/tests/components/plaato/ @JohNan
|
||||
/homeassistant/components/plex/ @jjlawren
|
||||
@ -1033,6 +1039,8 @@ build.json @home-assistant/supervisor
|
||||
/tests/components/recollect_waste/ @bachya
|
||||
/homeassistant/components/recorder/ @home-assistant/core
|
||||
/tests/components/recorder/ @home-assistant/core
|
||||
/homeassistant/components/recovery_mode/ @home-assistant/core
|
||||
/tests/components/recovery_mode/ @home-assistant/core
|
||||
/homeassistant/components/rejseplanen/ @DarkFox
|
||||
/homeassistant/components/remote/ @home-assistant/core
|
||||
/tests/components/remote/ @home-assistant/core
|
||||
@ -1063,8 +1071,8 @@ build.json @home-assistant/supervisor
|
||||
/tests/components/roborock/ @humbertogontijo @Lash-L
|
||||
/homeassistant/components/roku/ @ctalkington
|
||||
/tests/components/roku/ @ctalkington
|
||||
/homeassistant/components/roomba/ @pschmitt @cyr-ius @shenxn
|
||||
/tests/components/roomba/ @pschmitt @cyr-ius @shenxn
|
||||
/homeassistant/components/roomba/ @pschmitt @cyr-ius @shenxn @Xitee1
|
||||
/tests/components/roomba/ @pschmitt @cyr-ius @shenxn @Xitee1
|
||||
/homeassistant/components/roon/ @pavoni
|
||||
/tests/components/roon/ @pavoni
|
||||
/homeassistant/components/rpi_power/ @shenxn @swetoast
|
||||
@ -1083,8 +1091,6 @@ build.json @home-assistant/supervisor
|
||||
/tests/components/rympro/ @OnFreund @elad-bar @maorcc
|
||||
/homeassistant/components/sabnzbd/ @shaiu
|
||||
/tests/components/sabnzbd/ @shaiu
|
||||
/homeassistant/components/safe_mode/ @home-assistant/core
|
||||
/tests/components/safe_mode/ @home-assistant/core
|
||||
/homeassistant/components/saj/ @fredericvl
|
||||
/homeassistant/components/samsungtv/ @chemelli74 @epenet
|
||||
/tests/components/samsungtv/ @chemelli74 @epenet
|
||||
@ -1189,8 +1195,8 @@ build.json @home-assistant/supervisor
|
||||
/tests/components/sonarr/ @ctalkington
|
||||
/homeassistant/components/songpal/ @rytilahti @shenxn
|
||||
/tests/components/songpal/ @rytilahti @shenxn
|
||||
/homeassistant/components/sonos/ @cgtobi @jjlawren
|
||||
/tests/components/sonos/ @cgtobi @jjlawren
|
||||
/homeassistant/components/sonos/ @jjlawren
|
||||
/tests/components/sonos/ @jjlawren
|
||||
/homeassistant/components/soundtouch/ @kroimon
|
||||
/tests/components/soundtouch/ @kroimon
|
||||
/homeassistant/components/spaceapi/ @fabaff
|
||||
@ -1265,6 +1271,8 @@ build.json @home-assistant/supervisor
|
||||
/tests/components/tag/ @balloob @dmulcahey
|
||||
/homeassistant/components/tailscale/ @frenck
|
||||
/tests/components/tailscale/ @frenck
|
||||
/homeassistant/components/tami4/ @Guy293
|
||||
/tests/components/tami4/ @Guy293
|
||||
/homeassistant/components/tankerkoenig/ @guillempages @mib1185
|
||||
/tests/components/tankerkoenig/ @guillempages @mib1185
|
||||
/homeassistant/components/tapsaff/ @bazwilliams
|
||||
@ -1299,6 +1307,8 @@ build.json @home-assistant/supervisor
|
||||
/homeassistant/components/time_date/ @fabaff
|
||||
/tests/components/time_date/ @fabaff
|
||||
/homeassistant/components/tmb/ @alemuro
|
||||
/homeassistant/components/todo/ @home-assistant/core
|
||||
/tests/components/todo/ @home-assistant/core
|
||||
/homeassistant/components/todoist/ @boralyl
|
||||
/tests/components/todoist/ @boralyl
|
||||
/homeassistant/components/tolo/ @MatthiasLohr
|
||||
@ -1321,10 +1331,10 @@ build.json @home-assistant/supervisor
|
||||
/tests/components/trafikverket_camera/ @gjohansson-ST
|
||||
/homeassistant/components/trafikverket_ferry/ @gjohansson-ST
|
||||
/tests/components/trafikverket_ferry/ @gjohansson-ST
|
||||
/homeassistant/components/trafikverket_train/ @endor-force @gjohansson-ST
|
||||
/tests/components/trafikverket_train/ @endor-force @gjohansson-ST
|
||||
/homeassistant/components/trafikverket_weatherstation/ @endor-force @gjohansson-ST
|
||||
/tests/components/trafikverket_weatherstation/ @endor-force @gjohansson-ST
|
||||
/homeassistant/components/trafikverket_train/ @gjohansson-ST
|
||||
/tests/components/trafikverket_train/ @gjohansson-ST
|
||||
/homeassistant/components/trafikverket_weatherstation/ @gjohansson-ST
|
||||
/tests/components/trafikverket_weatherstation/ @gjohansson-ST
|
||||
/homeassistant/components/transmission/ @engrbm87 @JPHutchins
|
||||
/tests/components/transmission/ @engrbm87 @JPHutchins
|
||||
/homeassistant/components/trend/ @jpbede
|
||||
@ -1436,8 +1446,8 @@ build.json @home-assistant/supervisor
|
||||
/homeassistant/components/wilight/ @leofig-rj
|
||||
/tests/components/wilight/ @leofig-rj
|
||||
/homeassistant/components/wirelesstag/ @sergeymaysak
|
||||
/homeassistant/components/withings/ @vangorra @joostlek
|
||||
/tests/components/withings/ @vangorra @joostlek
|
||||
/homeassistant/components/withings/ @joostlek
|
||||
/tests/components/withings/ @joostlek
|
||||
/homeassistant/components/wiz/ @sbidy
|
||||
/tests/components/wiz/ @sbidy
|
||||
/homeassistant/components/wled/ @frenck
|
||||
|
16
Dockerfile
16
Dockerfile
@ -14,41 +14,29 @@ COPY requirements.txt homeassistant/
|
||||
COPY homeassistant/package_constraints.txt homeassistant/homeassistant/
|
||||
RUN \
|
||||
pip3 install \
|
||||
--no-cache-dir \
|
||||
--only-binary=:all: \
|
||||
--index-url "https://wheels.home-assistant.io/musllinux-index/" \
|
||||
-r homeassistant/requirements.txt
|
||||
|
||||
COPY requirements_all.txt home_assistant_frontend-* home_assistant_intents-* homeassistant/
|
||||
RUN \
|
||||
if ls homeassistant/home_assistant_frontend*.whl 1> /dev/null 2>&1; then \
|
||||
pip3 install \
|
||||
--no-cache-dir \
|
||||
--no-index \
|
||||
homeassistant/home_assistant_frontend-*.whl; \
|
||||
pip3 install homeassistant/home_assistant_frontend-*.whl; \
|
||||
fi \
|
||||
&& if ls homeassistant/home_assistant_intents*.whl 1> /dev/null 2>&1; then \
|
||||
pip3 install \
|
||||
--no-cache-dir \
|
||||
--no-index \
|
||||
homeassistant/home_assistant_intents-*.whl; \
|
||||
pip3 install homeassistant/home_assistant_intents-*.whl; \
|
||||
fi \
|
||||
&& \
|
||||
LD_PRELOAD="/usr/local/lib/libjemalloc.so.2" \
|
||||
MALLOC_CONF="background_thread:true,metadata_thp:auto,dirty_decay_ms:20000,muzzy_decay_ms:20000" \
|
||||
pip3 install \
|
||||
--no-cache-dir \
|
||||
--only-binary=:all: \
|
||||
--index-url "https://wheels.home-assistant.io/musllinux-index/" \
|
||||
-r homeassistant/requirements_all.txt
|
||||
|
||||
## Setup Home Assistant Core
|
||||
COPY . homeassistant/
|
||||
RUN \
|
||||
pip3 install \
|
||||
--no-cache-dir \
|
||||
--only-binary=:all: \
|
||||
--index-url "https://wheels.home-assistant.io/musllinux-index/" \
|
||||
-e ./homeassistant \
|
||||
&& python3 -m compileall \
|
||||
homeassistant/homeassistant
|
||||
|
10
build.yaml
10
build.yaml
@ -1,10 +1,10 @@
|
||||
image: ghcr.io/home-assistant/{arch}-homeassistant
|
||||
build_from:
|
||||
aarch64: ghcr.io/home-assistant/aarch64-homeassistant-base:2023.09.0
|
||||
armhf: ghcr.io/home-assistant/armhf-homeassistant-base:2023.09.0
|
||||
armv7: ghcr.io/home-assistant/armv7-homeassistant-base:2023.09.0
|
||||
amd64: ghcr.io/home-assistant/amd64-homeassistant-base:2023.09.0
|
||||
i386: ghcr.io/home-assistant/i386-homeassistant-base:2023.09.0
|
||||
aarch64: ghcr.io/home-assistant/aarch64-homeassistant-base:2023.10.1
|
||||
armhf: ghcr.io/home-assistant/armhf-homeassistant-base:2023.10.1
|
||||
armv7: ghcr.io/home-assistant/armv7-homeassistant-base:2023.10.1
|
||||
amd64: ghcr.io/home-assistant/amd64-homeassistant-base:2023.10.1
|
||||
i386: ghcr.io/home-assistant/i386-homeassistant-base:2023.10.1
|
||||
codenotary:
|
||||
signer: notary@home-assistant.io
|
||||
base_image: notary@home-assistant.io
|
||||
|
230
docs/Makefile
230
docs/Makefile
@ -1,230 +0,0 @@
|
||||
# Makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
BUILDDIR = build
|
||||
|
||||
# Internal variables.
|
||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||
PAPEROPT_letter = -D latex_paper_size=letter
|
||||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
|
||||
# the i18n builder cannot share the environment and doctrees with the others
|
||||
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
|
||||
|
||||
.PHONY: help
|
||||
help:
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo " html to make standalone HTML files"
|
||||
@echo " livehtml to make standalone HTML files via sphinx-autobuild"
|
||||
@echo " dirhtml to make HTML files named index.html in directories"
|
||||
@echo " singlehtml to make a single large HTML file"
|
||||
@echo " pickle to make pickle files"
|
||||
@echo " json to make JSON files"
|
||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
||||
@echo " qthelp to make HTML files and a qthelp project"
|
||||
@echo " applehelp to make an Apple Help Book"
|
||||
@echo " devhelp to make HTML files and a Devhelp project"
|
||||
@echo " epub to make an epub"
|
||||
@echo " epub3 to make an epub3"
|
||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
||||
@echo " latexpdf to make LaTeX files and run them through pdflatex"
|
||||
@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
|
||||
@echo " text to make text files"
|
||||
@echo " man to make manual pages"
|
||||
@echo " texinfo to make Texinfo files"
|
||||
@echo " info to make Texinfo files and run them through makeinfo"
|
||||
@echo " gettext to make PO message catalogs"
|
||||
@echo " changes to make an overview of all changed/added/deprecated items"
|
||||
@echo " xml to make Docutils-native XML files"
|
||||
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||
@echo " coverage to run coverage check of the documentation (if enabled)"
|
||||
@echo " dummy to check syntax errors of document sources"
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -rf $(BUILDDIR)/*
|
||||
|
||||
.PHONY: html
|
||||
html:
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||
|
||||
.PHONY: livehtml
|
||||
livehtml:
|
||||
sphinx-autobuild -z ../homeassistant/ --port 0 -B -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
|
||||
.PHONY: dirhtml
|
||||
dirhtml:
|
||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
||||
|
||||
.PHONY: singlehtml
|
||||
singlehtml:
|
||||
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
|
||||
|
||||
.PHONY: pickle
|
||||
pickle:
|
||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
||||
@echo
|
||||
@echo "Build finished; now you can process the pickle files."
|
||||
|
||||
.PHONY: json
|
||||
json:
|
||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
||||
@echo
|
||||
@echo "Build finished; now you can process the JSON files."
|
||||
|
||||
.PHONY: htmlhelp
|
||||
htmlhelp:
|
||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||
".hhp project file in $(BUILDDIR)/htmlhelp."
|
||||
|
||||
.PHONY: qthelp
|
||||
qthelp:
|
||||
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
||||
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Home-Assistant.qhcp"
|
||||
@echo "To view the help file:"
|
||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Home-Assistant.qhc"
|
||||
|
||||
.PHONY: applehelp
|
||||
applehelp:
|
||||
$(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
|
||||
@echo
|
||||
@echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
|
||||
@echo "N.B. You won't be able to view it unless you put it in" \
|
||||
"~/Library/Documentation/Help or install it in your application" \
|
||||
"bundle."
|
||||
|
||||
.PHONY: devhelp
|
||||
devhelp:
|
||||
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
|
||||
@echo
|
||||
@echo "Build finished."
|
||||
@echo "To view the help file:"
|
||||
@echo "# mkdir -p $$HOME/.local/share/devhelp/Home-Assistant"
|
||||
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Home-Assistant"
|
||||
@echo "# devhelp"
|
||||
|
||||
.PHONY: epub
|
||||
epub:
|
||||
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
|
||||
@echo
|
||||
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
|
||||
|
||||
.PHONY: epub3
|
||||
epub3:
|
||||
$(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3
|
||||
@echo
|
||||
@echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3."
|
||||
|
||||
.PHONY: latex
|
||||
latex:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo
|
||||
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
||||
@echo "Run \`make' in that directory to run these through (pdf)latex" \
|
||||
"(use \`make latexpdf' here to do that automatically)."
|
||||
|
||||
.PHONY: latexpdf
|
||||
latexpdf:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through pdflatex..."
|
||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
.PHONY: latexpdfja
|
||||
latexpdfja:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through platex and dvipdfmx..."
|
||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
.PHONY: text
|
||||
text:
|
||||
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
|
||||
@echo
|
||||
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
||||
|
||||
.PHONY: man
|
||||
man:
|
||||
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
||||
@echo
|
||||
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
||||
|
||||
.PHONY: texinfo
|
||||
texinfo:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo
|
||||
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
|
||||
@echo "Run \`make' in that directory to run these through makeinfo" \
|
||||
"(use \`make info' here to do that automatically)."
|
||||
|
||||
.PHONY: info
|
||||
info:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo "Running Texinfo files through makeinfo..."
|
||||
make -C $(BUILDDIR)/texinfo info
|
||||
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
|
||||
|
||||
.PHONY: gettext
|
||||
gettext:
|
||||
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
|
||||
@echo
|
||||
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
|
||||
|
||||
.PHONY: changes
|
||||
changes:
|
||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
||||
@echo
|
||||
@echo "The overview file is in $(BUILDDIR)/changes."
|
||||
|
||||
.PHONY: linkcheck
|
||||
linkcheck:
|
||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
||||
@echo
|
||||
@echo "Link check complete; look for any errors in the above output " \
|
||||
"or in $(BUILDDIR)/linkcheck/output.txt."
|
||||
|
||||
.PHONY: doctest
|
||||
doctest:
|
||||
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
||||
@echo "Testing of doctests in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/doctest/output.txt."
|
||||
|
||||
.PHONY: coverage
|
||||
coverage:
|
||||
$(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
|
||||
@echo "Testing of coverage in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/coverage/python.txt."
|
||||
|
||||
.PHONY: xml
|
||||
xml:
|
||||
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
|
||||
@echo
|
||||
@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
|
||||
|
||||
.PHONY: pseudoxml
|
||||
pseudoxml:
|
||||
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
|
||||
@echo
|
||||
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
|
||||
|
||||
.PHONY: dummy
|
||||
dummy:
|
||||
$(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy
|
||||
@echo
|
||||
@echo "Build finished. Dummy builder generates no files."
|
0
docs/build/.empty
vendored
0
docs/build/.empty
vendored
281
docs/make.bat
281
docs/make.bat
@ -1,281 +0,0 @@
|
||||
@ECHO OFF
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set BUILDDIR=build
|
||||
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source
|
||||
set I18NSPHINXOPTS=%SPHINXOPTS% source
|
||||
if NOT "%PAPER%" == "" (
|
||||
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
|
||||
set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
|
||||
)
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
if "%1" == "help" (
|
||||
:help
|
||||
echo.Please use `make ^<target^>` where ^<target^> is one of
|
||||
echo. html to make standalone HTML files
|
||||
echo. dirhtml to make HTML files named index.html in directories
|
||||
echo. singlehtml to make a single large HTML file
|
||||
echo. pickle to make pickle files
|
||||
echo. json to make JSON files
|
||||
echo. htmlhelp to make HTML files and a HTML help project
|
||||
echo. qthelp to make HTML files and a qthelp project
|
||||
echo. devhelp to make HTML files and a Devhelp project
|
||||
echo. epub to make an epub
|
||||
echo. epub3 to make an epub3
|
||||
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
|
||||
echo. text to make text files
|
||||
echo. man to make manual pages
|
||||
echo. texinfo to make Texinfo files
|
||||
echo. gettext to make PO message catalogs
|
||||
echo. changes to make an overview over all changed/added/deprecated items
|
||||
echo. xml to make Docutils-native XML files
|
||||
echo. pseudoxml to make pseudoxml-XML files for display purposes
|
||||
echo. linkcheck to check all external links for integrity
|
||||
echo. doctest to run all doctests embedded in the documentation if enabled
|
||||
echo. coverage to run coverage check of the documentation if enabled
|
||||
echo. dummy to check syntax errors of document sources
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "clean" (
|
||||
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
|
||||
del /q /s %BUILDDIR%\*
|
||||
goto end
|
||||
)
|
||||
|
||||
|
||||
REM Check if sphinx-build is available and fallback to Python version if any
|
||||
%SPHINXBUILD% 1>NUL 2>NUL
|
||||
if errorlevel 9009 goto sphinx_python
|
||||
goto sphinx_ok
|
||||
|
||||
:sphinx_python
|
||||
|
||||
set SPHINXBUILD=python -m sphinx.__init__
|
||||
%SPHINXBUILD% 2> nul
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.http://sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
:sphinx_ok
|
||||
|
||||
|
||||
if "%1" == "html" (
|
||||
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "dirhtml" (
|
||||
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "singlehtml" (
|
||||
%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "pickle" (
|
||||
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can process the pickle files.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "json" (
|
||||
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can process the JSON files.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "htmlhelp" (
|
||||
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can run HTML Help Workshop with the ^
|
||||
.hhp project file in %BUILDDIR%/htmlhelp.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "qthelp" (
|
||||
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can run "qcollectiongenerator" with the ^
|
||||
.qhcp project file in %BUILDDIR%/qthelp, like this:
|
||||
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Home-Assistant.qhcp
|
||||
echo.To view the help file:
|
||||
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Home-Assistant.ghc
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "devhelp" (
|
||||
%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "epub" (
|
||||
%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The epub file is in %BUILDDIR%/epub.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "epub3" (
|
||||
%SPHINXBUILD% -b epub3 %ALLSPHINXOPTS% %BUILDDIR%/epub3
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The epub3 file is in %BUILDDIR%/epub3.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "latex" (
|
||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "latexpdf" (
|
||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
||||
cd %BUILDDIR%/latex
|
||||
make all-pdf
|
||||
cd %~dp0
|
||||
echo.
|
||||
echo.Build finished; the PDF files are in %BUILDDIR%/latex.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "latexpdfja" (
|
||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
||||
cd %BUILDDIR%/latex
|
||||
make all-pdf-ja
|
||||
cd %~dp0
|
||||
echo.
|
||||
echo.Build finished; the PDF files are in %BUILDDIR%/latex.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "text" (
|
||||
%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The text files are in %BUILDDIR%/text.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "man" (
|
||||
%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The manual pages are in %BUILDDIR%/man.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "texinfo" (
|
||||
%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "gettext" (
|
||||
%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "changes" (
|
||||
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.The overview file is in %BUILDDIR%/changes.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "linkcheck" (
|
||||
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Link check complete; look for any errors in the above output ^
|
||||
or in %BUILDDIR%/linkcheck/output.txt.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "doctest" (
|
||||
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Testing of doctests in the sources finished, look at the ^
|
||||
results in %BUILDDIR%/doctest/output.txt.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "coverage" (
|
||||
%SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Testing of coverage in the sources finished, look at the ^
|
||||
results in %BUILDDIR%/coverage/python.txt.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "xml" (
|
||||
%SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The XML files are in %BUILDDIR%/xml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "pseudoxml" (
|
||||
%SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "dummy" (
|
||||
%SPHINXBUILD% -b dummy %ALLSPHINXOPTS% %BUILDDIR%/dummy
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. Dummy builder generates no files.
|
||||
goto end
|
||||
)
|
||||
|
||||
:end
|
Binary file not shown.
Before Width: | Height: | Size: 174 KiB |
Binary file not shown.
Before Width: | Height: | Size: 226 KiB |
@ -1,45 +0,0 @@
|
||||
"""Sphinx extension for ReadTheDocs-style "Edit on GitHub" links on the sidebar.
|
||||
|
||||
Loosely based on https://github.com/astropy/astropy/pull/347
|
||||
"""
|
||||
|
||||
import os
|
||||
import warnings
|
||||
|
||||
__licence__ = "BSD (3 clause)"
|
||||
|
||||
|
||||
def get_github_url(app, view, path):
|
||||
"""Build the GitHub URL."""
|
||||
return (
|
||||
f"https://github.com/{app.config.edit_on_github_project}/"
|
||||
f"{view}/{app.config.edit_on_github_branch}/"
|
||||
f"{app.config.edit_on_github_src_path}{path}"
|
||||
)
|
||||
|
||||
|
||||
def html_page_context(app, pagename, templatename, context, doctree):
|
||||
"""Build the HTML page."""
|
||||
if templatename != "page.html":
|
||||
return
|
||||
|
||||
if not app.config.edit_on_github_project:
|
||||
warnings.warn("edit_on_github_project not specified")
|
||||
return
|
||||
if not doctree:
|
||||
warnings.warn("doctree is None")
|
||||
return
|
||||
path = os.path.relpath(doctree.get("source"), app.builder.srcdir)
|
||||
show_url = get_github_url(app, "blob", path)
|
||||
edit_url = get_github_url(app, "edit", path)
|
||||
|
||||
context["show_on_github_url"] = show_url
|
||||
context["edit_on_github_url"] = edit_url
|
||||
|
||||
|
||||
def setup(app):
|
||||
"""Set up the app."""
|
||||
app.add_config_value("edit_on_github_project", "", True)
|
||||
app.add_config_value("edit_on_github_branch", "master", True)
|
||||
app.add_config_value("edit_on_github_src_path", "", True) # 'eg' "docs/"
|
||||
app.connect("html-page-context", html_page_context)
|
Binary file not shown.
Before Width: | Height: | Size: 18 KiB |
Binary file not shown.
Before Width: | Height: | Size: 13 KiB |
Binary file not shown.
Before Width: | Height: | Size: 13 KiB |
@ -1,6 +0,0 @@
|
||||
<ul>
|
||||
<li><a href="https://home-assistant.io/">Homepage</a></li>
|
||||
<li><a href="https://community.home-assistant.io">Community Forums</a></li>
|
||||
<li><a href="https://github.com/home-assistant/core">GitHub</a></li>
|
||||
<li><a href="https://discord.gg/c5DvZ4e">Discord</a></li>
|
||||
</ul>
|
@ -1,13 +0,0 @@
|
||||
{%- if show_source and has_source and sourcename %}
|
||||
<h3>{{ _('This Page') }}</h3>
|
||||
<ul class="this-page-menu">
|
||||
{%- if show_on_github_url %}
|
||||
<li><a href="{{ show_on_github_url }}"
|
||||
rel="nofollow">{{ _('Show on GitHub') }}</a></li>
|
||||
{%- endif %}
|
||||
{%- if edit_on_github_url %}
|
||||
<li><a href="{{ edit_on_github_url }}"
|
||||
rel="nofollow">{{ _('Edit on GitHub') }}</a></li>
|
||||
{%- endif %}
|
||||
</ul>
|
||||
{%- endif %}
|
@ -1,29 +0,0 @@
|
||||
:mod:`homeassistant.auth`
|
||||
=========================
|
||||
|
||||
.. automodule:: homeassistant.auth
|
||||
:members:
|
||||
|
||||
homeassistant.auth.auth\_store
|
||||
------------------------------
|
||||
|
||||
.. automodule:: homeassistant.auth.auth_store
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.auth.const
|
||||
------------------------
|
||||
|
||||
.. automodule:: homeassistant.auth.const
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.auth.models
|
||||
-------------------------
|
||||
|
||||
.. automodule:: homeassistant.auth.models
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
@ -1,7 +0,0 @@
|
||||
.. _bootstrap_module:
|
||||
|
||||
:mod:`homeassistant.bootstrap`
|
||||
------------------------------
|
||||
|
||||
.. automodule:: homeassistant.bootstrap
|
||||
:members:
|
@ -1,170 +0,0 @@
|
||||
:mod:`homeassistant.components`
|
||||
===============================
|
||||
|
||||
air\_quality
|
||||
--------------------------------------------
|
||||
|
||||
.. automodule:: homeassistant.components.air_quality
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
alarm\_control\_panel
|
||||
--------------------------------------------
|
||||
|
||||
.. automodule:: homeassistant.components.alarm_control_panel
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
binary\_sensor
|
||||
--------------------------------------------
|
||||
|
||||
.. automodule:: homeassistant.components.binary_sensor
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
camera
|
||||
---------------------------
|
||||
|
||||
.. automodule:: homeassistant.components.camera
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
calendar
|
||||
---------------------------
|
||||
|
||||
.. automodule:: homeassistant.components.calendar
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
climate
|
||||
---------------------------
|
||||
|
||||
.. automodule:: homeassistant.components.climate
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
conversation
|
||||
---------------------------
|
||||
|
||||
.. automodule:: homeassistant.components.conversation
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
cover
|
||||
---------------------------
|
||||
|
||||
.. automodule:: homeassistant.components.cover
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
device\_tracker
|
||||
---------------------------
|
||||
|
||||
.. automodule:: homeassistant.components.device_tracker
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
fan
|
||||
---------------------------
|
||||
|
||||
.. automodule:: homeassistant.components.fan
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
light
|
||||
---------------------------
|
||||
|
||||
.. automodule:: homeassistant.components.light
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
lock
|
||||
---------------------------
|
||||
|
||||
.. automodule:: homeassistant.components.lock
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
media\_player
|
||||
---------------------------
|
||||
|
||||
.. automodule:: homeassistant.components.media_player
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
notify
|
||||
---------------------------
|
||||
|
||||
.. automodule:: homeassistant.components.notify
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
remote
|
||||
---------------------------
|
||||
|
||||
.. automodule:: homeassistant.components.remote
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
switch
|
||||
---------------------------
|
||||
|
||||
.. automodule:: homeassistant.components.switch
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
sensor
|
||||
-------------------------------------
|
||||
|
||||
.. automodule:: homeassistant.components.sensor
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
vacuum
|
||||
-------------------------------------
|
||||
|
||||
.. automodule:: homeassistant.components.vacuum
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
water\_heater
|
||||
-------------------------------------
|
||||
|
||||
.. automodule:: homeassistant.components.water_heater
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
weather
|
||||
---------------------------
|
||||
|
||||
.. automodule:: homeassistant.components.weather
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
webhook
|
||||
---------------------------
|
||||
|
||||
.. automodule:: homeassistant.components.webhook
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
@ -1,7 +0,0 @@
|
||||
.. _config_entries_module:
|
||||
|
||||
:mod:`homeassistant.config_entries`
|
||||
-----------------------------------
|
||||
|
||||
.. automodule:: homeassistant.config_entries
|
||||
:members:
|
@ -1,7 +0,0 @@
|
||||
.. _core_module:
|
||||
|
||||
:mod:`homeassistant.core`
|
||||
-------------------------
|
||||
|
||||
.. automodule:: homeassistant.core
|
||||
:members:
|
@ -1,7 +0,0 @@
|
||||
.. _data_entry_flow_module:
|
||||
|
||||
:mod:`homeassistant.data_entry_flow`
|
||||
-----------------------------
|
||||
|
||||
.. automodule:: homeassistant.data_entry_flow
|
||||
:members:
|
@ -1,7 +0,0 @@
|
||||
.. _exceptions_module:
|
||||
|
||||
:mod:`homeassistant.exceptions`
|
||||
-------------------------------
|
||||
|
||||
.. automodule:: homeassistant.exceptions
|
||||
:members:
|
@ -1,335 +0,0 @@
|
||||
:mod:`homeassistant.helpers`
|
||||
============================
|
||||
|
||||
.. automodule:: homeassistant.helpers
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.aiohttp\_client
|
||||
-------------------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.aiohttp_client
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.area\_registry
|
||||
------------------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.area_registry
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.check\_config
|
||||
-----------------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.check_config
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.collection
|
||||
--------------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.collection
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.condition
|
||||
-------------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.condition
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.config\_entry\_flow
|
||||
-----------------------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.config_entry_flow
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.config\_entry\_oauth2\_flow
|
||||
-------------------------------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.config_entry_oauth2_flow
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.config\_validation
|
||||
----------------------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.config_validation
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.data\_entry\_flow
|
||||
---------------------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.data_entry_flow
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.debounce
|
||||
------------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.debounce
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.deprecation
|
||||
---------------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.deprecation
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.device\_registry
|
||||
--------------------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.device_registry
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.discovery
|
||||
-------------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.discovery
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.dispatcher
|
||||
--------------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.dispatcher
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.entity
|
||||
----------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.entity
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.entity\_component
|
||||
---------------------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.entity_component
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.entity\_platform
|
||||
--------------------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.entity_platform
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.entity\_registry
|
||||
--------------------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.entity_registry
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.entity\_values
|
||||
------------------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.entity_values
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.entityfilter
|
||||
----------------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.entityfilter
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.event
|
||||
---------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.event
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.icon
|
||||
--------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.icon
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.integration\_platform
|
||||
-------------------------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.integration_platform
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.intent
|
||||
----------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.intent
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.json
|
||||
--------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.json
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.location
|
||||
------------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.location
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.logging
|
||||
-----------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.logging
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.network
|
||||
-----------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.network
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.restore\_state
|
||||
------------------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.restore_state
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.script
|
||||
----------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.script
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.service
|
||||
-----------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.service
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.signal
|
||||
-----------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.signal
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.state
|
||||
---------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.state
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.storage
|
||||
-----------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.storage
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.sun
|
||||
-------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.sun
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.system\_info
|
||||
----------------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.system_info
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.temperature
|
||||
---------------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.temperature
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.template
|
||||
------------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.template
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.translation
|
||||
---------------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.translation
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.typing
|
||||
----------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.typing
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.helpers.update\_coordinator
|
||||
-----------------------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.update_coordinator
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
@ -1,7 +0,0 @@
|
||||
.. _loader_module:
|
||||
|
||||
:mod:`homeassistant.loader`
|
||||
---------------------------
|
||||
|
||||
.. automodule:: homeassistant.loader
|
||||
:members:
|
@ -1,151 +0,0 @@
|
||||
:mod:`homeassistant.util`
|
||||
=========================
|
||||
|
||||
.. automodule:: homeassistant.util
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.util.yaml
|
||||
-----------------------
|
||||
|
||||
.. automodule:: homeassistant.util.yaml
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.util.aiohttp
|
||||
--------------------------
|
||||
|
||||
.. automodule:: homeassistant.util.aiohttp
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.util.async\_
|
||||
--------------------------
|
||||
|
||||
.. automodule:: homeassistant.util.async_
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.util.color
|
||||
------------------------
|
||||
|
||||
.. automodule:: homeassistant.util.color
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.util.decorator
|
||||
----------------------------
|
||||
|
||||
.. automodule:: homeassistant.util.decorator
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.util.distance
|
||||
---------------------------
|
||||
|
||||
.. automodule:: homeassistant.util.distance
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.util.dt
|
||||
---------------------
|
||||
|
||||
.. automodule:: homeassistant.util.dt
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.util.json
|
||||
-----------------------
|
||||
|
||||
.. automodule:: homeassistant.util.json
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.util.location
|
||||
---------------------------
|
||||
|
||||
.. automodule:: homeassistant.util.location
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.util.logging
|
||||
--------------------------
|
||||
|
||||
.. automodule:: homeassistant.util.logging
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.util.network
|
||||
--------------------------
|
||||
|
||||
.. automodule:: homeassistant.util.network
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.util.package
|
||||
--------------------------
|
||||
|
||||
.. automodule:: homeassistant.util.package
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.util.pil
|
||||
----------------------
|
||||
|
||||
.. automodule:: homeassistant.util.pil
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.util.pressure
|
||||
---------------------------
|
||||
|
||||
.. automodule:: homeassistant.util.pressure
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.util.ssl
|
||||
----------------------
|
||||
|
||||
.. automodule:: homeassistant.util.ssl
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.util.temperature
|
||||
------------------------------
|
||||
|
||||
.. automodule:: homeassistant.util.temperature
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.util.unit\_system
|
||||
-------------------------------
|
||||
|
||||
.. automodule:: homeassistant.util.unit_system
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
homeassistant.util.volume
|
||||
-------------------------
|
||||
|
||||
.. automodule:: homeassistant.util.volume
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
@ -1,438 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Home Assistant documentation build configuration file.
|
||||
|
||||
This file is execfile()d with the current directory set to its
|
||||
containing dir.
|
||||
|
||||
Note that not all possible configuration values are present in this
|
||||
autogenerated file.
|
||||
|
||||
All configuration values have a default; values that are commented out
|
||||
serve to show the default.
|
||||
|
||||
If extensions (or modules to document with autodoc) are in another directory,
|
||||
add these directories to sys.path here. If the directory is relative to the
|
||||
documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
"""
|
||||
|
||||
import inspect
|
||||
import os
|
||||
import sys
|
||||
|
||||
from homeassistant.const import __short_version__, __version__
|
||||
|
||||
PROJECT_NAME = "Home Assistant"
|
||||
PROJECT_PACKAGE_NAME = "homeassistant"
|
||||
PROJECT_AUTHOR = "The Home Assistant Authors"
|
||||
PROJECT_COPYRIGHT = PROJECT_AUTHOR
|
||||
PROJECT_LONG_DESCRIPTION = (
|
||||
"Home Assistant is an open-source "
|
||||
"home automation platform running on Python 3. "
|
||||
"Track and control all devices at home and "
|
||||
"automate control. "
|
||||
"Installation in less than a minute."
|
||||
)
|
||||
PROJECT_GITHUB_USERNAME = "home-assistant"
|
||||
PROJECT_GITHUB_REPOSITORY = "home-assistant"
|
||||
|
||||
GITHUB_PATH = f"{PROJECT_GITHUB_USERNAME}/{PROJECT_GITHUB_REPOSITORY}"
|
||||
GITHUB_URL = f"https://github.com/{GITHUB_PATH}"
|
||||
|
||||
|
||||
sys.path.insert(0, os.path.abspath("_ext"))
|
||||
sys.path.insert(0, os.path.abspath("../homeassistant"))
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#
|
||||
# needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
"sphinx.ext.autodoc",
|
||||
"sphinx.ext.linkcode",
|
||||
"sphinx_autodoc_annotation",
|
||||
"edit_on_github",
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ["_templates"]
|
||||
|
||||
# The suffix(es) of source filenames.
|
||||
# You can specify multiple suffix as a list of string:
|
||||
#
|
||||
# source_suffix = ['.rst', '.md']
|
||||
source_suffix = ".rst"
|
||||
|
||||
# The encoding of source files.
|
||||
#
|
||||
# source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = "index"
|
||||
|
||||
# General information about the project.
|
||||
project = PROJECT_NAME
|
||||
copyright = PROJECT_COPYRIGHT
|
||||
author = PROJECT_AUTHOR
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = __short_version__
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = __version__
|
||||
|
||||
code_branch = "dev" if "dev" in __version__ else "master"
|
||||
|
||||
# Edit on Github config
|
||||
edit_on_github_project = GITHUB_PATH
|
||||
edit_on_github_branch = code_branch
|
||||
edit_on_github_src_path = "docs/source/"
|
||||
|
||||
|
||||
def linkcode_resolve(domain, info):
|
||||
"""Determine the URL corresponding to Python object."""
|
||||
if domain != "py":
|
||||
return None
|
||||
modname = info["module"]
|
||||
fullname = info["fullname"]
|
||||
submod = sys.modules.get(modname)
|
||||
if submod is None:
|
||||
return None
|
||||
obj = submod
|
||||
for part in fullname.split("."):
|
||||
try:
|
||||
obj = getattr(obj, part)
|
||||
except Exception: # pylint: disable=broad-except
|
||||
return None
|
||||
try:
|
||||
fn = inspect.getsourcefile(obj)
|
||||
except Exception: # pylint: disable=broad-except
|
||||
fn = None
|
||||
if not fn:
|
||||
return None
|
||||
try:
|
||||
source, lineno = inspect.findsource(obj)
|
||||
except Exception: # pylint: disable=broad-except
|
||||
lineno = None
|
||||
if lineno:
|
||||
linespec = "#L%d" % (lineno + 1)
|
||||
else:
|
||||
linespec = ""
|
||||
index = fn.find("/homeassistant/")
|
||||
if index == -1:
|
||||
index = 0
|
||||
|
||||
fn = fn[index:]
|
||||
|
||||
return f"{GITHUB_URL}/blob/{code_branch}/{fn}{linespec}"
|
||||
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# Usually you set "language" from the command line for these cases.
|
||||
language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
#
|
||||
# today = ''
|
||||
#
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
#
|
||||
# today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This patterns also effect to html_static_path and html_extra_path
|
||||
exclude_patterns = []
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all
|
||||
# documents.
|
||||
#
|
||||
# default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#
|
||||
# add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
#
|
||||
# add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
#
|
||||
# show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = "sphinx"
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
# modindex_common_prefix = []
|
||||
|
||||
# If true, keep warnings as "system message" paragraphs in the built documents.
|
||||
# keep_warnings = False
|
||||
|
||||
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||
todo_include_todos = False
|
||||
|
||||
|
||||
# -- Options for HTML output ----------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = "alabaster"
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#
|
||||
html_theme_options = {
|
||||
"logo": "logo.png",
|
||||
"logo_name": PROJECT_NAME,
|
||||
"description": PROJECT_LONG_DESCRIPTION,
|
||||
"github_user": PROJECT_GITHUB_USERNAME,
|
||||
"github_repo": PROJECT_GITHUB_REPOSITORY,
|
||||
"github_type": "star",
|
||||
"github_banner": True,
|
||||
"touch_icon": "logo-apple.png",
|
||||
# 'fixed_sidebar': True, # Re-enable when we have more content
|
||||
}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
# html_theme_path = []
|
||||
|
||||
# The name for this set of Sphinx documents.
|
||||
# "<project> v<release> documentation" by default.
|
||||
#
|
||||
# html_title = 'Home-Assistant v0.27.0'
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
#
|
||||
# html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
#
|
||||
# html_logo = '_static/logo.png'
|
||||
|
||||
# The name of an image file (relative to this directory) to use as a favicon of
|
||||
# the docs.
|
||||
# This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
#
|
||||
html_favicon = "_static/favicon.ico"
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ["_static"]
|
||||
|
||||
# Add any extra paths that contain custom files (such as robots.txt or
|
||||
# .htaccess) here, relative to this directory. These files are copied
|
||||
# directly to the root of the documentation.
|
||||
#
|
||||
# html_extra_path = []
|
||||
|
||||
# If not None, a 'Last updated on:' timestamp is inserted at every page
|
||||
# bottom, using the given strftime format.
|
||||
# The empty string is equivalent to '%b %d, %Y'.
|
||||
#
|
||||
html_last_updated_fmt = "%b %d, %Y"
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#
|
||||
html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
#
|
||||
html_sidebars = {
|
||||
"**": [
|
||||
"about.html",
|
||||
"links.html",
|
||||
"searchbox.html",
|
||||
"sourcelink.html",
|
||||
"navigation.html",
|
||||
"relations.html",
|
||||
]
|
||||
}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
#
|
||||
# html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
#
|
||||
# html_domain_indices = True
|
||||
|
||||
# If false, no index is generated.
|
||||
#
|
||||
# html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
#
|
||||
# html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
#
|
||||
# html_show_sourcelink = True
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
#
|
||||
# html_show_sphinx = True
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
#
|
||||
# html_show_copyright = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
#
|
||||
# html_use_opensearch = ''
|
||||
|
||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
# html_file_suffix = None
|
||||
|
||||
# Language to be used for generating the HTML full-text search index.
|
||||
# Sphinx supports the following languages:
|
||||
# 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja'
|
||||
# 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr', 'zh'
|
||||
#
|
||||
# html_search_language = 'en'
|
||||
|
||||
# A dictionary with options for the search language support, empty by default.
|
||||
# 'ja' uses this config value.
|
||||
# 'zh' user can custom change `jieba` dictionary path.
|
||||
#
|
||||
# html_search_options = {'type': 'default'}
|
||||
|
||||
# The name of a javascript file (relative to the configuration directory) that
|
||||
# implements a search results scorer. If empty, the default will be used.
|
||||
#
|
||||
# html_search_scorer = 'scorer.js'
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = "Home-Assistantdoc"
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#
|
||||
# 'papersize': 'letterpaper',
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#
|
||||
# 'pointsize': '10pt',
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#
|
||||
# 'preamble': '',
|
||||
# Latex figure (float) alignment
|
||||
#
|
||||
# 'figure_align': 'htbp',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
(
|
||||
master_doc,
|
||||
"home-assistant.tex",
|
||||
"Home Assistant Documentation",
|
||||
"Home Assistant Team",
|
||||
"manual",
|
||||
)
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
#
|
||||
# latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
#
|
||||
# latex_use_parts = False
|
||||
|
||||
# If true, show page references after internal links.
|
||||
#
|
||||
# latex_show_pagerefs = False
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#
|
||||
# latex_show_urls = False
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#
|
||||
# latex_appendices = []
|
||||
|
||||
# It false, will not define \strong, \code, itleref, \crossref ... but only
|
||||
# \sphinxstrong, ..., \sphinxtitleref, ... To help avoid clash with user added
|
||||
# packages.
|
||||
#
|
||||
# latex_keep_old_macro_names = True
|
||||
|
||||
# If false, no module index is generated.
|
||||
#
|
||||
# latex_domain_indices = True
|
||||
|
||||
|
||||
# -- Options for manual page output ---------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
(master_doc, "home-assistant", "Home Assistant Documentation", [author], 1)
|
||||
]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#
|
||||
# man_show_urls = False
|
||||
|
||||
|
||||
# -- Options for Texinfo output -------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
(
|
||||
master_doc,
|
||||
"Home-Assistant",
|
||||
"Home Assistant Documentation",
|
||||
author,
|
||||
"Home Assistant",
|
||||
"Open-source home automation platform.",
|
||||
"Miscellaneous",
|
||||
)
|
||||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#
|
||||
# texinfo_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#
|
||||
# texinfo_domain_indices = True
|
||||
|
||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||
#
|
||||
# texinfo_show_urls = 'footnote'
|
||||
|
||||
# If true, do not generate a @detailmenu in the "Top" node's menu.
|
||||
#
|
||||
# texinfo_no_detailmenu = False
|
@ -1,22 +0,0 @@
|
||||
================================
|
||||
Home Assistant API Documentation
|
||||
================================
|
||||
|
||||
Public API documentation for `Home Assistant developers`_.
|
||||
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:glob:
|
||||
|
||||
api/*
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
|
||||
.. _Home Assistant developers: https://developers.home-assistant.io/
|
@ -93,7 +93,9 @@ def get_arguments() -> argparse.Namespace:
|
||||
help="Directory that contains the Home Assistant configuration",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--safe-mode", action="store_true", help="Start Home Assistant in safe mode"
|
||||
"--recovery-mode",
|
||||
action="store_true",
|
||||
help="Start Home Assistant in recovery mode",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--debug", action="store_true", help="Start Home Assistant in debug mode"
|
||||
@ -183,7 +185,9 @@ def main() -> int:
|
||||
ensure_config_path(config_dir)
|
||||
|
||||
# pylint: disable-next=import-outside-toplevel
|
||||
from . import runner
|
||||
from . import config, runner
|
||||
|
||||
safe_mode = config.safe_mode_enabled(config_dir)
|
||||
|
||||
runtime_conf = runner.RuntimeConfig(
|
||||
config_dir=config_dir,
|
||||
@ -193,9 +197,10 @@ def main() -> int:
|
||||
log_no_color=args.log_no_color,
|
||||
skip_pip=args.skip_pip,
|
||||
skip_pip_packages=args.skip_pip_packages,
|
||||
safe_mode=args.safe_mode,
|
||||
recovery_mode=args.recovery_mode,
|
||||
debug=args.debug,
|
||||
open_ui=args.open_ui,
|
||||
safe_mode=safe_mode,
|
||||
)
|
||||
|
||||
fault_file_name = os.path.join(config_dir, FAULT_LOG_FILENAME)
|
||||
|
@ -5,6 +5,7 @@ import asyncio
|
||||
from collections import OrderedDict
|
||||
from collections.abc import Mapping
|
||||
from datetime import timedelta
|
||||
import time
|
||||
from typing import Any, cast
|
||||
|
||||
import jwt
|
||||
@ -12,7 +13,6 @@ import jwt
|
||||
from homeassistant import data_entry_flow
|
||||
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from . import auth_store, jwt_wrapper, models
|
||||
from .const import ACCESS_TOKEN_EXPIRATION, GROUP_ID_ADMIN
|
||||
@ -505,12 +505,13 @@ class AuthManager:
|
||||
|
||||
self._store.async_log_refresh_token_usage(refresh_token, remote_ip)
|
||||
|
||||
now = dt_util.utcnow()
|
||||
now = int(time.time())
|
||||
expire_seconds = int(refresh_token.access_token_expiration.total_seconds())
|
||||
return jwt.encode(
|
||||
{
|
||||
"iss": refresh_token.id,
|
||||
"iat": now,
|
||||
"exp": now + refresh_token.access_token_expiration,
|
||||
"exp": now + expire_seconds,
|
||||
},
|
||||
refresh_token.jwt_key,
|
||||
algorithm="HS256",
|
||||
|
@ -50,7 +50,7 @@ class MultiFactorAuthModule:
|
||||
|
||||
Default is same as type
|
||||
"""
|
||||
return self.config.get(CONF_ID, self.type)
|
||||
return self.config.get(CONF_ID, self.type) # type: ignore[no-any-return]
|
||||
|
||||
@property
|
||||
def type(self) -> str:
|
||||
@ -60,7 +60,7 @@ class MultiFactorAuthModule:
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""Return the name of the auth module."""
|
||||
return self.config.get(CONF_NAME, self.DEFAULT_TITLE)
|
||||
return self.config.get(CONF_NAME, self.DEFAULT_TITLE) # type: ignore[no-any-return]
|
||||
|
||||
# Implement by extending class
|
||||
|
||||
|
@ -1,32 +1,25 @@
|
||||
"""Permission constants for the websocket API.
|
||||
|
||||
Separate file to avoid circular imports.
|
||||
"""
|
||||
"""Permission for events."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Final
|
||||
|
||||
from homeassistant.components.frontend import EVENT_PANELS_UPDATED
|
||||
from homeassistant.components.lovelace import EVENT_LOVELACE_UPDATED
|
||||
from homeassistant.components.persistent_notification import (
|
||||
EVENT_PERSISTENT_NOTIFICATIONS_UPDATED,
|
||||
)
|
||||
from homeassistant.components.recorder import (
|
||||
EVENT_RECORDER_5MIN_STATISTICS_GENERATED,
|
||||
EVENT_RECORDER_HOURLY_STATISTICS_GENERATED,
|
||||
)
|
||||
from homeassistant.components.shopping_list import EVENT_SHOPPING_LIST_UPDATED
|
||||
from homeassistant.const import (
|
||||
EVENT_COMPONENT_LOADED,
|
||||
EVENT_CORE_CONFIG_UPDATE,
|
||||
EVENT_LOVELACE_UPDATED,
|
||||
EVENT_PANELS_UPDATED,
|
||||
EVENT_RECORDER_5MIN_STATISTICS_GENERATED,
|
||||
EVENT_RECORDER_HOURLY_STATISTICS_GENERATED,
|
||||
EVENT_SERVICE_REGISTERED,
|
||||
EVENT_SERVICE_REMOVED,
|
||||
EVENT_SHOPPING_LIST_UPDATED,
|
||||
EVENT_STATE_CHANGED,
|
||||
EVENT_THEMES_UPDATED,
|
||||
)
|
||||
from homeassistant.helpers.area_registry import EVENT_AREA_REGISTRY_UPDATED
|
||||
from homeassistant.helpers.device_registry import EVENT_DEVICE_REGISTRY_UPDATED
|
||||
from homeassistant.helpers.entity_registry import EVENT_ENTITY_REGISTRY_UPDATED
|
||||
from homeassistant.helpers.issue_registry import EVENT_REPAIRS_ISSUE_REGISTRY_UPDATED
|
||||
|
||||
# These are events that do not contain any sensitive data
|
||||
# Except for state_changed, which is handled accordingly.
|
||||
@ -36,9 +29,9 @@ SUBSCRIBE_ALLOWLIST: Final[set[str]] = {
|
||||
EVENT_CORE_CONFIG_UPDATE,
|
||||
EVENT_DEVICE_REGISTRY_UPDATED,
|
||||
EVENT_ENTITY_REGISTRY_UPDATED,
|
||||
EVENT_REPAIRS_ISSUE_REGISTRY_UPDATED,
|
||||
EVENT_LOVELACE_UPDATED,
|
||||
EVENT_PANELS_UPDATED,
|
||||
EVENT_PERSISTENT_NOTIFICATIONS_UPDATED,
|
||||
EVENT_RECORDER_5MIN_STATISTICS_GENERATED,
|
||||
EVENT_RECORDER_HOURLY_STATISTICS_GENERATED,
|
||||
EVENT_SERVICE_REGISTERED,
|
@ -109,4 +109,4 @@ def test_all(policy: CategoryType, key: str) -> bool:
|
||||
if not isinstance(all_policy, dict):
|
||||
return bool(all_policy)
|
||||
|
||||
return all_policy.get(key, False)
|
||||
return all_policy.get(key, False) # type: ignore[no-any-return]
|
||||
|
@ -67,7 +67,7 @@ class AuthProvider:
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""Return the name of the auth provider."""
|
||||
return self.config.get(CONF_NAME, self.DEFAULT_TITLE)
|
||||
return self.config.get(CONF_NAME, self.DEFAULT_TITLE) # type: ignore[no-any-return]
|
||||
|
||||
@property
|
||||
def support_mfa(self) -> bool:
|
||||
|
279
homeassistant/backports/LICENSE.Python
Normal file
279
homeassistant/backports/LICENSE.Python
Normal file
@ -0,0 +1,279 @@
|
||||
A. HISTORY OF THE SOFTWARE
|
||||
==========================
|
||||
|
||||
Python was created in the early 1990s by Guido van Rossum at Stichting
|
||||
Mathematisch Centrum (CWI, see https://www.cwi.nl) in the Netherlands
|
||||
as a successor of a language called ABC. Guido remains Python's
|
||||
principal author, although it includes many contributions from others.
|
||||
|
||||
In 1995, Guido continued his work on Python at the Corporation for
|
||||
National Research Initiatives (CNRI, see https://www.cnri.reston.va.us)
|
||||
in Reston, Virginia where he released several versions of the
|
||||
software.
|
||||
|
||||
In May 2000, Guido and the Python core development team moved to
|
||||
BeOpen.com to form the BeOpen PythonLabs team. In October of the same
|
||||
year, the PythonLabs team moved to Digital Creations, which became
|
||||
Zope Corporation. In 2001, the Python Software Foundation (PSF, see
|
||||
https://www.python.org/psf/) was formed, a non-profit organization
|
||||
created specifically to own Python-related Intellectual Property.
|
||||
Zope Corporation was a sponsoring member of the PSF.
|
||||
|
||||
All Python releases are Open Source (see https://opensource.org for
|
||||
the Open Source Definition). Historically, most, but not all, Python
|
||||
releases have also been GPL-compatible; the table below summarizes
|
||||
the various releases.
|
||||
|
||||
Release Derived Year Owner GPL-
|
||||
from compatible? (1)
|
||||
|
||||
0.9.0 thru 1.2 1991-1995 CWI yes
|
||||
1.3 thru 1.5.2 1.2 1995-1999 CNRI yes
|
||||
1.6 1.5.2 2000 CNRI no
|
||||
2.0 1.6 2000 BeOpen.com no
|
||||
1.6.1 1.6 2001 CNRI yes (2)
|
||||
2.1 2.0+1.6.1 2001 PSF no
|
||||
2.0.1 2.0+1.6.1 2001 PSF yes
|
||||
2.1.1 2.1+2.0.1 2001 PSF yes
|
||||
2.1.2 2.1.1 2002 PSF yes
|
||||
2.1.3 2.1.2 2002 PSF yes
|
||||
2.2 and above 2.1.1 2001-now PSF yes
|
||||
|
||||
Footnotes:
|
||||
|
||||
(1) GPL-compatible doesn't mean that we're distributing Python under
|
||||
the GPL. All Python licenses, unlike the GPL, let you distribute
|
||||
a modified version without making your changes open source. The
|
||||
GPL-compatible licenses make it possible to combine Python with
|
||||
other software that is released under the GPL; the others don't.
|
||||
|
||||
(2) According to Richard Stallman, 1.6.1 is not GPL-compatible,
|
||||
because its license has a choice of law clause. According to
|
||||
CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1
|
||||
is "not incompatible" with the GPL.
|
||||
|
||||
Thanks to the many outside volunteers who have worked under Guido's
|
||||
direction to make these releases possible.
|
||||
|
||||
|
||||
B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON
|
||||
===============================================================
|
||||
|
||||
Python software and documentation are licensed under the
|
||||
Python Software Foundation License Version 2.
|
||||
|
||||
Starting with Python 3.8.6, examples, recipes, and other code in
|
||||
the documentation are dual licensed under the PSF License Version 2
|
||||
and the Zero-Clause BSD license.
|
||||
|
||||
Some software incorporated into Python is under different licenses.
|
||||
The licenses are listed with code falling under that license.
|
||||
|
||||
|
||||
PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
|
||||
--------------------------------------------
|
||||
|
||||
1. This LICENSE AGREEMENT is between the Python Software Foundation
|
||||
("PSF"), and the Individual or Organization ("Licensee") accessing and
|
||||
otherwise using this software ("Python") in source or binary form and
|
||||
its associated documentation.
|
||||
|
||||
2. Subject to the terms and conditions of this License Agreement, PSF hereby
|
||||
grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
|
||||
analyze, test, perform and/or display publicly, prepare derivative works,
|
||||
distribute, and otherwise use Python alone or in any derivative version,
|
||||
provided, however, that PSF's License Agreement and PSF's notice of copyright,
|
||||
i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Python Software Foundation;
|
||||
All Rights Reserved" are retained in Python alone or in any derivative version
|
||||
prepared by Licensee.
|
||||
|
||||
3. In the event Licensee prepares a derivative work that is based on
|
||||
or incorporates Python or any part thereof, and wants to make
|
||||
the derivative work available to others as provided herein, then
|
||||
Licensee hereby agrees to include in any such work a brief summary of
|
||||
the changes made to Python.
|
||||
|
||||
4. PSF is making Python available to Licensee on an "AS IS"
|
||||
basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
||||
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
|
||||
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
||||
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
|
||||
INFRINGE ANY THIRD PARTY RIGHTS.
|
||||
|
||||
5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
|
||||
FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
|
||||
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
|
||||
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
||||
|
||||
6. This License Agreement will automatically terminate upon a material
|
||||
breach of its terms and conditions.
|
||||
|
||||
7. Nothing in this License Agreement shall be deemed to create any
|
||||
relationship of agency, partnership, or joint venture between PSF and
|
||||
Licensee. This License Agreement does not grant permission to use PSF
|
||||
trademarks or trade name in a trademark sense to endorse or promote
|
||||
products or services of Licensee, or any third party.
|
||||
|
||||
8. By copying, installing or otherwise using Python, Licensee
|
||||
agrees to be bound by the terms and conditions of this License
|
||||
Agreement.
|
||||
|
||||
|
||||
BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
|
||||
-------------------------------------------
|
||||
|
||||
BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
|
||||
|
||||
1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an
|
||||
office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
|
||||
Individual or Organization ("Licensee") accessing and otherwise using
|
||||
this software in source or binary form and its associated
|
||||
documentation ("the Software").
|
||||
|
||||
2. Subject to the terms and conditions of this BeOpen Python License
|
||||
Agreement, BeOpen hereby grants Licensee a non-exclusive,
|
||||
royalty-free, world-wide license to reproduce, analyze, test, perform
|
||||
and/or display publicly, prepare derivative works, distribute, and
|
||||
otherwise use the Software alone or in any derivative version,
|
||||
provided, however, that the BeOpen Python License is retained in the
|
||||
Software, alone or in any derivative version prepared by Licensee.
|
||||
|
||||
3. BeOpen is making the Software available to Licensee on an "AS IS"
|
||||
basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
||||
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
|
||||
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
||||
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
|
||||
INFRINGE ANY THIRD PARTY RIGHTS.
|
||||
|
||||
4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
|
||||
SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
|
||||
AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
|
||||
DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
||||
|
||||
5. This License Agreement will automatically terminate upon a material
|
||||
breach of its terms and conditions.
|
||||
|
||||
6. This License Agreement shall be governed by and interpreted in all
|
||||
respects by the law of the State of California, excluding conflict of
|
||||
law provisions. Nothing in this License Agreement shall be deemed to
|
||||
create any relationship of agency, partnership, or joint venture
|
||||
between BeOpen and Licensee. This License Agreement does not grant
|
||||
permission to use BeOpen trademarks or trade names in a trademark
|
||||
sense to endorse or promote products or services of Licensee, or any
|
||||
third party. As an exception, the "BeOpen Python" logos available at
|
||||
http://www.pythonlabs.com/logos.html may be used according to the
|
||||
permissions granted on that web page.
|
||||
|
||||
7. By copying, installing or otherwise using the software, Licensee
|
||||
agrees to be bound by the terms and conditions of this License
|
||||
Agreement.
|
||||
|
||||
|
||||
CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1
|
||||
---------------------------------------
|
||||
|
||||
1. This LICENSE AGREEMENT is between the Corporation for National
|
||||
Research Initiatives, having an office at 1895 Preston White Drive,
|
||||
Reston, VA 20191 ("CNRI"), and the Individual or Organization
|
||||
("Licensee") accessing and otherwise using Python 1.6.1 software in
|
||||
source or binary form and its associated documentation.
|
||||
|
||||
2. Subject to the terms and conditions of this License Agreement, CNRI
|
||||
hereby grants Licensee a nonexclusive, royalty-free, world-wide
|
||||
license to reproduce, analyze, test, perform and/or display publicly,
|
||||
prepare derivative works, distribute, and otherwise use Python 1.6.1
|
||||
alone or in any derivative version, provided, however, that CNRI's
|
||||
License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)
|
||||
1995-2001 Corporation for National Research Initiatives; All Rights
|
||||
Reserved" are retained in Python 1.6.1 alone or in any derivative
|
||||
version prepared by Licensee. Alternately, in lieu of CNRI's License
|
||||
Agreement, Licensee may substitute the following text (omitting the
|
||||
quotes): "Python 1.6.1 is made available subject to the terms and
|
||||
conditions in CNRI's License Agreement. This Agreement together with
|
||||
Python 1.6.1 may be located on the internet using the following
|
||||
unique, persistent identifier (known as a handle): 1895.22/1013. This
|
||||
Agreement may also be obtained from a proxy server on the internet
|
||||
using the following URL: http://hdl.handle.net/1895.22/1013".
|
||||
|
||||
3. In the event Licensee prepares a derivative work that is based on
|
||||
or incorporates Python 1.6.1 or any part thereof, and wants to make
|
||||
the derivative work available to others as provided herein, then
|
||||
Licensee hereby agrees to include in any such work a brief summary of
|
||||
the changes made to Python 1.6.1.
|
||||
|
||||
4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"
|
||||
basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
||||
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND
|
||||
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
||||
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT
|
||||
INFRINGE ANY THIRD PARTY RIGHTS.
|
||||
|
||||
5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
|
||||
1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
|
||||
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,
|
||||
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
||||
|
||||
6. This License Agreement will automatically terminate upon a material
|
||||
breach of its terms and conditions.
|
||||
|
||||
7. This License Agreement shall be governed by the federal
|
||||
intellectual property law of the United States, including without
|
||||
limitation the federal copyright law, and, to the extent such
|
||||
U.S. federal law does not apply, by the law of the Commonwealth of
|
||||
Virginia, excluding Virginia's conflict of law provisions.
|
||||
Notwithstanding the foregoing, with regard to derivative works based
|
||||
on Python 1.6.1 that incorporate non-separable material that was
|
||||
previously distributed under the GNU General Public License (GPL), the
|
||||
law of the Commonwealth of Virginia shall govern this License
|
||||
Agreement only as to issues arising under or with respect to
|
||||
Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this
|
||||
License Agreement shall be deemed to create any relationship of
|
||||
agency, partnership, or joint venture between CNRI and Licensee. This
|
||||
License Agreement does not grant permission to use CNRI trademarks or
|
||||
trade name in a trademark sense to endorse or promote products or
|
||||
services of Licensee, or any third party.
|
||||
|
||||
8. By clicking on the "ACCEPT" button where indicated, or by copying,
|
||||
installing or otherwise using Python 1.6.1, Licensee agrees to be
|
||||
bound by the terms and conditions of this License Agreement.
|
||||
|
||||
ACCEPT
|
||||
|
||||
|
||||
CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
|
||||
--------------------------------------------------
|
||||
|
||||
Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,
|
||||
The Netherlands. All rights reserved.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software and its
|
||||
documentation for any purpose and without fee is hereby granted,
|
||||
provided that the above copyright notice appear in all copies and that
|
||||
both that copyright notice and this permission notice appear in
|
||||
supporting documentation, and that the name of Stichting Mathematisch
|
||||
Centrum or CWI not be used in advertising or publicity pertaining to
|
||||
distribution of the software without specific, written prior
|
||||
permission.
|
||||
|
||||
STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
|
||||
THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
|
||||
FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
ZERO-CLAUSE BSD LICENSE FOR CODE IN THE PYTHON DOCUMENTATION
|
||||
----------------------------------------------------------------------
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
5
homeassistant/backports/README
Normal file
5
homeassistant/backports/README
Normal file
@ -0,0 +1,5 @@
|
||||
This package contains backports of Python functionality from future Python
|
||||
versions.
|
||||
|
||||
Some of the backports have been copied directly from the CPython project,
|
||||
and are subject to license agreement as detailed in LICENSE.Python.
|
@ -1,4 +1,14 @@
|
||||
"""Functools backports from standard lib."""
|
||||
|
||||
# This file contains parts of Python's module wrapper
|
||||
# for the _functools C module
|
||||
# to allow utilities written in Python to be added
|
||||
# to the functools module.
|
||||
# Written by Nick Coghlan <ncoghlan at gmail.com>,
|
||||
# Raymond Hettinger <python at rcn.com>,
|
||||
# and Łukasz Langa <lukasz at langa.pl>.
|
||||
# Copyright © 2001-2023 Python Software Foundation; All Rights Reserved
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
@ -68,4 +78,4 @@ class cached_property(Generic[_T]):
|
||||
raise TypeError(msg) from None
|
||||
return val
|
||||
|
||||
__class_getitem__ = classmethod(GenericAlias)
|
||||
__class_getitem__ = classmethod(GenericAlias) # type: ignore[var-annotated]
|
||||
|
@ -120,6 +120,7 @@ async def async_setup_hass(
|
||||
runtime_config.log_no_color,
|
||||
)
|
||||
|
||||
hass.config.safe_mode = runtime_config.safe_mode
|
||||
hass.config.skip_pip = runtime_config.skip_pip
|
||||
hass.config.skip_pip_packages = runtime_config.skip_pip_packages
|
||||
if runtime_config.skip_pip or runtime_config.skip_pip_packages:
|
||||
@ -137,14 +138,14 @@ async def async_setup_hass(
|
||||
config_dict = None
|
||||
basic_setup_success = False
|
||||
|
||||
if not (safe_mode := runtime_config.safe_mode):
|
||||
if not (recovery_mode := runtime_config.recovery_mode):
|
||||
await hass.async_add_executor_job(conf_util.process_ha_config_upgrade, hass)
|
||||
|
||||
try:
|
||||
config_dict = await conf_util.async_hass_config_yaml(hass)
|
||||
except HomeAssistantError as err:
|
||||
_LOGGER.error(
|
||||
"Failed to parse configuration.yaml: %s. Activating safe mode",
|
||||
"Failed to parse configuration.yaml: %s. Activating recovery mode",
|
||||
err,
|
||||
)
|
||||
else:
|
||||
@ -156,24 +157,24 @@ async def async_setup_hass(
|
||||
)
|
||||
|
||||
if config_dict is None:
|
||||
safe_mode = True
|
||||
recovery_mode = True
|
||||
|
||||
elif not basic_setup_success:
|
||||
_LOGGER.warning("Unable to set up core integrations. Activating safe mode")
|
||||
safe_mode = True
|
||||
_LOGGER.warning("Unable to set up core integrations. Activating recovery mode")
|
||||
recovery_mode = True
|
||||
|
||||
elif (
|
||||
"frontend" in hass.data.get(DATA_SETUP, {})
|
||||
and "frontend" not in hass.config.components
|
||||
):
|
||||
_LOGGER.warning("Detected that frontend did not load. Activating safe mode")
|
||||
_LOGGER.warning("Detected that frontend did not load. Activating recovery mode")
|
||||
# Ask integrations to shut down. It's messy but we can't
|
||||
# do a clean stop without knowing what is broken
|
||||
with contextlib.suppress(asyncio.TimeoutError):
|
||||
async with hass.timeout.async_timeout(10):
|
||||
await hass.async_stop()
|
||||
|
||||
safe_mode = True
|
||||
recovery_mode = True
|
||||
old_config = hass.config
|
||||
old_logging = hass.data.get(DATA_LOGGING)
|
||||
|
||||
@ -187,16 +188,18 @@ async def async_setup_hass(
|
||||
# Setup loader cache after the config dir has been set
|
||||
loader.async_setup(hass)
|
||||
|
||||
if safe_mode:
|
||||
_LOGGER.info("Starting in safe mode")
|
||||
hass.config.safe_mode = True
|
||||
if recovery_mode:
|
||||
_LOGGER.info("Starting in recovery mode")
|
||||
hass.config.recovery_mode = True
|
||||
|
||||
http_conf = (await http.async_get_last_config(hass)) or {}
|
||||
|
||||
await async_from_config_dict(
|
||||
{"safe_mode": {}, "http": http_conf},
|
||||
{"recovery_mode": {}, "http": http_conf},
|
||||
hass,
|
||||
)
|
||||
elif hass.config.safe_mode:
|
||||
_LOGGER.info("Starting in safe mode")
|
||||
|
||||
if runtime_config.open_ui:
|
||||
hass.add_job(open_hass_ui, hass)
|
||||
@ -471,7 +474,7 @@ def _get_domains(hass: core.HomeAssistant, config: dict[str, Any]) -> set[str]:
|
||||
domains = {key.partition(" ")[0] for key in config if key != core.DOMAIN}
|
||||
|
||||
# Add config entry domains
|
||||
if not hass.config.safe_mode:
|
||||
if not hass.config.recovery_mode:
|
||||
domains.update(hass.config_entries.async_domains())
|
||||
|
||||
# Make sure the Hass.io component is loaded
|
||||
|
@ -11,6 +11,7 @@
|
||||
"google_maps",
|
||||
"google_pubsub",
|
||||
"google_sheets",
|
||||
"google_tasks",
|
||||
"google_translate",
|
||||
"google_travel_time",
|
||||
"google_wifi",
|
||||
|
@ -1,6 +1,7 @@
|
||||
"""Support for the Abode Security System."""
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from functools import partial
|
||||
|
||||
from jaraco.abode.automation import Automation as AbodeAuto
|
||||
@ -25,7 +26,7 @@ from homeassistant.const import (
|
||||
EVENT_HOMEASSISTANT_STOP,
|
||||
Platform,
|
||||
)
|
||||
from homeassistant.core import Event, HomeAssistant, ServiceCall
|
||||
from homeassistant.core import CALLBACK_TYPE, Event, HomeAssistant, ServiceCall
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
||||
from homeassistant.helpers import config_validation as cv, entity
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
@ -71,15 +72,14 @@ PLATFORMS = [
|
||||
]
|
||||
|
||||
|
||||
@dataclass
|
||||
class AbodeSystem:
|
||||
"""Abode System class."""
|
||||
|
||||
def __init__(self, abode: Abode, polling: bool) -> None:
|
||||
"""Initialize the system."""
|
||||
self.abode = abode
|
||||
self.polling = polling
|
||||
self.entity_ids: set[str | None] = set()
|
||||
self.logout_listener = None
|
||||
abode: Abode
|
||||
polling: bool
|
||||
entity_ids: set[str | None] = field(default_factory=set)
|
||||
logout_listener: CALLBACK_TYPE | None = None
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
@ -8,5 +8,5 @@
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["accuweather"],
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["accuweather==1.0.0"]
|
||||
"requirements": ["accuweather==2.0.0"]
|
||||
}
|
||||
|
@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/adax",
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["adax", "adax_local"],
|
||||
"requirements": ["adax==0.2.0", "Adax-local==0.1.5"]
|
||||
"requirements": ["adax==0.3.0", "Adax-local==0.1.5"]
|
||||
}
|
||||
|
@ -7,5 +7,5 @@
|
||||
"integration_type": "service",
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["adguardhome"],
|
||||
"requirements": ["adguardhome==0.6.1"]
|
||||
"requirements": ["adguardhome==0.6.2"]
|
||||
}
|
||||
|
@ -127,7 +127,7 @@ class AdvantageAirAC(AdvantageAirAcEntity, ClimateEntity):
|
||||
"""Return the current target temperature."""
|
||||
# If the system is in MyZone mode, and a zone is set, return that temperature instead.
|
||||
if (
|
||||
self._ac["myZone"] > 0
|
||||
self._myzone
|
||||
and not self._ac.get(ADVANTAGE_AIR_MYAUTO_ENABLED)
|
||||
and not self._ac.get(ADVANTAGE_AIR_MYTEMP_ENABLED)
|
||||
):
|
||||
|
@ -63,7 +63,7 @@ class AdvantageAirAcEntity(AdvantageAirEntity):
|
||||
return self.coordinator.data["aircons"][self.ac_key]["info"]
|
||||
|
||||
@property
|
||||
def _myzone(self) -> dict[str, Any]:
|
||||
def _myzone(self) -> dict[str, Any] | None:
|
||||
return self.coordinator.data["aircons"][self.ac_key]["zones"].get(
|
||||
f"z{self._ac['myZone']:02}"
|
||||
)
|
||||
|
@ -9,8 +9,9 @@ ATTR_API_CAT_DESCRIPTION = "Name"
|
||||
ATTR_API_O3 = "O3"
|
||||
ATTR_API_PM25 = "PM2.5"
|
||||
ATTR_API_POLLUTANT = "Pollutant"
|
||||
ATTR_API_REPORT_DATE = "HourObserved"
|
||||
ATTR_API_REPORT_HOUR = "DateObserved"
|
||||
ATTR_API_REPORT_DATE = "DateObserved"
|
||||
ATTR_API_REPORT_HOUR = "HourObserved"
|
||||
ATTR_API_REPORT_TZ = "LocalTimeZone"
|
||||
ATTR_API_STATE = "StateCode"
|
||||
ATTR_API_STATION = "ReportingArea"
|
||||
ATTR_API_STATION_LATITUDE = "Latitude"
|
||||
|
@ -20,6 +20,7 @@ from .const import (
|
||||
ATTR_API_POLLUTANT,
|
||||
ATTR_API_REPORT_DATE,
|
||||
ATTR_API_REPORT_HOUR,
|
||||
ATTR_API_REPORT_TZ,
|
||||
ATTR_API_STATE,
|
||||
ATTR_API_STATION,
|
||||
ATTR_API_STATION_LATITUDE,
|
||||
@ -83,6 +84,7 @@ class AirNowDataUpdateCoordinator(DataUpdateCoordinator):
|
||||
# Copy Report Details
|
||||
data[ATTR_API_REPORT_DATE] = obv[ATTR_API_REPORT_DATE]
|
||||
data[ATTR_API_REPORT_HOUR] = obv[ATTR_API_REPORT_HOUR]
|
||||
data[ATTR_API_REPORT_TZ] = obv[ATTR_API_REPORT_TZ]
|
||||
|
||||
# Copy Station Details
|
||||
data[ATTR_API_STATE] = obv[ATTR_API_STATE]
|
||||
|
@ -3,6 +3,7 @@ from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
@ -13,6 +14,7 @@ from homeassistant.components.sensor import (
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
ATTR_TIME,
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
CONCENTRATION_PARTS_PER_MILLION,
|
||||
)
|
||||
@ -21,6 +23,7 @@ from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import StateType
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
from homeassistant.util.dt import get_time_zone
|
||||
|
||||
from . import AirNowDataUpdateCoordinator
|
||||
from .const import (
|
||||
@ -29,6 +32,9 @@ from .const import (
|
||||
ATTR_API_AQI_LEVEL,
|
||||
ATTR_API_O3,
|
||||
ATTR_API_PM25,
|
||||
ATTR_API_REPORT_DATE,
|
||||
ATTR_API_REPORT_HOUR,
|
||||
ATTR_API_REPORT_TZ,
|
||||
ATTR_API_STATION,
|
||||
ATTR_API_STATION_LATITUDE,
|
||||
ATTR_API_STATION_LONGITUDE,
|
||||
@ -78,6 +84,12 @@ SENSOR_TYPES: tuple[AirNowEntityDescription, ...] = (
|
||||
extra_state_attributes_fn=lambda data: {
|
||||
ATTR_DESCR: data[ATTR_API_AQI_DESCRIPTION],
|
||||
ATTR_LEVEL: data[ATTR_API_AQI_LEVEL],
|
||||
ATTR_TIME: datetime.strptime(
|
||||
f"{data[ATTR_API_REPORT_DATE]} {data[ATTR_API_REPORT_HOUR]}",
|
||||
"%Y-%m-%d %H",
|
||||
)
|
||||
.replace(tzinfo=get_time_zone(data[ATTR_API_REPORT_TZ]))
|
||||
.isoformat(),
|
||||
},
|
||||
),
|
||||
AirNowEntityDescription(
|
||||
|
@ -226,6 +226,14 @@ class AirthingsSensor(
|
||||
model=airthings_device.model,
|
||||
)
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Check if device and sensor is available in data."""
|
||||
return (
|
||||
super().available
|
||||
and self.entity_description.key in self.coordinator.data.sensors
|
||||
)
|
||||
|
||||
@property
|
||||
def native_value(self) -> StateType:
|
||||
"""Return the value reported by the sensor."""
|
||||
|
@ -5,7 +5,7 @@
|
||||
"user": {
|
||||
"description": "[%key:component::bluetooth::config::step::user::description%]",
|
||||
"data": {
|
||||
"address": "[%key:component::bluetooth::config::step::user::data::address%]"
|
||||
"address": "[%key:common::config_flow::data::device%]"
|
||||
}
|
||||
},
|
||||
"bluetooth_confirm": {
|
||||
|
@ -421,7 +421,6 @@ class AirVisualEntity(CoordinatorEntity):
|
||||
self._entry = entry
|
||||
self.entity_description = description
|
||||
|
||||
# pylint: disable-next=hass-missing-super-call
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Register callbacks."""
|
||||
await super().async_added_to_hass()
|
||||
|
@ -7,15 +7,21 @@ from aioairzone_cloud.common import OperationAction, OperationMode, TemperatureU
|
||||
from aioairzone_cloud.const import (
|
||||
API_MODE,
|
||||
API_OPTS,
|
||||
API_PARAMS,
|
||||
API_POWER,
|
||||
API_SETPOINT,
|
||||
API_UNITS,
|
||||
API_VALUE,
|
||||
AZD_ACTION,
|
||||
AZD_AIDOOS,
|
||||
AZD_GROUPS,
|
||||
AZD_HUMIDITY,
|
||||
AZD_INSTALLATIONS,
|
||||
AZD_MASTER,
|
||||
AZD_MODE,
|
||||
AZD_MODES,
|
||||
AZD_NUM_DEVICES,
|
||||
AZD_NUM_GROUPS,
|
||||
AZD_POWER,
|
||||
AZD_TEMP,
|
||||
AZD_TEMP_SET,
|
||||
@ -39,7 +45,13 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import AirzoneUpdateCoordinator
|
||||
from .entity import AirzoneEntity, AirzoneZoneEntity
|
||||
from .entity import (
|
||||
AirzoneAidooEntity,
|
||||
AirzoneEntity,
|
||||
AirzoneGroupEntity,
|
||||
AirzoneInstallationEntity,
|
||||
AirzoneZoneEntity,
|
||||
)
|
||||
|
||||
HVAC_ACTION_LIB_TO_HASS: Final[dict[OperationAction, HVACAction]] = {
|
||||
OperationAction.COOLING: HVACAction.COOLING,
|
||||
@ -82,6 +94,38 @@ async def async_setup_entry(
|
||||
|
||||
entities: list[AirzoneClimate] = []
|
||||
|
||||
# Aidoos
|
||||
for aidoo_id, aidoo_data in coordinator.data.get(AZD_AIDOOS, {}).items():
|
||||
entities.append(
|
||||
AirzoneAidooClimate(
|
||||
coordinator,
|
||||
aidoo_id,
|
||||
aidoo_data,
|
||||
)
|
||||
)
|
||||
|
||||
# Groups
|
||||
for group_id, group_data in coordinator.data.get(AZD_GROUPS, {}).items():
|
||||
if group_data[AZD_NUM_DEVICES] > 1:
|
||||
entities.append(
|
||||
AirzoneGroupClimate(
|
||||
coordinator,
|
||||
group_id,
|
||||
group_data,
|
||||
)
|
||||
)
|
||||
|
||||
# Installations
|
||||
for inst_id, inst_data in coordinator.data.get(AZD_INSTALLATIONS, {}).items():
|
||||
if inst_data[AZD_NUM_GROUPS] > 1:
|
||||
entities.append(
|
||||
AirzoneInstallationClimate(
|
||||
coordinator,
|
||||
inst_id,
|
||||
inst_data,
|
||||
)
|
||||
)
|
||||
|
||||
# Zones
|
||||
for zone_id, zone_data in coordinator.data.get(AZD_ZONES, {}).items():
|
||||
entities.append(
|
||||
@ -98,9 +142,39 @@ async def async_setup_entry(
|
||||
class AirzoneClimate(AirzoneEntity, ClimateEntity):
|
||||
"""Define an Airzone Cloud climate."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
_attr_name = None
|
||||
_attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
|
||||
@callback
|
||||
def _handle_coordinator_update(self) -> None:
|
||||
"""Update attributes when the coordinator updates."""
|
||||
self._async_update_attrs()
|
||||
super()._handle_coordinator_update()
|
||||
|
||||
@callback
|
||||
def _async_update_attrs(self) -> None:
|
||||
"""Update climate attributes."""
|
||||
self._attr_current_temperature = self.get_airzone_value(AZD_TEMP)
|
||||
self._attr_current_humidity = self.get_airzone_value(AZD_HUMIDITY)
|
||||
self._attr_hvac_action = HVAC_ACTION_LIB_TO_HASS[
|
||||
self.get_airzone_value(AZD_ACTION)
|
||||
]
|
||||
if self.get_airzone_value(AZD_POWER):
|
||||
self._attr_hvac_mode = HVAC_MODE_LIB_TO_HASS[
|
||||
self.get_airzone_value(AZD_MODE)
|
||||
]
|
||||
else:
|
||||
self._attr_hvac_mode = HVACMode.OFF
|
||||
self._attr_max_temp = self.get_airzone_value(AZD_TEMP_SET_MAX)
|
||||
self._attr_min_temp = self.get_airzone_value(AZD_TEMP_SET_MIN)
|
||||
self._attr_target_temperature = self.get_airzone_value(AZD_TEMP_SET)
|
||||
|
||||
|
||||
class AirzoneDeviceClimate(AirzoneClimate):
|
||||
"""Define an Airzone Cloud Device base class."""
|
||||
|
||||
async def async_turn_on(self) -> None:
|
||||
"""Turn the entity on."""
|
||||
params = {
|
||||
@ -131,36 +205,143 @@ class AirzoneClimate(AirzoneEntity, ClimateEntity):
|
||||
}
|
||||
await self._async_update_params(params)
|
||||
|
||||
@callback
|
||||
def _handle_coordinator_update(self) -> None:
|
||||
"""Update attributes when the coordinator updates."""
|
||||
self._async_update_attrs()
|
||||
super()._handle_coordinator_update()
|
||||
|
||||
@callback
|
||||
def _async_update_attrs(self) -> None:
|
||||
"""Update climate attributes."""
|
||||
self._attr_current_temperature = self.get_airzone_value(AZD_TEMP)
|
||||
self._attr_current_humidity = self.get_airzone_value(AZD_HUMIDITY)
|
||||
self._attr_hvac_action = HVAC_ACTION_LIB_TO_HASS[
|
||||
self.get_airzone_value(AZD_ACTION)
|
||||
]
|
||||
if self.get_airzone_value(AZD_POWER):
|
||||
self._attr_hvac_mode = HVAC_MODE_LIB_TO_HASS[
|
||||
self.get_airzone_value(AZD_MODE)
|
||||
]
|
||||
class AirzoneDeviceGroupClimate(AirzoneClimate):
|
||||
"""Define an Airzone Cloud DeviceGroup base class."""
|
||||
|
||||
async def async_turn_on(self) -> None:
|
||||
"""Turn the entity on."""
|
||||
params = {
|
||||
API_PARAMS: {
|
||||
API_POWER: True,
|
||||
},
|
||||
}
|
||||
await self._async_update_params(params)
|
||||
|
||||
async def async_turn_off(self) -> None:
|
||||
"""Turn the entity off."""
|
||||
params = {
|
||||
API_PARAMS: {
|
||||
API_POWER: False,
|
||||
},
|
||||
}
|
||||
await self._async_update_params(params)
|
||||
|
||||
async def async_set_temperature(self, **kwargs: Any) -> None:
|
||||
"""Set new target temperature."""
|
||||
params: dict[str, Any] = {}
|
||||
if ATTR_TEMPERATURE in kwargs:
|
||||
params[API_PARAMS] = {
|
||||
API_SETPOINT: kwargs[ATTR_TEMPERATURE],
|
||||
}
|
||||
params[API_OPTS] = {
|
||||
API_UNITS: TemperatureUnit.CELSIUS.value,
|
||||
}
|
||||
await self._async_update_params(params)
|
||||
|
||||
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
|
||||
"""Set hvac mode."""
|
||||
params: dict[str, Any] = {
|
||||
API_PARAMS: {},
|
||||
}
|
||||
if hvac_mode == HVACMode.OFF:
|
||||
params[API_PARAMS][API_POWER] = False
|
||||
else:
|
||||
self._attr_hvac_mode = HVACMode.OFF
|
||||
self._attr_max_temp = self.get_airzone_value(AZD_TEMP_SET_MAX)
|
||||
self._attr_min_temp = self.get_airzone_value(AZD_TEMP_SET_MIN)
|
||||
self._attr_target_temperature = self.get_airzone_value(AZD_TEMP_SET)
|
||||
mode = HVAC_MODE_HASS_TO_LIB[hvac_mode]
|
||||
params[API_PARAMS][API_MODE] = mode.value
|
||||
params[API_PARAMS][API_POWER] = True
|
||||
await self._async_update_params(params)
|
||||
|
||||
|
||||
class AirzoneZoneClimate(AirzoneZoneEntity, AirzoneClimate):
|
||||
class AirzoneAidooClimate(AirzoneAidooEntity, AirzoneDeviceClimate):
|
||||
"""Define an Airzone Cloud Aidoo climate."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: AirzoneUpdateCoordinator,
|
||||
aidoo_id: str,
|
||||
aidoo_data: dict,
|
||||
) -> None:
|
||||
"""Initialize Airzone Cloud Aidoo climate."""
|
||||
super().__init__(coordinator, aidoo_id, aidoo_data)
|
||||
|
||||
self._attr_unique_id = aidoo_id
|
||||
self._attr_target_temperature_step = self.get_airzone_value(AZD_TEMP_STEP)
|
||||
self._attr_hvac_modes = [
|
||||
HVAC_MODE_LIB_TO_HASS[mode] for mode in self.get_airzone_value(AZD_MODES)
|
||||
]
|
||||
if HVACMode.OFF not in self._attr_hvac_modes:
|
||||
self._attr_hvac_modes += [HVACMode.OFF]
|
||||
|
||||
self._async_update_attrs()
|
||||
|
||||
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
|
||||
"""Set hvac mode."""
|
||||
params: dict[str, Any] = {}
|
||||
if hvac_mode == HVACMode.OFF:
|
||||
params[API_POWER] = {
|
||||
API_VALUE: False,
|
||||
}
|
||||
else:
|
||||
mode = HVAC_MODE_HASS_TO_LIB[hvac_mode]
|
||||
params[API_MODE] = {
|
||||
API_VALUE: mode.value,
|
||||
}
|
||||
params[API_POWER] = {
|
||||
API_VALUE: True,
|
||||
}
|
||||
await self._async_update_params(params)
|
||||
|
||||
|
||||
class AirzoneGroupClimate(AirzoneGroupEntity, AirzoneDeviceGroupClimate):
|
||||
"""Define an Airzone Cloud Group climate."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: AirzoneUpdateCoordinator,
|
||||
group_id: str,
|
||||
group_data: dict,
|
||||
) -> None:
|
||||
"""Initialize Airzone Cloud Group climate."""
|
||||
super().__init__(coordinator, group_id, group_data)
|
||||
|
||||
self._attr_unique_id = group_id
|
||||
self._attr_target_temperature_step = self.get_airzone_value(AZD_TEMP_STEP)
|
||||
self._attr_hvac_modes = [
|
||||
HVAC_MODE_LIB_TO_HASS[mode] for mode in self.get_airzone_value(AZD_MODES)
|
||||
]
|
||||
if HVACMode.OFF not in self._attr_hvac_modes:
|
||||
self._attr_hvac_modes += [HVACMode.OFF]
|
||||
|
||||
self._async_update_attrs()
|
||||
|
||||
|
||||
class AirzoneInstallationClimate(AirzoneInstallationEntity, AirzoneDeviceGroupClimate):
|
||||
"""Define an Airzone Cloud Installation climate."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: AirzoneUpdateCoordinator,
|
||||
inst_id: str,
|
||||
inst_data: dict,
|
||||
) -> None:
|
||||
"""Initialize Airzone Cloud Installation climate."""
|
||||
super().__init__(coordinator, inst_id, inst_data)
|
||||
|
||||
self._attr_unique_id = inst_id
|
||||
self._attr_target_temperature_step = self.get_airzone_value(AZD_TEMP_STEP)
|
||||
self._attr_hvac_modes = [
|
||||
HVAC_MODE_LIB_TO_HASS[mode] for mode in self.get_airzone_value(AZD_MODES)
|
||||
]
|
||||
if HVACMode.OFF not in self._attr_hvac_modes:
|
||||
self._attr_hvac_modes += [HVACMode.OFF]
|
||||
|
||||
self._async_update_attrs()
|
||||
|
||||
|
||||
class AirzoneZoneClimate(AirzoneZoneEntity, AirzoneDeviceClimate):
|
||||
"""Define an Airzone Cloud Zone climate."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: AirzoneUpdateCoordinator,
|
||||
@ -191,7 +372,8 @@ class AirzoneZoneClimate(AirzoneZoneEntity, AirzoneClimate):
|
||||
}
|
||||
else:
|
||||
mode = HVAC_MODE_HASS_TO_LIB[hvac_mode]
|
||||
if mode != self.get_airzone_value(AZD_MODE):
|
||||
cur_mode = self.get_airzone_value(AZD_MODE)
|
||||
if hvac_mode != HVAC_MODE_LIB_TO_HASS[cur_mode]:
|
||||
if self.get_airzone_value(AZD_MASTER):
|
||||
params[API_MODE] = {
|
||||
API_VALUE: mode.value,
|
||||
|
@ -9,6 +9,8 @@ from aioairzone_cloud.const import (
|
||||
AZD_AIDOOS,
|
||||
AZD_AVAILABLE,
|
||||
AZD_FIRMWARE,
|
||||
AZD_GROUPS,
|
||||
AZD_INSTALLATIONS,
|
||||
AZD_NAME,
|
||||
AZD_SYSTEM_ID,
|
||||
AZD_SYSTEMS,
|
||||
@ -74,6 +76,104 @@ class AirzoneAidooEntity(AirzoneEntity):
|
||||
value = aidoo.get(key)
|
||||
return value
|
||||
|
||||
async def _async_update_params(self, params: dict[str, Any]) -> None:
|
||||
"""Send Aidoo parameters to Cloud API."""
|
||||
_LOGGER.debug("aidoo=%s: update_params=%s", self.name, params)
|
||||
try:
|
||||
await self.coordinator.airzone.api_set_aidoo_id_params(
|
||||
self.aidoo_id, params
|
||||
)
|
||||
except AirzoneCloudError as error:
|
||||
raise HomeAssistantError(
|
||||
f"Failed to set {self.name} params: {error}"
|
||||
) from error
|
||||
|
||||
self.coordinator.async_set_updated_data(self.coordinator.airzone.data())
|
||||
|
||||
|
||||
class AirzoneGroupEntity(AirzoneEntity):
|
||||
"""Define an Airzone Cloud Group entity."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: AirzoneUpdateCoordinator,
|
||||
group_id: str,
|
||||
group_data: dict[str, Any],
|
||||
) -> None:
|
||||
"""Initialize."""
|
||||
super().__init__(coordinator)
|
||||
|
||||
self.group_id = group_id
|
||||
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, group_id)},
|
||||
manufacturer=MANUFACTURER,
|
||||
name=group_data[AZD_NAME],
|
||||
)
|
||||
|
||||
def get_airzone_value(self, key: str) -> Any:
|
||||
"""Return Group value by key."""
|
||||
value = None
|
||||
if group := self.coordinator.data[AZD_GROUPS].get(self.group_id):
|
||||
value = group.get(key)
|
||||
return value
|
||||
|
||||
async def _async_update_params(self, params: dict[str, Any]) -> None:
|
||||
"""Send Group parameters to Cloud API."""
|
||||
_LOGGER.debug("group=%s: update_params=%s", self.name, params)
|
||||
try:
|
||||
await self.coordinator.airzone.api_set_group_id_params(
|
||||
self.group_id, params
|
||||
)
|
||||
except AirzoneCloudError as error:
|
||||
raise HomeAssistantError(
|
||||
f"Failed to set {self.name} params: {error}"
|
||||
) from error
|
||||
|
||||
self.coordinator.async_set_updated_data(self.coordinator.airzone.data())
|
||||
|
||||
|
||||
class AirzoneInstallationEntity(AirzoneEntity):
|
||||
"""Define an Airzone Cloud Installation entity."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: AirzoneUpdateCoordinator,
|
||||
inst_id: str,
|
||||
inst_data: dict[str, Any],
|
||||
) -> None:
|
||||
"""Initialize."""
|
||||
super().__init__(coordinator)
|
||||
|
||||
self.inst_id = inst_id
|
||||
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, inst_id)},
|
||||
manufacturer=MANUFACTURER,
|
||||
name=inst_data[AZD_NAME],
|
||||
)
|
||||
|
||||
def get_airzone_value(self, key: str) -> Any:
|
||||
"""Return Installation value by key."""
|
||||
value = None
|
||||
if inst := self.coordinator.data[AZD_INSTALLATIONS].get(self.inst_id):
|
||||
value = inst.get(key)
|
||||
return value
|
||||
|
||||
async def _async_update_params(self, params: dict[str, Any]) -> None:
|
||||
"""Send Installation parameters to Cloud API."""
|
||||
_LOGGER.debug("installation=%s: update_params=%s", self.name, params)
|
||||
try:
|
||||
await self.coordinator.airzone.api_set_installation_id_params(
|
||||
self.inst_id, params
|
||||
)
|
||||
except AirzoneCloudError as error:
|
||||
raise HomeAssistantError(
|
||||
f"Failed to set {self.name} params: {error}"
|
||||
) from error
|
||||
|
||||
self.coordinator.async_set_updated_data(self.coordinator.airzone.data())
|
||||
|
||||
|
||||
class AirzoneSystemEntity(AirzoneEntity):
|
||||
"""Define an Airzone Cloud System entity."""
|
||||
|
@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/airzone_cloud",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["aioairzone_cloud"],
|
||||
"requirements": ["aioairzone-cloud==0.2.3"]
|
||||
"requirements": ["aioairzone-cloud==0.3.1"]
|
||||
}
|
||||
|
@ -580,8 +580,8 @@ class AlexaBrightnessController(AlexaCapability):
|
||||
"""Read and return a property."""
|
||||
if name != "brightness":
|
||||
raise UnsupportedProperty(name)
|
||||
if "brightness" in self.entity.attributes:
|
||||
return round(self.entity.attributes["brightness"] / 255.0 * 100)
|
||||
if brightness := self.entity.attributes.get("brightness"):
|
||||
return round(brightness / 255.0 * 100)
|
||||
return 0
|
||||
|
||||
|
||||
@ -630,12 +630,16 @@ class AlexaColorController(AlexaCapability):
|
||||
if name != "color":
|
||||
raise UnsupportedProperty(name)
|
||||
|
||||
hue, saturation = self.entity.attributes.get(light.ATTR_HS_COLOR, (0, 0))
|
||||
hue_saturation: tuple[float, float] | None
|
||||
if (hue_saturation := self.entity.attributes.get(light.ATTR_HS_COLOR)) is None:
|
||||
hue_saturation = (0, 0)
|
||||
if (brightness := self.entity.attributes.get(light.ATTR_BRIGHTNESS)) is None:
|
||||
brightness = 0
|
||||
|
||||
return {
|
||||
"hue": hue,
|
||||
"saturation": saturation / 100.0,
|
||||
"brightness": self.entity.attributes.get(light.ATTR_BRIGHTNESS, 0) / 255.0,
|
||||
"hue": hue_saturation[0],
|
||||
"saturation": hue_saturation[1] / 100.0,
|
||||
"brightness": brightness / 255.0,
|
||||
}
|
||||
|
||||
|
||||
@ -683,10 +687,8 @@ class AlexaColorTemperatureController(AlexaCapability):
|
||||
"""Read and return a property."""
|
||||
if name != "colorTemperatureInKelvin":
|
||||
raise UnsupportedProperty(name)
|
||||
if "color_temp" in self.entity.attributes:
|
||||
return color_util.color_temperature_mired_to_kelvin(
|
||||
self.entity.attributes["color_temp"]
|
||||
)
|
||||
if color_temp := self.entity.attributes.get("color_temp"):
|
||||
return color_util.color_temperature_mired_to_kelvin(color_temp)
|
||||
return None
|
||||
|
||||
|
||||
|
@ -5,7 +5,6 @@ from homeassistant.components.mjpeg import MjpegCamera, filter_urllib3_logging
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONF_HOST,
|
||||
CONF_NAME,
|
||||
CONF_PASSWORD,
|
||||
CONF_USERNAME,
|
||||
HTTP_BASIC_AUTHENTICATION,
|
||||
@ -39,14 +38,7 @@ class IPWebcamCamera(MjpegCamera):
|
||||
|
||||
def __init__(self, coordinator: AndroidIPCamDataUpdateCoordinator) -> None:
|
||||
"""Initialize the camera."""
|
||||
name = None
|
||||
# keep imported name until YAML is removed
|
||||
if CONF_NAME in coordinator.config_entry.data:
|
||||
name = coordinator.config_entry.data[CONF_NAME]
|
||||
self._attr_has_entity_name = False
|
||||
|
||||
super().__init__(
|
||||
name=name,
|
||||
mjpeg_url=coordinator.cam.mjpeg_url,
|
||||
still_image_url=coordinator.cam.image_url,
|
||||
authentication=HTTP_BASIC_AUTHENTICATION,
|
||||
@ -56,5 +48,5 @@ class IPWebcamCamera(MjpegCamera):
|
||||
self._attr_unique_id = f"{coordinator.config_entry.entry_id}-camera"
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, coordinator.config_entry.entry_id)},
|
||||
name=name or coordinator.config_entry.data[CONF_HOST],
|
||||
name=coordinator.config_entry.data[CONF_HOST],
|
||||
)
|
||||
|
@ -19,11 +19,6 @@ class AndroidIPCamBaseEntity(CoordinatorEntity[AndroidIPCamDataUpdateCoordinator
|
||||
) -> None:
|
||||
"""Initialize the base entity."""
|
||||
super().__init__(coordinator)
|
||||
if CONF_NAME in coordinator.config_entry.data:
|
||||
# name is legacy imported from YAML config
|
||||
# this block can be removed when removing import from YAML
|
||||
self._attr_name = f"{coordinator.config_entry.data[CONF_NAME]} {self.entity_description.name}"
|
||||
self._attr_has_entity_name = False
|
||||
self.cam = coordinator.cam
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, coordinator.config_entry.entry_id)},
|
||||
|
@ -9,7 +9,7 @@
|
||||
"loggers": ["adb_shell", "androidtv", "pure_python_adb"],
|
||||
"requirements": [
|
||||
"adb-shell[async]==0.4.4",
|
||||
"androidtv[async]==0.0.72",
|
||||
"androidtv[async]==0.0.73",
|
||||
"pure-python-adb[async]==0.3.0.dev0"
|
||||
]
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
"""Support for Apache Kafka."""
|
||||
from datetime import datetime
|
||||
import json
|
||||
import sys
|
||||
|
||||
from aiokafka import AIOKafkaProducer
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.const import (
|
||||
@ -16,11 +16,16 @@ from homeassistant.const import (
|
||||
STATE_UNKNOWN,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.entityfilter import FILTER_SCHEMA
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
from homeassistant.util import ssl as ssl_util
|
||||
|
||||
if sys.version_info < (3, 12):
|
||||
from aiokafka import AIOKafkaProducer
|
||||
|
||||
|
||||
DOMAIN = "apache_kafka"
|
||||
|
||||
CONF_FILTER = "filter"
|
||||
@ -49,6 +54,10 @@ CONFIG_SCHEMA = vol.Schema(
|
||||
|
||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
"""Activate the Apache Kafka integration."""
|
||||
if sys.version_info >= (3, 12):
|
||||
raise HomeAssistantError(
|
||||
"Apache Kafka is not supported on Python 3.12. Please use Python 3.11."
|
||||
)
|
||||
conf = config[DOMAIN]
|
||||
|
||||
kafka = hass.data[DOMAIN] = KafkaManager(
|
||||
|
@ -7,7 +7,7 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/apple_tv",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["pyatv", "srptools"],
|
||||
"requirements": ["pyatv==0.13.4"],
|
||||
"requirements": ["pyatv==0.14.3"],
|
||||
"zeroconf": [
|
||||
"_mediaremotetv._tcp.local.",
|
||||
"_companion-link._tcp.local.",
|
||||
|
@ -371,11 +371,15 @@ class AppleTvMediaPlayer(AppleTVEntity, MediaPlayerEntity):
|
||||
@property
|
||||
def repeat(self) -> RepeatMode | None:
|
||||
"""Return current repeat mode."""
|
||||
if self._playing and self._is_feature_available(FeatureName.Repeat):
|
||||
if (
|
||||
self._playing
|
||||
and self._is_feature_available(FeatureName.Repeat)
|
||||
and (repeat := self._playing.repeat)
|
||||
):
|
||||
return {
|
||||
RepeatState.Track: RepeatMode.ONE,
|
||||
RepeatState.All: RepeatMode.ALL,
|
||||
}.get(self._playing.repeat, RepeatMode.OFF)
|
||||
}.get(repeat, RepeatMode.OFF)
|
||||
return None
|
||||
|
||||
@property
|
||||
|
@ -21,6 +21,15 @@ from .const import DOMAIN
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
PARALLEL_UPDATES = 0
|
||||
COMMAND_TO_ATTRIBUTE = {
|
||||
"wakeup": ("power", "turn_on"),
|
||||
"suspend": ("power", "turn_off"),
|
||||
"turn_on": ("power", "turn_on"),
|
||||
"turn_off": ("power", "turn_off"),
|
||||
"volume_up": ("audio", "volume_up"),
|
||||
"volume_down": ("audio", "volume_down"),
|
||||
"home_hold": ("remote_control", "home"),
|
||||
}
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
@ -61,6 +70,12 @@ class AppleTVRemote(AppleTVEntity, RemoteEntity):
|
||||
|
||||
for _ in range(num_repeats):
|
||||
for single_command in command:
|
||||
attr_value = None
|
||||
if attributes := COMMAND_TO_ATTRIBUTE.get(single_command):
|
||||
attr_value = self.atv
|
||||
for attr_name in attributes:
|
||||
attr_value = getattr(attr_value, attr_name, None)
|
||||
if not attr_value:
|
||||
attr_value = getattr(self.atv.remote_control, single_command, None)
|
||||
if not attr_value:
|
||||
raise ValueError("Command not found. Exiting sequence")
|
||||
|
@ -5,5 +5,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/apprise",
|
||||
"iot_class": "cloud_push",
|
||||
"loggers": ["apprise"],
|
||||
"requirements": ["apprise==1.5.0"]
|
||||
"requirements": ["apprise==1.6.0"]
|
||||
}
|
||||
|
@ -13,11 +13,11 @@
|
||||
"connectable": false
|
||||
}
|
||||
],
|
||||
"codeowners": ["@aschmitz"],
|
||||
"codeowners": ["@aschmitz", "@thecode"],
|
||||
"config_flow": true,
|
||||
"dependencies": ["bluetooth_adapters"],
|
||||
"documentation": "https://www.home-assistant.io/integrations/aranet",
|
||||
"integration_type": "device",
|
||||
"iot_class": "local_push",
|
||||
"requirements": ["aranet4==2.1.3"]
|
||||
"requirements": ["aranet4==2.2.2"]
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
from aranet4.client import Aranet4Advertisement
|
||||
from bleak.backends.device import BLEDevice
|
||||
@ -32,6 +33,7 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity import EntityDescription
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
@ -121,22 +123,22 @@ def sensor_update_to_bluetooth_data_update(
|
||||
adv: Aranet4Advertisement,
|
||||
) -> PassiveBluetoothDataUpdate:
|
||||
"""Convert a sensor update to a Bluetooth data update."""
|
||||
data: dict[PassiveBluetoothEntityKey, Any] = {}
|
||||
names: dict[PassiveBluetoothEntityKey, str | None] = {}
|
||||
descs: dict[PassiveBluetoothEntityKey, EntityDescription] = {}
|
||||
for key, desc in SENSOR_DESCRIPTIONS.items():
|
||||
tag = _device_key_to_bluetooth_entity_key(adv.device, key)
|
||||
val = getattr(adv.readings, key)
|
||||
if val == -1:
|
||||
continue
|
||||
data[tag] = val
|
||||
names[tag] = desc.name
|
||||
descs[tag] = desc
|
||||
return PassiveBluetoothDataUpdate(
|
||||
devices={adv.device.address: _sensor_device_info_to_hass(adv)},
|
||||
entity_descriptions={
|
||||
_device_key_to_bluetooth_entity_key(adv.device, key): desc
|
||||
for key, desc in SENSOR_DESCRIPTIONS.items()
|
||||
},
|
||||
entity_data={
|
||||
_device_key_to_bluetooth_entity_key(adv.device, key): getattr(
|
||||
adv.readings, key, None
|
||||
)
|
||||
for key in SENSOR_DESCRIPTIONS
|
||||
},
|
||||
entity_names={
|
||||
_device_key_to_bluetooth_entity_key(adv.device, key): desc.name
|
||||
for key, desc in SENSOR_DESCRIPTIONS.items()
|
||||
},
|
||||
entity_descriptions=descs,
|
||||
entity_data=data,
|
||||
entity_names=names,
|
||||
)
|
||||
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
"user": {
|
||||
"description": "[%key:component::bluetooth::config::step::user::description%]",
|
||||
"data": {
|
||||
"address": "[%key:component::bluetooth::config::step::user::data::address%]"
|
||||
"address": "[%key:common::config_flow::data::device%]"
|
||||
}
|
||||
},
|
||||
"bluetooth_confirm": {
|
||||
|
@ -60,7 +60,6 @@ class VariableSensorEntity(AsekoEntity, SensorEntity):
|
||||
|
||||
self._attr_icon = {
|
||||
"clf": "mdi:flask",
|
||||
"ph": "mdi:ph",
|
||||
"rx": "mdi:test-tube",
|
||||
"waterLevel": "mdi:waves",
|
||||
"waterTemp": "mdi:coolant-temperature",
|
||||
@ -69,6 +68,7 @@ class VariableSensorEntity(AsekoEntity, SensorEntity):
|
||||
self._attr_device_class = {
|
||||
"airTemp": SensorDeviceClass.TEMPERATURE,
|
||||
"waterTemp": SensorDeviceClass.TEMPERATURE,
|
||||
"ph": SensorDeviceClass.PH,
|
||||
}.get(self._variable.type)
|
||||
|
||||
@property
|
||||
|
@ -9,7 +9,7 @@ from homeassistant.components import stt
|
||||
from homeassistant.core import Context, HomeAssistant
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from .const import CONF_DEBUG_RECORDING_DIR, DATA_CONFIG, DOMAIN
|
||||
from .const import CONF_DEBUG_RECORDING_DIR, DATA_CONFIG, DATA_LAST_WAKE_UP, DOMAIN
|
||||
from .error import PipelineNotFound
|
||||
from .pipeline import (
|
||||
AudioSettings,
|
||||
@ -58,6 +58,9 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
"""Set up the Assist pipeline integration."""
|
||||
hass.data[DATA_CONFIG] = config.get(DOMAIN, {})
|
||||
|
||||
# wake_word_id -> timestamp of last detection (monotonic_ns)
|
||||
hass.data[DATA_LAST_WAKE_UP] = {}
|
||||
|
||||
await async_setup_pipeline_store(hass)
|
||||
async_register_websocket_api(hass)
|
||||
|
||||
|
@ -12,10 +12,12 @@ from pathlib import Path
|
||||
from queue import Queue
|
||||
from threading import Thread
|
||||
import time
|
||||
from typing import Any, Final, cast
|
||||
from typing import TYPE_CHECKING, Any, Final, cast
|
||||
import wave
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from webrtc_noise_gain import AudioProcessor
|
||||
|
||||
from homeassistant.components import (
|
||||
@ -522,6 +524,12 @@ class PipelineRun:
|
||||
# Initialize with audio settings
|
||||
self.audio_processor_buffer = AudioBuffer(AUDIO_PROCESSOR_BYTES)
|
||||
if self.audio_settings.needs_processor:
|
||||
# Delay import of webrtc so HA start up is not crashing
|
||||
# on older architectures (armhf).
|
||||
#
|
||||
# pylint: disable=import-outside-toplevel
|
||||
from webrtc_noise_gain import AudioProcessor
|
||||
|
||||
self.audio_processor = AudioProcessor(
|
||||
self.audio_settings.auto_gain_dbfs,
|
||||
self.audio_settings.noise_suppression_level,
|
||||
@ -681,7 +689,8 @@ class PipelineRun:
|
||||
wake_word_output: dict[str, Any] = {}
|
||||
else:
|
||||
# Avoid duplicate detections by checking cooldown
|
||||
last_wake_up = self.hass.data.get(DATA_LAST_WAKE_UP)
|
||||
wake_up_key = f"{self.wake_word_entity_id}.{result.wake_word_id}"
|
||||
last_wake_up = self.hass.data[DATA_LAST_WAKE_UP].get(wake_up_key)
|
||||
if last_wake_up is not None:
|
||||
sec_since_last_wake_up = time.monotonic() - last_wake_up
|
||||
if sec_since_last_wake_up < wake_word_settings.cooldown_seconds:
|
||||
@ -689,7 +698,7 @@ class PipelineRun:
|
||||
raise WakeWordDetectionAborted
|
||||
|
||||
# Record last wake up time to block duplicate detections
|
||||
self.hass.data[DATA_LAST_WAKE_UP] = time.monotonic()
|
||||
self.hass.data[DATA_LAST_WAKE_UP][wake_up_key] = time.monotonic()
|
||||
|
||||
if result.queued_audio:
|
||||
# Add audio that was pending at detection.
|
||||
|
@ -7,8 +7,6 @@ from dataclasses import dataclass
|
||||
from enum import StrEnum
|
||||
from typing import Final, cast
|
||||
|
||||
from webrtc_noise_gain import AudioProcessor
|
||||
|
||||
_SAMPLE_RATE: Final = 16000 # Hz
|
||||
_SAMPLE_WIDTH: Final = 2 # bytes
|
||||
|
||||
@ -51,6 +49,12 @@ class WebRtcVad(VoiceActivityDetector):
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize webrtcvad."""
|
||||
# Delay import of webrtc so HA start up is not crashing
|
||||
# on older architectures (armhf).
|
||||
#
|
||||
# pylint: disable=import-outside-toplevel
|
||||
from webrtc_noise_gain import AudioProcessor
|
||||
|
||||
# Just VAD: no noise suppression or auto gain
|
||||
self._audio_processor = AudioProcessor(0, 0)
|
||||
|
||||
|
@ -10,6 +10,8 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from .const import DATA_ASUSWRT, DOMAIN
|
||||
from .router import AsusWrtDevInfo, AsusWrtRouter
|
||||
|
||||
ATTR_LAST_TIME_REACHABLE = "last_time_reachable"
|
||||
|
||||
DEFAULT_DEVICE_NAME = "Unknown device"
|
||||
|
||||
|
||||
@ -52,13 +54,14 @@ def add_entities(
|
||||
class AsusWrtDevice(ScannerEntity):
|
||||
"""Representation of a AsusWrt device."""
|
||||
|
||||
_unrecorded_attributes = frozenset({ATTR_LAST_TIME_REACHABLE})
|
||||
|
||||
_attr_should_poll = False
|
||||
|
||||
def __init__(self, router: AsusWrtRouter, device: AsusWrtDevInfo) -> None:
|
||||
"""Initialize a AsusWrt device."""
|
||||
self._router = router
|
||||
self._device = device
|
||||
self._attr_unique_id = device.mac
|
||||
self._attr_name = device.name or DEFAULT_DEVICE_NAME
|
||||
|
||||
@property
|
||||
@ -98,7 +101,7 @@ class AsusWrtDevice(ScannerEntity):
|
||||
self._attr_extra_state_attributes = {}
|
||||
if self._device.last_activity:
|
||||
self._attr_extra_state_attributes[
|
||||
"last_time_reachable"
|
||||
ATTR_LAST_TIME_REACHABLE
|
||||
] = self._device.last_activity.isoformat(timespec="seconds")
|
||||
self.async_write_ha_state()
|
||||
|
||||
|
@ -25,13 +25,14 @@ from homeassistant.exceptions import (
|
||||
ConfigEntryNotReady,
|
||||
HomeAssistantError,
|
||||
)
|
||||
from homeassistant.helpers import aiohttp_client, device_registry as dr, discovery_flow
|
||||
from homeassistant.helpers import device_registry as dr, discovery_flow
|
||||
|
||||
from .activity import ActivityStream
|
||||
from .const import CONF_BRAND, DOMAIN, MIN_TIME_BETWEEN_DETAIL_UPDATES, PLATFORMS
|
||||
from .exceptions import CannotConnect, InvalidAuth, RequireValidation
|
||||
from .gateway import AugustGateway
|
||||
from .subscriber import AugustSubscriberMixin
|
||||
from .util import async_create_august_clientsession
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -46,10 +47,7 @@ YALEXS_BLE_DOMAIN = "yalexs_ble"
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up August from a config entry."""
|
||||
# Create an aiohttp session instead of using the default one since the
|
||||
# default one is likely to trigger august's WAF if another integration
|
||||
# is also using Cloudflare
|
||||
session = aiohttp_client.async_create_clientsession(hass)
|
||||
session = async_create_august_clientsession(hass)
|
||||
august_gateway = AugustGateway(hass, session)
|
||||
|
||||
try:
|
||||
|
@ -13,7 +13,6 @@ from homeassistant import config_entries
|
||||
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
from homeassistant.helpers import aiohttp_client
|
||||
|
||||
from .const import (
|
||||
CONF_ACCESS_TOKEN_CACHE_FILE,
|
||||
@ -26,6 +25,7 @@ from .const import (
|
||||
)
|
||||
from .exceptions import CannotConnect, InvalidAuth, RequireValidation
|
||||
from .gateway import AugustGateway
|
||||
from .util import async_create_august_clientsession
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -159,10 +159,7 @@ class AugustConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
"""Set up the gateway."""
|
||||
if self._august_gateway is not None:
|
||||
return self._august_gateway
|
||||
# Create an aiohttp session instead of using the default one since the
|
||||
# default one is likely to trigger august's WAF if another integration
|
||||
# is also using Cloudflare
|
||||
self._aiohttp_session = aiohttp_client.async_create_clientsession(self.hass)
|
||||
self._aiohttp_session = async_create_august_clientsession(self.hass)
|
||||
self._august_gateway = AugustGateway(self.hass, self._aiohttp_session)
|
||||
return self._august_gateway
|
||||
|
||||
|
@ -28,5 +28,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/august",
|
||||
"iot_class": "cloud_push",
|
||||
"loggers": ["pubnub", "yalexs"],
|
||||
"requirements": ["yalexs==1.10.0", "yalexs-ble==2.3.0"]
|
||||
"requirements": ["yalexs==1.10.0", "yalexs-ble==2.3.1"]
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ from yalexs.keypad import KeypadDetail
|
||||
from yalexs.lock import Lock, LockDetail
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
RestoreSensor,
|
||||
SensorDeviceClass,
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
@ -27,7 +28,6 @@ from homeassistant.const import (
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.restore_state import RestoreEntity
|
||||
|
||||
from . import AugustData
|
||||
from .const import (
|
||||
@ -174,8 +174,7 @@ async def _async_migrate_old_unique_ids(hass, devices):
|
||||
registry.async_update_entity(old_entity_id, new_unique_id=device.unique_id)
|
||||
|
||||
|
||||
# pylint: disable-next=hass-invalid-inheritance # needs fixing
|
||||
class AugustOperatorSensor(AugustEntityMixin, RestoreEntity, SensorEntity):
|
||||
class AugustOperatorSensor(AugustEntityMixin, RestoreSensor):
|
||||
"""Representation of an August lock operation sensor."""
|
||||
|
||||
_attr_translation_key = "operator"
|
||||
@ -247,10 +246,15 @@ class AugustOperatorSensor(AugustEntityMixin, RestoreEntity, SensorEntity):
|
||||
await super().async_added_to_hass()
|
||||
|
||||
last_state = await self.async_get_last_state()
|
||||
if not last_state or last_state.state == STATE_UNAVAILABLE:
|
||||
last_sensor_state = await self.async_get_last_sensor_data()
|
||||
if (
|
||||
not last_state
|
||||
or not last_sensor_state
|
||||
or last_state.state == STATE_UNAVAILABLE
|
||||
):
|
||||
return
|
||||
|
||||
self._attr_native_value = last_state.state
|
||||
self._attr_native_value = last_sensor_state.native_value
|
||||
if ATTR_ENTITY_PICTURE in last_state.attributes:
|
||||
self._attr_entity_picture = last_state.attributes[ATTR_ENTITY_PICTURE]
|
||||
if ATTR_OPERATION_REMOTE in last_state.attributes:
|
||||
|
24
homeassistant/components/august/util.py
Normal file
24
homeassistant/components/august/util.py
Normal file
@ -0,0 +1,24 @@
|
||||
"""August util functions."""
|
||||
|
||||
import socket
|
||||
|
||||
import aiohttp
|
||||
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import aiohttp_client
|
||||
|
||||
|
||||
@callback
|
||||
def async_create_august_clientsession(hass: HomeAssistant) -> aiohttp.ClientSession:
|
||||
"""Create an aiohttp session for the august integration."""
|
||||
# Create an aiohttp session instead of using the default one since the
|
||||
# default one is likely to trigger august's WAF if another integration
|
||||
# is also using Cloudflare
|
||||
#
|
||||
# The family is set to AF_INET because IPv6 keeps coming up as an issue
|
||||
# see https://github.com/home-assistant/core/issues/97146
|
||||
#
|
||||
# When https://github.com/aio-libs/aiohttp/issues/4451 is implemented
|
||||
# we can allow IPv6 again
|
||||
#
|
||||
return aiohttp_client.async_create_clientsession(hass, family=socket.AF_INET)
|
@ -733,14 +733,14 @@ class AutomationEntity(BaseAutomationEntity, RestoreEntity):
|
||||
|
||||
self.async_write_ha_state()
|
||||
|
||||
def _log_callback(self, level: int, msg: str, **kwargs: Any) -> None:
|
||||
"""Log helper callback."""
|
||||
self._logger.log(level, "%s %s", msg, self.name, **kwargs)
|
||||
|
||||
async def _async_attach_triggers(
|
||||
self, home_assistant_start: bool
|
||||
) -> Callable[[], None] | None:
|
||||
"""Set up the triggers."""
|
||||
|
||||
def log_cb(level: int, msg: str, **kwargs: Any) -> None:
|
||||
self._logger.log(level, "%s %s", msg, self.name, **kwargs)
|
||||
|
||||
this = None
|
||||
self.async_write_ha_state()
|
||||
if state := self.hass.states.get(self.entity_id):
|
||||
@ -763,7 +763,7 @@ class AutomationEntity(BaseAutomationEntity, RestoreEntity):
|
||||
self.async_trigger,
|
||||
DOMAIN,
|
||||
str(self.name),
|
||||
log_cb,
|
||||
self._log_callback,
|
||||
home_assistant_start,
|
||||
variables,
|
||||
)
|
||||
|
@ -42,7 +42,8 @@ class AxisEntity(Entity):
|
||||
self.device = device
|
||||
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(AXIS_DOMAIN, device.unique_id)}
|
||||
identifiers={(AXIS_DOMAIN, device.unique_id)},
|
||||
serial_number=device.unique_id,
|
||||
)
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
|
@ -247,8 +247,8 @@
|
||||
"presence": {
|
||||
"name": "Presence",
|
||||
"state": {
|
||||
"off": "[%key:component::device_tracker::entity_component::_::state::not_home%]",
|
||||
"on": "[%key:component::device_tracker::entity_component::_::state::home%]"
|
||||
"off": "[%key:common::state::not_home%]",
|
||||
"on": "[%key:common::state::home%]"
|
||||
}
|
||||
},
|
||||
"problem": {
|
||||
|
@ -8,14 +8,20 @@ from blebox_uniapi.feature import Feature
|
||||
from blebox_uniapi.session import ApiHost
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_HOST, CONF_PORT, Platform
|
||||
from homeassistant.const import (
|
||||
CONF_HOST,
|
||||
CONF_PASSWORD,
|
||||
CONF_PORT,
|
||||
CONF_USERNAME,
|
||||
Platform,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
from .const import DEFAULT_SETUP_TIMEOUT, DOMAIN, PRODUCT
|
||||
from .helpers import get_maybe_authenticated_session
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -36,12 +42,16 @@ _FeatureT = TypeVar("_FeatureT", bound=Feature)
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up BleBox devices from a config entry."""
|
||||
websession = async_get_clientsession(hass)
|
||||
|
||||
host = entry.data[CONF_HOST]
|
||||
port = entry.data[CONF_PORT]
|
||||
|
||||
username = entry.data.get(CONF_USERNAME)
|
||||
password = entry.data.get(CONF_PASSWORD)
|
||||
|
||||
timeout = DEFAULT_SETUP_TIMEOUT
|
||||
|
||||
websession = get_maybe_authenticated_session(hass, password, username)
|
||||
|
||||
api_host = ApiHost(host, port, timeout, websession, hass.loop)
|
||||
|
||||
try:
|
||||
|
@ -5,16 +5,22 @@ import logging
|
||||
from typing import Any
|
||||
|
||||
from blebox_uniapi.box import Box
|
||||
from blebox_uniapi.error import Error, UnsupportedBoxResponse, UnsupportedBoxVersion
|
||||
from blebox_uniapi.error import (
|
||||
Error,
|
||||
UnauthorizedRequest,
|
||||
UnsupportedBoxResponse,
|
||||
UnsupportedBoxVersion,
|
||||
)
|
||||
from blebox_uniapi.session import ApiHost
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components import zeroconf
|
||||
from homeassistant.const import CONF_HOST, CONF_PORT
|
||||
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT, CONF_USERNAME
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
from . import get_maybe_authenticated_session
|
||||
from .const import (
|
||||
ADDRESS_ALREADY_CONFIGURED,
|
||||
CANNOT_CONNECT,
|
||||
@ -46,6 +52,8 @@ def create_schema(previous_input=None):
|
||||
{
|
||||
vol.Required(CONF_HOST, default=host): str,
|
||||
vol.Required(CONF_PORT, default=port): int,
|
||||
vol.Inclusive(CONF_USERNAME, "auth"): str,
|
||||
vol.Inclusive(CONF_PASSWORD, "auth"): str,
|
||||
}
|
||||
)
|
||||
|
||||
@ -153,6 +161,9 @@ class BleBoxConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
|
||||
addr = host_port(user_input)
|
||||
|
||||
username = user_input.get(CONF_USERNAME)
|
||||
password = user_input.get(CONF_PASSWORD)
|
||||
|
||||
for entry in self._async_current_entries():
|
||||
if addr == host_port(entry.data):
|
||||
host, port = addr
|
||||
@ -160,7 +171,9 @@ class BleBoxConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
reason=ADDRESS_ALREADY_CONFIGURED,
|
||||
description_placeholders={"address": f"{host}:{port}"},
|
||||
)
|
||||
websession = async_get_clientsession(hass)
|
||||
|
||||
websession = get_maybe_authenticated_session(hass, password, username)
|
||||
|
||||
api_host = ApiHost(*addr, DEFAULT_SETUP_TIMEOUT, websession, hass.loop, _LOGGER)
|
||||
try:
|
||||
product = await Box.async_from_host(api_host)
|
||||
@ -169,6 +182,10 @@ class BleBoxConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
return self.handle_step_exception(
|
||||
"user", ex, schema, *addr, UNSUPPORTED_VERSION, _LOGGER.debug
|
||||
)
|
||||
except UnauthorizedRequest as ex:
|
||||
return self.handle_step_exception(
|
||||
"user", ex, schema, *addr, CANNOT_CONNECT, _LOGGER.error
|
||||
)
|
||||
|
||||
except Error as ex:
|
||||
return self.handle_step_exception(
|
||||
|
21
homeassistant/components/blebox/helpers.py
Normal file
21
homeassistant/components/blebox/helpers.py
Normal file
@ -0,0 +1,21 @@
|
||||
"""Blebox helpers."""
|
||||
from __future__ import annotations
|
||||
|
||||
import aiohttp
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.aiohttp_client import (
|
||||
async_create_clientsession,
|
||||
async_get_clientsession,
|
||||
)
|
||||
|
||||
|
||||
def get_maybe_authenticated_session(
|
||||
hass: HomeAssistant, password: str | None, username: str | None
|
||||
) -> aiohttp.ClientSession:
|
||||
"""Return proper session object."""
|
||||
if username and password:
|
||||
auth = aiohttp.BasicAuth(login=username, password=password)
|
||||
return async_create_clientsession(hass, auth=auth)
|
||||
|
||||
return async_get_clientsession(hass)
|
@ -6,6 +6,6 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/blebox",
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["blebox_uniapi"],
|
||||
"requirements": ["blebox-uniapi==2.1.4"],
|
||||
"requirements": ["blebox-uniapi==2.2.0"],
|
||||
"zeroconf": ["_bbxsrv._tcp.local."]
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
"""Support for Blink Home Camera System."""
|
||||
import asyncio
|
||||
from copy import deepcopy
|
||||
import logging
|
||||
|
||||
from aiohttp import ClientError
|
||||
from blinkpy.auth import Auth
|
||||
from blinkpy.blinkpy import Blink
|
||||
import voluptuous as vol
|
||||
@ -16,8 +18,9 @@ from homeassistant.const import (
|
||||
CONF_SCAN_INTERVAL,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
from .const import (
|
||||
DEFAULT_SCAN_INTERVAL,
|
||||
@ -28,6 +31,7 @@ from .const import (
|
||||
SERVICE_SAVE_VIDEO,
|
||||
SERVICE_SEND_PIN,
|
||||
)
|
||||
from .coordinator import BlinkUpdateCoordinator
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -40,23 +44,7 @@ SERVICE_SAVE_RECENT_CLIPS_SCHEMA = vol.Schema(
|
||||
)
|
||||
|
||||
|
||||
def _blink_startup_wrapper(hass: HomeAssistant, entry: ConfigEntry) -> Blink:
|
||||
"""Startup wrapper for blink."""
|
||||
blink = Blink()
|
||||
auth_data = deepcopy(dict(entry.data))
|
||||
blink.auth = Auth(auth_data, no_prompt=True)
|
||||
blink.refresh_rate = entry.options.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)
|
||||
|
||||
if blink.start():
|
||||
blink.setup_post_verify()
|
||||
elif blink.auth.check_key_required():
|
||||
_LOGGER.debug("Attempting a reauth flow")
|
||||
_reauth_flow_wrapper(hass, auth_data)
|
||||
|
||||
return blink
|
||||
|
||||
|
||||
def _reauth_flow_wrapper(hass, data):
|
||||
async def _reauth_flow_wrapper(hass, data):
|
||||
"""Reauth flow wrapper."""
|
||||
hass.add_job(
|
||||
hass.config_entries.flow.async_init(
|
||||
@ -79,10 +67,10 @@ async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
data = {**entry.data}
|
||||
if entry.version == 1:
|
||||
data.pop("login_response", None)
|
||||
await hass.async_add_executor_job(_reauth_flow_wrapper, hass, data)
|
||||
await _reauth_flow_wrapper(hass, data)
|
||||
return False
|
||||
if entry.version == 2:
|
||||
await hass.async_add_executor_job(_reauth_flow_wrapper, hass, data)
|
||||
await _reauth_flow_wrapper(hass, data)
|
||||
return False
|
||||
return True
|
||||
|
||||
@ -92,19 +80,34 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
|
||||
_async_import_options_from_data_if_missing(hass, entry)
|
||||
hass.data[DOMAIN][entry.entry_id] = await hass.async_add_executor_job(
|
||||
_blink_startup_wrapper, hass, entry
|
||||
)
|
||||
session = async_get_clientsession(hass)
|
||||
blink = Blink(session=session)
|
||||
auth_data = deepcopy(dict(entry.data))
|
||||
blink.auth = Auth(auth_data, no_prompt=True, session=session)
|
||||
blink.refresh_rate = entry.options.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)
|
||||
coordinator = BlinkUpdateCoordinator(hass, blink)
|
||||
|
||||
if not hass.data[DOMAIN][entry.entry_id].available:
|
||||
try:
|
||||
await blink.start()
|
||||
except (ClientError, asyncio.TimeoutError) as ex:
|
||||
raise ConfigEntryNotReady("Can not connect to host") from ex
|
||||
|
||||
if blink.auth.check_key_required():
|
||||
_LOGGER.debug("Attempting a reauth flow")
|
||||
raise ConfigEntryAuthFailed("Need 2FA for Blink")
|
||||
|
||||
if not blink.available:
|
||||
raise ConfigEntryNotReady
|
||||
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
hass.data[DOMAIN][entry.entry_id] = coordinator
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
entry.async_on_unload(entry.add_update_listener(update_listener))
|
||||
|
||||
def blink_refresh(event_time=None):
|
||||
async def blink_refresh(event_time=None):
|
||||
"""Call blink to refresh info."""
|
||||
hass.data[DOMAIN][entry.entry_id].refresh(force_cache=True)
|
||||
await coordinator.api.refresh(force_cache=True)
|
||||
|
||||
async def async_save_video(call):
|
||||
"""Call save video service handler."""
|
||||
@ -114,11 +117,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Call save recent clips service handler."""
|
||||
await async_handle_save_recent_clips_service(hass, entry, call)
|
||||
|
||||
def send_pin(call):
|
||||
async def send_pin(call):
|
||||
"""Call blink to send new pin."""
|
||||
pin = call.data[CONF_PIN]
|
||||
hass.data[DOMAIN][entry.entry_id].auth.send_auth_key(
|
||||
hass.data[DOMAIN][entry.entry_id],
|
||||
await coordinator.api.auth.send_auth_key(
|
||||
hass.data[DOMAIN][entry.entry_id].api,
|
||||
pin,
|
||||
)
|
||||
|
||||
@ -153,64 +156,53 @@ def _async_import_options_from_data_if_missing(
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload Blink entry."""
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
if not unload_ok:
|
||||
return False
|
||||
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
if len(hass.data[DOMAIN]) != 0:
|
||||
if not hass.data[DOMAIN]:
|
||||
return True
|
||||
|
||||
hass.services.async_remove(DOMAIN, SERVICE_REFRESH)
|
||||
hass.services.async_remove(DOMAIN, SERVICE_SAVE_VIDEO)
|
||||
hass.services.async_remove(DOMAIN, SERVICE_SEND_PIN)
|
||||
|
||||
return True
|
||||
return unload_ok
|
||||
|
||||
|
||||
async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
"""Handle options update."""
|
||||
blink: Blink = hass.data[DOMAIN][entry.entry_id]
|
||||
blink: Blink = hass.data[DOMAIN][entry.entry_id].api
|
||||
blink.refresh_rate = entry.options[CONF_SCAN_INTERVAL]
|
||||
|
||||
|
||||
async def async_handle_save_video_service(hass, entry, call):
|
||||
async def async_handle_save_video_service(
|
||||
hass: HomeAssistant, entry: ConfigEntry, call
|
||||
) -> None:
|
||||
"""Handle save video service calls."""
|
||||
camera_name = call.data[CONF_NAME]
|
||||
video_path = call.data[CONF_FILENAME]
|
||||
if not hass.config.is_allowed_path(video_path):
|
||||
_LOGGER.error("Can't write %s, no access to path!", video_path)
|
||||
return
|
||||
|
||||
def _write_video(name, file_path):
|
||||
"""Call video write."""
|
||||
all_cameras = hass.data[DOMAIN][entry.entry_id].cameras
|
||||
if name in all_cameras:
|
||||
all_cameras[name].video_to_file(file_path)
|
||||
|
||||
all_cameras = hass.data[DOMAIN][entry.entry_id].api.cameras
|
||||
if camera_name in all_cameras:
|
||||
try:
|
||||
await hass.async_add_executor_job(_write_video, camera_name, video_path)
|
||||
await all_cameras[camera_name].video_to_file(video_path)
|
||||
except OSError as err:
|
||||
_LOGGER.error("Can't write image to file: %s", err)
|
||||
|
||||
|
||||
async def async_handle_save_recent_clips_service(hass, entry, call):
|
||||
async def async_handle_save_recent_clips_service(
|
||||
hass: HomeAssistant, entry: ConfigEntry, call
|
||||
) -> None:
|
||||
"""Save multiple recent clips to output directory."""
|
||||
camera_name = call.data[CONF_NAME]
|
||||
clips_dir = call.data[CONF_FILE_PATH]
|
||||
if not hass.config.is_allowed_path(clips_dir):
|
||||
_LOGGER.error("Can't write to directory %s, no access to path!", clips_dir)
|
||||
return
|
||||
|
||||
def _save_recent_clips(name, output_dir):
|
||||
"""Call save recent clips."""
|
||||
all_cameras = hass.data[DOMAIN][entry.entry_id].cameras
|
||||
if name in all_cameras:
|
||||
all_cameras[name].save_recent_clips(output_dir=output_dir)
|
||||
|
||||
all_cameras = hass.data[DOMAIN][entry.entry_id].api.cameras
|
||||
if camera_name in all_cameras:
|
||||
try:
|
||||
await hass.async_add_executor_job(_save_recent_clips, camera_name, clips_dir)
|
||||
await all_cameras[camera_name].save_recent_clips(output_dir=clips_dir)
|
||||
except OSError as err:
|
||||
_LOGGER.error("Can't write recent clips to directory: %s", err)
|
||||
|
@ -1,8 +1,11 @@
|
||||
"""Support for Blink Alarm Control Panel."""
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
from blinkpy.blinkpy import Blink, BlinkSyncModule
|
||||
|
||||
from homeassistant.components.alarm_control_panel import (
|
||||
AlarmControlPanelEntity,
|
||||
AlarmControlPanelEntityFeature,
|
||||
@ -13,11 +16,14 @@ from homeassistant.const import (
|
||||
STATE_ALARM_ARMED_AWAY,
|
||||
STATE_ALARM_DISARMED,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import DEFAULT_ATTRIBUTION, DEFAULT_BRAND, DOMAIN
|
||||
from .coordinator import BlinkUpdateCoordinator
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -28,59 +34,75 @@ async def async_setup_entry(
|
||||
hass: HomeAssistant, config: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
) -> None:
|
||||
"""Set up the Blink Alarm Control Panels."""
|
||||
data = hass.data[DOMAIN][config.entry_id]
|
||||
coordinator: BlinkUpdateCoordinator = hass.data[DOMAIN][config.entry_id]
|
||||
|
||||
sync_modules = []
|
||||
for sync_name, sync_module in data.sync.items():
|
||||
sync_modules.append(BlinkSyncModule(data, sync_name, sync_module))
|
||||
for sync_name, sync_module in coordinator.api.sync.items():
|
||||
sync_modules.append(BlinkSyncModuleHA(coordinator, sync_name, sync_module))
|
||||
async_add_entities(sync_modules)
|
||||
|
||||
|
||||
class BlinkSyncModule(AlarmControlPanelEntity):
|
||||
class BlinkSyncModuleHA(
|
||||
CoordinatorEntity[BlinkUpdateCoordinator], AlarmControlPanelEntity
|
||||
):
|
||||
"""Representation of a Blink Alarm Control Panel."""
|
||||
|
||||
_attr_icon = ICON
|
||||
_attr_supported_features = AlarmControlPanelEntityFeature.ARM_AWAY
|
||||
_attr_name = None
|
||||
_attr_has_entity_name = True
|
||||
_attr_name = None
|
||||
|
||||
def __init__(self, data, name, sync):
|
||||
def __init__(
|
||||
self, coordinator: BlinkUpdateCoordinator, name: str, sync: BlinkSyncModule
|
||||
) -> None:
|
||||
"""Initialize the alarm control panel."""
|
||||
self.data = data
|
||||
super().__init__(coordinator)
|
||||
self.api: Blink = coordinator.api
|
||||
self._coordinator = coordinator
|
||||
self.sync = sync
|
||||
self._name = name
|
||||
self._attr_unique_id = sync.serial
|
||||
self._attr_unique_id: str = sync.serial
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, sync.serial)},
|
||||
name=f"{DOMAIN} {name}",
|
||||
manufacturer=DEFAULT_BRAND,
|
||||
serial_number=sync.serial,
|
||||
)
|
||||
self._update_attr()
|
||||
|
||||
def update(self) -> None:
|
||||
"""Update the state of the device."""
|
||||
if self.data.check_if_ok_to_update():
|
||||
_LOGGER.debug(
|
||||
"Initiating a blink.refresh() from BlinkSyncModule('%s') (%s)",
|
||||
self._name,
|
||||
self.data,
|
||||
)
|
||||
self.data.refresh()
|
||||
_LOGGER.info("Updating State of Blink Alarm Control Panel '%s'", self._name)
|
||||
@callback
|
||||
def _handle_coordinator_update(self) -> None:
|
||||
"""Handle coordinator update."""
|
||||
self._update_attr()
|
||||
super()._handle_coordinator_update()
|
||||
|
||||
self._attr_state = (
|
||||
STATE_ALARM_ARMED_AWAY if self.sync.arm else STATE_ALARM_DISARMED
|
||||
)
|
||||
self.sync.attributes["network_info"] = self.data.networks
|
||||
@callback
|
||||
def _update_attr(self) -> None:
|
||||
"""Update attributes for alarm control panel."""
|
||||
self.sync.attributes["network_info"] = self.api.networks
|
||||
self.sync.attributes["associated_cameras"] = list(self.sync.cameras)
|
||||
self.sync.attributes[ATTR_ATTRIBUTION] = DEFAULT_ATTRIBUTION
|
||||
self._attr_extra_state_attributes = self.sync.attributes
|
||||
self._attr_state = (
|
||||
STATE_ALARM_ARMED_AWAY if self.sync.arm else STATE_ALARM_DISARMED
|
||||
)
|
||||
|
||||
def alarm_disarm(self, code: str | None = None) -> None:
|
||||
async def async_alarm_disarm(self, code: str | None = None) -> None:
|
||||
"""Send disarm command."""
|
||||
self.sync.arm = False
|
||||
self.sync.refresh()
|
||||
try:
|
||||
await self.sync.async_arm(False)
|
||||
|
||||
def alarm_arm_away(self, code: str | None = None) -> None:
|
||||
except asyncio.TimeoutError as er:
|
||||
raise HomeAssistantError("Blink failed to disarm camera") from er
|
||||
|
||||
await self._coordinator.async_refresh()
|
||||
|
||||
async def async_alarm_arm_away(self, code: str | None = None) -> None:
|
||||
"""Send arm command."""
|
||||
self.sync.arm = True
|
||||
self.sync.refresh()
|
||||
try:
|
||||
await self.sync.async_arm(True)
|
||||
|
||||
except asyncio.TimeoutError as er:
|
||||
raise HomeAssistantError("Blink failed to arm camera away") from er
|
||||
|
||||
await self._coordinator.async_refresh()
|
||||
self.async_write_ha_state()
|
||||
|
@ -10,9 +10,10 @@ from homeassistant.components.binary_sensor import (
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import EntityCategory
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import (
|
||||
DEFAULT_BRAND,
|
||||
@ -21,6 +22,7 @@ from .const import (
|
||||
TYPE_CAMERA_ARMED,
|
||||
TYPE_MOTION_DETECTED,
|
||||
)
|
||||
from .coordinator import BlinkUpdateCoordinator
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -45,45 +47,58 @@ async def async_setup_entry(
|
||||
hass: HomeAssistant, config: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
) -> None:
|
||||
"""Set up the blink binary sensors."""
|
||||
data = hass.data[DOMAIN][config.entry_id]
|
||||
coordinator: BlinkUpdateCoordinator = hass.data[DOMAIN][config.entry_id]
|
||||
|
||||
entities = [
|
||||
BlinkBinarySensor(data, camera, description)
|
||||
for camera in data.cameras
|
||||
BlinkBinarySensor(coordinator, camera, description)
|
||||
for camera in coordinator.api.cameras
|
||||
for description in BINARY_SENSORS_TYPES
|
||||
]
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
class BlinkBinarySensor(BinarySensorEntity):
|
||||
class BlinkBinarySensor(CoordinatorEntity[BlinkUpdateCoordinator], BinarySensorEntity):
|
||||
"""Representation of a Blink binary sensor."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(
|
||||
self, data, camera, description: BinarySensorEntityDescription
|
||||
self,
|
||||
coordinator: BlinkUpdateCoordinator,
|
||||
camera,
|
||||
description: BinarySensorEntityDescription,
|
||||
) -> None:
|
||||
"""Initialize the sensor."""
|
||||
self.data = data
|
||||
super().__init__(coordinator)
|
||||
self.entity_description = description
|
||||
self._camera = data.cameras[camera]
|
||||
self._attr_unique_id = f"{self._camera.serial}-{description.key}"
|
||||
self._camera = coordinator.api.cameras[camera]
|
||||
serial = self._camera.serial
|
||||
self._attr_unique_id = f"{serial}-{description.key}"
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, self._camera.serial)},
|
||||
identifiers={(DOMAIN, serial)},
|
||||
serial_number=serial,
|
||||
name=camera,
|
||||
manufacturer=DEFAULT_BRAND,
|
||||
model=self._camera.camera_type,
|
||||
)
|
||||
self._update_attrs()
|
||||
|
||||
def update(self) -> None:
|
||||
"""Update sensor state."""
|
||||
state = self._camera.attributes[self.entity_description.key]
|
||||
@callback
|
||||
def _handle_coordinator_update(self) -> None:
|
||||
"""Handle update from data coordinator."""
|
||||
self._update_attrs()
|
||||
super()._handle_coordinator_update()
|
||||
|
||||
@callback
|
||||
def _update_attrs(self) -> None:
|
||||
"""Update attributes for binary sensor."""
|
||||
is_on = self._camera.attributes[self.entity_description.key]
|
||||
_LOGGER.debug(
|
||||
"'%s' %s = %s",
|
||||
self._camera.attributes["name"],
|
||||
self.entity_description.key,
|
||||
state,
|
||||
is_on,
|
||||
)
|
||||
if self.entity_description.key == TYPE_BATTERY:
|
||||
state = state != "ok"
|
||||
self._attr_is_on = state
|
||||
is_on = is_on != "ok"
|
||||
self._attr_is_on = is_on
|
||||
|
@ -1,32 +1,41 @@
|
||||
"""Support for Blink system camera."""
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from collections.abc import Mapping
|
||||
import contextlib
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from requests.exceptions import ChunkedEncodingError
|
||||
|
||||
from homeassistant.components.camera import Camera
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import entity_platform
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import DEFAULT_BRAND, DOMAIN, SERVICE_TRIGGER
|
||||
from .coordinator import BlinkUpdateCoordinator
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
ATTR_VIDEO_CLIP = "video"
|
||||
ATTR_IMAGE = "image"
|
||||
PARALLEL_UPDATES = 1
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, config: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
) -> None:
|
||||
"""Set up a Blink Camera."""
|
||||
data = hass.data[DOMAIN][config.entry_id]
|
||||
coordinator: BlinkUpdateCoordinator = hass.data[DOMAIN][config.entry_id]
|
||||
entities = [
|
||||
BlinkCamera(data, name, camera) for name, camera in data.cameras.items()
|
||||
BlinkCamera(coordinator, name, camera)
|
||||
for name, camera in coordinator.api.cameras.items()
|
||||
]
|
||||
|
||||
async_add_entities(entities)
|
||||
@ -35,20 +44,22 @@ async def async_setup_entry(
|
||||
platform.async_register_entity_service(SERVICE_TRIGGER, {}, "trigger_camera")
|
||||
|
||||
|
||||
class BlinkCamera(Camera):
|
||||
class BlinkCamera(CoordinatorEntity[BlinkUpdateCoordinator], Camera):
|
||||
"""An implementation of a Blink Camera."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
_attr_name = None
|
||||
|
||||
def __init__(self, data, name, camera):
|
||||
def __init__(self, coordinator: BlinkUpdateCoordinator, name, camera) -> None:
|
||||
"""Initialize a camera."""
|
||||
super().__init__()
|
||||
self.data = data
|
||||
super().__init__(coordinator)
|
||||
Camera.__init__(self)
|
||||
self._coordinator = coordinator
|
||||
self._camera = camera
|
||||
self._attr_unique_id = f"{camera.serial}-camera"
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, camera.serial)},
|
||||
serial_number=camera.serial,
|
||||
name=name,
|
||||
manufacturer=DEFAULT_BRAND,
|
||||
model=camera.camera_type,
|
||||
@ -56,19 +67,30 @@ class BlinkCamera(Camera):
|
||||
_LOGGER.debug("Initialized blink camera %s", self.name)
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self):
|
||||
def extra_state_attributes(self) -> Mapping[str, Any] | None:
|
||||
"""Return the camera attributes."""
|
||||
return self._camera.attributes
|
||||
|
||||
def enable_motion_detection(self) -> None:
|
||||
async def async_enable_motion_detection(self) -> None:
|
||||
"""Enable motion detection for the camera."""
|
||||
self._camera.arm = True
|
||||
self.data.refresh()
|
||||
try:
|
||||
await self._camera.async_arm(True)
|
||||
|
||||
def disable_motion_detection(self) -> None:
|
||||
except asyncio.TimeoutError as er:
|
||||
raise HomeAssistantError("Blink failed to arm camera") from er
|
||||
|
||||
self._camera.motion_enabled = True
|
||||
await self._coordinator.async_refresh()
|
||||
|
||||
async def async_disable_motion_detection(self) -> None:
|
||||
"""Disable motion detection for the camera."""
|
||||
self._camera.arm = False
|
||||
self.data.refresh()
|
||||
try:
|
||||
await self._camera.async_arm(False)
|
||||
except asyncio.TimeoutError as er:
|
||||
raise HomeAssistantError("Blink failed to disarm camera") from er
|
||||
|
||||
self._camera.motion_enabled = False
|
||||
await self._coordinator.async_refresh()
|
||||
|
||||
@property
|
||||
def motion_detection_enabled(self) -> bool:
|
||||
@ -76,21 +98,23 @@ class BlinkCamera(Camera):
|
||||
return self._camera.arm
|
||||
|
||||
@property
|
||||
def brand(self):
|
||||
def brand(self) -> str | None:
|
||||
"""Return the camera brand."""
|
||||
return DEFAULT_BRAND
|
||||
|
||||
def trigger_camera(self):
|
||||
async def trigger_camera(self) -> None:
|
||||
"""Trigger camera to take a snapshot."""
|
||||
self._camera.snap_picture()
|
||||
self.data.refresh()
|
||||
with contextlib.suppress(asyncio.TimeoutError):
|
||||
await self._camera.snap_picture()
|
||||
await self._coordinator.api.refresh()
|
||||
self.async_write_ha_state()
|
||||
|
||||
def camera_image(
|
||||
self, width: int | None = None, height: int | None = None
|
||||
) -> bytes | None:
|
||||
"""Return a still image response from the camera."""
|
||||
try:
|
||||
return self._camera.image_from_cache.content
|
||||
return self._camera.image_from_cache
|
||||
except ChunkedEncodingError:
|
||||
_LOGGER.debug("Could not retrieve image for %s", self._camera.name)
|
||||
return None
|
||||
|
@ -16,10 +16,11 @@ from homeassistant.const import (
|
||||
CONF_SCAN_INTERVAL,
|
||||
CONF_USERNAME,
|
||||
)
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import selector
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.schema_config_entry_flow import (
|
||||
SchemaFlowFormStep,
|
||||
SchemaOptionsFlowHandler,
|
||||
@ -49,23 +50,23 @@ OPTIONS_FLOW = {
|
||||
}
|
||||
|
||||
|
||||
def validate_input(auth: Auth) -> None:
|
||||
async def validate_input(auth: Auth) -> None:
|
||||
"""Validate the user input allows us to connect."""
|
||||
try:
|
||||
auth.startup()
|
||||
await auth.startup()
|
||||
except (LoginError, TokenRefreshFailed) as err:
|
||||
raise InvalidAuth from err
|
||||
if auth.check_key_required():
|
||||
raise Require2FA
|
||||
|
||||
|
||||
def _send_blink_2fa_pin(auth: Auth, pin: str | None) -> bool:
|
||||
async def _send_blink_2fa_pin(hass: HomeAssistant, auth: Auth, pin: str | None) -> bool:
|
||||
"""Send 2FA pin to blink servers."""
|
||||
blink = Blink()
|
||||
blink = Blink(session=async_get_clientsession(hass))
|
||||
blink.auth = auth
|
||||
blink.setup_login_ids()
|
||||
blink.setup_urls()
|
||||
return auth.send_auth_key(blink, pin)
|
||||
return await auth.send_auth_key(blink, pin)
|
||||
|
||||
|
||||
class BlinkConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
@ -91,11 +92,15 @@ class BlinkConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
"""Handle a flow initiated by the user."""
|
||||
errors = {}
|
||||
if user_input is not None:
|
||||
self.auth = Auth({**user_input, "device_id": DEVICE_ID}, no_prompt=True)
|
||||
self.auth = Auth(
|
||||
{**user_input, "device_id": DEVICE_ID},
|
||||
no_prompt=True,
|
||||
session=async_get_clientsession(self.hass),
|
||||
)
|
||||
await self.async_set_unique_id(user_input[CONF_USERNAME])
|
||||
|
||||
try:
|
||||
await self.hass.async_add_executor_job(validate_input, self.auth)
|
||||
await validate_input(self.auth)
|
||||
return self._async_finish_flow()
|
||||
except Require2FA:
|
||||
return await self.async_step_2fa()
|
||||
@ -122,11 +127,9 @@ class BlinkConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
"""Handle 2FA step."""
|
||||
errors = {}
|
||||
if user_input is not None:
|
||||
pin: str | None = user_input.get(CONF_PIN)
|
||||
try:
|
||||
assert self.auth
|
||||
valid_token = await self.hass.async_add_executor_job(
|
||||
_send_blink_2fa_pin, self.auth, pin
|
||||
valid_token = await _send_blink_2fa_pin(
|
||||
self.hass, self.auth, user_input.get(CONF_PIN)
|
||||
)
|
||||
except BlinkSetupError:
|
||||
errors["base"] = "cannot_connect"
|
||||
|
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