Compare commits

..

1 Commits

Author SHA1 Message Date
Erik 918185fda4 Extend script continue_on_error suppression when calling actions 2026-04-21 10:01:08 +02:00
8172 changed files with 32057 additions and 88196 deletions
+4 -5
View File
@@ -27,13 +27,12 @@ description: Reviews GitHub pull requests and provides feedback comments. This i
- No need to highlight things that are already good.
## Output format:
- List specific comments for each file/line that needs attention.
- List specific comments for each file/line that needs attention
- In the end, summarize with an overall assessment (approve, request changes, or comment) and bullet point list of changes suggested, if any.
- Example output:
```
Overall assessment: request changes.
- [CRITICAL] sensor.py:143 - Memory leak
- [PROBLEM] data_processing.py:87 - Inefficient algorithm
- [SUGGESTION] test_init.py:45 - Improve x variable name
- [CRITICAL] Memory leak in homeassistant/components/sensor/my_sensor.py:143
- [PROBLEM] Inefficient algorithm in homeassistant/helpers/data_processing.py:87
- [SUGGESTION] Improve variable naming in homeassistant/helpers/config_validation.py:45
```
- Make sure to include the file and line number when possible in the bullet points.
@@ -1,5 +1,5 @@
---
name: ha-integration-knowledge
name: Home Assistant Integration knowledge
description: Everything you need to know to build, test and review Home Assistant Integrations. If you're looking at an integration, you must use this as your primary reference.
---
@@ -14,16 +14,11 @@ description: Everything you need to know to build, test and review Home Assistan
- Do NOT allow users to set config entry names in config flows. Names are automatically generated or can be customized later in UI. Exception: helper integrations may allow custom names.
- For entity actions and entity services, avoid requesting redundant defensive checks for fields already enforced by Home Assistant validation schemas and entity filters; only request extra guards when values bypass validation or are transformed unsafely.
- When validation guarantees a key is present, prefer direct dictionary indexing (`data["key"]`) over `.get("key")` so invalid assumptions fail fast.
- Integrations should be thin wrappers. Protocol parsing, device state machines, or other domain logic belong in a separate PyPI library, not in the integration itself. If unsure, ask before inlining.
- Integrations should not implement fixes or workarounds for limitations in libraries. Instead, the library should be updated to fix the issue.
The following platforms have extra guidelines:
- **Diagnostics**: [`platform-diagnostics.md`](platform-diagnostics.md) for diagnostic data collection
- **Repairs**: [`platform-repairs.md`](platform-repairs.md) for user-actionable repair issues
## Entity platforms
- Ensure `async_added_to_hass()` and `async_will_remove_from_hass()` have symmetrical behavior. For example, if a subscription is created in `async_added_to_hass()`, it should be unsubscribed in `async_will_remove_from_hass()`. Also, if something is torn down in `async_will_remove_from_hass()`, it should be set up in `async_added_to_hass()`.
## Integration Quality Scale
-1
View File
@@ -36,7 +36,6 @@ base_platforms: &base_platforms
- homeassistant/components/image_processing/**
- homeassistant/components/infrared/**
- homeassistant/components/lawn_mower/**
- homeassistant/components/radio_frequency/**
- homeassistant/components/light/**
- homeassistant/components/lock/**
- homeassistant/components/media_player/**
+13 -13
View File
@@ -5,8 +5,7 @@
# Copilot code review instructions
- Start review comments with a short, one-sentence summary of the suggested fix.
- Do not comment on code style, formatting or linting issues.
- 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.
- Do not add comments about code style, formatting or linting issues.
# GitHub Copilot & Claude Code Instructions
@@ -22,20 +21,21 @@ This repository contains the core of Home Assistant, a Python 3 based home autom
## Python Syntax Notes
- Home Assistant officially supports Python 3.14 as its minimum version. Do not flag syntax or features that require Python 3.14 as issues, and do not suggest workarounds for older Python versions.
- Python 3.14 explicitly allows `except TypeA, TypeB:` without parentheses. Never flag this as an issue.
- Python 3.14 evaluates annotations lazily (PEP 649). Forward references in annotations do not need to be quoted — annotations can reference names defined later in the module without quoting them or using `from __future__ import annotations`. Do not flag unquoted forward references in annotations as issues.
- Python 3.14 explicitly allows `except TypeA, TypeB:` without parentheses.
## Testing
- When writing or modifying tests, ensure all test function parameters have type annotations.
- Prefer concrete types (for example, `HomeAssistant`, `MockConfigEntry`, etc.) over `Any`.
- Avoid using conditions/branching in tests. Instead, either split tests or adjust the test parametrization to cover all cases without branching.
- If multiple tests share most of their code, use `pytest.mark.parametrize` to merge them into a single parameterized test instead of duplicating the body.
When writing or modifying tests, ensure all test function parameters have type annotations.
Prefer concrete types (for example, `HomeAssistant`, `MockConfigEntry`, etc.) over `Any`.
## Good practices
- 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.
- 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.
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.
# Skills
- Home Assistant Integration knowledge: .claude/skills/integrations/SKILL.md
@@ -1,50 +0,0 @@
---
applyTo: "homeassistant/components/**, tests/components/**"
excludeAgent: "cloud-agent"
---
<!-- Automatically generated by gen_copilot_instructions.py, do not edit -->
## File Locations
- **Integration code**: `./homeassistant/components/<integration_domain>/`
- **Integration tests**: `./tests/components/<integration_domain>/`
## General guidelines
- When looking for examples, prefer integrations with the platinum or gold quality scale level first.
- Polling intervals are NOT user-configurable. Never add scan_interval, update_interval, or polling frequency options to config flows or config entries.
- Do NOT allow users to set config entry names in config flows. Names are automatically generated or can be customized later in UI. Exception: helper integrations may allow custom names.
- For entity actions and entity services, avoid requesting redundant defensive checks for fields already enforced by Home Assistant validation schemas and entity filters; only request extra guards when values bypass validation or are transformed unsafely.
- When validation guarantees a key is present, prefer direct dictionary indexing (`data["key"]`) over `.get("key")` so invalid assumptions fail fast.
- Integrations should be thin wrappers. Protocol parsing, device state machines, or other domain logic belong in a separate PyPI library, not in the integration itself. If unsure, ask before inlining.
- Integrations should not implement fixes or workarounds for limitations in libraries. Instead, the library should be updated to fix the issue.
The following platforms have extra guidelines:
- **Diagnostics**: [`platform-diagnostics.md`](platform-diagnostics.md) for diagnostic data collection
- **Repairs**: [`platform-repairs.md`](platform-repairs.md) for user-actionable repair issues
## Entity platforms
- Ensure `async_added_to_hass()` and `async_will_remove_from_hass()` have symmetrical behavior. For example, if a subscription is created in `async_added_to_hass()`, it should be unsubscribed in `async_will_remove_from_hass()`. Also, if something is torn down in `async_will_remove_from_hass()`, it should be set up in `async_added_to_hass()`.
## Integration Quality Scale
- When validating the quality scale rules, check them at https://developers.home-assistant.io/docs/core/integration-quality-scale/rules
- When implementing or reviewing an integration, always consider the quality scale rules, since they promote best practices.
Template scale file: `./script/scaffold/templates/integration/integration/quality_scale.yaml`
### How Rules Apply
1. **Check `manifest.json`**: Look for `"quality_scale"` key to determine integration level
2. **Bronze Rules**: Always required for any integration with quality scale
3. **Higher Tier Rules**: Only apply if integration targets that tier or higher
4. **Rule Status**: Check `quality_scale.yaml` in integration folder for:
- `done`: Rule implemented
- `exempt`: Rule doesn't apply (with reason in comment)
- `todo`: Rule needs implementation
## Testing Requirements
- Tests should avoid interacting or mocking internal integration details. For more info, see https://developers.home-assistant.io/docs/development_testing/#writing-tests-for-integrations
+2 -3
View File
@@ -6,7 +6,7 @@
"pep621",
"pip_requirements",
"pre-commit",
"custom.regex",
"regex",
"homeassistant-manifest"
],
@@ -27,9 +27,8 @@
]
},
"customManagers": [
"regexManagers": [
{
"customType": "regex",
"description": "Update ruff required-version in pyproject.toml",
"managerFilePatterns": ["/^pyproject\\.toml$/"],
"matchStrings": ["required-version = \">=(?<currentValue>[\\d.]+)\""],
+3 -3
View File
@@ -14,7 +14,7 @@ env:
UV_HTTP_TIMEOUT: 60
UV_SYSTEM_PYTHON: "true"
# Base image version from https://github.com/home-assistant/docker
BASE_IMAGE_VERSION: "2026.04.0"
BASE_IMAGE_VERSION: "2026.01.0"
ARCHITECTURES: '["amd64", "aarch64"]'
permissions: {}
@@ -108,7 +108,7 @@ jobs:
- name: Download nightly wheels of frontend
if: needs.init.outputs.channel == 'dev'
uses: dawidd6/action-download-artifact@b6e2e70617bc3265edd6dab6c906732b2f1ae151 # v21
uses: dawidd6/action-download-artifact@8305c0f1062bb0d184d09ef4493ecb9288447732 # v20
with:
github_token: ${{secrets.GITHUB_TOKEN}}
repo: home-assistant/frontend
@@ -119,7 +119,7 @@ jobs:
- name: Download nightly wheels of intents
if: needs.init.outputs.channel == 'dev'
uses: dawidd6/action-download-artifact@b6e2e70617bc3265edd6dab6c906732b2f1ae151 # v21
uses: dawidd6/action-download-artifact@8305c0f1062bb0d184d09ef4493ecb9288447732 # v20
with:
github_token: ${{secrets.GITHUB_TOKEN}}
repo: OHF-Voice/intents-package
+23 -44
View File
@@ -38,8 +38,9 @@ on:
env:
CACHE_VERSION: 3
UV_CACHE_VERSION: 1
MYPY_CACHE_VERSION: 1
HA_SHORT_VERSION: "2026.6"
HA_SHORT_VERSION: "2026.5"
ADDITIONAL_PYTHON_VERSIONS: "[]"
# 10.3 is the oldest supported version
# - 10.3.32 is the version currently shipped with Synology (as of 17 Feb 2022)
@@ -357,33 +358,32 @@ jobs:
with:
python-version: ${{ matrix.python-version }}
check-latest: true
- name: Generate partial uv restore key
id: generate-uv-key
run: |
uv_version=$(cat requirements.txt | grep uv | cut -d '=' -f 3)
echo "version=${uv_version}" >> $GITHUB_OUTPUT
echo "key=uv-${UV_CACHE_VERSION}-${uv_version}-${HA_SHORT_VERSION}-$(date -u '+%Y-%m-%dT%H:%M:%s')" >> $GITHUB_OUTPUT
- name: Restore base Python virtual environment
id: cache-venv
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: venv
key: >-
${{ runner.os }}-${{ runner.arch }}-${{ steps.python.outputs.python-version }}-${{
needs.info.outputs.python_cache_key }}
- name: Generate partial uv restore key
if: steps.cache-venv.outputs.cache-hit != 'true'
id: generate-uv-key
env:
RUNNER_OS: ${{ runner.os }}
RUNNER_ARCH: ${{ runner.arch }}
PYTHON_VERSION: ${{ steps.python.outputs.python-version }}
HASH_FILES: ${{ hashFiles('requirements.txt', 'requirements_all.txt', 'requirements_test.txt', 'homeassistant/package_constraints.txt') }}
run: |
partial_key="${RUNNER_OS}-${RUNNER_ARCH}-${PYTHON_VERSION}-uv-"
echo "partial_key=${partial_key}" >> $GITHUB_OUTPUT
echo "full_key=${partial_key}${HASH_FILES}" >> $GITHUB_OUTPUT
- name: Restore uv wheel cache
if: steps.cache-venv.outputs.cache-hit != 'true'
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ${{ env.UV_CACHE_DIR }}
key: ${{ steps.generate-uv-key.outputs.full_key }}
restore-keys: ${{ steps.generate-uv-key.outputs.partial_key }}
key: >-
${{ runner.os }}-${{ runner.arch }}-${{ steps.python.outputs.python-version }}-${{
steps.generate-uv-key.outputs.key }}
restore-keys: |
${{ runner.os }}-${{ runner.arch }}-${{ steps.python.outputs.python-version }}-uv-${{
env.UV_CACHE_VERSION }}-${{ steps.generate-uv-key.outputs.version }}-${{
env.HA_SHORT_VERSION }}-
- name: Check if apt cache exists
id: cache-apt-check
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
@@ -398,7 +398,6 @@ jobs:
if: |
steps.cache-venv.outputs.cache-hit != 'true'
|| steps.cache-apt-check.outputs.cache-hit != 'true'
id: install-os-deps
timeout-minutes: 10
env:
APT_CACHE_HIT: ${{ steps.cache-apt-check.outputs.cache-hit }}
@@ -432,10 +431,7 @@ jobs:
sudo chmod -R 755 ${APT_CACHE_BASE}
fi
- name: Save apt cache
if: |
always()
&& steps.cache-apt-check.outputs.cache-hit != 'true'
&& steps.install-os-deps.outcome == 'success'
if: steps.cache-apt-check.outputs.cache-hit != 'true'
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: |
@@ -445,7 +441,6 @@ jobs:
${{ runner.os }}-${{ runner.arch }}-${{ needs.info.outputs.apt_cache_key }}
- name: Create Python virtual environment
if: steps.cache-venv.outputs.cache-hit != 'true'
id: create-venv
run: |
python -m venv venv
. venv/bin/activate
@@ -453,7 +448,8 @@ jobs:
pip install "$(grep '^uv' < requirements.txt)"
uv pip install -U "pip>=25.2"
uv pip install -r requirements.txt
uv pip install -r requirements_all.txt -r requirements_test.txt
python -m script.gen_requirements_all ci
uv pip install -r requirements_all_pytest.txt -r requirements_test.txt
uv pip install -e . --config-settings editable_mode=compat
- name: Dump pip freeze
run: |
@@ -469,29 +465,12 @@ jobs:
overwrite: true
- name: Remove pip_freeze
run: rm pip_freeze.txt
- name: Remove generated requirements_all
if: steps.cache-venv.outputs.cache-hit != 'true'
run: rm requirements_all_pytest.txt requirements_all_wheels_*.txt
- name: Check dirty
run: |
./script/check_dirty
- name: Prune uv cache
if: steps.cache-venv.outputs.cache-hit != 'true'
id: prune-uv-cache
run: |
. venv/bin/activate
uv cache prune --ci
- name: Save uv wheel cache
if: steps.cache-venv.outputs.cache-hit != 'true'
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ${{ env.UV_CACHE_DIR }}
key: ${{ steps.generate-uv-key.outputs.full_key }}
- name: Save base Python virtual environment
if: always() && steps.create-venv.outcome == 'success'
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: venv
key: >-
${{ runner.os }}-${{ runner.arch }}-${{ steps.python.outputs.python-version }}-${{
needs.info.outputs.python_cache_key }}
hassfest:
name: Check hassfest
+2 -2
View File
@@ -28,11 +28,11 @@ jobs:
persist-credentials: false
- name: Initialize CodeQL
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
uses: github/codeql-action/init@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1
with:
languages: python
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
uses: github/codeql-action/analyze@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1
with:
category: "/language:python"
+2 -2
View File
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.12
rev: v0.15.10
hooks:
- id: ruff-check
args:
@@ -18,7 +18,7 @@ repos:
exclude_types: [csv, json, html]
exclude: ^tests/fixtures/|homeassistant/generated/|tests/components/.*/snapshots/
- repo: https://github.com/zizmorcore/zizmor-pre-commit
rev: v1.24.1
rev: v1.24.0
hooks:
- id: zizmor
args:
+1 -1
View File
@@ -1 +1 @@
3.14.4
3.14.2
-2
View File
@@ -442,7 +442,6 @@ homeassistant.components.private_ble_device.*
homeassistant.components.prometheus.*
homeassistant.components.proximity.*
homeassistant.components.prusalink.*
homeassistant.components.ptdevices.*
homeassistant.components.pure_energie.*
homeassistant.components.purpleair.*
homeassistant.components.pushbullet.*
@@ -600,7 +599,6 @@ homeassistant.components.vallox.*
homeassistant.components.valve.*
homeassistant.components.velbus.*
homeassistant.components.velux.*
homeassistant.components.victron_gx.*
homeassistant.components.vivotek.*
homeassistant.components.vlc_telnet.*
homeassistant.components.vodafone_station.*
+7 -11
View File
@@ -12,20 +12,16 @@ This repository contains the core of Home Assistant, a Python 3 based home autom
## Python Syntax Notes
- Home Assistant officially supports Python 3.14 as its minimum version. Do not flag syntax or features that require Python 3.14 as issues, and do not suggest workarounds for older Python versions.
- Python 3.14 explicitly allows `except TypeA, TypeB:` without parentheses. Never flag this as an issue.
- Python 3.14 evaluates annotations lazily (PEP 649). Forward references in annotations do not need to be quoted — annotations can reference names defined later in the module without quoting them or using `from __future__ import annotations`. Do not flag unquoted forward references in annotations as issues.
- Python 3.14 explicitly allows `except TypeA, TypeB:` without parentheses.
## Testing
- When writing or modifying tests, ensure all test function parameters have type annotations.
- Prefer concrete types (for example, `HomeAssistant`, `MockConfigEntry`, etc.) over `Any`.
- Avoid using conditions/branching in tests. Instead, either split tests or adjust the test parametrization to cover all cases without branching.
- If multiple tests share most of their code, use `pytest.mark.parametrize` to merge them into a single parameterized test instead of duplicating the body.
When writing or modifying tests, ensure all test function parameters have type annotations.
Prefer concrete types (for example, `HomeAssistant`, `MockConfigEntry`, etc.) over `Any`.
## Good practices
- 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.
- 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.
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.
Generated
+4 -20
View File
@@ -400,8 +400,6 @@ CLAUDE.md @home-assistant/core
/tests/components/dnsip/ @gjohansson-ST
/homeassistant/components/door/ @home-assistant/core
/tests/components/door/ @home-assistant/core
/homeassistant/components/doorbell/ @home-assistant/core
/tests/components/doorbell/ @home-assistant/core
/homeassistant/components/doorbird/ @oblogic7 @bdraco @flacjacket
/tests/components/doorbird/ @oblogic7 @bdraco @flacjacket
/homeassistant/components/dormakaba_dkey/ @emontnemery
@@ -758,8 +756,6 @@ CLAUDE.md @home-assistant/core
/tests/components/homewizard/ @DCSBL
/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
/tests/components/hr_energy_qube/ @MattieGit
/homeassistant/components/html5/ @alexyao2015 @tr4nt0r
@@ -851,8 +847,8 @@ CLAUDE.md @home-assistant/core
/tests/components/input_select/ @home-assistant/core
/homeassistant/components/input_text/ @home-assistant/core
/tests/components/input_text/ @home-assistant/core
/homeassistant/components/insteon/ @teharris1 @ssyrell
/tests/components/insteon/ @teharris1 @ssyrell
/homeassistant/components/insteon/ @teharris1
/tests/components/insteon/ @teharris1
/homeassistant/components/integration/ @dgomes
/tests/components/integration/ @dgomes
/homeassistant/components/intelliclima/ @dvdinth
@@ -1092,8 +1088,6 @@ CLAUDE.md @home-assistant/core
/tests/components/minecraft_server/ @elmurato @zachdeibert
/homeassistant/components/minio/ @tkislan
/tests/components/minio/ @tkislan
/homeassistant/components/mitsubishi_comfort/ @nikolairahimi
/tests/components/mitsubishi_comfort/ @nikolairahimi
/homeassistant/components/moat/ @bdraco
/tests/components/moat/ @bdraco
/homeassistant/components/mobile_app/ @home-assistant/core
@@ -1205,8 +1199,6 @@ CLAUDE.md @home-assistant/core
/tests/components/notify_events/ @matrozov @papajojo
/homeassistant/components/notion/ @bachya
/tests/components/notion/ @bachya
/homeassistant/components/novy_cooker_hood/ @piitaya
/tests/components/novy_cooker_hood/ @piitaya
/homeassistant/components/nrgkick/ @andijakl
/tests/components/nrgkick/ @andijakl
/homeassistant/components/nsw_fuel_station/ @nickw444
@@ -1243,8 +1235,6 @@ CLAUDE.md @home-assistant/core
/homeassistant/components/ollama/ @synesthesiam
/tests/components/ollama/ @synesthesiam
/homeassistant/components/ombi/ @larssont
/homeassistant/components/omie/ @luuuis
/tests/components/omie/ @luuuis
/homeassistant/components/onboarding/ @home-assistant/core
/tests/components/onboarding/ @home-assistant/core
/homeassistant/components/ondilo_ico/ @JeromeHXP
@@ -1263,8 +1253,6 @@ CLAUDE.md @home-assistant/core
/tests/components/open_meteo/ @frenck
/homeassistant/components/open_router/ @joostlek @ab3lson
/tests/components/open_router/ @joostlek @ab3lson
/homeassistant/components/openai_conversation/ @Shulyaka
/tests/components/openai_conversation/ @Shulyaka
/homeassistant/components/opendisplay/ @g4bri3lDev
/tests/components/opendisplay/ @g4bri3lDev
/homeassistant/components/openerz/ @misialq
@@ -1380,8 +1368,6 @@ CLAUDE.md @home-assistant/core
/tests/components/proxmoxve/ @Corbeno @erwindouna @CoMPaTech
/homeassistant/components/ps4/ @ktnrg45
/tests/components/ps4/ @ktnrg45
/homeassistant/components/ptdevices/ @ParemTech-Inc @frogman85978
/tests/components/ptdevices/ @ParemTech-Inc @frogman85978
/homeassistant/components/pterodactyl/ @elmurato
/tests/components/pterodactyl/ @elmurato
/homeassistant/components/pure_energie/ @klaasnicolaas
@@ -1425,8 +1411,6 @@ CLAUDE.md @home-assistant/core
/tests/components/radarr/ @tkdrob
/homeassistant/components/radio_browser/ @frenck
/tests/components/radio_browser/ @frenck
/homeassistant/components/radio_frequency/ @home-assistant/core
/tests/components/radio_frequency/ @home-assistant/core
/homeassistant/components/radiotherm/ @vinnyfuria
/tests/components/radiotherm/ @vinnyfuria
/homeassistant/components/rainbird/ @konikvranik @allenporter
@@ -1995,8 +1979,8 @@ CLAUDE.md @home-assistant/core
/tests/components/wled/ @frenck @mik-laj
/homeassistant/components/wmspro/ @mback2k
/tests/components/wmspro/ @mback2k
/homeassistant/components/wolflink/ @adamkrol93 @EnjoyingM
/tests/components/wolflink/ @adamkrol93 @EnjoyingM
/homeassistant/components/wolflink/ @adamkrol93 @mtielen
/tests/components/wolflink/ @adamkrol93 @mtielen
/homeassistant/components/workday/ @fabaff @gjohansson-ST
/tests/components/workday/ @fabaff @gjohansson-ST
/homeassistant/components/worldclock/ @fabaff
+2
View File
@@ -1,5 +1,7 @@
"""Start Home Assistant."""
from __future__ import annotations
import argparse
from contextlib import suppress
import faulthandler
+2
View File
@@ -1,5 +1,7 @@
"""Provide an authentication layer for Home Assistant."""
from __future__ import annotations
import asyncio
from collections import OrderedDict
from collections.abc import Mapping
+2
View File
@@ -1,5 +1,7 @@
"""Storage for auth models."""
from __future__ import annotations
from datetime import timedelta
import hmac
import itertools
+2
View File
@@ -5,6 +5,8 @@ we can cache the result of the decode of valid tokens
to speed up the process.
"""
from __future__ import annotations
from collections.abc import Container, Iterable, Sequence
from datetime import timedelta
from functools import lru_cache
@@ -1,5 +1,7 @@
"""Pluggable auth modules for Home Assistant."""
from __future__ import annotations
import logging
import types
from typing import Any
@@ -1,5 +1,7 @@
"""Example auth module."""
from __future__ import annotations
from typing import Any
import voluptuous as vol
+2
View File
@@ -3,6 +3,8 @@
Sending HOTP through notify service
"""
from __future__ import annotations
import asyncio
import logging
from typing import Any, cast
+2
View File
@@ -1,5 +1,7 @@
"""Time-based One Time Password auth module."""
from __future__ import annotations
import asyncio
from io import BytesIO
from typing import Any, cast
+2
View File
@@ -1,5 +1,7 @@
"""Auth models."""
from __future__ import annotations
from datetime import datetime, timedelta
from ipaddress import IPv4Address, IPv6Address
import secrets
+3 -16
View File
@@ -1,7 +1,8 @@
"""Permissions for Home Assistant."""
from collections.abc import Callable, Iterable
from typing import TYPE_CHECKING
from __future__ import annotations
from collections.abc import Callable
import voluptuous as vol
@@ -12,9 +13,6 @@ from .models import PermissionLookup
from .types import PolicyType
from .util import test_all
if TYPE_CHECKING:
from ..models import User
POLICY_SCHEMA = vol.Schema({vol.Optional(CAT_ENTITIES): ENTITY_POLICY_SCHEMA})
__all__ = [
@@ -24,21 +22,10 @@ __all__ = [
"PermissionLookup",
"PolicyPermissions",
"PolicyType",
"filter_entity_ids_by_permission",
"merge_policies",
]
def filter_entity_ids_by_permission(
user: User, entity_ids: Iterable[str], key: str
) -> list[str]:
"""Filter entity IDs to those the user can access for the given policy key."""
if user.is_admin or user.permissions.access_all_entities(key):
return list(entity_ids)
check_entity = user.permissions.check_entity
return [entity_id for entity_id in entity_ids if check_entity(entity_id, key)]
class AbstractPermissions:
"""Default permissions class."""
@@ -1,5 +1,7 @@
"""Entity permissions."""
from __future__ import annotations
from collections import OrderedDict
from collections.abc import Callable
+2
View File
@@ -1,5 +1,7 @@
"""Permission for events."""
from __future__ import annotations
from typing import Any, Final
from homeassistant.const import (
+2
View File
@@ -1,5 +1,7 @@
"""Merging of policies."""
from __future__ import annotations
from typing import cast
from .types import CategoryType, PolicyType
+2
View File
@@ -1,5 +1,7 @@
"""Models for permissions."""
from __future__ import annotations
from typing import TYPE_CHECKING
import attr
+2
View File
@@ -1,5 +1,7 @@
"""Helpers to deal with permissions."""
from __future__ import annotations
from collections.abc import Callable
from functools import wraps
from typing import cast
+2
View File
@@ -1,5 +1,7 @@
"""Auth providers for Home Assistant."""
from __future__ import annotations
from collections.abc import Mapping
import logging
import types
@@ -1,5 +1,7 @@
"""Auth provider that validates credentials via an external command."""
from __future__ import annotations
import asyncio
from collections.abc import Mapping
import logging
@@ -1,5 +1,7 @@
"""Home Assistant auth provider."""
from __future__ import annotations
import asyncio
import base64
from collections.abc import Mapping
@@ -1,5 +1,7 @@
"""Example auth provider."""
from __future__ import annotations
from collections.abc import Mapping
import hmac
@@ -4,6 +4,8 @@ It shows list of users if access from trusted network.
Abort login flow if not access from trusted network.
"""
from __future__ import annotations
from collections.abc import Mapping
from ipaddress import (
IPv4Address,
+2
View File
@@ -1,5 +1,7 @@
"""Home Assistant module to handle restoring backups."""
from __future__ import annotations
from collections.abc import Iterable
from dataclasses import dataclass
import json
+2
View File
@@ -1,5 +1,7 @@
"""Provide methods to bootstrap a Home Assistant instance."""
from __future__ import annotations
import asyncio
from collections import defaultdict
import contextlib
+1 -1
View File
@@ -1,5 +1,5 @@
{
"domain": "honeywell",
"name": "Honeywell",
"integrations": ["lyric", "evohome", "honeywell", "honeywell_string_lights"]
"integrations": ["lyric", "evohome", "honeywell"]
}
+21 -26
View File
@@ -1,5 +1,7 @@
"""Support for the Abode Security System."""
from __future__ import annotations
from dataclasses import dataclass, field
from functools import partial
from pathlib import Path
@@ -28,7 +30,7 @@ from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.typing import ConfigType
from .const import CONF_POLLING, DOMAIN, LOGGER
from .const import CONF_POLLING, DOMAIN, DOMAIN_DATA, LOGGER
from .services import async_setup_services
ATTR_DEVICE_NAME = "device_name"
@@ -65,16 +67,13 @@ class AbodeSystem:
logout_listener: CALLBACK_TYPE | None = None
type AbodeConfigEntry = ConfigEntry[AbodeSystem]
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the Abode component."""
async_setup_services(hass)
return True
async def async_setup_entry(hass: HomeAssistant, entry: AbodeConfigEntry) -> bool:
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Abode integration from a config entry."""
username = entry.data[CONF_USERNAME]
password = entry.data[CONF_PASSWORD]
@@ -100,54 +99,50 @@ async def async_setup_entry(hass: HomeAssistant, entry: AbodeConfigEntry) -> boo
except (AbodeException, ConnectTimeout, HTTPError) as ex:
raise ConfigEntryNotReady(f"Unable to connect to Abode: {ex}") from ex
entry.runtime_data = AbodeSystem(abode, polling)
hass.data[DOMAIN_DATA] = AbodeSystem(abode, polling)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
await setup_hass_events(hass, entry)
await hass.async_add_executor_job(setup_abode_events, hass, entry)
await setup_hass_events(hass)
await hass.async_add_executor_job(setup_abode_events, hass)
return True
def _shutdown_client(abode: Abode) -> None:
"""Shutdown client."""
abode.events.stop()
abode.logout()
async def async_unload_entry(hass: HomeAssistant, entry: AbodeConfigEntry) -> bool:
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
await hass.async_add_executor_job(_shutdown_client, entry.runtime_data.abode)
await hass.async_add_executor_job(hass.data[DOMAIN_DATA].abode.events.stop)
await hass.async_add_executor_job(hass.data[DOMAIN_DATA].abode.logout)
if logout_listener := entry.runtime_data.logout_listener:
if logout_listener := hass.data[DOMAIN_DATA].logout_listener:
logout_listener()
hass.data.pop(DOMAIN_DATA)
return unload_ok
async def setup_hass_events(hass: HomeAssistant, entry: AbodeConfigEntry) -> None:
async def setup_hass_events(hass: HomeAssistant) -> None:
"""Home Assistant start and stop callbacks."""
def logout(event: Event) -> None:
"""Logout of Abode."""
if not entry.runtime_data.polling:
entry.runtime_data.abode.events.stop()
if not hass.data[DOMAIN_DATA].polling:
hass.data[DOMAIN_DATA].abode.events.stop()
entry.runtime_data.abode.logout()
hass.data[DOMAIN_DATA].abode.logout()
LOGGER.info("Logged out of Abode")
if not entry.runtime_data.polling:
await hass.async_add_executor_job(entry.runtime_data.abode.events.start)
if not hass.data[DOMAIN_DATA].polling:
await hass.async_add_executor_job(hass.data[DOMAIN_DATA].abode.events.start)
entry.runtime_data.logout_listener = hass.bus.async_listen_once(
hass.data[DOMAIN_DATA].logout_listener = hass.bus.async_listen_once(
EVENT_HOMEASSISTANT_STOP, logout
)
def setup_abode_events(hass: HomeAssistant, entry: AbodeConfigEntry) -> None:
def setup_abode_events(hass: HomeAssistant) -> None:
"""Event callbacks."""
def event_callback(event: str, event_json: dict[str, str]) -> None:
@@ -184,6 +179,6 @@ def setup_abode_events(hass: HomeAssistant, entry: AbodeConfigEntry) -> None:
]
for event in events:
entry.runtime_data.abode.events.add_event_callback(
hass.data[DOMAIN_DATA].abode.events.add_event_callback(
event, partial(event_callback, event)
)
@@ -1,5 +1,7 @@
"""Support for Abode Security System alarm control panels."""
from __future__ import annotations
from jaraco.abode.devices.alarm import Alarm
from homeassistant.components.alarm_control_panel import (
@@ -7,20 +9,21 @@ from homeassistant.components.alarm_control_panel import (
AlarmControlPanelEntityFeature,
AlarmControlPanelState,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import AbodeConfigEntry
from .const import DOMAIN_DATA
from .entity import AbodeDevice
async def async_setup_entry(
hass: HomeAssistant,
entry: AbodeConfigEntry,
entry: ConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up Abode alarm control panel device."""
data = entry.runtime_data
data = hass.data[DOMAIN_DATA]
async_add_entities(
[AbodeAlarm(data, await hass.async_add_executor_job(data.abode.get_alarm))]
)
@@ -1,5 +1,7 @@
"""Support for Abode Security System binary sensors."""
from __future__ import annotations
from typing import cast
from jaraco.abode.devices.binary_sensor import BinarySensor
@@ -8,21 +10,22 @@ from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass,
BinarySensorEntity,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.util.enum import try_parse_enum
from . import AbodeConfigEntry
from .const import DOMAIN_DATA
from .entity import AbodeDevice
async def async_setup_entry(
hass: HomeAssistant,
entry: AbodeConfigEntry,
entry: ConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up Abode binary sensor devices."""
data = entry.runtime_data
data = hass.data[DOMAIN_DATA]
device_types = [
"connectivity",
+7 -4
View File
@@ -1,5 +1,7 @@
"""Support for Abode Security System cameras."""
from __future__ import annotations
from datetime import timedelta
from typing import Any, cast
@@ -10,13 +12,14 @@ import requests
from requests.models import Response
from homeassistant.components.camera import Camera
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import Event, HomeAssistant
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.util import Throttle
from . import AbodeConfigEntry, AbodeSystem
from .const import LOGGER
from . import AbodeSystem
from .const import DOMAIN_DATA, LOGGER
from .entity import AbodeDevice
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=90)
@@ -24,11 +27,11 @@ MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=90)
async def async_setup_entry(
hass: HomeAssistant,
entry: AbodeConfigEntry,
entry: ConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up Abode camera devices."""
data = entry.runtime_data
data = hass.data[DOMAIN_DATA]
async_add_entities(
AbodeCamera(data, device, timeline.CAPTURE_IMAGE)
@@ -1,5 +1,7 @@
"""Config flow for the Abode Security System component."""
from __future__ import annotations
from collections.abc import Mapping
from http import HTTPStatus
from typing import Any, cast
+9
View File
@@ -1,10 +1,19 @@
"""Constants for the Abode Security System component."""
from __future__ import annotations
import logging
from typing import TYPE_CHECKING
from homeassistant.util.hass_dict import HassKey
if TYPE_CHECKING:
from . import AbodeSystem
LOGGER = logging.getLogger(__package__)
DOMAIN = "abode"
DOMAIN_DATA: HassKey[AbodeSystem] = HassKey(DOMAIN)
ATTRIBUTION = "Data provided by goabode.com"
CONF_POLLING = "polling"
+4 -3
View File
@@ -5,20 +5,21 @@ from typing import Any
from jaraco.abode.devices.cover import Cover
from homeassistant.components.cover import CoverEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import AbodeConfigEntry
from .const import DOMAIN_DATA
from .entity import AbodeDevice
async def async_setup_entry(
hass: HomeAssistant,
entry: AbodeConfigEntry,
entry: ConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up Abode cover devices."""
data = entry.runtime_data
data = hass.data[DOMAIN_DATA]
async_add_entities(
AbodeCover(data, device)
+2 -2
View File
@@ -7,7 +7,7 @@ from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity import Entity
from . import AbodeSystem
from .const import ATTRIBUTION, DOMAIN
from .const import ATTRIBUTION, DOMAIN, DOMAIN_DATA
class AbodeEntity(Entity):
@@ -29,7 +29,7 @@ class AbodeEntity(Entity):
self._update_connection_status,
)
self._data.entity_ids.add(self.entity_id)
self.hass.data[DOMAIN_DATA].entity_ids.add(self.entity_id)
async def async_will_remove_from_hass(self) -> None:
"""Unsubscribe from Abode connection status updates."""
+6 -3
View File
@@ -1,5 +1,7 @@
"""Support for Abode Security System lights."""
from __future__ import annotations
from math import ceil
from typing import Any
@@ -14,20 +16,21 @@ from homeassistant.components.light import (
ColorMode,
LightEntity,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import AbodeConfigEntry
from .const import DOMAIN_DATA
from .entity import AbodeDevice
async def async_setup_entry(
hass: HomeAssistant,
entry: AbodeConfigEntry,
entry: ConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up Abode light devices."""
data = entry.runtime_data
data = hass.data[DOMAIN_DATA]
async_add_entities(
AbodeLight(data, device)
+4 -3
View File
@@ -5,20 +5,21 @@ from typing import Any
from jaraco.abode.devices.lock import Lock
from homeassistant.components.lock import LockEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import AbodeConfigEntry
from .const import DOMAIN_DATA
from .entity import AbodeDevice
async def async_setup_entry(
hass: HomeAssistant,
entry: AbodeConfigEntry,
entry: ConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up Abode lock devices."""
data = entry.runtime_data
data = hass.data[DOMAIN_DATA]
async_add_entities(
AbodeLock(data, device)
+7 -3
View File
@@ -1,5 +1,7 @@
"""Support for Abode Security System sensors."""
from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
from typing import cast
@@ -12,11 +14,13 @@ from homeassistant.components.sensor import (
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import LIGHT_LUX, PERCENTAGE, UnitOfTemperature
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import AbodeConfigEntry, AbodeSystem
from . import AbodeSystem
from .const import DOMAIN_DATA
from .entity import AbodeDevice
ABODE_TEMPERATURE_UNIT_HA_UNIT = {
@@ -62,11 +66,11 @@ SENSOR_TYPES: tuple[AbodeSensorDescription, ...] = (
async def async_setup_entry(
hass: HomeAssistant,
entry: AbodeConfigEntry,
entry: ConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up Abode sensor devices."""
data = entry.runtime_data
data = hass.data[DOMAIN_DATA]
async_add_entities(
AbodeSensor(data, device, description)
+5 -17
View File
@@ -1,20 +1,16 @@
"""Support for the Abode Security System."""
from typing import TYPE_CHECKING
from __future__ import annotations
from jaraco.abode.exceptions import Exception as AbodeException
import voluptuous as vol
from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.core import HomeAssistant, ServiceCall, callback
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
if TYPE_CHECKING:
from . import AbodeConfigEntry, AbodeSystem
from .const import DOMAIN, DOMAIN_DATA, LOGGER
ATTR_SETTING = "setting"
ATTR_VALUE = "value"
@@ -29,21 +25,13 @@ CAPTURE_IMAGE_SCHEMA = vol.Schema({ATTR_ENTITY_ID: cv.entity_ids})
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."""
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:
"""Change an Abode system setting."""
setting = call.data[ATTR_SETTING]
value = call.data[ATTR_VALUE]
try:
_get_abode_system(call.hass).abode.set_setting(setting, value)
call.hass.data[DOMAIN_DATA].abode.set_setting(setting, value)
except AbodeException as ex:
LOGGER.warning(ex)
@@ -54,7 +42,7 @@ def _capture_image(call: ServiceCall) -> None:
target_entities = [
entity_id
for entity_id in _get_abode_system(call.hass).entity_ids
for entity_id in call.hass.data[DOMAIN_DATA].entity_ids
if entity_id in entity_ids
]
@@ -69,7 +57,7 @@ def _trigger_automation(call: ServiceCall) -> None:
target_entities = [
entity_id
for entity_id in _get_abode_system(call.hass).entity_ids
for entity_id in call.hass.data[DOMAIN_DATA].entity_ids
if entity_id in entity_ids
]
+6 -3
View File
@@ -1,15 +1,18 @@
"""Support for Abode Security System switches."""
from __future__ import annotations
from typing import Any, cast
from jaraco.abode.devices.switch import Switch
from homeassistant.components.switch import SwitchEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import AbodeConfigEntry
from .const import DOMAIN_DATA
from .entity import AbodeAutomation, AbodeDevice
DEVICE_TYPES = ["switch", "valve"]
@@ -17,11 +20,11 @@ DEVICE_TYPES = ["switch", "valve"]
async def async_setup_entry(
hass: HomeAssistant,
entry: AbodeConfigEntry,
entry: ConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up Abode switch devices."""
data = entry.runtime_data
data = hass.data[DOMAIN_DATA]
entities: list[SwitchEntity] = [
AbodeSwitch(data, device)
@@ -1,5 +1,7 @@
"""Coordinator for Acaia integration."""
from __future__ import annotations
from datetime import timedelta
import logging
@@ -1,5 +1,7 @@
"""Diagnostics support for Acaia."""
from __future__ import annotations
from dataclasses import asdict
from typing import Any
+1 -1
View File
@@ -143,4 +143,4 @@ class AcaiaRestoreSensor(AcaiaEntity, RestoreSensor):
@property
def available(self) -> bool:
"""Return True if entity is available."""
return super().available or self.native_value is not None
return super().available or self._restored_data is not None
@@ -1,5 +1,7 @@
"""The AccuWeather component."""
from __future__ import annotations
import asyncio
import logging
@@ -1,8 +1,10 @@
"""Adds config flow for AccuWeather."""
from __future__ import annotations
from asyncio import timeout
from collections.abc import Mapping
from typing import TYPE_CHECKING, Any
from typing import Any
from accuweather import AccuWeather, ApiError, InvalidApiKeyError, RequestsExceededError
from aiohttp import ClientError
@@ -10,7 +12,7 @@ from aiohttp.client_exceptions import ClientConnectorError
import voluptuous as vol
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.aiohttp_client import async_get_clientsession
@@ -53,11 +55,8 @@ class AccuWeatherFlowHandler(ConfigFlow, domain=DOMAIN):
)
self._abort_if_unique_id_configured()
if TYPE_CHECKING:
assert accuweather.location_name is not None
return self.async_create_entry(
title=accuweather.location_name, data=user_input
title=user_input[CONF_NAME], data=user_input
)
return self.async_show_form(
@@ -71,6 +70,9 @@ class AccuWeatherFlowHandler(ConfigFlow, domain=DOMAIN):
vol.Optional(
CONF_LONGITUDE, default=self.hass.config.longitude
): cv.longitude,
vol.Optional(
CONF_NAME, default=self.hass.config.location_name
): str,
}
),
errors=errors,
@@ -1,5 +1,7 @@
"""Constants for AccuWeather integration."""
from __future__ import annotations
from datetime import timedelta
from typing import Final
@@ -1,5 +1,7 @@
"""The AccuWeather coordinator."""
from __future__ import annotations
from asyncio import timeout
from collections.abc import Awaitable, Callable
from dataclasses import dataclass
@@ -62,7 +64,7 @@ class AccuWeatherObservationDataUpdateCoordinator(
"""Initialize."""
self.accuweather = accuweather
self.location_key = accuweather.location_key
name = config_entry.data.get(CONF_NAME) or config_entry.title
name = config_entry.data[CONF_NAME]
if TYPE_CHECKING:
assert self.location_key is not None
@@ -120,7 +122,7 @@ class AccuWeatherForecastDataUpdateCoordinator(
self.accuweather = accuweather
self.location_key = accuweather.location_key
self._fetch_method = fetch_method
name = config_entry.data.get(CONF_NAME) or config_entry.title
name = config_entry.data[CONF_NAME]
if TYPE_CHECKING:
assert self.location_key is not None
@@ -1,5 +1,7 @@
"""Diagnostics support for AccuWeather."""
from __future__ import annotations
from typing import Any
from homeassistant.components.diagnostics import async_redact_data
@@ -1,5 +1,7 @@
"""Support for the AccuWeather service."""
from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
from typing import Any, cast
@@ -25,7 +25,8 @@
"data": {
"api_key": "[%key:common::config_flow::data::api_key%]",
"latitude": "[%key:common::config_flow::data::latitude%]",
"longitude": "[%key:common::config_flow::data::longitude%]"
"longitude": "[%key:common::config_flow::data::longitude%]",
"name": "[%key:common::config_flow::data::name%]"
},
"data_description": {
"api_key": "API key generated in the AccuWeather APIs portal."
@@ -1,5 +1,7 @@
"""Provide info to system health."""
from __future__ import annotations
from typing import Any
from accuweather.const import ENDPOINT
@@ -1,5 +1,7 @@
"""Support for the AccuWeather service."""
from __future__ import annotations
from typing import cast
from homeassistant.components.weather import (
@@ -1,5 +1,7 @@
"""Use serial protocol of Acer projector to obtain state of the projector."""
from __future__ import annotations
from typing import Final
from homeassistant.const import STATE_OFF, STATE_ON
@@ -1,5 +1,7 @@
"""Use serial protocol of Acer projector to obtain state of the projector."""
from __future__ import annotations
import logging
import re
from typing import Any
@@ -1,5 +1,7 @@
"""Config flow for Rollease Acmeda Automate Pulse Hub."""
from __future__ import annotations
from asyncio import timeout
from contextlib import suppress
from typing import Any
+2
View File
@@ -1,5 +1,7 @@
"""Support for Acmeda Roller Blinds."""
from __future__ import annotations
from typing import Any
from homeassistant.components.cover import (
@@ -1,5 +1,7 @@
"""Base class for Acmeda Roller Blinds."""
from __future__ import annotations
import aiopulse
from homeassistant.core import callback
@@ -1,5 +1,7 @@
"""Helper functions for Acmeda Pulse."""
from __future__ import annotations
from typing import TYPE_CHECKING
from aiopulse import Roller
+2
View File
@@ -1,5 +1,7 @@
"""Code to handle a Pulse Hub."""
from __future__ import annotations
import asyncio
from collections.abc import Callable
@@ -1,5 +1,7 @@
"""Support for Acmeda Roller Blind Batteries."""
from __future__ import annotations
from homeassistant.components.sensor import SensorDeviceClass, SensorEntity
from homeassistant.const import PERCENTAGE
from homeassistant.core import HomeAssistant, callback
@@ -1,5 +1,7 @@
"""Support for Actiontec MI424WR (Verizon FIOS) routers."""
from __future__ import annotations
import re
from typing import Final
@@ -1,5 +1,7 @@
"""Support for Actiontec MI424WR (Verizon FIOS) routers."""
from __future__ import annotations
import logging
from typing import Final
+3 -26
View File
@@ -38,7 +38,6 @@ HVAC_MODE_MAPPING_ACTRONAIR_TO_HA = {
"HEAT": HVACMode.HEAT,
"FAN": HVACMode.FAN_ONLY,
"AUTO": HVACMode.AUTO,
"DRY": HVACMode.DRY,
"OFF": HVACMode.OFF,
}
HVAC_MODE_MAPPING_HA_TO_ACTRONAIR = {
@@ -80,6 +79,7 @@ class ActronAirClimateEntity(ClimateEntity):
)
_attr_name = None
_attr_fan_modes = list(FAN_MODE_MAPPING_ACTRONAIR_TO_HA.values())
_attr_hvac_modes = list(HVAC_MODE_MAPPING_ACTRONAIR_TO_HA.values())
class ActronSystemClimate(ActronAirAcEntity, ActronAirClimateEntity):
@@ -93,17 +93,6 @@ class ActronSystemClimate(ActronAirAcEntity, ActronAirClimateEntity):
super().__init__(coordinator)
self._attr_unique_id = self._serial_number
@property
def hvac_modes(self) -> list[HVACMode]:
"""Return the list of supported HVAC modes."""
modes = [
HVAC_MODE_MAPPING_ACTRONAIR_TO_HA[mode]
for mode in self._status.user_aircon_settings.supported_modes
if mode in HVAC_MODE_MAPPING_ACTRONAIR_TO_HA
]
modes.append(HVACMode.OFF)
return modes
@property
def min_temp(self) -> float:
"""Return the minimum temperature that can be set."""
@@ -147,7 +136,7 @@ class ActronSystemClimate(ActronAirAcEntity, ActronAirClimateEntity):
@property
def target_temperature(self) -> float:
"""Return the target temperature."""
return self._status.user_aircon_settings.current_setpoint
return self._status.user_aircon_settings.temperature_setpoint_cool_c
@actron_air_command
async def async_set_fan_mode(self, fan_mode: str) -> None:
@@ -190,18 +179,6 @@ class ActronZoneClimate(ActronAirZoneEntity, ActronAirClimateEntity):
super().__init__(coordinator, zone)
self._attr_unique_id: str = self._zone_identifier
@property
def hvac_modes(self) -> list[HVACMode]:
"""Return the list of supported HVAC modes."""
status = self.coordinator.data
modes = [
HVAC_MODE_MAPPING_ACTRONAIR_TO_HA[mode]
for mode in status.user_aircon_settings.supported_modes
if mode in HVAC_MODE_MAPPING_ACTRONAIR_TO_HA
]
modes.append(HVACMode.OFF)
return modes
@property
def min_temp(self) -> float:
"""Return the minimum temperature that can be set."""
@@ -239,7 +216,7 @@ class ActronZoneClimate(ActronAirZoneEntity, ActronAirClimateEntity):
@property
def target_temperature(self) -> float | None:
"""Return the target temperature."""
return self._zone.current_setpoint
return self._zone.temperature_setpoint_cool_c
@actron_air_command
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
@@ -6,12 +6,7 @@ from typing import Any
from actron_neo_api import ActronAirAPI, ActronAirAuthError
from homeassistant.config_entries import (
SOURCE_REAUTH,
SOURCE_RECONFIGURE,
ConfigFlow,
ConfigFlowResult,
)
from homeassistant.config_entries import SOURCE_REAUTH, ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_API_TOKEN
from homeassistant.exceptions import HomeAssistantError
@@ -110,14 +105,6 @@ class ActronAirConfigFlow(ConfigFlow, domain=DOMAIN):
data_updates={CONF_API_TOKEN: self._api.refresh_token_value},
)
# Check if this is a reconfigure flow
if self.source == SOURCE_RECONFIGURE:
self._abort_if_unique_id_mismatch(reason="wrong_account")
return self.async_update_reload_and_abort(
self._get_reconfigure_entry(),
data_updates={CONF_API_TOKEN: self._api.refresh_token_value},
)
self._abort_if_unique_id_configured()
return self.async_create_entry(
title=user_data.email,
@@ -151,20 +138,6 @@ class ActronAirConfigFlow(ConfigFlow, domain=DOMAIN):
return self.async_show_form(step_id="reauth_confirm")
async def async_step_reconfigure(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle reconfiguration request."""
return await self.async_step_reconfigure_confirm()
async def async_step_reconfigure_confirm(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Confirm reconfiguration dialog."""
if user_input is not None:
return await self.async_step_user()
return self.async_show_form(step_id="reconfigure_confirm")
async def async_step_connection_error(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
@@ -1,5 +1,7 @@
"""Coordinator for Actron Air integration."""
from __future__ import annotations
from dataclasses import dataclass
from datetime import timedelta
@@ -1,5 +1,7 @@
"""Diagnostics support for Actron Air."""
from __future__ import annotations
from typing import Any
from homeassistant.components.diagnostics import async_redact_data
@@ -13,5 +13,5 @@
"integration_type": "hub",
"iot_class": "cloud_polling",
"quality_scale": "silver",
"requirements": ["actron-neo-api==0.5.6"]
"requirements": ["actron-neo-api==0.5.3"]
}
@@ -60,7 +60,7 @@ rules:
entity-translations: done
exception-translations: done
icon-translations: done
reconfiguration-flow: done
reconfiguration-flow: todo
repair-issues:
status: exempt
comment: This integration does not have any known issues that require repair.
@@ -4,8 +4,7 @@
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]",
"oauth2_error": "Failed to start authentication flow",
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]",
"wrong_account": "You must authenticate with the same Actron Air account that was originally configured."
"wrong_account": "You must reauthenticate with the same Actron Air account that was originally configured."
},
"error": {
"oauth2_error": "Failed to start authentication flow. Please try again later."
@@ -23,10 +22,6 @@
"description": "Your Actron Air authentication has expired. Select continue to reauthenticate with your Actron Air account. You will be prompted to log in again to restore the connection.",
"title": "Authentication expired"
},
"reconfigure_confirm": {
"description": "Reconfigure your Actron Air account. You will be prompted to log in again. Note: you must use the same account that was originally configured.",
"title": "Reconfigure Actron Air"
},
"timeout": {
"data": {},
"description": "The authentication process timed out. Please try again.",
@@ -1,5 +1,7 @@
"""The Adax integration."""
from __future__ import annotations
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
+2
View File
@@ -1,5 +1,7 @@
"""Support for Adax wifi-enabled home heaters."""
from __future__ import annotations
from typing import Any, cast
from adax import Adax
@@ -1,5 +1,7 @@
"""Config flow for Adax integration."""
from __future__ import annotations
import logging
from typing import Any
+2
View File
@@ -1,5 +1,7 @@
"""Support for Adax energy sensors."""
from __future__ import annotations
from dataclasses import dataclass
from typing import cast
@@ -1,5 +1,7 @@
"""Support for AdGuard Home."""
from __future__ import annotations
from dataclasses import dataclass
from adguardhome import AdGuardHome, AdGuardHomeConnectionError
@@ -1,5 +1,7 @@
"""Config flow to configure the AdGuard Home integration."""
from __future__ import annotations
from typing import Any
from adguardhome import AdGuardHome, AdGuardHomeConnectionError
@@ -1,5 +1,7 @@
"""AdGuard Home base entity."""
from __future__ import annotations
from adguardhome import AdGuardHomeError
from homeassistant.config_entries import SOURCE_HASSIO
@@ -1,5 +1,7 @@
"""Support for AdGuard Home sensors."""
from __future__ import annotations
from collections.abc import Callable, Coroutine
from dataclasses import dataclass
from datetime import timedelta
@@ -1,5 +1,7 @@
"""Support for AdGuard Home switches."""
from __future__ import annotations
from collections.abc import Callable, Coroutine
from dataclasses import dataclass
from datetime import timedelta
@@ -1,5 +1,7 @@
"""AdGuard Home Update platform."""
from __future__ import annotations
from datetime import timedelta
from typing import Any
@@ -1,5 +1,7 @@
"""Support for ADS binary sensors."""
from __future__ import annotations
import pyads
import voluptuous as vol
+2
View File
@@ -1,5 +1,7 @@
"""Support for Automation Device Specification (ADS)."""
from __future__ import annotations
from enum import StrEnum
from typing import TYPE_CHECKING
+2
View File
@@ -1,5 +1,7 @@
"""Support for ADS covers."""
from __future__ import annotations
from typing import Any
import pyads
+2
View File
@@ -1,5 +1,7 @@
"""Support for ADS light sources."""
from __future__ import annotations
from typing import Any
import pyads
+2
View File
@@ -1,5 +1,7 @@
"""Support for ADS select entities."""
from __future__ import annotations
import pyads
import voluptuous as vol
+2
View File
@@ -1,5 +1,7 @@
"""Support for ADS sensors."""
from __future__ import annotations
import voluptuous as vol
from homeassistant.components.sensor import (
+2
View File
@@ -1,5 +1,7 @@
"""Support for ADS switch platform."""
from __future__ import annotations
from typing import Any
import pyads
+2
View File
@@ -1,5 +1,7 @@
"""Support for ADS valves."""
from __future__ import annotations
import pyads
import voluptuous as vol
@@ -1,5 +1,7 @@
"""Binary Sensor platform for Advantage Air integration."""
from __future__ import annotations
from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass,
BinarySensorEntity,

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