mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 21:27:38 +00:00
2025.2.0 (#137448)
This commit is contained in:
commit
5c383f3d88
@ -62,7 +62,7 @@
|
|||||||
"json.schemas": [
|
"json.schemas": [
|
||||||
{
|
{
|
||||||
"fileMatch": ["homeassistant/components/*/manifest.json"],
|
"fileMatch": ["homeassistant/components/*/manifest.json"],
|
||||||
"url": "./script/json_schemas/manifest_schema.json"
|
"url": "${containerWorkspaceFolder}/script/json_schemas/manifest_schema.json"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
11
.gitattributes
vendored
11
.gitattributes
vendored
@ -11,3 +11,14 @@
|
|||||||
*.pcm binary
|
*.pcm binary
|
||||||
|
|
||||||
Dockerfile.dev linguist-language=Dockerfile
|
Dockerfile.dev linguist-language=Dockerfile
|
||||||
|
|
||||||
|
# Generated files
|
||||||
|
CODEOWNERS linguist-generated=true
|
||||||
|
Dockerfile linguist-generated=true
|
||||||
|
homeassistant/generated/*.py linguist-generated=true
|
||||||
|
mypy.ini linguist-generated=true
|
||||||
|
requirements.txt linguist-generated=true
|
||||||
|
requirements_all.txt linguist-generated=true
|
||||||
|
requirements_test_all.txt linguist-generated=true
|
||||||
|
requirements_test_pre_commit.txt linguist-generated=true
|
||||||
|
script/hassfest/docker/Dockerfile linguist-generated=true
|
||||||
|
18
.github/workflows/builder.yml
vendored
18
.github/workflows/builder.yml
vendored
@ -32,7 +32,7 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||||
uses: actions/setup-python@v5.3.0
|
uses: actions/setup-python@v5.4.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||||
|
|
||||||
@ -69,7 +69,7 @@ jobs:
|
|||||||
run: find ./homeassistant/components/*/translations -name "*.json" | tar zcvf translations.tar.gz -T -
|
run: find ./homeassistant/components/*/translations -name "*.json" | tar zcvf translations.tar.gz -T -
|
||||||
|
|
||||||
- name: Upload translations
|
- name: Upload translations
|
||||||
uses: actions/upload-artifact@v4.5.0
|
uses: actions/upload-artifact@v4.6.0
|
||||||
with:
|
with:
|
||||||
name: translations
|
name: translations
|
||||||
path: translations.tar.gz
|
path: translations.tar.gz
|
||||||
@ -94,7 +94,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Download nightly wheels of frontend
|
- name: Download nightly wheels of frontend
|
||||||
if: needs.init.outputs.channel == 'dev'
|
if: needs.init.outputs.channel == 'dev'
|
||||||
uses: dawidd6/action-download-artifact@v7
|
uses: dawidd6/action-download-artifact@v8
|
||||||
with:
|
with:
|
||||||
github_token: ${{secrets.GITHUB_TOKEN}}
|
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||||
repo: home-assistant/frontend
|
repo: home-assistant/frontend
|
||||||
@ -105,7 +105,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Download nightly wheels of intents
|
- name: Download nightly wheels of intents
|
||||||
if: needs.init.outputs.channel == 'dev'
|
if: needs.init.outputs.channel == 'dev'
|
||||||
uses: dawidd6/action-download-artifact@v7
|
uses: dawidd6/action-download-artifact@v8
|
||||||
with:
|
with:
|
||||||
github_token: ${{secrets.GITHUB_TOKEN}}
|
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||||
repo: home-assistant/intents-package
|
repo: home-assistant/intents-package
|
||||||
@ -116,7 +116,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||||
if: needs.init.outputs.channel == 'dev'
|
if: needs.init.outputs.channel == 'dev'
|
||||||
uses: actions/setup-python@v5.3.0
|
uses: actions/setup-python@v5.4.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||||
|
|
||||||
@ -454,7 +454,7 @@ jobs:
|
|||||||
uses: actions/checkout@v4.2.2
|
uses: actions/checkout@v4.2.2
|
||||||
|
|
||||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||||
uses: actions/setup-python@v5.3.0
|
uses: actions/setup-python@v5.4.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||||
|
|
||||||
@ -509,7 +509,7 @@ jobs:
|
|||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Build Docker image
|
- name: Build Docker image
|
||||||
uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 # v6.10.0
|
uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # v6.13.0
|
||||||
with:
|
with:
|
||||||
context: . # So action will not pull the repository again
|
context: . # So action will not pull the repository again
|
||||||
file: ./script/hassfest/docker/Dockerfile
|
file: ./script/hassfest/docker/Dockerfile
|
||||||
@ -522,7 +522,7 @@ jobs:
|
|||||||
- name: Push Docker image
|
- name: Push Docker image
|
||||||
if: needs.init.outputs.channel != 'dev' && needs.init.outputs.publish == 'true'
|
if: needs.init.outputs.channel != 'dev' && needs.init.outputs.publish == 'true'
|
||||||
id: push
|
id: push
|
||||||
uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 # v6.10.0
|
uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # v6.13.0
|
||||||
with:
|
with:
|
||||||
context: . # So action will not pull the repository again
|
context: . # So action will not pull the repository again
|
||||||
file: ./script/hassfest/docker/Dockerfile
|
file: ./script/hassfest/docker/Dockerfile
|
||||||
@ -531,7 +531,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Generate artifact attestation
|
- name: Generate artifact attestation
|
||||||
if: needs.init.outputs.channel != 'dev' && needs.init.outputs.publish == 'true'
|
if: needs.init.outputs.channel != 'dev' && needs.init.outputs.publish == 'true'
|
||||||
uses: actions/attest-build-provenance@7668571508540a607bdfd90a87a560489fe372eb # v2.1.0
|
uses: actions/attest-build-provenance@520d128f165991a6c774bcb264f323e3d70747f4 # v2.2.0
|
||||||
with:
|
with:
|
||||||
subject-name: ${{ env.HASSFEST_IMAGE_NAME }}
|
subject-name: ${{ env.HASSFEST_IMAGE_NAME }}
|
||||||
subject-digest: ${{ steps.push.outputs.digest }}
|
subject-digest: ${{ steps.push.outputs.digest }}
|
||||||
|
64
.github/workflows/ci.yaml
vendored
64
.github/workflows/ci.yaml
vendored
@ -40,9 +40,9 @@ env:
|
|||||||
CACHE_VERSION: 11
|
CACHE_VERSION: 11
|
||||||
UV_CACHE_VERSION: 1
|
UV_CACHE_VERSION: 1
|
||||||
MYPY_CACHE_VERSION: 9
|
MYPY_CACHE_VERSION: 9
|
||||||
HA_SHORT_VERSION: "2025.1"
|
HA_SHORT_VERSION: "2025.2"
|
||||||
DEFAULT_PYTHON: "3.12"
|
DEFAULT_PYTHON: "3.13"
|
||||||
ALL_PYTHON_VERSIONS: "['3.12', '3.13']"
|
ALL_PYTHON_VERSIONS: "['3.13']"
|
||||||
# 10.3 is the oldest supported version
|
# 10.3 is the oldest supported version
|
||||||
# - 10.3.32 is the version currently shipped with Synology (as of 17 Feb 2022)
|
# - 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 is the current long-term-support
|
||||||
@ -234,7 +234,7 @@ jobs:
|
|||||||
uses: actions/checkout@v4.2.2
|
uses: actions/checkout@v4.2.2
|
||||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||||
id: python
|
id: python
|
||||||
uses: actions/setup-python@v5.3.0
|
uses: actions/setup-python@v5.4.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@ -279,7 +279,7 @@ jobs:
|
|||||||
- name: Check out code from GitHub
|
- name: Check out code from GitHub
|
||||||
uses: actions/checkout@v4.2.2
|
uses: actions/checkout@v4.2.2
|
||||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||||
uses: actions/setup-python@v5.3.0
|
uses: actions/setup-python@v5.4.0
|
||||||
id: python
|
id: python
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||||
@ -319,7 +319,7 @@ jobs:
|
|||||||
- name: Check out code from GitHub
|
- name: Check out code from GitHub
|
||||||
uses: actions/checkout@v4.2.2
|
uses: actions/checkout@v4.2.2
|
||||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||||
uses: actions/setup-python@v5.3.0
|
uses: actions/setup-python@v5.4.0
|
||||||
id: python
|
id: python
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||||
@ -359,7 +359,7 @@ jobs:
|
|||||||
- name: Check out code from GitHub
|
- name: Check out code from GitHub
|
||||||
uses: actions/checkout@v4.2.2
|
uses: actions/checkout@v4.2.2
|
||||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||||
uses: actions/setup-python@v5.3.0
|
uses: actions/setup-python@v5.4.0
|
||||||
id: python
|
id: python
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||||
@ -469,7 +469,7 @@ jobs:
|
|||||||
uses: actions/checkout@v4.2.2
|
uses: actions/checkout@v4.2.2
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
id: python
|
id: python
|
||||||
uses: actions/setup-python@v5.3.0
|
uses: actions/setup-python@v5.4.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@ -537,7 +537,7 @@ jobs:
|
|||||||
python --version
|
python --version
|
||||||
uv pip freeze >> pip_freeze.txt
|
uv pip freeze >> pip_freeze.txt
|
||||||
- name: Upload pip_freeze artifact
|
- name: Upload pip_freeze artifact
|
||||||
uses: actions/upload-artifact@v4.5.0
|
uses: actions/upload-artifact@v4.6.0
|
||||||
with:
|
with:
|
||||||
name: pip-freeze-${{ matrix.python-version }}
|
name: pip-freeze-${{ matrix.python-version }}
|
||||||
path: pip_freeze.txt
|
path: pip_freeze.txt
|
||||||
@ -572,7 +572,7 @@ jobs:
|
|||||||
uses: actions/checkout@v4.2.2
|
uses: actions/checkout@v4.2.2
|
||||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||||
id: python
|
id: python
|
||||||
uses: actions/setup-python@v5.3.0
|
uses: actions/setup-python@v5.4.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@ -605,7 +605,7 @@ jobs:
|
|||||||
uses: actions/checkout@v4.2.2
|
uses: actions/checkout@v4.2.2
|
||||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||||
id: python
|
id: python
|
||||||
uses: actions/setup-python@v5.3.0
|
uses: actions/setup-python@v5.4.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@ -643,7 +643,7 @@ jobs:
|
|||||||
uses: actions/checkout@v4.2.2
|
uses: actions/checkout@v4.2.2
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
id: python
|
id: python
|
||||||
uses: actions/setup-python@v5.3.0
|
uses: actions/setup-python@v5.4.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@ -661,7 +661,7 @@ jobs:
|
|||||||
. venv/bin/activate
|
. venv/bin/activate
|
||||||
python -m script.licenses extract --output-file=licenses-${{ matrix.python-version }}.json
|
python -m script.licenses extract --output-file=licenses-${{ matrix.python-version }}.json
|
||||||
- name: Upload licenses
|
- name: Upload licenses
|
||||||
uses: actions/upload-artifact@v4.5.0
|
uses: actions/upload-artifact@v4.6.0
|
||||||
with:
|
with:
|
||||||
name: licenses-${{ github.run_number }}-${{ matrix.python-version }}
|
name: licenses-${{ github.run_number }}-${{ matrix.python-version }}
|
||||||
path: licenses-${{ matrix.python-version }}.json
|
path: licenses-${{ matrix.python-version }}.json
|
||||||
@ -686,7 +686,7 @@ jobs:
|
|||||||
uses: actions/checkout@v4.2.2
|
uses: actions/checkout@v4.2.2
|
||||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||||
id: python
|
id: python
|
||||||
uses: actions/setup-python@v5.3.0
|
uses: actions/setup-python@v5.4.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@ -733,7 +733,7 @@ jobs:
|
|||||||
uses: actions/checkout@v4.2.2
|
uses: actions/checkout@v4.2.2
|
||||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||||
id: python
|
id: python
|
||||||
uses: actions/setup-python@v5.3.0
|
uses: actions/setup-python@v5.4.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@ -778,7 +778,7 @@ jobs:
|
|||||||
uses: actions/checkout@v4.2.2
|
uses: actions/checkout@v4.2.2
|
||||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||||
id: python
|
id: python
|
||||||
uses: actions/setup-python@v5.3.0
|
uses: actions/setup-python@v5.4.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@ -859,7 +859,7 @@ jobs:
|
|||||||
uses: actions/checkout@v4.2.2
|
uses: actions/checkout@v4.2.2
|
||||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||||
id: python
|
id: python
|
||||||
uses: actions/setup-python@v5.3.0
|
uses: actions/setup-python@v5.4.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@ -877,7 +877,7 @@ jobs:
|
|||||||
. venv/bin/activate
|
. venv/bin/activate
|
||||||
python -m script.split_tests ${{ needs.info.outputs.test_group_count }} tests
|
python -m script.split_tests ${{ needs.info.outputs.test_group_count }} tests
|
||||||
- name: Upload pytest_buckets
|
- name: Upload pytest_buckets
|
||||||
uses: actions/upload-artifact@v4.5.0
|
uses: actions/upload-artifact@v4.6.0
|
||||||
with:
|
with:
|
||||||
name: pytest_buckets
|
name: pytest_buckets
|
||||||
path: pytest_buckets.txt
|
path: pytest_buckets.txt
|
||||||
@ -923,7 +923,7 @@ jobs:
|
|||||||
uses: actions/checkout@v4.2.2
|
uses: actions/checkout@v4.2.2
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
id: python
|
id: python
|
||||||
uses: actions/setup-python@v5.3.0
|
uses: actions/setup-python@v5.4.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@ -979,14 +979,14 @@ jobs:
|
|||||||
2>&1 | tee pytest-${{ matrix.python-version }}-${{ matrix.group }}.txt
|
2>&1 | tee pytest-${{ matrix.python-version }}-${{ matrix.group }}.txt
|
||||||
- name: Upload pytest output
|
- name: Upload pytest output
|
||||||
if: success() || failure() && steps.pytest-full.conclusion == 'failure'
|
if: success() || failure() && steps.pytest-full.conclusion == 'failure'
|
||||||
uses: actions/upload-artifact@v4.5.0
|
uses: actions/upload-artifact@v4.6.0
|
||||||
with:
|
with:
|
||||||
name: pytest-${{ github.run_number }}-${{ matrix.python-version }}-${{ matrix.group }}
|
name: pytest-${{ github.run_number }}-${{ matrix.python-version }}-${{ matrix.group }}
|
||||||
path: pytest-*.txt
|
path: pytest-*.txt
|
||||||
overwrite: true
|
overwrite: true
|
||||||
- name: Upload coverage artifact
|
- name: Upload coverage artifact
|
||||||
if: needs.info.outputs.skip_coverage != 'true'
|
if: needs.info.outputs.skip_coverage != 'true'
|
||||||
uses: actions/upload-artifact@v4.5.0
|
uses: actions/upload-artifact@v4.6.0
|
||||||
with:
|
with:
|
||||||
name: coverage-${{ matrix.python-version }}-${{ matrix.group }}
|
name: coverage-${{ matrix.python-version }}-${{ matrix.group }}
|
||||||
path: coverage.xml
|
path: coverage.xml
|
||||||
@ -1044,7 +1044,7 @@ jobs:
|
|||||||
uses: actions/checkout@v4.2.2
|
uses: actions/checkout@v4.2.2
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
id: python
|
id: python
|
||||||
uses: actions/setup-python@v5.3.0
|
uses: actions/setup-python@v5.4.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@ -1106,7 +1106,7 @@ jobs:
|
|||||||
2>&1 | tee pytest-${{ matrix.python-version }}-${mariadb}.txt
|
2>&1 | tee pytest-${{ matrix.python-version }}-${mariadb}.txt
|
||||||
- name: Upload pytest output
|
- name: Upload pytest output
|
||||||
if: success() || failure() && steps.pytest-partial.conclusion == 'failure'
|
if: success() || failure() && steps.pytest-partial.conclusion == 'failure'
|
||||||
uses: actions/upload-artifact@v4.5.0
|
uses: actions/upload-artifact@v4.6.0
|
||||||
with:
|
with:
|
||||||
name: pytest-${{ github.run_number }}-${{ matrix.python-version }}-${{
|
name: pytest-${{ github.run_number }}-${{ matrix.python-version }}-${{
|
||||||
steps.pytest-partial.outputs.mariadb }}
|
steps.pytest-partial.outputs.mariadb }}
|
||||||
@ -1114,7 +1114,7 @@ jobs:
|
|||||||
overwrite: true
|
overwrite: true
|
||||||
- name: Upload coverage artifact
|
- name: Upload coverage artifact
|
||||||
if: needs.info.outputs.skip_coverage != 'true'
|
if: needs.info.outputs.skip_coverage != 'true'
|
||||||
uses: actions/upload-artifact@v4.5.0
|
uses: actions/upload-artifact@v4.6.0
|
||||||
with:
|
with:
|
||||||
name: coverage-${{ matrix.python-version }}-${{
|
name: coverage-${{ matrix.python-version }}-${{
|
||||||
steps.pytest-partial.outputs.mariadb }}
|
steps.pytest-partial.outputs.mariadb }}
|
||||||
@ -1173,7 +1173,7 @@ jobs:
|
|||||||
uses: actions/checkout@v4.2.2
|
uses: actions/checkout@v4.2.2
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
id: python
|
id: python
|
||||||
uses: actions/setup-python@v5.3.0
|
uses: actions/setup-python@v5.4.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@ -1236,7 +1236,7 @@ jobs:
|
|||||||
2>&1 | tee pytest-${{ matrix.python-version }}-${postgresql}.txt
|
2>&1 | tee pytest-${{ matrix.python-version }}-${postgresql}.txt
|
||||||
- name: Upload pytest output
|
- name: Upload pytest output
|
||||||
if: success() || failure() && steps.pytest-partial.conclusion == 'failure'
|
if: success() || failure() && steps.pytest-partial.conclusion == 'failure'
|
||||||
uses: actions/upload-artifact@v4.5.0
|
uses: actions/upload-artifact@v4.6.0
|
||||||
with:
|
with:
|
||||||
name: pytest-${{ github.run_number }}-${{ matrix.python-version }}-${{
|
name: pytest-${{ github.run_number }}-${{ matrix.python-version }}-${{
|
||||||
steps.pytest-partial.outputs.postgresql }}
|
steps.pytest-partial.outputs.postgresql }}
|
||||||
@ -1244,7 +1244,7 @@ jobs:
|
|||||||
overwrite: true
|
overwrite: true
|
||||||
- name: Upload coverage artifact
|
- name: Upload coverage artifact
|
||||||
if: needs.info.outputs.skip_coverage != 'true'
|
if: needs.info.outputs.skip_coverage != 'true'
|
||||||
uses: actions/upload-artifact@v4.5.0
|
uses: actions/upload-artifact@v4.6.0
|
||||||
with:
|
with:
|
||||||
name: coverage-${{ matrix.python-version }}-${{
|
name: coverage-${{ matrix.python-version }}-${{
|
||||||
steps.pytest-partial.outputs.postgresql }}
|
steps.pytest-partial.outputs.postgresql }}
|
||||||
@ -1273,7 +1273,7 @@ jobs:
|
|||||||
pattern: coverage-*
|
pattern: coverage-*
|
||||||
- name: Upload coverage to Codecov
|
- name: Upload coverage to Codecov
|
||||||
if: needs.info.outputs.test_full_suite == 'true'
|
if: needs.info.outputs.test_full_suite == 'true'
|
||||||
uses: codecov/codecov-action@v5.1.2
|
uses: codecov/codecov-action@v5.3.1
|
||||||
with:
|
with:
|
||||||
fail_ci_if_error: true
|
fail_ci_if_error: true
|
||||||
flags: full-suite
|
flags: full-suite
|
||||||
@ -1319,7 +1319,7 @@ jobs:
|
|||||||
uses: actions/checkout@v4.2.2
|
uses: actions/checkout@v4.2.2
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
id: python
|
id: python
|
||||||
uses: actions/setup-python@v5.3.0
|
uses: actions/setup-python@v5.4.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@ -1378,14 +1378,14 @@ jobs:
|
|||||||
2>&1 | tee pytest-${{ matrix.python-version }}-${{ matrix.group }}.txt
|
2>&1 | tee pytest-${{ matrix.python-version }}-${{ matrix.group }}.txt
|
||||||
- name: Upload pytest output
|
- name: Upload pytest output
|
||||||
if: success() || failure() && steps.pytest-partial.conclusion == 'failure'
|
if: success() || failure() && steps.pytest-partial.conclusion == 'failure'
|
||||||
uses: actions/upload-artifact@v4.5.0
|
uses: actions/upload-artifact@v4.6.0
|
||||||
with:
|
with:
|
||||||
name: pytest-${{ github.run_number }}-${{ matrix.python-version }}-${{ matrix.group }}
|
name: pytest-${{ github.run_number }}-${{ matrix.python-version }}-${{ matrix.group }}
|
||||||
path: pytest-*.txt
|
path: pytest-*.txt
|
||||||
overwrite: true
|
overwrite: true
|
||||||
- name: Upload coverage artifact
|
- name: Upload coverage artifact
|
||||||
if: needs.info.outputs.skip_coverage != 'true'
|
if: needs.info.outputs.skip_coverage != 'true'
|
||||||
uses: actions/upload-artifact@v4.5.0
|
uses: actions/upload-artifact@v4.6.0
|
||||||
with:
|
with:
|
||||||
name: coverage-${{ matrix.python-version }}-${{ matrix.group }}
|
name: coverage-${{ matrix.python-version }}-${{ matrix.group }}
|
||||||
path: coverage.xml
|
path: coverage.xml
|
||||||
@ -1411,7 +1411,7 @@ jobs:
|
|||||||
pattern: coverage-*
|
pattern: coverage-*
|
||||||
- name: Upload coverage to Codecov
|
- name: Upload coverage to Codecov
|
||||||
if: needs.info.outputs.test_full_suite == 'false'
|
if: needs.info.outputs.test_full_suite == 'false'
|
||||||
uses: codecov/codecov-action@v5.1.2
|
uses: codecov/codecov-action@v5.3.1
|
||||||
with:
|
with:
|
||||||
fail_ci_if_error: true
|
fail_ci_if_error: true
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
4
.github/workflows/codeql.yml
vendored
4
.github/workflows/codeql.yml
vendored
@ -24,11 +24,11 @@ jobs:
|
|||||||
uses: actions/checkout@v4.2.2
|
uses: actions/checkout@v4.2.2
|
||||||
|
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v3.28.0
|
uses: github/codeql-action/init@v3.28.6
|
||||||
with:
|
with:
|
||||||
languages: python
|
languages: python
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v3.28.0
|
uses: github/codeql-action/analyze@v3.28.6
|
||||||
with:
|
with:
|
||||||
category: "/language:python"
|
category: "/language:python"
|
||||||
|
6
.github/workflows/stale.yml
vendored
6
.github/workflows/stale.yml
vendored
@ -17,7 +17,7 @@ jobs:
|
|||||||
# - No PRs marked as no-stale
|
# - No PRs marked as no-stale
|
||||||
# - No issues (-1)
|
# - No issues (-1)
|
||||||
- name: 60 days stale PRs policy
|
- name: 60 days stale PRs policy
|
||||||
uses: actions/stale@v9.0.0
|
uses: actions/stale@v9.1.0
|
||||||
with:
|
with:
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
days-before-stale: 60
|
days-before-stale: 60
|
||||||
@ -57,7 +57,7 @@ jobs:
|
|||||||
# - No issues marked as no-stale or help-wanted
|
# - No issues marked as no-stale or help-wanted
|
||||||
# - No PRs (-1)
|
# - No PRs (-1)
|
||||||
- name: 90 days stale issues
|
- name: 90 days stale issues
|
||||||
uses: actions/stale@v9.0.0
|
uses: actions/stale@v9.1.0
|
||||||
with:
|
with:
|
||||||
repo-token: ${{ steps.token.outputs.token }}
|
repo-token: ${{ steps.token.outputs.token }}
|
||||||
days-before-stale: 90
|
days-before-stale: 90
|
||||||
@ -87,7 +87,7 @@ jobs:
|
|||||||
# - No Issues marked as no-stale or help-wanted
|
# - No Issues marked as no-stale or help-wanted
|
||||||
# - No PRs (-1)
|
# - No PRs (-1)
|
||||||
- name: Needs more information stale issues policy
|
- name: Needs more information stale issues policy
|
||||||
uses: actions/stale@v9.0.0
|
uses: actions/stale@v9.1.0
|
||||||
with:
|
with:
|
||||||
repo-token: ${{ steps.token.outputs.token }}
|
repo-token: ${{ steps.token.outputs.token }}
|
||||||
only-labels: "needs-more-information"
|
only-labels: "needs-more-information"
|
||||||
|
4
.github/workflows/translations.yml
vendored
4
.github/workflows/translations.yml
vendored
@ -10,7 +10,7 @@ on:
|
|||||||
- "**strings.json"
|
- "**strings.json"
|
||||||
|
|
||||||
env:
|
env:
|
||||||
DEFAULT_PYTHON: "3.12"
|
DEFAULT_PYTHON: "3.13"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
upload:
|
upload:
|
||||||
@ -22,7 +22,7 @@ jobs:
|
|||||||
uses: actions/checkout@v4.2.2
|
uses: actions/checkout@v4.2.2
|
||||||
|
|
||||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||||
uses: actions/setup-python@v5.3.0
|
uses: actions/setup-python@v5.4.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||||
|
|
||||||
|
51
.github/workflows/wheels.yml
vendored
51
.github/workflows/wheels.yml
vendored
@ -17,7 +17,7 @@ on:
|
|||||||
- "script/gen_requirements_all.py"
|
- "script/gen_requirements_all.py"
|
||||||
|
|
||||||
env:
|
env:
|
||||||
DEFAULT_PYTHON: "3.12"
|
DEFAULT_PYTHON: "3.13"
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.ref_name}}
|
group: ${{ github.workflow }}-${{ github.ref_name}}
|
||||||
@ -36,7 +36,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||||
id: python
|
id: python
|
||||||
uses: actions/setup-python@v5.3.0
|
uses: actions/setup-python@v5.4.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@ -76,18 +76,37 @@ jobs:
|
|||||||
|
|
||||||
# Use C-Extension for SQLAlchemy
|
# Use C-Extension for SQLAlchemy
|
||||||
echo "REQUIRE_SQLALCHEMY_CEXT=1"
|
echo "REQUIRE_SQLALCHEMY_CEXT=1"
|
||||||
|
|
||||||
|
# Add additional pip wheel build constraints
|
||||||
|
echo "PIP_CONSTRAINT=build_constraints.txt"
|
||||||
) > .env_file
|
) > .env_file
|
||||||
|
|
||||||
|
- name: Write pip wheel build constraints
|
||||||
|
run: |
|
||||||
|
(
|
||||||
|
# ninja 1.11.1.2 + 1.11.1.3 seem to be broken on at least armhf
|
||||||
|
# this caused the numpy builds to fail
|
||||||
|
# https://github.com/scikit-build/ninja-python-distributions/issues/274
|
||||||
|
echo "ninja==1.11.1.1"
|
||||||
|
) > build_constraints.txt
|
||||||
|
|
||||||
- name: Upload env_file
|
- name: Upload env_file
|
||||||
uses: actions/upload-artifact@v4.5.0
|
uses: actions/upload-artifact@v4.6.0
|
||||||
with:
|
with:
|
||||||
name: env_file
|
name: env_file
|
||||||
path: ./.env_file
|
path: ./.env_file
|
||||||
include-hidden-files: true
|
include-hidden-files: true
|
||||||
overwrite: true
|
overwrite: true
|
||||||
|
|
||||||
|
- name: Upload build_constraints
|
||||||
|
uses: actions/upload-artifact@v4.6.0
|
||||||
|
with:
|
||||||
|
name: build_constraints
|
||||||
|
path: ./build_constraints.txt
|
||||||
|
overwrite: true
|
||||||
|
|
||||||
- name: Upload requirements_diff
|
- name: Upload requirements_diff
|
||||||
uses: actions/upload-artifact@v4.5.0
|
uses: actions/upload-artifact@v4.6.0
|
||||||
with:
|
with:
|
||||||
name: requirements_diff
|
name: requirements_diff
|
||||||
path: ./requirements_diff.txt
|
path: ./requirements_diff.txt
|
||||||
@ -99,7 +118,7 @@ jobs:
|
|||||||
python -m script.gen_requirements_all ci
|
python -m script.gen_requirements_all ci
|
||||||
|
|
||||||
- name: Upload requirements_all_wheels
|
- name: Upload requirements_all_wheels
|
||||||
uses: actions/upload-artifact@v4.5.0
|
uses: actions/upload-artifact@v4.6.0
|
||||||
with:
|
with:
|
||||||
name: requirements_all_wheels
|
name: requirements_all_wheels
|
||||||
path: ./requirements_all_wheels_*.txt
|
path: ./requirements_all_wheels_*.txt
|
||||||
@ -112,7 +131,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
abi: ["cp312", "cp313"]
|
abi: ["cp313"]
|
||||||
arch: ${{ fromJson(needs.init.outputs.architectures) }}
|
arch: ${{ fromJson(needs.init.outputs.architectures) }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
@ -123,6 +142,11 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: env_file
|
name: env_file
|
||||||
|
|
||||||
|
- name: Download build_constraints
|
||||||
|
uses: actions/download-artifact@v4.1.8
|
||||||
|
with:
|
||||||
|
name: build_constraints
|
||||||
|
|
||||||
- name: Download requirements_diff
|
- name: Download requirements_diff
|
||||||
uses: actions/download-artifact@v4.1.8
|
uses: actions/download-artifact@v4.1.8
|
||||||
with:
|
with:
|
||||||
@ -142,7 +166,7 @@ jobs:
|
|||||||
arch: ${{ matrix.arch }}
|
arch: ${{ matrix.arch }}
|
||||||
wheels-key: ${{ secrets.WHEELS_KEY }}
|
wheels-key: ${{ secrets.WHEELS_KEY }}
|
||||||
env-file: true
|
env-file: true
|
||||||
apk: "libffi-dev;openssl-dev;yaml-dev;nasm;zlib-dev"
|
apk: "libffi-dev;openssl-dev;yaml-dev;nasm;zlib-ng-dev"
|
||||||
skip-binary: aiohttp;multidict;propcache;yarl;SQLAlchemy
|
skip-binary: aiohttp;multidict;propcache;yarl;SQLAlchemy
|
||||||
constraints: "homeassistant/package_constraints.txt"
|
constraints: "homeassistant/package_constraints.txt"
|
||||||
requirements-diff: "requirements_diff.txt"
|
requirements-diff: "requirements_diff.txt"
|
||||||
@ -156,7 +180,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
abi: ["cp312", "cp313"]
|
abi: ["cp313"]
|
||||||
arch: ${{ fromJson(needs.init.outputs.architectures) }}
|
arch: ${{ fromJson(needs.init.outputs.architectures) }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
@ -167,6 +191,11 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: env_file
|
name: env_file
|
||||||
|
|
||||||
|
- name: Download build_constraints
|
||||||
|
uses: actions/download-artifact@v4.1.8
|
||||||
|
with:
|
||||||
|
name: build_constraints
|
||||||
|
|
||||||
- name: Download requirements_diff
|
- name: Download requirements_diff
|
||||||
uses: actions/download-artifact@v4.1.8
|
uses: actions/download-artifact@v4.1.8
|
||||||
with:
|
with:
|
||||||
@ -205,7 +234,7 @@ jobs:
|
|||||||
arch: ${{ matrix.arch }}
|
arch: ${{ matrix.arch }}
|
||||||
wheels-key: ${{ secrets.WHEELS_KEY }}
|
wheels-key: ${{ secrets.WHEELS_KEY }}
|
||||||
env-file: true
|
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;nasm;zlib-dev"
|
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;nasm;zlib-ng-dev"
|
||||||
skip-binary: aiohttp;charset-normalizer;grpcio;multidict;SQLAlchemy;propcache;protobuf;pymicro-vad;yarl
|
skip-binary: aiohttp;charset-normalizer;grpcio;multidict;SQLAlchemy;propcache;protobuf;pymicro-vad;yarl
|
||||||
constraints: "homeassistant/package_constraints.txt"
|
constraints: "homeassistant/package_constraints.txt"
|
||||||
requirements-diff: "requirements_diff.txt"
|
requirements-diff: "requirements_diff.txt"
|
||||||
@ -219,7 +248,7 @@ jobs:
|
|||||||
arch: ${{ matrix.arch }}
|
arch: ${{ matrix.arch }}
|
||||||
wheels-key: ${{ secrets.WHEELS_KEY }}
|
wheels-key: ${{ secrets.WHEELS_KEY }}
|
||||||
env-file: true
|
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;nasm;zlib-dev"
|
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;nasm;zlib-ng-dev"
|
||||||
skip-binary: aiohttp;charset-normalizer;grpcio;multidict;SQLAlchemy;propcache;protobuf;pymicro-vad;yarl
|
skip-binary: aiohttp;charset-normalizer;grpcio;multidict;SQLAlchemy;propcache;protobuf;pymicro-vad;yarl
|
||||||
constraints: "homeassistant/package_constraints.txt"
|
constraints: "homeassistant/package_constraints.txt"
|
||||||
requirements-diff: "requirements_diff.txt"
|
requirements-diff: "requirements_diff.txt"
|
||||||
@ -233,7 +262,7 @@ jobs:
|
|||||||
arch: ${{ matrix.arch }}
|
arch: ${{ matrix.arch }}
|
||||||
wheels-key: ${{ secrets.WHEELS_KEY }}
|
wheels-key: ${{ secrets.WHEELS_KEY }}
|
||||||
env-file: true
|
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;nasm;zlib-dev"
|
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;nasm;zlib-ng-dev"
|
||||||
skip-binary: aiohttp;charset-normalizer;grpcio;multidict;SQLAlchemy;propcache;protobuf;pymicro-vad;yarl
|
skip-binary: aiohttp;charset-normalizer;grpcio;multidict;SQLAlchemy;propcache;protobuf;pymicro-vad;yarl
|
||||||
constraints: "homeassistant/package_constraints.txt"
|
constraints: "homeassistant/package_constraints.txt"
|
||||||
requirements-diff: "requirements_diff.txt"
|
requirements-diff: "requirements_diff.txt"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
rev: v0.8.3
|
rev: v0.9.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: ruff
|
- id: ruff
|
||||||
args:
|
args:
|
||||||
@ -61,13 +61,14 @@ repos:
|
|||||||
name: mypy
|
name: mypy
|
||||||
entry: script/run-in-env.sh mypy
|
entry: script/run-in-env.sh mypy
|
||||||
language: script
|
language: script
|
||||||
types_or: [python, pyi]
|
|
||||||
require_serial: true
|
require_serial: true
|
||||||
|
types_or: [python, pyi]
|
||||||
files: ^(homeassistant|pylint)/.+\.(py|pyi)$
|
files: ^(homeassistant|pylint)/.+\.(py|pyi)$
|
||||||
- id: pylint
|
- id: pylint
|
||||||
name: pylint
|
name: pylint
|
||||||
entry: script/run-in-env.sh pylint -j 0 --ignore-missing-annotations=y
|
entry: script/run-in-env.sh pylint --ignore-missing-annotations=y
|
||||||
language: script
|
language: script
|
||||||
|
require_serial: true
|
||||||
types_or: [python, pyi]
|
types_or: [python, pyi]
|
||||||
files: ^(homeassistant|tests)/.+\.(py|pyi)$
|
files: ^(homeassistant|tests)/.+\.(py|pyi)$
|
||||||
- id: gen_requirements_all
|
- id: gen_requirements_all
|
||||||
|
@ -217,6 +217,7 @@ homeassistant.components.goalzero.*
|
|||||||
homeassistant.components.google.*
|
homeassistant.components.google.*
|
||||||
homeassistant.components.google_assistant_sdk.*
|
homeassistant.components.google_assistant_sdk.*
|
||||||
homeassistant.components.google_cloud.*
|
homeassistant.components.google_cloud.*
|
||||||
|
homeassistant.components.google_drive.*
|
||||||
homeassistant.components.google_photos.*
|
homeassistant.components.google_photos.*
|
||||||
homeassistant.components.google_sheets.*
|
homeassistant.components.google_sheets.*
|
||||||
homeassistant.components.govee_ble.*
|
homeassistant.components.govee_ble.*
|
||||||
@ -224,8 +225,10 @@ homeassistant.components.gpsd.*
|
|||||||
homeassistant.components.greeneye_monitor.*
|
homeassistant.components.greeneye_monitor.*
|
||||||
homeassistant.components.group.*
|
homeassistant.components.group.*
|
||||||
homeassistant.components.guardian.*
|
homeassistant.components.guardian.*
|
||||||
|
homeassistant.components.habitica.*
|
||||||
homeassistant.components.hardkernel.*
|
homeassistant.components.hardkernel.*
|
||||||
homeassistant.components.hardware.*
|
homeassistant.components.hardware.*
|
||||||
|
homeassistant.components.heos.*
|
||||||
homeassistant.components.here_travel_time.*
|
homeassistant.components.here_travel_time.*
|
||||||
homeassistant.components.history.*
|
homeassistant.components.history.*
|
||||||
homeassistant.components.history_stats.*
|
homeassistant.components.history_stats.*
|
||||||
@ -236,6 +239,7 @@ homeassistant.components.homeassistant_green.*
|
|||||||
homeassistant.components.homeassistant_hardware.*
|
homeassistant.components.homeassistant_hardware.*
|
||||||
homeassistant.components.homeassistant_sky_connect.*
|
homeassistant.components.homeassistant_sky_connect.*
|
||||||
homeassistant.components.homeassistant_yellow.*
|
homeassistant.components.homeassistant_yellow.*
|
||||||
|
homeassistant.components.homee.*
|
||||||
homeassistant.components.homekit.*
|
homeassistant.components.homekit.*
|
||||||
homeassistant.components.homekit_controller
|
homeassistant.components.homekit_controller
|
||||||
homeassistant.components.homekit_controller.alarm_control_panel
|
homeassistant.components.homekit_controller.alarm_control_panel
|
||||||
@ -261,6 +265,7 @@ homeassistant.components.image_processing.*
|
|||||||
homeassistant.components.image_upload.*
|
homeassistant.components.image_upload.*
|
||||||
homeassistant.components.imap.*
|
homeassistant.components.imap.*
|
||||||
homeassistant.components.imgw_pib.*
|
homeassistant.components.imgw_pib.*
|
||||||
|
homeassistant.components.incomfort.*
|
||||||
homeassistant.components.input_button.*
|
homeassistant.components.input_button.*
|
||||||
homeassistant.components.input_select.*
|
homeassistant.components.input_select.*
|
||||||
homeassistant.components.input_text.*
|
homeassistant.components.input_text.*
|
||||||
@ -291,6 +296,7 @@ homeassistant.components.lcn.*
|
|||||||
homeassistant.components.ld2410_ble.*
|
homeassistant.components.ld2410_ble.*
|
||||||
homeassistant.components.led_ble.*
|
homeassistant.components.led_ble.*
|
||||||
homeassistant.components.lektrico.*
|
homeassistant.components.lektrico.*
|
||||||
|
homeassistant.components.letpot.*
|
||||||
homeassistant.components.lidarr.*
|
homeassistant.components.lidarr.*
|
||||||
homeassistant.components.lifx.*
|
homeassistant.components.lifx.*
|
||||||
homeassistant.components.light.*
|
homeassistant.components.light.*
|
||||||
@ -305,12 +311,15 @@ homeassistant.components.logbook.*
|
|||||||
homeassistant.components.logger.*
|
homeassistant.components.logger.*
|
||||||
homeassistant.components.london_underground.*
|
homeassistant.components.london_underground.*
|
||||||
homeassistant.components.lookin.*
|
homeassistant.components.lookin.*
|
||||||
|
homeassistant.components.lovelace.*
|
||||||
homeassistant.components.luftdaten.*
|
homeassistant.components.luftdaten.*
|
||||||
homeassistant.components.madvr.*
|
homeassistant.components.madvr.*
|
||||||
homeassistant.components.manual.*
|
homeassistant.components.manual.*
|
||||||
homeassistant.components.mastodon.*
|
homeassistant.components.mastodon.*
|
||||||
homeassistant.components.matrix.*
|
homeassistant.components.matrix.*
|
||||||
homeassistant.components.matter.*
|
homeassistant.components.matter.*
|
||||||
|
homeassistant.components.mcp.*
|
||||||
|
homeassistant.components.mcp_server.*
|
||||||
homeassistant.components.mealie.*
|
homeassistant.components.mealie.*
|
||||||
homeassistant.components.media_extractor.*
|
homeassistant.components.media_extractor.*
|
||||||
homeassistant.components.media_player.*
|
homeassistant.components.media_player.*
|
||||||
@ -352,6 +361,7 @@ homeassistant.components.number.*
|
|||||||
homeassistant.components.nut.*
|
homeassistant.components.nut.*
|
||||||
homeassistant.components.onboarding.*
|
homeassistant.components.onboarding.*
|
||||||
homeassistant.components.oncue.*
|
homeassistant.components.oncue.*
|
||||||
|
homeassistant.components.onedrive.*
|
||||||
homeassistant.components.onewire.*
|
homeassistant.components.onewire.*
|
||||||
homeassistant.components.onkyo.*
|
homeassistant.components.onkyo.*
|
||||||
homeassistant.components.open_meteo.*
|
homeassistant.components.open_meteo.*
|
||||||
@ -362,11 +372,14 @@ homeassistant.components.openuv.*
|
|||||||
homeassistant.components.oralb.*
|
homeassistant.components.oralb.*
|
||||||
homeassistant.components.otbr.*
|
homeassistant.components.otbr.*
|
||||||
homeassistant.components.overkiz.*
|
homeassistant.components.overkiz.*
|
||||||
|
homeassistant.components.overseerr.*
|
||||||
homeassistant.components.p1_monitor.*
|
homeassistant.components.p1_monitor.*
|
||||||
|
homeassistant.components.pandora.*
|
||||||
homeassistant.components.panel_custom.*
|
homeassistant.components.panel_custom.*
|
||||||
homeassistant.components.peblar.*
|
homeassistant.components.peblar.*
|
||||||
homeassistant.components.peco.*
|
homeassistant.components.peco.*
|
||||||
homeassistant.components.persistent_notification.*
|
homeassistant.components.persistent_notification.*
|
||||||
|
homeassistant.components.person.*
|
||||||
homeassistant.components.pi_hole.*
|
homeassistant.components.pi_hole.*
|
||||||
homeassistant.components.ping.*
|
homeassistant.components.ping.*
|
||||||
homeassistant.components.plugwise.*
|
homeassistant.components.plugwise.*
|
||||||
@ -380,6 +393,8 @@ homeassistant.components.pure_energie.*
|
|||||||
homeassistant.components.purpleair.*
|
homeassistant.components.purpleair.*
|
||||||
homeassistant.components.pushbullet.*
|
homeassistant.components.pushbullet.*
|
||||||
homeassistant.components.pvoutput.*
|
homeassistant.components.pvoutput.*
|
||||||
|
homeassistant.components.python_script.*
|
||||||
|
homeassistant.components.qbus.*
|
||||||
homeassistant.components.qnap_qsw.*
|
homeassistant.components.qnap_qsw.*
|
||||||
homeassistant.components.rabbitair.*
|
homeassistant.components.rabbitair.*
|
||||||
homeassistant.components.radarr.*
|
homeassistant.components.radarr.*
|
||||||
|
3
.vscode/settings.default.json
vendored
3
.vscode/settings.default.json
vendored
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
// Please keep this file in sync with settings in home-assistant/.devcontainer/devcontainer.json
|
// Please keep this file (mostly!) in sync with settings in home-assistant/.devcontainer/devcontainer.json
|
||||||
// Added --no-cov to work around TypeError: message must be set
|
// Added --no-cov to work around TypeError: message must be set
|
||||||
// https://github.com/microsoft/vscode-python/issues/14067
|
// https://github.com/microsoft/vscode-python/issues/14067
|
||||||
"python.testing.pytestArgs": ["--no-cov"],
|
"python.testing.pytestArgs": ["--no-cov"],
|
||||||
@ -12,6 +12,7 @@
|
|||||||
"fileMatch": [
|
"fileMatch": [
|
||||||
"homeassistant/components/*/manifest.json"
|
"homeassistant/components/*/manifest.json"
|
||||||
],
|
],
|
||||||
|
// This value differs between working with devcontainer and locally, therefor this value should NOT be in sync!
|
||||||
"url": "./script/json_schemas/manifest_schema.json"
|
"url": "./script/json_schemas/manifest_schema.json"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
62
CODEOWNERS
generated
62
CODEOWNERS
generated
@ -566,6 +566,8 @@ build.json @home-assistant/supervisor
|
|||||||
/tests/components/google_assistant_sdk/ @tronikos
|
/tests/components/google_assistant_sdk/ @tronikos
|
||||||
/homeassistant/components/google_cloud/ @lufton @tronikos
|
/homeassistant/components/google_cloud/ @lufton @tronikos
|
||||||
/tests/components/google_cloud/ @lufton @tronikos
|
/tests/components/google_cloud/ @lufton @tronikos
|
||||||
|
/homeassistant/components/google_drive/ @tronikos
|
||||||
|
/tests/components/google_drive/ @tronikos
|
||||||
/homeassistant/components/google_generative_ai_conversation/ @tronikos
|
/homeassistant/components/google_generative_ai_conversation/ @tronikos
|
||||||
/tests/components/google_generative_ai_conversation/ @tronikos
|
/tests/components/google_generative_ai_conversation/ @tronikos
|
||||||
/homeassistant/components/google_mail/ @tkdrob
|
/homeassistant/components/google_mail/ @tkdrob
|
||||||
@ -637,6 +639,8 @@ build.json @home-assistant/supervisor
|
|||||||
/tests/components/homeassistant_sky_connect/ @home-assistant/core
|
/tests/components/homeassistant_sky_connect/ @home-assistant/core
|
||||||
/homeassistant/components/homeassistant_yellow/ @home-assistant/core
|
/homeassistant/components/homeassistant_yellow/ @home-assistant/core
|
||||||
/tests/components/homeassistant_yellow/ @home-assistant/core
|
/tests/components/homeassistant_yellow/ @home-assistant/core
|
||||||
|
/homeassistant/components/homee/ @Taraman17
|
||||||
|
/tests/components/homee/ @Taraman17
|
||||||
/homeassistant/components/homekit/ @bdraco
|
/homeassistant/components/homekit/ @bdraco
|
||||||
/tests/components/homekit/ @bdraco
|
/tests/components/homekit/ @bdraco
|
||||||
/homeassistant/components/homekit_controller/ @Jc2k @bdraco
|
/homeassistant/components/homekit_controller/ @Jc2k @bdraco
|
||||||
@ -680,12 +684,12 @@ build.json @home-assistant/supervisor
|
|||||||
/homeassistant/components/iammeter/ @lewei50
|
/homeassistant/components/iammeter/ @lewei50
|
||||||
/homeassistant/components/iaqualink/ @flz
|
/homeassistant/components/iaqualink/ @flz
|
||||||
/tests/components/iaqualink/ @flz
|
/tests/components/iaqualink/ @flz
|
||||||
/homeassistant/components/ibeacon/ @bdraco
|
|
||||||
/tests/components/ibeacon/ @bdraco
|
|
||||||
/homeassistant/components/icloud/ @Quentame @nzapponi
|
/homeassistant/components/icloud/ @Quentame @nzapponi
|
||||||
/tests/components/icloud/ @Quentame @nzapponi
|
/tests/components/icloud/ @Quentame @nzapponi
|
||||||
/homeassistant/components/idasen_desk/ @abmantis
|
/homeassistant/components/idasen_desk/ @abmantis
|
||||||
/tests/components/idasen_desk/ @abmantis
|
/tests/components/idasen_desk/ @abmantis
|
||||||
|
/homeassistant/components/igloohome/ @keithle888
|
||||||
|
/tests/components/igloohome/ @keithle888
|
||||||
/homeassistant/components/ign_sismologia/ @exxamalte
|
/homeassistant/components/ign_sismologia/ @exxamalte
|
||||||
/tests/components/ign_sismologia/ @exxamalte
|
/tests/components/ign_sismologia/ @exxamalte
|
||||||
/homeassistant/components/image/ @home-assistant/core
|
/homeassistant/components/image/ @home-assistant/core
|
||||||
@ -827,6 +831,8 @@ build.json @home-assistant/supervisor
|
|||||||
/tests/components/led_ble/ @bdraco
|
/tests/components/led_ble/ @bdraco
|
||||||
/homeassistant/components/lektrico/ @lektrico
|
/homeassistant/components/lektrico/ @lektrico
|
||||||
/tests/components/lektrico/ @lektrico
|
/tests/components/lektrico/ @lektrico
|
||||||
|
/homeassistant/components/letpot/ @jpelgrom
|
||||||
|
/tests/components/letpot/ @jpelgrom
|
||||||
/homeassistant/components/lg_netcast/ @Drafteed @splinter98
|
/homeassistant/components/lg_netcast/ @Drafteed @splinter98
|
||||||
/tests/components/lg_netcast/ @Drafteed @splinter98
|
/tests/components/lg_netcast/ @Drafteed @splinter98
|
||||||
/homeassistant/components/lg_thinq/ @LG-ThinQ-Integration
|
/homeassistant/components/lg_thinq/ @LG-ThinQ-Integration
|
||||||
@ -887,6 +893,10 @@ build.json @home-assistant/supervisor
|
|||||||
/tests/components/matrix/ @PaarthShah
|
/tests/components/matrix/ @PaarthShah
|
||||||
/homeassistant/components/matter/ @home-assistant/matter
|
/homeassistant/components/matter/ @home-assistant/matter
|
||||||
/tests/components/matter/ @home-assistant/matter
|
/tests/components/matter/ @home-assistant/matter
|
||||||
|
/homeassistant/components/mcp/ @allenporter
|
||||||
|
/tests/components/mcp/ @allenporter
|
||||||
|
/homeassistant/components/mcp_server/ @allenporter
|
||||||
|
/tests/components/mcp_server/ @allenporter
|
||||||
/homeassistant/components/mealie/ @joostlek @andrew-codechimp
|
/homeassistant/components/mealie/ @joostlek @andrew-codechimp
|
||||||
/tests/components/mealie/ @joostlek @andrew-codechimp
|
/tests/components/mealie/ @joostlek @andrew-codechimp
|
||||||
/homeassistant/components/meater/ @Sotolotl @emontnemery
|
/homeassistant/components/meater/ @Sotolotl @emontnemery
|
||||||
@ -1016,7 +1026,6 @@ build.json @home-assistant/supervisor
|
|||||||
/homeassistant/components/nina/ @DeerMaximum
|
/homeassistant/components/nina/ @DeerMaximum
|
||||||
/tests/components/nina/ @DeerMaximum
|
/tests/components/nina/ @DeerMaximum
|
||||||
/homeassistant/components/nissan_leaf/ @filcole
|
/homeassistant/components/nissan_leaf/ @filcole
|
||||||
/homeassistant/components/nmbs/ @thibmaek
|
|
||||||
/homeassistant/components/noaa_tides/ @jdelaney72
|
/homeassistant/components/noaa_tides/ @jdelaney72
|
||||||
/homeassistant/components/nobo_hub/ @echoromeo @oyvindwe
|
/homeassistant/components/nobo_hub/ @echoromeo @oyvindwe
|
||||||
/tests/components/nobo_hub/ @echoromeo @oyvindwe
|
/tests/components/nobo_hub/ @echoromeo @oyvindwe
|
||||||
@ -1064,12 +1073,14 @@ build.json @home-assistant/supervisor
|
|||||||
/tests/components/oncue/ @bdraco @peterager
|
/tests/components/oncue/ @bdraco @peterager
|
||||||
/homeassistant/components/ondilo_ico/ @JeromeHXP
|
/homeassistant/components/ondilo_ico/ @JeromeHXP
|
||||||
/tests/components/ondilo_ico/ @JeromeHXP
|
/tests/components/ondilo_ico/ @JeromeHXP
|
||||||
|
/homeassistant/components/onedrive/ @zweckj
|
||||||
|
/tests/components/onedrive/ @zweckj
|
||||||
/homeassistant/components/onewire/ @garbled1 @epenet
|
/homeassistant/components/onewire/ @garbled1 @epenet
|
||||||
/tests/components/onewire/ @garbled1 @epenet
|
/tests/components/onewire/ @garbled1 @epenet
|
||||||
/homeassistant/components/onkyo/ @arturpragacz @eclair4151
|
/homeassistant/components/onkyo/ @arturpragacz @eclair4151
|
||||||
/tests/components/onkyo/ @arturpragacz @eclair4151
|
/tests/components/onkyo/ @arturpragacz @eclair4151
|
||||||
/homeassistant/components/onvif/ @hunterjm
|
/homeassistant/components/onvif/ @hunterjm @jterrace
|
||||||
/tests/components/onvif/ @hunterjm
|
/tests/components/onvif/ @hunterjm @jterrace
|
||||||
/homeassistant/components/open_meteo/ @frenck
|
/homeassistant/components/open_meteo/ @frenck
|
||||||
/tests/components/open_meteo/ @frenck
|
/tests/components/open_meteo/ @frenck
|
||||||
/homeassistant/components/openai_conversation/ @balloob
|
/homeassistant/components/openai_conversation/ @balloob
|
||||||
@ -1103,8 +1114,10 @@ build.json @home-assistant/supervisor
|
|||||||
/tests/components/otbr/ @home-assistant/core
|
/tests/components/otbr/ @home-assistant/core
|
||||||
/homeassistant/components/ourgroceries/ @OnFreund
|
/homeassistant/components/ourgroceries/ @OnFreund
|
||||||
/tests/components/ourgroceries/ @OnFreund
|
/tests/components/ourgroceries/ @OnFreund
|
||||||
/homeassistant/components/overkiz/ @imicknl @vlebourl @tetienne @nyroDev @tronix117 @alexfp14
|
/homeassistant/components/overkiz/ @imicknl
|
||||||
/tests/components/overkiz/ @imicknl @vlebourl @tetienne @nyroDev @tronix117 @alexfp14
|
/tests/components/overkiz/ @imicknl
|
||||||
|
/homeassistant/components/overseerr/ @joostlek
|
||||||
|
/tests/components/overseerr/ @joostlek
|
||||||
/homeassistant/components/ovo_energy/ @timmo001
|
/homeassistant/components/ovo_energy/ @timmo001
|
||||||
/tests/components/ovo_energy/ @timmo001
|
/tests/components/ovo_energy/ @timmo001
|
||||||
/homeassistant/components/p1_monitor/ @klaasnicolaas
|
/homeassistant/components/p1_monitor/ @klaasnicolaas
|
||||||
@ -1135,8 +1148,8 @@ build.json @home-assistant/supervisor
|
|||||||
/tests/components/plaato/ @JohNan
|
/tests/components/plaato/ @JohNan
|
||||||
/homeassistant/components/plex/ @jjlawren
|
/homeassistant/components/plex/ @jjlawren
|
||||||
/tests/components/plex/ @jjlawren
|
/tests/components/plex/ @jjlawren
|
||||||
/homeassistant/components/plugwise/ @CoMPaTech @bouwew @frenck
|
/homeassistant/components/plugwise/ @CoMPaTech @bouwew
|
||||||
/tests/components/plugwise/ @CoMPaTech @bouwew @frenck
|
/tests/components/plugwise/ @CoMPaTech @bouwew
|
||||||
/homeassistant/components/plum_lightpad/ @ColinHarrington @prystupa
|
/homeassistant/components/plum_lightpad/ @ColinHarrington @prystupa
|
||||||
/tests/components/plum_lightpad/ @ColinHarrington @prystupa
|
/tests/components/plum_lightpad/ @ColinHarrington @prystupa
|
||||||
/homeassistant/components/point/ @fredrike
|
/homeassistant/components/point/ @fredrike
|
||||||
@ -1182,6 +1195,8 @@ build.json @home-assistant/supervisor
|
|||||||
/tests/components/pyload/ @tr4nt0r
|
/tests/components/pyload/ @tr4nt0r
|
||||||
/homeassistant/components/qbittorrent/ @geoffreylagaisse @finder39
|
/homeassistant/components/qbittorrent/ @geoffreylagaisse @finder39
|
||||||
/tests/components/qbittorrent/ @geoffreylagaisse @finder39
|
/tests/components/qbittorrent/ @geoffreylagaisse @finder39
|
||||||
|
/homeassistant/components/qbus/ @Qbus-iot @thomasddn
|
||||||
|
/tests/components/qbus/ @Qbus-iot @thomasddn
|
||||||
/homeassistant/components/qingping/ @bdraco
|
/homeassistant/components/qingping/ @bdraco
|
||||||
/tests/components/qingping/ @bdraco
|
/tests/components/qingping/ @bdraco
|
||||||
/homeassistant/components/qld_bushfire/ @exxamalte
|
/homeassistant/components/qld_bushfire/ @exxamalte
|
||||||
@ -1258,8 +1273,8 @@ build.json @home-assistant/supervisor
|
|||||||
/tests/components/rituals_perfume_genie/ @milanmeu @frenck
|
/tests/components/rituals_perfume_genie/ @milanmeu @frenck
|
||||||
/homeassistant/components/rmvtransport/ @cgtobi
|
/homeassistant/components/rmvtransport/ @cgtobi
|
||||||
/tests/components/rmvtransport/ @cgtobi
|
/tests/components/rmvtransport/ @cgtobi
|
||||||
/homeassistant/components/roborock/ @Lash-L
|
/homeassistant/components/roborock/ @Lash-L @allenporter
|
||||||
/tests/components/roborock/ @Lash-L
|
/tests/components/roborock/ @Lash-L @allenporter
|
||||||
/homeassistant/components/roku/ @ctalkington
|
/homeassistant/components/roku/ @ctalkington
|
||||||
/tests/components/roku/ @ctalkington
|
/tests/components/roku/ @ctalkington
|
||||||
/homeassistant/components/romy/ @xeniter
|
/homeassistant/components/romy/ @xeniter
|
||||||
@ -1278,6 +1293,7 @@ build.json @home-assistant/supervisor
|
|||||||
/tests/components/ruckus_unleashed/ @lanrat @ms264556 @gabe565
|
/tests/components/ruckus_unleashed/ @lanrat @ms264556 @gabe565
|
||||||
/homeassistant/components/russound_rio/ @noahhusby
|
/homeassistant/components/russound_rio/ @noahhusby
|
||||||
/tests/components/russound_rio/ @noahhusby
|
/tests/components/russound_rio/ @noahhusby
|
||||||
|
/homeassistant/components/russound_rnet/ @noahhusby
|
||||||
/homeassistant/components/ruuvi_gateway/ @akx
|
/homeassistant/components/ruuvi_gateway/ @akx
|
||||||
/tests/components/ruuvi_gateway/ @akx
|
/tests/components/ruuvi_gateway/ @akx
|
||||||
/homeassistant/components/ruuvitag_ble/ @akx
|
/homeassistant/components/ruuvitag_ble/ @akx
|
||||||
@ -1371,8 +1387,8 @@ build.json @home-assistant/supervisor
|
|||||||
/tests/components/slide_local/ @dontinelli
|
/tests/components/slide_local/ @dontinelli
|
||||||
/homeassistant/components/slimproto/ @marcelveldt
|
/homeassistant/components/slimproto/ @marcelveldt
|
||||||
/tests/components/slimproto/ @marcelveldt
|
/tests/components/slimproto/ @marcelveldt
|
||||||
/homeassistant/components/sma/ @kellerza @rklomp
|
/homeassistant/components/sma/ @kellerza @rklomp @erwindouna
|
||||||
/tests/components/sma/ @kellerza @rklomp
|
/tests/components/sma/ @kellerza @rklomp @erwindouna
|
||||||
/homeassistant/components/smappee/ @bsmappee
|
/homeassistant/components/smappee/ @bsmappee
|
||||||
/tests/components/smappee/ @bsmappee
|
/tests/components/smappee/ @bsmappee
|
||||||
/homeassistant/components/smart_meter_texas/ @grahamwetzler
|
/homeassistant/components/smart_meter_texas/ @grahamwetzler
|
||||||
@ -1398,8 +1414,8 @@ build.json @home-assistant/supervisor
|
|||||||
/homeassistant/components/solaredge_local/ @drobtravels @scheric
|
/homeassistant/components/solaredge_local/ @drobtravels @scheric
|
||||||
/homeassistant/components/solarlog/ @Ernst79 @dontinelli
|
/homeassistant/components/solarlog/ @Ernst79 @dontinelli
|
||||||
/tests/components/solarlog/ @Ernst79 @dontinelli
|
/tests/components/solarlog/ @Ernst79 @dontinelli
|
||||||
/homeassistant/components/solax/ @squishykid
|
/homeassistant/components/solax/ @squishykid @Darsstar
|
||||||
/tests/components/solax/ @squishykid
|
/tests/components/solax/ @squishykid @Darsstar
|
||||||
/homeassistant/components/soma/ @ratsept @sebfortier2288
|
/homeassistant/components/soma/ @ratsept @sebfortier2288
|
||||||
/tests/components/soma/ @ratsept @sebfortier2288
|
/tests/components/soma/ @ratsept @sebfortier2288
|
||||||
/homeassistant/components/sonarr/ @ctalkington
|
/homeassistant/components/sonarr/ @ctalkington
|
||||||
@ -1478,8 +1494,8 @@ build.json @home-assistant/supervisor
|
|||||||
/tests/components/system_bridge/ @timmo001
|
/tests/components/system_bridge/ @timmo001
|
||||||
/homeassistant/components/systemmonitor/ @gjohansson-ST
|
/homeassistant/components/systemmonitor/ @gjohansson-ST
|
||||||
/tests/components/systemmonitor/ @gjohansson-ST
|
/tests/components/systemmonitor/ @gjohansson-ST
|
||||||
/homeassistant/components/tado/ @chiefdragon @erwindouna
|
/homeassistant/components/tado/ @erwindouna
|
||||||
/tests/components/tado/ @chiefdragon @erwindouna
|
/tests/components/tado/ @erwindouna
|
||||||
/homeassistant/components/tag/ @balloob @dmulcahey
|
/homeassistant/components/tag/ @balloob @dmulcahey
|
||||||
/tests/components/tag/ @balloob @dmulcahey
|
/tests/components/tag/ @balloob @dmulcahey
|
||||||
/homeassistant/components/tailscale/ @frenck
|
/homeassistant/components/tailscale/ @frenck
|
||||||
@ -1573,8 +1589,8 @@ build.json @home-assistant/supervisor
|
|||||||
/tests/components/triggercmd/ @rvmey
|
/tests/components/triggercmd/ @rvmey
|
||||||
/homeassistant/components/tts/ @home-assistant/core
|
/homeassistant/components/tts/ @home-assistant/core
|
||||||
/tests/components/tts/ @home-assistant/core
|
/tests/components/tts/ @home-assistant/core
|
||||||
/homeassistant/components/tuya/ @Tuya @zlinoliver @frenck
|
/homeassistant/components/tuya/ @Tuya @zlinoliver
|
||||||
/tests/components/tuya/ @Tuya @zlinoliver @frenck
|
/tests/components/tuya/ @Tuya @zlinoliver
|
||||||
/homeassistant/components/twentemilieu/ @frenck
|
/homeassistant/components/twentemilieu/ @frenck
|
||||||
/tests/components/twentemilieu/ @frenck
|
/tests/components/twentemilieu/ @frenck
|
||||||
/homeassistant/components/twinkly/ @dr1rrb @Robbie1221 @Olen
|
/homeassistant/components/twinkly/ @dr1rrb @Robbie1221 @Olen
|
||||||
@ -1618,15 +1634,15 @@ build.json @home-assistant/supervisor
|
|||||||
/tests/components/valve/ @home-assistant/core
|
/tests/components/valve/ @home-assistant/core
|
||||||
/homeassistant/components/velbus/ @Cereal2nd @brefra
|
/homeassistant/components/velbus/ @Cereal2nd @brefra
|
||||||
/tests/components/velbus/ @Cereal2nd @brefra
|
/tests/components/velbus/ @Cereal2nd @brefra
|
||||||
/homeassistant/components/velux/ @Julius2342 @DeerMaximum
|
/homeassistant/components/velux/ @Julius2342 @DeerMaximum @pawlizio
|
||||||
/tests/components/velux/ @Julius2342 @DeerMaximum
|
/tests/components/velux/ @Julius2342 @DeerMaximum @pawlizio
|
||||||
/homeassistant/components/venstar/ @garbled1 @jhollowe
|
/homeassistant/components/venstar/ @garbled1 @jhollowe
|
||||||
/tests/components/venstar/ @garbled1 @jhollowe
|
/tests/components/venstar/ @garbled1 @jhollowe
|
||||||
/homeassistant/components/versasense/ @imstevenxyz
|
/homeassistant/components/versasense/ @imstevenxyz
|
||||||
/homeassistant/components/version/ @ludeeus
|
/homeassistant/components/version/ @ludeeus
|
||||||
/tests/components/version/ @ludeeus
|
/tests/components/version/ @ludeeus
|
||||||
/homeassistant/components/vesync/ @markperdue @webdjoe @thegardenmonkey @cdnninja
|
/homeassistant/components/vesync/ @markperdue @webdjoe @thegardenmonkey @cdnninja @iprak
|
||||||
/tests/components/vesync/ @markperdue @webdjoe @thegardenmonkey @cdnninja
|
/tests/components/vesync/ @markperdue @webdjoe @thegardenmonkey @cdnninja @iprak
|
||||||
/homeassistant/components/vicare/ @CFenner
|
/homeassistant/components/vicare/ @CFenner
|
||||||
/tests/components/vicare/ @CFenner
|
/tests/components/vicare/ @CFenner
|
||||||
/homeassistant/components/vilfo/ @ManneW
|
/homeassistant/components/vilfo/ @ManneW
|
||||||
|
4
Dockerfile
generated
4
Dockerfile
generated
@ -13,7 +13,7 @@ ENV \
|
|||||||
ARG QEMU_CPU
|
ARG QEMU_CPU
|
||||||
|
|
||||||
# Install uv
|
# Install uv
|
||||||
RUN pip3 install uv==0.5.8
|
RUN pip3 install uv==0.5.21
|
||||||
|
|
||||||
WORKDIR /usr/src
|
WORKDIR /usr/src
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ RUN \
|
|||||||
"armv7") go2rtc_suffix='arm' ;; \
|
"armv7") go2rtc_suffix='arm' ;; \
|
||||||
*) go2rtc_suffix=${BUILD_ARCH} ;; \
|
*) go2rtc_suffix=${BUILD_ARCH} ;; \
|
||||||
esac \
|
esac \
|
||||||
&& curl -L https://github.com/AlexxIT/go2rtc/releases/download/v1.9.7/go2rtc_linux_${go2rtc_suffix} --output /bin/go2rtc \
|
&& curl -L https://github.com/AlexxIT/go2rtc/releases/download/v1.9.8/go2rtc_linux_${go2rtc_suffix} --output /bin/go2rtc \
|
||||||
&& chmod +x /bin/go2rtc \
|
&& chmod +x /bin/go2rtc \
|
||||||
# Verify go2rtc can be executed
|
# Verify go2rtc can be executed
|
||||||
&& go2rtc --version
|
&& go2rtc --version
|
||||||
|
@ -308,7 +308,7 @@ class AuthStore:
|
|||||||
credentials.data = data
|
credentials.data = data
|
||||||
self._async_schedule_save()
|
self._async_schedule_save()
|
||||||
|
|
||||||
async def async_load(self) -> None: # noqa: C901
|
async def async_load(self) -> None:
|
||||||
"""Load the users."""
|
"""Load the users."""
|
||||||
if self._loaded:
|
if self._loaded:
|
||||||
raise RuntimeError("Auth storage is already loaded")
|
raise RuntimeError("Auth storage is already loaded")
|
||||||
|
@ -4,9 +4,8 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
import types
|
import types
|
||||||
from typing import Any, Generic
|
from typing import Any
|
||||||
|
|
||||||
from typing_extensions import TypeVar
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
from voluptuous.humanize import humanize_error
|
from voluptuous.humanize import humanize_error
|
||||||
|
|
||||||
@ -35,12 +34,6 @@ DATA_REQS: HassKey[set[str]] = HassKey("mfa_auth_module_reqs_processed")
|
|||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
_MultiFactorAuthModuleT = TypeVar(
|
|
||||||
"_MultiFactorAuthModuleT",
|
|
||||||
bound="MultiFactorAuthModule",
|
|
||||||
default="MultiFactorAuthModule",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class MultiFactorAuthModule:
|
class MultiFactorAuthModule:
|
||||||
"""Multi-factor Auth Module of validation function."""
|
"""Multi-factor Auth Module of validation function."""
|
||||||
@ -102,7 +95,9 @@ class MultiFactorAuthModule:
|
|||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
class SetupFlow(data_entry_flow.FlowHandler, Generic[_MultiFactorAuthModuleT]):
|
class SetupFlow[_MultiFactorAuthModuleT: MultiFactorAuthModule = MultiFactorAuthModule](
|
||||||
|
data_entry_flow.FlowHandler
|
||||||
|
):
|
||||||
"""Handler for the setup flow."""
|
"""Handler for the setup flow."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
|
@ -11,7 +11,7 @@ import uuid
|
|||||||
import attr
|
import attr
|
||||||
from attr import Attribute
|
from attr import Attribute
|
||||||
from attr.setters import validate
|
from attr.setters import validate
|
||||||
from propcache import cached_property
|
from propcache.api import cached_property
|
||||||
|
|
||||||
from homeassistant.const import __version__
|
from homeassistant.const import __version__
|
||||||
from homeassistant.data_entry_flow import FlowContext, FlowResult
|
from homeassistant.data_entry_flow import FlowContext, FlowResult
|
||||||
|
@ -17,12 +17,12 @@ POLICY_SCHEMA = vol.Schema({vol.Optional(CAT_ENTITIES): ENTITY_POLICY_SCHEMA})
|
|||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"POLICY_SCHEMA",
|
"POLICY_SCHEMA",
|
||||||
"merge_policies",
|
|
||||||
"PermissionLookup",
|
|
||||||
"PolicyType",
|
|
||||||
"AbstractPermissions",
|
"AbstractPermissions",
|
||||||
"PolicyPermissions",
|
|
||||||
"OwnerPermissions",
|
"OwnerPermissions",
|
||||||
|
"PermissionLookup",
|
||||||
|
"PolicyPermissions",
|
||||||
|
"PolicyType",
|
||||||
|
"merge_policies",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -5,9 +5,8 @@ from __future__ import annotations
|
|||||||
from collections.abc import Mapping
|
from collections.abc import Mapping
|
||||||
import logging
|
import logging
|
||||||
import types
|
import types
|
||||||
from typing import Any, Generic
|
from typing import Any
|
||||||
|
|
||||||
from typing_extensions import TypeVar
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
from voluptuous.humanize import humanize_error
|
from voluptuous.humanize import humanize_error
|
||||||
|
|
||||||
@ -47,8 +46,6 @@ AUTH_PROVIDER_SCHEMA = vol.Schema(
|
|||||||
extra=vol.ALLOW_EXTRA,
|
extra=vol.ALLOW_EXTRA,
|
||||||
)
|
)
|
||||||
|
|
||||||
_AuthProviderT = TypeVar("_AuthProviderT", bound="AuthProvider", default="AuthProvider")
|
|
||||||
|
|
||||||
|
|
||||||
class AuthProvider:
|
class AuthProvider:
|
||||||
"""Provider of user authentication."""
|
"""Provider of user authentication."""
|
||||||
@ -195,9 +192,8 @@ async def load_auth_provider_module(
|
|||||||
return module
|
return module
|
||||||
|
|
||||||
|
|
||||||
class LoginFlow(
|
class LoginFlow[_AuthProviderT: AuthProvider = AuthProvider](
|
||||||
FlowHandler[AuthFlowContext, AuthFlowResult, tuple[str, str]],
|
FlowHandler[AuthFlowContext, AuthFlowResult, tuple[str, str]],
|
||||||
Generic[_AuthProviderT],
|
|
||||||
):
|
):
|
||||||
"""Handler for the login flow."""
|
"""Handler for the login flow."""
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ import voluptuous as vol
|
|||||||
|
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
import homeassistant.helpers.config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.network import is_cloud_connection
|
from homeassistant.helpers.network import is_cloud_connection
|
||||||
|
|
||||||
from .. import InvalidAuthError
|
from .. import InvalidAuthError
|
||||||
|
@ -18,6 +18,7 @@ import securetar
|
|||||||
from .const import __version__ as HA_VERSION
|
from .const import __version__ as HA_VERSION
|
||||||
|
|
||||||
RESTORE_BACKUP_FILE = ".HA_RESTORE"
|
RESTORE_BACKUP_FILE = ".HA_RESTORE"
|
||||||
|
RESTORE_BACKUP_RESULT_FILE = ".HA_RESTORE_RESULT"
|
||||||
KEEP_BACKUPS = ("backups",)
|
KEEP_BACKUPS = ("backups",)
|
||||||
KEEP_DATABASE = (
|
KEEP_DATABASE = (
|
||||||
"home-assistant_v2.db",
|
"home-assistant_v2.db",
|
||||||
@ -62,7 +63,10 @@ def restore_backup_file_content(config_dir: Path) -> RestoreBackupFileContent |
|
|||||||
restore_database=instruction_content["restore_database"],
|
restore_database=instruction_content["restore_database"],
|
||||||
restore_homeassistant=instruction_content["restore_homeassistant"],
|
restore_homeassistant=instruction_content["restore_homeassistant"],
|
||||||
)
|
)
|
||||||
except (FileNotFoundError, KeyError, json.JSONDecodeError):
|
except FileNotFoundError:
|
||||||
|
return None
|
||||||
|
except (KeyError, json.JSONDecodeError) as err:
|
||||||
|
_write_restore_result_file(config_dir, False, err)
|
||||||
return None
|
return None
|
||||||
finally:
|
finally:
|
||||||
# Always remove the backup instruction file to prevent a boot loop
|
# Always remove the backup instruction file to prevent a boot loop
|
||||||
@ -119,7 +123,7 @@ def _extract_backup(
|
|||||||
Path(
|
Path(
|
||||||
tempdir,
|
tempdir,
|
||||||
"extracted",
|
"extracted",
|
||||||
f"homeassistant.tar{'.gz' if backup_meta["compressed"] else ''}",
|
f"homeassistant.tar{'.gz' if backup_meta['compressed'] else ''}",
|
||||||
),
|
),
|
||||||
gzip=backup_meta["compressed"],
|
gzip=backup_meta["compressed"],
|
||||||
key=password_to_key(restore_content.password)
|
key=password_to_key(restore_content.password)
|
||||||
@ -142,6 +146,7 @@ def _extract_backup(
|
|||||||
config_dir,
|
config_dir,
|
||||||
dirs_exist_ok=True,
|
dirs_exist_ok=True,
|
||||||
ignore=shutil.ignore_patterns(*(keep)),
|
ignore=shutil.ignore_patterns(*(keep)),
|
||||||
|
ignore_dangling_symlinks=True,
|
||||||
)
|
)
|
||||||
elif restore_content.restore_database:
|
elif restore_content.restore_database:
|
||||||
for entry in KEEP_DATABASE:
|
for entry in KEEP_DATABASE:
|
||||||
@ -159,6 +164,23 @@ def _extract_backup(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _write_restore_result_file(
|
||||||
|
config_dir: Path, success: bool, error: Exception | None
|
||||||
|
) -> None:
|
||||||
|
"""Write the restore result file."""
|
||||||
|
result_path = config_dir.joinpath(RESTORE_BACKUP_RESULT_FILE)
|
||||||
|
result_path.write_text(
|
||||||
|
json.dumps(
|
||||||
|
{
|
||||||
|
"success": success,
|
||||||
|
"error": str(error) if error else None,
|
||||||
|
"error_type": str(type(error).__name__) if error else None,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def restore_backup(config_dir_path: str) -> bool:
|
def restore_backup(config_dir_path: str) -> bool:
|
||||||
"""Restore the backup file if any.
|
"""Restore the backup file if any.
|
||||||
|
|
||||||
@ -177,7 +199,14 @@ def restore_backup(config_dir_path: str) -> bool:
|
|||||||
restore_content=restore_content,
|
restore_content=restore_content,
|
||||||
)
|
)
|
||||||
except FileNotFoundError as err:
|
except FileNotFoundError as err:
|
||||||
raise ValueError(f"Backup file {backup_file_path} does not exist") from err
|
file_not_found = ValueError(f"Backup file {backup_file_path} does not exist")
|
||||||
|
_write_restore_result_file(config_dir, False, file_not_found)
|
||||||
|
raise file_not_found from err
|
||||||
|
except Exception as err:
|
||||||
|
_write_restore_result_file(config_dir, False, err)
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
_write_restore_result_file(config_dir, True, None)
|
||||||
if restore_content.remove_after_restore:
|
if restore_content.remove_after_restore:
|
||||||
backup_file_path.unlink(missing_ok=True)
|
backup_file_path.unlink(missing_ok=True)
|
||||||
_LOGGER.info("Restore complete, restarting")
|
_LOGGER.info("Restore complete, restarting")
|
||||||
|
@ -31,7 +31,7 @@ def _check_import_call_allowed(mapped_args: dict[str, Any]) -> bool:
|
|||||||
def _check_file_allowed(mapped_args: dict[str, Any]) -> bool:
|
def _check_file_allowed(mapped_args: dict[str, Any]) -> bool:
|
||||||
# If the file is in /proc we can ignore it.
|
# If the file is in /proc we can ignore it.
|
||||||
args = mapped_args["args"]
|
args = mapped_args["args"]
|
||||||
path = args[0] if type(args[0]) is str else str(args[0]) # noqa: E721
|
path = args[0] if type(args[0]) is str else str(args[0])
|
||||||
return path.startswith(ALLOWED_FILE_PREFIXES)
|
return path.startswith(ALLOWED_FILE_PREFIXES)
|
||||||
|
|
||||||
|
|
||||||
|
@ -112,6 +112,11 @@ with contextlib.suppress(ImportError):
|
|||||||
# Ensure anyio backend is imported to avoid it being imported in the event loop
|
# Ensure anyio backend is imported to avoid it being imported in the event loop
|
||||||
from anyio._backends import _asyncio # noqa: F401
|
from anyio._backends import _asyncio # noqa: F401
|
||||||
|
|
||||||
|
with contextlib.suppress(ImportError):
|
||||||
|
# httpx will import trio if it is installed which does
|
||||||
|
# blocking I/O in the event loop. We want to avoid that.
|
||||||
|
import trio # noqa: F401
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .runner import RuntimeConfig
|
from .runner import RuntimeConfig
|
||||||
@ -156,6 +161,16 @@ FRONTEND_INTEGRATIONS = {
|
|||||||
# integrations can be removed and database migration status is
|
# integrations can be removed and database migration status is
|
||||||
# visible in frontend
|
# visible in frontend
|
||||||
"frontend",
|
"frontend",
|
||||||
|
# Hassio is an after dependency of backup, after dependencies
|
||||||
|
# are not promoted from stage 2 to earlier stages, so we need to
|
||||||
|
# add it here. Hassio needs to be setup before backup, otherwise
|
||||||
|
# the backup integration will think we are a container/core install
|
||||||
|
# when using HAOS or Supervised install.
|
||||||
|
"hassio",
|
||||||
|
# Backup is an after dependency of frontend, after dependencies
|
||||||
|
# are not promoted from stage 2 to earlier stages, so we need to
|
||||||
|
# add it here.
|
||||||
|
"backup",
|
||||||
}
|
}
|
||||||
RECORDER_INTEGRATIONS = {
|
RECORDER_INTEGRATIONS = {
|
||||||
# Setup after frontend
|
# Setup after frontend
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
"google_assistant",
|
"google_assistant",
|
||||||
"google_assistant_sdk",
|
"google_assistant_sdk",
|
||||||
"google_cloud",
|
"google_cloud",
|
||||||
|
"google_drive",
|
||||||
"google_generative_ai_conversation",
|
"google_generative_ai_conversation",
|
||||||
"google_mail",
|
"google_mail",
|
||||||
"google_maps",
|
"google_maps",
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
"domain": "microsoft",
|
"domain": "microsoft",
|
||||||
"name": "Microsoft",
|
"name": "Microsoft",
|
||||||
"integrations": [
|
"integrations": [
|
||||||
|
"azure_data_explorer",
|
||||||
"azure_devops",
|
"azure_devops",
|
||||||
"azure_event_hub",
|
"azure_event_hub",
|
||||||
"azure_service_bus",
|
"azure_service_bus",
|
||||||
@ -10,6 +11,7 @@
|
|||||||
"microsoft_face",
|
"microsoft_face",
|
||||||
"microsoft",
|
"microsoft",
|
||||||
"msteams",
|
"msteams",
|
||||||
|
"onedrive",
|
||||||
"xbox"
|
"xbox"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -34,17 +34,17 @@
|
|||||||
"services": {
|
"services": {
|
||||||
"capture_image": {
|
"capture_image": {
|
||||||
"name": "Capture image",
|
"name": "Capture image",
|
||||||
"description": "Request a new image capture from a camera device.",
|
"description": "Requests a new image capture from a camera device.",
|
||||||
"fields": {
|
"fields": {
|
||||||
"entity_id": {
|
"entity_id": {
|
||||||
"name": "Entity",
|
"name": "Entity",
|
||||||
"description": "Entity id of the camera to request an image."
|
"description": "Entity ID of the camera to request an image from."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"change_setting": {
|
"change_setting": {
|
||||||
"name": "Change setting",
|
"name": "Change setting",
|
||||||
"description": "Change an Abode system setting.",
|
"description": "Changes an Abode system setting.",
|
||||||
"fields": {
|
"fields": {
|
||||||
"setting": {
|
"setting": {
|
||||||
"name": "Setting",
|
"name": "Setting",
|
||||||
@ -58,11 +58,11 @@
|
|||||||
},
|
},
|
||||||
"trigger_automation": {
|
"trigger_automation": {
|
||||||
"name": "Trigger automation",
|
"name": "Trigger automation",
|
||||||
"description": "Trigger an Abode automation.",
|
"description": "Triggers an Abode automation.",
|
||||||
"fields": {
|
"fields": {
|
||||||
"entity_id": {
|
"entity_id": {
|
||||||
"name": "Entity",
|
"name": "Entity",
|
||||||
"description": "Entity id of the automation to trigger."
|
"description": "Entity ID of the automation to trigger."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,5 +26,5 @@
|
|||||||
"iot_class": "local_push",
|
"iot_class": "local_push",
|
||||||
"loggers": ["aioacaia"],
|
"loggers": ["aioacaia"],
|
||||||
"quality_scale": "platinum",
|
"quality_scale": "platinum",
|
||||||
"requirements": ["aioacaia==0.1.13"]
|
"requirements": ["aioacaia==0.1.14"]
|
||||||
}
|
}
|
||||||
|
@ -12,8 +12,8 @@ import voluptuous as vol
|
|||||||
|
|
||||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
||||||
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME
|
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME
|
||||||
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
import homeassistant.helpers.config_validation as cv
|
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ from homeassistant.const import (
|
|||||||
STATE_UNKNOWN,
|
STATE_UNKNOWN,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
import homeassistant.helpers.config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import Platform
|
from homeassistant.const import Platform
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
import homeassistant.helpers.entity_registry as er
|
from homeassistant.helpers import entity_registry as er
|
||||||
|
|
||||||
from .hub import PulseHub
|
from .hub import PulseHub
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ class PulseHub:
|
|||||||
|
|
||||||
async def async_notify_update(self, update_type: aiopulse.UpdateType) -> None:
|
async def async_notify_update(self, update_type: aiopulse.UpdateType) -> None:
|
||||||
"""Evaluate entities when hub reports that update has occurred."""
|
"""Evaluate entities when hub reports that update has occurred."""
|
||||||
LOGGER.debug("Hub {update_type.name} updated")
|
LOGGER.debug("Hub %s updated", update_type.name)
|
||||||
|
|
||||||
if update_type == aiopulse.UpdateType.rollers:
|
if update_type == aiopulse.UpdateType.rollers:
|
||||||
await update_devices(self.hass, self.config_entry, self.api.rollers)
|
await update_devices(self.hass, self.config_entry, self.api.rollers)
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import telnetlib # pylint: disable=deprecated-module
|
|
||||||
from typing import Final
|
from typing import Final
|
||||||
|
|
||||||
|
import telnetlib # pylint: disable=deprecated-module
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.device_tracker import (
|
from homeassistant.components.device_tracker import (
|
||||||
@ -15,7 +15,7 @@ from homeassistant.components.device_tracker import (
|
|||||||
)
|
)
|
||||||
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
|
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
import homeassistant.helpers.config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
|
|
||||||
from .const import LEASES_REGEX
|
from .const import LEASES_REGEX
|
||||||
|
@ -34,9 +34,12 @@ from .const import (
|
|||||||
SERVICE_REMOVE_URL,
|
SERVICE_REMOVE_URL,
|
||||||
)
|
)
|
||||||
|
|
||||||
SERVICE_URL_SCHEMA = vol.Schema({vol.Required(CONF_URL): cv.url})
|
SERVICE_URL_SCHEMA = vol.Schema({vol.Required(CONF_URL): vol.Any(cv.url, cv.path)})
|
||||||
SERVICE_ADD_URL_SCHEMA = vol.Schema(
|
SERVICE_ADD_URL_SCHEMA = vol.Schema(
|
||||||
{vol.Required(CONF_NAME): cv.string, vol.Required(CONF_URL): cv.url}
|
{
|
||||||
|
vol.Required(CONF_NAME): cv.string,
|
||||||
|
vol.Required(CONF_URL): vol.Any(cv.url, cv.path),
|
||||||
|
}
|
||||||
)
|
)
|
||||||
SERVICE_REFRESH_SCHEMA = vol.Schema(
|
SERVICE_REFRESH_SCHEMA = vol.Schema(
|
||||||
{vol.Optional(CONF_FORCE, default=False): cv.boolean}
|
{vol.Optional(CONF_FORCE, default=False): cv.boolean}
|
||||||
|
@ -12,7 +12,7 @@ from homeassistant.const import (
|
|||||||
EVENT_HOMEASSISTANT_STOP,
|
EVENT_HOMEASSISTANT_STOP,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant, ServiceCall
|
from homeassistant.core import HomeAssistant, ServiceCall
|
||||||
import homeassistant.helpers.config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
|
|
||||||
from .const import CONF_ADS_VAR, DATA_ADS, DOMAIN, AdsType
|
from .const import CONF_ADS_VAR, DATA_ADS, DOMAIN, AdsType
|
||||||
|
@ -13,7 +13,7 @@ from homeassistant.components.binary_sensor import (
|
|||||||
)
|
)
|
||||||
from homeassistant.const import CONF_DEVICE_CLASS, CONF_NAME
|
from homeassistant.const import CONF_DEVICE_CLASS, CONF_NAME
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
import homeassistant.helpers.config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ from homeassistant.components.cover import (
|
|||||||
)
|
)
|
||||||
from homeassistant.const import CONF_DEVICE_CLASS, CONF_NAME
|
from homeassistant.const import CONF_DEVICE_CLASS, CONF_NAME
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
import homeassistant.helpers.config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ from homeassistant.components.light import (
|
|||||||
)
|
)
|
||||||
from homeassistant.const import CONF_NAME
|
from homeassistant.const import CONF_NAME
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
import homeassistant.helpers.config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ from homeassistant.components.select import (
|
|||||||
)
|
)
|
||||||
from homeassistant.const import CONF_NAME
|
from homeassistant.const import CONF_NAME
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
import homeassistant.helpers.config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ from homeassistant.components.sensor import (
|
|||||||
)
|
)
|
||||||
from homeassistant.const import CONF_DEVICE_CLASS, CONF_NAME, CONF_UNIT_OF_MEASUREMENT
|
from homeassistant.const import CONF_DEVICE_CLASS, CONF_NAME, CONF_UNIT_OF_MEASUREMENT
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
import homeassistant.helpers.config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType, StateType
|
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType, StateType
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ from homeassistant.components.switch import (
|
|||||||
)
|
)
|
||||||
from homeassistant.const import CONF_NAME
|
from homeassistant.const import CONF_NAME
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
import homeassistant.helpers.config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ from homeassistant.components.valve import (
|
|||||||
)
|
)
|
||||||
from homeassistant.const import CONF_DEVICE_CLASS, CONF_NAME
|
from homeassistant.const import CONF_DEVICE_CLASS, CONF_NAME
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
import homeassistant.helpers.config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ class AdvantageAirZoneMotion(AdvantageAirZoneEntity, BinarySensorEntity):
|
|||||||
def __init__(self, instance: AdvantageAirData, ac_key: str, zone_key: str) -> None:
|
def __init__(self, instance: AdvantageAirData, ac_key: str, zone_key: str) -> None:
|
||||||
"""Initialize an Advantage Air Zone Motion sensor."""
|
"""Initialize an Advantage Air Zone Motion sensor."""
|
||||||
super().__init__(instance, ac_key, zone_key)
|
super().__init__(instance, ac_key, zone_key)
|
||||||
self._attr_name = f'{self._zone["name"]} motion'
|
self._attr_name = f"{self._zone['name']} motion"
|
||||||
self._attr_unique_id += "-motion"
|
self._attr_unique_id += "-motion"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -84,7 +84,7 @@ class AdvantageAirZoneMyZone(AdvantageAirZoneEntity, BinarySensorEntity):
|
|||||||
def __init__(self, instance: AdvantageAirData, ac_key: str, zone_key: str) -> None:
|
def __init__(self, instance: AdvantageAirData, ac_key: str, zone_key: str) -> None:
|
||||||
"""Initialize an Advantage Air Zone MyZone sensor."""
|
"""Initialize an Advantage Air Zone MyZone sensor."""
|
||||||
super().__init__(instance, ac_key, zone_key)
|
super().__init__(instance, ac_key, zone_key)
|
||||||
self._attr_name = f'{self._zone["name"]} myZone'
|
self._attr_name = f"{self._zone['name']} myZone"
|
||||||
self._attr_unique_id += "-myzone"
|
self._attr_unique_id += "-myzone"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -103,7 +103,7 @@ class AdvantageAirZoneVent(AdvantageAirZoneEntity, SensorEntity):
|
|||||||
def __init__(self, instance: AdvantageAirData, ac_key: str, zone_key: str) -> None:
|
def __init__(self, instance: AdvantageAirData, ac_key: str, zone_key: str) -> None:
|
||||||
"""Initialize an Advantage Air Zone Vent Sensor."""
|
"""Initialize an Advantage Air Zone Vent Sensor."""
|
||||||
super().__init__(instance, ac_key, zone_key=zone_key)
|
super().__init__(instance, ac_key, zone_key=zone_key)
|
||||||
self._attr_name = f'{self._zone["name"]} vent'
|
self._attr_name = f"{self._zone['name']} vent"
|
||||||
self._attr_unique_id += "-vent"
|
self._attr_unique_id += "-vent"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -131,7 +131,7 @@ class AdvantageAirZoneSignal(AdvantageAirZoneEntity, SensorEntity):
|
|||||||
def __init__(self, instance: AdvantageAirData, ac_key: str, zone_key: str) -> None:
|
def __init__(self, instance: AdvantageAirData, ac_key: str, zone_key: str) -> None:
|
||||||
"""Initialize an Advantage Air Zone wireless signal sensor."""
|
"""Initialize an Advantage Air Zone wireless signal sensor."""
|
||||||
super().__init__(instance, ac_key, zone_key)
|
super().__init__(instance, ac_key, zone_key)
|
||||||
self._attr_name = f'{self._zone["name"]} signal'
|
self._attr_name = f"{self._zone['name']} signal"
|
||||||
self._attr_unique_id += "-signal"
|
self._attr_unique_id += "-signal"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -165,7 +165,7 @@ class AdvantageAirZoneTemp(AdvantageAirZoneEntity, SensorEntity):
|
|||||||
def __init__(self, instance: AdvantageAirData, ac_key: str, zone_key: str) -> None:
|
def __init__(self, instance: AdvantageAirData, ac_key: str, zone_key: str) -> None:
|
||||||
"""Initialize an Advantage Air Zone Temp Sensor."""
|
"""Initialize an Advantage Air Zone Temp Sensor."""
|
||||||
super().__init__(instance, ac_key, zone_key)
|
super().__init__(instance, ac_key, zone_key)
|
||||||
self._attr_name = f'{self._zone["name"]} temperature'
|
self._attr_name = f"{self._zone['name']} temperature"
|
||||||
self._attr_unique_id += "-temp"
|
self._attr_unique_id += "-temp"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -7,7 +7,7 @@ from typing import Final
|
|||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
import homeassistant.helpers.config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
|
|
||||||
DOMAIN: Final = "aftership"
|
DOMAIN: Final = "aftership"
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ from pyaftership import AfterShip, AfterShipException
|
|||||||
|
|
||||||
from homeassistant.components.sensor import SensorEntity
|
from homeassistant.components.sensor import SensorEntity
|
||||||
from homeassistant.core import HomeAssistant, ServiceCall
|
from homeassistant.core import HomeAssistant, ServiceCall
|
||||||
import homeassistant.helpers.config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.dispatcher import (
|
from homeassistant.helpers.dispatcher import (
|
||||||
async_dispatcher_connect,
|
async_dispatcher_connect,
|
||||||
async_dispatcher_send,
|
async_dispatcher_send,
|
||||||
|
@ -18,7 +18,9 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||||||
from . import AirGradientConfigEntry
|
from . import AirGradientConfigEntry
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
from .coordinator import AirGradientCoordinator
|
from .coordinator import AirGradientCoordinator
|
||||||
from .entity import AirGradientEntity
|
from .entity import AirGradientEntity, exception_handler
|
||||||
|
|
||||||
|
PARALLEL_UPDATES = 1
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True, kw_only=True)
|
@dataclass(frozen=True, kw_only=True)
|
||||||
@ -100,6 +102,7 @@ class AirGradientButton(AirGradientEntity, ButtonEntity):
|
|||||||
self.entity_description = description
|
self.entity_description = description
|
||||||
self._attr_unique_id = f"{coordinator.serial_number}-{description.key}"
|
self._attr_unique_id = f"{coordinator.serial_number}-{description.key}"
|
||||||
|
|
||||||
|
@exception_handler
|
||||||
async def async_press(self) -> None:
|
async def async_press(self) -> None:
|
||||||
"""Press the button."""
|
"""Press the button."""
|
||||||
await self.entity_description.press_fn(self.coordinator.client)
|
await self.entity_description.press_fn(self.coordinator.client)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
"""Config flow for Airgradient."""
|
"""Config flow for Airgradient."""
|
||||||
|
|
||||||
|
from collections.abc import Mapping
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from airgradient import (
|
from airgradient import (
|
||||||
@ -11,10 +12,15 @@ from airgradient import (
|
|||||||
from awesomeversion import AwesomeVersion
|
from awesomeversion import AwesomeVersion
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components import zeroconf
|
from homeassistant.config_entries import (
|
||||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
SOURCE_RECONFIGURE,
|
||||||
|
SOURCE_USER,
|
||||||
|
ConfigFlow,
|
||||||
|
ConfigFlowResult,
|
||||||
|
)
|
||||||
from homeassistant.const import CONF_HOST, CONF_MODEL
|
from homeassistant.const import CONF_HOST, CONF_MODEL
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
|
from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
|
|
||||||
@ -37,7 +43,7 @@ class AirGradientConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
await self.client.set_configuration_control(ConfigurationControl.LOCAL)
|
await self.client.set_configuration_control(ConfigurationControl.LOCAL)
|
||||||
|
|
||||||
async def async_step_zeroconf(
|
async def async_step_zeroconf(
|
||||||
self, discovery_info: zeroconf.ZeroconfServiceInfo
|
self, discovery_info: ZeroconfServiceInfo
|
||||||
) -> ConfigFlowResult:
|
) -> ConfigFlowResult:
|
||||||
"""Handle zeroconf discovery."""
|
"""Handle zeroconf discovery."""
|
||||||
self.data[CONF_HOST] = host = discovery_info.host
|
self.data[CONF_HOST] = host = discovery_info.host
|
||||||
@ -95,10 +101,18 @@ class AirGradientConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
await self.async_set_unique_id(
|
await self.async_set_unique_id(
|
||||||
current_measures.serial_number, raise_on_progress=False
|
current_measures.serial_number, raise_on_progress=False
|
||||||
)
|
)
|
||||||
self._abort_if_unique_id_configured()
|
if self.source == SOURCE_USER:
|
||||||
|
self._abort_if_unique_id_configured()
|
||||||
|
if self.source == SOURCE_RECONFIGURE:
|
||||||
|
self._abort_if_unique_id_mismatch()
|
||||||
await self.set_configuration_source()
|
await self.set_configuration_source()
|
||||||
return self.async_create_entry(
|
if self.source == SOURCE_USER:
|
||||||
title=current_measures.model,
|
return self.async_create_entry(
|
||||||
|
title=current_measures.model,
|
||||||
|
data={CONF_HOST: user_input[CONF_HOST]},
|
||||||
|
)
|
||||||
|
return self.async_update_reload_and_abort(
|
||||||
|
self._get_reconfigure_entry(),
|
||||||
data={CONF_HOST: user_input[CONF_HOST]},
|
data={CONF_HOST: user_input[CONF_HOST]},
|
||||||
)
|
)
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
@ -106,3 +120,9 @@ class AirGradientConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
data_schema=vol.Schema({vol.Required(CONF_HOST): str}),
|
data_schema=vol.Schema({vol.Required(CONF_HOST): str}),
|
||||||
errors=errors,
|
errors=errors,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def async_step_reconfigure(
|
||||||
|
self, user_input: Mapping[str, Any]
|
||||||
|
) -> ConfigFlowResult:
|
||||||
|
"""Handle reconfiguration."""
|
||||||
|
return await self.async_step_user()
|
||||||
|
@ -55,7 +55,11 @@ class AirGradientCoordinator(DataUpdateCoordinator[AirGradientData]):
|
|||||||
measures = await self.client.get_current_measures()
|
measures = await self.client.get_current_measures()
|
||||||
config = await self.client.get_config()
|
config = await self.client.get_config()
|
||||||
except AirGradientError as error:
|
except AirGradientError as error:
|
||||||
raise UpdateFailed(error) from error
|
raise UpdateFailed(
|
||||||
|
translation_domain=DOMAIN,
|
||||||
|
translation_key="update_error",
|
||||||
|
translation_placeholders={"error": str(error)},
|
||||||
|
) from error
|
||||||
if measures.firmware_version != self._current_version:
|
if measures.firmware_version != self._current_version:
|
||||||
device_registry = dr.async_get(self.hass)
|
device_registry = dr.async_get(self.hass)
|
||||||
device_entry = device_registry.async_get_device(
|
device_entry = device_registry.async_get_device(
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
"""Base class for AirGradient entities."""
|
"""Base class for AirGradient entities."""
|
||||||
|
|
||||||
from airgradient import get_model_name
|
from collections.abc import Callable, Coroutine
|
||||||
|
from typing import Any, Concatenate
|
||||||
|
|
||||||
|
from airgradient import AirGradientConnectionError, AirGradientError, get_model_name
|
||||||
|
|
||||||
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers.device_registry import DeviceInfo
|
from homeassistant.helpers.device_registry import DeviceInfo
|
||||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
|
|
||||||
@ -26,3 +30,31 @@ class AirGradientEntity(CoordinatorEntity[AirGradientCoordinator]):
|
|||||||
serial_number=coordinator.serial_number,
|
serial_number=coordinator.serial_number,
|
||||||
sw_version=measures.firmware_version,
|
sw_version=measures.firmware_version,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def exception_handler[_EntityT: AirGradientEntity, **_P](
|
||||||
|
func: Callable[Concatenate[_EntityT, _P], Coroutine[Any, Any, Any]],
|
||||||
|
) -> Callable[Concatenate[_EntityT, _P], Coroutine[Any, Any, None]]:
|
||||||
|
"""Decorate AirGradient calls to handle exceptions.
|
||||||
|
|
||||||
|
A decorator that wraps the passed in function, catches AirGradient errors.
|
||||||
|
"""
|
||||||
|
|
||||||
|
async def handler(self: _EntityT, *args: _P.args, **kwargs: _P.kwargs) -> None:
|
||||||
|
try:
|
||||||
|
await func(self, *args, **kwargs)
|
||||||
|
except AirGradientConnectionError as error:
|
||||||
|
raise HomeAssistantError(
|
||||||
|
translation_domain=DOMAIN,
|
||||||
|
translation_key="communication_error",
|
||||||
|
translation_placeholders={"error": str(error)},
|
||||||
|
) from error
|
||||||
|
|
||||||
|
except AirGradientError as error:
|
||||||
|
raise HomeAssistantError(
|
||||||
|
translation_domain=DOMAIN,
|
||||||
|
translation_key="unknown_error",
|
||||||
|
translation_placeholders={"error": str(error)},
|
||||||
|
) from error
|
||||||
|
|
||||||
|
return handler
|
||||||
|
@ -19,7 +19,9 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||||||
from . import AirGradientConfigEntry
|
from . import AirGradientConfigEntry
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
from .coordinator import AirGradientCoordinator
|
from .coordinator import AirGradientCoordinator
|
||||||
from .entity import AirGradientEntity
|
from .entity import AirGradientEntity, exception_handler
|
||||||
|
|
||||||
|
PARALLEL_UPDATES = 1
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True, kw_only=True)
|
@dataclass(frozen=True, kw_only=True)
|
||||||
@ -121,6 +123,7 @@ class AirGradientNumber(AirGradientEntity, NumberEntity):
|
|||||||
"""Return the state of the number."""
|
"""Return the state of the number."""
|
||||||
return self.entity_description.value_fn(self.coordinator.data.config)
|
return self.entity_description.value_fn(self.coordinator.data.config)
|
||||||
|
|
||||||
|
@exception_handler
|
||||||
async def async_set_native_value(self, value: float) -> None:
|
async def async_set_native_value(self, value: float) -> None:
|
||||||
"""Set the selected value."""
|
"""Set the selected value."""
|
||||||
await self.entity_description.set_value_fn(self.coordinator.client, int(value))
|
await self.entity_description.set_value_fn(self.coordinator.client, int(value))
|
||||||
|
@ -29,7 +29,7 @@ rules:
|
|||||||
unique-config-entry: done
|
unique-config-entry: done
|
||||||
|
|
||||||
# Silver
|
# Silver
|
||||||
action-exceptions: todo
|
action-exceptions: done
|
||||||
config-entry-unloading: done
|
config-entry-unloading: done
|
||||||
docs-configuration-parameters:
|
docs-configuration-parameters:
|
||||||
status: exempt
|
status: exempt
|
||||||
@ -38,7 +38,7 @@ rules:
|
|||||||
entity-unavailable: done
|
entity-unavailable: done
|
||||||
integration-owner: done
|
integration-owner: done
|
||||||
log-when-unavailable: done
|
log-when-unavailable: done
|
||||||
parallel-updates: todo
|
parallel-updates: done
|
||||||
reauthentication-flow:
|
reauthentication-flow:
|
||||||
status: exempt
|
status: exempt
|
||||||
comment: |
|
comment: |
|
||||||
@ -68,9 +68,9 @@ rules:
|
|||||||
entity-device-class: done
|
entity-device-class: done
|
||||||
entity-disabled-by-default: done
|
entity-disabled-by-default: done
|
||||||
entity-translations: done
|
entity-translations: done
|
||||||
exception-translations: todo
|
exception-translations: done
|
||||||
icon-translations: done
|
icon-translations: done
|
||||||
reconfiguration-flow: todo
|
reconfiguration-flow: done
|
||||||
repair-issues:
|
repair-issues:
|
||||||
status: exempt
|
status: exempt
|
||||||
comment: |
|
comment: |
|
||||||
|
@ -19,7 +19,9 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||||||
from . import AirGradientConfigEntry
|
from . import AirGradientConfigEntry
|
||||||
from .const import DOMAIN, PM_STANDARD, PM_STANDARD_REVERSE
|
from .const import DOMAIN, PM_STANDARD, PM_STANDARD_REVERSE
|
||||||
from .coordinator import AirGradientCoordinator
|
from .coordinator import AirGradientCoordinator
|
||||||
from .entity import AirGradientEntity
|
from .entity import AirGradientEntity, exception_handler
|
||||||
|
|
||||||
|
PARALLEL_UPDATES = 1
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True, kw_only=True)
|
@dataclass(frozen=True, kw_only=True)
|
||||||
@ -216,6 +218,7 @@ class AirGradientSelect(AirGradientEntity, SelectEntity):
|
|||||||
"""Return the state of the select."""
|
"""Return the state of the select."""
|
||||||
return self.entity_description.value_fn(self.coordinator.data.config)
|
return self.entity_description.value_fn(self.coordinator.data.config)
|
||||||
|
|
||||||
|
@exception_handler
|
||||||
async def async_select_option(self, option: str) -> None:
|
async def async_select_option(self, option: str) -> None:
|
||||||
"""Change the selected option."""
|
"""Change the selected option."""
|
||||||
await self.entity_description.set_value_fn(self.coordinator.client, option)
|
await self.entity_description.set_value_fn(self.coordinator.client, option)
|
||||||
|
@ -35,6 +35,8 @@ from .const import PM_STANDARD, PM_STANDARD_REVERSE
|
|||||||
from .coordinator import AirGradientCoordinator
|
from .coordinator import AirGradientCoordinator
|
||||||
from .entity import AirGradientEntity
|
from .entity import AirGradientEntity
|
||||||
|
|
||||||
|
PARALLEL_UPDATES = 0
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True, kw_only=True)
|
@dataclass(frozen=True, kw_only=True)
|
||||||
class AirGradientMeasurementSensorEntityDescription(SensorEntityDescription):
|
class AirGradientMeasurementSensorEntityDescription(SensorEntityDescription):
|
||||||
@ -137,6 +139,15 @@ MEASUREMENT_SENSOR_TYPES: tuple[AirGradientMeasurementSensorEntityDescription, .
|
|||||||
entity_registry_enabled_default=False,
|
entity_registry_enabled_default=False,
|
||||||
value_fn=lambda status: status.raw_total_volatile_organic_component,
|
value_fn=lambda status: status.raw_total_volatile_organic_component,
|
||||||
),
|
),
|
||||||
|
AirGradientMeasurementSensorEntityDescription(
|
||||||
|
key="pm02_raw",
|
||||||
|
translation_key="raw_pm02",
|
||||||
|
device_class=SensorDeviceClass.PM25,
|
||||||
|
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
|
entity_registry_enabled_default=False,
|
||||||
|
value_fn=lambda status: status.raw_pm02,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
CONFIG_SENSOR_TYPES: tuple[AirGradientConfigSensorEntityDescription, ...] = (
|
CONFIG_SENSOR_TYPES: tuple[AirGradientConfigSensorEntityDescription, ...] = (
|
||||||
|
@ -17,7 +17,9 @@
|
|||||||
"abort": {
|
"abort": {
|
||||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||||
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
|
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
|
||||||
"invalid_version": "This firmware version is unsupported. Please upgrade the firmware of the device to at least version 3.1.1."
|
"invalid_version": "This firmware version is unsupported. Please upgrade the firmware of the device to at least version 3.1.1.",
|
||||||
|
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]",
|
||||||
|
"unique_id_mismatch": "Please ensure you reconfigure against the same device."
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||||
@ -119,6 +121,9 @@
|
|||||||
"raw_nitrogen": {
|
"raw_nitrogen": {
|
||||||
"name": "Raw NOx"
|
"name": "Raw NOx"
|
||||||
},
|
},
|
||||||
|
"raw_pm02": {
|
||||||
|
"name": "Raw PM2.5"
|
||||||
|
},
|
||||||
"display_pm_standard": {
|
"display_pm_standard": {
|
||||||
"name": "[%key:component::airgradient::entity::select::display_pm_standard::name%]",
|
"name": "[%key:component::airgradient::entity::select::display_pm_standard::name%]",
|
||||||
"state": {
|
"state": {
|
||||||
@ -162,5 +167,16 @@
|
|||||||
"name": "Post data to Airgradient"
|
"name": "Post data to Airgradient"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"exceptions": {
|
||||||
|
"communication_error": {
|
||||||
|
"message": "An error occurred while communicating with the Airgradient device: {error}"
|
||||||
|
},
|
||||||
|
"unknown_error": {
|
||||||
|
"message": "An unknown error occurred while communicating with the Airgradient device: {error}"
|
||||||
|
},
|
||||||
|
"update_error": {
|
||||||
|
"message": "An error occurred while communicating with the Airgradient device: {error}"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,9 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||||||
from . import AirGradientConfigEntry
|
from . import AirGradientConfigEntry
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
from .coordinator import AirGradientCoordinator
|
from .coordinator import AirGradientCoordinator
|
||||||
from .entity import AirGradientEntity
|
from .entity import AirGradientEntity, exception_handler
|
||||||
|
|
||||||
|
PARALLEL_UPDATES = 1
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True, kw_only=True)
|
@dataclass(frozen=True, kw_only=True)
|
||||||
@ -99,11 +101,13 @@ class AirGradientSwitch(AirGradientEntity, SwitchEntity):
|
|||||||
"""Return the state of the switch."""
|
"""Return the state of the switch."""
|
||||||
return self.entity_description.value_fn(self.coordinator.data.config)
|
return self.entity_description.value_fn(self.coordinator.data.config)
|
||||||
|
|
||||||
|
@exception_handler
|
||||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||||
"""Turn the switch on."""
|
"""Turn the switch on."""
|
||||||
await self.entity_description.set_value_fn(self.coordinator.client, True)
|
await self.entity_description.set_value_fn(self.coordinator.client, True)
|
||||||
await self.coordinator.async_request_refresh()
|
await self.coordinator.async_request_refresh()
|
||||||
|
|
||||||
|
@exception_handler
|
||||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
"""Turn the switch off."""
|
"""Turn the switch off."""
|
||||||
await self.entity_description.set_value_fn(self.coordinator.client, False)
|
await self.entity_description.set_value_fn(self.coordinator.client, False)
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
from propcache import cached_property
|
from propcache.api import cached_property
|
||||||
|
|
||||||
from homeassistant.components.update import UpdateDeviceClass, UpdateEntity
|
from homeassistant.components.update import UpdateDeviceClass, UpdateEntity
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
@ -11,6 +11,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||||||
from . import AirGradientConfigEntry, AirGradientCoordinator
|
from . import AirGradientConfigEntry, AirGradientCoordinator
|
||||||
from .entity import AirGradientEntity
|
from .entity import AirGradientEntity
|
||||||
|
|
||||||
|
PARALLEL_UPDATES = 1
|
||||||
SCAN_INTERVAL = timedelta(hours=1)
|
SCAN_INTERVAL = timedelta(hours=1)
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,8 +13,8 @@ import voluptuous as vol
|
|||||||
|
|
||||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
||||||
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME
|
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME
|
||||||
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
import homeassistant.helpers.config_validation as cv
|
|
||||||
|
|
||||||
from .const import CONF_USE_NEAREST, DOMAIN, NO_AIRLY_SENSORS
|
from .const import CONF_USE_NEAREST, DOMAIN, NO_AIRLY_SENSORS
|
||||||
|
|
||||||
|
@ -18,8 +18,8 @@ from homeassistant.config_entries import (
|
|||||||
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_RADIUS
|
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_RADIUS
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
import homeassistant.helpers.config_validation as cv
|
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
|
|
||||||
|
@ -21,7 +21,6 @@ from .const import (
|
|||||||
ATTR_API_CAT_DESCRIPTION,
|
ATTR_API_CAT_DESCRIPTION,
|
||||||
ATTR_API_CAT_LEVEL,
|
ATTR_API_CAT_LEVEL,
|
||||||
ATTR_API_CATEGORY,
|
ATTR_API_CATEGORY,
|
||||||
ATTR_API_PM25,
|
|
||||||
ATTR_API_POLLUTANT,
|
ATTR_API_POLLUTANT,
|
||||||
ATTR_API_REPORT_DATE,
|
ATTR_API_REPORT_DATE,
|
||||||
ATTR_API_REPORT_HOUR,
|
ATTR_API_REPORT_HOUR,
|
||||||
@ -91,18 +90,16 @@ class AirNowDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
|||||||
max_aqi_desc = obv[ATTR_API_CATEGORY][ATTR_API_CAT_DESCRIPTION]
|
max_aqi_desc = obv[ATTR_API_CATEGORY][ATTR_API_CAT_DESCRIPTION]
|
||||||
max_aqi_poll = pollutant
|
max_aqi_poll = pollutant
|
||||||
|
|
||||||
# Copy other data from PM2.5 Value
|
# Copy Report Details
|
||||||
if obv[ATTR_API_AQI_PARAM] == ATTR_API_PM25:
|
data[ATTR_API_REPORT_DATE] = obv[ATTR_API_REPORT_DATE]
|
||||||
# Copy Report Details
|
data[ATTR_API_REPORT_HOUR] = obv[ATTR_API_REPORT_HOUR]
|
||||||
data[ATTR_API_REPORT_DATE] = obv[ATTR_API_REPORT_DATE]
|
data[ATTR_API_REPORT_TZ] = obv[ATTR_API_REPORT_TZ]
|
||||||
data[ATTR_API_REPORT_HOUR] = obv[ATTR_API_REPORT_HOUR]
|
|
||||||
data[ATTR_API_REPORT_TZ] = obv[ATTR_API_REPORT_TZ]
|
|
||||||
|
|
||||||
# Copy Station Details
|
# Copy Station Details
|
||||||
data[ATTR_API_STATE] = obv[ATTR_API_STATE]
|
data[ATTR_API_STATE] = obv[ATTR_API_STATE]
|
||||||
data[ATTR_API_STATION] = obv[ATTR_API_STATION]
|
data[ATTR_API_STATION] = obv[ATTR_API_STATION]
|
||||||
data[ATTR_API_STATION_LATITUDE] = obv[ATTR_API_STATION_LATITUDE]
|
data[ATTR_API_STATION_LATITUDE] = obv[ATTR_API_STATION_LATITUDE]
|
||||||
data[ATTR_API_STATION_LONGITUDE] = obv[ATTR_API_STATION_LONGITUDE]
|
data[ATTR_API_STATION_LONGITUDE] = obv[ATTR_API_STATION_LONGITUDE]
|
||||||
|
|
||||||
# Store Overall AQI
|
# Store Overall AQI
|
||||||
data[ATTR_API_AQI] = max_aqi
|
data[ATTR_API_AQI] = max_aqi
|
||||||
|
@ -39,45 +39,54 @@ SENSORS: dict[str, SensorEntityDescription] = {
|
|||||||
key="temp",
|
key="temp",
|
||||||
device_class=SensorDeviceClass.TEMPERATURE,
|
device_class=SensorDeviceClass.TEMPERATURE,
|
||||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
),
|
),
|
||||||
"humidity": SensorEntityDescription(
|
"humidity": SensorEntityDescription(
|
||||||
key="humidity",
|
key="humidity",
|
||||||
device_class=SensorDeviceClass.HUMIDITY,
|
device_class=SensorDeviceClass.HUMIDITY,
|
||||||
native_unit_of_measurement=PERCENTAGE,
|
native_unit_of_measurement=PERCENTAGE,
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
),
|
),
|
||||||
"pressure": SensorEntityDescription(
|
"pressure": SensorEntityDescription(
|
||||||
key="pressure",
|
key="pressure",
|
||||||
device_class=SensorDeviceClass.ATMOSPHERIC_PRESSURE,
|
device_class=SensorDeviceClass.ATMOSPHERIC_PRESSURE,
|
||||||
native_unit_of_measurement=UnitOfPressure.MBAR,
|
native_unit_of_measurement=UnitOfPressure.MBAR,
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
),
|
),
|
||||||
"battery": SensorEntityDescription(
|
"battery": SensorEntityDescription(
|
||||||
key="battery",
|
key="battery",
|
||||||
device_class=SensorDeviceClass.BATTERY,
|
device_class=SensorDeviceClass.BATTERY,
|
||||||
native_unit_of_measurement=PERCENTAGE,
|
native_unit_of_measurement=PERCENTAGE,
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
),
|
),
|
||||||
"co2": SensorEntityDescription(
|
"co2": SensorEntityDescription(
|
||||||
key="co2",
|
key="co2",
|
||||||
device_class=SensorDeviceClass.CO2,
|
device_class=SensorDeviceClass.CO2,
|
||||||
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
|
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
),
|
),
|
||||||
"voc": SensorEntityDescription(
|
"voc": SensorEntityDescription(
|
||||||
key="voc",
|
key="voc",
|
||||||
device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS_PARTS,
|
device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS_PARTS,
|
||||||
native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION,
|
native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION,
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
),
|
),
|
||||||
"light": SensorEntityDescription(
|
"light": SensorEntityDescription(
|
||||||
key="light",
|
key="light",
|
||||||
native_unit_of_measurement=PERCENTAGE,
|
native_unit_of_measurement=PERCENTAGE,
|
||||||
translation_key="light",
|
translation_key="light",
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
),
|
),
|
||||||
"virusRisk": SensorEntityDescription(
|
"virusRisk": SensorEntityDescription(
|
||||||
key="virusRisk",
|
key="virusRisk",
|
||||||
translation_key="virus_risk",
|
translation_key="virus_risk",
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
),
|
),
|
||||||
"mold": SensorEntityDescription(
|
"mold": SensorEntityDescription(
|
||||||
key="mold",
|
key="mold",
|
||||||
translation_key="mold",
|
translation_key="mold",
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
),
|
),
|
||||||
"rssi": SensorEntityDescription(
|
"rssi": SensorEntityDescription(
|
||||||
key="rssi",
|
key="rssi",
|
||||||
@ -85,16 +94,19 @@ SENSORS: dict[str, SensorEntityDescription] = {
|
|||||||
device_class=SensorDeviceClass.SIGNAL_STRENGTH,
|
device_class=SensorDeviceClass.SIGNAL_STRENGTH,
|
||||||
entity_registry_enabled_default=False,
|
entity_registry_enabled_default=False,
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
),
|
),
|
||||||
"pm1": SensorEntityDescription(
|
"pm1": SensorEntityDescription(
|
||||||
key="pm1",
|
key="pm1",
|
||||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||||
device_class=SensorDeviceClass.PM1,
|
device_class=SensorDeviceClass.PM1,
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
),
|
),
|
||||||
"pm25": SensorEntityDescription(
|
"pm25": SensorEntityDescription(
|
||||||
key="pm25",
|
key="pm25",
|
||||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||||
device_class=SensorDeviceClass.PM25,
|
device_class=SensorDeviceClass.PM25,
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,8 +155,7 @@ class AirthingsHeaterEnergySensor(
|
|||||||
self._id = airthings_device.device_id
|
self._id = airthings_device.device_id
|
||||||
self._attr_device_info = DeviceInfo(
|
self._attr_device_info = DeviceInfo(
|
||||||
configuration_url=(
|
configuration_url=(
|
||||||
"https://dashboard.airthings.com/devices/"
|
f"https://dashboard.airthings.com/devices/{airthings_device.device_id}"
|
||||||
f"{airthings_device.device_id}"
|
|
||||||
),
|
),
|
||||||
identifiers={(DOMAIN, airthings_device.device_id)},
|
identifiers={(DOMAIN, airthings_device.device_id)},
|
||||||
name=airthings_device.name,
|
name=airthings_device.name,
|
||||||
|
@ -144,7 +144,7 @@ class AirthingsConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
|
|
||||||
return self.async_create_entry(title=discovery.name, data={})
|
return self.async_create_entry(title=discovery.name, data={})
|
||||||
|
|
||||||
current_addresses = self._async_current_ids()
|
current_addresses = self._async_current_ids(include_ignore=False)
|
||||||
for discovery_info in async_discovered_service_info(self.hass):
|
for discovery_info in async_discovered_service_info(self.hass):
|
||||||
address = discovery_info.address
|
address = discovery_info.address
|
||||||
if address in current_addresses or address in self._discovered_devices:
|
if address in current_addresses or address in self._discovered_devices:
|
||||||
|
@ -67,18 +67,21 @@ SENSORS_MAPPING_TEMPLATE: dict[str, SensorEntityDescription] = {
|
|||||||
device_class=SensorDeviceClass.TEMPERATURE,
|
device_class=SensorDeviceClass.TEMPERATURE,
|
||||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
|
suggested_display_precision=1,
|
||||||
),
|
),
|
||||||
"humidity": SensorEntityDescription(
|
"humidity": SensorEntityDescription(
|
||||||
key="humidity",
|
key="humidity",
|
||||||
device_class=SensorDeviceClass.HUMIDITY,
|
device_class=SensorDeviceClass.HUMIDITY,
|
||||||
native_unit_of_measurement=PERCENTAGE,
|
native_unit_of_measurement=PERCENTAGE,
|
||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
|
suggested_display_precision=1,
|
||||||
),
|
),
|
||||||
"pressure": SensorEntityDescription(
|
"pressure": SensorEntityDescription(
|
||||||
key="pressure",
|
key="pressure",
|
||||||
device_class=SensorDeviceClass.ATMOSPHERIC_PRESSURE,
|
device_class=SensorDeviceClass.ATMOSPHERIC_PRESSURE,
|
||||||
native_unit_of_measurement=UnitOfPressure.MBAR,
|
native_unit_of_measurement=UnitOfPressure.MBAR,
|
||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
|
suggested_display_precision=1,
|
||||||
),
|
),
|
||||||
"battery": SensorEntityDescription(
|
"battery": SensorEntityDescription(
|
||||||
key="battery",
|
key="battery",
|
||||||
@ -86,24 +89,28 @@ SENSORS_MAPPING_TEMPLATE: dict[str, SensorEntityDescription] = {
|
|||||||
native_unit_of_measurement=PERCENTAGE,
|
native_unit_of_measurement=PERCENTAGE,
|
||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
|
suggested_display_precision=0,
|
||||||
),
|
),
|
||||||
"co2": SensorEntityDescription(
|
"co2": SensorEntityDescription(
|
||||||
key="co2",
|
key="co2",
|
||||||
device_class=SensorDeviceClass.CO2,
|
device_class=SensorDeviceClass.CO2,
|
||||||
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
|
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
|
||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
|
suggested_display_precision=0,
|
||||||
),
|
),
|
||||||
"voc": SensorEntityDescription(
|
"voc": SensorEntityDescription(
|
||||||
key="voc",
|
key="voc",
|
||||||
device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS_PARTS,
|
device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS_PARTS,
|
||||||
native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION,
|
native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION,
|
||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
|
suggested_display_precision=0,
|
||||||
),
|
),
|
||||||
"illuminance": SensorEntityDescription(
|
"illuminance": SensorEntityDescription(
|
||||||
key="illuminance",
|
key="illuminance",
|
||||||
translation_key="illuminance",
|
translation_key="illuminance",
|
||||||
native_unit_of_measurement=PERCENTAGE,
|
native_unit_of_measurement=PERCENTAGE,
|
||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
|
suggested_display_precision=0,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ SENSOR_DESCRIPTIONS = (
|
|||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
value_fn=lambda settings, status, measurements, history: int(
|
value_fn=lambda settings, status, measurements, history: int(
|
||||||
history.get(
|
history.get(
|
||||||
f'Outdoor {"AQI(US)" if settings["is_aqi_usa"] else "AQI(CN)"}', -1
|
f"Outdoor {'AQI(US)' if settings['is_aqi_usa'] else 'AQI(CN)'}", -1
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
translation_key="outdoor_air_quality_index",
|
translation_key="outdoor_air_quality_index",
|
||||||
|
@ -5,7 +5,14 @@ from __future__ import annotations
|
|||||||
import logging
|
import logging
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from aioairzone.const import AZD_MAC, AZD_WEBSERVER, DEFAULT_SYSTEM_ID
|
from aioairzone.const import (
|
||||||
|
AZD_FIRMWARE,
|
||||||
|
AZD_FULL_NAME,
|
||||||
|
AZD_MAC,
|
||||||
|
AZD_MODEL,
|
||||||
|
AZD_WEBSERVER,
|
||||||
|
DEFAULT_SYSTEM_ID,
|
||||||
|
)
|
||||||
from aioairzone.localapi import AirzoneLocalApi, ConnectionOptions
|
from aioairzone.localapi import AirzoneLocalApi, ConnectionOptions
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
@ -17,6 +24,7 @@ from homeassistant.helpers import (
|
|||||||
entity_registry as er,
|
entity_registry as er,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from .const import DOMAIN, MANUFACTURER
|
||||||
from .coordinator import AirzoneUpdateCoordinator
|
from .coordinator import AirzoneUpdateCoordinator
|
||||||
|
|
||||||
PLATFORMS: list[Platform] = [
|
PLATFORMS: list[Platform] = [
|
||||||
@ -78,7 +86,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: AirzoneConfigEntry) -> b
|
|||||||
options = ConnectionOptions(
|
options = ConnectionOptions(
|
||||||
entry.data[CONF_HOST],
|
entry.data[CONF_HOST],
|
||||||
entry.data[CONF_PORT],
|
entry.data[CONF_PORT],
|
||||||
entry.data.get(CONF_ID, DEFAULT_SYSTEM_ID),
|
entry.data[CONF_ID],
|
||||||
)
|
)
|
||||||
|
|
||||||
airzone = AirzoneLocalApi(aiohttp_client.async_get_clientsession(hass), options)
|
airzone = AirzoneLocalApi(aiohttp_client.async_get_clientsession(hass), options)
|
||||||
@ -88,6 +96,22 @@ async def async_setup_entry(hass: HomeAssistant, entry: AirzoneConfigEntry) -> b
|
|||||||
|
|
||||||
entry.runtime_data = coordinator
|
entry.runtime_data = coordinator
|
||||||
|
|
||||||
|
device_registry = dr.async_get(hass)
|
||||||
|
|
||||||
|
ws_data: dict[str, Any] | None = coordinator.data.get(AZD_WEBSERVER)
|
||||||
|
if ws_data is not None:
|
||||||
|
mac = ws_data.get(AZD_MAC, "")
|
||||||
|
|
||||||
|
device_registry.async_get_or_create(
|
||||||
|
config_entry_id=entry.entry_id,
|
||||||
|
connections={(dr.CONNECTION_NETWORK_MAC, mac)},
|
||||||
|
identifiers={(DOMAIN, f"{entry.entry_id}_ws")},
|
||||||
|
manufacturer=MANUFACTURER,
|
||||||
|
model=ws_data.get(AZD_MODEL),
|
||||||
|
name=ws_data.get(AZD_FULL_NAME),
|
||||||
|
sw_version=ws_data.get(AZD_FIRMWARE),
|
||||||
|
)
|
||||||
|
|
||||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
@ -96,3 +120,25 @@ async def async_setup_entry(hass: HomeAssistant, entry: AirzoneConfigEntry) -> b
|
|||||||
async def async_unload_entry(hass: HomeAssistant, entry: AirzoneConfigEntry) -> bool:
|
async def async_unload_entry(hass: HomeAssistant, entry: AirzoneConfigEntry) -> bool:
|
||||||
"""Unload a config entry."""
|
"""Unload a config entry."""
|
||||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_migrate_entry(hass: HomeAssistant, entry: AirzoneConfigEntry) -> bool:
|
||||||
|
"""Migrate an old entry."""
|
||||||
|
if entry.version == 1 and entry.minor_version < 2:
|
||||||
|
# Add missing CONF_ID
|
||||||
|
system_id = entry.data.get(CONF_ID, DEFAULT_SYSTEM_ID)
|
||||||
|
new_data = entry.data.copy()
|
||||||
|
new_data[CONF_ID] = system_id
|
||||||
|
hass.config_entries.async_update_entry(
|
||||||
|
entry,
|
||||||
|
data=new_data,
|
||||||
|
minor_version=2,
|
||||||
|
)
|
||||||
|
|
||||||
|
_LOGGER.info(
|
||||||
|
"Migration to configuration version %s.%s successful",
|
||||||
|
entry.version,
|
||||||
|
entry.minor_version,
|
||||||
|
)
|
||||||
|
|
||||||
|
return True
|
||||||
|
@ -10,12 +10,12 @@ from aioairzone.exceptions import AirzoneError, InvalidSystem
|
|||||||
from aioairzone.localapi import AirzoneLocalApi, ConnectionOptions
|
from aioairzone.localapi import AirzoneLocalApi, ConnectionOptions
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components import dhcp
|
|
||||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
||||||
from homeassistant.const import CONF_HOST, CONF_ID, CONF_PORT
|
from homeassistant.const import CONF_HOST, CONF_ID, CONF_PORT
|
||||||
from homeassistant.data_entry_flow import AbortFlow
|
from homeassistant.data_entry_flow import AbortFlow
|
||||||
from homeassistant.helpers import aiohttp_client
|
from homeassistant.helpers import aiohttp_client
|
||||||
from homeassistant.helpers.device_registry import format_mac
|
from homeassistant.helpers.device_registry import format_mac
|
||||||
|
from homeassistant.helpers.service_info.dhcp import DhcpServiceInfo
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
|
|
||||||
@ -44,6 +44,7 @@ class AirZoneConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
|
|
||||||
_discovered_ip: str | None = None
|
_discovered_ip: str | None = None
|
||||||
_discovered_mac: str | None = None
|
_discovered_mac: str | None = None
|
||||||
|
MINOR_VERSION = 2
|
||||||
|
|
||||||
async def async_step_user(
|
async def async_step_user(
|
||||||
self, user_input: dict[str, Any] | None = None
|
self, user_input: dict[str, Any] | None = None
|
||||||
@ -53,6 +54,9 @@ class AirZoneConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
errors = {}
|
errors = {}
|
||||||
|
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
|
if CONF_ID not in user_input:
|
||||||
|
user_input[CONF_ID] = DEFAULT_SYSTEM_ID
|
||||||
|
|
||||||
self._async_abort_entries_match(user_input)
|
self._async_abort_entries_match(user_input)
|
||||||
|
|
||||||
airzone = AirzoneLocalApi(
|
airzone = AirzoneLocalApi(
|
||||||
@ -60,7 +64,7 @@ class AirZoneConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
ConnectionOptions(
|
ConnectionOptions(
|
||||||
user_input[CONF_HOST],
|
user_input[CONF_HOST],
|
||||||
user_input[CONF_PORT],
|
user_input[CONF_PORT],
|
||||||
user_input.get(CONF_ID, DEFAULT_SYSTEM_ID),
|
user_input[CONF_ID],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -84,6 +88,9 @@ class AirZoneConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
)
|
)
|
||||||
|
|
||||||
title = f"Airzone {user_input[CONF_HOST]}:{user_input[CONF_PORT]}"
|
title = f"Airzone {user_input[CONF_HOST]}:{user_input[CONF_PORT]}"
|
||||||
|
if user_input[CONF_ID] != DEFAULT_SYSTEM_ID:
|
||||||
|
title += f" #{user_input[CONF_ID]}"
|
||||||
|
|
||||||
return self.async_create_entry(title=title, data=user_input)
|
return self.async_create_entry(title=title, data=user_input)
|
||||||
|
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
@ -93,7 +100,7 @@ class AirZoneConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
)
|
)
|
||||||
|
|
||||||
async def async_step_dhcp(
|
async def async_step_dhcp(
|
||||||
self, discovery_info: dhcp.DhcpServiceInfo
|
self, discovery_info: DhcpServiceInfo
|
||||||
) -> ConfigFlowResult:
|
) -> ConfigFlowResult:
|
||||||
"""Handle DHCP discovery."""
|
"""Handle DHCP discovery."""
|
||||||
self._discovered_ip = discovery_info.ip
|
self._discovered_ip = discovery_info.ip
|
||||||
|
@ -68,8 +68,9 @@ class AirzoneSystemEntity(AirzoneEntity):
|
|||||||
model=self.get_airzone_value(AZD_MODEL),
|
model=self.get_airzone_value(AZD_MODEL),
|
||||||
name=f"System {self.system_id}",
|
name=f"System {self.system_id}",
|
||||||
sw_version=self.get_airzone_value(AZD_FIRMWARE),
|
sw_version=self.get_airzone_value(AZD_FIRMWARE),
|
||||||
via_device=(DOMAIN, f"{entry.entry_id}_ws"),
|
|
||||||
)
|
)
|
||||||
|
if AZD_WEBSERVER in self.coordinator.data:
|
||||||
|
self._attr_device_info["via_device"] = (DOMAIN, f"{entry.entry_id}_ws")
|
||||||
self._attr_unique_id = entry.unique_id or entry.entry_id
|
self._attr_unique_id = entry.unique_id or entry.entry_id
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -102,8 +103,9 @@ class AirzoneHotWaterEntity(AirzoneEntity):
|
|||||||
manufacturer=MANUFACTURER,
|
manufacturer=MANUFACTURER,
|
||||||
model="DHW",
|
model="DHW",
|
||||||
name=self.get_airzone_value(AZD_NAME),
|
name=self.get_airzone_value(AZD_NAME),
|
||||||
via_device=(DOMAIN, f"{entry.entry_id}_ws"),
|
|
||||||
)
|
)
|
||||||
|
if AZD_WEBSERVER in self.coordinator.data:
|
||||||
|
self._attr_device_info["via_device"] = (DOMAIN, f"{entry.entry_id}_ws")
|
||||||
self._attr_unique_id = entry.unique_id or entry.entry_id
|
self._attr_unique_id = entry.unique_id or entry.entry_id
|
||||||
|
|
||||||
def get_airzone_value(self, key: str) -> Any:
|
def get_airzone_value(self, key: str) -> Any:
|
||||||
|
@ -7,7 +7,7 @@ from datetime import timedelta
|
|||||||
import logging
|
import logging
|
||||||
from typing import TYPE_CHECKING, Any, Final, final
|
from typing import TYPE_CHECKING, Any, Final, final
|
||||||
|
|
||||||
from propcache import cached_property
|
from propcache.api import cached_property
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
@ -24,7 +24,7 @@ from homeassistant.const import (
|
|||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.exceptions import ServiceValidationError
|
from homeassistant.exceptions import ServiceValidationError
|
||||||
import homeassistant.helpers.config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.config_validation import make_entity_service_schema
|
from homeassistant.helpers.config_validation import make_entity_service_schema
|
||||||
from homeassistant.helpers.entity import Entity, EntityDescription
|
from homeassistant.helpers.entity import Entity, EntityDescription
|
||||||
from homeassistant.helpers.entity_component import EntityComponent
|
from homeassistant.helpers.entity_component import EntityComponent
|
||||||
|
@ -23,8 +23,7 @@ from homeassistant.const import (
|
|||||||
SERVICE_ALARM_TRIGGER,
|
SERVICE_ALARM_TRIGGER,
|
||||||
)
|
)
|
||||||
from homeassistant.core import Context, HomeAssistant
|
from homeassistant.core import Context, HomeAssistant
|
||||||
from homeassistant.helpers import entity_registry as er
|
from homeassistant.helpers import config_validation as cv, entity_registry as er
|
||||||
import homeassistant.helpers.config_validation as cv
|
|
||||||
from homeassistant.helpers.entity import get_supported_features
|
from homeassistant.helpers.entity import get_supported_features
|
||||||
from homeassistant.helpers.typing import ConfigType, TemplateVarsType
|
from homeassistant.helpers.typing import ConfigType, TemplateVarsType
|
||||||
|
|
||||||
|
@ -12,8 +12,7 @@ from homeassistant.components.alarm_control_panel import (
|
|||||||
)
|
)
|
||||||
from homeassistant.const import ATTR_CODE
|
from homeassistant.const import ATTR_CODE
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers import entity_platform
|
from homeassistant.helpers import config_validation as cv, entity_platform
|
||||||
import homeassistant.helpers.config_validation as cv
|
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ from homeassistant.const import (
|
|||||||
STATE_ON,
|
STATE_ON,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
import homeassistant.helpers.config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.entity_component import EntityComponent
|
from homeassistant.helpers.entity_component import EntityComponent
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
|
|
||||||
|
@ -50,8 +50,7 @@ from homeassistant.const import (
|
|||||||
UnitOfVolume,
|
UnitOfVolume,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant, State
|
from homeassistant.core import HomeAssistant, State
|
||||||
import homeassistant.util.color as color_util
|
from homeassistant.util import color as color_util, dt as dt_util
|
||||||
import homeassistant.util.dt as dt_util
|
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
API_TEMP_UNITS,
|
API_TEMP_UNITS,
|
||||||
|
@ -474,25 +474,30 @@ class ClimateCapabilities(AlexaEntity):
|
|||||||
# If we support two modes, one being off, we allow turning on too.
|
# If we support two modes, one being off, we allow turning on too.
|
||||||
supported_features = self.entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
|
supported_features = self.entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
|
||||||
if (
|
if (
|
||||||
self.entity.domain == climate.DOMAIN
|
(
|
||||||
and climate.HVACMode.OFF
|
self.entity.domain == climate.DOMAIN
|
||||||
in (self.entity.attributes.get(climate.ATTR_HVAC_MODES) or [])
|
and climate.HVACMode.OFF
|
||||||
or self.entity.domain == climate.DOMAIN
|
in (self.entity.attributes.get(climate.ATTR_HVAC_MODES) or [])
|
||||||
and (
|
)
|
||||||
supported_features
|
or (
|
||||||
& (
|
self.entity.domain == climate.DOMAIN
|
||||||
climate.ClimateEntityFeature.TURN_ON
|
and (
|
||||||
| climate.ClimateEntityFeature.TURN_OFF
|
supported_features
|
||||||
|
& (
|
||||||
|
climate.ClimateEntityFeature.TURN_ON
|
||||||
|
| climate.ClimateEntityFeature.TURN_OFF
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
or self.entity.domain == water_heater.DOMAIN
|
or (
|
||||||
and (supported_features & water_heater.WaterHeaterEntityFeature.ON_OFF)
|
self.entity.domain == water_heater.DOMAIN
|
||||||
|
and (supported_features & water_heater.WaterHeaterEntityFeature.ON_OFF)
|
||||||
|
)
|
||||||
):
|
):
|
||||||
yield AlexaPowerController(self.entity)
|
yield AlexaPowerController(self.entity)
|
||||||
|
|
||||||
if (
|
if self.entity.domain == climate.DOMAIN or (
|
||||||
self.entity.domain == climate.DOMAIN
|
self.entity.domain == water_heater.DOMAIN
|
||||||
or self.entity.domain == water_heater.DOMAIN
|
|
||||||
and (
|
and (
|
||||||
supported_features
|
supported_features
|
||||||
& water_heater.WaterHeaterEntityFeature.OPERATION_MODE
|
& water_heater.WaterHeaterEntityFeature.OPERATION_MODE
|
||||||
|
@ -12,7 +12,7 @@ from homeassistant.const import CONF_PASSWORD
|
|||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.helpers import template
|
from homeassistant.helpers import template
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
import homeassistant.util.dt as dt_util
|
from homeassistant.util import dt as dt_util
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
API_PASSWORD,
|
API_PASSWORD,
|
||||||
|
@ -24,7 +24,7 @@ from homeassistant.core import (
|
|||||||
)
|
)
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
from homeassistant.helpers.significant_change import create_checker
|
from homeassistant.helpers.significant_change import create_checker
|
||||||
import homeassistant.util.dt as dt_util
|
from homeassistant.util import dt as dt_util
|
||||||
from homeassistant.util.json import JsonObjectType, json_loads_object
|
from homeassistant.util.json import JsonObjectType, json_loads_object
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
@ -317,9 +317,8 @@ async def async_enable_proactive_mode(
|
|||||||
|
|
||||||
if should_doorbell:
|
if should_doorbell:
|
||||||
old_state = data["old_state"]
|
old_state = data["old_state"]
|
||||||
if (
|
if new_state.domain == event.DOMAIN or (
|
||||||
new_state.domain == event.DOMAIN
|
new_state.state == STATE_ON
|
||||||
or new_state.state == STATE_ON
|
|
||||||
and (old_state is None or old_state.state != STATE_ON)
|
and (old_state is None or old_state.state != STATE_ON)
|
||||||
):
|
):
|
||||||
await async_send_doorbell_event_message(
|
await async_send_doorbell_event_message(
|
||||||
|
@ -16,7 +16,7 @@ from homeassistant.components.sensor import (
|
|||||||
)
|
)
|
||||||
from homeassistant.const import CONF_API_KEY, CONF_CURRENCY, CONF_NAME
|
from homeassistant.const import CONF_API_KEY, CONF_CURRENCY, CONF_NAME
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
import homeassistant.helpers.config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ from homeassistant.generated.amazon_polly import (
|
|||||||
SUPPORTED_REGIONS,
|
SUPPORTED_REGIONS,
|
||||||
SUPPORTED_VOICES,
|
SUPPORTED_VOICES,
|
||||||
)
|
)
|
||||||
import homeassistant.helpers.config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
|
@ -17,9 +17,8 @@ from homeassistant.const import (
|
|||||||
)
|
)
|
||||||
from homeassistant.core import Event, HomeAssistant, callback
|
from homeassistant.core import Event, HomeAssistant, callback
|
||||||
from homeassistant.exceptions import ConfigEntryNotReady
|
from homeassistant.exceptions import ConfigEntryNotReady
|
||||||
import homeassistant.helpers.device_registry as dr
|
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||||
import homeassistant.helpers.entity_registry as er
|
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
ATTR_LAST_DATA,
|
ATTR_LAST_DATA,
|
||||||
|
@ -37,8 +37,7 @@ from homeassistant.const import (
|
|||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant, ServiceCall, callback
|
from homeassistant.core import HomeAssistant, ServiceCall, callback
|
||||||
from homeassistant.exceptions import Unauthorized, UnknownUser
|
from homeassistant.exceptions import Unauthorized, UnknownUser
|
||||||
from homeassistant.helpers import discovery
|
from homeassistant.helpers import config_validation as cv, discovery
|
||||||
import homeassistant.helpers.config_validation as cv
|
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_send, dispatcher_send
|
from homeassistant.helpers.dispatcher import async_dispatcher_send, dispatcher_send
|
||||||
from homeassistant.helpers.event import async_track_time_interval
|
from homeassistant.helpers.event import async_track_time_interval
|
||||||
from homeassistant.helpers.service import async_extract_entity_ids
|
from homeassistant.helpers.service import async_extract_entity_ids
|
||||||
|
@ -14,8 +14,8 @@ from homeassistant.components.air_quality import (
|
|||||||
)
|
)
|
||||||
from homeassistant.const import CONF_NAME
|
from homeassistant.const import CONF_NAME
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
import homeassistant.helpers.config_validation as cv
|
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||||
from homeassistant.util import Throttle
|
from homeassistant.util import Throttle
|
||||||
|
@ -7,7 +7,7 @@ import voluptuous as vol
|
|||||||
from homeassistant.components import websocket_api
|
from homeassistant.components import websocket_api
|
||||||
from homeassistant.const import EVENT_HOMEASSISTANT_STARTED
|
from homeassistant.const import EVENT_HOMEASSISTANT_STARTED
|
||||||
from homeassistant.core import Event, HassJob, HomeAssistant, callback
|
from homeassistant.core import Event, HassJob, HomeAssistant, callback
|
||||||
import homeassistant.helpers.config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.event import async_call_later, async_track_time_interval
|
from homeassistant.helpers.event import async_call_later, async_track_time_interval
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
from homeassistant.util.hass_dict import HassKey
|
from homeassistant.util.hass_dict import HassKey
|
||||||
|
@ -11,6 +11,7 @@ import uuid
|
|||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
|
||||||
|
from homeassistant import config as conf_util
|
||||||
from homeassistant.components import hassio
|
from homeassistant.components import hassio
|
||||||
from homeassistant.components.api import ATTR_INSTALLATION_TYPE
|
from homeassistant.components.api import ATTR_INSTALLATION_TYPE
|
||||||
from homeassistant.components.automation import DOMAIN as AUTOMATION_DOMAIN
|
from homeassistant.components.automation import DOMAIN as AUTOMATION_DOMAIN
|
||||||
@ -22,13 +23,12 @@ from homeassistant.components.recorder import (
|
|||||||
DOMAIN as RECORDER_DOMAIN,
|
DOMAIN as RECORDER_DOMAIN,
|
||||||
get_instance as get_recorder_instance,
|
get_instance as get_recorder_instance,
|
||||||
)
|
)
|
||||||
import homeassistant.config as conf_util
|
|
||||||
from homeassistant.config_entries import SOURCE_IGNORE
|
from homeassistant.config_entries import SOURCE_IGNORE
|
||||||
from homeassistant.const import ATTR_DOMAIN, __version__ as HA_VERSION
|
from homeassistant.const import ATTR_DOMAIN, __version__ as HA_VERSION
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
|
from homeassistant.helpers import entity_registry as er
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
import homeassistant.helpers.entity_registry as er
|
|
||||||
from homeassistant.helpers.hassio import is_hassio
|
from homeassistant.helpers.hassio import is_hassio
|
||||||
from homeassistant.helpers.storage import Store
|
from homeassistant.helpers.storage import Store
|
||||||
from homeassistant.helpers.system_info import async_get_system_info
|
from homeassistant.helpers.system_info import async_get_system_info
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||||
"invalid_unique_id": "Impossible to determine a valid unique id for the device"
|
"invalid_unique_id": "Impossible to determine a valid unique ID for the device"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
@ -38,17 +38,17 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"apps": {
|
"apps": {
|
||||||
"title": "Configure Android Apps",
|
"title": "Configure Android apps",
|
||||||
"description": "Configure application id {app_id}",
|
"description": "Configure application ID {app_id}",
|
||||||
"data": {
|
"data": {
|
||||||
"app_name": "Application Name",
|
"app_name": "Application name",
|
||||||
"app_id": "Application ID",
|
"app_id": "Application ID",
|
||||||
"app_delete": "Check to delete this application"
|
"app_delete": "Check to delete this application"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"rules": {
|
"rules": {
|
||||||
"title": "Configure Android state detection rules",
|
"title": "Configure Android state detection rules",
|
||||||
"description": "Configure detection rule for application id {rule_id}",
|
"description": "Configure detection rule for application ID {rule_id}",
|
||||||
"data": {
|
"data": {
|
||||||
"rule_id": "[%key:component::androidtv::options::step::apps::data::app_id%]",
|
"rule_id": "[%key:component::androidtv::options::step::apps::data::app_id%]",
|
||||||
"rule_values": "List of state detection rules (see documentation)",
|
"rule_values": "List of state detection rules (see documentation)",
|
||||||
|
@ -14,7 +14,6 @@ from androidtvremote2 import (
|
|||||||
)
|
)
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components import zeroconf
|
|
||||||
from homeassistant.config_entries import (
|
from homeassistant.config_entries import (
|
||||||
SOURCE_REAUTH,
|
SOURCE_REAUTH,
|
||||||
ConfigEntry,
|
ConfigEntry,
|
||||||
@ -31,6 +30,7 @@ from homeassistant.helpers.selector import (
|
|||||||
SelectSelectorConfig,
|
SelectSelectorConfig,
|
||||||
SelectSelectorMode,
|
SelectSelectorMode,
|
||||||
)
|
)
|
||||||
|
from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo
|
||||||
|
|
||||||
from .const import CONF_APP_ICON, CONF_APP_NAME, CONF_APPS, CONF_ENABLE_IME, DOMAIN
|
from .const import CONF_APP_ICON, CONF_APP_NAME, CONF_APPS, CONF_ENABLE_IME, DOMAIN
|
||||||
from .helpers import create_api, get_enable_ime
|
from .helpers import create_api, get_enable_ime
|
||||||
@ -142,7 +142,7 @@ class AndroidTVRemoteConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
)
|
)
|
||||||
|
|
||||||
async def async_step_zeroconf(
|
async def async_step_zeroconf(
|
||||||
self, discovery_info: zeroconf.ZeroconfServiceInfo
|
self, discovery_info: ZeroconfServiceInfo
|
||||||
) -> ConfigFlowResult:
|
) -> ConfigFlowResult:
|
||||||
"""Handle zeroconf discovery."""
|
"""Handle zeroconf discovery."""
|
||||||
_LOGGER.debug("Android TV device found via zeroconf: %s", discovery_info)
|
_LOGGER.debug("Android TV device found via zeroconf: %s", discovery_info)
|
||||||
|
@ -15,7 +15,7 @@ from homeassistant.components.switch import (
|
|||||||
)
|
)
|
||||||
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
|
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
import homeassistant.helpers.config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||||
from homeassistant.util import Throttle
|
from homeassistant.util import Throttle
|
||||||
|
@ -12,7 +12,7 @@ import voluptuous as vol
|
|||||||
|
|
||||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
||||||
from homeassistant.const import CONF_HOST, CONF_MAC, CONF_MODEL, CONF_PORT
|
from homeassistant.const import CONF_HOST, CONF_MAC, CONF_MODEL, CONF_PORT
|
||||||
import homeassistant.helpers.config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.device_registry import format_mac
|
from homeassistant.helpers.device_registry import format_mac
|
||||||
|
|
||||||
from .const import DEFAULT_NAME, DEFAULT_PORT, DEVICE_TIMEOUT_SECONDS, DOMAIN
|
from .const import DEFAULT_NAME, DEFAULT_PORT, DEVICE_TIMEOUT_SECONDS, DOMAIN
|
||||||
|
@ -27,7 +27,7 @@ from homeassistant.core import HomeAssistant
|
|||||||
from homeassistant.exceptions import HomeAssistantError, TemplateError
|
from homeassistant.exceptions import HomeAssistantError, TemplateError
|
||||||
from homeassistant.helpers import device_registry as dr, intent, llm, template
|
from homeassistant.helpers import device_registry as dr, intent, llm, template
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.util import ulid
|
from homeassistant.util import ulid as ulid_util
|
||||||
|
|
||||||
from . import AnthropicConfigEntry
|
from . import AnthropicConfigEntry
|
||||||
from .const import (
|
from .const import (
|
||||||
@ -164,7 +164,7 @@ class AnthropicConversationEntity(
|
|||||||
]
|
]
|
||||||
|
|
||||||
if user_input.conversation_id is None:
|
if user_input.conversation_id is None:
|
||||||
conversation_id = ulid.ulid_now()
|
conversation_id = ulid_util.ulid_now()
|
||||||
messages = []
|
messages = []
|
||||||
|
|
||||||
elif user_input.conversation_id in self.history:
|
elif user_input.conversation_id in self.history:
|
||||||
@ -177,8 +177,8 @@ class AnthropicConversationEntity(
|
|||||||
# a new conversation was started. If the user picks their own, they
|
# a new conversation was started. If the user picks their own, they
|
||||||
# want to track a conversation and we respect it.
|
# want to track a conversation and we respect it.
|
||||||
try:
|
try:
|
||||||
ulid.ulid_to_bytes(user_input.conversation_id)
|
ulid_util.ulid_to_bytes(user_input.conversation_id)
|
||||||
conversation_id = ulid.ulid_now()
|
conversation_id = ulid_util.ulid_now()
|
||||||
except ValueError:
|
except ValueError:
|
||||||
conversation_id = user_input.conversation_id
|
conversation_id = user_input.conversation_id
|
||||||
|
|
||||||
|
@ -8,5 +8,5 @@
|
|||||||
"documentation": "https://www.home-assistant.io/integrations/anthropic",
|
"documentation": "https://www.home-assistant.io/integrations/anthropic",
|
||||||
"integration_type": "service",
|
"integration_type": "service",
|
||||||
"iot_class": "cloud_polling",
|
"iot_class": "cloud_polling",
|
||||||
"requirements": ["anthropic==0.31.2"]
|
"requirements": ["anthropic==0.44.0"]
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ from homeassistant.const import (
|
|||||||
EVENT_STATE_CHANGED,
|
EVENT_STATE_CHANGED,
|
||||||
)
|
)
|
||||||
from homeassistant.core import Event, EventStateChangedData, HomeAssistant
|
from homeassistant.core import Event, EventStateChangedData, HomeAssistant
|
||||||
import homeassistant.helpers.config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.entityfilter import FILTER_SCHEMA, EntityFilter
|
from homeassistant.helpers.entityfilter import FILTER_SCHEMA, EntityFilter
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
from homeassistant.util import ssl as ssl_util
|
from homeassistant.util import ssl as ssl_util
|
||||||
|
@ -10,8 +10,7 @@ import voluptuous as vol
|
|||||||
|
|
||||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
||||||
from homeassistant.const import CONF_HOST, CONF_PORT
|
from homeassistant.const import CONF_HOST, CONF_PORT
|
||||||
from homeassistant.helpers import selector
|
from homeassistant.helpers import config_validation as cv, selector
|
||||||
import homeassistant.helpers.config_validation as cv
|
|
||||||
|
|
||||||
from .const import CONNECTION_TIMEOUT, DOMAIN
|
from .const import CONNECTION_TIMEOUT, DOMAIN
|
||||||
from .coordinator import APCUPSdData
|
from .coordinator import APCUPSdData
|
||||||
|
@ -11,6 +11,7 @@ from aiohttp import web
|
|||||||
from aiohttp.web_exceptions import HTTPBadRequest
|
from aiohttp.web_exceptions import HTTPBadRequest
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant import core as ha
|
||||||
from homeassistant.auth.models import User
|
from homeassistant.auth.models import User
|
||||||
from homeassistant.auth.permissions.const import POLICY_READ
|
from homeassistant.auth.permissions.const import POLICY_READ
|
||||||
from homeassistant.components.http import (
|
from homeassistant.components.http import (
|
||||||
@ -36,7 +37,6 @@ from homeassistant.const import (
|
|||||||
URL_API_STREAM,
|
URL_API_STREAM,
|
||||||
URL_API_TEMPLATE,
|
URL_API_TEMPLATE,
|
||||||
)
|
)
|
||||||
import homeassistant.core as ha
|
|
||||||
from homeassistant.core import Event, EventStateChangedData, HomeAssistant
|
from homeassistant.core import Event, EventStateChangedData, HomeAssistant
|
||||||
from homeassistant.exceptions import (
|
from homeassistant.exceptions import (
|
||||||
InvalidEntityFormatError,
|
InvalidEntityFormatError,
|
||||||
|
@ -34,6 +34,7 @@ from homeassistant.helpers.schema_config_entry_flow import (
|
|||||||
SchemaFlowFormStep,
|
SchemaFlowFormStep,
|
||||||
SchemaOptionsFlowHandler,
|
SchemaOptionsFlowHandler,
|
||||||
)
|
)
|
||||||
|
from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo
|
||||||
|
|
||||||
from .const import CONF_CREDENTIALS, CONF_IDENTIFIERS, CONF_START_OFF, DOMAIN
|
from .const import CONF_CREDENTIALS, CONF_IDENTIFIERS, CONF_START_OFF, DOMAIN
|
||||||
|
|
||||||
@ -204,7 +205,7 @@ class AppleTVConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
)
|
)
|
||||||
|
|
||||||
async def async_step_zeroconf(
|
async def async_step_zeroconf(
|
||||||
self, discovery_info: zeroconf.ZeroconfServiceInfo
|
self, discovery_info: ZeroconfServiceInfo
|
||||||
) -> ConfigFlowResult:
|
) -> ConfigFlowResult:
|
||||||
"""Handle device found via zeroconf."""
|
"""Handle device found via zeroconf."""
|
||||||
if discovery_info.ip_address.version == 6:
|
if discovery_info.ip_address.version == 6:
|
||||||
|
@ -40,7 +40,7 @@ from homeassistant.components.media_player import (
|
|||||||
from homeassistant.const import CONF_NAME
|
from homeassistant.const import CONF_NAME
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
import homeassistant.util.dt as dt_util
|
from homeassistant.util import dt as dt_util
|
||||||
|
|
||||||
from . import AppleTvConfigEntry, AppleTVManager
|
from . import AppleTvConfigEntry, AppleTVManager
|
||||||
from .browse_media import build_app_list
|
from .browse_media import build_app_list
|
||||||
|
@ -26,8 +26,11 @@ from homeassistant.const import (
|
|||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers import collection, config_entry_oauth2_flow
|
from homeassistant.helpers import (
|
||||||
import homeassistant.helpers.config_validation as cv
|
collection,
|
||||||
|
config_entry_oauth2_flow,
|
||||||
|
config_validation as cv,
|
||||||
|
)
|
||||||
from homeassistant.helpers.storage import Store
|
from homeassistant.helpers.storage import Store
|
||||||
from homeassistant.helpers.typing import ConfigType, VolDictType
|
from homeassistant.helpers.typing import ConfigType, VolDictType
|
||||||
from homeassistant.loader import (
|
from homeassistant.loader import (
|
||||||
@ -38,7 +41,7 @@ from homeassistant.loader import (
|
|||||||
from homeassistant.util import slugify
|
from homeassistant.util import slugify
|
||||||
from homeassistant.util.hass_dict import HassKey
|
from homeassistant.util.hass_dict import HassKey
|
||||||
|
|
||||||
__all__ = ["ClientCredential", "AuthorizationServer", "async_import_client_credential"]
|
__all__ = ["AuthorizationServer", "ClientCredential", "async_import_client_credential"]
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -143,8 +146,6 @@ class ApplicationCredentialsStorageCollection(collection.DictStorageCollection):
|
|||||||
|
|
||||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||||
"""Set up Application Credentials."""
|
"""Set up Application Credentials."""
|
||||||
hass.data[DOMAIN] = {}
|
|
||||||
|
|
||||||
id_manager = collection.IDManager()
|
id_manager = collection.IDManager()
|
||||||
storage_collection = ApplicationCredentialsStorageCollection(
|
storage_collection = ApplicationCredentialsStorageCollection(
|
||||||
Store(hass, STORAGE_VERSION, STORAGE_KEY),
|
Store(hass, STORAGE_VERSION, STORAGE_KEY),
|
||||||
|
@ -17,7 +17,7 @@ from homeassistant.components.notify import (
|
|||||||
)
|
)
|
||||||
from homeassistant.const import CONF_URL
|
from homeassistant.const import CONF_URL
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
import homeassistant.helpers.config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
@ -10,7 +10,7 @@ import voluptuous as vol
|
|||||||
|
|
||||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
||||||
from homeassistant.const import CONF_HOST, CONF_PORT
|
from homeassistant.const import CONF_HOST, CONF_PORT
|
||||||
import homeassistant.helpers.config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.device_registry import format_mac
|
from homeassistant.helpers.device_registry import format_mac
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
|
@ -11,7 +11,7 @@ from pyaprilaire.const import MODELS, Attribute, FunctionalDomain
|
|||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
|
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
|
||||||
import homeassistant.helpers.device_registry as dr
|
from homeassistant.helpers import device_registry as dr
|
||||||
from homeassistant.helpers.device_registry import DeviceInfo
|
from homeassistant.helpers.device_registry import DeviceInfo
|
||||||
from homeassistant.helpers.update_coordinator import BaseDataUpdateCoordinatorProtocol
|
from homeassistant.helpers.update_coordinator import BaseDataUpdateCoordinatorProtocol
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ from homeassistant.const import (
|
|||||||
EVENT_HOMEASSISTANT_STOP,
|
EVENT_HOMEASSISTANT_STOP,
|
||||||
)
|
)
|
||||||
from homeassistant.core import Event, HomeAssistant
|
from homeassistant.core import Event, HomeAssistant
|
||||||
import homeassistant.helpers.config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||||
from homeassistant.util import slugify
|
from homeassistant.util import slugify
|
||||||
|
|
||||||
|
@ -8,8 +8,8 @@ import voluptuous as vol
|
|||||||
|
|
||||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
||||||
from homeassistant.const import CONF_IP_ADDRESS, CONF_PORT
|
from homeassistant.const import CONF_IP_ADDRESS, CONF_PORT
|
||||||
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
import homeassistant.helpers.config_validation as cv
|
|
||||||
|
|
||||||
from .const import DEFAULT_PORT, DOMAIN
|
from .const import DEFAULT_PORT, DOMAIN
|
||||||
|
|
||||||
|
@ -29,6 +29,8 @@ class ApSystemsSensorData:
|
|||||||
class ApSystemsDataCoordinator(DataUpdateCoordinator[ApSystemsSensorData]):
|
class ApSystemsDataCoordinator(DataUpdateCoordinator[ApSystemsSensorData]):
|
||||||
"""Coordinator used for all sensors."""
|
"""Coordinator used for all sensors."""
|
||||||
|
|
||||||
|
device_version: str
|
||||||
|
|
||||||
def __init__(self, hass: HomeAssistant, api: APsystemsEZ1M) -> None:
|
def __init__(self, hass: HomeAssistant, api: APsystemsEZ1M) -> None:
|
||||||
"""Initialize my coordinator."""
|
"""Initialize my coordinator."""
|
||||||
super().__init__(
|
super().__init__(
|
||||||
@ -46,6 +48,7 @@ class ApSystemsDataCoordinator(DataUpdateCoordinator[ApSystemsSensorData]):
|
|||||||
raise UpdateFailed from None
|
raise UpdateFailed from None
|
||||||
self.api.max_power = device_info.maxPower
|
self.api.max_power = device_info.maxPower
|
||||||
self.api.min_power = device_info.minPower
|
self.api.min_power = device_info.minPower
|
||||||
|
self.device_version = device_info.devVer
|
||||||
|
|
||||||
async def _async_update_data(self) -> ApSystemsSensorData:
|
async def _async_update_data(self) -> ApSystemsSensorData:
|
||||||
try:
|
try:
|
||||||
|
@ -21,7 +21,8 @@ class ApSystemsEntity(Entity):
|
|||||||
"""Initialize the APsystems entity."""
|
"""Initialize the APsystems entity."""
|
||||||
self._attr_device_info = DeviceInfo(
|
self._attr_device_info = DeviceInfo(
|
||||||
identifiers={(DOMAIN, data.device_id)},
|
identifiers={(DOMAIN, data.device_id)},
|
||||||
serial_number=data.device_id,
|
|
||||||
manufacturer="APsystems",
|
manufacturer="APsystems",
|
||||||
model="EZ1-M",
|
model="EZ1-M",
|
||||||
|
serial_number=data.device_id,
|
||||||
|
sw_version=data.coordinator.device_version.split(" ")[1],
|
||||||
)
|
)
|
||||||
|
@ -19,7 +19,7 @@ from homeassistant.const import (
|
|||||||
UnitOfTemperature,
|
UnitOfTemperature,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
import homeassistant.helpers.config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||||
|
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