Compare commits

..

13 Commits

Author SHA1 Message Date
Robert Resch bb39a68390 test 2026-06-22 14:34:36 +00:00
Robert Resch 0abbefae33 Fix 2026-06-22 14:09:42 +00:00
Robert Resch 5c107a2a47 Merge branch 'dev' into edenhaus/requirements-lock 2026-06-22 13:57:28 +00:00
Robert Resch c9f55f6b3c Activate lock file maintenance 2026-06-22 13:53:10 +00:00
Robert Resch 1bb5e08149 Remove hashes for now 2026-06-22 13:52:01 +00:00
Robert Resch 489b84192f Regerenate lock files 2026-06-16 08:50:22 +00:00
Robert Resch 6af0fbcc01 Merge branch 'dev' into edenhaus/requirements-lock 2026-06-16 08:47:17 +00:00
Robert Resch d6edd6b5ee use lock file in production 2026-06-11 14:35:59 +00:00
Robert Resch d37ef21cf8 Let renovate update the lock file 2026-06-10 13:31:37 +00:00
Robert Resch 686daaed85 Combine hash 2026-06-08 11:45:35 +00:00
Robert Resch bce05bd2c7 Generate always lockfiles 2026-06-08 11:43:02 +00:00
Robert Resch 33363ef07b Fix autogenerated comment 2026-06-08 11:37:49 +00:00
Robert Resch 32a9e115da Generate and use requirements lock files 2026-06-08 10:37:19 +00:00
7165 changed files with 91287 additions and 115948 deletions
+2 -3
View File
@@ -18,8 +18,7 @@ homeassistant/generated/*.py linguist-generated=true
pylint/plugins/pylint_home_assistant/generated/*.py linguist-generated=true
machine/* linguist-generated=true
mypy.ini linguist-generated=true
requirements.txt linguist-generated=true
requirements_all.txt linguist-generated=true
requirements_test_pre_commit.txt linguist-generated=true
requirements*.txt linguist-generated=true
requirements_*.lock linguist-generated=true
script/hassfest/docker/Dockerfile linguist-generated=true
.github/workflows/*.lock.yml linguist-generated=true merge=ours
-1
View File
@@ -53,4 +53,3 @@ This repository contains the core of Home Assistant, a Python 3 based home autom
- When validation guarantees a dict key exists, prefer direct key access (`data["key"]`) instead of `.get("key")` so contract violations are surfaced instead of silently masked.
- Keep comments concise. Prefer one short line stating the non-obvious constraint, or no comment at all.
- Do not add comments that just restate the code on the following line(s) (e.g. `# Check if initialized` above `if self.initialized:`). Comments should only explain why (non-obvious constraints, surprising behavior, or workarounds), never what. Never add comments that justify a change by referencing what the code looked like before.
- Do not add section or divider comments (e.g. `# --- XYZ Triggers ---`) inside or outside of functions, since those can easily become stale and be misleading.
+10
View File
@@ -5,6 +5,7 @@
"enabledManagers": [
"pep621",
"pip_requirements",
"pip-compile",
"pre-commit",
"dockerfile",
"custom.regex",
@@ -22,6 +23,10 @@
]
},
"pip-compile": {
"managerFilePatterns": ["/(^|/)requirements[\\w_-]*\\.lock$/"]
},
"dockerfile": {
"managerFilePatterns": ["/^Dockerfile$/"]
},
@@ -63,6 +68,11 @@
"automerge": false,
"lockFileMaintenance": {
"enabled": true,
"schedule": ["before 6am on monday"]
},
"vulnerabilityAlerts": {
"enabled": false
},
+257 -253
View File
@@ -38,7 +38,7 @@ jobs:
base_image_version: ${{ env.BASE_IMAGE_VERSION }}
steps:
- name: Checkout the repository
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
@@ -53,10 +53,10 @@ jobs:
with:
type: ${{ env.BUILD_TYPE }}
- name: Verify version
uses: home-assistant/actions/helpers/verify-version@master # zizmor: ignore[unpinned-uses]
with:
ignore-dev: true
# - name: Verify version
# uses: home-assistant/actions/helpers/verify-version@master # zizmor: ignore[unpinned-uses]
# with:
# ignore-dev: true
- name: Fail if translations files are checked in
run: |
@@ -102,7 +102,7 @@ jobs:
os: ubuntu-24.04-arm
steps:
- name: Checkout the repository
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
@@ -157,6 +157,8 @@ jobs:
homeassistant/package_constraints.txt
sed -i "s|home-assistant-frontend==.*||" requirements_all.txt
# Drop the released pin from the lock file; the nightly wheel is installed separately
sed -i "/^home-assistant-frontend==/,/^[^[:space:]#]/ { /^home-assistant-frontend==/d; /^[[:space:]#]/d }" requirements_all.lock
fi
if [[ "$(ls home_assistant_intents*.whl)" =~ ^home_assistant_intents-(.*)-py3-none-any.whl$ ]]; then
@@ -175,6 +177,8 @@ jobs:
homeassistant/package_constraints.txt
sed -i "s|home-assistant-intents==.*||" requirements_all.txt requirements.txt
# Drop the released pin from the lock file; the nightly wheel is installed separately
sed -i "/^home-assistant-intents==/,/^[^[:space:]#]/ { /^home-assistant-intents==/d; /^[[:space:]#]/d }" requirements_all.lock
fi
- name: Download translations
@@ -245,7 +249,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout the repository
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
@@ -282,276 +286,276 @@ jobs:
push: true
version: ${{ needs.init.outputs.version }}
publish_ha:
name: Publish version files
environment: ${{ needs.init.outputs.channel }}
if: github.repository_owner == 'home-assistant'
needs: ["init", "build_machine"]
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout the repository
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
# publish_ha:
# name: Publish version files
# environment: ${{ needs.init.outputs.channel }}
# if: github.repository_owner == 'home-assistant'
# needs: ["init", "build_machine"]
# runs-on: ubuntu-latest
# permissions:
# contents: read
# steps:
# - name: Checkout the repository
# uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
# with:
# persist-credentials: false
- name: Initialize git
uses: home-assistant/actions/helpers/git-init@master # zizmor: ignore[unpinned-uses]
with:
name: ${{ secrets.GIT_NAME }}
email: ${{ secrets.GIT_EMAIL }}
token: ${{ secrets.GIT_TOKEN }}
# - name: Initialize git
# uses: home-assistant/actions/helpers/git-init@master # zizmor: ignore[unpinned-uses]
# with:
# name: ${{ secrets.GIT_NAME }}
# email: ${{ secrets.GIT_EMAIL }}
# token: ${{ secrets.GIT_TOKEN }}
- name: Update version file
uses: home-assistant/actions/helpers/version-push@master # zizmor: ignore[unpinned-uses]
with:
key: "homeassistant[]"
key-description: "Home Assistant Core"
version: ${{ needs.init.outputs.version }}
channel: ${{ needs.init.outputs.channel }}
exclude-list: '["odroid-xu","qemuarm","qemux86","raspberrypi","raspberrypi2","raspberrypi3","raspberrypi4","tinker"]'
# - name: Update version file
# uses: home-assistant/actions/helpers/version-push@master # zizmor: ignore[unpinned-uses]
# with:
# key: "homeassistant[]"
# key-description: "Home Assistant Core"
# version: ${{ needs.init.outputs.version }}
# channel: ${{ needs.init.outputs.channel }}
# exclude-list: '["odroid-xu","qemuarm","qemux86","raspberrypi","raspberrypi2","raspberrypi3","raspberrypi4","tinker"]'
- name: Update version file (stable -> beta)
if: needs.init.outputs.channel == 'stable'
uses: home-assistant/actions/helpers/version-push@master # zizmor: ignore[unpinned-uses]
with:
key: "homeassistant[]"
key-description: "Home Assistant Core"
version: ${{ needs.init.outputs.version }}
channel: beta
exclude-list: '["odroid-xu","qemuarm","qemux86","raspberrypi","raspberrypi2","raspberrypi3","raspberrypi4","tinker"]'
# - name: Update version file (stable -> beta)
# if: needs.init.outputs.channel == 'stable'
# uses: home-assistant/actions/helpers/version-push@master # zizmor: ignore[unpinned-uses]
# with:
# key: "homeassistant[]"
# key-description: "Home Assistant Core"
# version: ${{ needs.init.outputs.version }}
# channel: beta
# exclude-list: '["odroid-xu","qemuarm","qemux86","raspberrypi","raspberrypi2","raspberrypi3","raspberrypi4","tinker"]'
publish_container:
name: Publish to ${{ matrix.registry }}
environment: ${{ needs.init.outputs.channel }}
if: github.repository_owner == 'home-assistant'
needs: ["init", "build_base"]
runs-on: ubuntu-latest
permissions:
contents: read # To check out the repository
packages: write # To push to GHCR
id-token: write # For cosign signing
strategy:
fail-fast: false
matrix:
registry: ["ghcr.io/home-assistant", "docker.io/homeassistant"]
steps:
- name: Install Cosign
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
# publish_container:
# name: Publish to ${{ matrix.registry }}
# environment: ${{ needs.init.outputs.channel }}
# if: github.repository_owner == 'home-assistant'
# needs: ["init", "build_base"]
# runs-on: ubuntu-latest
# permissions:
# contents: read # To check out the repository
# packages: write # To push to GHCR
# id-token: write # For cosign signing
# strategy:
# fail-fast: false
# matrix:
# registry: ["ghcr.io/home-assistant", "docker.io/homeassistant"]
# steps:
# - name: Install Cosign
# uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
- name: Login to DockerHub
if: matrix.registry == 'docker.io/homeassistant'
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
# - name: Login to DockerHub
# if: matrix.registry == 'docker.io/homeassistant'
# uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
# with:
# username: ${{ secrets.DOCKERHUB_USERNAME }}
# password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
# - name: Login to GitHub Container Registry
# uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
# with:
# registry: ghcr.io
# username: ${{ github.repository_owner }}
# password: ${{ secrets.GITHUB_TOKEN }}
- name: Verify architecture image signatures
shell: bash
env:
ARCHITECTURES: ${{ needs.init.outputs.architectures }}
VERSION: ${{ needs.init.outputs.version }}
run: |
ARCHS=$(echo "${ARCHITECTURES}" | jq -r '.[]')
for arch in $ARCHS; do
echo "Verifying ${arch} image signature..."
cosign verify \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
--certificate-identity-regexp https://github.com/home-assistant/core/.* \
"ghcr.io/home-assistant/${arch}-homeassistant:${VERSION}"
done
echo "✓ All images verified successfully"
# - name: Verify architecture image signatures
# shell: bash
# env:
# ARCHITECTURES: ${{ needs.init.outputs.architectures }}
# VERSION: ${{ needs.init.outputs.version }}
# run: |
# ARCHS=$(echo "${ARCHITECTURES}" | jq -r '.[]')
# for arch in $ARCHS; do
# echo "Verifying ${arch} image signature..."
# cosign verify \
# --certificate-oidc-issuer https://token.actions.githubusercontent.com \
# --certificate-identity-regexp https://github.com/home-assistant/core/.* \
# "ghcr.io/home-assistant/${arch}-homeassistant:${VERSION}"
# done
# echo "✓ All images verified successfully"
# Generate all Docker tags based on version string
# Version format: YYYY.MM.PATCH, YYYY.MM.PATCHbN (beta), or YYYY.MM.PATCH.devYYYYMMDDHHMM (dev)
# Examples:
# 2025.12.1 (stable) -> tags: 2025.12.1, 2025.12, stable, latest, beta, rc
# 2025.12.0b3 (beta) -> tags: 2025.12.0b3, beta, rc
# 2025.12.0.dev202511250240 -> tags: 2025.12.0.dev202511250240, dev
- name: Generate Docker metadata
id: meta
uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0
with:
images: ${{ matrix.registry }}/home-assistant
sep-tags: ","
tags: |
type=raw,value=${{ needs.init.outputs.version }},priority=9999
type=raw,value=dev,enable=${{ contains(needs.init.outputs.version, 'd') }}
type=raw,value=beta,enable=${{ !contains(needs.init.outputs.version, 'd') }}
type=raw,value=rc,enable=${{ !contains(needs.init.outputs.version, 'd') }}
type=raw,value=stable,enable=${{ !contains(needs.init.outputs.version, 'd') && !contains(needs.init.outputs.version, 'b') }}
type=raw,value=latest,enable=${{ !contains(needs.init.outputs.version, 'd') && !contains(needs.init.outputs.version, 'b') }}
type=semver,pattern={{major}}.{{minor}},value=${{ needs.init.outputs.version }},enable=${{ !contains(needs.init.outputs.version, 'd') && !contains(needs.init.outputs.version, 'b') }}
# # Generate all Docker tags based on version string
# # Version format: YYYY.MM.PATCH, YYYY.MM.PATCHbN (beta), or YYYY.MM.PATCH.devYYYYMMDDHHMM (dev)
# # Examples:
# # 2025.12.1 (stable) -> tags: 2025.12.1, 2025.12, stable, latest, beta, rc
# # 2025.12.0b3 (beta) -> tags: 2025.12.0b3, beta, rc
# # 2025.12.0.dev202511250240 -> tags: 2025.12.0.dev202511250240, dev
# - name: Generate Docker metadata
# id: meta
# uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0
# with:
# images: ${{ matrix.registry }}/home-assistant
# sep-tags: ","
# tags: |
# type=raw,value=${{ needs.init.outputs.version }},priority=9999
# type=raw,value=dev,enable=${{ contains(needs.init.outputs.version, 'd') }}
# type=raw,value=beta,enable=${{ !contains(needs.init.outputs.version, 'd') }}
# type=raw,value=rc,enable=${{ !contains(needs.init.outputs.version, 'd') }}
# type=raw,value=stable,enable=${{ !contains(needs.init.outputs.version, 'd') && !contains(needs.init.outputs.version, 'b') }}
# type=raw,value=latest,enable=${{ !contains(needs.init.outputs.version, 'd') && !contains(needs.init.outputs.version, 'b') }}
# type=semver,pattern={{major}}.{{minor}},value=${{ needs.init.outputs.version }},enable=${{ !contains(needs.init.outputs.version, 'd') && !contains(needs.init.outputs.version, 'b') }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v3.7.1
# - name: Set up Docker Buildx
# uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v3.7.1
- name: Copy architecture images to DockerHub
if: matrix.registry == 'docker.io/homeassistant'
shell: bash
env:
ARCHITECTURES: ${{ needs.init.outputs.architectures }}
VERSION: ${{ needs.init.outputs.version }}
run: |
# Use imagetools to copy image blobs directly between registries
# This preserves provenance/attestations and seems to be much faster than pull/push
ARCHS=$(echo "${ARCHITECTURES}" | jq -r '.[]')
for arch in $ARCHS; do
echo "Copying ${arch} image to DockerHub..."
for attempt in 1 2 3; do
if docker buildx imagetools create \
--tag "docker.io/homeassistant/${arch}-homeassistant:${VERSION}" \
"ghcr.io/home-assistant/${arch}-homeassistant:${VERSION}"; then
break
fi
echo "Attempt ${attempt} failed, retrying in 10 seconds..."
sleep 10
if [ "${attempt}" -eq 3 ]; then
echo "Failed after 3 attempts"
exit 1
fi
done
cosign sign --yes "docker.io/homeassistant/${arch}-homeassistant:${VERSION}"
done
# - name: Copy architecture images to DockerHub
# if: matrix.registry == 'docker.io/homeassistant'
# shell: bash
# env:
# ARCHITECTURES: ${{ needs.init.outputs.architectures }}
# VERSION: ${{ needs.init.outputs.version }}
# run: |
# # Use imagetools to copy image blobs directly between registries
# # This preserves provenance/attestations and seems to be much faster than pull/push
# ARCHS=$(echo "${ARCHITECTURES}" | jq -r '.[]')
# for arch in $ARCHS; do
# echo "Copying ${arch} image to DockerHub..."
# for attempt in 1 2 3; do
# if docker buildx imagetools create \
# --tag "docker.io/homeassistant/${arch}-homeassistant:${VERSION}" \
# "ghcr.io/home-assistant/${arch}-homeassistant:${VERSION}"; then
# break
# fi
# echo "Attempt ${attempt} failed, retrying in 10 seconds..."
# sleep 10
# if [ "${attempt}" -eq 3 ]; then
# echo "Failed after 3 attempts"
# exit 1
# fi
# done
# cosign sign --yes "docker.io/homeassistant/${arch}-homeassistant:${VERSION}"
# done
- name: Create and push multi-arch manifests
shell: bash
env:
ARCHITECTURES: ${{ needs.init.outputs.architectures }}
REGISTRY: ${{ matrix.registry }}
VERSION: ${{ needs.init.outputs.version }}
META_TAGS: ${{ steps.meta.outputs.tags }}
run: |
# Build list of architecture images dynamically
ARCHS=$(echo "${ARCHITECTURES}" | jq -r '.[]')
ARCH_IMAGES=()
for arch in $ARCHS; do
ARCH_IMAGES+=("${REGISTRY}/${arch}-homeassistant:${VERSION}")
done
# - name: Create and push multi-arch manifests
# shell: bash
# env:
# ARCHITECTURES: ${{ needs.init.outputs.architectures }}
# REGISTRY: ${{ matrix.registry }}
# VERSION: ${{ needs.init.outputs.version }}
# META_TAGS: ${{ steps.meta.outputs.tags }}
# run: |
# # Build list of architecture images dynamically
# ARCHS=$(echo "${ARCHITECTURES}" | jq -r '.[]')
# ARCH_IMAGES=()
# for arch in $ARCHS; do
# ARCH_IMAGES+=("${REGISTRY}/${arch}-homeassistant:${VERSION}")
# done
# Build list of all tags for single manifest creation
# Note: Using sep-tags=',' in metadata-action for easier parsing
TAG_ARGS=()
IFS=',' read -ra TAGS <<< "${META_TAGS}"
for tag in "${TAGS[@]}"; do
TAG_ARGS+=("--tag" "${tag}")
done
# # Build list of all tags for single manifest creation
# # Note: Using sep-tags=',' in metadata-action for easier parsing
# TAG_ARGS=()
# IFS=',' read -ra TAGS <<< "${META_TAGS}"
# for tag in "${TAGS[@]}"; do
# TAG_ARGS+=("--tag" "${tag}")
# done
# Create manifest with ALL tags in a single operation (much faster!)
echo "Creating multi-arch manifest with tags: ${TAGS[*]}"
docker buildx imagetools create "${TAG_ARGS[@]}" "${ARCH_IMAGES[@]}"
# # Create manifest with ALL tags in a single operation (much faster!)
# echo "Creating multi-arch manifest with tags: ${TAGS[*]}"
# docker buildx imagetools create "${TAG_ARGS[@]}" "${ARCH_IMAGES[@]}"
# Sign each tag separately (signing requires individual tag names)
echo "Signing all tags..."
for tag in "${TAGS[@]}"; do
echo "Signing ${tag}"
cosign sign --yes "${tag}"
done
# # Sign each tag separately (signing requires individual tag names)
# echo "Signing all tags..."
# for tag in "${TAGS[@]}"; do
# echo "Signing ${tag}"
# cosign sign --yes "${tag}"
# done
echo "All manifests created and signed successfully"
# echo "All manifests created and signed successfully"
build_python:
name: Build PyPi package
environment: ${{ needs.init.outputs.channel }}
needs: ["init", "build_base"]
runs-on: ubuntu-latest
permissions:
contents: read # To check out the repository
id-token: write # For PyPI trusted publishing
if: github.repository_owner == 'home-assistant' && needs.init.outputs.publish == 'true'
steps:
- name: Checkout the repository
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
# build_python:
# name: Build PyPi package
# environment: ${{ needs.init.outputs.channel }}
# needs: ["init", "build_base"]
# runs-on: ubuntu-latest
# permissions:
# contents: read # To check out the repository
# id-token: write # For PyPI trusted publishing
# if: github.repository_owner == 'home-assistant' && needs.init.outputs.publish == 'true'
# steps:
# - name: Checkout the repository
# uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
# with:
# persist-credentials: false
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version-file: ".python-version"
# - name: Set up Python
# uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
# with:
# python-version-file: ".python-version"
- name: Download translations
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: translations
# - name: Download translations
# uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
# with:
# name: translations
- name: Extract translations
run: |
tar xvf translations.tar.gz
rm translations.tar.gz
# - name: Extract translations
# run: |
# tar xvf translations.tar.gz
# rm translations.tar.gz
- name: Build package
shell: bash
run: |
# Remove dist, build, and homeassistant.egg-info
# when build locally for testing!
pip install build
python -m build
# - name: Build package
# shell: bash
# run: |
# # Remove dist, build, and homeassistant.egg-info
# # when build locally for testing!
# pip install build
# python -m build
- name: Upload package to PyPI
uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
with:
skip-existing: true
# - name: Upload package to PyPI
# uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
# with:
# skip-existing: true
hassfest-image:
name: Build and test hassfest image
runs-on: ubuntu-latest
permissions:
contents: read # To check out the repository
packages: write # To push to GHCR
attestations: write # For build provenance attestation
id-token: write # For build provenance attestation
needs: ["init"]
if: github.repository_owner == 'home-assistant'
env:
HASSFEST_IMAGE_NAME: ghcr.io/home-assistant/hassfest
HASSFEST_IMAGE_TAG: ghcr.io/home-assistant/hassfest:${{ needs.init.outputs.version }}
steps:
- name: Checkout repository
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
# hassfest-image:
# name: Build and test hassfest image
# runs-on: ubuntu-latest
# permissions:
# contents: read # To check out the repository
# packages: write # To push to GHCR
# attestations: write # For build provenance attestation
# id-token: write # For build provenance attestation
# needs: ["init"]
# if: github.repository_owner == 'home-assistant'
# env:
# HASSFEST_IMAGE_NAME: ghcr.io/home-assistant/hassfest
# HASSFEST_IMAGE_TAG: ghcr.io/home-assistant/hassfest:${{ needs.init.outputs.version }}
# steps:
# - name: Checkout repository
# uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
# with:
# persist-credentials: false
- name: Login to GitHub Container Registry
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
# - name: Login to GitHub Container Registry
# uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
# with:
# registry: ghcr.io
# username: ${{ github.repository_owner }}
# password: ${{ secrets.GITHUB_TOKEN }}
- name: Build Docker image
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
with:
context: . # So action will not pull the repository again
file: ./script/hassfest/docker/Dockerfile
load: true
tags: ${{ env.HASSFEST_IMAGE_TAG }}
# - name: Build Docker image
# uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
# with:
# context: . # So action will not pull the repository again
# file: ./script/hassfest/docker/Dockerfile
# load: true
# tags: ${{ env.HASSFEST_IMAGE_TAG }}
- name: Run hassfest against core
run: docker run --rm -v "${GITHUB_WORKSPACE}":/github/workspace "${HASSFEST_IMAGE_TAG}" --core-path=/github/workspace
# - name: Run hassfest against core
# run: docker run --rm -v "${GITHUB_WORKSPACE}":/github/workspace "${HASSFEST_IMAGE_TAG}" --core-path=/github/workspace
- name: Push Docker image
if: needs.init.outputs.channel != 'dev' && needs.init.outputs.publish == 'true'
id: push
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
with:
context: . # So action will not pull the repository again
file: ./script/hassfest/docker/Dockerfile
push: true
tags: ${{ env.HASSFEST_IMAGE_TAG }},${{ env.HASSFEST_IMAGE_NAME }}:latest
# - name: Push Docker image
# if: needs.init.outputs.channel != 'dev' && needs.init.outputs.publish == 'true'
# id: push
# uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
# with:
# context: . # So action will not pull the repository again
# file: ./script/hassfest/docker/Dockerfile
# push: true
# tags: ${{ env.HASSFEST_IMAGE_TAG }},${{ env.HASSFEST_IMAGE_NAME }}:latest
- name: Generate artifact attestation
if: needs.init.outputs.channel != 'dev' && needs.init.outputs.publish == 'true'
uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0
with:
subject-name: ${{ env.HASSFEST_IMAGE_NAME }}
subject-digest: ${{ steps.push.outputs.digest }}
push-to-registry: true
# - name: Generate artifact attestation
# if: needs.init.outputs.channel != 'dev' && needs.init.outputs.publish == 'true'
# uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0
# with:
# subject-name: ${{ env.HASSFEST_IMAGE_NAME }}
# subject-digest: ${{ steps.push.outputs.digest }}
# push-to-registry: true
@@ -8,11 +8,15 @@ name: Check requirements (deterministic)
# yamllint disable-line rule:truthy
on:
pull_request:
types: [opened, synchronize, reopened]
paths:
- "requirements*.txt"
- "homeassistant/package_constraints.txt"
# Auto-trigger on PRs that touch tracked requirement files is disabled
# for now while we iterate — testing the workflow_run handoff to the
# agentic stage is hard with an auto-trigger. Re-enable once the chain
# has been validated end-to-end.
# pull_request:
# types: [opened, synchronize, reopened]
# paths:
# - "**/requirements*.txt"
# - "homeassistant/package_constraints.txt"
workflow_dispatch:
inputs:
pull_request_number:
@@ -36,7 +40,7 @@ jobs:
timeout-minutes: 10
steps:
- name: Check out code from GitHub
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Set up Python
+4 -4
View File
@@ -31,7 +31,7 @@
# - GITHUB_TOKEN
#
# Custom actions used:
# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# - actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
# - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
@@ -155,7 +155,7 @@ jobs:
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
sparse-checkout: |
@@ -404,7 +404,7 @@ jobs:
echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json"
} >> "$GITHUB_OUTPUT"
- name: Checkout repository
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Create gh-aw temp directory
@@ -1236,7 +1236,7 @@ jobs:
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
- name: Checkout repository for patch context
if: needs.agent.outputs.has_patch == 'true'
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
# --- Threat Detection ---
+24 -29
View File
@@ -98,21 +98,17 @@ jobs:
skip_coverage: ${{ steps.info.outputs.skip_coverage }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Generate partial Python venv restore key
id: generate_python_cache_key
env:
HASH_REQUIREMENTS_TEST: ${{ hashFiles('requirements_test.txt', 'requirements_test_pre_commit.txt') }}
HASH_REQUIREMENTS: ${{ hashFiles('requirements.txt') }}
HASH_REQUIREMENTS_ALL: ${{ hashFiles('requirements_all.txt') }}
HASH_PACKAGE_CONSTRAINTS: ${{ hashFiles('homeassistant/package_constraints.txt') }}
HASH_GEN_REQUIREMENTS: ${{ hashFiles('script/gen_requirements_all.py') }}
HASH_FILES: ${{ hashFiles('requirements_ci.lock', 'script/gen_requirements_all.py') }}
run: |
# Include HA_SHORT_VERSION to force the immediate creation
# of a new uv cache entry after a version bump.
echo "key=venv-${CACHE_VERSION}-${HA_SHORT_VERSION}-${HASH_REQUIREMENTS_TEST}-${HASH_REQUIREMENTS}-${HASH_REQUIREMENTS_ALL}-${HASH_PACKAGE_CONSTRAINTS}-${HASH_GEN_REQUIREMENTS}" >> $GITHUB_OUTPUT
echo "key=venv-${CACHE_VERSION}-${HA_SHORT_VERSION}-${HASH_FILES}" >> $GITHUB_OUTPUT
- name: Filter for core changes
uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
id: core
@@ -264,7 +260,7 @@ jobs:
&& github.event.inputs.audit-licenses-only != 'true'
steps:
- name: Check out code from GitHub
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Register problem matchers
@@ -291,7 +287,7 @@ jobs:
&& github.event.inputs.audit-licenses-only != 'true'
steps:
- name: Check out code from GitHub
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Run zizmor
@@ -318,7 +314,7 @@ jobs:
- script/hassfest/docker/Dockerfile
steps:
- name: Check out code from GitHub
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Register hadolint problem matcher
@@ -341,7 +337,7 @@ jobs:
python-version: ${{ fromJson(needs.info.outputs.python_versions) }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Set up Python ${{ matrix.python-version }}
@@ -365,7 +361,7 @@ jobs:
RUNNER_OS: ${{ runner.os }}
RUNNER_ARCH: ${{ runner.arch }}
PYTHON_VERSION: ${{ steps.python.outputs.python-version }}
HASH_FILES: ${{ hashFiles('requirements.txt', 'requirements_all.txt', 'requirements_test.txt', 'homeassistant/package_constraints.txt') }}
HASH_FILES: ${{ hashFiles('requirements_ci.lock') }}
run: |
partial_key="${RUNNER_OS}-${RUNNER_ARCH}-${PYTHON_VERSION}-uv-"
echo "partial_key=${partial_key}" >> $GITHUB_OUTPUT
@@ -414,8 +410,7 @@ jobs:
python -m venv venv
. venv/bin/activate
python --version
uv pip install -r requirements.txt
uv pip install -r requirements_all.txt -r requirements_test.txt
uv pip install -r requirements_ci.lock
uv pip install -e . --config-settings editable_mode=compat
- name: Dump pip freeze
run: |
@@ -469,7 +464,7 @@ jobs:
&& github.event.inputs.audit-licenses-only != 'true'
steps:
- name: Check out code from GitHub
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Install additional OS dependencies
@@ -512,7 +507,7 @@ jobs:
&& github.event.inputs.audit-licenses-only != 'true'
steps:
- name: Check out code from GitHub
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Set up Python
@@ -548,7 +543,7 @@ jobs:
&& github.event.inputs.audit-licenses-only != 'true'
steps:
- name: Check out code from GitHub
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Set up Python
@@ -576,7 +571,7 @@ jobs:
&& github.event_name == 'pull_request'
steps:
- name: Check out code from GitHub
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Dependency review
@@ -603,7 +598,7 @@ jobs:
python-version: ${{ fromJson(needs.info.outputs.python_versions) }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Set up Python ${{ matrix.python-version }}
@@ -654,7 +649,7 @@ jobs:
|| github.event.inputs.pylint-only == 'true'
steps:
- name: Check out code from GitHub
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Set up Python
@@ -707,7 +702,7 @@ jobs:
&& (needs.info.outputs.tests_glob || needs.info.outputs.test_full_suite == 'true')
steps:
- name: Check out code from GitHub
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Set up Python
@@ -758,7 +753,7 @@ jobs:
|| github.event.inputs.mypy-only == 'true'
steps:
- name: Check out code from GitHub
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Set up Python
@@ -825,7 +820,7 @@ jobs:
- base
steps:
- name: Check out code from GitHub
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Install additional OS dependencies
@@ -889,7 +884,7 @@ jobs:
group: ${{ fromJson(needs.info.outputs.test_groups) }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Install additional OS dependencies
@@ -1030,7 +1025,7 @@ jobs:
mariadb-group: ${{ fromJson(needs.info.outputs.mariadb_groups) }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Install additional OS dependencies
@@ -1179,7 +1174,7 @@ jobs:
postgresql-group: ${{ fromJson(needs.info.outputs.postgresql_groups) }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Install additional OS dependencies
@@ -1317,7 +1312,7 @@ jobs:
if: needs.info.outputs.skip_coverage != 'true'
steps:
- name: Check out code from GitHub
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Download all coverage artifacts
@@ -1355,7 +1350,7 @@ jobs:
group: ${{ fromJson(needs.info.outputs.test_groups) }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Install additional OS dependencies
@@ -1476,7 +1471,7 @@ jobs:
- pytest-partial
steps:
- name: Check out code from GitHub
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Download all coverage artifacts
+1 -1
View File
@@ -23,7 +23,7 @@ jobs:
steps:
- name: Check out code from GitHub
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
+1 -1
View File
@@ -22,7 +22,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
+3 -3
View File
@@ -29,7 +29,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
@@ -116,7 +116,7 @@ jobs:
os: ubuntu-24.04-arm
steps:
- name: Checkout the repository
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
@@ -167,7 +167,7 @@ jobs:
os: ubuntu-24.04-arm
steps:
- name: Checkout the repository
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
+2
View File
@@ -43,6 +43,7 @@ homeassistant.components
homeassistant.components.abode.*
homeassistant.components.acaia.*
homeassistant.components.accuweather.*
homeassistant.components.acer_projector.*
homeassistant.components.acmeda.*
homeassistant.components.actiontec.*
homeassistant.components.actron_air.*
@@ -76,6 +77,7 @@ homeassistant.components.amberelectric.*
homeassistant.components.ambient_network.*
homeassistant.components.ambient_station.*
homeassistant.components.amcrest.*
homeassistant.components.ampio.*
homeassistant.components.analytics.*
homeassistant.components.analytics_insights.*
homeassistant.components.android_ip_webcam.*
-1
View File
@@ -42,4 +42,3 @@ This repository contains the core of Home Assistant, a Python 3 based home autom
- When validation guarantees a dict key exists, prefer direct key access (`data["key"]`) instead of `.get("key")` so contract violations are surfaced instead of silently masked.
- Keep comments concise. Prefer one short line stating the non-obvious constraint, or no comment at all.
- Do not add comments that just restate the code on the following line(s) (e.g. `# Check if initialized` above `if self.initialized:`). Comments should only explain why (non-obvious constraints, surprising behavior, or workarounds), never what. Never add comments that justify a change by referencing what the code looked like before.
- Do not add section or divider comments (e.g. `# --- XYZ Triggers ---`) inside or outside of functions, since those can easily become stale and be misleading.
Generated
+8 -6
View File
@@ -230,6 +230,7 @@ CLAUDE.md @home-assistant/core
/tests/components/battery/ @home-assistant/core
/homeassistant/components/bayesian/ @HarvsG
/tests/components/bayesian/ @HarvsG
/homeassistant/components/beewi_smartclim/ @alemuro
/homeassistant/components/binary_sensor/ @home-assistant/core
/tests/components/binary_sensor/ @home-assistant/core
/homeassistant/components/bizkaibus/ @UgaitzEtxebarria
@@ -253,8 +254,8 @@ CLAUDE.md @home-assistant/core
/tests/components/bond/ @bdraco @prystupa @joshs85 @marciogranzotto
/homeassistant/components/bosch_alarm/ @mag1024 @sanjay900
/tests/components/bosch_alarm/ @mag1024 @sanjay900
/homeassistant/components/bosch_shc/ @tschamm @mosandlt
/tests/components/bosch_shc/ @tschamm @mosandlt
/homeassistant/components/bosch_shc/ @tschamm
/tests/components/bosch_shc/ @tschamm
/homeassistant/components/brands/ @home-assistant/core
/tests/components/brands/ @home-assistant/core
/homeassistant/components/braviatv/ @bieniu @Drafteed
@@ -1602,8 +1603,8 @@ CLAUDE.md @home-assistant/core
/tests/components/sensorpush/ @bdraco
/homeassistant/components/sensorpush_cloud/ @sstallion
/tests/components/sensorpush_cloud/ @sstallion
/homeassistant/components/sensoterra/ @SanderBakkumCuriousInc @curious-florian @markruys
/tests/components/sensoterra/ @SanderBakkumCuriousInc @curious-florian @markruys
/homeassistant/components/sensoterra/ @markruys
/tests/components/sensoterra/ @markruys
/homeassistant/components/sentry/ @dcramer @frenck
/tests/components/sentry/ @dcramer @frenck
/homeassistant/components/senz/ @milanmeu
@@ -1711,8 +1712,8 @@ CLAUDE.md @home-assistant/core
/tests/components/sql/ @gjohansson-ST @dougiteixeira
/homeassistant/components/squeezebox/ @rajlaud @pssc @peteS-UK
/tests/components/squeezebox/ @rajlaud @pssc @peteS-UK
/homeassistant/components/srp_energy/ @briglx @ammmze
/tests/components/srp_energy/ @briglx @ammmze
/homeassistant/components/srp_energy/ @briglx
/tests/components/srp_energy/ @briglx
/homeassistant/components/starline/ @anonym-tsk
/tests/components/starline/ @anonym-tsk
/homeassistant/components/statistics/ @ThomDietrich @gjohansson-ST
@@ -1899,6 +1900,7 @@ CLAUDE.md @home-assistant/core
/tests/components/unifi_direct/ @tofuSCHNITZEL
/homeassistant/components/unifi_discovery/ @RaHehl
/tests/components/unifi_discovery/ @RaHehl
/homeassistant/components/unifiled/ @florisvdk
/homeassistant/components/unifiprotect/ @RaHehl
/tests/components/unifiprotect/ @RaHehl
/homeassistant/components/upb/ @gwww
+5 -11
View File
@@ -29,24 +29,18 @@ COPY rootfs /
COPY --from=ghcr.io/alexxit/go2rtc:1.9.14@sha256:675c318b23c06fd862a61d262240c9a63436b4050d177ffc68a32710d9e05bae /usr/local/bin/go2rtc /bin/go2rtc
## Setup Home Assistant Core dependencies
COPY --parents requirements.txt homeassistant/package_constraints.txt homeassistant/
COPY --parents requirements_all.lock home_assistant_frontend-* home_assistant_intents-* homeassistant/
RUN \
# Verify go2rtc can be executed
go2rtc --version \
# Install uv at the version pinned in the requirements file
&& pip3 install --no-cache-dir "uv==$(awk -F'==' '/^uv==/{print $2}' homeassistant/requirements.txt)" \
&& uv pip install \
--no-build \
-r homeassistant/requirements.txt
COPY requirements_all.txt home_assistant_frontend-* home_assistant_intents-* homeassistant/
RUN \
if ls homeassistant/home_assistant_*.whl 1> /dev/null 2>&1; then \
# Install uv at the version pinned in the lock file
&& pip3 install --no-cache-dir "uv==$(awk -F'[= ;]+' '/^uv==/{print $2}' homeassistant/requirements_all.lock)" \
&& if ls homeassistant/home_assistant_*.whl 1> /dev/null 2>&1; then \
uv pip install homeassistant/home_assistant_*.whl; \
fi \
&& uv pip install \
--no-build \
-r homeassistant/requirements_all.txt
-r homeassistant/requirements_all.lock
## Setup Home Assistant Core
COPY --parents LICENSE* README* homeassistant/ pyproject.toml homeassistant/
+1 -3
View File
@@ -6,7 +6,7 @@ from collections.abc import Mapping
from datetime import datetime, timedelta
from functools import partial
import time
from typing import Any, cast, override
from typing import Any, cast
import jwt
@@ -109,7 +109,6 @@ class AuthManagerFlowManager(
super().__init__(hass)
self.auth_manager = auth_manager
@override
async def async_create_flow(
self,
handler_key: tuple[str, str],
@@ -123,7 +122,6 @@ class AuthManagerFlowManager(
raise KeyError(f"Unknown auth provider {handler_key}")
return await auth_provider.async_login_flow(context)
@override
async def async_finish_flow(
self,
flow: FlowHandler[AuthFlowContext, AuthFlowResult, tuple[str, str]],
-1
View File
@@ -39,7 +39,6 @@ class _PyJWSWithLoadCache(PyJWS):
# We only ever have a global instance of this class
# so we do not have to worry about the LRU growing
# each time we create a new instance.
@override
def _load(self, jwt: str | bytes) -> tuple[bytes, bytes, dict, bytes]:
"""Load a JWS."""
return super()._load(jwt)
@@ -1,6 +1,6 @@
"""Example auth module."""
from typing import Any, override
from typing import Any
import voluptuous as vol
@@ -35,7 +35,6 @@ class InsecureExampleModule(MultiFactorAuthModule):
self._data = config["data"]
@property
@override
def input_schema(self) -> vol.Schema:
"""Validate login flow input data."""
return vol.Schema({vol.Required("pin"): str})
@@ -45,7 +44,6 @@ class InsecureExampleModule(MultiFactorAuthModule):
"""Validate async_setup_user input data."""
return vol.Schema({vol.Required("pin"): str})
@override
async def async_setup_flow(self, user_id: str) -> SetupFlow:
"""Return a data entry flow handler for setup module.
@@ -53,7 +51,6 @@ class InsecureExampleModule(MultiFactorAuthModule):
"""
return SetupFlow(self, self.setup_schema, user_id)
@override
async def async_setup_user(self, user_id: str, setup_data: Any) -> Any:
"""Set up user to use mfa module."""
# data shall has been validate in caller
@@ -67,7 +64,6 @@ class InsecureExampleModule(MultiFactorAuthModule):
self._data.append({"user_id": user_id, "pin": pin})
@override
async def async_depose_user(self, user_id: str) -> None:
"""Remove user from mfa module."""
found = None
@@ -78,12 +74,10 @@ class InsecureExampleModule(MultiFactorAuthModule):
if found:
self._data.remove(found)
@override
async def async_is_user_setup(self, user_id: str) -> bool:
"""Return whether user is setup."""
return any(data["user_id"] == user_id for data in self._data)
@override
async def async_validate(self, user_id: str, user_input: dict[str, Any]) -> bool:
"""Return True if validation passed."""
return any(
+1 -8
View File
@@ -5,7 +5,7 @@ Sending HOTP through notify service
import asyncio
import logging
from typing import Any, cast, override
from typing import Any, cast
import attr
import voluptuous as vol
@@ -107,7 +107,6 @@ class NotifyAuthModule(MultiFactorAuthModule):
self._init_lock = asyncio.Lock()
@property
@override
def input_schema(self) -> vol.Schema:
"""Validate login flow input data."""
return vol.Schema({vol.Required(INPUT_FIELD_CODE): str})
@@ -160,7 +159,6 @@ class NotifyAuthModule(MultiFactorAuthModule):
return sorted(unordered_services)
@override
async def async_setup_flow(self, user_id: str) -> NotifySetupFlow:
"""Return a data entry flow handler for setup module.
@@ -170,7 +168,6 @@ class NotifyAuthModule(MultiFactorAuthModule):
self, self.input_schema, user_id, self.aync_get_available_notify_services()
)
@override
async def async_setup_user(self, user_id: str, setup_data: Any) -> Any:
"""Set up auth module for user."""
if self._user_settings is None:
@@ -184,7 +181,6 @@ class NotifyAuthModule(MultiFactorAuthModule):
await self._async_save()
@override
async def async_depose_user(self, user_id: str) -> None:
"""Depose auth module for user."""
if self._user_settings is None:
@@ -194,7 +190,6 @@ class NotifyAuthModule(MultiFactorAuthModule):
if self._user_settings.pop(user_id, None):
await self._async_save()
@override
async def async_is_user_setup(self, user_id: str) -> bool:
"""Return whether user is setup."""
if self._user_settings is None:
@@ -203,7 +198,6 @@ class NotifyAuthModule(MultiFactorAuthModule):
return user_id in self._user_settings
@override
async def async_validate(self, user_id: str, user_input: dict[str, Any]) -> bool:
"""Return True if validation passed."""
if self._user_settings is None:
@@ -289,7 +283,6 @@ class NotifySetupFlow(SetupFlow[NotifyAuthModule]):
self._notify_service: str | None = None
self._target: str | None = None
@override
async def async_step_init(
self, user_input: dict[str, str] | None = None
) -> FlowResult:
+1 -8
View File
@@ -2,7 +2,7 @@
import asyncio
from io import BytesIO
from typing import Any, cast, override
from typing import Any, cast
import voluptuous as vol
@@ -87,7 +87,6 @@ class TotpAuthModule(MultiFactorAuthModule):
self._init_lock = asyncio.Lock()
@property
@override
def input_schema(self) -> vol.Schema:
"""Validate login flow input data."""
return vol.Schema({vol.Required(INPUT_FIELD_CODE): str})
@@ -116,7 +115,6 @@ class TotpAuthModule(MultiFactorAuthModule):
self._users[user_id] = ota_secret # type: ignore[index]
return ota_secret
@override
async def async_setup_flow(self, user_id: str) -> TotpSetupFlow:
"""Return a data entry flow handler for setup module.
@@ -126,7 +124,6 @@ class TotpAuthModule(MultiFactorAuthModule):
assert user is not None
return TotpSetupFlow(self, self.input_schema, user)
@override
async def async_setup_user(self, user_id: str, setup_data: Any) -> str:
"""Set up auth module for user."""
if self._users is None:
@@ -139,7 +136,6 @@ class TotpAuthModule(MultiFactorAuthModule):
await self._async_save()
return result
@override
async def async_depose_user(self, user_id: str) -> None:
"""Depose auth module for user."""
if self._users is None:
@@ -148,7 +144,6 @@ class TotpAuthModule(MultiFactorAuthModule):
if self._users.pop(user_id, None): # type: ignore[union-attr]
await self._async_save()
@override
async def async_is_user_setup(self, user_id: str) -> bool:
"""Return whether user is setup."""
if self._users is None:
@@ -156,7 +151,6 @@ class TotpAuthModule(MultiFactorAuthModule):
return user_id in self._users # type: ignore[operator]
@override
async def async_validate(self, user_id: str, user_input: dict[str, Any]) -> bool:
"""Return True if validation passed."""
if self._users is None:
@@ -195,7 +189,6 @@ class TotpSetupFlow(SetupFlow[TotpAuthModule]):
super().__init__(auth_module, setup_schema, user.id)
self._user = user
@override
async def async_step_init(
self, user_input: dict[str, str] | None = None
) -> FlowResult:
+1 -6
View File
@@ -1,7 +1,7 @@
"""Permissions for Home Assistant."""
from collections.abc import Callable, Iterable
from typing import TYPE_CHECKING, override
from typing import TYPE_CHECKING
import voluptuous as vol
@@ -68,17 +68,14 @@ class PolicyPermissions(AbstractPermissions):
self._policy = policy
self._perm_lookup = perm_lookup
@override
def access_all_entities(self, key: str) -> bool:
"""Check if we have a certain access to all entities."""
return test_all(self._policy.get(CAT_ENTITIES), key)
@override
def _entity_func(self) -> Callable[[str, str], bool]:
"""Return a function that can test entity access."""
return compile_entities(self._policy.get(CAT_ENTITIES), self._perm_lookup)
@override
def __eq__(self, other: object) -> bool:
"""Equals check."""
return isinstance(other, PolicyPermissions) and other._policy == self._policy
@@ -87,12 +84,10 @@ class PolicyPermissions(AbstractPermissions):
class _OwnerPermissions(AbstractPermissions):
"""Owner permissions."""
@override
def access_all_entities(self, key: str) -> bool:
"""Check if we have a certain access to all entities."""
return True
@override
def _entity_func(self) -> Callable[[str, str], bool]:
"""Return a function that can test entity access."""
return lambda entity_id, key: True
+1 -5
View File
@@ -4,7 +4,7 @@ import asyncio
from collections.abc import Mapping
import logging
import os
from typing import Any, override
from typing import Any
import voluptuous as vol
@@ -57,7 +57,6 @@ class CommandLineAuthProvider(AuthProvider):
super().__init__(*args, **kwargs)
self._user_meta: dict[str, dict[str, Any]] = {}
@override
async def async_login_flow(
self, context: AuthFlowContext | None
) -> CommandLineLoginFlow:
@@ -106,7 +105,6 @@ class CommandLineAuthProvider(AuthProvider):
meta[key] = value
self._user_meta[username] = meta
@override
async def async_get_or_create_credentials(
self, flow_result: Mapping[str, str]
) -> Credentials:
@@ -119,7 +117,6 @@ class CommandLineAuthProvider(AuthProvider):
# Create new credentials.
return self.async_create_credentials({"username": username})
@override
async def async_user_meta_for_credentials(
self, credentials: Credentials
) -> UserMeta:
@@ -139,7 +136,6 @@ class CommandLineAuthProvider(AuthProvider):
class CommandLineLoginFlow(LoginFlow[CommandLineAuthProvider]):
"""Handler for the login flow."""
@override
async def async_step_init(
self, user_input: dict[str, str] | None = None
) -> AuthFlowResult:
@@ -4,7 +4,7 @@ import asyncio
import base64
from collections.abc import Mapping
import logging
from typing import Any, cast, override
from typing import Any, cast
import bcrypt
import voluptuous as vol
@@ -302,7 +302,6 @@ class HassAuthProvider(AuthProvider):
self.data: Data | None = None
self._init_lock = asyncio.Lock()
@override
async def async_initialize(self) -> None:
"""Initialize the auth provider."""
async with self._init_lock:
@@ -313,7 +312,6 @@ class HassAuthProvider(AuthProvider):
await data.async_load()
self.data = data
@override
async def async_login_flow(self, context: AuthFlowContext | None) -> HassLoginFlow:
"""Return a flow to login."""
return HassLoginFlow(self)
@@ -371,7 +369,6 @@ class HassAuthProvider(AuthProvider):
)
await self.data.async_save()
@override
async def async_get_or_create_credentials(
self, flow_result: Mapping[str, str]
) -> Credentials:
@@ -390,7 +387,6 @@ class HassAuthProvider(AuthProvider):
# Create new credentials.
return self.async_create_credentials({"username": username})
@override
async def async_user_meta_for_credentials(
self, credentials: Credentials
) -> UserMeta:
@@ -414,7 +410,6 @@ class HassAuthProvider(AuthProvider):
class HassLoginFlow(LoginFlow[HassAuthProvider]):
"""Handler for the login flow."""
@override
async def async_step_init(
self, user_input: dict[str, str] | None = None
) -> AuthFlowResult:
@@ -2,7 +2,6 @@
from collections.abc import Mapping
import hmac
from typing import override
import voluptuous as vol
@@ -34,7 +33,6 @@ class InvalidAuthError(HomeAssistantError):
class ExampleAuthProvider(AuthProvider):
"""Example auth provider based on hardcoded usernames and passwords."""
@override
async def async_login_flow(
self, context: AuthFlowContext | None
) -> ExampleLoginFlow:
@@ -63,7 +61,6 @@ class ExampleAuthProvider(AuthProvider):
):
raise InvalidAuthError
@override
async def async_get_or_create_credentials(
self, flow_result: Mapping[str, str]
) -> Credentials:
@@ -77,7 +74,6 @@ class ExampleAuthProvider(AuthProvider):
# Create new credentials.
return self.async_create_credentials({"username": username})
@override
async def async_user_meta_for_credentials(
self, credentials: Credentials
) -> UserMeta:
@@ -99,7 +95,6 @@ class ExampleAuthProvider(AuthProvider):
class ExampleLoginFlow(LoginFlow[ExampleAuthProvider]):
"""Handler for the login flow."""
@override
async def async_step_init(
self, user_input: dict[str, str] | None = None
) -> AuthFlowResult:
@@ -13,7 +13,7 @@ from ipaddress import (
ip_address,
ip_network,
)
from typing import Any, cast, override
from typing import Any, cast
import voluptuous as vol
@@ -98,12 +98,10 @@ class TrustedNetworksAuthProvider(AuthProvider):
]
@property
@override
def support_mfa(self) -> bool:
"""Trusted Networks auth provider does not support MFA."""
return False
@override
async def async_login_flow(
self, context: AuthFlowContext | None
) -> TrustedNetworksLoginFlow:
@@ -146,7 +144,6 @@ class TrustedNetworksAuthProvider(AuthProvider):
self.config[CONF_ALLOW_BYPASS_LOGIN],
)
@override
async def async_get_or_create_credentials(
self, flow_result: Mapping[str, str]
) -> Credentials:
@@ -175,7 +172,6 @@ class TrustedNetworksAuthProvider(AuthProvider):
# We only allow login as exist user
raise InvalidUserError
@override
async def async_user_meta_for_credentials(
self, credentials: Credentials
) -> UserMeta:
@@ -207,7 +203,6 @@ class TrustedNetworksAuthProvider(AuthProvider):
raise InvalidAuthError("Can't allow access from Home Assistant Cloud")
@callback
@override
def async_validate_refresh_token(
self, refresh_token: RefreshToken, remote_ip: str | None = None
) -> None:
@@ -235,7 +230,6 @@ class TrustedNetworksLoginFlow(LoginFlow[TrustedNetworksAuthProvider]):
self._ip_address = ip_addr
self._allow_bypass_login = allow_bypass_login
@override
async def async_step_init(
self, user_input: dict[str, str] | None = None
) -> AuthFlowResult:
+1 -2
View File
@@ -14,7 +14,7 @@ import platform
import sys
import threading
from time import monotonic
from typing import TYPE_CHECKING, Any, override
from typing import TYPE_CHECKING, Any
# Import cryptography early since import openssl is not thread-safe
# _frozen_importlib._DeadlockError: deadlock detected by
@@ -697,7 +697,6 @@ def _create_log_file(
class _RotatingFileHandlerWithoutShouldRollOver(RotatingFileHandler):
"""RotatingFileHandler that does not check if it should roll over on every log."""
@override
def shouldRollover(self, record: logging.LogRecord) -> bool:
"""Never roll over.
+1
View File
@@ -7,6 +7,7 @@
"unifi_access",
"unifi_direct",
"unifi_discovery",
"unifiled",
"unifiprotect"
]
}
@@ -1,7 +1,5 @@
"""Support for Abode Security System alarm control panels."""
from typing import override
from jaraco.abode.devices.alarm import Alarm
from homeassistant.components.alarm_control_panel import (
@@ -40,7 +38,6 @@ class AbodeAlarm(AbodeDevice, AlarmControlPanelEntity):
_device: Alarm
@property
@override
def alarm_state(self) -> AlarmControlPanelState | None:
"""Return the state of the device."""
if self._device.is_standby:
@@ -51,23 +48,19 @@ class AbodeAlarm(AbodeDevice, AlarmControlPanelEntity):
return AlarmControlPanelState.ARMED_HOME
return None
@override
def alarm_disarm(self, code: str | None = None) -> None:
"""Send disarm command."""
self._device.set_standby()
@override
def alarm_arm_home(self, code: str | None = None) -> None:
"""Send arm home command."""
self._device.set_home()
@override
def alarm_arm_away(self, code: str | None = None) -> None:
"""Send arm away command."""
self._device.set_away()
@property
@override
def extra_state_attributes(self) -> dict[str, str]:
"""Return the state attributes."""
return {
@@ -1,6 +1,6 @@
"""Support for Abode Security System binary sensors."""
from typing import cast, override
from typing import cast
from jaraco.abode.devices.binary_sensor import BinarySensor
@@ -45,13 +45,11 @@ class AbodeBinarySensor(AbodeDevice, BinarySensorEntity):
_device: BinarySensor
@property
@override
def is_on(self) -> bool:
"""Return True if the binary sensor is on."""
return cast(bool, self._device.is_on)
@property
@override
def device_class(self) -> BinarySensorDeviceClass | None:
"""Return the class of the binary sensor."""
if self._device.get_value("is_window") == "1":
+1 -6
View File
@@ -1,7 +1,7 @@
"""Support for Abode Security System cameras."""
from datetime import timedelta
from typing import Any, cast, override
from typing import Any, cast
from jaraco.abode.devices.base import Device
from jaraco.abode.devices.camera import Camera as AbodeCam
@@ -49,7 +49,6 @@ class AbodeCamera(AbodeDevice, Camera):
self._event = event
self._response: Response | None = None
@override
async def async_added_to_hass(self) -> None:
"""Subscribe Abode events."""
await super().async_added_to_hass()
@@ -88,7 +87,6 @@ class AbodeCamera(AbodeDevice, Camera):
else:
self._response = None
@override
def camera_image(
self, width: int | None = None, height: int | None = None
) -> bytes | None:
@@ -100,12 +98,10 @@ class AbodeCamera(AbodeDevice, Camera):
return None
@override
def turn_on(self) -> None:
"""Turn on camera."""
self._device.privacy_mode(False)
@override
def turn_off(self) -> None:
"""Turn off camera."""
self._device.privacy_mode(True)
@@ -117,7 +113,6 @@ class AbodeCamera(AbodeDevice, Camera):
self.schedule_update_ha_state()
@property
@override
def is_on(self) -> bool:
"""Return true if on."""
return cast(bool, self._device.is_on)
@@ -2,7 +2,7 @@
from collections.abc import Mapping
from http import HTTPStatus
from typing import Any, cast, override
from typing import Any, cast
from jaraco.abode.client import Client as Abode
from jaraco.abode.exceptions import (
@@ -106,7 +106,6 @@ class AbodeFlowHandler(ConfigFlow, domain=DOMAIN):
title=cast(str, self._username), data=config_data
)
@override
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
+1 -4
View File
@@ -1,6 +1,6 @@
"""Support for Abode Security System covers."""
from typing import Any, override
from typing import Any
from jaraco.abode.devices.cover import Cover
@@ -33,17 +33,14 @@ class AbodeCover(AbodeDevice, CoverEntity):
_attr_name = None
@property
@override
def is_closed(self) -> bool:
"""Return true if cover is closed, else False."""
return not self._device.is_open
@override
def close_cover(self, **kwargs: Any) -> None:
"""Issue close command to cover."""
self._device.close_cover()
@override
def open_cover(self, **kwargs: Any) -> None:
"""Issue open command to cover."""
self._device.open_cover()
-8
View File
@@ -1,7 +1,5 @@
"""Support for Abode Security System entities."""
from typing import override
from jaraco.abode.automation import Automation as AbodeAuto
from jaraco.abode.devices.base import Device as AbodeDev
@@ -23,7 +21,6 @@ class AbodeEntity(Entity):
self._data = data
self._attr_should_poll = data.polling
@override
async def async_added_to_hass(self) -> None:
"""Subscribe to Abode connection status updates."""
await self.hass.async_add_executor_job(
@@ -34,7 +31,6 @@ class AbodeEntity(Entity):
self._data.entity_ids.add(self.entity_id)
@override
async def async_will_remove_from_hass(self) -> None:
"""Unsubscribe from Abode connection status updates."""
await self.hass.async_add_executor_job(
@@ -56,7 +52,6 @@ class AbodeDevice(AbodeEntity):
self._device = device
self._attr_unique_id = device.uuid
@override
async def async_added_to_hass(self) -> None:
"""Subscribe to device events."""
await super().async_added_to_hass()
@@ -66,7 +61,6 @@ class AbodeDevice(AbodeEntity):
self._update_callback,
)
@override
async def async_will_remove_from_hass(self) -> None:
"""Unsubscribe from device events."""
await super().async_will_remove_from_hass()
@@ -79,7 +73,6 @@ class AbodeDevice(AbodeEntity):
self._device.refresh()
@property
@override
def extra_state_attributes(self) -> dict[str, str]:
"""Return the state attributes."""
return {
@@ -90,7 +83,6 @@ class AbodeDevice(AbodeEntity):
}
@property
@override
def device_info(self) -> DeviceInfo:
"""Return device registry information for this entity."""
return DeviceInfo(
+1 -9
View File
@@ -1,7 +1,7 @@
"""Support for Abode Security System lights."""
from math import ceil
from typing import Any, override
from typing import Any
from jaraco.abode.devices.light import Light
@@ -43,7 +43,6 @@ class AbodeLight(AbodeDevice, LightEntity):
_attr_max_color_temp_kelvin = DEFAULT_MAX_KELVIN
_attr_min_color_temp_kelvin = DEFAULT_MIN_KELVIN
@override
def turn_on(self, **kwargs: Any) -> None:
"""Turn on the light."""
if ATTR_COLOR_TEMP_KELVIN in kwargs and self._device.is_color_capable:
@@ -62,19 +61,16 @@ class AbodeLight(AbodeDevice, LightEntity):
self._device.switch_on()
@override
def turn_off(self, **kwargs: Any) -> None:
"""Turn off the light."""
self._device.switch_off()
@property
@override
def is_on(self) -> bool:
"""Return true if device is on."""
return bool(self._device.is_on)
@property
@override
def brightness(self) -> int | None:
"""Return the brightness of the light."""
if self._device.is_dimmable and self._device.has_brightness:
@@ -85,7 +81,6 @@ class AbodeLight(AbodeDevice, LightEntity):
return None
@property
@override
def color_temp_kelvin(self) -> int | None:
"""Return the color temp of the light."""
if self._device.has_color:
@@ -93,7 +88,6 @@ class AbodeLight(AbodeDevice, LightEntity):
return None
@property
@override
def hs_color(self) -> tuple[float, float] | None:
"""Return the color of the light."""
_hs = None
@@ -102,7 +96,6 @@ class AbodeLight(AbodeDevice, LightEntity):
return _hs
@property
@override
def color_mode(self) -> ColorMode:
"""Return the color mode of the light."""
if self._device.is_dimmable and self._device.is_color_capable:
@@ -114,7 +107,6 @@ class AbodeLight(AbodeDevice, LightEntity):
return ColorMode.ONOFF
@property
@override
def supported_color_modes(self) -> set[ColorMode]:
"""Flag supported color modes."""
if self._device.is_dimmable and self._device.is_color_capable:
+1 -4
View File
@@ -1,6 +1,6 @@
"""Support for the Abode Security System locks."""
from typing import Any, override
from typing import Any
from jaraco.abode.devices.lock import Lock
@@ -32,18 +32,15 @@ class AbodeLock(AbodeDevice, LockEntity):
_device: Lock
_attr_name = None
@override
def lock(self, **kwargs: Any) -> None:
"""Lock the device."""
self._device.lock()
@override
def unlock(self, **kwargs: Any) -> None:
"""Unlock the device."""
self._device.unlock()
@property
@override
def is_locked(self) -> bool:
"""Return true if device is on."""
return bool(self._device.is_locked)
+1 -3
View File
@@ -2,7 +2,7 @@
from collections.abc import Callable
from dataclasses import dataclass
from typing import cast, override
from typing import cast
from jaraco.abode.devices.sensor import Sensor
@@ -94,13 +94,11 @@ class AbodeSensor(AbodeDevice, SensorEntity):
self._attr_unique_id = f"{device.uuid}-{description.key}"
@property
@override
def native_value(self) -> float:
"""Return the state of the sensor."""
return self.entity_description.value_fn(self._device)
@property
@override
def native_unit_of_measurement(self) -> str:
"""Return the native unit of measurement."""
return self.entity_description.native_unit_of_measurement_fn(self._device)
+1 -8
View File
@@ -1,6 +1,6 @@
"""Support for Abode Security System switches."""
from typing import Any, cast, override
from typing import Any, cast
from jaraco.abode.devices.switch import Switch
@@ -43,18 +43,15 @@ class AbodeSwitch(AbodeDevice, SwitchEntity):
_device: Switch
_attr_name = None
@override
def turn_on(self, **kwargs: Any) -> None:
"""Turn on the device."""
self._device.switch_on()
@override
def turn_off(self, **kwargs: Any) -> None:
"""Turn off the device."""
self._device.switch_off()
@property
@override
def is_on(self) -> bool:
"""Return true if device is on."""
return cast(bool, self._device.is_on)
@@ -65,7 +62,6 @@ class AbodeAutomationSwitch(AbodeAutomation, SwitchEntity):
_attr_translation_key = "automation"
@override
async def async_added_to_hass(self) -> None:
"""Set up trigger automation service."""
await super().async_added_to_hass()
@@ -73,13 +69,11 @@ class AbodeAutomationSwitch(AbodeAutomation, SwitchEntity):
signal = f"abode_trigger_automation_{self.entity_id}"
self.async_on_remove(async_dispatcher_connect(self.hass, signal, self.trigger))
@override
def turn_on(self, **kwargs: Any) -> None:
"""Enable the automation."""
if self._automation.enable(True):
self.schedule_update_ha_state()
@override
def turn_off(self, **kwargs: Any) -> None:
"""Disable the automation."""
if self._automation.enable(False):
@@ -90,7 +84,6 @@ class AbodeAutomationSwitch(AbodeAutomation, SwitchEntity):
self._automation.trigger()
@property
@override
def is_on(self) -> bool:
"""Return True if the automation is enabled."""
return bool(self._automation.enabled)
@@ -2,7 +2,6 @@
from collections.abc import Callable
from dataclasses import dataclass
from typing import override
from aioacaia.acaiascale import AcaiaScale
@@ -57,7 +56,6 @@ class AcaiaBinarySensor(AcaiaEntity, BinarySensorEntity):
entity_description: AcaiaBinarySensorEntityDescription
@property
@override
def is_on(self) -> bool:
"""Return true if the binary sensor is on."""
return self.entity_description.is_on_fn(self._scale)
+1 -2
View File
@@ -2,7 +2,7 @@
from collections.abc import Callable, Coroutine
from dataclasses import dataclass
from typing import Any, override
from typing import Any
from aioacaia.acaiascale import AcaiaScale
@@ -58,7 +58,6 @@ class AcaiaButton(AcaiaEntity, ButtonEntity):
entity_description: AcaiaButtonEntityDescription
@override
async def async_press(self) -> None:
"""Handle the button press."""
await self.entity_description.press_fn(self._scale)
@@ -1,7 +1,7 @@
"""Config flow for Acaia integration."""
import logging
from typing import Any, override
from typing import Any
from aioacaia.exceptions import AcaiaDeviceNotFound, AcaiaError, AcaiaUnknownDevice
from aioacaia.helpers import is_new_scale
@@ -34,7 +34,6 @@ class AcaiaConfigFlow(ConfigFlow, domain=DOMAIN):
self._discovered: dict[str, Any] = {}
self._discovered_devices: dict[str, str] = {}
@override
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
@@ -95,7 +94,6 @@ class AcaiaConfigFlow(ConfigFlow, domain=DOMAIN):
errors=errors,
)
@override
async def async_step_bluetooth(
self, discovery_info: BluetoothServiceInfoBleak
) -> ConfigFlowResult:
@@ -2,7 +2,6 @@
from datetime import timedelta
import logging
from typing import override
from aioacaia.acaiascale import AcaiaScale
from aioacaia.exceptions import AcaiaDeviceNotFound, AcaiaError
@@ -60,7 +59,6 @@ class AcaiaCoordinator(DataUpdateCoordinator[None]):
"""Return the scale object."""
return self._scale
@override
async def _async_update_data(self) -> None:
"""Fetch data."""
-2
View File
@@ -1,7 +1,6 @@
"""Base class for Acaia entities."""
from dataclasses import dataclass
from typing import override
from homeassistant.helpers.device_registry import (
CONNECTION_BLUETOOTH,
@@ -42,7 +41,6 @@ class AcaiaEntity(CoordinatorEntity[AcaiaCoordinator]):
)
@property
@override
def available(self) -> bool:
"""Returns whether entity is available."""
return super().available and self._scale.connected
@@ -14,15 +14,9 @@ rules:
status: exempt
comment: |
No custom actions are defined.
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup:
status: exempt
comment: |
-6
View File
@@ -2,7 +2,6 @@
from collections.abc import Callable
from dataclasses import dataclass
from typing import override
from aioacaia.acaiascale import AcaiaDeviceState, AcaiaScale
from aioacaia.const import UnitMass as AcaiaUnitOfMass
@@ -99,7 +98,6 @@ class AcaiaSensor(AcaiaEntity, SensorEntity):
entity_description: AcaiaDynamicUnitSensorEntityDescription
@property
@override
def native_unit_of_measurement(self) -> str | None:
"""Return the unit of measurement of this entity."""
if (
@@ -110,7 +108,6 @@ class AcaiaSensor(AcaiaEntity, SensorEntity):
return self.entity_description.native_unit_of_measurement
@property
@override
def native_value(self) -> int | float | None:
"""Return the state of the entity."""
return self.entity_description.value_fn(self._scale)
@@ -122,7 +119,6 @@ class AcaiaRestoreSensor(AcaiaEntity, RestoreSensor):
entity_description: AcaiaSensorEntityDescription
_restored_data: SensorExtraStoredData | None = None
@override
async def async_added_to_hass(self) -> None:
"""Handle entity which will be added."""
await super().async_added_to_hass()
@@ -138,7 +134,6 @@ class AcaiaRestoreSensor(AcaiaEntity, RestoreSensor):
self._attr_native_value = self.entity_description.value_fn(self._scale)
@callback
@override
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
if self._scale.device_state is not None:
@@ -146,7 +141,6 @@ class AcaiaRestoreSensor(AcaiaEntity, RestoreSensor):
self._async_write_ha_state()
@property
@override
def available(self) -> bool:
"""Return True if entity is available."""
return super().available or self.native_value is not None
@@ -2,7 +2,7 @@
from asyncio import timeout
from collections.abc import Mapping
from typing import TYPE_CHECKING, Any, override
from typing import TYPE_CHECKING, Any
from accuweather import AccuWeather, ApiError, InvalidApiKeyError, RequestsExceededError
from aiohttp import ClientError
@@ -24,7 +24,6 @@ class AccuWeatherFlowHandler(ConfigFlow, domain=DOMAIN):
_latitude: float | None = None
_longitude: float | None = None
@override
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
@@ -5,7 +5,7 @@ from collections.abc import Awaitable, Callable
from dataclasses import dataclass
from datetime import timedelta
import logging
from typing import TYPE_CHECKING, Any, override
from typing import TYPE_CHECKING, Any
from accuweather import AccuWeather, ApiError, InvalidApiKeyError, RequestsExceededError
from aiohttp.client_exceptions import ClientConnectorError
@@ -77,7 +77,6 @@ class AccuWeatherObservationDataUpdateCoordinator(
update_interval=UPDATE_INTERVAL_OBSERVATION,
)
@override
async def _async_update_data(self) -> dict[str, Any]:
"""Update data via library."""
try:
@@ -136,7 +135,6 @@ class AccuWeatherForecastDataUpdateCoordinator(
update_interval=update_interval,
)
@override
async def _async_update_data(self) -> list[dict[str, Any]]:
"""Update forecast data via library."""
try:
@@ -2,7 +2,7 @@
from collections.abc import Callable
from dataclasses import dataclass
from typing import Any, cast, override
from typing import Any, cast
from homeassistant.components.sensor import (
SensorDeviceClass,
@@ -436,19 +436,16 @@ class AccuWeatherSensor(
self._attr_device_info = coordinator.device_info
@property
@override
def native_value(self) -> str | int | float | None:
"""Return the state."""
return self.entity_description.value_fn(self._sensor_data)
@property
@override
def extra_state_attributes(self) -> dict[str, Any]:
"""Return the state attributes."""
return self.entity_description.attr_fn(self.coordinator.data)
@callback
@override
def _handle_coordinator_update(self) -> None:
"""Handle data update."""
self._sensor_data = self._get_sensor_data(
@@ -498,19 +495,16 @@ class AccuWeatherForecastSensor(
self.forecast_day = forecast_day
@property
@override
def native_value(self) -> str | int | float | None:
"""Return the state."""
return self.entity_description.value_fn(self._sensor_data)
@property
@override
def extra_state_attributes(self) -> dict[str, Any]:
"""Return the state attributes."""
return self.entity_description.attr_fn(self._sensor_data)
@callback
@override
def _handle_coordinator_update(self) -> None:
"""Handle data update."""
self._sensor_data = self._get_sensor_data(
@@ -1,6 +1,6 @@
"""Support for the AccuWeather service."""
from typing import cast, override
from typing import cast
from homeassistant.components.weather import (
ATTR_FORECAST_CLOUD_COVERAGE,
@@ -96,19 +96,16 @@ class AccuWeatherEntity(
self.hourly_coordinator = accuweather_data.coordinator_hourly_forecast
@property
@override
def condition(self) -> str | None:
"""Return the current condition."""
return CONDITION_MAP.get(self.observation_coordinator.data["WeatherIcon"])
@property
@override
def cloud_coverage(self) -> float:
"""Return the Cloud coverage in %."""
return cast(float, self.observation_coordinator.data["CloudCover"])
@property
@override
def native_apparent_temperature(self) -> float:
"""Return the apparent temperature."""
return cast(
@@ -119,7 +116,6 @@ class AccuWeatherEntity(
)
@property
@override
def native_temperature(self) -> float:
"""Return the temperature."""
return cast(
@@ -128,7 +124,6 @@ class AccuWeatherEntity(
)
@property
@override
def native_pressure(self) -> float:
"""Return the pressure."""
return cast(
@@ -136,7 +131,6 @@ class AccuWeatherEntity(
)
@property
@override
def native_dew_point(self) -> float:
"""Return the dew point."""
return cast(
@@ -144,13 +138,11 @@ class AccuWeatherEntity(
)
@property
@override
def humidity(self) -> int:
"""Return the humidity."""
return cast(int, self.observation_coordinator.data["RelativeHumidity"])
@property
@override
def native_wind_gust_speed(self) -> float:
"""Return the wind gust speed."""
return cast(
@@ -161,7 +153,6 @@ class AccuWeatherEntity(
)
@property
@override
def native_wind_speed(self) -> float:
"""Return the wind speed."""
return cast(
@@ -172,7 +163,6 @@ class AccuWeatherEntity(
)
@property
@override
def wind_bearing(self) -> int:
"""Return the wind bearing."""
return cast(
@@ -180,7 +170,6 @@ class AccuWeatherEntity(
)
@property
@override
def native_visibility(self) -> float:
"""Return the visibility."""
return cast(
@@ -189,13 +178,11 @@ class AccuWeatherEntity(
)
@property
@override
def uv_index(self) -> float:
"""Return the UV index."""
return cast(float, self.observation_coordinator.data["UVIndex"])
@callback
@override
def _async_forecast_daily(self) -> list[Forecast] | None:
"""Return the daily forecast in native units."""
return [
@@ -226,7 +213,6 @@ class AccuWeatherEntity(
]
@callback
@override
def _async_forecast_hourly(self) -> list[Forecast] | None:
"""Return the hourly forecast in native units."""
return [
@@ -0,0 +1 @@
"""The acer_projector component."""
@@ -0,0 +1,34 @@
"""Use serial protocol of Acer projector to obtain state of the projector."""
from typing import Final
from homeassistant.const import STATE_OFF, STATE_ON
CONF_READ_TIMEOUT: Final = "timeout"
CONF_WRITE_TIMEOUT: Final = "write_timeout"
DEFAULT_NAME: Final = "Acer Projector"
DEFAULT_READ_TIMEOUT: Final = 1
DEFAULT_WRITE_TIMEOUT: Final = 1
ECO_MODE: Final = "ECO Mode"
ICON: Final = "mdi:projector"
INPUT_SOURCE: Final = "Input Source"
LAMP: Final = "Lamp"
LAMP_HOURS: Final = "Lamp Hours"
MODEL: Final = "Model"
# Commands known to the projector
CMD_DICT: Final[dict[str, str]] = {
LAMP: "* 0 Lamp ?\r",
LAMP_HOURS: "* 0 Lamp\r",
INPUT_SOURCE: "* 0 Src ?\r",
ECO_MODE: "* 0 IR 052\r",
MODEL: "* 0 IR 035\r",
STATE_ON: "* 0 IR 001\r",
STATE_OFF: "* 0 IR 002\r",
}
@@ -0,0 +1,9 @@
{
"domain": "acer_projector",
"name": "Acer Projector",
"codeowners": [],
"documentation": "https://www.home-assistant.io/integrations/acer_projector",
"iot_class": "local_polling",
"quality_scale": "legacy",
"requirements": ["serialx==1.8.2"]
}
@@ -0,0 +1,153 @@
"""Use serial protocol of Acer projector to obtain state of the projector."""
import logging
import re
from typing import Any
from serialx import Serial, SerialException
import voluptuous as vol
from homeassistant.components.switch import (
PLATFORM_SCHEMA as SWITCH_PLATFORM_SCHEMA,
SwitchEntity,
)
from homeassistant.const import (
CONF_FILENAME,
CONF_NAME,
STATE_OFF,
STATE_ON,
STATE_UNKNOWN,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from .const import (
CMD_DICT,
CONF_READ_TIMEOUT,
CONF_WRITE_TIMEOUT,
DEFAULT_NAME,
DEFAULT_READ_TIMEOUT,
DEFAULT_WRITE_TIMEOUT,
ECO_MODE,
ICON,
INPUT_SOURCE,
LAMP,
LAMP_HOURS,
)
_LOGGER = logging.getLogger(__name__)
PLATFORM_SCHEMA = SWITCH_PLATFORM_SCHEMA.extend(
{
vol.Required(CONF_FILENAME): cv.isdevice,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_READ_TIMEOUT, default=DEFAULT_READ_TIMEOUT): cv.positive_int,
vol.Optional(
CONF_WRITE_TIMEOUT, default=DEFAULT_WRITE_TIMEOUT
): cv.positive_int,
}
)
def setup_platform(
hass: HomeAssistant,
config: ConfigType,
add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Connect with serial port and return Acer Projector."""
serial_port = config[CONF_FILENAME]
name = config[CONF_NAME]
read_timeout = config[CONF_READ_TIMEOUT]
write_timeout = config[CONF_WRITE_TIMEOUT]
add_entities([AcerSwitch(serial_port, name, read_timeout, write_timeout)], True)
class AcerSwitch(SwitchEntity):
"""Represents an Acer Projector as a switch."""
_attr_icon = ICON
def __init__(
self,
serial_port: str,
name: str,
read_timeout: int,
write_timeout: int,
) -> None:
"""Init of the Acer projector."""
self._serial_port = serial_port
self._read_timeout = read_timeout
self._write_timeout = write_timeout
self._attr_name = name
self._attributes = {
LAMP_HOURS: STATE_UNKNOWN,
INPUT_SOURCE: STATE_UNKNOWN,
ECO_MODE: STATE_UNKNOWN,
}
def _write_read(self, msg: str) -> str:
"""Write to the projector and read the return."""
# Sometimes the projector won't answer for no reason or the projector
# was disconnected during runtime.
# This way the projector can be reconnected and will still work
try:
with Serial.from_url(
self._serial_port,
read_timeout=self._read_timeout,
write_timeout=self._write_timeout,
) as serial:
serial.write(msg.encode("utf-8"))
# Size is an experience value there is no real limit.
# AFAIK there is no limit and no end character so we will usually
# need to wait for timeout
return serial.read_until(size=20).decode("utf-8")
except (OSError, SerialException, TimeoutError) as exc:
raise HomeAssistantError(
f"Problem communicating with {self._serial_port}"
) from exc
def _write_read_format(self, msg: str) -> str:
"""Write msg, obtain answer and format output."""
# answers are formatted as ***\answer\r***
awns = self._write_read(msg)
if match := re.search(r"\r(.+)\r", awns):
return match.group(1)
return STATE_UNKNOWN
def update(self) -> None:
"""Get the latest state from the projector."""
awns = self._write_read_format(CMD_DICT[LAMP])
if awns == "Lamp 1":
self._attr_is_on = True
self._attr_available = True
elif awns == "Lamp 0":
self._attr_is_on = False
self._attr_available = True
else:
self._attr_available = False
for key in self._attributes:
if msg := CMD_DICT.get(key):
awns = self._write_read_format(msg)
self._attributes[key] = awns
self._attr_extra_state_attributes = self._attributes
def turn_on(self, **kwargs: Any) -> None:
"""Turn the projector on."""
msg = CMD_DICT[STATE_ON]
self._write_read(msg)
self._attr_is_on = True
def turn_off(self, **kwargs: Any) -> None:
"""Turn the projector off."""
msg = CMD_DICT[STATE_OFF]
self._write_read(msg)
self._attr_is_on = False
@@ -2,7 +2,7 @@
from asyncio import timeout
from contextlib import suppress
from typing import Any, override
from typing import Any
import aiopulse
import voluptuous as vol
@@ -22,7 +22,6 @@ class AcmedaFlowHandler(ConfigFlow, domain=DOMAIN):
"""Initialize the config flow."""
self.discovered_hubs: dict[str, aiopulse.Hub] | None = None
@override
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
+1 -13
View File
@@ -1,6 +1,6 @@
"""Support for Acmeda Roller Blinds."""
from typing import Any, override
from typing import Any
from homeassistant.components.cover import (
ATTR_POSITION,
@@ -48,7 +48,6 @@ class AcmedaCover(AcmedaEntity, CoverEntity):
_attr_name = None
@property
@override
def current_cover_position(self) -> int | None:
"""Return the current position of the roller blind.
@@ -60,7 +59,6 @@ class AcmedaCover(AcmedaEntity, CoverEntity):
return position
@property
@override
def current_cover_tilt_position(self) -> int | None:
"""Return the current tilt of the roller blind.
@@ -72,7 +70,6 @@ class AcmedaCover(AcmedaEntity, CoverEntity):
return position
@property
@override
def supported_features(self) -> CoverEntityFeature:
"""Flag supported features."""
supported_features = CoverEntityFeature(0)
@@ -94,47 +91,38 @@ class AcmedaCover(AcmedaEntity, CoverEntity):
return supported_features
@property
@override
def is_closed(self) -> bool:
"""Return if the cover is closed."""
return self.roller.closed_percent == 100 # type: ignore[no-any-return]
@override
async def async_close_cover(self, **kwargs: Any) -> None:
"""Close the roller."""
await self.roller.move_down()
@override
async def async_open_cover(self, **kwargs: Any) -> None:
"""Open the roller."""
await self.roller.move_up()
@override
async def async_stop_cover(self, **kwargs: Any) -> None:
"""Stop the roller."""
await self.roller.move_stop()
@override
async def async_set_cover_position(self, **kwargs: Any) -> None:
"""Move the roller shutter to a specific position."""
await self.roller.move_to(100 - kwargs[ATTR_POSITION])
@override
async def async_close_cover_tilt(self, **kwargs: Any) -> None:
"""Close the roller."""
await self.roller.move_down()
@override
async def async_open_cover_tilt(self, **kwargs: Any) -> None:
"""Open the roller."""
await self.roller.move_up()
@override
async def async_stop_cover_tilt(self, **kwargs: Any) -> None:
"""Stop the roller."""
await self.roller.move_stop()
@override
async def async_set_cover_tilt_position(self, **kwargs: Any) -> None:
"""Tilt the roller shutter to a specific position."""
await self.roller.move_to(100 - kwargs[ATTR_POSITION])
@@ -1,7 +1,5 @@
"""Base class for Acmeda Roller Blinds."""
from typing import override
import aiopulse
from homeassistant.core import callback
@@ -42,7 +40,6 @@ class AcmedaEntity(entity.Entity):
await self.async_remove(force_remove=True)
@override
async def async_added_to_hass(self) -> None:
"""Entity has been added to hass."""
self.roller.callback_subscribe(self.notify_update)
@@ -55,7 +52,6 @@ class AcmedaEntity(entity.Entity):
)
)
@override
async def async_will_remove_from_hass(self) -> None:
"""Entity being removed from hass."""
self.roller.callback_unsubscribe(self.notify_update)
@@ -67,7 +63,6 @@ class AcmedaEntity(entity.Entity):
self.async_write_ha_state()
@property
@override
def unique_id(self) -> str:
"""Return the unique ID of this roller."""
return str(self.roller.id)
@@ -78,7 +73,6 @@ class AcmedaEntity(entity.Entity):
return self.roller.id # type: ignore[no-any-return]
@property
@override
def device_info(self) -> dr.DeviceInfo:
"""Return the device info."""
return dr.DeviceInfo(
@@ -1,7 +1,5 @@
"""Support for Acmeda Roller Blind Batteries."""
from typing import override
from homeassistant.components.sensor import SensorDeviceClass, SensorEntity
from homeassistant.const import PERCENTAGE
from homeassistant.core import HomeAssistant, callback
@@ -46,7 +44,6 @@ class AcmedaBattery(AcmedaEntity, SensorEntity):
_attr_native_unit_of_measurement = PERCENTAGE
@property
@override
def native_value(self) -> float | int | None:
"""Return the state of the device."""
return self.roller.battery # type: ignore[no-any-return]
@@ -1,7 +1,7 @@
"""Support for Actiontec MI424WR (Verizon FIOS) routers."""
import logging
from typing import Final, override
from typing import Final
import telnetlib # pylint: disable=deprecated-module
import voluptuous as vol
@@ -50,13 +50,11 @@ class ActiontecDeviceScanner(DeviceScanner):
data = self.get_actiontec_data()
self.success_init = data is not None
@override
def scan_devices(self) -> list[str]:
"""Scan for new devices and return a list with found device IDs."""
self._update_info()
return [client.mac_address for client in self.last_results]
@override
def get_device_name(self, device: str) -> str | None:
"""Return the name of the given device or None if we don't know."""
for client in self.last_results:
+1 -21
View File
@@ -1,6 +1,6 @@
"""Climate platform for Actron Air integration."""
from typing import Any, override
from typing import Any
from actron_neo_api import ActronAirStatus, ActronAirZone
@@ -94,7 +94,6 @@ class ActronSystemClimate(ActronAirAcEntity, ActronAirClimateEntity):
self._attr_unique_id = self._serial_number
@property
@override
def hvac_modes(self) -> list[HVACMode]:
"""Return the list of supported HVAC modes."""
modes = [
@@ -106,13 +105,11 @@ class ActronSystemClimate(ActronAirAcEntity, ActronAirClimateEntity):
return modes
@property
@override
def min_temp(self) -> float:
"""Return the minimum temperature that can be set."""
return self._status.min_temp
@property
@override
def max_temp(self) -> float:
"""Return the maximum temperature that can be set."""
return self._status.max_temp
@@ -123,7 +120,6 @@ class ActronSystemClimate(ActronAirAcEntity, ActronAirClimateEntity):
return self.coordinator.data
@property
@override
def hvac_mode(self) -> HVACMode | None:
"""Return the current HVAC mode."""
if not self._status.user_aircon_settings.is_on:
@@ -133,46 +129,39 @@ class ActronSystemClimate(ActronAirAcEntity, ActronAirClimateEntity):
return HVAC_MODE_MAPPING_ACTRONAIR_TO_HA.get(mode)
@property
@override
def fan_mode(self) -> str | None:
"""Return the current fan mode."""
fan_mode = self._status.user_aircon_settings.base_fan_mode
return FAN_MODE_MAPPING_ACTRONAIR_TO_HA.get(fan_mode)
@property
@override
def current_humidity(self) -> float:
"""Return the current humidity."""
return self._status.master_info.live_humidity_pc
@property
@override
def current_temperature(self) -> float:
"""Return the current temperature."""
return self._status.master_info.live_temp_c
@property
@override
def target_temperature(self) -> float:
"""Return the target temperature."""
return self._status.user_aircon_settings.current_setpoint
@actron_air_command
@override
async def async_set_fan_mode(self, fan_mode: str) -> None:
"""Set a new fan mode."""
api_fan_mode = FAN_MODE_MAPPING_HA_TO_ACTRONAIR[fan_mode]
await self._status.user_aircon_settings.set_fan_mode(api_fan_mode)
@actron_air_command
@override
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set the HVAC mode."""
ac_mode = HVAC_MODE_MAPPING_HA_TO_ACTRONAIR[hvac_mode]
await self._status.ac_system.set_system_mode(ac_mode)
@actron_air_command
@override
async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set the temperature."""
if (temperature := kwargs.get(ATTR_TEMPERATURE)) is None:
@@ -202,7 +191,6 @@ class ActronZoneClimate(ActronAirZoneEntity, ActronAirClimateEntity):
self._attr_unique_id: str = self._zone_identifier
@property
@override
def hvac_modes(self) -> list[HVACMode]:
"""Return the list of supported HVAC modes."""
status = self.coordinator.data
@@ -215,13 +203,11 @@ class ActronZoneClimate(ActronAirZoneEntity, ActronAirClimateEntity):
return modes
@property
@override
def min_temp(self) -> float:
"""Return the minimum temperature that can be set."""
return self._zone.min_temp
@property
@override
def max_temp(self) -> float:
"""Return the maximum temperature that can be set."""
return self._zone.max_temp
@@ -233,7 +219,6 @@ class ActronZoneClimate(ActronAirZoneEntity, ActronAirClimateEntity):
return status.zones[self._zone_id]
@property
@override
def hvac_mode(self) -> HVACMode | None:
"""Return the current HVAC mode."""
if self._zone.is_active:
@@ -242,32 +227,27 @@ class ActronZoneClimate(ActronAirZoneEntity, ActronAirClimateEntity):
return HVACMode.OFF
@property
@override
def current_humidity(self) -> float | None:
"""Return the current humidity."""
return self._zone.humidity
@property
@override
def current_temperature(self) -> float | None:
"""Return the current temperature."""
return self._zone.live_temp_c
@property
@override
def target_temperature(self) -> float | None:
"""Return the target temperature."""
return self._zone.current_setpoint
@actron_air_command
@override
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set the HVAC mode."""
is_enabled = hvac_mode != HVACMode.OFF
await self._zone.enable(is_enabled)
@actron_air_command
@override
async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set the temperature."""
if (temperature := kwargs.get(ATTR_TEMPERATURE)) is None:
@@ -2,7 +2,7 @@
import asyncio
from collections.abc import Mapping
from typing import Any, override
from typing import Any
from actron_neo_api import ActronAirAPI, ActronAirAuthError
@@ -30,7 +30,6 @@ class ActronAirConfigFlow(ConfigFlow, domain=DOMAIN):
self._expires_minutes: str = "30"
self.login_task: asyncio.Task[None] | None = None
@override
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
@@ -2,7 +2,6 @@
from dataclasses import dataclass
from datetime import timedelta
from typing import override
from actron_neo_api import (
ActronAirAPI,
@@ -61,7 +60,6 @@ class ActronAirSystemCoordinator(DataUpdateCoordinator[ActronAirStatus]):
self.status = self.api.state_manager.get_status(self.serial_number)
self.last_seen = dt_util.utcnow()
@override
async def _async_update_data(self) -> ActronAirStatus:
"""Fetch updates and merge incremental changes into the full state."""
try:
@@ -2,7 +2,7 @@
from collections.abc import Callable, Coroutine
from functools import wraps
from typing import Any, Concatenate, override
from typing import Any, Concatenate
from actron_neo_api import ActronAirAPIError, ActronAirZone
@@ -50,7 +50,6 @@ class ActronAirEntity(CoordinatorEntity[ActronAirSystemCoordinator]):
self._serial_number = coordinator.serial_number
@property
@override
def available(self) -> bool:
"""Return True if entity is available."""
return not self.coordinator.is_device_stale()
@@ -12,15 +12,9 @@ rules:
docs-actions:
status: exempt
comment: This integration does not have custom service actions.
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup:
status: exempt
comment: This integration does not subscribe to external events.
@@ -2,7 +2,7 @@
from collections.abc import Awaitable, Callable
from dataclasses import dataclass
from typing import Any, override
from typing import Any
from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
from homeassistant.const import EntityCategory
@@ -101,19 +101,16 @@ class ActronAirSwitch(ActronAirAcEntity, SwitchEntity):
self._attr_unique_id = f"{coordinator.serial_number}_{description.key}"
@property
@override
def is_on(self) -> bool:
"""Return true if the switch is on."""
return self.entity_description.is_on_fn(self.coordinator)
@actron_air_command
@override
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the switch on."""
await self.entity_description.set_fn(self.coordinator, True)
@actron_air_command
@override
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the switch off."""
await self.entity_description.set_fn(self.coordinator, False)
+1 -9
View File
@@ -1,6 +1,6 @@
"""Support for Adax wifi-enabled home heaters."""
from typing import Any, cast, override
from typing import Any, cast
from adax import Adax
from adax_local import Adax as AdaxLocal
@@ -82,7 +82,6 @@ class AdaxDevice(CoordinatorEntity[AdaxCloudCoordinator], ClimateEntity):
self._apply_data(self.room)
@property
@override
def available(self) -> bool:
"""Whether the entity is available or not."""
return super().available and self._device_id in self.coordinator.data
@@ -92,7 +91,6 @@ class AdaxDevice(CoordinatorEntity[AdaxCloudCoordinator], ClimateEntity):
"""Gets the data for this particular device."""
return self.coordinator.data[self._device_id]
@override
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set hvac mode."""
if hvac_mode == HVACMode.HEAT:
@@ -110,7 +108,6 @@ class AdaxDevice(CoordinatorEntity[AdaxCloudCoordinator], ClimateEntity):
# Request data refresh from source to verify that update was successful
await self.coordinator.async_request_refresh()
@override
async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set new target temperature."""
if (temperature := kwargs.get(ATTR_TEMPERATURE)) is None:
@@ -120,7 +117,6 @@ class AdaxDevice(CoordinatorEntity[AdaxCloudCoordinator], ClimateEntity):
)
@callback
@override
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
if room := self.room:
@@ -165,7 +161,6 @@ class LocalAdaxDevice(CoordinatorEntity[AdaxLocalCoordinator], ClimateEntity):
manufacturer="Adax",
)
@override
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set hvac mode."""
if hvac_mode == HVACMode.HEAT:
@@ -184,7 +179,6 @@ class LocalAdaxDevice(CoordinatorEntity[AdaxLocalCoordinator], ClimateEntity):
self._attr_hvac_mode = hvac_mode
self.async_write_ha_state()
@override
async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set new target temperature."""
if (temperature := kwargs.get(ATTR_TEMPERATURE)) is None:
@@ -217,13 +211,11 @@ class LocalAdaxDevice(CoordinatorEntity[AdaxLocalCoordinator], ClimateEntity):
self._attr_target_temperature = target_temp
@callback
@override
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
self._update_hvac_attributes()
super()._handle_coordinator_update()
@override
async def async_added_to_hass(self) -> None:
"""When entity is added to hass."""
await super().async_added_to_hass()
+1 -2
View File
@@ -1,7 +1,7 @@
"""Config flow for Adax integration."""
import logging
from typing import Any, override
from typing import Any
import adax
import adax_local
@@ -39,7 +39,6 @@ class AdaxConfigFlow(ConfigFlow, domain=DOMAIN):
VERSION = 2
@override
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
+1 -3
View File
@@ -1,7 +1,7 @@
"""DataUpdateCoordinator for the Adax component."""
import logging
from typing import Any, cast, override
from typing import Any, cast
from adax import Adax
from adax_local import Adax as AdaxLocal
@@ -39,7 +39,6 @@ class AdaxCloudCoordinator(DataUpdateCoordinator[dict[str, dict[str, Any]]]):
websession=async_get_clientsession(hass),
)
@override
async def _async_update_data(self) -> dict[str, dict[str, Any]]:
"""Fetch data from the Adax."""
try:
@@ -88,7 +87,6 @@ class AdaxLocalCoordinator(DataUpdateCoordinator[dict[str, Any] | None]):
websession=async_get_clientsession(hass, verify_ssl=False),
)
@override
async def _async_update_data(self) -> dict[str, Any]:
"""Fetch data from the Adax."""
if result := await self.adax_data_handler.get_status():
+1 -3
View File
@@ -1,7 +1,7 @@
"""Support for Adax energy sensors."""
from dataclasses import dataclass
from typing import cast, override
from typing import cast
from homeassistant.components.sensor import (
SensorDeviceClass,
@@ -95,7 +95,6 @@ class AdaxSensor(CoordinatorEntity[AdaxCloudCoordinator], SensorEntity):
)
@property
@override
def available(self) -> bool:
"""Return True if entity is available."""
return (
@@ -105,7 +104,6 @@ class AdaxSensor(CoordinatorEntity[AdaxCloudCoordinator], SensorEntity):
)
@property
@override
def native_value(self) -> int | float | None:
"""Return the native value of the sensor."""
return self.coordinator.data[self._device_id].get(
@@ -1,6 +1,6 @@
"""Config flow to configure the AdGuard Home integration."""
from typing import Any, override
from typing import Any
from adguardhome import AdGuardHome, AdGuardHomeConnectionError
import voluptuous as vol
@@ -57,7 +57,6 @@ class AdGuardHomeFlowHandler(ConfigFlow, domain=DOMAIN):
errors=errors or {},
)
@override
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
@@ -103,7 +102,6 @@ class AdGuardHomeFlowHandler(ConfigFlow, domain=DOMAIN):
},
)
@override
async def async_step_hassio(
self, discovery_info: HassioServiceInfo
) -> ConfigFlowResult:
@@ -1,7 +1,5 @@
"""AdGuard Home base entity."""
from typing import override
from adguardhome import AdGuardHomeError
from homeassistant.config_entries import SOURCE_HASSIO
@@ -49,7 +47,6 @@ class AdGuardHomeEntity(Entity):
raise NotImplementedError
@property
@override
def device_info(self) -> DeviceInfo:
"""Return device information about this AdGuard Home instance."""
if self._entry.source == SOURCE_HASSIO:
+1 -2
View File
@@ -3,7 +3,7 @@
from collections.abc import Callable, Coroutine
from dataclasses import dataclass
from datetime import timedelta
from typing import Any, override
from typing import Any
from adguardhome import AdGuardHome
@@ -118,7 +118,6 @@ class AdGuardHomeSensor(AdGuardHomeEntity, SensorEntity):
]
)
@override
async def _adguard_update(self) -> None:
"""Update AdGuard Home entity."""
value = await self.entity_description.value_fn(self.adguard)
+1 -4
View File
@@ -3,7 +3,7 @@
from collections.abc import Callable, Coroutine
from dataclasses import dataclass
from datetime import timedelta
from typing import Any, override
from typing import Any
from adguardhome import AdGuardHome, AdGuardHomeError
@@ -113,7 +113,6 @@ class AdGuardHomeSwitch(AdGuardHomeEntity, SwitchEntity):
]
)
@override
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn off the switch."""
try:
@@ -125,7 +124,6 @@ class AdGuardHomeSwitch(AdGuardHomeEntity, SwitchEntity):
translation_key="error_while_turn_off",
) from err
@override
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn on the switch."""
try:
@@ -137,7 +135,6 @@ class AdGuardHomeSwitch(AdGuardHomeEntity, SwitchEntity):
translation_key="error_while_turn_on",
) from err
@override
async def _adguard_update(self) -> None:
"""Update AdGuard Home entity."""
self._attr_is_on = await self.entity_description.is_on_fn(self.adguard)()
+1 -3
View File
@@ -1,7 +1,7 @@
"""AdGuard Home Update platform."""
from datetime import timedelta
from typing import Any, override
from typing import Any
from adguardhome import AdGuardHomeError
@@ -50,7 +50,6 @@ class AdGuardHomeUpdate(AdGuardHomeEntity, UpdateEntity):
[DOMAIN, self.adguard.host, str(self.adguard.port), "update"]
)
@override
async def _adguard_update(self) -> None:
"""Update AdGuard Home entity."""
value = await self.adguard.update.update_available()
@@ -59,7 +58,6 @@ class AdGuardHomeUpdate(AdGuardHomeEntity, UpdateEntity):
self._attr_release_summary = value.announcement
self._attr_release_url = value.announcement_url
@override
async def async_install(
self, version: str | None, backup: bool, **kwargs: Any
) -> None:
@@ -1,7 +1,5 @@
"""Support for ADS binary sensors."""
from typing import override
import pyads
import voluptuous as vol
@@ -62,13 +60,11 @@ class AdsBinarySensor(AdsEntity, BinarySensorEntity):
super().__init__(ads_hub, name, ads_var)
self._attr_device_class = device_class or BinarySensorDeviceClass.MOVING
@override
async def async_added_to_hass(self) -> None:
"""Register device notification."""
await self.async_initialize_device(self._ads_var, pyads.PLCTYPE_BOOL)
@property
@override
def is_on(self) -> bool:
"""Return True if the entity is on."""
return self._state_dict[STATE_KEY_STATE]
+1 -9
View File
@@ -1,6 +1,6 @@
"""Support for ADS covers."""
from typing import Any, override
from typing import Any
import pyads
import voluptuous as vol
@@ -122,7 +122,6 @@ class AdsCover(AdsEntity, CoverEntity):
if ads_var_pos_set is not None:
self._attr_supported_features |= CoverEntityFeature.SET_POSITION
@override
async def async_added_to_hass(self) -> None:
"""Register device notification."""
if self._ads_var is not None:
@@ -134,7 +133,6 @@ class AdsCover(AdsEntity, CoverEntity):
)
@property
@override
def is_closed(self) -> bool | None:
"""Return if the cover is closed."""
if self._ads_var is not None:
@@ -144,18 +142,15 @@ class AdsCover(AdsEntity, CoverEntity):
return None
@property
@override
def current_cover_position(self) -> int:
"""Return current position of cover."""
return self._state_dict[STATE_KEY_POSITION]
@override
def stop_cover(self, **kwargs: Any) -> None:
"""Fire the stop action."""
if self._ads_var_stop:
self._ads_hub.write_by_name(self._ads_var_stop, True, pyads.PLCTYPE_BOOL)
@override
def set_cover_position(self, **kwargs: Any) -> None:
"""Set cover position."""
position = kwargs[ATTR_POSITION]
@@ -164,7 +159,6 @@ class AdsCover(AdsEntity, CoverEntity):
self._ads_var_pos_set, position, pyads.PLCTYPE_BYTE
)
@override
def open_cover(self, **kwargs: Any) -> None:
"""Move the cover up."""
if self._ads_var_open is not None:
@@ -172,7 +166,6 @@ class AdsCover(AdsEntity, CoverEntity):
elif self._ads_var_pos_set is not None:
self.set_cover_position(position=100)
@override
def close_cover(self, **kwargs: Any) -> None:
"""Move the cover down."""
if self._ads_var_close is not None:
@@ -181,7 +174,6 @@ class AdsCover(AdsEntity, CoverEntity):
self.set_cover_position(position=0)
@property
@override
def available(self) -> bool:
"""Return False if state has not been updated yet."""
if self._ads_var is not None or self._ads_var_position is not None:
+1 -2
View File
@@ -3,7 +3,7 @@
import asyncio
from asyncio import timeout
import logging
from typing import Any, override
from typing import Any
from homeassistant.helpers.entity import Entity
@@ -65,7 +65,6 @@ class AdsEntity(Entity):
_LOGGER.debug("Variable %s: Timeout during first update", ads_var)
@property
@override
def available(self) -> bool:
"""Return False if state has not been updated yet."""
return self._state_dict[STATE_KEY_STATE] is not None
+1 -7
View File
@@ -1,6 +1,6 @@
"""Support for ADS light sources."""
from typing import Any, override
from typing import Any
import pyads
import voluptuous as vol
@@ -120,7 +120,6 @@ class AdsLight(AdsEntity, LightEntity):
else DEFAULT_MAX_KELVIN
)
@override
async def async_added_to_hass(self) -> None:
"""Register device notification."""
await self.async_initialize_device(self._ads_var, pyads.PLCTYPE_BOOL)
@@ -140,24 +139,20 @@ class AdsLight(AdsEntity, LightEntity):
)
@property
@override
def brightness(self) -> int | None:
"""Return the brightness of the light (0..255)."""
return self._state_dict[STATE_KEY_BRIGHTNESS]
@property
@override
def color_temp_kelvin(self) -> int | None:
"""Return the color temperature in Kelvin."""
return self._state_dict[STATE_KEY_COLOR_TEMP_KELVIN]
@property
@override
def is_on(self) -> bool:
"""Return True if the entity is on."""
return self._state_dict[STATE_KEY_STATE]
@override
def turn_on(self, **kwargs: Any) -> None:
"""Turn the light on or set a specific dimmer value."""
brightness = kwargs.get(ATTR_BRIGHTNESS)
@@ -175,7 +170,6 @@ class AdsLight(AdsEntity, LightEntity):
self._ads_var_color_temp_kelvin, color_temp, pyads.PLCTYPE_UINT
)
@override
def turn_off(self, **kwargs: Any) -> None:
"""Turn the light off."""
self._ads_hub.write_by_name(self._ads_var, False, pyads.PLCTYPE_BOOL)
-4
View File
@@ -1,7 +1,5 @@
"""Support for ADS select entities."""
from typing import override
import pyads
import voluptuous as vol
@@ -63,7 +61,6 @@ class AdsSelect(AdsEntity, SelectEntity):
self._attr_options = options
self._attr_current_option = None
@override
async def async_added_to_hass(self) -> None:
"""Register device notification."""
await self.async_initialize_device(self._ads_var, pyads.PLCTYPE_INT)
@@ -71,7 +68,6 @@ class AdsSelect(AdsEntity, SelectEntity):
self._ads_var, pyads.PLCTYPE_INT, self._handle_ads_value
)
@override
def select_option(self, option: str) -> None:
"""Change the selected option."""
if option in self._attr_options:
-4
View File
@@ -1,7 +1,5 @@
"""Support for ADS sensors."""
from typing import override
import voluptuous as vol
from homeassistant.components.sensor import (
@@ -110,7 +108,6 @@ class AdsSensor(AdsEntity, SensorEntity):
self._attr_state_class = state_class
self._attr_native_unit_of_measurement = unit_of_measurement
@override
async def async_added_to_hass(self) -> None:
"""Register device notification."""
await self.async_initialize_device(
@@ -121,7 +118,6 @@ class AdsSensor(AdsEntity, SensorEntity):
)
@property
@override
def native_value(self) -> StateType:
"""Return the state of the device."""
return self._state_dict[STATE_KEY_STATE]
+1 -5
View File
@@ -1,6 +1,6 @@
"""Support for ADS switch platform."""
from typing import Any, override
from typing import Any
import pyads
import voluptuous as vol
@@ -46,23 +46,19 @@ def setup_platform(
class AdsSwitch(AdsEntity, SwitchEntity):
"""Representation of an ADS switch device."""
@override
async def async_added_to_hass(self) -> None:
"""Register device notification."""
await self.async_initialize_device(self._ads_var, pyads.PLCTYPE_BOOL)
@property
@override
def is_on(self) -> bool:
"""Return True if the entity is on."""
return self._state_dict[STATE_KEY_STATE]
@override
def turn_on(self, **kwargs: Any) -> None:
"""Turn the switch on."""
self._ads_hub.write_by_name(self._ads_var, True, pyads.PLCTYPE_BOOL)
@override
def turn_off(self, **kwargs: Any) -> None:
"""Turn the switch off."""
self._ads_hub.write_by_name(self._ads_var, False, pyads.PLCTYPE_BOOL)
-5
View File
@@ -1,7 +1,5 @@
"""Support for ADS valves."""
from typing import override
import pyads
import voluptuous as vol
@@ -69,18 +67,15 @@ class AdsValve(AdsEntity, ValveEntity):
self._attr_reports_position = False
self._attr_is_closed = True
@override
async def async_added_to_hass(self) -> None:
"""Register device notification."""
await self.async_initialize_device(self._ads_var, pyads.PLCTYPE_BOOL)
@override
def open_valve(self, **kwargs) -> None:
"""Open the valve."""
self._ads_hub.write_by_name(self._ads_var, True, pyads.PLCTYPE_BOOL)
self._attr_is_closed = False
@override
def close_valve(self, **kwargs) -> None:
"""Close the valve."""
self._ads_hub.write_by_name(self._ads_var, False, pyads.PLCTYPE_BOOL)
@@ -1,7 +1,5 @@
"""Binary Sensor platform for Advantage Air integration."""
from typing import override
from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass,
BinarySensorEntity,
@@ -57,7 +55,6 @@ class AdvantageAirFilter(AdvantageAirAcEntity, BinarySensorEntity):
self._attr_unique_id += "-filter"
@property
@override
def is_on(self) -> bool:
"""Return if filter needs cleaning."""
return self._ac["filterCleanStatus"]
@@ -77,7 +74,6 @@ class AdvantageAirZoneMotion(AdvantageAirZoneEntity, BinarySensorEntity):
self._attr_unique_id += "-motion"
@property
@override
def is_on(self) -> bool:
"""Return if motion is detect."""
return self._zone["motion"] == 20
@@ -98,7 +94,6 @@ class AdvantageAirZoneMyZone(AdvantageAirZoneEntity, BinarySensorEntity):
self._attr_unique_id += "-myzone"
@property
@override
def is_on(self) -> bool:
"""Return if this zone is the myZone."""
return self._zone["number"] == self._ac["myZone"]
@@ -2,7 +2,7 @@
from decimal import Decimal
import logging
from typing import Any, override
from typing import Any
from homeassistant.components.climate import (
ATTR_TARGET_TEMP_HIGH,
@@ -156,14 +156,12 @@ class AdvantageAirAC(AdvantageAirAcEntity, ClimateEntity):
)
@callback
@override
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
self._async_configure_preset()
super()._handle_coordinator_update()
@property
@override
def current_temperature(self) -> float | None:
"""Return the selected zones current temperature."""
if self._myzone:
@@ -171,7 +169,6 @@ class AdvantageAirAC(AdvantageAirAcEntity, ClimateEntity):
return None
@property
@override
def target_temperature(self) -> float | None:
"""Return the current target temperature."""
# If the system is in MyZone mode, and a zone is set,
@@ -181,7 +178,6 @@ class AdvantageAirAC(AdvantageAirAcEntity, ClimateEntity):
return self._ac["setTemp"]
@property
@override
def hvac_mode(self) -> HVACMode | None:
"""Return the current HVAC modes."""
if self._ac["state"] == ADVANTAGE_AIR_STATE_ON:
@@ -189,7 +185,6 @@ class AdvantageAirAC(AdvantageAirAcEntity, ClimateEntity):
return HVACMode.OFF
@property
@override
def hvac_action(self) -> HVACAction | None:
"""Return the current running HVAC action."""
if self._ac["state"] == ADVANTAGE_AIR_STATE_OFF:
@@ -201,29 +196,24 @@ class AdvantageAirAC(AdvantageAirAcEntity, ClimateEntity):
return HVAC_ACTIONS.get(self._ac["mode"])
@property
@override
def fan_mode(self) -> str | None:
"""Return the current fan modes."""
return FAN_AUTO if self._ac["fan"] == ADVANTAGE_AIR_MYFAN else self._ac["fan"]
@property
@override
def target_temperature_high(self) -> float | None:
"""Return the temperature cool mode is enabled."""
return self._ac.get(ADVANTAGE_AIR_COOL_TARGET)
@property
@override
def target_temperature_low(self) -> float | None:
"""Return the temperature heat mode is enabled."""
return self._ac.get(ADVANTAGE_AIR_HEAT_TARGET)
@override
async def async_turn_on(self) -> None:
"""Set the HVAC State to on."""
await self.async_update_ac({"state": ADVANTAGE_AIR_STATE_ON})
@override
async def async_turn_off(self) -> None:
"""Set the HVAC State to off."""
await self.async_update_ac(
@@ -232,7 +222,6 @@ class AdvantageAirAC(AdvantageAirAcEntity, ClimateEntity):
}
)
@override
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set the HVAC Mode and State."""
if hvac_mode == HVACMode.OFF:
@@ -247,7 +236,6 @@ class AdvantageAirAC(AdvantageAirAcEntity, ClimateEntity):
}
)
@override
async def async_set_fan_mode(self, fan_mode: str) -> None:
"""Set the Fan Mode."""
if fan_mode == FAN_AUTO and self._ac.get(ADVANTAGE_AIR_AUTOFAN_ENABLED):
@@ -256,7 +244,6 @@ class AdvantageAirAC(AdvantageAirAcEntity, ClimateEntity):
mode = fan_mode
await self.async_update_ac({"fan": mode})
@override
async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set the Temperature."""
if ATTR_TEMPERATURE in kwargs:
@@ -269,7 +256,6 @@ class AdvantageAirAC(AdvantageAirAcEntity, ClimateEntity):
}
)
@override
async def async_set_preset_mode(self, preset_mode: str) -> None:
"""Set the preset mode."""
change = {}
@@ -303,7 +289,6 @@ class AdvantageAirZone(AdvantageAirZoneEntity, ClimateEntity):
self._attr_name = self._zone["name"]
@property
@override
def hvac_mode(self) -> HVACMode:
"""Return the current state as HVAC mode."""
if self._zone["state"] == ADVANTAGE_AIR_STATE_OPEN:
@@ -311,7 +296,6 @@ class AdvantageAirZone(AdvantageAirZoneEntity, ClimateEntity):
return HVACMode.OFF
@property
@override
def hvac_action(self) -> HVACAction | None:
"""Return the HVAC action.
@@ -332,28 +316,23 @@ class AdvantageAirZone(AdvantageAirZoneEntity, ClimateEntity):
return HVACAction.OFF
@property
@override
def current_temperature(self) -> float | None:
"""Return the current temperature."""
return self._zone["measuredTemp"]
@property
@override
def target_temperature(self) -> float:
"""Return the target temperature."""
return self._zone["setTemp"]
@override
async def async_turn_on(self) -> None:
"""Set the HVAC State to on."""
await self.async_update_zone({"state": ADVANTAGE_AIR_STATE_OPEN})
@override
async def async_turn_off(self) -> None:
"""Set the HVAC State to off."""
await self.async_update_zone({"state": ADVANTAGE_AIR_STATE_CLOSE})
@override
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set the HVAC Mode and State."""
if hvac_mode == HVACMode.OFF:
@@ -361,7 +340,6 @@ class AdvantageAirZone(AdvantageAirZoneEntity, ClimateEntity):
else:
await self.async_turn_on()
@override
async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set the Temperature."""
temp = kwargs.get(ATTR_TEMPERATURE)
@@ -1,6 +1,6 @@
"""Config Flow for Advantage Air integration."""
from typing import Any, override
from typing import Any
from advantage_air import ApiError, advantage_air
import voluptuous as vol
@@ -28,7 +28,6 @@ class AdvantageAirConfigFlow(ConfigFlow, domain=DOMAIN):
DOMAIN = DOMAIN
@override
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
@@ -2,7 +2,7 @@
from datetime import timedelta
import logging
from typing import Any, override
from typing import Any
from advantage_air import ApiError, advantage_air
@@ -45,7 +45,6 @@ class AdvantageAirCoordinator(DataUpdateCoordinator[dict[str, Any]]):
)
self.api = api
@override
async def _async_update_data(self) -> dict[str, Any]:
"""Fetch data from the API."""
try:
@@ -1,6 +1,6 @@
"""Cover platform for Advantage Air integration."""
from typing import Any, override
from typing import Any
from homeassistant.components.cover import (
ATTR_POSITION,
@@ -66,32 +66,27 @@ class AdvantageAirZoneVent(AdvantageAirZoneEntity, CoverEntity):
self._attr_name = self._zone["name"]
@property
@override
def is_closed(self) -> bool:
"""Return if vent is fully closed."""
return self._zone["state"] == ADVANTAGE_AIR_STATE_CLOSE
@property
@override
def current_cover_position(self) -> int:
"""Return vents current position as a percentage."""
if self._zone["state"] == ADVANTAGE_AIR_STATE_OPEN:
return self._zone["value"]
return 0
@override
async def async_open_cover(self, **kwargs: Any) -> None:
"""Fully open zone vent."""
await self.async_update_zone(
{"state": ADVANTAGE_AIR_STATE_OPEN, "value": 100},
)
@override
async def async_close_cover(self, **kwargs: Any) -> None:
"""Fully close zone vent."""
await self.async_update_zone({"state": ADVANTAGE_AIR_STATE_CLOSE})
@override
async def async_set_cover_position(self, **kwargs: Any) -> None:
"""Change vent position."""
position = round(kwargs[ATTR_POSITION] / 5) * 5
@@ -122,17 +117,14 @@ class AdvantageAirThingCover(AdvantageAirThingEntity, CoverEntity):
self._attr_device_class = device_class
@property
@override
def is_closed(self) -> bool:
"""Return if cover is fully closed."""
return self._data["value"] == 0
@override
async def async_open_cover(self, **kwargs: Any) -> None:
"""Fully open zone vent."""
return await self.async_turn_on()
@override
async def async_close_cover(self, **kwargs: Any) -> None:
"""Fully close zone vent."""
return await self.async_turn_off()
@@ -1,6 +1,6 @@
"""Light platform for Advantage Air integration."""
from typing import Any, override
from typing import Any
from homeassistant.components.light import ATTR_BRIGHTNESS, ColorMode, LightEntity
from homeassistant.core import HomeAssistant
@@ -70,17 +70,14 @@ class AdvantageAirLight(AdvantageAirEntity, LightEntity):
return self.coordinator.data["myLights"]["lights"][self._id]
@property
@override
def is_on(self) -> bool:
"""Return if the light is on."""
return self._data["state"] == ADVANTAGE_AIR_STATE_ON
@override
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the light on."""
await self.async_update_state(True)
@override
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the light off."""
await self.async_update_state(False)
@@ -102,12 +99,10 @@ class AdvantageAirLightDimmable(AdvantageAirLight):
)
@property
@override
def brightness(self) -> int:
"""Return the brightness of this light between 0..255."""
return round(self._data["value"] * 255 / 100)
@override
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the light on and optionally set the brightness."""
if ATTR_BRIGHTNESS in kwargs:
@@ -129,12 +124,10 @@ class AdvantageAirThingLightDimmable(AdvantageAirThingEntity, LightEntity):
_attr_supported_color_modes = {ColorMode.BRIGHTNESS}
@property
@override
def brightness(self) -> int:
"""Return the brightness of this light between 0..255."""
return round(self._data["value"] * 255 / 100)
@override
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the light on by setting the brightness."""
await self.async_update_value(round(kwargs.get(ATTR_BRIGHTNESS, 255) / 2.55))
@@ -18,15 +18,9 @@ rules:
comment: Data descriptions missing
dependency-transparency: done
docs-actions: done
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: done
docs-installation-instructions: todo
docs-removal-instructions: todo
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup:
status: exempt
comment: Entities do not explicitly subscribe to events.
@@ -1,7 +1,5 @@
"""Select platform for Advantage Air integration."""
from typing import override
from homeassistant.components.select import SelectEntity
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
@@ -50,12 +48,10 @@ class AdvantageAirMyZone(AdvantageAirAcEntity, SelectEntity):
self._attr_options.append(zone["name"])
@property
@override
def current_option(self) -> str:
"""Return the current MyZone."""
return self._number_to_name[self._ac["myZone"]]
@override
async def async_select_option(self, option: str) -> None:
"""Set the MyZone."""
await self.async_update_ac({"myZone": self._name_to_number[option]})
@@ -1,7 +1,7 @@
"""Sensor platform for Advantage Air integration."""
from decimal import Decimal
from typing import Any, override
from typing import Any
from homeassistant.components.sensor import (
SensorDeviceClass,
@@ -67,13 +67,11 @@ class AdvantageAirTimeTo(AdvantageAirAcEntity, SensorEntity):
self._attr_unique_id += f"-timeto{action}"
@property
@override
def native_value(self) -> Decimal:
"""Return the current value."""
return self._ac[self._time_key]
@property
@override
def icon(self) -> str:
"""Return a representative icon of the timer."""
if self._ac[self._time_key] > 0:
@@ -102,7 +100,6 @@ class AdvantageAirZoneVent(AdvantageAirZoneEntity, SensorEntity):
self._attr_unique_id += "-vent"
@property
@override
def native_value(self) -> Decimal:
"""Return the current value of the air vent."""
if self._zone["state"] == ADVANTAGE_AIR_STATE_OPEN:
@@ -110,7 +107,6 @@ class AdvantageAirZoneVent(AdvantageAirZoneEntity, SensorEntity):
return Decimal(0)
@property
@override
def icon(self) -> str:
"""Return a representative icon."""
if self._zone["state"] == ADVANTAGE_AIR_STATE_OPEN:
@@ -134,13 +130,11 @@ class AdvantageAirZoneSignal(AdvantageAirZoneEntity, SensorEntity):
self._attr_unique_id += "-signal"
@property
@override
def native_value(self) -> Decimal:
"""Return the current value of the wireless signal."""
return self._zone["rssi"]
@property
@override
def icon(self) -> str:
"""Return a representative icon."""
if self._zone["rssi"] >= 80:
@@ -172,7 +166,6 @@ class AdvantageAirZoneTemp(AdvantageAirZoneEntity, SensorEntity):
self._attr_unique_id += "-temp"
@property
@override
def native_value(self) -> Decimal:
"""Return the current value of the measured temperature."""
return self._zone["measuredTemp"]
@@ -1,6 +1,6 @@
"""Switch platform for Advantage Air integration."""
from typing import Any, override
from typing import Any
from homeassistant.components.switch import SwitchDeviceClass, SwitchEntity
from homeassistant.core import HomeAssistant
@@ -57,17 +57,14 @@ class AdvantageAirFreshAir(AdvantageAirAcEntity, SwitchEntity):
self._attr_unique_id += "-freshair"
@property
@override
def is_on(self) -> bool:
"""Return the fresh air status."""
return self._ac["freshAirStatus"] == ADVANTAGE_AIR_STATE_ON
@override
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn fresh air on."""
await self.async_update_ac({"freshAirStatus": ADVANTAGE_AIR_STATE_ON})
@override
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn fresh air off."""
await self.async_update_ac({"freshAirStatus": ADVANTAGE_AIR_STATE_OFF})
@@ -86,17 +83,14 @@ class AdvantageAirMyFan(AdvantageAirAcEntity, SwitchEntity):
self._attr_unique_id += "-myfan"
@property
@override
def is_on(self) -> bool:
"""Return the MyFan status."""
return self._ac[ADVANTAGE_AIR_AUTOFAN_ENABLED]
@override
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn MyFan on."""
await self.async_update_ac({ADVANTAGE_AIR_AUTOFAN_ENABLED: True})
@override
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn MyFan off."""
await self.async_update_ac({ADVANTAGE_AIR_AUTOFAN_ENABLED: False})
@@ -115,17 +109,14 @@ class AdvantageAirNightMode(AdvantageAirAcEntity, SwitchEntity):
self._attr_unique_id += "-nightmode"
@property
@override
def is_on(self) -> bool:
"""Return the Night Mode status."""
return self._ac[ADVANTAGE_AIR_NIGHT_MODE_ENABLED]
@override
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn Night Mode on."""
await self.async_update_ac({ADVANTAGE_AIR_NIGHT_MODE_ENABLED: True})
@override
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn Night Mode off."""
await self.async_update_ac({ADVANTAGE_AIR_NIGHT_MODE_ENABLED: False})
@@ -1,7 +1,5 @@
"""Advantage Air Update platform."""
from typing import override
from homeassistant.components.update import UpdateEntity
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceInfo
@@ -42,13 +40,11 @@ class AdvantageAirApp(AdvantageAirEntity, UpdateEntity):
)
@property
@override
def installed_version(self) -> str:
"""Return the current app version."""
return self.coordinator.data["system"]["myAppRev"]
@property
@override
def latest_version(self) -> str:
"""Return if there is an update."""
if self.coordinator.data["system"]["needsUpdate"]:
@@ -1,6 +1,6 @@
"""Config flow for AEMET OpenData."""
from typing import Any, override
from typing import Any
from aemet_opendata.exceptions import AuthError
from aemet_opendata.interface import AEMET, ConnectionOptions
@@ -31,7 +31,6 @@ OPTIONS_FLOW = {
class AemetConfigFlow(ConfigFlow, domain=DOMAIN):
"""Config flow for AEMET OpenData."""
@override
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
@@ -83,7 +82,6 @@ class AemetConfigFlow(ConfigFlow, domain=DOMAIN):
@staticmethod
@callback
@override
def async_get_options_flow(
config_entry: ConfigEntry,
) -> SchemaOptionsFlowHandler:
@@ -4,7 +4,7 @@ from asyncio import timeout
from dataclasses import dataclass
from datetime import timedelta
import logging
from typing import Any, Final, cast, override
from typing import Any, Final, cast
from aemet_opendata.const import (
AOD_CONDITION,
@@ -60,7 +60,6 @@ class WeatherUpdateCoordinator(DataUpdateCoordinator):
update_interval=WEATHER_UPDATE_INTERVAL,
)
@override
async def _async_update_data(self) -> dict[str, Any]:
"""Update coordinator data."""
async with timeout(API_TIMEOUT):
+1 -2
View File
@@ -1,6 +1,6 @@
"""Support for the AEMET OpenData images."""
from typing import Final, override
from typing import Final
from aemet_opendata.const import AOD_DATETIME, AOD_IMG_BYTES, AOD_IMG_TYPE, AOD_RADAR
from aemet_opendata.helpers import dict_nested_value
@@ -68,7 +68,6 @@ class AemetImage(AemetEntity, ImageEntity):
self._async_update_attrs()
@callback
@override
def _handle_coordinator_update(self) -> None:
"""Update attributes when the coordinator updates."""
self._async_update_attrs()
+1 -2
View File
@@ -3,7 +3,7 @@
from collections.abc import Callable
from dataclasses import dataclass
from datetime import datetime
from typing import Final, override
from typing import Final
from aemet_opendata.const import (
AOD_CONDITION,
@@ -399,7 +399,6 @@ class AemetSensor(AemetEntity, SensorEntity):
self._attr_unique_id = f"{unique_id}-{description.key}"
@property
@override
def native_value(self):
"""Return the state of the device."""
value = self.get_aemet_value(self.entity_description.keys)
-11
View File
@@ -1,7 +1,5 @@
"""Support for the AEMET OpenData service."""
from typing import override
from aemet_opendata.const import (
AOD_CONDITION,
AOD_FORECAST_DAILY,
@@ -76,56 +74,47 @@ class AemetWeather(
self._attr_unique_id = unique_id
@property
@override
def condition(self) -> str | None:
"""Return the current condition."""
cond = self.get_aemet_value([AOD_WEATHER, AOD_CONDITION])
return CONDITIONS_MAP.get(cond)
@callback
@override
def _async_forecast_daily(self) -> list[Forecast]:
"""Return the daily forecast in native units."""
return self.get_aemet_forecast(AOD_FORECAST_DAILY)
@callback
@override
def _async_forecast_hourly(self) -> list[Forecast]:
"""Return the hourly forecast in native units."""
return self.get_aemet_forecast(AOD_FORECAST_HOURLY)
@property
@override
def humidity(self) -> float | None:
"""Return the humidity."""
return self.get_aemet_value([AOD_WEATHER, AOD_HUMIDITY])
@property
@override
def native_pressure(self) -> float | None:
"""Return the pressure."""
return self.get_aemet_value([AOD_WEATHER, AOD_PRESSURE])
@property
@override
def native_temperature(self) -> float | None:
"""Return the temperature."""
return self.get_aemet_value([AOD_WEATHER, AOD_TEMP])
@property
@override
def wind_bearing(self) -> float | None:
"""Return the wind bearing."""
return self.get_aemet_value([AOD_WEATHER, AOD_WIND_DIRECTION])
@property
@override
def native_wind_gust_speed(self) -> float | None:
"""Return the wind gust speed in native units."""
return self.get_aemet_value([AOD_WEATHER, AOD_WIND_SPEED_MAX])
@property
@override
def native_wind_speed(self) -> float | None:
"""Return the wind speed."""
return self.get_aemet_value([AOD_WEATHER, AOD_WIND_SPEED])
@@ -1,7 +1,7 @@
"""Config flow for AfterShip integration."""
import logging
from typing import Any, override
from typing import Any
from pyaftership import AfterShip, AfterShipException
import voluptuous as vol
@@ -20,7 +20,6 @@ class AfterShipConfigFlow(ConfigFlow, domain=DOMAIN):
VERSION = 1
@override
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
+1 -4
View File
@@ -1,7 +1,7 @@
"""Support for non-delivered packages recorded in AfterShip."""
import logging
from typing import Any, Final, override
from typing import Any, Final
from pyaftership import AfterShip, AfterShipException
@@ -96,18 +96,15 @@ class AfterShipSensor(SensorEntity):
self._attr_name = name
@property
@override
def native_value(self) -> int | None:
"""Return the state of the sensor."""
return self._state
@property
@override
def extra_state_attributes(self) -> dict[str, str]:
"""Return attributes for the sensor."""
return self._attributes
@override
async def async_added_to_hass(self) -> None:
"""Register callbacks."""
self.async_on_remove(
@@ -1,7 +1,5 @@
"""Support for Agent DVR Alarm Control Panels."""
from typing import override
from homeassistant.components.alarm_control_panel import (
AlarmControlPanelEntity,
AlarmControlPanelEntityFeature,
@@ -72,27 +70,23 @@ class AgentBaseStation(AlarmControlPanelEntity):
else:
self._attr_alarm_state = AlarmControlPanelState.DISARMED
@override
async def async_alarm_disarm(self, code: str | None = None) -> None:
"""Send disarm command."""
await self._client.disarm()
self._attr_alarm_state = AlarmControlPanelState.DISARMED
@override
async def async_alarm_arm_away(self, code: str | None = None) -> None:
"""Send arm away command. Uses custom mode."""
await self._client.arm()
await self._client.set_active_profile(CONF_AWAY_MODE_NAME)
self._attr_alarm_state = AlarmControlPanelState.ARMED_AWAY
@override
async def async_alarm_arm_home(self, code: str | None = None) -> None:
"""Send arm home command. Uses custom mode."""
await self._client.arm()
await self._client.set_active_profile(CONF_HOME_MODE_NAME)
self._attr_alarm_state = AlarmControlPanelState.ARMED_HOME
@override
async def async_alarm_arm_night(self, code: str | None = None) -> None:
"""Send arm night command. Uses custom mode."""
await self._client.arm()
@@ -2,7 +2,6 @@
from datetime import timedelta
import logging
from typing import override
from agent import AgentError
@@ -96,7 +95,6 @@ class AgentCamera(MjpegCamera):
}
@property
@override
def is_recording(self) -> bool:
"""Return whether the monitor is recording."""
return self.device.recording
@@ -117,13 +115,11 @@ class AgentCamera(MjpegCamera):
return self.device.connected
@property
@override
def is_on(self) -> bool:
"""Return true if on."""
return self.device.online
@property
@override
def motion_detection_enabled(self) -> bool:
"""Return the camera motion detection status."""
return self.device.detector_active
@@ -136,12 +132,10 @@ class AgentCamera(MjpegCamera):
"""Disable alerts."""
await self.device.alerts_off()
@override
async def async_enable_motion_detection(self) -> None:
"""Enable motion detection."""
await self.device.detector_on()
@override
async def async_disable_motion_detection(self) -> None:
"""Disable motion detection."""
await self.device.detector_off()
@@ -154,7 +148,6 @@ class AgentCamera(MjpegCamera):
"""Stop recording."""
await self.device.record_stop()
@override
async def async_turn_on(self) -> None:
"""Enable the camera."""
await self.device.enable()
@@ -163,7 +156,6 @@ class AgentCamera(MjpegCamera):
"""Take a snapshot."""
await self.device.snapshot()
@override
async def async_turn_off(self) -> None:
"""Disable the camera."""
await self.device.disable()
@@ -1,7 +1,7 @@
"""Config flow to configure Agent devices."""
from contextlib import suppress
from typing import Any, override
from typing import Any
from agent import AgentConnectionError, AgentError
from agent.a import Agent
@@ -20,7 +20,6 @@ DEFAULT_PORT = 8090
class AgentFlowHandler(ConfigFlow, domain=DOMAIN):
"""Handle an Agent config flow."""
@override
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:

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