Compare commits

..

102 Commits

Author SHA1 Message Date
Paul Bottein 6860e0f3b9 Remove comment 2026-06-25 13:02:39 +02:00
Paul Bottein d3be0cc852 Simplify live attribute handling and use event enum 2026-06-25 12:49:01 +02:00
Paul Bottein c830c05cbd Expose selected state attributes in the logbook 2026-06-25 12:15:44 +02:00
Ronald van der Meer 2be55a06cc Migrate Duco sensor units to UnitOfRatio (#174791) 2026-06-25 10:09:55 +02:00
bkobus-bbx d786fb16a0 Migrate blebox to UnitOfDensity / UnitOfRatio enums (#174790) 2026-06-25 10:08:56 +02:00
J. Nick Koston f78dd797b1 Bump habluetooth to 6.25.1 (#174700) 2026-06-25 09:40:43 +02:00
davidrule1969 0d957a971d Bump pySwitchbot to 2.3.0 (#174678) 2026-06-25 08:51:22 +02:00
Raphael Hehl cff3a711f3 Bump uiprotect to 15.0.0 (#174709) 2026-06-25 08:42:32 +02:00
Brandon Rothweiler 177c4a4fb5 Bump dropbox to silver quality (#174706) 2026-06-25 07:39:05 +02:00
Samuel Xiao 7d8204f5e7 Bump switchbot-api to 2.12.0 (#174705) 2026-06-25 07:37:59 +02:00
Franck Nijhof 9aed167f71 Bump version to 2026.8.0.dev0 (#174693) 2026-06-25 00:44:19 +02:00
Franck Nijhof a8630f5570 Add delegated charging mode to Renault integration (#174687) 2026-06-24 23:35:13 +02:00
J. Nick Koston 2a75b0e2fb Bump habluetooth to 6.24.0 (#174688) 2026-06-24 23:34:38 +02:00
Brandon Rothweiler 9c4ad761c4 Add missing scope and authorize param to Dropbox OAuth (#174587) 2026-06-24 22:26:30 +02:00
Erwin Douna 8e3e1044a1 Tami4 group executor job (#174668) 2026-06-24 22:01:24 +02:00
Colin bec6c94e32 openevse: Convert config to textselector (#174675) 2026-06-24 21:50:41 +02:00
Colin c9729df69a openevse: Add missing callback test (#174560) 2026-06-24 21:33:30 +02:00
Christian Lackas 70ff0fd682 Bump homematicip to 2.13.2 (#174673) 2026-06-24 20:43:23 +02:00
Erwin Douna 258ae6d506 Vera core group executor job (#174669) 2026-06-24 20:37:08 +02:00
Ville Skyttä 4f93afd6ae Remove myself from huawei_lte codeowners (#174671) 2026-06-24 19:57:24 +02:00
Erwin Douna 7968fc4809 Huawei group executor job (#174666) 2026-06-24 19:43:54 +02:00
TheJulianJES 975f2a831e Bump zha-quirks to 2.1.0 (#174662) 2026-06-24 18:42:41 +02:00
Leonardo Merza cc2944d626 Fix ecobee active sensor reporting for custom presets and shared device names (#174417) 2026-06-24 17:21:45 +01:00
Mick Vleeshouwer 548ec5cacf Enable action queue to batch concurrent commands in Overkiz (#174275) 2026-06-24 17:19:15 +02:00
karwosts dc6eef2844 Fix date-only input_datetime timestamp attribute to use the correct TZ (#174357) 2026-06-24 17:15:09 +02:00
Stefan Agner 0808e30e37 Add repair when IPv6 is disabled for Matter (#174653)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-24 17:11:54 +02:00
Manu f0ed257f47 Refactor Steam integration config flow and tests (#174504)
Co-authored-by: Erwin Douna <e.douna@gmail.com>
2026-06-24 17:06:29 +02:00
Petro31 b4b710b474 Add restore state framework for template entities (#172847) 2026-06-24 16:54:56 +02:00
Bram Kragten 0004a82fe4 Update frontend to 20260624.0 (#174657) 2026-06-24 15:46:44 +01:00
epenet 0c4bc95bdd Migrate base entity attributes to StrEnum (#174633) 2026-06-24 15:38:49 +01:00
Manu 5fdab795e8 Remove Avi-on integration (#174649) 2026-06-24 16:35:22 +02:00
Manu 2193665909 Remove BeeWi SmartClim (#174651) 2026-06-24 16:32:05 +02:00
Martin Hjelmare c9d91d5812 Add enabled entity limit per config entry (#174194) 2026-06-24 16:07:54 +02:00
Erik Montnemery de9d9c66c1 Add additional sun conditions (#174537) 2026-06-24 16:00:08 +02:00
Paul Bottein dfcc4d1ae4 Fix friendly name for restored unavailable entities (#174614) 2026-06-24 14:47:39 +01:00
TimL d71812f09b Bump pysmlight to 0.4.0 (#174640) 2026-06-24 15:19:35 +02:00
Ariel Ebersberger a323ebe634 Reword "advanced operation" channel warning in Home Assistant Hardware (#174645) 2026-06-24 14:57:18 +02:00
Ariel Ebersberger 024bba55cf Reword "Configure advanced voice settings" in ElevenLabs (#174642) 2026-06-24 14:33:41 +02:00
Ariel Ebersberger a5546566e7 Rename "(advanced)" service names in Z-Wave (#174644) 2026-06-24 14:32:38 +02:00
Ariel Ebersberger 3d9994ee4f Rename "Local Risco Panel (advanced)" option in Risco (#174643) 2026-06-24 14:31:55 +02:00
Erik Montnemery c542f38387 Add checks for did_not_trigger calls to trigger tests (#174636)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 13:05:08 +01:00
wollew 49d6166b7e Bump pyvlx to 0.2.36 (#174638) 2026-06-24 13:21:45 +02:00
J. Nick Koston 7249190c64 Bump habluetooth to 6.23.1 (#174639) 2026-06-24 13:21:12 +02:00
epenet cebdde6ab4 Migrate device tracker entity attributes to StrEnum (#174621) 2026-06-24 12:53:43 +02:00
epenet 031f4cd965 Migrate remaining platform entity attributes to StrEnum (#174625) 2026-06-24 12:52:50 +02:00
Ariel Ebersberger a734f7110c Rename "Advanced options" to "Additional options" in DNS IP (#174628) 2026-06-24 12:38:16 +02:00
Ariel Ebersberger 395e949591 Rename "Advanced settings" to "Additional settings" in History Stats (#174629) 2026-06-24 12:38:00 +02:00
Ariel Ebersberger 484e60a1c4 Rename "Advanced options" to "Additional options" in SQL (#174631) 2026-06-24 12:19:38 +02:00
Ariel Ebersberger b7a234fbd9 Rename "Advanced settings" to "Additional settings" in Telegram bot (#174632) 2026-06-24 12:19:08 +02:00
Ariel Ebersberger a1982fbd54 Rename "Advanced settings" to "Additional settings" in Autoskope (#174630) 2026-06-24 12:18:53 +02:00
epenet c384cd9894 Migrate calendar entity attributes to StrEnum (#174615) 2026-06-24 12:10:37 +02:00
Tom 1aefd2a5ac Bump airOS dependency to support open wireless (#174559) 2026-06-24 12:00:38 +02:00
epenet e3605be5cd Migrate siren entity attributes to StrEnum (#174616) 2026-06-24 11:54:13 +02:00
epenet e87a41a01d Migrate text entity attributes to StrEnum (#174619) 2026-06-24 11:51:28 +02:00
epenet 190ff034aa Migrate vacuum entity attributes to StrEnum (#174617) 2026-06-24 11:46:29 +02:00
epenet b301925687 Migrate cover entity attributes to StrEnum (#174601) 2026-06-24 11:18:37 +02:00
epenet 7a0f5b066e Migrate humidifier entity attributes to StrEnum (#174609) 2026-06-24 11:17:45 +02:00
epenet 308fad166d Migrate media player entity attributes to StrEnum (#174605) 2026-06-24 11:16:33 +02:00
epenet 1305c2978c Migrate fan entity attributes to StrEnum (#174610) 2026-06-24 11:12:45 +02:00
epenet 955ad6db1b Migrate valve entity attributes to StrEnum (#174611) 2026-06-24 11:12:02 +02:00
Andreas Schneider 87dc013803 Use age-based filter for Matter BLE advertisement history replay (#173488) 2026-06-24 04:11:29 -05:00
Stefan Agner 1bb41cb2dd Use translated message when Matter Server add-on is not ready (#174529)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 11:02:08 +02:00
epenet 277af6c60b Migrate update entity attributes to StrEnum (#174608) 2026-06-24 10:57:58 +02:00
Raphael Hehl 69e18aa580 Source UniFi Protect light auto-shutoff duration from the public API (#174518) 2026-06-24 03:54:38 -05:00
Raphael Hehl 75852fc191 Add ufp_public_enabled_fn for public-API availability gating to UniFi Protect (#174544) 2026-06-24 03:53:49 -05:00
Mark Ruys a661b678a2 Add Sensoterra CODEOWNERs (#174431)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-24 10:53:36 +02:00
Raphael Hehl bd0951110d Drive the UniFi Protect doorbell ring event from the public events websocket (#174546) 2026-06-24 03:53:01 -05:00
epenet 899f904cf3 Migrate weather entity attributes to StrEnum (#174604) 2026-06-24 10:38:25 +02:00
epenet d2e7426aa5 Migrate image entity attributes to StrEnum (#174606) 2026-06-24 10:37:38 +02:00
epenet c0e02457bc Migrate lock entity attributes to StrEnum (#174607) 2026-06-24 10:37:11 +02:00
epenet e7562b50cf Migrate water heater entity attributes to StrEnum (#174603) 2026-06-24 10:32:38 +02:00
Thomas c36e4a03e0 Bump boschshcpy to 0.3.5 (#174550) 2026-06-24 10:16:45 +02:00
Nathan Spencer 71430af6ff Bump pylitterbot to 2025.5.0 (#174554) 2026-06-24 10:15:43 +02:00
Åke Strandberg 815cce5a0c Tweak aqvify entity names (#174597) 2026-06-24 10:08:15 +02:00
Robert Svensson 32929755eb Tighten the Axis unique ID in config flow (#172283) 2026-06-24 10:07:10 +02:00
Manu 88d4d1c879 Remove SCSGate integration (#174571) 2026-06-24 10:02:23 +02:00
Manu 51bd71d096 Remove Acer projector integration (#174579) 2026-06-24 09:58:09 +02:00
Thomas 1fcf9eb5b7 Add @mosandlt as codeowner for bosch_shc (#174563) 2026-06-24 09:57:19 +02:00
epenet 1917a007f8 Migrate event entity attributes to StrEnum (#174592) 2026-06-24 09:56:20 +02:00
Paul Bottein b095baa65a Bump Yoto quality scale to platinum (#174598) 2026-06-24 09:52:20 +02:00
Przemysław Szypowicz 2bd81c7351 Remove broken Ampio Smog integration (#173080) 2026-06-24 09:50:46 +02:00
Paul Bottein a576aef9a4 Add dynamic and stale device handling to Yoto (#173298)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-24 09:32:10 +02:00
Michael Hansen c2e780dfd2 Improve Wyoming satellite reconnect and tolerance of other satellites (#174460)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 09:22:03 +02:00
epenet 687064d5cc Migrate Ratio units to StrEnum (#172568) 2026-06-24 09:21:28 +02:00
epenet 0b801d74cd Migrate camera entity attributes to StrEnum (#174591) 2026-06-24 09:15:44 +02:00
epenet dbdcb1a91e Migrate alarm control panel entity attributes to StrEnum (#174590) 2026-06-24 09:06:16 +02:00
epenet f9bf7ab122 Migrate sensor entity attributes to StrEnum (#174595) 2026-06-24 09:05:32 +02:00
epenet d484f75c7b Migrate number entity attributes to StrEnum (#174593) 2026-06-24 09:04:22 +02:00
dependabot[bot] 5ba4d588b6 Bump actions/checkout from 6.0.3 to 7.0.0 (#174596)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-24 08:59:49 +02:00
epenet 42da67e7a9 Migrate light entity attributes to StrEnum (#174589) 2026-06-24 08:58:00 +02:00
Joost Lekkerkerker 7d08a7b898 Add new integration quality scale rules (#174555) 2026-06-24 08:53:46 +02:00
puddly 05016e46c8 Bump ZHA to 2.0.0 (#174586) 2026-06-24 08:52:44 +02:00
J. Nick Koston 58603326e3 Bump habluetooth to 6.19.1 (#174582) 2026-06-23 22:58:58 -05:00
Steve Rice 10fe3dc13f Add cloudhook support to switchbot_cloud webhook (#174566)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 21:50:08 -04:00
J. Nick Koston adf2f2854c Active scan govee_ble only when needed and widen the window to 30s (#174557) 2026-06-23 21:03:38 -04:00
A. Gideonse 7fd101005d Bump indevolt-api to 1.8.6 (#174573) 2026-06-24 03:03:09 +02:00
J. Nick Koston 40aa8dd617 Widen the INKBIRD IBS-TH2 active scan window to 30s (#174565) 2026-06-23 21:02:55 -04:00
Matthias Alphart fb283dfb93 Update knx-frontend to 2026.6.23.203726 (#174567) 2026-06-23 20:55:45 -04:00
J. Nick Koston 2084d52504 Bump habluetooth to 6.13.0 (#174568) 2026-06-23 20:55:34 -04:00
Fabian Munkes cb914495e7 Add username parameter to play media action in Music Assistant (#174486) 2026-06-24 01:04:44 +02:00
J. Nick Koston 97f2eecc57 Bump bluetooth-adapters to 2.4.0 (#174575) 2026-06-23 17:58:06 -05:00
J. Nick Koston 2d1b3f799d Bump dbus-fast to 5.0.22 (#174569) 2026-06-23 23:32:27 +02:00
1771 changed files with 70650 additions and 65915 deletions
+6 -6
View File
@@ -38,7 +38,7 @@ jobs:
base_image_version: ${{ env.BASE_IMAGE_VERSION }}
steps:
- name: Checkout the repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
@@ -102,7 +102,7 @@ jobs:
os: ubuntu-24.04-arm
steps:
- name: Checkout the repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
@@ -245,7 +245,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout the repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
@@ -292,7 +292,7 @@ jobs:
contents: read
steps:
- name: Checkout the repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
@@ -469,7 +469,7 @@ jobs:
if: github.repository_owner == 'home-assistant' && needs.init.outputs.publish == 'true'
steps:
- name: Checkout the repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
@@ -516,7 +516,7 @@ jobs:
HASSFEST_IMAGE_TAG: ghcr.io/home-assistant/hassfest:${{ needs.init.outputs.version }}
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
@@ -36,7 +36,7 @@ jobs:
timeout-minutes: 10
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Set up Python
+4 -4
View File
@@ -31,7 +31,7 @@
# - GITHUB_TOKEN
#
# Custom actions used:
# - actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# - 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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
sparse-checkout: |
@@ -404,7 +404,7 @@ jobs:
echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json"
} >> "$GITHUB_OUTPUT"
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Create gh-aw temp directory
@@ -1236,7 +1236,7 @@ jobs:
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
- name: Checkout repository for patch context
if: needs.agent.outputs.has_patch == 'true'
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
# --- Threat Detection ---
+21 -21
View File
@@ -39,7 +39,7 @@ on:
env:
CACHE_VERSION: 3
MYPY_CACHE_VERSION: 1
HA_SHORT_VERSION: "2026.7"
HA_SHORT_VERSION: "2026.8"
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)
@@ -98,7 +98,7 @@ jobs:
skip_coverage: ${{ steps.info.outputs.skip_coverage }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Set up Python
@@ -825,7 +825,7 @@ jobs:
- base
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Download all coverage artifacts
+1 -1
View File
@@ -23,7 +23,7 @@ jobs:
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
+1 -1
View File
@@ -22,7 +22,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
+3 -3
View File
@@ -29,7 +29,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
@@ -116,7 +116,7 @@ jobs:
os: ubuntu-24.04-arm
steps:
- name: Checkout the repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
@@ -167,7 +167,7 @@ jobs:
os: ubuntu-24.04-arm
steps:
- name: Checkout the repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
-2
View File
@@ -43,7 +43,6 @@ 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.*
@@ -77,7 +76,6 @@ 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.*
Generated
+6 -7
View File
@@ -230,7 +230,6 @@ 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
@@ -254,8 +253,8 @@ CLAUDE.md @home-assistant/core
/tests/components/bond/ @bdraco @prystupa @joshs85 @marciogranzotto
/homeassistant/components/bosch_alarm/ @mag1024 @sanjay900
/tests/components/bosch_alarm/ @mag1024 @sanjay900
/homeassistant/components/bosch_shc/ @tschamm
/tests/components/bosch_shc/ @tschamm
/homeassistant/components/bosch_shc/ @tschamm @mosandlt
/tests/components/bosch_shc/ @tschamm @mosandlt
/homeassistant/components/brands/ @home-assistant/core
/tests/components/brands/ @home-assistant/core
/homeassistant/components/braviatv/ @bieniu @Drafteed
@@ -791,8 +790,8 @@ CLAUDE.md @home-assistant/core
/tests/components/html5/ @alexyao2015 @tr4nt0r
/homeassistant/components/http/ @home-assistant/core
/tests/components/http/ @home-assistant/core
/homeassistant/components/huawei_lte/ @scop @fphammerle
/tests/components/huawei_lte/ @scop @fphammerle
/homeassistant/components/huawei_lte/ @fphammerle
/tests/components/huawei_lte/ @fphammerle
/homeassistant/components/hue/ @marcelveldt
/tests/components/hue/ @marcelveldt
/homeassistant/components/hue_ble/ @flip-dots
@@ -1603,8 +1602,8 @@ CLAUDE.md @home-assistant/core
/tests/components/sensorpush/ @bdraco
/homeassistant/components/sensorpush_cloud/ @sstallion
/tests/components/sensorpush_cloud/ @sstallion
/homeassistant/components/sensoterra/ @markruys
/tests/components/sensoterra/ @markruys
/homeassistant/components/sensoterra/ @SanderBakkumCuriousInc @curious-florian @markruys
/tests/components/sensoterra/ @SanderBakkumCuriousInc @curious-florian @markruys
/homeassistant/components/sentry/ @dcramer @frenck
/tests/components/sentry/ @dcramer @frenck
/homeassistant/components/senz/ @milanmeu
@@ -14,9 +14,15 @@ 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: |
@@ -1 +0,0 @@
"""The acer_projector component."""
@@ -1,34 +0,0 @@
"""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",
}
@@ -1,9 +0,0 @@
{
"domain": "acer_projector",
"name": "Acer Projector",
"codeowners": [],
"documentation": "https://www.home-assistant.io/integrations/acer_projector",
"iot_class": "local_polling",
"quality_scale": "legacy",
"requirements": ["serialx==1.8.2"]
}
@@ -1,155 +0,0 @@
"""Use serial protocol of Acer projector to obtain state of the projector."""
import logging
import re
from typing import Any, override
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
@override
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
@override
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
@@ -12,9 +12,15 @@ 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.
@@ -18,9 +18,15 @@ 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.
@@ -12,9 +12,15 @@ rules:
docs-actions:
status: exempt
comment: This integration does not provide additional 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 register any events.
@@ -14,9 +14,15 @@ rules:
status: exempt
comment: |
This integration does not provide additional 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: |
@@ -14,9 +14,15 @@ rules:
docs-actions:
status: exempt
comment: Integration does not register custom 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: Integration does not subscribe to events.
@@ -12,9 +12,15 @@ rules:
docs-actions:
status: exempt
comment: Integration does not register custom 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: Integration does not use event subscriptions.
+1 -1
View File
@@ -8,5 +8,5 @@
"integration_type": "device",
"iot_class": "local_polling",
"quality_scale": "platinum",
"requirements": ["airos==0.6.8"]
"requirements": ["airos==0.6.9"]
}
@@ -12,9 +12,15 @@ rules:
docs-actions:
status: exempt
comment: airOS does not have 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: local_polling without events
@@ -10,9 +10,15 @@ rules:
docs-actions:
status: exempt
comment: Integration does not provide custom 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: |
@@ -12,9 +12,15 @@ rules:
docs-actions:
status: exempt
comment: Integration does not register any 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: Integration does not subscribe to external events.
@@ -8,7 +8,7 @@ from propcache.api import cached_property
import voluptuous as vol
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
from homeassistant.const import ( # noqa: F401
ATTR_CODE,
ATTR_CODE_FORMAT,
SERVICE_ALARM_ARM_AWAY,
@@ -28,11 +28,12 @@ from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.typing import ConfigType
from homeassistant.util.hass_dict import HassKey
from .const import (
from .const import ( # noqa: F401
ATTR_CHANGED_BY,
ATTR_CODE_ARM_REQUIRED,
DOMAIN,
AlarmControlPanelEntityFeature,
AlarmControlPanelEntityStateAttribute,
AlarmControlPanelState,
CodeFormat,
)
@@ -303,9 +304,11 @@ class AlarmControlPanelEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_A
def state_attributes(self) -> dict[str, Any] | None:
"""Return the state attributes."""
return {
ATTR_CODE_FORMAT: self.code_format,
ATTR_CHANGED_BY: self.changed_by,
ATTR_CODE_ARM_REQUIRED: self.code_arm_required,
AlarmControlPanelEntityStateAttribute.CODE_FORMAT: self.code_format,
AlarmControlPanelEntityStateAttribute.CHANGED_BY: self.changed_by,
AlarmControlPanelEntityStateAttribute.CODE_ARM_REQUIRED: (
self.code_arm_required
),
}
@override
@@ -9,6 +9,14 @@ ATTR_CHANGED_BY: Final = "changed_by"
ATTR_CODE_ARM_REQUIRED: Final = "code_arm_required"
class AlarmControlPanelEntityStateAttribute(StrEnum):
"""State attributes for alarm control panel entities."""
CODE_FORMAT = "code_format"
CHANGED_BY = "changed_by"
CODE_ARM_REQUIRED = "code_arm_required"
class AlarmControlPanelState(StrEnum):
"""Alarm control panel entity states."""
@@ -12,9 +12,15 @@ rules:
docs-actions:
status: exempt
comment: no 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: entities do not explicitly subscribe to events
@@ -14,9 +14,15 @@ rules:
status: exempt
comment: |
This integration does not provide additional 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: |
@@ -1 +0,0 @@
"""The Ampio component."""
@@ -1,105 +0,0 @@
"""Support for Ampio Air Quality data."""
import logging
from typing import Final, override
from asmog import AmpioSmog
import voluptuous as vol
from homeassistant.components.air_quality import (
PLATFORM_SCHEMA as AIR_QUALITY_PLATFORM_SCHEMA,
AirQualityEntity,
)
from homeassistant.const import CONF_NAME
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.util import Throttle
from .const import CONF_STATION_ID, SCAN_INTERVAL
_LOGGER: Final = logging.getLogger(__name__)
PLATFORM_SCHEMA: Final = AIR_QUALITY_PLATFORM_SCHEMA.extend(
{vol.Required(CONF_STATION_ID): cv.string, vol.Optional(CONF_NAME): cv.string}
)
async def async_setup_platform(
hass: HomeAssistant,
config: ConfigType,
async_add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the Ampio Smog air quality platform."""
name = config.get(CONF_NAME)
station_id = config[CONF_STATION_ID]
session = async_get_clientsession(hass)
api = AmpioSmogMapData(AmpioSmog(station_id, hass.loop, session))
await api.async_update()
if not api.api.data:
_LOGGER.error("Station %s is not available", station_id)
return
async_add_entities([AmpioSmogQuality(api, station_id, name)], True)
class AmpioSmogQuality(AirQualityEntity):
"""Implementation of an Ampio Smog air quality entity."""
_attr_attribution = "Data provided by Ampio"
def __init__(
self, api: AmpioSmogMapData, station_id: str, name: str | None
) -> None:
"""Initialize the air quality entity."""
self._ampio = api
self._station_id = station_id
self._name = name or api.api.name
@property
@override
def name(self) -> str:
"""Return the name of the air quality entity."""
return self._name
@property
@override
def unique_id(self) -> str:
"""Return unique_name."""
return f"ampio_smog_{self._station_id}" # pylint: disable=home-assistant-entity-unique-id-redundant-domain
@property
@override
def particulate_matter_2_5(self) -> str | None:
"""Return the particulate matter 2.5 level."""
return self._ampio.api.pm2_5 # type: ignore[no-any-return]
@property
@override
def particulate_matter_10(self) -> str | None:
"""Return the particulate matter 10 level."""
return self._ampio.api.pm10 # type: ignore[no-any-return]
async def async_update(self) -> None:
"""Get the latest data from the AmpioMap API."""
await self._ampio.async_update()
class AmpioSmogMapData:
"""Get the latest data and update the states."""
def __init__(self, api: AmpioSmog) -> None:
"""Initialize the data object."""
self.api = api
@Throttle(SCAN_INTERVAL)
async def async_update(self) -> None:
"""Get the latest data from AmpioMap."""
await self.api.get_data()
-7
View File
@@ -1,7 +0,0 @@
"""Constants for Ampio Air Quality platform."""
from datetime import timedelta
from typing import Final
CONF_STATION_ID: Final = "station_id"
SCAN_INTERVAL: Final = timedelta(minutes=10)
@@ -1,10 +0,0 @@
{
"domain": "ampio",
"name": "Ampio Smart Smog System",
"codeowners": [],
"documentation": "https://www.home-assistant.io/integrations/ampio",
"iot_class": "cloud_polling",
"loggers": ["asmog"],
"quality_scale": "legacy",
"requirements": ["asmog==0.0.6"]
}
@@ -14,9 +14,15 @@ rules:
status: exempt
comment: |
This integration does not provide additional actions.
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: todo
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: |
@@ -12,9 +12,15 @@ rules:
config-flow: done
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: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup: done
entity-unique-id: done
has-entity-name: done
@@ -14,9 +14,15 @@ 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: done
entity-unique-id: done
has-entity-name: done
@@ -14,9 +14,15 @@ rules:
status: exempt
comment: |
Integration has no 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: |
@@ -11,9 +11,15 @@ rules:
status: exempt
comment: |
The integration does not provide any 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: |
+7 -7
View File
@@ -1,23 +1,23 @@
{
"entity": {
"sensor": {
"available_volume": {
"default": "mdi:car-coolant-level"
},
"ground_water_level": {
"default": "mdi:arrow-collapse-down"
},
"in_flow": {
"default": "mdi:water-plus-outline"
},
"meter_value": {
"level_from_sensor": {
"default": "mdi:waves-arrow-up"
},
"level_from_top": {
"default": "mdi:waves"
},
"out_volume": {
"default": "mdi:water-pump"
},
"stored_volume": {
"default": "mdi:car-coolant-level"
},
"water_level": {
"default": "mdi:waves"
}
}
}
@@ -14,9 +14,15 @@ rules:
status: exempt
comment: |
The integration does not provide any 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: |
+6 -6
View File
@@ -46,8 +46,8 @@ class AqvifySensorAggrEntityDescription(SensorEntityDescription):
ENTITIES: tuple[AqvifySensorEntityDescription, ...] = (
AqvifySensorEntityDescription(
key="meter_value",
translation_key="meter_value",
key="level_from_sensor",
translation_key="level_from_sensor",
native_unit_of_measurement=UnitOfLength.METERS,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.DISTANCE,
@@ -55,8 +55,8 @@ ENTITIES: tuple[AqvifySensorEntityDescription, ...] = (
value_fn=lambda value: value.meter_value,
),
AqvifySensorEntityDescription(
key="water_level",
translation_key="water_level",
key="level_from_top",
translation_key="level_from_top",
native_unit_of_measurement=UnitOfLength.METERS,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.DISTANCE,
@@ -64,8 +64,8 @@ ENTITIES: tuple[AqvifySensorEntityDescription, ...] = (
value_fn=lambda value: value.water_level,
),
AqvifySensorEntityDescription(
key="stored_volume",
translation_key="stored_volume",
key="available_volume",
translation_key="available_volume",
native_unit_of_measurement=UnitOfVolume.LITERS,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.VOLUME_STORAGE,
+8 -8
View File
@@ -34,23 +34,23 @@
},
"entity": {
"sensor": {
"available_volume": {
"name": "Available volume"
},
"ground_water_level": {
"name": "Ground water level"
},
"in_flow": {
"name": "Inflow"
},
"meter_value": {
"name": "Meter value"
"level_from_sensor": {
"name": "Level from sensor"
},
"level_from_top": {
"name": "Level from top"
},
"out_volume": {
"name": "Outflow"
},
"stored_volume": {
"name": "Stored volume"
},
"water_level": {
"name": "Water level"
}
}
},
@@ -14,9 +14,15 @@ rules:
status: exempt
comment: |
This integration does not provide additional 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: |
@@ -12,7 +12,7 @@ import voluptuous as vol
from homeassistant.components import websocket_api
from homeassistant.components.blueprint import CONF_USE_BLUEPRINT
from homeassistant.const import (
from homeassistant.const import ( # noqa: F401
ATTR_AREA_ID,
ATTR_ENTITY_ID,
ATTR_FLOOR_ID,
@@ -58,7 +58,7 @@ from homeassistant.helpers.issue_registry import (
async_delete_issue,
)
from homeassistant.helpers.restore_state import RestoreEntity
from homeassistant.helpers.script import (
from homeassistant.helpers.script import ( # noqa: F401
ATTR_CUR,
ATTR_MAX,
CONF_MAX,
@@ -91,6 +91,8 @@ from .const import (
DEFAULT_INITIAL_STATE,
DOMAIN,
LOGGER,
AutomationEntityCapabilityAttribute,
AutomationEntityStateAttribute,
)
from .helpers import async_get_blueprints
from .trace import trace_automation
@@ -318,7 +320,13 @@ class BaseAutomationEntity(ToggleEntity, ABC):
"""Base class for automation entities."""
_entity_component_unrecorded_attributes = frozenset(
(ATTR_LAST_TRIGGERED, ATTR_MODE, ATTR_CUR, ATTR_MAX, CONF_ID)
(
AutomationEntityStateAttribute.LAST_TRIGGERED,
AutomationEntityStateAttribute.MODE,
AutomationEntityStateAttribute.CUR,
AutomationEntityStateAttribute.MAX,
AutomationEntityCapabilityAttribute.ID,
)
)
raw_config: ConfigType | None
@@ -327,7 +335,7 @@ class BaseAutomationEntity(ToggleEntity, ABC):
def capability_attributes(self) -> dict[str, Any] | None:
"""Return capability attributes."""
if self.unique_id is not None:
return {CONF_ID: self.unique_id}
return {AutomationEntityCapabilityAttribute.ID: self.unique_id}
return None
@cached_property
@@ -507,13 +515,15 @@ class AutomationEntity(BaseAutomationEntity, RestoreEntity):
@override
def extra_state_attributes(self) -> dict[str, Any]:
"""Return the entity state attributes."""
attrs = {
ATTR_LAST_TRIGGERED: self.action_script.last_triggered,
ATTR_MODE: self.action_script.script_mode,
ATTR_CUR: self.action_script.runs,
attrs: dict[str, Any] = {
AutomationEntityStateAttribute.LAST_TRIGGERED: (
self.action_script.last_triggered
),
AutomationEntityStateAttribute.MODE: self.action_script.script_mode,
AutomationEntityStateAttribute.CUR: self.action_script.runs,
}
if self.action_script.supports_max:
attrs[ATTR_MAX] = self.action_script.max_runs
attrs[AutomationEntityStateAttribute.MAX] = self.action_script.max_runs
return attrs
@property
@@ -1,10 +1,27 @@
"""Constants for the automation integration."""
from enum import StrEnum
import logging
CONF_TRIGGER_VARIABLES = "trigger_variables"
DOMAIN = "automation"
class AutomationEntityCapabilityAttribute(StrEnum):
"""Capability attributes for automation entities."""
ID = "id"
class AutomationEntityStateAttribute(StrEnum):
"""State attributes for automation entities."""
LAST_TRIGGERED = "last_triggered"
MODE = "mode"
CUR = "current"
MAX = "max"
CONF_HIDE_ENTITY = "hide_entity"
CONF_CONDITION_TYPE = "condition_type"
@@ -16,9 +16,15 @@ rules:
status: exempt
comment: |
Integration does not provide custom services.
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: done
entity-unique-id: done
has-entity-name: done
@@ -38,7 +38,7 @@
"data_description": {
"host": "The URL of your Autoskope API endpoint. Only change this if you use a white-label portal."
},
"name": "Advanced settings"
"name": "Additional settings"
}
},
"title": "Connect to Autoskope"
@@ -1 +0,0 @@
"""The avion component."""
-123
View File
@@ -1,123 +0,0 @@
"""Support for Avion dimmers."""
import importlib
import time
from typing import Any, override
import voluptuous as vol
from homeassistant.components.light import (
ATTR_BRIGHTNESS,
PLATFORM_SCHEMA as LIGHT_PLATFORM_SCHEMA,
ColorMode,
LightEntity,
)
from homeassistant.const import (
CONF_API_KEY,
CONF_DEVICES,
CONF_ID,
CONF_NAME,
CONF_PASSWORD,
CONF_USERNAME,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
DEVICE_SCHEMA = vol.Schema(
{
vol.Required(CONF_API_KEY): cv.string,
vol.Optional(CONF_ID): cv.positive_int,
vol.Optional(CONF_NAME): cv.string,
}
)
PLATFORM_SCHEMA = LIGHT_PLATFORM_SCHEMA.extend(
{
vol.Optional(CONF_DEVICES, default={}): {cv.string: DEVICE_SCHEMA},
vol.Optional(CONF_USERNAME): cv.string,
vol.Optional(CONF_PASSWORD): cv.string,
}
)
def setup_platform(
hass: HomeAssistant,
config: ConfigType,
add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up an Avion switch."""
avion = importlib.import_module("avion")
lights = [
AvionLight(
avion.Avion(
mac=address,
passphrase=device_config[CONF_API_KEY],
name=device_config.get(CONF_NAME),
object_id=device_config.get(CONF_ID),
connect=False,
)
)
for address, device_config in config[CONF_DEVICES].items()
]
if CONF_USERNAME in config and CONF_PASSWORD in config:
lights.extend(
AvionLight(device)
for device in avion.get_devices(
config[CONF_USERNAME], config[CONF_PASSWORD]
)
)
add_entities(lights)
class AvionLight(LightEntity):
"""Representation of an Avion light."""
_attr_support_color_mode = ColorMode.BRIGHTNESS
_attr_support_color_modes = {ColorMode.BRIGHTNESS}
_attr_should_poll = False
_attr_assumed_state = True
_attr_is_on = True
def __init__(self, device):
"""Initialize the light."""
self._attr_name = device.name
self._attr_unique_id = device.mac
self._attr_brightness = 255
self._switch = device
def set_state(self, brightness):
"""Set the state of this lamp to the provided brightness."""
avion = importlib.import_module("avion")
# Bluetooth LE is unreliable, and the connection may drop at any
# time. Make an effort to re-establish the link.
initial = time.monotonic()
while True:
if time.monotonic() - initial >= 10:
return False
try:
self._switch.set_brightness(brightness)
break
except avion.AvionException:
self._switch.connect()
return True
@override
def turn_on(self, **kwargs: Any) -> None:
"""Turn the specified or all lights on."""
if (brightness := kwargs.get(ATTR_BRIGHTNESS)) is not None:
self._attr_brightness = brightness
self.set_state(self.brightness)
self._attr_is_on = True
@override
def turn_off(self, **kwargs: Any) -> None:
"""Turn the specified or all lights off."""
self.set_state(0)
self._attr_is_on = False
@@ -1,9 +0,0 @@
{
"domain": "avion",
"name": "Avi-on",
"codeowners": [],
"documentation": "https://www.home-assistant.io/integrations/avion",
"iot_class": "assumed_state",
"quality_scale": "legacy",
"requirements": ["avion==0.10"]
}
@@ -28,9 +28,15 @@ rules:
comment: |
Dependency is not built in the CI
docs-actions: todo
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: No explicit event subscription
@@ -12,9 +12,15 @@ rules:
docs-actions:
status: exempt
comment: This integration does not have any custom 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: Entities of this integration does not explicitly subscribe to events.
+24 -20
View File
@@ -27,6 +27,7 @@ from homeassistant.const import (
)
from homeassistant.core import callback
from homeassistant.helpers.device_registry import format_mac
from homeassistant.helpers.selector import TextSelector, TextSelectorConfig
from homeassistant.helpers.service_info.dhcp import DhcpServiceInfo
from homeassistant.helpers.service_info.ssdp import (
ATTR_UPNP_FRIENDLY_NAME,
@@ -98,8 +99,11 @@ class AxisFlowHandler(ConfigFlow, domain=DOMAIN):
errors["base"] = "cannot_connect"
else:
if (serial := self._get_serial_number(api)) is None:
return self.async_abort(reason="no_serial_number")
if not self.unique_id:
if (serial := self._get_formatted_serial(api)) is None:
return self.async_abort(reason="no_serial_number")
await self.async_set_unique_id(serial)
config = {
CONF_PROTOCOL: user_input[CONF_PROTOCOL],
CONF_HOST: user_input[CONF_HOST],
@@ -108,8 +112,6 @@ class AxisFlowHandler(ConfigFlow, domain=DOMAIN):
CONF_PASSWORD: user_input[CONF_PASSWORD],
}
await self.async_set_unique_id(format_mac(serial))
if self.source == SOURCE_REAUTH:
self._abort_if_unique_id_mismatch()
return self.async_update_and_abort(
@@ -124,7 +126,7 @@ class AxisFlowHandler(ConfigFlow, domain=DOMAIN):
self.config = config | {CONF_MODEL: api.vapix.product_number}
return await self._create_entry(serial)
return await self._create_entry()
data = self.discovery_schema or {
vol.Required(CONF_PROTOCOL): vol.In(PROTOCOL_CHOICES),
@@ -141,7 +143,7 @@ class AxisFlowHandler(ConfigFlow, domain=DOMAIN):
errors=errors,
)
async def _create_entry(self, serial: str) -> ConfigFlowResult:
async def _create_entry(self) -> ConfigFlowResult:
"""Create entry for device.
Use the discovered device name when available.
@@ -149,7 +151,7 @@ class AxisFlowHandler(ConfigFlow, domain=DOMAIN):
if (title_placeholders := self.context.get("title_placeholders")) is not None:
name = title_placeholders[CONF_NAME]
else:
name = f"{self.config[CONF_MODEL]} - {serial}"
name = f"{self.config[CONF_MODEL]} - {self.unique_id}"
self.config[CONF_NAME] = name
return self.async_create_entry(title=name, data=self.config)
@@ -196,7 +198,7 @@ class AxisFlowHandler(ConfigFlow, domain=DOMAIN):
return await self._process_discovered_device(
{
CONF_HOST: discovery_info.ip,
CONF_MAC: format_mac(discovery_info.macaddress),
CONF_MAC: discovery_info.macaddress,
CONF_NAME: discovery_info.hostname,
CONF_PORT: 80,
}
@@ -211,7 +213,7 @@ class AxisFlowHandler(ConfigFlow, domain=DOMAIN):
return await self._process_discovered_device(
{
CONF_HOST: url.hostname,
CONF_MAC: format_mac(discovery_info.upnp[ATTR_UPNP_SERIAL]),
CONF_MAC: discovery_info.upnp[ATTR_UPNP_SERIAL],
CONF_NAME: f"{discovery_info.upnp[ATTR_UPNP_FRIENDLY_NAME]}",
CONF_PORT: url.port,
}
@@ -225,7 +227,7 @@ class AxisFlowHandler(ConfigFlow, domain=DOMAIN):
return await self._process_discovered_device(
{
CONF_HOST: discovery_info.host,
CONF_MAC: format_mac(discovery_info.properties["macaddress"]),
CONF_MAC: discovery_info.properties["macaddress"],
CONF_NAME: discovery_info.name.split(".", 1)[0],
CONF_PORT: discovery_info.port,
}
@@ -235,17 +237,17 @@ class AxisFlowHandler(ConfigFlow, domain=DOMAIN):
self, discovery_info: dict[str, Any]
) -> ConfigFlowResult:
"""Prepare configuration for a discovered Axis device."""
if discovery_info[CONF_MAC][:8] not in AXIS_OUI:
serial = format_mac(discovery_info[CONF_MAC])
if serial[:8] not in AXIS_OUI:
return self.async_abort(reason="not_axis_device")
if is_link_local(ip_address(discovery_info[CONF_HOST])):
return self.async_abort(reason="link_local_address")
await self.async_set_unique_id(discovery_info[CONF_MAC])
self._abort_if_unique_id_configured(
updates={CONF_HOST: discovery_info[CONF_HOST]}, reload_on_update=False
)
if await self.async_set_unique_id(serial):
self._abort_if_unique_id_configured(
updates={CONF_HOST: discovery_info[CONF_HOST]}, reload_on_update=False
)
self.context.update(
{
@@ -259,7 +261,9 @@ class AxisFlowHandler(ConfigFlow, domain=DOMAIN):
self.discovery_schema = {
vol.Required(CONF_PROTOCOL): vol.In(PROTOCOL_CHOICES),
vol.Required(CONF_HOST, default=discovery_info[CONF_HOST]): str,
vol.Required(CONF_HOST, default=discovery_info[CONF_HOST]): TextSelector(
TextSelectorConfig(read_only=True)
),
vol.Required(CONF_USERNAME): str,
vol.Required(CONF_PASSWORD): str,
vol.Required(CONF_PORT, default=DEFAULT_PORT): int,
@@ -268,16 +272,16 @@ class AxisFlowHandler(ConfigFlow, domain=DOMAIN):
return await self.async_step_user()
@staticmethod
def _get_serial_number(api: axis.AxisDevice) -> str | None:
def _get_formatted_serial(api: axis.AxisDevice) -> str | None:
"""Retrieve the device serial number from the Axis API.
Tries basic_device_info first, then property_handler. Returns None if not found.
"""
vapix = api.vapix
if vapix.basic_device_info.initialized:
return vapix.basic_device_info["0"].serial_number
return format_mac(vapix.basic_device_info["0"].serial_number)
if vapix.params.property_handler.initialized:
return vapix.params.property_handler["0"].system_serial_number
return format_mac(vapix.params.property_handler["0"].system_serial_number)
return None
@@ -16,9 +16,15 @@ rules:
status: exempt
comment: |
This integration does not have any custom 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: |
@@ -14,9 +14,15 @@ rules:
docs-actions:
status: exempt
comment: This integration does not have any custom 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: Entities of this integration do not explicitly subscribe to events.
@@ -1 +0,0 @@
"""The beewi_smartclim component."""
@@ -1,10 +0,0 @@
{
"domain": "beewi_smartclim",
"name": "BeeWi SmartClim BLE sensor",
"codeowners": ["@alemuro"],
"documentation": "https://www.home-assistant.io/integrations/beewi_smartclim",
"iot_class": "local_polling",
"loggers": ["beewi_smartclim"],
"quality_scale": "legacy",
"requirements": ["beewi-smartclim==0.0.10"]
}
@@ -1,84 +0,0 @@
"""Platform for beewi_smartclim integration."""
from beewi_smartclim import BeewiSmartClimPoller
import voluptuous as vol
from homeassistant.components.sensor import (
PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
SensorDeviceClass,
SensorEntity,
)
from homeassistant.const import CONF_MAC, CONF_NAME, PERCENTAGE, UnitOfTemperature
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
# Default values
DEFAULT_NAME = "BeeWi SmartClim"
# Sensor config
SENSOR_TYPES = [
[SensorDeviceClass.TEMPERATURE, "Temperature", UnitOfTemperature.CELSIUS],
[SensorDeviceClass.HUMIDITY, "Humidity", PERCENTAGE],
[SensorDeviceClass.BATTERY, "Battery", PERCENTAGE],
]
PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
{
vol.Required(CONF_MAC): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
}
)
def setup_platform(
hass: HomeAssistant,
config: ConfigType,
add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the beewi_smartclim platform."""
mac = config[CONF_MAC]
prefix = config[CONF_NAME]
poller = BeewiSmartClimPoller(mac)
sensors = []
for sensor_type in SENSOR_TYPES:
device = sensor_type[0]
name = sensor_type[1]
unit = sensor_type[2]
# `prefix` is the name configured by the user for the sensor, we're appending
# the device type at the end of the name (garden -> garden temperature)
if prefix:
name = f"{prefix} {name}"
sensors.append(BeewiSmartclimSensor(poller, name, mac, device, unit))
add_entities(sensors)
class BeewiSmartclimSensor(SensorEntity):
"""Representation of a Sensor."""
def __init__(self, poller, name, mac, device, unit):
"""Initialize the sensor."""
self._poller = poller
self._attr_name = name
self._device = device
self._attr_native_unit_of_measurement = unit
self._attr_device_class = self._device
self._attr_unique_id = f"{mac}_{device}"
def update(self) -> None:
"""Fetch new state data from the poller."""
self._poller.update_sensor()
self._attr_native_value = None
if self._device == SensorDeviceClass.TEMPERATURE:
self._attr_native_value = self._poller.get_temperature()
if self._device == SensorDeviceClass.HUMIDITY:
self._attr_native_value = self._poller.get_humidity()
if self._device == SensorDeviceClass.BATTERY:
self._attr_native_value = self._poller.get_battery()
+7 -8
View File
@@ -15,16 +15,15 @@ from homeassistant.components.sensor import (
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_MILLION,
LIGHT_LUX,
PERCENTAGE,
UnitOfApparentPower,
UnitOfDensity,
UnitOfElectricCurrent,
UnitOfElectricPotential,
UnitOfEnergy,
UnitOfFrequency,
UnitOfPower,
UnitOfRatio,
UnitOfReactiveEnergy,
UnitOfReactivePower,
UnitOfSpeed,
@@ -53,19 +52,19 @@ SENSOR_TYPES: tuple[BleBoxSensorEntityDescription, ...] = (
BleBoxSensorEntityDescription(
key="pm1",
device_class=SensorDeviceClass.PM1,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
BleBoxSensorEntityDescription(
key="pm2_5",
device_class=SensorDeviceClass.PM25,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
BleBoxSensorEntityDescription(
key="pm10",
device_class=SensorDeviceClass.PM10,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
BleBoxSensorEntityDescription(
@@ -84,7 +83,7 @@ SENSOR_TYPES: tuple[BleBoxSensorEntityDescription, ...] = (
BleBoxSensorEntityDescription(
key="humidity",
device_class=SensorDeviceClass.HUMIDITY,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
BleBoxSensorEntityDescription(
@@ -179,7 +178,7 @@ SENSOR_TYPES: tuple[BleBoxSensorEntityDescription, ...] = (
BleBoxSensorEntityDescription(
key="co2",
device_class=SensorDeviceClass.CO2,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
state_class=SensorStateClass.MEASUREMENT,
),
BleBoxSensorEntityDescription(
@@ -17,10 +17,10 @@
"requirements": [
"bleak==3.0.2",
"bleak-retry-connector==4.6.1",
"bluetooth-adapters==2.3.0",
"bluetooth-adapters==2.4.0",
"bluetooth-auto-recovery==1.6.4",
"bluetooth-data-tools==1.29.18",
"dbus-fast==5.0.16",
"habluetooth==6.8.3"
"dbus-fast==5.0.22",
"habluetooth==6.25.1"
]
}
@@ -14,9 +14,15 @@ rules:
config-flow: done
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: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup: done
entity-unique-id: done
has-entity-name: done
@@ -2,13 +2,13 @@
"domain": "bosch_shc",
"name": "Bosch SHC",
"after_dependencies": ["zeroconf"],
"codeowners": ["@tschamm"],
"codeowners": ["@tschamm", "@mosandlt"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/bosch_shc",
"integration_type": "hub",
"iot_class": "local_push",
"loggers": ["boschshcpy"],
"requirements": ["boschshcpy==0.2.111"],
"requirements": ["boschshcpy==0.3.5"],
"zeroconf": [
{
"name": "bosch shc*",
@@ -10,9 +10,15 @@ rules:
config-flow: done
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: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup:
status: exempt
comment: The integration registers no events
@@ -12,9 +12,15 @@ rules:
docs-actions:
status: exempt
comment: The integration does not register services.
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: done
entity-unique-id: done
has-entity-name: done
@@ -8,9 +8,15 @@ rules:
config-flow: done
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: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup:
status: exempt
comment: |
+14 -7
View File
@@ -68,6 +68,7 @@ from .const import (
EVENT_UID,
LIST_EVENT_FIELDS,
CalendarEntityFeature,
CalendarEntityStateAttribute,
)
# mypy: disallow-any-generics
@@ -519,7 +520,9 @@ class CalendarEntity(Entity):
entity_description: CalendarEntityDescription
_entity_component_unrecorded_attributes = frozenset({"description"})
_entity_component_unrecorded_attributes = frozenset(
{CalendarEntityStateAttribute.DESCRIPTION}
)
_alarm_unsubs: list[CALLBACK_TYPE] | None = None
_event_listeners: (
@@ -573,12 +576,16 @@ class CalendarEntity(Entity):
return None
return {
"message": event.summary,
"all_day": event.all_day,
"start_time": event.start_datetime_local.strftime(DATE_STR_FORMAT),
"end_time": event.end_datetime_local.strftime(DATE_STR_FORMAT),
"location": event.location or "",
"description": event.description or "",
CalendarEntityStateAttribute.MESSAGE: event.summary,
CalendarEntityStateAttribute.ALL_DAY: event.all_day,
CalendarEntityStateAttribute.START_TIME: event.start_datetime_local.strftime(
DATE_STR_FORMAT
),
CalendarEntityStateAttribute.END_TIME: event.end_datetime_local.strftime(
DATE_STR_FORMAT
),
CalendarEntityStateAttribute.LOCATION: event.location or "",
CalendarEntityStateAttribute.DESCRIPTION: event.description or "",
}
@final
+12 -1
View File
@@ -1,6 +1,6 @@
"""Constants for calendar components."""
from enum import IntFlag
from enum import IntFlag, StrEnum
from typing import TYPE_CHECKING
from homeassistant.util.hass_dict import HassKey
@@ -14,6 +14,17 @@ DOMAIN = "calendar"
DATA_COMPONENT: HassKey[EntityComponent[CalendarEntity]] = HassKey(DOMAIN)
class CalendarEntityStateAttribute(StrEnum):
"""State attributes for calendar entities."""
MESSAGE = "message"
ALL_DAY = "all_day"
START_TIME = "start_time"
END_TIME = "end_time"
LOCATION = "location"
DESCRIPTION = "description"
class CalendarEntityFeature(IntFlag):
"""Supported features of the calendar entity."""
@@ -14,9 +14,15 @@ rules:
config-flow: done
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: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup: done
entity-unique-id: done
has-entity-name: done
+11 -6
View File
@@ -68,6 +68,7 @@ from .const import (
PREF_ORIENTATION,
PREF_PRELOAD_STREAM,
SERVICE_RECORD,
CameraEntityStateAttribute,
CameraState,
StreamType,
)
@@ -421,7 +422,7 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
"""The base class for camera entities."""
_entity_component_unrecorded_attributes = frozenset(
{"access_token", "entity_picture"}
{CameraEntityStateAttribute.ACCESS_TOKEN, "entity_picture"}
)
# Entity Properties
@@ -649,18 +650,22 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
@final
@property
@override
def state_attributes(self) -> dict[str, str | None]:
def state_attributes(self) -> dict[str, str | bool | None]:
"""Return the camera state attributes."""
attrs = {"access_token": self.access_tokens[-1]}
attrs: dict[str, str | bool | None] = {
CameraEntityStateAttribute.ACCESS_TOKEN: self.access_tokens[-1]
}
if model := self.model:
attrs["model_name"] = model
attrs[CameraEntityStateAttribute.MODEL_NAME] = model
if brand := self.brand:
attrs["brand"] = brand
attrs[CameraEntityStateAttribute.BRAND] = brand
if motion_detection_enabled := self.motion_detection_enabled:
attrs["motion_detection"] = motion_detection_enabled
attrs[CameraEntityStateAttribute.MOTION_DETECTION] = (
motion_detection_enabled
)
return attrs
+9
View File
@@ -28,6 +28,15 @@ CAMERA_STREAM_SOURCE_TIMEOUT: Final = 10
CAMERA_IMAGE_TIMEOUT: Final = 10
class CameraEntityStateAttribute(StrEnum):
"""State attributes for camera entities."""
ACCESS_TOKEN = "access_token"
MODEL_NAME = "model_name"
BRAND = "brand"
MOTION_DETECTION = "motion_detection"
class CameraState(StrEnum):
"""Camera entity states."""
@@ -12,9 +12,15 @@ rules:
docs-actions:
status: exempt
comment: No custom actions/services.
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: done
entity-unique-id: done
has-entity-name: done
@@ -12,9 +12,15 @@ rules:
docs-actions:
status: exempt
comment: This integration does not provide 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: done
entity-unique-id: done
has-entity-name: done
@@ -18,9 +18,15 @@ rules:
docs-actions:
status: exempt
comment: Integration does not register custom 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: Integration does not subscribe to events.
@@ -15,9 +15,15 @@ rules:
docs-actions:
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: done
comment: |
@@ -12,9 +12,15 @@ rules:
docs-actions:
status: exempt
comment: There are no custom 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: Entities do not explicitly subscribe to events
@@ -8,9 +8,15 @@ rules:
config-flow: done
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: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup: done
entity-unique-id: done
has-entity-name: done
@@ -14,9 +14,15 @@ rules:
docs-actions:
status: exempt
comment: This integration does not have any custom 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: Entities of this integration do not explicitly subscribe to events.
@@ -25,9 +25,15 @@ rules:
status: exempt
comment: |
The integration does not provide any 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: |
@@ -12,9 +12,15 @@ rules:
docs-actions:
status: exempt
comment: no 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: no events
@@ -17,9 +17,15 @@ rules:
status: exempt
comment: |
This integration does not provide additional 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: |
@@ -12,11 +12,17 @@ rules:
docs-actions:
status: exempt
comment: No service actions implemented
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions:
status: exempt
comment: No special external action required
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup:
status: exempt
comment: No callbacks are implemented
+4 -3
View File
@@ -41,6 +41,7 @@ from .const import (
INTENT_OPEN_COVER,
CoverDeviceClass,
CoverEntityFeature,
CoverEntityStateAttribute,
CoverState,
)
from .trigger import make_cover_closed_trigger, make_cover_opened_trigger
@@ -260,13 +261,13 @@ class CoverEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
"""Return the state attributes."""
data: dict[str, Any] = {}
data[ATTR_IS_CLOSED] = self.is_closed
data[CoverEntityStateAttribute.IS_CLOSED] = self.is_closed
if (current := self.current_cover_position) is not None:
data[ATTR_CURRENT_POSITION] = current
data[CoverEntityStateAttribute.CURRENT_POSITION] = current
if (current_tilt := self.current_cover_tilt_position) is not None:
data[ATTR_CURRENT_TILT_POSITION] = current_tilt
data[CoverEntityStateAttribute.CURRENT_TILT_POSITION] = current_tilt
return data
+9
View File
@@ -10,6 +10,15 @@ ATTR_IS_CLOSED = "is_closed"
ATTR_POSITION = "position"
ATTR_TILT_POSITION = "tilt_position"
class CoverEntityStateAttribute(StrEnum):
"""State attributes for cover entities."""
IS_CLOSED = "is_closed"
CURRENT_POSITION = "current_position"
CURRENT_TILT_POSITION = "current_tilt_position"
INTENT_OPEN_COVER = "HassOpenCover"
INTENT_CLOSE_COVER = "HassCloseCover"
@@ -14,9 +14,15 @@ rules:
status: exempt
comment: |
This integration does not provide additional 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: done
entity-unique-id: done
has-entity-name: done
@@ -12,9 +12,15 @@ rules:
docs-actions:
status: exempt
comment: This integration does not register custom 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: Entities use the coordinator pattern and do not subscribe to events.
@@ -8,9 +8,15 @@ rules:
config-flow: done
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: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup: done
entity-unique-id: done
has-entity-name: done
@@ -37,6 +37,35 @@ class TrackingType(StrEnum):
POSITION = "position"
class DeviceTrackerEntityCapabilityAttribute(StrEnum):
"""Capability attributes for device tracker entities."""
TRACKING_TYPE = "tracking_type"
class DeviceTrackerEntityStateAttribute(StrEnum):
"""State attributes common to device tracker entities."""
SOURCE_TYPE = "source_type"
IN_ZONES = "in_zones"
class TrackerEntityStateAttribute(StrEnum):
"""State attributes set by TrackerEntity."""
LATITUDE = "latitude"
LONGITUDE = "longitude"
GPS_ACCURACY = "gps_accuracy"
class ScannerEntityStateAttribute(StrEnum):
"""State attributes set by ScannerEntity."""
IP = "ip"
MAC = "mac"
HOST_NAME = "host_name"
CONF_SCAN_INTERVAL: Final = "interval_seconds"
SCAN_INTERVAL: Final = timedelta(seconds=12)
@@ -8,7 +8,7 @@ from propcache.api import cached_property
from homeassistant.components import zone
from homeassistant.components.zone import ATTR_PASSIVE, ATTR_RADIUS
from homeassistant.const import (
from homeassistant.const import ( # noqa: F401
ATTR_BATTERY_LEVEL,
ATTR_GPS_ACCURACY,
ATTR_LATITUDE,
@@ -42,7 +42,7 @@ from homeassistant.helpers.event import async_track_state_change_event
from homeassistant.loader import async_suggest_report_issue
from homeassistant.util.hass_dict import HassKey
from .const import (
from .const import ( # noqa: F401
ATTR_HOST_NAME,
ATTR_IN_ZONES,
ATTR_IP,
@@ -53,7 +53,11 @@ from .const import (
CONNECTED_DEVICE_REGISTERED,
DOMAIN,
LOGGER,
DeviceTrackerEntityCapabilityAttribute,
DeviceTrackerEntityStateAttribute,
ScannerEntityStateAttribute,
SourceType,
TrackerEntityStateAttribute,
TrackingType,
)
@@ -215,7 +219,9 @@ class BaseTrackerEntity(Entity):
@override
def state_attributes(self) -> dict[str, Any]:
"""Return the device state attributes."""
attr: dict[str, Any] = {ATTR_SOURCE_TYPE: self.source_type}
attr: dict[str, Any] = {
DeviceTrackerEntityStateAttribute.SOURCE_TYPE: self.source_type
}
if self.battery_level is not None:
attr[ATTR_BATTERY_LEVEL] = self.battery_level
@@ -243,7 +249,7 @@ class TrackerEntity(
entity_description: TrackerEntityDescription
_attr_capability_attributes: dict[str, Any] = {
ATTR_TRACKING_TYPE: TrackingType.POSITION
DeviceTrackerEntityCapabilityAttribute.TRACKING_TYPE: TrackingType.POSITION
}
_attr_in_zones: list[str] | None = None
_attr_latitude: float | None = None
@@ -406,13 +412,15 @@ class TrackerEntity(
@override
def state_attributes(self) -> dict[str, Any]:
"""Return the device state attributes."""
attr: dict[str, Any] = {ATTR_IN_ZONES: self.__in_zones or []}
attr: dict[str, Any] = {
DeviceTrackerEntityStateAttribute.IN_ZONES: self.__in_zones or []
}
attr.update(super().state_attributes)
if self.latitude is not None and self.longitude is not None:
attr[ATTR_LATITUDE] = self.latitude
attr[ATTR_LONGITUDE] = self.longitude
attr[ATTR_GPS_ACCURACY] = self.location_accuracy
attr[TrackerEntityStateAttribute.LATITUDE] = self.latitude
attr[TrackerEntityStateAttribute.LONGITUDE] = self.longitude
attr[TrackerEntityStateAttribute.GPS_ACCURACY] = self.location_accuracy
return attr
@@ -425,7 +433,7 @@ class BaseScannerEntity(BaseTrackerEntity):
"""
_attr_capability_attributes: dict[str, Any] = {
ATTR_TRACKING_TYPE: TrackingType.CONNECTION
DeviceTrackerEntityCapabilityAttribute.TRACKING_TYPE: TrackingType.CONNECTION
}
_scanner_option_associated_zone: str = zone.ENTITY_ID_HOME
_scanner_option_associated_zone_unsub: CALLBACK_TYPE | None = None
@@ -556,7 +564,7 @@ class BaseScannerEntity(BaseTrackerEntity):
@override
def state_attributes(self) -> dict[str, Any]:
"""Return the device state attributes."""
attr: dict[str, Any] = {ATTR_IN_ZONES: []}
attr: dict[str, Any] = {DeviceTrackerEntityStateAttribute.IN_ZONES: []}
attr.update(super().state_attributes)
if not self.is_connected:
@@ -571,7 +579,7 @@ class BaseScannerEntity(BaseTrackerEntity):
):
return attr
attr[ATTR_IN_ZONES] = [
attr[DeviceTrackerEntityStateAttribute.IN_ZONES] = [
associated_zone,
*zone.async_get_enclosing_zones(self.hass, associated_zone),
]
@@ -721,10 +729,10 @@ class ScannerEntity(
attr = super().state_attributes
if ip_address := self.ip_address:
attr[ATTR_IP] = ip_address
attr[ScannerEntityStateAttribute.IP] = ip_address
if (mac_address := self.mac_address) is not None:
attr[ATTR_MAC] = mac_address
attr[ScannerEntityStateAttribute.MAC] = mac_address
if (hostname := self.hostname) is not None:
attr[ATTR_HOST_NAME] = hostname
attr[ScannerEntityStateAttribute.HOST_NAME] = hostname
return attr
@@ -17,9 +17,15 @@ rules:
status: exempt
comment: |
This integration does not provide additional 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: done
entity-unique-id: done
has-entity-name: done
@@ -14,9 +14,15 @@ rules:
status: exempt
comment: |
This integration does not provide additional 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: |
@@ -14,9 +14,15 @@ rules:
status: exempt
comment: |
The integration does not provide any additional 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: |
+1 -1
View File
@@ -29,7 +29,7 @@
"resolver_ipv6": "Resolver used for the IPv6 lookup."
},
"description": "Optionally change resolvers and ports.",
"name": "Advanced options"
"name": "Additional options"
}
}
}
+14 -1
View File
@@ -17,7 +17,7 @@ from homeassistant.helpers.config_entry_oauth2_flow import (
)
from .auth import DropboxConfigEntryAuth
from .const import DATA_BACKUP_AGENT_LISTENERS, DOMAIN
from .const import DATA_BACKUP_AGENT_LISTENERS, DOMAIN, OAUTH2_SCOPES
type DropboxConfigEntry = ConfigEntry[DropboxAPIClient]
@@ -31,6 +31,19 @@ async def async_setup_entry(hass: HomeAssistant, entry: DropboxConfigEntry) -> b
translation_domain=DOMAIN,
translation_key="oauth2_implementation_unavailable",
) from err
token = entry.data["token"]
if not set(token.get("scope", "").split()).issuperset(OAUTH2_SCOPES):
raise ConfigEntryAuthFailed(
translation_domain=DOMAIN,
translation_key="missing_scopes",
)
if "refresh_token" not in token:
raise ConfigEntryAuthFailed(
translation_domain=DOMAIN,
translation_key="missing_refresh_token",
)
oauth2_session = OAuth2Session(hass, entry, oauth2_implementation)
auth = DropboxConfigEntryAuth(
@@ -1,7 +1,5 @@
"""Application credentials platform for the Dropbox integration."""
from typing import override
from homeassistant.components.application_credentials import ClientCredential
from homeassistant.core import HomeAssistant
from homeassistant.helpers.config_entry_oauth2_flow import (
@@ -9,14 +7,14 @@ from homeassistant.helpers.config_entry_oauth2_flow import (
LocalOAuth2ImplementationWithPkce,
)
from .const import OAUTH2_AUTHORIZE, OAUTH2_SCOPES, OAUTH2_TOKEN
from .const import OAUTH2_AUTHORIZE, OAUTH2_TOKEN
async def async_get_auth_implementation(
hass: HomeAssistant, auth_domain: str, credential: ClientCredential
) -> AbstractOAuth2Implementation:
"""Return custom auth implementation."""
return DropboxOAuth2Implementation(
"""Return auth implementation."""
return LocalOAuth2ImplementationWithPkce(
hass,
auth_domain,
credential.client_id,
@@ -24,21 +22,3 @@ async def async_get_auth_implementation(
OAUTH2_TOKEN,
credential.client_secret,
)
class DropboxOAuth2Implementation(LocalOAuth2ImplementationWithPkce):
"""Custom Dropbox OAuth2 implementation.
Adds the necessary authorize url parameters.
"""
@property
@override
def extra_authorize_data(self) -> dict:
"""Extra data that needs to be appended to the authorize url."""
data: dict = {
"token_access_type": "offline",
"scope": " ".join(OAUTH2_SCOPES),
}
data.update(super().extra_authorize_data)
return data
@@ -12,7 +12,7 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.config_entry_oauth2_flow import AbstractOAuth2FlowHandler
from .auth import DropboxConfigFlowAuth
from .const import DOMAIN
from .const import DOMAIN, OAUTH2_SCOPES
class DropboxConfigFlow(AbstractOAuth2FlowHandler, domain=DOMAIN):
@@ -26,6 +26,15 @@ class DropboxConfigFlow(AbstractOAuth2FlowHandler, domain=DOMAIN):
"""Return logger."""
return logging.getLogger(__name__)
@property
@override
def extra_authorize_data(self) -> dict:
"""Extra data that needs to be appended to the authorize url."""
return {
"token_access_type": "offline",
"scope": " ".join(OAUTH2_SCOPES),
}
@override
async def async_oauth_create_entry(self, data: dict[str, Any]) -> ConfigFlowResult:
"""Create an entry for the flow, or update existing entry."""
@@ -51,6 +60,9 @@ class DropboxConfigFlow(AbstractOAuth2FlowHandler, domain=DOMAIN):
self, entry_data: Mapping[str, Any]
) -> ConfigFlowResult:
"""Perform reauth upon an API authentication error."""
token = entry_data[CONF_TOKEN]
if not set(token.get("scope", "").split()).issuperset(OAUTH2_SCOPES):
return await self.async_step_reauth_permissions()
return await self.async_step_reauth_confirm()
async def async_step_reauth_confirm(
@@ -60,3 +72,11 @@ class DropboxConfigFlow(AbstractOAuth2FlowHandler, domain=DOMAIN):
if user_input is None:
return self.async_show_form(step_id="reauth_confirm")
return await self.async_step_user()
async def async_step_reauth_permissions(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Dialog that informs the user that additional permissions are required."""
if user_input is None:
return self.async_show_form(step_id="reauth_permissions")
return await self.async_step_user()
@@ -12,6 +12,7 @@ OAUTH2_SCOPES = [
"account_info.read",
"files.content.read",
"files.content.write",
"files.metadata.read",
]
DATA_BACKUP_AGENT_LISTENERS: HassKey[list[Callable[[], None]]] = HassKey(
@@ -8,6 +8,6 @@
"documentation": "https://www.home-assistant.io/integrations/dropbox",
"integration_type": "service",
"iot_class": "cloud_polling",
"quality_scale": "bronze",
"quality_scale": "silver",
"requirements": ["python-dropbox-api==0.1.4"]
}
@@ -16,9 +16,15 @@ rules:
docs-actions:
status: exempt
comment: Integration does not register any 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: Integration does not have any entities.
@@ -46,7 +52,9 @@ rules:
status: exempt
comment: Integration does not have any entities.
integration-owner: done
log-when-unavailable: todo
log-when-unavailable:
status: exempt
comment: Integration does not have any entities.
parallel-updates:
status: exempt
comment: Integration does not make any entity updates.
@@ -24,10 +24,20 @@
"reauth_confirm": {
"description": "The Dropbox integration needs to re-authenticate your account.",
"title": "[%key:common::config_flow::title::reauth%]"
},
"reauth_permissions": {
"description": "The Dropbox integration requires additional permissions to function correctly.",
"title": "[%key:common::config_flow::title::reauth%]"
}
}
},
"exceptions": {
"missing_refresh_token": {
"message": "[%key:component::dropbox::config::step::reauth_confirm::description%]"
},
"missing_scopes": {
"message": "[%key:component::dropbox::config::step::reauth_permissions::description%]"
},
"oauth2_implementation_unavailable": {
"message": "[%key:common::exceptions::oauth2_implementation_unavailable::message%]"
}
@@ -17,9 +17,15 @@ 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: done
entity-unique-id: done
has-entity-name: done
@@ -10,9 +10,15 @@ rules:
status: exempt
comment: no external dependencies
docs-actions: done
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: integration has no entities

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