mirror of
https://github.com/home-assistant/core.git
synced 2026-06-25 16:15:28 +02:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 77fe61db3e | |||
| ac3a1493aa | |||
| fbda9f4fc3 | |||
| 5802cada22 | |||
| f357dd3d49 | |||
| 1eca57a01b | |||
| 0dde553653 | |||
| 5943b70577 | |||
| 99ee92029d | |||
| 717acdf771 | |||
| e76d83241e | |||
| 6024f96d56 | |||
| 5eec6167cc | |||
| 184025cfb4 |
@@ -6,7 +6,6 @@
|
||||
|
||||
- Start review comments with a short, one-sentence summary of the suggested fix.
|
||||
- Do not comment on code style, formatting or linting issues.
|
||||
- Flag comments that over-explain straightforward code, narrate the obvious, or read like AI commentary (multi-sentence justifications for a single line).
|
||||
- A Pull Request with a dependency version bump should only contain changes required for the version bump. If the PR includes other changes, request that they are removed from the PR.
|
||||
|
||||
# GitHub Copilot & Claude Code Instructions
|
||||
@@ -24,7 +23,7 @@ This repository contains the core of Home Assistant, a Python 3 based home autom
|
||||
|
||||
## Development Commands
|
||||
|
||||
- When entering a new environment or worktree, run `script/setup` to set up the virtual environment with all development dependencies (pylint, pre-commit hooks, etc.). This is required before committing. If uv reports that no download was found for the required Python version, the environment is running an outdated version of uv; upgrade it with `curl -LsSf https://astral.sh/uv/install.sh | sh` and run `script/setup` again.
|
||||
- When entering a new environment or worktree, run `script/setup` to set up the virtual environment with all development dependencies (pylint, pre-commit hooks, etc.). This is required before committing.
|
||||
- .vscode/tasks.json contains useful commands used for development.
|
||||
- After finishing a code session, run `uv run prek run --all-files` to check for linting and formatting issues.
|
||||
|
||||
@@ -51,6 +50,4 @@ This repository contains the core of Home Assistant, a Python 3 based home autom
|
||||
- Integrations with Platinum or Gold level in the Integration Quality Scale reflect a high standard of code quality and maintainability. When looking for examples of something, these are good places to start. The level is indicated in the manifest.json of the integration.
|
||||
- When reviewing entity actions, do not suggest extra defensive checks for input fields that are already validated by Home Assistant's service/action schemas and entity selection filters. Suggest additional guards only when data bypasses those validators or is transformed into a less-safe form.
|
||||
- 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.
|
||||
- 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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -193,7 +193,7 @@ jobs:
|
||||
echo "${GITHUB_SHA};${GITHUB_REF};${GITHUB_EVENT_NAME};${GITHUB_ACTOR}" > rootfs/OFFICIAL_IMAGE
|
||||
|
||||
- name: Build base image
|
||||
uses: home-assistant/builder/actions/build-image@4de35182ce1e329181bffcbcc84d33db5e2c7e10 # 2026.06.0
|
||||
uses: home-assistant/builder/actions/build-image@62a1597b84b3461abad9816d9cd92862a2b542c3 # 2026.03.2
|
||||
with:
|
||||
arch: ${{ matrix.arch }}
|
||||
build-args: |
|
||||
@@ -245,7 +245,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
|
||||
|
||||
@@ -264,7 +264,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Build machine image
|
||||
uses: home-assistant/builder/actions/build-image@4de35182ce1e329181bffcbcc84d33db5e2c7e10 # 2026.06.0
|
||||
uses: home-assistant/builder/actions/build-image@62a1597b84b3461abad9816d9cd92862a2b542c3 # 2026.03.2
|
||||
with:
|
||||
arch: ${{ matrix.arch }}
|
||||
build-args: |
|
||||
@@ -292,7 +292,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -339,6 +339,8 @@ jobs:
|
||||
steps:
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
|
||||
with:
|
||||
cosign-release: "v2.5.3"
|
||||
|
||||
- name: Login to DockerHub
|
||||
if: matrix.registry == 'docker.io/homeassistant'
|
||||
@@ -469,7 +471,7 @@ jobs:
|
||||
if: github.repository_owner == 'home-assistant' && needs.init.outputs.publish == 'true'
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -516,7 +518,7 @@ jobs:
|
||||
HASSFEST_IMAGE_TAG: ghcr.io/home-assistant/hassfest:${{ needs.init.outputs.version }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -46,24 +50,19 @@ jobs:
|
||||
check-latest: true
|
||||
- name: Install script dependencies
|
||||
run: pip install -r script/check_requirements/requirements.txt
|
||||
- name: Collect PR diff and head SHA
|
||||
id: pr
|
||||
- name: Collect PR diff
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PR_NUMBER: ${{ inputs.pull_request_number || github.event.pull_request.number }}
|
||||
run: |
|
||||
mkdir -p deterministic
|
||||
gh pr diff "${PR_NUMBER}" > deterministic/pr.diff
|
||||
HEAD_SHA=$(gh pr view "${PR_NUMBER}" --json headRefOid --jq '.headRefOid')
|
||||
echo "head_sha=${HEAD_SHA}" >> "${GITHUB_OUTPUT}"
|
||||
- name: Run deterministic checks
|
||||
env:
|
||||
PR_NUMBER: ${{ inputs.pull_request_number || github.event.pull_request.number }}
|
||||
HEAD_SHA: ${{ steps.pr.outputs.head_sha }}
|
||||
run: |
|
||||
python -m script.check_requirements \
|
||||
--pr-number "${PR_NUMBER}" \
|
||||
--head-sha "${HEAD_SHA}" \
|
||||
--diff deterministic/pr.diff \
|
||||
--output deterministic/results.json
|
||||
- name: Upload deterministic-results artifact
|
||||
|
||||
+7
-81
@@ -1,4 +1,4 @@
|
||||
# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"7b142e96e0f8b454cdcc9c0c25070cf9a52c44d83a6b1fbc3ad6725b6567337c","body_hash":"3894ded07d5934ac5f29d160ffb1f9115cf72b6da8a7e453a4d4f69e8641a48e","compiler_version":"v0.79.6","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.60"}}
|
||||
# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"e4fcdd04986da27ef3059faa0cea3d64bb879fe12085ebfdec0041bbc31ec181","body_hash":"3894ded07d5934ac5f29d160ffb1f9115cf72b6da8a7e453a4d4f69e8641a48e","compiler_version":"v0.79.6","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.60"}}
|
||||
# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"df4cb1c069e1874edd31b4311f1884172cec0e10","version":"v6.0.3"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"v0.79.6","version":"v0.79.6"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.2","digest":"sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2","digest":"sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2@sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2","digest":"sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2@sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.25","digest":"sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.25@sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa"},{"image":"ghcr.io/github/github-mcp-server:v1.1.2","digest":"sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c","pinned_image":"ghcr.io/github/github-mcp-server:v1.1.2@sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c"}]}
|
||||
# ___ _ _
|
||||
# / _ \ | | (_)
|
||||
@@ -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: |
|
||||
@@ -345,7 +345,6 @@ jobs:
|
||||
needs:
|
||||
- activation
|
||||
- extract_pr_number
|
||||
- gate
|
||||
if: needs.activation.outputs.daily_effective_workflow_exceeded != 'true'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
@@ -404,7 +403,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
|
||||
@@ -995,7 +994,6 @@ jobs:
|
||||
- agent
|
||||
- detection
|
||||
- extract_pr_number
|
||||
- gate
|
||||
- safe_outputs
|
||||
if: >
|
||||
always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true' ||
|
||||
@@ -1236,7 +1234,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 ---
|
||||
@@ -1430,8 +1428,8 @@ jobs:
|
||||
}
|
||||
|
||||
extract_pr_number:
|
||||
needs: gate
|
||||
if: needs.gate.outputs.skip != 'true' && github.event.workflow_run.conclusion == 'success'
|
||||
needs: activation
|
||||
if: github.event.workflow_run.conclusion == 'success'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
@@ -1462,78 +1460,6 @@ jobs:
|
||||
PR=$(jq -r '.pr_number' /tmp/deterministic/results.json)
|
||||
echo "pr_number=${PR}" >> "${GITHUB_OUTPUT}"
|
||||
|
||||
gate:
|
||||
needs: activation
|
||||
if: github.event.workflow_run.conclusion == 'success'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
pull-requests: read
|
||||
|
||||
outputs:
|
||||
skip: ${{ steps.gate.outputs.skip }}
|
||||
steps:
|
||||
- name: Configure GH_HOST for enterprise compatibility
|
||||
id: ghes-host-config
|
||||
shell: bash
|
||||
# zizmor: ignore[github-env] - GITHUB_SERVER_URL is set by GitHub Actions, not user input.
|
||||
run: |
|
||||
# Derive GH_HOST from GITHUB_SERVER_URL so the gh CLI targets the correct
|
||||
# GitHub instance (GHES/GHEC). On github.com this is a harmless no-op.
|
||||
GH_HOST="${GITHUB_SERVER_URL#https://}"
|
||||
GH_HOST="${GH_HOST#http://}"
|
||||
echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV"
|
||||
- name: Download deterministic-results artifact
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
name: check-requirements-deterministic
|
||||
path: /tmp/gate
|
||||
run-id: ${{ github.event.workflow_run.id }}
|
||||
- name: Decide whether requirements changed since the last comment
|
||||
id: gate
|
||||
run: |
|
||||
PR=$(jq -r '.pr_number' /tmp/gate/results.json)
|
||||
HEAD=$(jq -r '.head_sha // empty' /tmp/gate/results.json)
|
||||
if [ -z "${HEAD}" ]; then
|
||||
echo "Artifact has no head_sha; running the agent."
|
||||
exit 0
|
||||
fi
|
||||
# Recover the commit recorded in the most recent requirements-check
|
||||
# comment from the "Checked at commit" link
|
||||
PRIOR=$(gh api --paginate "repos/${GITHUB_REPOSITORY}/issues/${PR}/comments" \
|
||||
--jq '.[] | select(.body | contains("<!-- requirements-check -->")) | .body' \
|
||||
| grep -oiE '/commit/[0-9a-f]{40}' \
|
||||
| grep -oiE '[0-9a-f]{40}' | tail -1 || true)
|
||||
if [ -z "${PRIOR}" ]; then
|
||||
echo "No previous comment with a recorded commit; running the agent."
|
||||
exit 0
|
||||
fi
|
||||
if [ "${PRIOR}" = "${HEAD}" ]; then
|
||||
echo "Head ${HEAD} unchanged since the last comment; skipping the agent."
|
||||
echo "skip=true" >> "${GITHUB_OUTPUT}"
|
||||
exit 0
|
||||
fi
|
||||
# List files changed between the recorded commit and the current head.
|
||||
# Tracked patterns mirror script/check_requirements/diff.py TRACKED_PATTERNS.
|
||||
CHANGED=$(gh api "repos/${GITHUB_REPOSITORY}/compare/${PRIOR}...${HEAD}" \
|
||||
--jq '.files[].filename' 2>/dev/null) || {
|
||||
echo "Could not compare ${PRIOR}...${HEAD}; running the agent."
|
||||
exit 0
|
||||
}
|
||||
TRACKED=$(printf '%s\n' "${CHANGED}" \
|
||||
| grep -Ex 'requirements.*\.txt|homeassistant/package_constraints\.txt' || true)
|
||||
if [ -z "${TRACKED}" ]; then
|
||||
echo "No tracked requirement files changed since ${PRIOR}; skipping the agent."
|
||||
echo "skip=true" >> "${GITHUB_OUTPUT}"
|
||||
else
|
||||
echo "Tracked requirement files changed since ${PRIOR}; running the agent:"
|
||||
printf '%s\n' "${TRACKED}"
|
||||
fi
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
pre_activation:
|
||||
runs-on: ubuntu-slim
|
||||
outputs:
|
||||
|
||||
@@ -22,69 +22,8 @@ safe-outputs:
|
||||
needs:
|
||||
- extract_pr_number
|
||||
jobs:
|
||||
gate:
|
||||
# Skip the (token-spending) agent when no tracked requirement file changed
|
||||
if: github.event.workflow_run.conclusion == 'success'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
pull-requests: read
|
||||
outputs:
|
||||
skip: ${{ steps.gate.outputs.skip }}
|
||||
steps:
|
||||
- name: Download deterministic-results artifact
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: check-requirements-deterministic
|
||||
path: /tmp/gate
|
||||
run-id: ${{ github.event.workflow_run.id }}
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Decide whether requirements changed since the last comment
|
||||
id: gate
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
PR=$(jq -r '.pr_number' /tmp/gate/results.json)
|
||||
HEAD=$(jq -r '.head_sha // empty' /tmp/gate/results.json)
|
||||
if [ -z "${HEAD}" ]; then
|
||||
echo "Artifact has no head_sha; running the agent."
|
||||
exit 0
|
||||
fi
|
||||
# Recover the commit recorded in the most recent requirements-check
|
||||
# comment from the "Checked at commit" link
|
||||
PRIOR=$(gh api --paginate "repos/${GITHUB_REPOSITORY}/issues/${PR}/comments" \
|
||||
--jq '.[] | select(.body | contains("<!-- requirements-check -->")) | .body' \
|
||||
| grep -oiE '/commit/[0-9a-f]{40}' \
|
||||
| grep -oiE '[0-9a-f]{40}' | tail -1 || true)
|
||||
if [ -z "${PRIOR}" ]; then
|
||||
echo "No previous comment with a recorded commit; running the agent."
|
||||
exit 0
|
||||
fi
|
||||
if [ "${PRIOR}" = "${HEAD}" ]; then
|
||||
echo "Head ${HEAD} unchanged since the last comment; skipping the agent."
|
||||
echo "skip=true" >> "${GITHUB_OUTPUT}"
|
||||
exit 0
|
||||
fi
|
||||
# List files changed between the recorded commit and the current head.
|
||||
# Tracked patterns mirror script/check_requirements/diff.py TRACKED_PATTERNS.
|
||||
CHANGED=$(gh api "repos/${GITHUB_REPOSITORY}/compare/${PRIOR}...${HEAD}" \
|
||||
--jq '.files[].filename' 2>/dev/null) || {
|
||||
echo "Could not compare ${PRIOR}...${HEAD}; running the agent."
|
||||
exit 0
|
||||
}
|
||||
TRACKED=$(printf '%s\n' "${CHANGED}" \
|
||||
| grep -Ex 'requirements.*\.txt|homeassistant/package_constraints\.txt' || true)
|
||||
if [ -z "${TRACKED}" ]; then
|
||||
echo "No tracked requirement files changed since ${PRIOR}; skipping the agent."
|
||||
echo "skip=true" >> "${GITHUB_OUTPUT}"
|
||||
else
|
||||
echo "Tracked requirement files changed since ${PRIOR}; running the agent:"
|
||||
printf '%s\n' "${TRACKED}"
|
||||
fi
|
||||
extract_pr_number:
|
||||
needs: gate
|
||||
if: needs.gate.outputs.skip != 'true' && github.event.workflow_run.conclusion == 'success'
|
||||
if: github.event.workflow_run.conclusion == 'success'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
@@ -164,9 +103,6 @@ Read the JSON directly for the full schema. Key fields:
|
||||
- `{{CHECK_DETAIL:<pkg>:<kind>}}` → `<icon> <one-line explanation>`
|
||||
(the bullet's `- **<label>**:` prefix is already rendered; replace
|
||||
only the placeholder).
|
||||
- `{{SUMMARY}}` → the single top-of-comment summary line, present only
|
||||
when at least one check needed resolving. Fill it **after** resolving
|
||||
every check, based on the final cell verdicts (see Step 3).
|
||||
|
||||
Do not modify other content in `rendered_comment`, do not re-evaluate
|
||||
deterministic checks, do not add or remove packages. If `needs_agent`
|
||||
@@ -195,13 +131,6 @@ Replace every placeholder with the resolved value and emit
|
||||
`<!-- requirements-check -->` marker. The PR target is already wired;
|
||||
do not pass `item_number`.
|
||||
|
||||
If a `{{SUMMARY}}` placeholder is present, replace it last, once every
|
||||
`{{CHECK_CELL:…}}` is resolved:
|
||||
- `All requirements checks passed. ✅` — when every check cell across all
|
||||
packages is `✅` or `☑️` (treat `—`/skipped as not a problem).
|
||||
- `⚠️ Some checks require attention — see the details below.` — when any
|
||||
cell is `⚠️` or `❌`.
|
||||
|
||||
## Check instructions
|
||||
|
||||
### Check kind: `repo_public`
|
||||
|
||||
+20
-20
@@ -98,7 +98,7 @@ 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
|
||||
@@ -264,7 +264,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 +291,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 +318,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 +341,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 }}
|
||||
@@ -469,7 +469,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 +512,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 +548,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 +576,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 +603,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 +654,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 +707,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 +758,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 +825,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 +889,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 +1030,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 +1179,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 +1317,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 +1355,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 +1476,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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
repos:
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.15.17
|
||||
rev: v0.15.16
|
||||
hooks:
|
||||
- id: ruff-check
|
||||
args:
|
||||
@@ -102,7 +102,7 @@ repos:
|
||||
pass_filenames: false
|
||||
language: script
|
||||
types: [text]
|
||||
files: ^(homeassistant/.+/(icons|manifest|strings)\.json|homeassistant/.+/(conditions|quality_scale|services|triggers)\.yaml|homeassistant/brands/.*\.json|script/hassfest/(?!metadata|mypy_config).+\.py|homeassistant/components/sensor/const\.py|requirements.+\.txt)$
|
||||
files: ^(homeassistant/.+/(icons|manifest|strings)\.json|homeassistant/.+/(conditions|quality_scale|services|triggers)\.yaml|homeassistant/brands/.*\.json|script/hassfest/(?!metadata|mypy_config).+\.py|requirements.+\.txt)$
|
||||
- id: hassfest-metadata
|
||||
name: hassfest-metadata
|
||||
entry: script/run-in-env.sh python3 -m script.hassfest -p metadata,docker
|
||||
|
||||
+2
-3
@@ -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.*
|
||||
@@ -140,7 +142,6 @@ homeassistant.components.canary.*
|
||||
homeassistant.components.casper_glow.*
|
||||
homeassistant.components.centriconnect.*
|
||||
homeassistant.components.cert_expiry.*
|
||||
homeassistant.components.chef_iq.*
|
||||
homeassistant.components.clickatell.*
|
||||
homeassistant.components.clicksend.*
|
||||
homeassistant.components.climate.*
|
||||
@@ -625,7 +626,6 @@ homeassistant.components.waqi.*
|
||||
homeassistant.components.water_heater.*
|
||||
homeassistant.components.watts.*
|
||||
homeassistant.components.watttime.*
|
||||
homeassistant.components.wattwaechter.*
|
||||
homeassistant.components.weather.*
|
||||
homeassistant.components.web_rtc.*
|
||||
homeassistant.components.webhook.*
|
||||
@@ -642,7 +642,6 @@ homeassistant.components.xbox.*
|
||||
homeassistant.components.xiaomi_ble.*
|
||||
homeassistant.components.yale_smart_alarm.*
|
||||
homeassistant.components.yalexs_ble.*
|
||||
homeassistant.components.yoto.*
|
||||
homeassistant.components.youtube.*
|
||||
homeassistant.components.zeroconf.*
|
||||
homeassistant.components.zinvolt.*
|
||||
|
||||
@@ -13,7 +13,7 @@ This repository contains the core of Home Assistant, a Python 3 based home autom
|
||||
|
||||
## Development Commands
|
||||
|
||||
- When entering a new environment or worktree, run `script/setup` to set up the virtual environment with all development dependencies (pylint, pre-commit hooks, etc.). This is required before committing. If uv reports that no download was found for the required Python version, the environment is running an outdated version of uv; upgrade it with `curl -LsSf https://astral.sh/uv/install.sh | sh` and run `script/setup` again.
|
||||
- When entering a new environment or worktree, run `script/setup` to set up the virtual environment with all development dependencies (pylint, pre-commit hooks, etc.). This is required before committing.
|
||||
- .vscode/tasks.json contains useful commands used for development.
|
||||
- After finishing a code session, run `uv run prek run --all-files` to check for linting and formatting issues.
|
||||
|
||||
@@ -40,6 +40,4 @@ This repository contains the core of Home Assistant, a Python 3 based home autom
|
||||
- Integrations with Platinum or Gold level in the Integration Quality Scale reflect a high standard of code quality and maintainability. When looking for examples of something, these are good places to start. The level is indicated in the manifest.json of the integration.
|
||||
- When reviewing entity actions, do not suggest extra defensive checks for input fields that are already validated by Home Assistant's service/action schemas and entity selection filters. Suggest additional guards only when data bypasses those validators or is transformed into a less-safe form.
|
||||
- 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.
|
||||
- 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.
|
||||
|
||||
Generated
+18
-23
@@ -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,16 +254,16 @@ 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
|
||||
/tests/components/braviatv/ @bieniu @Drafteed
|
||||
/homeassistant/components/bring/ @miaucl @tr4nt0r
|
||||
/tests/components/bring/ @miaucl @tr4nt0r
|
||||
/homeassistant/components/broadlink/ @danielhiversen @felipediel @L-I-Am
|
||||
/tests/components/broadlink/ @danielhiversen @felipediel @L-I-Am
|
||||
/homeassistant/components/broadlink/ @danielhiversen @felipediel @L-I-Am @eifinger
|
||||
/tests/components/broadlink/ @danielhiversen @felipediel @L-I-Am @eifinger
|
||||
/homeassistant/components/brother/ @bieniu
|
||||
/tests/components/brother/ @bieniu
|
||||
/homeassistant/components/brottsplatskartan/ @gjohansson-ST
|
||||
@@ -298,8 +299,6 @@ CLAUDE.md @home-assistant/core
|
||||
/tests/components/cert_expiry/ @jjlawren
|
||||
/homeassistant/components/chacon_dio/ @cnico
|
||||
/tests/components/chacon_dio/ @cnico
|
||||
/homeassistant/components/chef_iq/ @Invader444
|
||||
/tests/components/chef_iq/ @Invader444
|
||||
/homeassistant/components/chess_com/ @joostlek
|
||||
/tests/components/chess_com/ @joostlek
|
||||
/homeassistant/components/cielo_home/ @ihsan-cielo @mudasar-cielo
|
||||
@@ -495,8 +494,6 @@ CLAUDE.md @home-assistant/core
|
||||
/tests/components/emulated_kasa/ @kbickar
|
||||
/homeassistant/components/energenie_power_sockets/ @gnumpi
|
||||
/tests/components/energenie_power_sockets/ @gnumpi
|
||||
/homeassistant/components/energieleser/ @AjinkyaGokhale @amitkio
|
||||
/tests/components/energieleser/ @AjinkyaGokhale @amitkio
|
||||
/homeassistant/components/energy/ @home-assistant/core
|
||||
/tests/components/energy/ @home-assistant/core
|
||||
/homeassistant/components/energyid/ @JrtPec @Molier
|
||||
@@ -698,8 +695,6 @@ CLAUDE.md @home-assistant/core
|
||||
/tests/components/gree/ @cmroche
|
||||
/homeassistant/components/green_planet_energy/ @petschni
|
||||
/tests/components/green_planet_energy/ @petschni
|
||||
/homeassistant/components/greencell/ @BrzezowskiGC
|
||||
/tests/components/greencell/ @BrzezowskiGC
|
||||
/homeassistant/components/greeneye_monitor/ @jkeljo
|
||||
/tests/components/greeneye_monitor/ @jkeljo
|
||||
/homeassistant/components/group/ @home-assistant/core
|
||||
@@ -780,8 +775,8 @@ CLAUDE.md @home-assistant/core
|
||||
/tests/components/homevolt/ @danielhiversen @liudger
|
||||
/homeassistant/components/homewizard/ @DCSBL
|
||||
/tests/components/homewizard/ @DCSBL
|
||||
/homeassistant/components/honeywell/ @mkmer
|
||||
/tests/components/honeywell/ @mkmer
|
||||
/homeassistant/components/honeywell/ @rdfurman @mkmer
|
||||
/tests/components/honeywell/ @rdfurman @mkmer
|
||||
/homeassistant/components/honeywell_string_lights/ @balloob
|
||||
/tests/components/honeywell_string_lights/ @balloob
|
||||
/homeassistant/components/hr_energy_qube/ @MattieGit
|
||||
@@ -1027,6 +1022,8 @@ CLAUDE.md @home-assistant/core
|
||||
/tests/components/litterrobot/ @natekspencer @tkdrob
|
||||
/homeassistant/components/livisi/ @StefanIacobLivisi @planbnet
|
||||
/tests/components/livisi/ @StefanIacobLivisi @planbnet
|
||||
/homeassistant/components/llm/ @home-assistant/core
|
||||
/tests/components/llm/ @home-assistant/core
|
||||
/homeassistant/components/local_calendar/ @allenporter
|
||||
/tests/components/local_calendar/ @allenporter
|
||||
/homeassistant/components/local_ip/ @issacg
|
||||
@@ -1160,6 +1157,7 @@ CLAUDE.md @home-assistant/core
|
||||
/tests/components/motionmount/ @laiho-vogels
|
||||
/homeassistant/components/mqtt/ @emontnemery @jbouwh @bdraco
|
||||
/tests/components/mqtt/ @emontnemery @jbouwh @bdraco
|
||||
/homeassistant/components/msteams/ @peroyvind
|
||||
/homeassistant/components/mta/ @OnFreund
|
||||
/tests/components/mta/ @OnFreund
|
||||
/homeassistant/components/mullvad/ @meichthys
|
||||
@@ -1170,8 +1168,8 @@ CLAUDE.md @home-assistant/core
|
||||
/tests/components/mutesync/ @currentoor
|
||||
/homeassistant/components/my/ @home-assistant/core
|
||||
/tests/components/my/ @home-assistant/core
|
||||
/homeassistant/components/myneomitis/ @Epyes
|
||||
/tests/components/myneomitis/ @Epyes
|
||||
/homeassistant/components/myneomitis/ @l-pr
|
||||
/tests/components/myneomitis/ @l-pr
|
||||
/homeassistant/components/mysensors/ @MartinHjelmare @functionpointer
|
||||
/tests/components/mysensors/ @MartinHjelmare @functionpointer
|
||||
/homeassistant/components/mystrom/ @fabaff
|
||||
@@ -1557,8 +1555,7 @@ CLAUDE.md @home-assistant/core
|
||||
/tests/components/rympro/ @OnFreund @elad-bar @maorcc
|
||||
/homeassistant/components/sabnzbd/ @shaiu @jpbede
|
||||
/tests/components/sabnzbd/ @shaiu @jpbede
|
||||
/homeassistant/components/saj/ @fredericvl @edurenye
|
||||
/tests/components/saj/ @fredericvl @edurenye
|
||||
/homeassistant/components/saj/ @fredericvl
|
||||
/homeassistant/components/samsung_infrared/ @lmaertin
|
||||
/tests/components/samsung_infrared/ @lmaertin
|
||||
/homeassistant/components/samsungtv/ @chemelli74
|
||||
@@ -1602,8 +1599,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 +1708,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
|
||||
@@ -1896,9 +1893,9 @@ CLAUDE.md @home-assistant/core
|
||||
/homeassistant/components/unifi_access/ @imhotep @RaHehl
|
||||
/tests/components/unifi_access/ @imhotep @RaHehl
|
||||
/homeassistant/components/unifi_direct/ @tofuSCHNITZEL
|
||||
/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
|
||||
@@ -1992,8 +1989,6 @@ CLAUDE.md @home-assistant/core
|
||||
/tests/components/watts/ @theobld-ww @devender-verma-ww @ssi-spyro
|
||||
/homeassistant/components/watttime/ @bachya
|
||||
/tests/components/watttime/ @bachya
|
||||
/homeassistant/components/wattwaechter/ @smartcircuits
|
||||
/tests/components/wattwaechter/ @smartcircuits
|
||||
/homeassistant/components/waze_travel_time/ @eifinger
|
||||
/tests/components/waze_travel_time/ @eifinger
|
||||
/homeassistant/components/weather/ @home-assistant/core
|
||||
|
||||
@@ -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]],
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
"microsoft_face_identify",
|
||||
"microsoft_face",
|
||||
"microsoft",
|
||||
"msteams",
|
||||
"onedrive",
|
||||
"onedrive_for_business",
|
||||
"xbox"
|
||||
|
||||
@@ -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,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,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()
|
||||
|
||||
@@ -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,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,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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -7,7 +7,8 @@ import voluptuous as vol
|
||||
|
||||
from homeassistant.const import ATTR_ENTITY_ID
|
||||
from homeassistant.core import HomeAssistant, ServiceCall, callback
|
||||
from homeassistant.helpers import config_validation as cv, service
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.dispatcher import dispatcher_send
|
||||
|
||||
from .const import DOMAIN, LOGGER
|
||||
@@ -30,8 +31,10 @@ AUTOMATION_SCHEMA = vol.Schema({ATTR_ENTITY_ID: cv.entity_ids})
|
||||
|
||||
def _get_abode_system(hass: HomeAssistant) -> AbodeSystem:
|
||||
"""Return the Abode system for the loaded config entry."""
|
||||
entry: AbodeConfigEntry = service.async_get_config_entry(hass, DOMAIN, None)
|
||||
return entry.runtime_data
|
||||
entries: list[AbodeConfigEntry] = hass.config_entries.async_loaded_entries(DOMAIN)
|
||||
if not entries:
|
||||
raise ServiceValidationError("Abode integration is not loaded")
|
||||
return entries[0].runtime_data
|
||||
|
||||
|
||||
def _change_setting(call: ServiceCall) -> None:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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."""
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -26,5 +26,5 @@
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["aioacaia"],
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["aioacaia==0.1.18"]
|
||||
"requirements": ["aioacaia==0.1.17"]
|
||||
}
|
||||
|
||||
@@ -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: |
|
||||
|
||||
@@ -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.0"]
|
||||
}
|
||||
@@ -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,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(
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/acmeda",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["aiopulse"],
|
||||
"requirements": ["aiopulse==0.4.7"]
|
||||
"requirements": ["aiopulse==0.4.6"]
|
||||
}
|
||||
|
||||
@@ -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,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,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,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,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,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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -108,7 +108,7 @@ class AdGuardHomeSensor(AdGuardHomeEntity, SensorEntity):
|
||||
"""Initialize AdGuard Home sensor."""
|
||||
super().__init__(data, entry)
|
||||
self.entity_description = description
|
||||
self._attr_unique_id = "_".join( # pylint: disable=home-assistant-entity-unique-id-redundant-domain
|
||||
self._attr_unique_id = "_".join(
|
||||
[
|
||||
DOMAIN,
|
||||
self.adguard.host,
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -103,7 +103,7 @@ class AdGuardHomeSwitch(AdGuardHomeEntity, SwitchEntity):
|
||||
"""Initialize AdGuard Home switch."""
|
||||
super().__init__(data, entry)
|
||||
self.entity_description = description
|
||||
self._attr_unique_id = "_".join( # pylint: disable=home-assistant-entity-unique-id-redundant-domain
|
||||
self._attr_unique_id = "_".join(
|
||||
[
|
||||
DOMAIN,
|
||||
self.adguard.host,
|
||||
@@ -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,7 +1,7 @@
|
||||
"""AdGuard Home Update platform."""
|
||||
|
||||
from datetime import timedelta
|
||||
from typing import Any, override
|
||||
from typing import Any
|
||||
|
||||
from adguardhome import AdGuardHomeError
|
||||
|
||||
@@ -46,11 +46,10 @@ class AdGuardHomeUpdate(AdGuardHomeEntity, UpdateEntity):
|
||||
"""Initialize AdGuard Home update."""
|
||||
super().__init__(data, entry)
|
||||
|
||||
self._attr_unique_id = "_".join( # pylint: disable=home-assistant-entity-unique-id-redundant-domain
|
||||
self._attr_unique_id = "_".join(
|
||||
[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,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:
|
||||
|
||||
@@ -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,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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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,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)
|
||||
|
||||
@@ -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,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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,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(
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user