Compare commits

..

2 Commits

Author SHA1 Message Date
Franck Nijhof df22f6a5bd Use base class config_entry instead of private attribute 2026-06-25 06:28:00 +00:00
Franck Nijhof 00f43f3bda Avoid blocking call in Anthropic client construction 2026-06-24 21:03:06 +00:00
154 changed files with 1803 additions and 2741 deletions
@@ -12,7 +12,6 @@ on:
types: [opened, synchronize, reopened]
paths:
- "requirements*.txt"
- "**/requirements*.txt"
- "homeassistant/package_constraints.txt"
workflow_dispatch:
inputs:
@@ -59,7 +58,6 @@ jobs:
echo "head_sha=${HEAD_SHA}" >> "${GITHUB_OUTPUT}"
- name: Run deterministic checks
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ inputs.pull_request_number || github.event.pull_request.number }}
HEAD_SHA: ${{ steps.pr.outputs.head_sha }}
run: |
+125 -54
View File
@@ -1,5 +1,5 @@
# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"36a7fc263a2ce868d74a266f23eb7772d82fd397806464384fe087479ddd4a70","body_hash":"bba8c011f2b82bb4d9847a359f43f0e7d91245b280678c20e5112b3c9e77d5cd","compiler_version":"v0.79.6","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.60"}}
# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"df4cb1c069e1874edd31b4311f1884172cec0e10","version":"v6.0.3"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"5c2fe865bb4dc46e1450f6ee0d0541d759aea73a","version":"v0.79.6"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.2","digest":"sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2","digest":"sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2@sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2","digest":"sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2@sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.25","digest":"sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.25@sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa"},{"image":"ghcr.io/github/github-mcp-server:v1.1.2","digest":"sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c","pinned_image":"ghcr.io/github/github-mcp-server:v1.1.2@sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c"}]}
# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"7b142e96e0f8b454cdcc9c0c25070cf9a52c44d83a6b1fbc3ad6725b6567337c","body_hash":"3894ded07d5934ac5f29d160ffb1f9115cf72b6da8a7e453a4d4f69e8641a48e","compiler_version":"v0.79.6","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.60"}}
# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"df4cb1c069e1874edd31b4311f1884172cec0e10","version":"v6.0.3"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"v0.79.6","version":"v0.79.6"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.2","digest":"sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2","digest":"sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2@sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2","digest":"sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2@sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.25","digest":"sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.25@sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa"},{"image":"ghcr.io/github/github-mcp-server:v1.1.2","digest":"sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c","pinned_image":"ghcr.io/github/github-mcp-server:v1.1.2@sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c"}]}
# ___ _ _
# / _ \ | | (_)
# | |_| | __ _ ___ _ __ | |_ _ ___
@@ -36,7 +36,7 @@
# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
# - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
# - github/gh-aw-actions/setup@5c2fe865bb4dc46e1450f6ee0d0541d759aea73a # v0.79.6
# - github/gh-aw-actions/setup@v0.79.6
#
# Container images used:
# - ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6
@@ -92,7 +92,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@5c2fe865bb4dc46e1450f6ee0d0541d759aea73a # v0.79.6
uses: github/gh-aw-actions/setup@v0.79.6
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@@ -344,8 +344,9 @@ jobs:
agent:
needs:
- activation
- prepare
if: (needs.prepare.outputs.skip != 'true') && (needs.activation.outputs.daily_effective_workflow_exceeded != 'true')
- extract_pr_number
- gate
if: needs.activation.outputs.daily_effective_workflow_exceeded != 'true'
runs-on: ubuntu-latest
permissions:
actions: read
@@ -382,7 +383,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@5c2fe865bb4dc46e1450f6ee0d0541d759aea73a # v0.79.6
uses: github/gh-aw-actions/setup@v0.79.6
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@@ -488,15 +489,15 @@ jobs:
mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs"
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_823c5547a5e52957_EOF'
{"add_comment":{"max":1,"target":"${{ needs.prepare.outputs.pr_number }}"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{}}
GH_AW_SAFE_OUTPUTS_CONFIG_823c5547a5e52957_EOF
cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_f496a449c5dccca1_EOF'
{"add_comment":{"max":1,"target":"${{ needs.extract_pr_number.outputs.pr_number }}"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{}}
GH_AW_SAFE_OUTPUTS_CONFIG_f496a449c5dccca1_EOF
- name: Generate Safe Outputs Tools
env:
GH_AW_TOOLS_META_JSON: |
{
"description_suffixes": {
"add_comment": " CONSTRAINTS: Maximum 1 comment(s) can be added. Target: ${{ needs.prepare.outputs.pr_number }}. Supports reply_to_id for discussion threading."
"add_comment": " CONSTRAINTS: Maximum 1 comment(s) can be added. Target: ${{ needs.extract_pr_number.outputs.pr_number }}. Supports reply_to_id for discussion threading."
},
"repo_params": {},
"dynamic_tools": []
@@ -993,7 +994,8 @@ jobs:
- activation
- agent
- detection
- prepare
- extract_pr_number
- gate
- safe_outputs
if: >
always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true' ||
@@ -1016,7 +1018,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@5c2fe865bb4dc46e1450f6ee0d0541d759aea73a # v0.79.6
uses: github/gh-aw-actions/setup@v0.79.6
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@@ -1206,7 +1208,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@5c2fe865bb4dc46e1450f6ee0d0541d759aea73a # v0.79.6
uses: github/gh-aw-actions/setup@v0.79.6
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@@ -1427,6 +1429,111 @@ jobs:
}
}
extract_pr_number:
needs: gate
if: needs.gate.outputs.skip != 'true' && github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
permissions:
actions: read
outputs:
pr_number: ${{ steps.extract.outputs.pr_number }}
steps:
- name: Configure GH_HOST for enterprise compatibility
id: ghes-host-config
shell: bash
# zizmor: ignore[github-env] - GITHUB_SERVER_URL is set by GitHub Actions, not user input.
run: |
# Derive GH_HOST from GITHUB_SERVER_URL so the gh CLI targets the correct
# GitHub instance (GHES/GHEC). On github.com this is a harmless no-op.
GH_HOST="${GITHUB_SERVER_URL#https://}"
GH_HOST="${GH_HOST#http://}"
echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV"
- name: Download deterministic-results artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
name: check-requirements-deterministic
path: /tmp/deterministic
run-id: ${{ github.event.workflow_run.id }}
- name: Extract PR number from artifact
id: extract
run: |
PR=$(jq -r '.pr_number' /tmp/deterministic/results.json)
echo "pr_number=${PR}" >> "${GITHUB_OUTPUT}"
gate:
needs: activation
if: github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
pull-requests: read
outputs:
skip: ${{ steps.gate.outputs.skip }}
steps:
- name: Configure GH_HOST for enterprise compatibility
id: ghes-host-config
shell: bash
# zizmor: ignore[github-env] - GITHUB_SERVER_URL is set by GitHub Actions, not user input.
run: |
# Derive GH_HOST from GITHUB_SERVER_URL so the gh CLI targets the correct
# GitHub instance (GHES/GHEC). On github.com this is a harmless no-op.
GH_HOST="${GITHUB_SERVER_URL#https://}"
GH_HOST="${GH_HOST#http://}"
echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV"
- name: Download deterministic-results artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
name: check-requirements-deterministic
path: /tmp/gate
run-id: ${{ github.event.workflow_run.id }}
- name: Decide whether requirements changed since the last comment
id: gate
run: |
PR=$(jq -r '.pr_number' /tmp/gate/results.json)
HEAD=$(jq -r '.head_sha // empty' /tmp/gate/results.json)
if [ -z "${HEAD}" ]; then
echo "Artifact has no head_sha; running the agent."
exit 0
fi
# Recover the commit recorded in the most recent requirements-check
# comment from the "Checked at commit" link
PRIOR=$(gh api --paginate "repos/${GITHUB_REPOSITORY}/issues/${PR}/comments" \
--jq '.[] | select(.body | contains("<!-- requirements-check -->")) | .body' \
| grep -oiE '/commit/[0-9a-f]{40}' \
| grep -oiE '[0-9a-f]{40}' | tail -1 || true)
if [ -z "${PRIOR}" ]; then
echo "No previous comment with a recorded commit; running the agent."
exit 0
fi
if [ "${PRIOR}" = "${HEAD}" ]; then
echo "Head ${HEAD} unchanged since the last comment; skipping the agent."
echo "skip=true" >> "${GITHUB_OUTPUT}"
exit 0
fi
# List files changed between the recorded commit and the current head.
# Tracked patterns mirror script/check_requirements/diff.py TRACKED_PATTERNS.
CHANGED=$(gh api "repos/${GITHUB_REPOSITORY}/compare/${PRIOR}...${HEAD}" \
--jq '.files[].filename' 2>/dev/null) || {
echo "Could not compare ${PRIOR}...${HEAD}; running the agent."
exit 0
}
TRACKED=$(printf '%s\n' "${CHANGED}" \
| grep -Ex 'requirements.*\.txt|homeassistant/package_constraints\.txt' || true)
if [ -z "${TRACKED}" ]; then
echo "No tracked requirement files changed since ${PRIOR}; skipping the agent."
echo "skip=true" >> "${GITHUB_OUTPUT}"
else
echo "Tracked requirement files changed since ${PRIOR}; running the agent:"
printf '%s\n' "${TRACKED}"
fi
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
pre_activation:
runs-on: ubuntu-slim
outputs:
@@ -1438,7 +1545,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@5c2fe865bb4dc46e1450f6ee0d0541d759aea73a # v0.79.6
uses: github/gh-aw-actions/setup@v0.79.6
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@@ -1461,48 +1568,12 @@ jobs:
const { main } = require('${{ runner.temp }}/gh-aw/actions/check_membership.cjs');
await main();
prepare:
needs: activation
if: github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
outputs:
pr_number: ${{ steps.prepare.outputs.pr_number }}
skip: ${{ steps.prepare.outputs.skip }}
steps:
- name: Configure GH_HOST for enterprise compatibility
id: ghes-host-config
shell: bash
# zizmor: ignore[github-env] - GITHUB_SERVER_URL is set by GitHub Actions, not user input.
run: |
# Derive GH_HOST from GITHUB_SERVER_URL so the gh CLI targets the correct
# GitHub instance (GHES/GHEC). On github.com this is a harmless no-op.
GH_HOST="${GITHUB_SERVER_URL#https://}"
GH_HOST="${GH_HOST#http://}"
echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV"
- name: Download deterministic-results artifact
id: download
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
name: check-requirements-deterministic
path: /tmp/deterministic
run-id: ${{ github.event.workflow_run.id }}
- name: Resolve skip and PR number from the artifact
id: prepare
run: |
echo "skip=$(jq -r '.skip_aw' /tmp/deterministic/results.json)" >> "${GITHUB_OUTPUT}"
echo "pr_number=$(jq -r '.pr_number' /tmp/deterministic/results.json)" >> "${GITHUB_OUTPUT}"
safe_outputs:
needs:
- activation
- agent
- detection
- prepare
- extract_pr_number
if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success'
runs-on: ubuntu-slim
permissions:
@@ -1538,7 +1609,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@5c2fe865bb4dc46e1450f6ee0d0541d759aea73a # v0.79.6
uses: github/gh-aw-actions/setup@v0.79.6
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@@ -1583,7 +1654,7 @@ jobs:
GH_AW_ALLOWED_DOMAINS: "*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,binstar.org,bootstrap.pypa.io,conda.anaconda.org,conda.binstar.org,files.pythonhosted.org,github.com,host.docker.internal,pip.pypa.io,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.npmjs.org,repo.anaconda.com,repo.continuum.io,telemetry.enterprise.githubcopilot.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1,\"target\":\"${{ needs.prepare.outputs.pr_number }}\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"report_incomplete\":{}}"
GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1,\"target\":\"${{ needs.extract_pr_number.outputs.pr_number }}\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"report_incomplete\":{}}"
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
+68 -15
View File
@@ -15,41 +15,94 @@ tools:
github:
toolsets: [repos, pull_requests]
min-integrity: unapproved
if: needs.prepare.outputs.skip != 'true'
safe-outputs:
add-comment:
max: 1
target: "${{ needs.prepare.outputs.pr_number }}"
target: "${{ needs.extract_pr_number.outputs.pr_number }}"
needs:
- prepare
- extract_pr_number
jobs:
prepare:
# The deterministic stage always uploads an artifact; its `skip_aw` flag is
# true when no tracked requirement file changed since the last comment,
# which is our cue to skip the (token-spending) agent. Recover the PR number
# to comment on either way.
gate:
# Skip the (token-spending) agent when no tracked requirement file changed
if: github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
pull-requests: read
outputs:
skip: ${{ steps.prepare.outputs.skip }}
pr_number: ${{ steps.prepare.outputs.pr_number }}
skip: ${{ steps.gate.outputs.skip }}
steps:
- name: Download deterministic-results artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: check-requirements-deterministic
path: /tmp/gate
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Decide whether requirements changed since the last comment
id: gate
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
PR=$(jq -r '.pr_number' /tmp/gate/results.json)
HEAD=$(jq -r '.head_sha // empty' /tmp/gate/results.json)
if [ -z "${HEAD}" ]; then
echo "Artifact has no head_sha; running the agent."
exit 0
fi
# Recover the commit recorded in the most recent requirements-check
# comment from the "Checked at commit" link
PRIOR=$(gh api --paginate "repos/${GITHUB_REPOSITORY}/issues/${PR}/comments" \
--jq '.[] | select(.body | contains("<!-- requirements-check -->")) | .body' \
| grep -oiE '/commit/[0-9a-f]{40}' \
| grep -oiE '[0-9a-f]{40}' | tail -1 || true)
if [ -z "${PRIOR}" ]; then
echo "No previous comment with a recorded commit; running the agent."
exit 0
fi
if [ "${PRIOR}" = "${HEAD}" ]; then
echo "Head ${HEAD} unchanged since the last comment; skipping the agent."
echo "skip=true" >> "${GITHUB_OUTPUT}"
exit 0
fi
# List files changed between the recorded commit and the current head.
# Tracked patterns mirror script/check_requirements/diff.py TRACKED_PATTERNS.
CHANGED=$(gh api "repos/${GITHUB_REPOSITORY}/compare/${PRIOR}...${HEAD}" \
--jq '.files[].filename' 2>/dev/null) || {
echo "Could not compare ${PRIOR}...${HEAD}; running the agent."
exit 0
}
TRACKED=$(printf '%s\n' "${CHANGED}" \
| grep -Ex 'requirements.*\.txt|homeassistant/package_constraints\.txt' || true)
if [ -z "${TRACKED}" ]; then
echo "No tracked requirement files changed since ${PRIOR}; skipping the agent."
echo "skip=true" >> "${GITHUB_OUTPUT}"
else
echo "Tracked requirement files changed since ${PRIOR}; running the agent:"
printf '%s\n' "${TRACKED}"
fi
extract_pr_number:
needs: gate
if: needs.gate.outputs.skip != 'true' && github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
permissions:
actions: read
outputs:
pr_number: ${{ steps.extract.outputs.pr_number }}
steps:
- name: Download deterministic-results artifact
id: download
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: check-requirements-deterministic
path: /tmp/deterministic
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Resolve skip and PR number from the artifact
id: prepare
- name: Extract PR number from artifact
id: extract
run: |
echo "skip=$(jq -r '.skip_aw' /tmp/deterministic/results.json)" >> "${GITHUB_OUTPUT}"
echo "pr_number=$(jq -r '.pr_number' /tmp/deterministic/results.json)" >> "${GITHUB_OUTPUT}"
PR=$(jq -r '.pr_number' /tmp/deterministic/results.json)
echo "pr_number=${PR}" >> "${GITHUB_OUTPUT}"
concurrency:
group: ${{ github.workflow }}-${{ github.event.workflow_run.id }}
cancel-in-progress: true
+1 -1
View File
@@ -39,7 +39,7 @@ on:
env:
CACHE_VERSION: 3
MYPY_CACHE_VERSION: 1
HA_SHORT_VERSION: "2026.8"
HA_SHORT_VERSION: "2026.7"
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)
@@ -27,7 +27,7 @@ from .const import CONDITIONS_MAP, DOMAIN, FORECAST_MAP
_LOGGER = logging.getLogger(__name__)
API_TIMEOUT: Final[int] = 120
WEATHER_UPDATE_INTERVAL = timedelta(minutes=20)
WEATHER_UPDATE_INTERVAL = timedelta(minutes=10)
type AemetConfigEntry = ConfigEntry[AemetData]
+10 -10
View File
@@ -11,10 +11,10 @@ from homeassistant.components.sensor import (
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONF_NAME,
UnitOfDensity,
PERCENTAGE,
UnitOfPressure,
UnitOfRatio,
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant, callback
@@ -76,14 +76,14 @@ SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = (
AirlySensorEntityDescription(
key=ATTR_API_PM1,
device_class=SensorDeviceClass.PM1,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
),
AirlySensorEntityDescription(
key=ATTR_API_PM25,
device_class=SensorDeviceClass.PM25,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
attrs=lambda data: {
@@ -94,7 +94,7 @@ SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = (
AirlySensorEntityDescription(
key=ATTR_API_PM10,
device_class=SensorDeviceClass.PM10,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
attrs=lambda data: {
@@ -105,7 +105,7 @@ SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = (
AirlySensorEntityDescription(
key=ATTR_API_HUMIDITY,
device_class=SensorDeviceClass.HUMIDITY,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=1,
),
@@ -126,7 +126,7 @@ SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = (
AirlySensorEntityDescription(
key=ATTR_API_CO,
device_class=SensorDeviceClass.CO,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
attrs=lambda data: {
@@ -137,7 +137,7 @@ SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = (
AirlySensorEntityDescription(
key=ATTR_API_NO2,
device_class=SensorDeviceClass.NITROGEN_DIOXIDE,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
attrs=lambda data: {
@@ -148,7 +148,7 @@ SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = (
AirlySensorEntityDescription(
key=ATTR_API_SO2,
device_class=SensorDeviceClass.SULPHUR_DIOXIDE,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
attrs=lambda data: {
@@ -159,7 +159,7 @@ SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = (
AirlySensorEntityDescription(
key=ATTR_API_O3,
device_class=SensorDeviceClass.OZONE,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
attrs=lambda data: {
+8 -4
View File
@@ -12,7 +12,11 @@ from homeassistant.components.sensor import (
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.const import ATTR_TIME, UnitOfDensity, UnitOfRatio
from homeassistant.const import (
ATTR_TIME,
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_MILLION,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
@@ -91,7 +95,7 @@ SENSOR_TYPES: tuple[AirNowEntityDescription, ...] = (
AirNowEntityDescription(
key=ATTR_API_PM10,
translation_key="pm10",
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.PM10,
value_fn=lambda data: data.get(ATTR_API_PM10),
@@ -100,7 +104,7 @@ SENSOR_TYPES: tuple[AirNowEntityDescription, ...] = (
AirNowEntityDescription(
key=ATTR_API_PM25,
translation_key="pm25",
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.PM25,
value_fn=lambda data: data.get(ATTR_API_PM25),
@@ -109,7 +113,7 @@ SENSOR_TYPES: tuple[AirNowEntityDescription, ...] = (
AirNowEntityDescription(
key=ATTR_API_O3,
translation_key="o3",
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda data: data.get(ATTR_API_O3),
extra_state_attributes_fn=None,
+2 -2
View File
@@ -8,7 +8,7 @@ from typing import override
from aioairq.core import AirQ
from homeassistant.components.number import NumberEntity, NumberEntityDescription
from homeassistant.const import UnitOfRatio
from homeassistant.const import PERCENTAGE
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
@@ -32,7 +32,7 @@ AIRQ_LED_BRIGHTNESS = AirQBrightnessDescription(
native_min_value=0.0,
native_max_value=100.0,
native_step=1.0,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
value=lambda data: data["brightness"],
set_value=lambda device, value: device.set_current_brightness(value),
)
+51 -47
View File
@@ -12,9 +12,13 @@ from homeassistant.components.sensor import (
SensorStateClass,
)
from homeassistant.const import (
UnitOfDensity,
CONCENTRATION_GRAMS_PER_CUBIC_METER,
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_BILLION,
CONCENTRATION_PARTS_PER_MILLION,
PERCENTAGE,
UnitOfPressure,
UnitOfRatio,
UnitOfSoundPressure,
UnitOfTemperature,
)
@@ -40,70 +44,70 @@ SENSOR_TYPES: list[AirQEntityDescription] = [
AirQEntityDescription(
key="c2h4o",
translation_key="acetaldehyde",
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("c2h4o"),
),
AirQEntityDescription(
key="nh3_MR100",
translation_key="ammonia",
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("nh3_MR100"),
),
AirQEntityDescription(
key="ash3",
translation_key="arsine",
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("ash3"),
),
AirQEntityDescription(
key="br2",
translation_key="bromine",
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("br2"),
),
AirQEntityDescription(
key="ch4s",
translation_key="methanethiol",
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("ch4s"),
),
AirQEntityDescription(
key="cl2_M20",
translation_key="chlorine",
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("cl2_M20"),
),
AirQEntityDescription(
key="clo2",
translation_key="chlorine_dioxide",
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("clo2"),
),
AirQEntityDescription(
key="co",
translation_key="carbon_monoxide",
native_unit_of_measurement=UnitOfDensity.MILLIGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("co"),
),
AirQEntityDescription(
key="co2",
device_class=SensorDeviceClass.CO2,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("co2"),
),
AirQEntityDescription(
key="cs2",
translation_key="carbon_disulfide",
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("cs2"),
),
@@ -118,182 +122,182 @@ SENSOR_TYPES: list[AirQEntityDescription] = [
AirQEntityDescription(
key="ethanol",
translation_key="ethanol",
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("ethanol"),
),
AirQEntityDescription(
key="c2h4",
translation_key="ethylene",
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("c2h4"),
),
AirQEntityDescription(
key="ch2o_M10",
translation_key="formaldehyde",
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("ch2o_M10"),
),
AirQEntityDescription(
key="f2",
translation_key="fluorine",
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("f2"),
),
AirQEntityDescription(
key="h2s",
translation_key="hydrogen_sulfide",
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("h2s"),
),
AirQEntityDescription(
key="hcl",
translation_key="hydrochloric_acid",
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("hcl"),
),
AirQEntityDescription(
key="hcn",
translation_key="hydrogen_cyanide",
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("hcn"),
),
AirQEntityDescription(
key="hf",
translation_key="hydrogen_fluoride",
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("hf"),
),
AirQEntityDescription(
key="health",
translation_key="health_index",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("health", 0.0) / 10.0,
),
AirQEntityDescription(
key="humidity",
device_class=SensorDeviceClass.HUMIDITY,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("humidity"),
),
AirQEntityDescription(
key="humidity_abs",
device_class=SensorDeviceClass.ABSOLUTE_HUMIDITY,
native_unit_of_measurement=UnitOfDensity.GRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_GRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("humidity_abs"),
),
AirQEntityDescription(
key="h2_M1000",
translation_key="hydrogen",
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("h2_M1000"),
),
AirQEntityDescription(
key="h2o2",
translation_key="hydrogen_peroxide",
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("h2o2"),
),
AirQEntityDescription(
key="ch4_MIPEX",
translation_key="methane",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("ch4_MIPEX"),
),
AirQEntityDescription(
key="mold",
translation_key="mold",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("mold"),
),
AirQEntityDescription(
key="n2o",
device_class=SensorDeviceClass.NITROUS_OXIDE,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("n2o"),
),
AirQEntityDescription(
key="no_M250",
device_class=SensorDeviceClass.NITROGEN_MONOXIDE,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("no_M250"),
),
AirQEntityDescription(
key="no2",
device_class=SensorDeviceClass.NITROGEN_DIOXIDE,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("no2"),
),
AirQEntityDescription(
key="acid_M100",
translation_key="organic_acid",
native_unit_of_measurement=UnitOfRatio.PARTS_PER_BILLION,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("acid_M100"),
),
AirQEntityDescription(
key="oxygen",
translation_key="oxygen",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("oxygen"),
),
AirQEntityDescription(
key="o3",
device_class=SensorDeviceClass.OZONE,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("o3"),
),
AirQEntityDescription(
key="performance",
translation_key="performance_index",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("performance", 0.0) / 10.0,
),
AirQEntityDescription(
key="ph3",
translation_key="hydrogen_phosphide",
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("ph3"),
),
AirQEntityDescription(
key="pm1",
device_class=SensorDeviceClass.PM1,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("pm1"),
),
AirQEntityDescription(
key="pm2_5",
device_class=SensorDeviceClass.PM25,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("pm2_5"),
),
AirQEntityDescription(
key="pm10",
device_class=SensorDeviceClass.PM10,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("pm10"),
),
@@ -315,42 +319,42 @@ SENSOR_TYPES: list[AirQEntityDescription] = [
AirQEntityDescription(
key="c3h8_MIPEX",
translation_key="propane",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("c3h8_MIPEX"),
),
AirQEntityDescription(
key="r32",
translation_key="r32",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("r32"),
),
AirQEntityDescription(
key="r454b",
translation_key="r454b",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("r454b"),
),
AirQEntityDescription(
key="r454c",
translation_key="r454c",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("r454c"),
),
AirQEntityDescription(
key="sih4",
translation_key="silicon_hydride",
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("sih4"),
),
AirQEntityDescription(
key="so2",
device_class=SensorDeviceClass.SULPHUR_DIOXIDE,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("so2"),
),
@@ -387,7 +391,7 @@ SENSOR_TYPES: list[AirQEntityDescription] = [
AirQEntityDescription(
key="tvoc",
device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS_PARTS,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_BILLION,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("tvoc"),
),
@@ -395,14 +399,14 @@ SENSOR_TYPES: list[AirQEntityDescription] = [
key="tvoc_ionsc",
translation_key="industrial_volatile_organic_compounds",
device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS_PARTS,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_BILLION,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("tvoc_ionsc"),
),
AirQEntityDescription(
key="virus",
translation_key="virus_index",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("virus", 0.0),
),
@@ -1,8 +1,6 @@
"""Support for airthings ble sensors."""
from collections.abc import Callable
import dataclasses
from dataclasses import dataclass
import logging
from typing import override
@@ -15,11 +13,13 @@ from homeassistant.components.sensor import (
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_PARTS_PER_BILLION,
CONCENTRATION_PARTS_PER_MILLION,
LIGHT_LUX,
PERCENTAGE,
EntityCategory,
Platform,
UnitOfPressure,
UnitOfRatio,
UnitOfSoundPressure,
UnitOfTemperature,
)
@@ -46,108 +46,87 @@ CONNECTIVITY_MODE_MAP = {
AirthingsConnectivityMode.NOT_CONFIGURED.value: "not_configured",
}
def get_connectivity_mode(value: str | float | None) -> str | None:
"""Get connectivity mode."""
if not isinstance(value, str):
return None
return CONNECTIVITY_MODE_MAP.get(value)
@dataclass(frozen=True, kw_only=True)
class AirthingsBLESensorEntityDescription(SensorEntityDescription):
"""Describes Airthings BLE sensor entity."""
value_fn: Callable[[str | float | None], str | float | None] = lambda x: x
SENSORS_MAPPING_TEMPLATE: dict[str, AirthingsBLESensorEntityDescription] = {
"radon_1day_avg": AirthingsBLESensorEntityDescription(
SENSORS_MAPPING_TEMPLATE: dict[str, SensorEntityDescription] = {
"radon_1day_avg": SensorEntityDescription(
key="radon_1day_avg",
translation_key="radon_1day_avg",
native_unit_of_measurement=VOLUME_BECQUEREL,
suggested_display_precision=0,
state_class=SensorStateClass.MEASUREMENT,
),
"radon_longterm_avg": AirthingsBLESensorEntityDescription(
"radon_longterm_avg": SensorEntityDescription(
key="radon_longterm_avg",
translation_key="radon_longterm_avg",
native_unit_of_measurement=VOLUME_BECQUEREL,
suggested_display_precision=0,
state_class=SensorStateClass.MEASUREMENT,
),
"radon_1day_level": AirthingsBLESensorEntityDescription(
"radon_1day_level": SensorEntityDescription(
key="radon_1day_level",
translation_key="radon_1day_level",
device_class=SensorDeviceClass.ENUM,
options=["good", "fair", "poor"],
value_fn=lambda value: value if value != "unknown" else None,
),
"radon_longterm_level": AirthingsBLESensorEntityDescription(
"radon_longterm_level": SensorEntityDescription(
key="radon_longterm_level",
translation_key="radon_longterm_level",
device_class=SensorDeviceClass.ENUM,
options=["good", "fair", "poor"],
value_fn=lambda value: value if value != "unknown" else None,
),
"temperature": AirthingsBLESensorEntityDescription(
"temperature": SensorEntityDescription(
key="temperature",
device_class=SensorDeviceClass.TEMPERATURE,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=1,
),
"humidity": AirthingsBLESensorEntityDescription(
"humidity": SensorEntityDescription(
key="humidity",
device_class=SensorDeviceClass.HUMIDITY,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=1,
),
"pressure": AirthingsBLESensorEntityDescription(
"pressure": SensorEntityDescription(
key="pressure",
device_class=SensorDeviceClass.ATMOSPHERIC_PRESSURE,
native_unit_of_measurement=UnitOfPressure.MBAR,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=1,
),
"battery": AirthingsBLESensorEntityDescription(
"battery": SensorEntityDescription(
key="battery",
device_class=SensorDeviceClass.BATTERY,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
suggested_display_precision=0,
),
"co2": AirthingsBLESensorEntityDescription(
"co2": SensorEntityDescription(
key="co2",
device_class=SensorDeviceClass.CO2,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
),
"voc": AirthingsBLESensorEntityDescription(
"voc": SensorEntityDescription(
key="voc",
device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS_PARTS,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_BILLION,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
),
"illuminance": AirthingsBLESensorEntityDescription(
"illuminance": SensorEntityDescription(
key="illuminance",
translation_key="illuminance",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
),
"lux": AirthingsBLESensorEntityDescription(
"lux": SensorEntityDescription(
key="lux",
device_class=SensorDeviceClass.ILLUMINANCE,
native_unit_of_measurement=LIGHT_LUX,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
),
"noise": AirthingsBLESensorEntityDescription(
"noise": SensorEntityDescription(
key="noise",
translation_key="ambient_noise",
device_class=SensorDeviceClass.SOUND_PRESSURE,
@@ -155,14 +134,13 @@ SENSORS_MAPPING_TEMPLATE: dict[str, AirthingsBLESensorEntityDescription] = {
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
),
"connectivity_mode": AirthingsBLESensorEntityDescription(
"connectivity_mode": SensorEntityDescription(
key="connectivity_mode",
translation_key="connectivity_mode",
device_class=SensorDeviceClass.ENUM,
options=list(CONNECTIVITY_MODE_MAP.values()),
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
value_fn=get_connectivity_mode,
),
}
@@ -250,13 +228,12 @@ class AirthingsSensor(
"""Airthings BLE sensors for the device."""
_attr_has_entity_name = True
entity_description: AirthingsBLESensorEntityDescription
def __init__(
self,
coordinator: AirthingsBLEDataUpdateCoordinator,
airthings_device: AirthingsDevice,
entity_description: AirthingsBLESensorEntityDescription,
entity_description: SensorEntityDescription,
) -> None:
"""Populate the airthings entity with relevant data."""
super().__init__(coordinator)
@@ -295,4 +272,11 @@ class AirthingsSensor(
def native_value(self) -> StateType:
"""Return the value reported by the sensor."""
value = self.coordinator.data.sensors[self.entity_description.key]
return self.entity_description.value_fn(value)
# Map connectivity mode to enum values
if self.entity_description.key == "connectivity_mode":
if not isinstance(value, str):
return None
return CONNECTIVITY_MODE_MAP.get(value)
return value
@@ -45,23 +45,13 @@
"name": "Radon 1-day average"
},
"radon_1day_level": {
"name": "Radon 1-day level",
"state": {
"fair": "Fair",
"good": "Good",
"poor": "Poor"
}
"name": "Radon 1-day level"
},
"radon_longterm_avg": {
"name": "Radon longterm average"
},
"radon_longterm_level": {
"name": "Radon longterm level",
"state": {
"fair": "[%key:component::airthings_ble::entity::sensor::radon_1day_level::state::fair%]",
"good": "[%key:component::airthings_ble::entity::sensor::radon_1day_level::state::good%]",
"poor": "[%key:component::airthings_ble::entity::sensor::radon_1day_level::state::poor%]"
}
"name": "Radon longterm level"
}
}
},
@@ -8,5 +8,5 @@
"iot_class": "cloud_polling",
"loggers": ["aioamazondevices"],
"quality_scale": "platinum",
"requirements": ["aioamazondevices==14.1.6"]
"requirements": ["aioamazondevices==14.1.3"]
}
@@ -28,7 +28,7 @@ async def async_update_unique_id(
for serial_num in coordinator.data:
unique_id = f"{serial_num}-{old_key}"
if entity_id := entity_registry.async_get_entity_id(
platform, DOMAIN, unique_id
DOMAIN, platform, unique_id
):
_LOGGER.debug("Updating unique_id for %s", entity_id)
new_unique_id = unique_id.replace(old_key, new_key)
@@ -48,7 +48,7 @@ async def async_remove_entity_from_virtual_group(
for serial_num in coordinator.data:
unique_id = f"{serial_num}-{key}"
entity_id = entity_registry.async_get_entity_id(platform, DOMAIN, unique_id)
entity_id = entity_registry.async_get_entity_id(DOMAIN, platform, unique_id)
is_group = coordinator.data[serial_num].device_family == SPEAKER_GROUP_FAMILY
if entity_id and is_group:
entity_registry.async_remove(entity_id)
@@ -70,7 +70,7 @@ async def async_remove_unsupported_notification_sensors(
):
unique_id = f"{serial_num}-{notification_key}"
entity_id = entity_registry.async_get_entity_id(
SENSOR_DOMAIN, DOMAIN, unique_id=unique_id
DOMAIN, SENSOR_DOMAIN, unique_id=unique_id
)
is_unsupported = not coordinator.data[serial_num].notifications_supported
@@ -11,14 +11,15 @@ from homeassistant.components.sensor import (
)
from homeassistant.const import (
ATTR_NAME,
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_MILLION,
DEGREE,
LIGHT_LUX,
UnitOfDensity,
PERCENTAGE,
UnitOfIrradiance,
UnitOfLength,
UnitOfPrecipitationDepth,
UnitOfPressure,
UnitOfRatio,
UnitOfSpeed,
UnitOfTemperature,
UnitOfVolumetricFlux,
@@ -156,13 +157,13 @@ SENSOR_DESCRIPTIONS = (
SensorEntityDescription(
key=TYPE_PM25_IN_AQIN,
translation_key="pm25_indoor_aqin",
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM25,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_PM25_IN_24H_AQIN,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
translation_key="pm25_indoor_24h_aqin",
device_class=SensorDeviceClass.PM25,
state_class=SensorStateClass.MEASUREMENT,
@@ -170,28 +171,28 @@ SENSOR_DESCRIPTIONS = (
SensorEntityDescription(
key=TYPE_PM10_IN_AQIN,
translation_key="pm10_indoor_aqin",
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM10,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_PM10_IN_24H_AQIN,
translation_key="pm10_indoor_24h_aqin",
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM10,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_CO2_IN_AQIN,
translation_key="co2_indoor_aqin",
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
device_class=SensorDeviceClass.CO2,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_CO2_IN_24H_AQIN,
translation_key="co2_indoor_24h_aqin",
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
device_class=SensorDeviceClass.CO2,
state_class=SensorStateClass.MEASUREMENT,
),
@@ -205,7 +206,7 @@ SENSOR_DESCRIPTIONS = (
SensorEntityDescription(
key=TYPE_PM_IN_HUMIDITY_AQIN,
translation_key="pm_indoor_humidity_aqin",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
@@ -249,7 +250,7 @@ SENSOR_DESCRIPTIONS = (
),
SensorEntityDescription(
key=TYPE_CO2,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
device_class=SensorDeviceClass.CO2,
state_class=SensorStateClass.MEASUREMENT,
),
@@ -290,83 +291,83 @@ SENSOR_DESCRIPTIONS = (
SensorEntityDescription(
key=TYPE_HUMIDITY10,
translation_key="humidity_10",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_HUMIDITY1,
translation_key="humidity_1",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_HUMIDITY2,
translation_key="humidity_2",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_HUMIDITY3,
translation_key="humidity_3",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_HUMIDITY4,
translation_key="humidity_4",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_HUMIDITY5,
translation_key="humidity_5",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_HUMIDITY6,
translation_key="humidity_6",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_HUMIDITY7,
translation_key="humidity_7",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_HUMIDITY8,
translation_key="humidity_8",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_HUMIDITY9,
translation_key="humidity_9",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_HUMIDITY,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_HUMIDITYIN,
translation_key="humidity_indoor",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
@@ -416,95 +417,95 @@ SENSOR_DESCRIPTIONS = (
SensorEntityDescription(
key=TYPE_PM25_24H,
translation_key="pm25_24h_average",
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM25,
),
SensorEntityDescription(
key=TYPE_PM25_IN,
translation_key="pm25_indoor",
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM25,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_PM25_IN_24H,
translation_key="pm25_indoor_24h_average",
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM25,
),
SensorEntityDescription(
key=TYPE_PM25,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM25,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILHUM10,
translation_key="soil_humidity_10",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILHUM1,
translation_key="soil_humidity_1",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILHUM2,
translation_key="soil_humidity_2",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILHUM3,
translation_key="soil_humidity_3",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILHUM4,
translation_key="soil_humidity_4",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILHUM5,
translation_key="soil_humidity_5",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILHUM6,
translation_key="soil_humidity_6",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILHUM7,
translation_key="soil_humidity_7",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILHUM8,
translation_key="soil_humidity_8",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILHUM9,
translation_key="soil_humidity_9",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
@@ -29,6 +29,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
async def async_setup_entry(hass: HomeAssistant, entry: AnthropicConfigEntry) -> bool:
"""Set up Anthropic from a config entry."""
coordinator = AnthropicCoordinator(hass, entry)
await coordinator.async_setup()
await coordinator.async_config_entry_first_refresh()
entry.runtime_data = coordinator
LOGGER.debug("Available models: %s", coordinator.data)
@@ -28,7 +28,6 @@ from homeassistant.const import (
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import config_validation as cv, llm
from homeassistant.helpers.httpx_client import get_async_client
from homeassistant.helpers.selector import (
NumberSelector,
NumberSelectorConfig,
@@ -66,7 +65,7 @@ from .const import (
TOOL_SEARCH_UNSUPPORTED_MODELS,
PromptCaching,
)
from .coordinator import model_alias
from .coordinator import async_create_client, model_alias
if TYPE_CHECKING:
from . import AnthropicConfigEntry
@@ -95,9 +94,7 @@ async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> None:
Data has the keys from STEP_USER_DATA_SCHEMA with values provided by the user.
"""
client = anthropic.AsyncAnthropic(
api_key=data[CONF_API_KEY], http_client=get_async_client(hass)
)
client = await async_create_client(hass, data[CONF_API_KEY])
await client.models.list(timeout=10.0)
@@ -549,9 +546,8 @@ class ConversationSubentryFlowHandler(ConfigSubentryFlow):
location_data: dict[str, str] = {}
zone_home = self.hass.states.get(ENTITY_ID_HOME)
if zone_home is not None:
client = anthropic.AsyncAnthropic(
api_key=self._get_entry().data[CONF_API_KEY],
http_client=get_async_client(self.hass),
client = await async_create_client(
self.hass, self._get_entry().data[CONF_API_KEY]
)
location_schema = vol.Schema(
{
@@ -1,6 +1,7 @@
"""Coordinator for the Anthropic integration."""
import datetime
from functools import partial
from typing import override
import anthropic
@@ -20,6 +21,19 @@ UPDATE_INTERVAL_DISCONNECTED = datetime.timedelta(minutes=1)
type AnthropicConfigEntry = ConfigEntry[AnthropicCoordinator]
async def async_create_client(
hass: HomeAssistant, api_key: str
) -> anthropic.AsyncAnthropic:
"""Create an Anthropic client."""
return await hass.async_add_executor_job(
partial(
anthropic.AsyncAnthropic,
api_key=api_key,
http_client=get_async_client(hass),
)
)
@callback
def model_alias(model_id: str) -> str:
"""Resolve alias from versioned model name."""
@@ -33,7 +47,8 @@ def model_alias(model_id: str) -> str:
class AnthropicCoordinator(DataUpdateCoordinator[list[anthropic.types.ModelInfo]]):
"""Coordinator using different intervals after success and failure."""
client: anthropic.AsyncAnthropic
config_entry: AnthropicConfigEntry
_client: anthropic.AsyncAnthropic | None = None
def __init__(self, hass: HomeAssistant, config_entry: AnthropicConfigEntry) -> None:
"""Initialize the coordinator."""
@@ -46,8 +61,18 @@ class AnthropicCoordinator(DataUpdateCoordinator[list[anthropic.types.ModelInfo]
update_method=self.async_update_data,
always_update=False,
)
self.client = anthropic.AsyncAnthropic(
api_key=config_entry.data[CONF_API_KEY], http_client=get_async_client(hass)
@property
def client(self) -> anthropic.AsyncAnthropic:
"""Return the Anthropic client."""
if self._client is None:
raise RuntimeError("Anthropic client is not set up")
return self._client
async def async_setup(self) -> None:
"""Set up the coordinator."""
self._client = await async_create_client(
self.hass, self.config_entry.data[CONF_API_KEY]
)
@callback
+12 -29
View File
@@ -731,32 +731,17 @@ class AutomationEntity(BaseAutomationEntity, RestoreEntity):
trace_element = TraceElement(variables, trigger_path)
trace_append_element(trace_element)
if not skip_condition and self._condition is not None:
try:
conditions_pass = self._condition.async_check(variables=variables)
except (vol.Invalid, HomeAssistantError) as err:
self._logger.error(
"Error while checking conditions of automation %s: %s",
self.entity_id,
err,
)
automation_trace.set_error(err)
return None
except Exception as err:
self._logger.exception(
"Unexpected error while checking conditions of automation %s",
self.entity_id,
)
automation_trace.set_error(err)
return None
if not conditions_pass:
self._logger.debug(
"Conditions not met, aborting automation. Condition summary: %s",
trace_get(clear=False),
)
script_execution_set("failed_conditions")
return None
if (
not skip_condition
and self._condition is not None
and not self._condition.async_check(variables=variables)
):
self._logger.debug(
"Conditions not met, aborting automation. Condition summary: %s",
trace_get(clear=False),
)
script_execution_set("failed_conditions")
return None
self.async_set_context(trigger_context)
event_data = {
@@ -809,9 +794,7 @@ class AutomationEntity(BaseAutomationEntity, RestoreEntity):
)
automation_trace.set_error(err)
except Exception as err:
self._logger.exception(
"Unexpected error while executing automation %s", self.entity_id
)
self._logger.exception("While executing automation %s", self.entity_id)
automation_trace.set_error(err)
return None
+8 -7
View File
@@ -15,15 +15,16 @@ 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,
@@ -52,19 +53,19 @@ SENSOR_TYPES: tuple[BleBoxSensorEntityDescription, ...] = (
BleBoxSensorEntityDescription(
key="pm1",
device_class=SensorDeviceClass.PM1,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
BleBoxSensorEntityDescription(
key="pm2_5",
device_class=SensorDeviceClass.PM25,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
BleBoxSensorEntityDescription(
key="pm10",
device_class=SensorDeviceClass.PM10,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
BleBoxSensorEntityDescription(
@@ -83,7 +84,7 @@ SENSOR_TYPES: tuple[BleBoxSensorEntityDescription, ...] = (
BleBoxSensorEntityDescription(
key="humidity",
device_class=SensorDeviceClass.HUMIDITY,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
BleBoxSensorEntityDescription(
@@ -178,7 +179,7 @@ SENSOR_TYPES: tuple[BleBoxSensorEntityDescription, ...] = (
BleBoxSensorEntityDescription(
key="co2",
device_class=SensorDeviceClass.CO2,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
state_class=SensorStateClass.MEASUREMENT,
),
BleBoxSensorEntityDescription(
@@ -21,6 +21,6 @@
"bluetooth-auto-recovery==1.6.4",
"bluetooth-data-tools==1.29.18",
"dbus-fast==5.0.22",
"habluetooth==6.25.1"
"habluetooth==6.23.1"
]
}
@@ -805,10 +805,6 @@ class DefaultAgent(ConversationEntity):
else:
num_unmatched_entities += 1
# Literal text matched is the dominant signal
same_text_matched = (maybe_result is not None) and (
result.text_chunks_matched == maybe_result.text_chunks_matched
)
if (
(maybe_result is None) # first result
or (
@@ -817,25 +813,22 @@ class DefaultAgent(ConversationEntity):
)
or (
# More entities matched
same_text_matched
and (num_matched_entities > best_num_matched_entities)
num_matched_entities > best_num_matched_entities
)
or (
# Fewer unmatched entities
same_text_matched
and (num_matched_entities == best_num_matched_entities)
(num_matched_entities == best_num_matched_entities)
and (num_unmatched_entities < best_num_unmatched_entities)
)
or (
# Prefer unmatched ranges
same_text_matched
and (num_matched_entities == best_num_matched_entities)
(num_matched_entities == best_num_matched_entities)
and (num_unmatched_entities == best_num_unmatched_entities)
and (num_unmatched_ranges > best_num_unmatched_ranges)
)
or (
# Prefer match failures with entities
same_text_matched
(result.text_chunks_matched == maybe_result.text_chunks_matched)
and (num_unmatched_entities == best_num_unmatched_entities)
and (num_unmatched_ranges == best_num_unmatched_ranges)
and (
@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/conversation",
"integration_type": "entity",
"quality_scale": "internal",
"requirements": ["hassil==3.8.0", "home-assistant-intents==2026.6.24"]
"requirements": ["hassil==3.8.0", "home-assistant-intents==2026.6.1"]
}
@@ -8,6 +8,6 @@
"documentation": "https://www.home-assistant.io/integrations/dropbox",
"integration_type": "service",
"iot_class": "cloud_polling",
"quality_scale": "silver",
"quality_scale": "bronze",
"requirements": ["python-dropbox-api==0.1.4"]
}
@@ -52,9 +52,7 @@ rules:
status: exempt
comment: Integration does not have any entities.
integration-owner: done
log-when-unavailable:
status: exempt
comment: Integration does not have any entities.
log-when-unavailable: todo
parallel-updates:
status: exempt
comment: Integration does not make any entity updates.
+7 -6
View File
@@ -15,9 +15,10 @@ from homeassistant.components.sensor import (
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_PARTS_PER_MILLION,
PERCENTAGE,
SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
EntityCategory,
UnitOfRatio,
UnitOfTime,
)
from homeassistant.core import HomeAssistant, callback
@@ -71,7 +72,7 @@ SENSOR_DESCRIPTIONS: tuple[DucoSensorEntityDescription, ...] = (
key="target_flow_level",
translation_key="target_flow_level",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
suggested_display_precision=0,
value_fn=lambda node: (
node.ventilation.flow_lvl_tgt if node.ventilation else None
@@ -95,7 +96,7 @@ SENSOR_DESCRIPTIONS: tuple[DucoSensorEntityDescription, ...] = (
key="co2",
device_class=SensorDeviceClass.CO2,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
value_fn=lambda node: node.sensor.co2 if node.sensor else None,
node_types=(
NodeType.BSCO2,
@@ -107,7 +108,7 @@ SENSOR_DESCRIPTIONS: tuple[DucoSensorEntityDescription, ...] = (
DucoSensorEntityDescription(
key="iaq_co2",
translation_key="iaq_co2",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
value_fn=lambda node: node.sensor.iaq_co2 if node.sensor else None,
@@ -122,14 +123,14 @@ SENSOR_DESCRIPTIONS: tuple[DucoSensorEntityDescription, ...] = (
key="humidity",
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
value_fn=lambda node: node.sensor.rh if node.sensor else None,
node_types=(NodeType.BSRH, NodeType.UCRH, NodeType.VLVRH, NodeType.VLVCO2RH),
),
DucoSensorEntityDescription(
key="iaq_rh",
translation_key="iaq_rh",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
value_fn=lambda node: node.sensor.iaq_rh if node.sensor else None,
+12 -11
View File
@@ -14,17 +14,18 @@ from homeassistant.components.sensor import (
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_MILLION,
DEGREE,
LIGHT_LUX,
PERCENTAGE,
UV_INDEX,
EntityCategory,
UnitOfDensity,
UnitOfElectricPotential,
UnitOfIrradiance,
UnitOfLength,
UnitOfPrecipitationDepth,
UnitOfPressure,
UnitOfRatio,
UnitOfSpeed,
UnitOfTemperature,
UnitOfVolumetricFlux,
@@ -98,7 +99,7 @@ ECOWITT_SENSORS_MAPPING: Final = {
EcoWittSensorTypes.HUMIDITY: SensorEntityDescription(
key="HUMIDITY",
device_class=SensorDeviceClass.HUMIDITY,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
EcoWittSensorTypes.DEGREE: SensorEntityDescription(
@@ -121,19 +122,19 @@ ECOWITT_SENSORS_MAPPING: Final = {
EcoWittSensorTypes.PM25: SensorEntityDescription(
key="PM25",
device_class=SensorDeviceClass.PM25,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
EcoWittSensorTypes.PM10: SensorEntityDescription(
key="PM10",
device_class=SensorDeviceClass.PM10,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
EcoWittSensorTypes.BATTERY_PERCENTAGE: SensorEntityDescription(
key="BATTERY_PERCENTAGE",
device_class=SensorDeviceClass.BATTERY,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
),
@@ -148,7 +149,7 @@ ECOWITT_SENSORS_MAPPING: Final = {
EcoWittSensorTypes.CO2_PPM: SensorEntityDescription(
key="CO2_PPM",
device_class=SensorDeviceClass.CO2,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
state_class=SensorStateClass.MEASUREMENT,
),
EcoWittSensorTypes.LUX: SensorEntityDescription(
@@ -262,13 +263,13 @@ ECOWITT_SENSORS_MAPPING: Final = {
),
EcoWittSensorTypes.PERCENTAGE: SensorEntityDescription(
key="PERCENTAGE",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
EcoWittSensorTypes.SOIL_MOISTURE: SensorEntityDescription(
key="SOIL_MOISTURE",
device_class=SensorDeviceClass.MOISTURE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
EcoWittSensorTypes.DISTANCE_MM: SensorEntityDescription(
@@ -285,13 +286,13 @@ ECOWITT_SENSORS_MAPPING: Final = {
EcoWittSensorTypes.PM1: SensorEntityDescription(
key="PM1",
device_class=SensorDeviceClass.PM1,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
EcoWittSensorTypes.PM4: SensorEntityDescription(
key="PM4",
device_class=SensorDeviceClass.PM4,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
}
@@ -65,14 +65,6 @@ async def _get_fixture_collection(envoy: Envoy, serial: str) -> dict[str, Any]:
"/ivp/meters/readings",
"/ivp/pdm/device_data",
"/home",
"/inventory.json?deleted=1",
"/admin/lib/acb_config",
"/ivp/sc/sched",
"/admin/lib/network_display",
"/admin/lib/wireless_display",
"/ivp/ensemble/relay",
"/ivp/livedata/status",
"/ivp/pdm/energy",
]
for end_point in end_points:
@@ -142,15 +134,16 @@ async def async_get_config_entry_diagnostics(
"encharge_power": envoy_data.encharge_power,
"encharge_aggregate": envoy_data.encharge_aggregate,
"enpower": envoy_data.enpower,
"acb_power": envoy_data.acb_power,
"acb_inventory": envoy_data.acb_inventory,
"battery_aggregate": envoy_data.battery_aggregate,
"collar": envoy_data.collar,
"c6cc": envoy_data.c6cc,
"system_consumption": envoy_data.system_consumption,
"system_production": envoy_data.system_production,
"system_consumption_phases": envoy_data.system_consumption_phases,
"system_production_phases": envoy_data.system_production_phases,
"ctmeter_production": envoy_data.ctmeter_production,
"ctmeter_consumption": envoy_data.ctmeter_consumption,
"ctmeter_storage": envoy_data.ctmeter_storage,
"ctmeter_production_phases": envoy_data.ctmeter_production_phases,
"ctmeter_consumption_phases": envoy_data.ctmeter_consumption_phases,
"ctmeter_storage_phases": envoy_data.ctmeter_storage_phases,
"ctmeters": envoy_data.ctmeters,
"ctmeters_phases": envoy_data.ctmeters_phases,
"dry_contact_status": envoy_data.dry_contact_status,
@@ -17,7 +17,7 @@
"mqtt": ["esphome/discover/#"],
"quality_scale": "platinum",
"requirements": [
"aioesphomeapi==45.5.2",
"aioesphomeapi==45.3.1",
"esphome-dashboard-api==1.3.0",
"bleak-esphome==3.9.4"
],
@@ -21,5 +21,5 @@
"integration_type": "system",
"preview_features": { "winter_mode": {} },
"quality_scale": "internal",
"requirements": ["home-assistant-frontend==20260624.1"]
"requirements": ["home-assistant-frontend==20260624.0"]
}
+10 -10
View File
@@ -14,7 +14,7 @@ from homeassistant.components.sensor import (
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.const import UnitOfDensity
from homeassistant.const import CONCENTRATION_MICROGRAMS_PER_CUBIC_METER
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
@@ -63,7 +63,7 @@ SENSOR_TYPES: tuple[GiosSensorEntityDescription, ...] = (
key=ATTR_C6H6,
value=lambda sensors: sensors.c6h6.value if sensors.c6h6 else None,
suggested_display_precision=0,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
translation_key="c6h6",
),
@@ -72,7 +72,7 @@ SENSOR_TYPES: tuple[GiosSensorEntityDescription, ...] = (
value=lambda sensors: sensors.co.value if sensors.co else None,
suggested_display_precision=0,
device_class=SensorDeviceClass.CO,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
GiosSensorEntityDescription(
@@ -80,7 +80,7 @@ SENSOR_TYPES: tuple[GiosSensorEntityDescription, ...] = (
value=lambda sensors: sensors.no.value if sensors.no else None,
suggested_display_precision=0,
device_class=SensorDeviceClass.NITROGEN_MONOXIDE,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
GiosSensorEntityDescription(
@@ -88,7 +88,7 @@ SENSOR_TYPES: tuple[GiosSensorEntityDescription, ...] = (
value=lambda sensors: sensors.no2.value if sensors.no2 else None,
suggested_display_precision=0,
device_class=SensorDeviceClass.NITROGEN_DIOXIDE,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
GiosSensorEntityDescription(
@@ -104,7 +104,7 @@ SENSOR_TYPES: tuple[GiosSensorEntityDescription, ...] = (
translation_key=ATTR_NOX,
value=lambda sensors: sensors.nox.value if sensors.nox else None,
suggested_display_precision=0,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
GiosSensorEntityDescription(
@@ -112,7 +112,7 @@ SENSOR_TYPES: tuple[GiosSensorEntityDescription, ...] = (
value=lambda sensors: sensors.o3.value if sensors.o3 else None,
suggested_display_precision=0,
device_class=SensorDeviceClass.OZONE,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
GiosSensorEntityDescription(
@@ -128,7 +128,7 @@ SENSOR_TYPES: tuple[GiosSensorEntityDescription, ...] = (
value=lambda sensors: sensors.pm10.value if sensors.pm10 else None,
suggested_display_precision=0,
device_class=SensorDeviceClass.PM10,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
GiosSensorEntityDescription(
@@ -144,7 +144,7 @@ SENSOR_TYPES: tuple[GiosSensorEntityDescription, ...] = (
value=lambda sensors: sensors.pm25.value if sensors.pm25 else None,
suggested_display_precision=0,
device_class=SensorDeviceClass.PM25,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
GiosSensorEntityDescription(
@@ -160,7 +160,7 @@ SENSOR_TYPES: tuple[GiosSensorEntityDescription, ...] = (
value=lambda sensors: sensors.so2.value if sensors.so2 else None,
suggested_display_precision=0,
device_class=SensorDeviceClass.SULPHUR_DIOXIDE,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
GiosSensorEntityDescription(
+1 -1
View File
@@ -92,7 +92,7 @@ class SupervisorJobs:
# We catch all errors to prevent an error in one from stopping the others
for match in [job for job in self._jobs.values() if subscription.matches(job)]:
try:
subscription.event_callback(match)
return subscription.event_callback(match)
except Exception as err: # noqa: BLE001
_LOGGER.error(
"Error encountered processing Supervisor Job (%s %s %s) - %s",
@@ -13,7 +13,7 @@ from homeassistant.components.sensor import (
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.const import EntityCategory, UnitOfRatio, UnitOfVolume
from homeassistant.const import PERCENTAGE, EntityCategory, UnitOfVolume
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.util import dt as dt_util, slugify
@@ -67,7 +67,7 @@ BSH_PROGRAM_SENSORS = (
),
HomeConnectSensorEntityDescription(
key=EventKey.BSH_COMMON_OPTION_PROGRAM_PROGRESS,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
translation_key="program_progress",
appliance_types=APPLIANCES_WITH_PROGRAMS,
),
@@ -158,7 +158,6 @@ SENSORS = (
HomeConnectSensorEntityDescription(
key=StatusKey.BSH_COMMON_BATTERY_LEVEL,
device_class=SensorDeviceClass.BATTERY,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
),
HomeConnectSensorEntityDescription(
key=StatusKey.BSH_COMMON_VIDEO_CAMERA_STATE,
@@ -26,17 +26,18 @@ from homeassistant.components.sensor import (
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_MILLION,
LIGHT_LUX,
PERCENTAGE,
SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
EntityCategory,
Platform,
UnitOfDensity,
UnitOfElectricCurrent,
UnitOfElectricPotential,
UnitOfEnergy,
UnitOfPower,
UnitOfPressure,
UnitOfRatio,
UnitOfSoundPressure,
UnitOfTemperature,
)
@@ -253,7 +254,7 @@ SIMPLE_SENSOR: dict[str, HomeKitSensorEntityDescription] = {
name="Current Humidity",
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
# This sensor is only for humidity characteristics that are not part
# of a humidity sensor service.
probe=(lambda char: char.service.type != ServicesTypes.HUMIDITY_SENSOR),
@@ -269,42 +270,42 @@ SIMPLE_SENSOR: dict[str, HomeKitSensorEntityDescription] = {
name="PM2.5 Density",
device_class=SensorDeviceClass.PM25,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
),
CharacteristicsTypes.DENSITY_PM10: HomeKitSensorEntityDescription(
key=CharacteristicsTypes.DENSITY_PM10,
name="PM10 Density",
device_class=SensorDeviceClass.PM10,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
),
CharacteristicsTypes.DENSITY_OZONE: HomeKitSensorEntityDescription(
key=CharacteristicsTypes.DENSITY_OZONE,
name="Ozone Density",
device_class=SensorDeviceClass.OZONE,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
),
CharacteristicsTypes.DENSITY_NO2: HomeKitSensorEntityDescription(
key=CharacteristicsTypes.DENSITY_NO2,
name="Nitrogen Dioxide Density",
device_class=SensorDeviceClass.NITROGEN_DIOXIDE,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
),
CharacteristicsTypes.DENSITY_SO2: HomeKitSensorEntityDescription(
key=CharacteristicsTypes.DENSITY_SO2,
name="Sulphur Dioxide Density",
device_class=SensorDeviceClass.SULPHUR_DIOXIDE,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
),
CharacteristicsTypes.DENSITY_VOC: HomeKitSensorEntityDescription(
key=CharacteristicsTypes.DENSITY_VOC,
name="Volatile Organic Compound Density",
device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
),
CharacteristicsTypes.THREAD_NODE_CAPABILITIES: HomeKitSensorEntityDescription(
key=CharacteristicsTypes.THREAD_NODE_CAPABILITIES,
@@ -362,13 +363,13 @@ SIMPLE_SENSOR: dict[str, HomeKitSensorEntityDescription] = {
key=CharacteristicsTypes.FILTER_LIFE_LEVEL,
name="Filter lifetime",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
),
CharacteristicsTypes.WATER_LEVEL: HomeKitSensorEntityDescription(
key=CharacteristicsTypes.WATER_LEVEL,
name="Water level",
translation_key="water_level",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
CharacteristicsTypes.VENDOR_EVE_THERMO_VALVE_POSITION: (
@@ -378,7 +379,7 @@ SIMPLE_SENSOR: dict[str, HomeKitSensorEntityDescription] = {
translation_key="valve_position",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
)
),
}
@@ -408,7 +409,7 @@ class HomeKitHumiditySensor(HomeKitSensor):
"""Representation of a Homekit humidity sensor."""
_attr_device_class = SensorDeviceClass.HUMIDITY
_attr_native_unit_of_measurement = UnitOfRatio.PERCENTAGE
_attr_native_unit_of_measurement = PERCENTAGE
@override
def get_characteristic_types(self) -> list[str]:
@@ -480,7 +481,7 @@ class HomeKitCarbonDioxideSensor(HomeKitSensor):
"""Representation of a Homekit Carbon Dioxide sensor."""
_attr_device_class = SensorDeviceClass.CO2
_attr_native_unit_of_measurement = UnitOfRatio.PARTS_PER_MILLION
_attr_native_unit_of_measurement = CONCENTRATION_PARTS_PER_MILLION
@override
def get_characteristic_types(self) -> list[str]:
@@ -504,7 +505,7 @@ class HomeKitBatterySensor(HomeKitSensor):
"""Representation of a Homekit battery sensor."""
_attr_device_class = SensorDeviceClass.BATTERY
_attr_native_unit_of_measurement = UnitOfRatio.PERCENTAGE
_attr_native_unit_of_measurement = PERCENTAGE
_attr_entity_category = EntityCategory.DIAGNOSTIC
@override
@@ -50,13 +50,14 @@ from homeassistant.components.sensor import (
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_GRAMS_PER_CUBIC_METER,
CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
DEGREE,
LIGHT_LUX,
UnitOfDensity,
PERCENTAGE,
UnitOfEnergy,
UnitOfPower,
UnitOfPrecipitationDepth,
UnitOfRatio,
UnitOfSpeed,
UnitOfTemperature,
UnitOfVolume,
@@ -83,7 +84,7 @@ SMOKE_DETECTOR_SENSORS: tuple[HmipSmokeDetectorSensorDescription, ...] = (
HmipSmokeDetectorSensorDescription(
key="dirt_level",
translation_key="smoke_detector_dirt_level",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
channel_field="dirtLevel",
@@ -531,7 +532,7 @@ class HomematicipFloorTerminalBlockMechanicChannelValve(
):
"""Representation of the HomematicIP floor terminal block."""
_attr_native_unit_of_measurement = UnitOfRatio.PERCENTAGE
_attr_native_unit_of_measurement = PERCENTAGE
_attr_state_class = SensorStateClass.MEASUREMENT
def __init__(
@@ -580,7 +581,7 @@ class HomematicipAccesspointDutyCycle(HomematicipGenericEntity, SensorEntity):
"""Representation of then HomeMaticIP access point."""
_attr_icon = "mdi:access-point-network"
_attr_native_unit_of_measurement = UnitOfRatio.PERCENTAGE
_attr_native_unit_of_measurement = PERCENTAGE
_attr_state_class = SensorStateClass.MEASUREMENT
def __init__(self, hap: HomematicipHAP, device) -> None:
@@ -599,7 +600,7 @@ class HomematicipAccesspointDutyCycle(HomematicipGenericEntity, SensorEntity):
class HomematicipHeatingThermostat(HomematicipGenericEntity, SensorEntity):
"""Representation of the HomematicIP heating thermostat."""
_attr_native_unit_of_measurement = UnitOfRatio.PERCENTAGE
_attr_native_unit_of_measurement = PERCENTAGE
def __init__(self, hap: HomematicipHAP, device) -> None:
"""Initialize heating thermostat device."""
@@ -628,7 +629,7 @@ class HomematicipHumiditySensor(HomematicipGenericEntity, SensorEntity):
"""Representation of the HomematicIP humidity sensor."""
_attr_device_class = SensorDeviceClass.HUMIDITY
_attr_native_unit_of_measurement = UnitOfRatio.PERCENTAGE
_attr_native_unit_of_measurement = PERCENTAGE
_attr_state_class = SensorStateClass.MEASUREMENT
def __init__(self, hap: HomematicipHAP, device) -> None:
@@ -679,9 +680,9 @@ class HomematicipAbsoluteHumiditySensor(HomematicipGenericEntity, SensorEntity):
"""Representation of the HomematicIP absolute humidity sensor."""
_attr_device_class = SensorDeviceClass.ABSOLUTE_HUMIDITY
_attr_native_unit_of_measurement = UnitOfDensity.GRAMS_PER_CUBIC_METER
_attr_native_unit_of_measurement = CONCENTRATION_GRAMS_PER_CUBIC_METER
_attr_suggested_display_precision = 1
_attr_suggested_unit_of_measurement = UnitOfDensity.MILLIGRAMS_PER_CUBIC_METER
_attr_suggested_unit_of_measurement = CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER
_attr_state_class = SensorStateClass.MEASUREMENT
def __init__(self, hap: HomematicipHAP, device) -> None:
@@ -1142,7 +1143,7 @@ class HomematicipSoilMoistureSensor(HomematicipGenericEntity, SensorEntity):
"""Representation of the HomematicIP soil moisture sensor."""
_attr_device_class = SensorDeviceClass.MOISTURE
_attr_native_unit_of_measurement = UnitOfRatio.PERCENTAGE
_attr_native_unit_of_measurement = PERCENTAGE
_attr_state_class = SensorStateClass.MEASUREMENT
def __init__(self, hap: HomematicipHAP, device) -> None:
@@ -161,7 +161,5 @@ SENSOR_KEYS: Final[dict[int, list[str]]] = {
IndevoltSolar.CUMULATIVE_PRODUCTION,
IndevoltBattery.GEN_2_CYCLE_COUNT,
IndevoltBattery.GEN_2_TRANSFORMER_TEMPERATURE,
IndevoltBattery.REMAINING_CHARGING_TIME,
IndevoltBattery.REMAINING_DISCHARGING_TIME,
],
}
@@ -27,7 +27,6 @@ from homeassistant.const import (
UnitOfFrequency,
UnitOfPower,
UnitOfTemperature,
UnitOfTime,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
@@ -46,7 +45,6 @@ class IndevoltSensorEntityDescription(SensorEntityDescription):
state_mapping: dict[str | int, str] = field(default_factory=dict)
generation: tuple[int, ...] = (1, 2)
energy_mode: IndevoltEnergyMode | None = None
charge_discharge_state: int | None = None
SENSORS: Final = (
@@ -244,26 +242,6 @@ SENSORS: Final = (
state_mapping={1000: "static", 1001: "charging", 1002: "discharging"},
device_class=SensorDeviceClass.ENUM,
),
IndevoltSensorEntityDescription(
key=IndevoltBattery.REMAINING_CHARGING_TIME,
generation=(2,),
translation_key="remaining_charging_time",
native_unit_of_measurement=UnitOfTime.MINUTES,
device_class=SensorDeviceClass.DURATION,
state_class=SensorStateClass.MEASUREMENT,
charge_discharge_state=1001,
entity_registry_enabled_default=False,
),
IndevoltSensorEntityDescription(
key=IndevoltBattery.REMAINING_DISCHARGING_TIME,
generation=(2,),
translation_key="remaining_discharging_time",
native_unit_of_measurement=UnitOfTime.MINUTES,
device_class=SensorDeviceClass.DURATION,
state_class=SensorStateClass.MEASUREMENT,
charge_discharge_state=1002,
entity_registry_enabled_default=False,
),
IndevoltSensorEntityDescription(
key=IndevoltBattery.SOC,
translation_key="battery_soc",
@@ -970,14 +948,6 @@ class IndevoltSensorEntity(IndevoltEntity, SensorEntity):
if energy_mode != self.entity_description.energy_mode:
return False
# Check whether the battery is not in the required charge/discharge state
if (
self.entity_description.charge_discharge_state is not None
and self.coordinator.data.get(IndevoltBattery.CHARGE_DISCHARGE_STATE)
!= self.entity_description.charge_discharge_state
):
return False
# Check whether inverter is reporting 0 degrees with heater not active (thus reporting to indicate "idle")
# Pending fix by Indevolt: https://discord.com/channels/1417471269942591571/1510277757689659522
if self.entity_description.key == IndevoltBattery.GEN_1_INVERTER_TEMPERATURE:
@@ -365,12 +365,6 @@
"realtime_target_soc": {
"name": "Real-time target SOC"
},
"remaining_charging_time": {
"name": "Remaining charging time"
},
"remaining_discharging_time": {
"name": "Remaining discharging time"
},
"serial_number": {
"name": "Serial number"
},
+6 -4
View File
@@ -12,9 +12,11 @@ from homeassistant.components.sensor import (
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_PARTS_PER_BILLION,
CONCENTRATION_PARTS_PER_MILLION,
LIGHT_LUX,
PERCENTAGE,
UnitOfPressure,
UnitOfRatio,
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant
@@ -30,7 +32,7 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
SensorType.AIR_HUMIDITY: SensorEntityDescription(
key="air_humidity",
device_class=SensorDeviceClass.HUMIDITY,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorType.AIR_PRESSURE: SensorEntityDescription(
@@ -47,7 +49,7 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
SensorType.ECO2: SensorEntityDescription(
key="eco2",
device_class=SensorDeviceClass.CO2,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
state_class=SensorStateClass.MEASUREMENT,
),
SensorType.LIGHT: SensorEntityDescription(
@@ -65,7 +67,7 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
SensorType.VOC: SensorEntityDescription(
key="voc",
device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS_PARTS,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_BILLION,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION,
state_class=SensorStateClass.MEASUREMENT,
),
}
+21 -20
View File
@@ -22,19 +22,20 @@ from homeassistant.components.sensor import (
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_MILLION,
LIGHT_LUX,
PERCENTAGE,
REVOLUTIONS_PER_MINUTE,
SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
EntityCategory,
Platform,
UnitOfApparentPower,
UnitOfDensity,
UnitOfElectricCurrent,
UnitOfElectricPotential,
UnitOfEnergy,
UnitOfPower,
UnitOfPressure,
UnitOfRatio,
UnitOfReactivePower,
UnitOfTemperature,
UnitOfTime,
@@ -443,7 +444,7 @@ DISCOVERY_SCHEMAS = [
platform=Platform.SENSOR,
entity_description=MatterSensorEntityDescription(
key="HumiditySensor",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
device_to_ha=lambda x: x / HUMIDITY_SCALING_FACTOR,
state_class=SensorStateClass.MEASUREMENT,
@@ -458,7 +459,7 @@ DISCOVERY_SCHEMAS = [
platform=Platform.SENSOR,
entity_description=MatterSensorEntityDescription(
key="SoilMoistureSensor",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.MOISTURE,
state_class=SensorStateClass.MEASUREMENT,
),
@@ -483,7 +484,7 @@ DISCOVERY_SCHEMAS = [
platform=Platform.SENSOR,
entity_description=MatterSensorEntityDescription(
key="PowerSource",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
# value has double precision
@@ -624,7 +625,7 @@ DISCOVERY_SCHEMAS = [
entity_description=MatterSensorEntityDescription(
key="EveThermoValvePosition",
translation_key="valve_position",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
),
entity_class=MatterSensor,
required_attributes=(EveCluster.Attributes.ValvePosition,),
@@ -657,7 +658,7 @@ DISCOVERY_SCHEMAS = [
platform=Platform.SENSOR,
entity_description=MatterSensorEntityDescription(
key="CarbonDioxideSensor",
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
device_class=SensorDeviceClass.CO2,
state_class=SensorStateClass.MEASUREMENT,
),
@@ -670,7 +671,7 @@ DISCOVERY_SCHEMAS = [
platform=Platform.SENSOR,
entity_description=MatterSensorEntityDescription(
key="TotalVolatileOrganicCompoundsSensor",
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS_PARTS,
state_class=SensorStateClass.MEASUREMENT,
),
@@ -698,7 +699,7 @@ DISCOVERY_SCHEMAS = [
platform=Platform.SENSOR,
entity_description=MatterSensorEntityDescription(
key="PM1Sensor",
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM1,
state_class=SensorStateClass.MEASUREMENT,
),
@@ -711,7 +712,7 @@ DISCOVERY_SCHEMAS = [
platform=Platform.SENSOR,
entity_description=MatterSensorEntityDescription(
key="PM25Sensor",
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM25,
state_class=SensorStateClass.MEASUREMENT,
),
@@ -724,7 +725,7 @@ DISCOVERY_SCHEMAS = [
platform=Platform.SENSOR,
entity_description=MatterSensorEntityDescription(
key="PM10Sensor",
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM10,
state_class=SensorStateClass.MEASUREMENT,
),
@@ -749,7 +750,7 @@ DISCOVERY_SCHEMAS = [
platform=Platform.SENSOR,
entity_description=MatterSensorEntityDescription(
key="CarbonMonoxideSensor",
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
device_class=SensorDeviceClass.CO,
state_class=SensorStateClass.MEASUREMENT,
),
@@ -762,7 +763,7 @@ DISCOVERY_SCHEMAS = [
platform=Platform.SENSOR,
entity_description=MatterSensorEntityDescription(
key="NitrogenDioxideSensor",
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
device_class=SensorDeviceClass.NITROGEN_DIOXIDE,
state_class=SensorStateClass.MEASUREMENT,
),
@@ -775,7 +776,7 @@ DISCOVERY_SCHEMAS = [
platform=Platform.SENSOR,
entity_description=MatterSensorEntityDescription(
key="OzoneConcentrationSensor",
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
device_class=SensorDeviceClass.OZONE,
state_class=SensorStateClass.MEASUREMENT,
),
@@ -801,7 +802,7 @@ DISCOVERY_SCHEMAS = [
platform=Platform.SENSOR,
entity_description=MatterSensorEntityDescription(
key="HepaFilterCondition",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
translation_key="hepa_filter_condition",
),
@@ -812,7 +813,7 @@ DISCOVERY_SCHEMAS = [
platform=Platform.SENSOR,
entity_description=MatterSensorEntityDescription(
key="ActivatedCarbonFilterCondition",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
translation_key="activated_carbon_filter_condition",
),
@@ -1296,7 +1297,7 @@ DISCOVERY_SCHEMAS = [
entity_description=MatterSensorEntityDescription(
key="ThermostatPIHeatingDemand",
translation_key="pi_heating_demand",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
entity_category=EntityCategory.DIAGNOSTIC,
),
entity_class=MatterSensor,
@@ -1378,7 +1379,7 @@ DISCOVERY_SCHEMAS = [
entity_registry_enabled_default=False,
translation_key="window_covering_target_position",
device_to_ha=lambda x: round((10000 - x) / 100),
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
),
entity_class=MatterSensor,
required_attributes=(
@@ -1463,7 +1464,7 @@ DISCOVERY_SCHEMAS = [
entity_description=MatterSensorEntityDescription(
key="EnergyEvseStateOfCharge",
translation_key="evse_soc",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.BATTERY,
state_class=SensorStateClass.MEASUREMENT,
),
@@ -1487,7 +1488,7 @@ DISCOVERY_SCHEMAS = [
entity_description=MatterSensorEntityDescription(
key="WaterHeaterManagementTankPercentage",
translation_key="tank_percentage",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
entity_class=MatterSensor,
@@ -11,7 +11,6 @@ from homeassistant.components.sensor import (
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.typing import StateType
@@ -74,7 +73,6 @@ async def async_setup_entry(
class MealieStatisticSensors(MealieEntity, SensorEntity):
"""Defines a Mealie sensor."""
_attr_entity_category = EntityCategory.DIAGNOSTIC
entity_description: MealieStatisticsSensorEntityDescription
coordinator: MealieStatisticsCoordinator
@@ -0,0 +1,21 @@
"""Support for Mycroft AI."""
import voluptuous as vol
from homeassistant.const import CONF_HOST, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv, discovery
from homeassistant.helpers.typing import ConfigType
DOMAIN = "mycroft"
CONFIG_SCHEMA = vol.Schema(
{DOMAIN: vol.Schema({vol.Required(CONF_HOST): cv.string})}, extra=vol.ALLOW_EXTRA
)
def setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the Mycroft component."""
hass.data[DOMAIN] = config[DOMAIN][CONF_HOST]
discovery.load_platform(hass, Platform.NOTIFY, DOMAIN, {}, config)
return True
@@ -0,0 +1,11 @@
{
"domain": "mycroft",
"name": "Mycroft",
"codeowners": [],
"disabled": "Dependencies not compatible with the new pip resolver",
"documentation": "https://www.home-assistant.io/integrations/mycroft",
"iot_class": "local_push",
"loggers": ["mycroftapi"],
"quality_scale": "legacy",
"requirements": ["mycroftapi==2.0"]
}
@@ -0,0 +1,42 @@
"""Mycroft AI notification platform."""
import logging
from typing import Any, override
from mycroftapi import MycroftAPI
from homeassistant.components.notify import BaseNotificationService
from homeassistant.core import HomeAssistant
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import DOMAIN
_LOGGER = logging.getLogger(__name__)
def get_service(
hass: HomeAssistant,
config: ConfigType,
discovery_info: DiscoveryInfoType | None = None,
) -> MycroftNotificationService:
"""Get the Mycroft notification service."""
return MycroftNotificationService(hass.data[DOMAIN])
class MycroftNotificationService(BaseNotificationService):
"""The Mycroft Notification Service."""
def __init__(self, mycroft_ip: str) -> None:
"""Initialize the service."""
self.mycroft_ip = mycroft_ip
@override
def send_message(self, message: str = "", **kwargs: Any) -> None:
"""Send a message mycroft to speak on instance."""
text = message
mycroft = MycroftAPI(self.mycroft_ip)
if mycroft is not None:
mycroft.speak_text(text)
else:
_LOGGER.warning("Could not reach this instance of mycroft")
+17 -16
View File
@@ -16,12 +16,13 @@ from homeassistant.components.sensor import (
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_MILLION,
LIGHT_LUX,
PERCENTAGE,
SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
EntityCategory,
UnitOfDensity,
UnitOfPressure,
UnitOfRatio,
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant
@@ -96,7 +97,7 @@ SENSORS: tuple[NAMSensorEntityDescription, ...] = (
key=ATTR_BME280_HUMIDITY,
translation_key="bme280_humidity",
suggested_display_precision=1,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
value=lambda sensors: sensors.bme280_humidity,
@@ -168,7 +169,7 @@ SENSORS: tuple[NAMSensorEntityDescription, ...] = (
key=ATTR_HECA_HUMIDITY,
translation_key="heca_humidity",
suggested_display_precision=1,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
value=lambda sensors: sensors.heca_humidity,
@@ -186,7 +187,7 @@ SENSORS: tuple[NAMSensorEntityDescription, ...] = (
key=ATTR_MHZ14A_CARBON_DIOXIDE,
translation_key="mhz14a_carbon_dioxide",
suggested_display_precision=0,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
device_class=SensorDeviceClass.CO2,
state_class=SensorStateClass.MEASUREMENT,
value=lambda sensors: sensors.mhz14a_carbon_dioxide,
@@ -207,7 +208,7 @@ SENSORS: tuple[NAMSensorEntityDescription, ...] = (
key=ATTR_PMSX003_P0,
translation_key="pmsx003_pm1",
suggested_display_precision=0,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM1,
state_class=SensorStateClass.MEASUREMENT,
value=lambda sensors: sensors.pms_p0,
@@ -216,7 +217,7 @@ SENSORS: tuple[NAMSensorEntityDescription, ...] = (
key=ATTR_PMSX003_P1,
translation_key="pmsx003_pm10",
suggested_display_precision=0,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM10,
state_class=SensorStateClass.MEASUREMENT,
value=lambda sensors: sensors.pms_p1,
@@ -225,7 +226,7 @@ SENSORS: tuple[NAMSensorEntityDescription, ...] = (
key=ATTR_PMSX003_P2,
translation_key="pmsx003_pm25",
suggested_display_precision=0,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM25,
state_class=SensorStateClass.MEASUREMENT,
value=lambda sensors: sensors.pms_p2,
@@ -246,7 +247,7 @@ SENSORS: tuple[NAMSensorEntityDescription, ...] = (
key=ATTR_SDS011_P1,
translation_key="sds011_pm10",
suggested_display_precision=0,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM10,
state_class=SensorStateClass.MEASUREMENT,
value=lambda sensors: sensors.sds011_p1,
@@ -255,7 +256,7 @@ SENSORS: tuple[NAMSensorEntityDescription, ...] = (
key=ATTR_SDS011_P2,
translation_key="sds011_pm25",
suggested_display_precision=0,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM25,
state_class=SensorStateClass.MEASUREMENT,
value=lambda sensors: sensors.sds011_p2,
@@ -264,7 +265,7 @@ SENSORS: tuple[NAMSensorEntityDescription, ...] = (
key=ATTR_SHT3X_HUMIDITY,
translation_key="sht3x_humidity",
suggested_display_precision=1,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
value=lambda sensors: sensors.sht3x_humidity,
@@ -294,7 +295,7 @@ SENSORS: tuple[NAMSensorEntityDescription, ...] = (
key=ATTR_SPS30_P0,
translation_key="sps30_pm1",
suggested_display_precision=0,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM1,
state_class=SensorStateClass.MEASUREMENT,
value=lambda sensors: sensors.sps30_p0,
@@ -303,7 +304,7 @@ SENSORS: tuple[NAMSensorEntityDescription, ...] = (
key=ATTR_SPS30_P1,
translation_key="sps30_pm10",
suggested_display_precision=0,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM10,
state_class=SensorStateClass.MEASUREMENT,
value=lambda sensors: sensors.sps30_p1,
@@ -312,7 +313,7 @@ SENSORS: tuple[NAMSensorEntityDescription, ...] = (
key=ATTR_SPS30_P2,
translation_key="sps30_pm25",
suggested_display_precision=0,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM25,
state_class=SensorStateClass.MEASUREMENT,
value=lambda sensors: sensors.sps30_p2,
@@ -321,7 +322,7 @@ SENSORS: tuple[NAMSensorEntityDescription, ...] = (
key=ATTR_SPS30_P4,
translation_key="sps30_pm4",
suggested_display_precision=0,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM4,
state_class=SensorStateClass.MEASUREMENT,
value=lambda sensors: sensors.sps30_p4,
@@ -330,7 +331,7 @@ SENSORS: tuple[NAMSensorEntityDescription, ...] = (
key=ATTR_DHT22_HUMIDITY,
translation_key="dht22_humidity",
suggested_display_precision=1,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
value=lambda sensors: sensors.dht22_humidity,
+5 -5
View File
@@ -165,7 +165,7 @@ class NexiaZone(NexiaThermostatZoneEntity, ClimateEntity):
self, coordinator: NexiaDataUpdateCoordinator, zone: NexiaThermostatZone
) -> None:
"""Initialize the thermostat."""
super().__init__(coordinator, zone, zone.zone_id) # type: ignore[arg-type] # until fix issue #139773
super().__init__(coordinator, zone, zone.zone_id)
thermostat = self._thermostat
unit = thermostat.get_unit()
min_humidity, max_humidity = thermostat.get_humidity_setpoint_limits()
@@ -198,7 +198,7 @@ class NexiaZone(NexiaThermostatZoneEntity, ClimateEntity):
@property
@override
def current_temperature(self) -> float:
def current_temperature(self) -> int:
"""Return the current temperature."""
return self._zone.get_temperature()
@@ -288,7 +288,7 @@ class NexiaZone(NexiaThermostatZoneEntity, ClimateEntity):
@property
@override
def target_temperature(self) -> float | None:
def target_temperature(self) -> int | None:
"""Temperature we try to reach."""
current_mode = self._zone.get_current_mode()
@@ -300,7 +300,7 @@ class NexiaZone(NexiaThermostatZoneEntity, ClimateEntity):
@property
@override
def target_temperature_high(self) -> float | None:
def target_temperature_high(self) -> int | None:
"""Highest temperature we are trying to reach."""
current_mode = self._zone.get_current_mode()
@@ -310,7 +310,7 @@ class NexiaZone(NexiaThermostatZoneEntity, ClimateEntity):
@property
@override
def target_temperature_low(self) -> float | None:
def target_temperature_low(self) -> int | None:
"""Lowest temperature we are trying to reach."""
current_mode = self._zone.get_current_mode()
@@ -40,6 +40,4 @@ class NexiaDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
@override
async def _async_update_data(self) -> dict[str, Any]:
"""Fetch data from API endpoint."""
update_data = await self.nexia_home.update() # can return None
return update_data or {}
return await self.nexia_home.update()
+3 -3
View File
@@ -56,7 +56,7 @@ class NexiaThermostatEntity(NexiaEntity):
thermostat_id = thermostat.thermostat_id
self._attr_device_info = DeviceInfo(
configuration_url=self.coordinator.nexia_home.root_url,
identifiers={(DOMAIN, thermostat_id)}, # type: ignore[arg-type] # until fix issue #139773
identifiers={(DOMAIN, thermostat_id)},
manufacturer=MANUFACTURER,
model=thermostat.get_model(),
name=thermostat.get_name(),
@@ -110,10 +110,10 @@ class NexiaThermostatZoneEntity(NexiaThermostatEntity):
if TYPE_CHECKING:
assert self._attr_device_info is not None
self._attr_device_info |= {
ATTR_IDENTIFIERS: {(DOMAIN, zone.zone_id)}, # type: ignore[arg-type] # until fix issue #139773
ATTR_IDENTIFIERS: {(DOMAIN, zone.zone_id)},
ATTR_NAME: zone_name,
ATTR_SUGGESTED_AREA: zone_name,
ATTR_VIA_DEVICE: (DOMAIN, zone.thermostat.thermostat_id), # type: ignore[typeddict-item] # until fix issue #139773
ATTR_VIA_DEVICE: (DOMAIN, zone.thermostat.thermostat_id),
}
self._zone_signal = f"{SIGNAL_ZONE_UPDATE}-{zone.zone_id}"
+1 -1
View File
@@ -13,5 +13,5 @@
"integration_type": "hub",
"iot_class": "cloud_polling",
"loggers": ["nexia"],
"requirements": ["nexia==2.13.0"]
"requirements": ["nexia==2.11.1"]
}
+1 -1
View File
@@ -42,7 +42,7 @@ class NexiaAutomationScene(NexiaEntity, Scene):
self, coordinator: NexiaDataUpdateCoordinator, automation: NexiaAutomation
) -> None:
"""Initialize the automation scene."""
super().__init__(coordinator, automation.automation_id) # type: ignore[arg-type] # until fix issue #139773
super().__init__(coordinator, automation.automation_id)
self._attr_name = automation.name
self._automation = automation
self._attr_extra_state_attributes = {ATTR_DESCRIPTION: automation.description}
+3 -3
View File
@@ -37,7 +37,7 @@ async def async_setup_entry(
coordinator = config_entry.runtime_data
nexia_home = coordinator.nexia_home
entities: list[SwitchEntity] = []
room_iq_zones: dict[str | int, NexiaRoomIQHarmonizer] = {}
room_iq_zones: dict[int, NexiaRoomIQHarmonizer] = {}
for thermostat_id in nexia_home.get_thermostat_ids():
thermostat: NexiaThermostat = nexia_home.get_thermostat_by_id(thermostat_id)
if thermostat.has_emergency_heat():
@@ -69,7 +69,7 @@ class NexiaHoldSwitch(NexiaThermostatZoneEntity, SwitchEntity):
) -> None:
"""Initialize the hold mode switch."""
zone_id = zone.zone_id
super().__init__(coordinator, zone, zone_id) # type: ignore[arg-type] # until fix issue #139773
super().__init__(coordinator, zone, zone_id)
@property
@override
@@ -103,7 +103,7 @@ class NexiaRoomIQSwitch(NexiaThermostatZoneEntity, SwitchEntity):
coordinator: NexiaDataUpdateCoordinator,
zone: NexiaThermostatZone,
sensor: NexiaSensor,
room_iq_zones: dict[str | int, NexiaRoomIQHarmonizer],
room_iq_zones: dict[int, NexiaRoomIQHarmonizer],
) -> None:
"""Initialize the RoomIQ sensor switch."""
super().__init__(coordinator, zone, f"{sensor.id}_room_iq_sensor")
+1 -3
View File
@@ -365,7 +365,5 @@ async def create_rexel_client(
gateway_id=entry.data[CONF_GATEWAY_ID],
),
session=async_create_clientsession(hass),
settings=OverkizClientSettings(
action_queue=ActionQueueSettings(), default_rts_command_duration=0
),
settings=OverkizClientSettings(action_queue=ActionQueueSettings()),
)
+7 -6
View File
@@ -15,12 +15,13 @@ from homeassistant.components.sensor import (
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_PARTS_PER_MILLION,
LIGHT_LUX,
PERCENTAGE,
SIGNAL_STRENGTH_DECIBELS,
EntityCategory,
UnitOfEnergy,
UnitOfPower,
UnitOfRatio,
UnitOfSpeed,
UnitOfTemperature,
UnitOfTime,
@@ -54,7 +55,7 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [
OverkizSensorDescription(
key=OverkizState.CORE_BATTERY_LEVEL,
name="Battery level",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.BATTERY,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
@@ -328,7 +329,7 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [
native_value=lambda value: round(cast(float, value), 2),
device_class=SensorDeviceClass.HUMIDITY,
# core:MeasuredValueType = core:RelativeValueInPercentage
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
# TemperatureSensor/TemperatureSensor
@@ -368,7 +369,7 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [
key=OverkizState.CORE_CO_CONCENTRATION,
name="CO concentration",
device_class=SensorDeviceClass.CO,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
state_class=SensorStateClass.MEASUREMENT,
),
# AirSensor/CO2Sensor
@@ -376,7 +377,7 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [
key=OverkizState.CORE_CO2_CONCENTRATION,
name="CO2 concentration",
device_class=SensorDeviceClass.CO2,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
state_class=SensorStateClass.MEASUREMENT,
),
# SunSensor/SunEnergySensor
@@ -488,7 +489,7 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [
OverkizSensorDescription(
key=OverkizState.CORE_TARGET_CLOSURE,
name="Target closure",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
entity_registry_enabled_default=False,
),
# ThreeWayWindowHandle/WindowHandle
+3 -3
View File
@@ -16,7 +16,7 @@ from homeassistant.components.sensor import (
SensorStateClass,
StateType,
)
from homeassistant.const import UnitOfInformation, UnitOfRatio
from homeassistant.const import PERCENTAGE, UnitOfInformation
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
@@ -122,7 +122,7 @@ CONTAINER_SENSORS: tuple[PortainerContainerSensorEntityDescription, ...] = (
and data.stats.memory_stats.usage > 0
else 0.0
),
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
entity_category=EntityCategory.DIAGNOSTIC,
suggested_display_precision=2,
state_class=SensorStateClass.MEASUREMENT,
@@ -151,7 +151,7 @@ CONTAINER_SENSORS: tuple[PortainerContainerSensorEntityDescription, ...] = (
and data.stats.cpu_stats.online_cpus > 0
else 0.0
),
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
entity_category=EntityCategory.DIAGNOSTIC,
suggested_display_precision=2,
state_class=SensorStateClass.MEASUREMENT,
+10 -4
View File
@@ -12,7 +12,13 @@ from homeassistant.components.sensor import (
SensorStateClass,
StateType,
)
from homeassistant.const import UnitOfPressure, UnitOfRatio, UnitOfTemperature
from homeassistant.const import (
CONCENTRATION_PARTS_PER_BILLION,
CONCENTRATION_PARTS_PER_MILLION,
PERCENTAGE,
UnitOfPressure,
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
@@ -48,13 +54,13 @@ ENTITIES: tuple[PranaSensorEntityDescription, ...] = (
PranaSensorEntityDescription(
key=PranaSensorType.HUMIDITY,
value_fn=lambda coord: coord.data.humidity,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
),
PranaSensorEntityDescription(
key=PranaSensorType.VOC,
value_fn=lambda coord: coord.data.voc,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_BILLION,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION,
device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS_PARTS,
),
PranaSensorEntityDescription(
@@ -66,7 +72,7 @@ ENTITIES: tuple[PranaSensorEntityDescription, ...] = (
PranaSensorEntityDescription(
key=PranaSensorType.CO2,
value_fn=lambda coord: coord.data.co2,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
device_class=SensorDeviceClass.CO2,
),
PranaSensorEntityDescription(
+6 -5
View File
@@ -22,14 +22,15 @@ from homeassistant.components.sensor import (
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_PARTS_PER_MILLION,
LIGHT_LUX,
PERCENTAGE,
UnitOfElectricCurrent,
UnitOfElectricPotential,
UnitOfEnergy,
UnitOfLength,
UnitOfPower,
UnitOfPressure,
UnitOfRatio,
UnitOfSoundPressure,
UnitOfSpeed,
UnitOfTemperature,
@@ -125,7 +126,7 @@ _GAUGE_VARIANT_DESCRIPTIONS = {
"AIRQUALITY": SensorEntityDescription(
key="airquality",
device_class=SensorDeviceClass.CO2,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
state_class=SensorStateClass.MEASUREMENT,
),
"CURRENT": SensorEntityDescription(
@@ -155,7 +156,7 @@ _GAUGE_VARIANT_DESCRIPTIONS = {
"HUMIDITY": SensorEntityDescription(
key="humidity",
device_class=SensorDeviceClass.HUMIDITY,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
"LIGHT": SensorEntityDescription(
@@ -352,7 +353,7 @@ class QbusHumiditySensor(QbusEntity, SensorEntity):
_attr_device_class = SensorDeviceClass.HUMIDITY
_attr_name = None
_attr_native_unit_of_measurement = UnitOfRatio.PERCENTAGE
_attr_native_unit_of_measurement = PERCENTAGE
_attr_state_class = SensorStateClass.MEASUREMENT
@override
@@ -381,7 +382,7 @@ class QbusVentilationSensor(QbusEntity, SensorEntity):
_attr_device_class = SensorDeviceClass.CO2
_attr_name = None
_attr_native_unit_of_measurement = UnitOfRatio.PARTS_PER_MILLION
_attr_native_unit_of_measurement = CONCENTRATION_PARTS_PER_MILLION
_attr_state_class = SensorStateClass.MEASUREMENT
_attr_suggested_display_precision = 0
@@ -30,7 +30,6 @@ from homeassistant.util.event_type import EventType
# startup
from . import (
backup, # noqa: F401
entity_options,
entity_registry,
websocket_api,
)
@@ -43,7 +42,6 @@ from .const import ( # noqa: F401
SupportedDialect,
)
from .core import Recorder
from .entity_options import is_entity_recorded # noqa: F401
from .services import async_setup_services
from .tasks import AddRecorderPlatformTask
from .util import get_instance
@@ -127,6 +125,15 @@ CONFIG_SCHEMA = vol.Schema(
)
def is_entity_recorded(hass: HomeAssistant, entity_id: str) -> bool:
"""Check if an entity is being recorded.
Async friendly.
"""
instance = get_instance(hass)
return instance.entity_filter is None or instance.entity_filter(entity_id)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the recorder."""
conf = config[DOMAIN]
@@ -160,7 +167,6 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
)
get_instance.cache_clear()
entity_registry.async_setup(hass)
entity_options.async_setup(hass)
instance.async_initialize()
instance.async_register()
instance.start()
@@ -1,68 +0,0 @@
"""Control recorder entity options."""
import dataclasses
from enum import StrEnum
from typing import Any
import voluptuous as vol
from homeassistant.components import websocket_api
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import config_validation as cv
from .util import get_instance
def is_entity_recorded(hass: HomeAssistant, entity_id: str) -> bool:
"""Check if an entity is being recorded.
Async friendly.
"""
instance = get_instance(hass)
return instance.entity_filter is None or instance.entity_filter(entity_id)
@callback
def async_setup(hass: HomeAssistant) -> None:
"""Set up the recorder entity options."""
websocket_api.async_register_command(hass, ws_get_entity_options)
class EntityRecordingDisabler(StrEnum):
"""What disabled recording of an entity."""
USER = "user"
@dataclasses.dataclass(frozen=True)
class RecorderEntityOptions:
"""Recorder options for an entity."""
recording_disabled_by: EntityRecordingDisabler | None = None
def to_json(self) -> dict[str, Any]:
"""Return a JSON serializable representation for storage."""
return {
"recording_disabled_by": self.recording_disabled_by,
}
@websocket_api.require_admin
@websocket_api.websocket_command(
{
vol.Required("type"): "recorder/entity_options/get",
vol.Required("entity_id"): cv.strict_entity_id,
}
)
@callback
def ws_get_entity_options(
hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any]
) -> None:
"""Get recorder settings for a single entity."""
entity_id: str = msg["entity_id"]
recording_disabled = (
None if is_entity_recorded(hass, entity_id) else EntityRecordingDisabler.USER
)
options = RecorderEntityOptions(recording_disabled_by=recording_disabled)
connection.send_result(msg["id"], options.to_json())
@@ -321,7 +321,6 @@ SENSOR_TYPES: tuple[RenaultSensorEntityDescription[Any], ...] = (
options=[
"always",
"delayed",
"delegated",
"scheduled",
],
value_lambda=_get_charging_settings_mode_formatted,
@@ -157,7 +157,6 @@
"state": {
"always": "Always",
"delayed": "Delayed",
"delegated": "Delegated",
"scheduled": "Scheduled"
}
},
+2 -6
View File
@@ -286,10 +286,7 @@ async def register_callbacks(
return async_camera_wake
host.api.baichuan.register_callback(
"privacy_mode_change_623", async_privacy_mode_change, 623
)
host.api.baichuan.register_callback(
"privacy_mode_change_574", async_privacy_mode_change, 574
"privacy_mode_change", async_privacy_mode_change, 623
)
for channel in host.api.channels:
if host.api.supported(channel, "battery"):
@@ -309,8 +306,7 @@ async def async_unload_entry(
await host.stop()
host.api.baichuan.unregister_callback("privacy_mode_change_623")
host.api.baichuan.unregister_callback("privacy_mode_change_574")
host.api.baichuan.unregister_callback("privacy_mode_change")
for channel in host.api.channels:
if host.api.supported(channel, "battery"):
host.api.baichuan.unregister_callback(f"camera_{channel}_wake")
@@ -75,7 +75,6 @@ LIGHT_ENTITIES = (
ReolinkLightEntityDescription(
key="status_led",
cmd_key="GetPowerLed",
cmd_id=208,
translation_key="status_led",
entity_category=EntityCategory.CONFIG,
supported=lambda api, ch: api.supported(ch, "power_led"),
@@ -20,5 +20,5 @@
"iot_class": "local_push",
"loggers": ["reolink_aio"],
"quality_scale": "platinum",
"requirements": ["reolink-aio==0.21.3"]
"requirements": ["reolink-aio==0.21.2"]
}
@@ -195,7 +195,6 @@ NUMBER_ENTITIES = (
key="volume",
cmd_key="GetAudioCfg",
translation_key="volume",
cmd_id=264,
entity_category=EntityCategory.CONFIG,
native_step=1,
native_min_value=0,
@@ -207,7 +206,6 @@ NUMBER_ENTITIES = (
ReolinkNumberEntityDescription(
key="volume_speak",
cmd_key="GetAudioCfg",
cmd_id=264,
translation_key="volume_speak",
entity_category=EntityCategory.CONFIG,
native_step=1,
@@ -220,7 +218,6 @@ NUMBER_ENTITIES = (
ReolinkNumberEntityDescription(
key="volume_doorbell",
cmd_key="GetAudioCfg",
cmd_id=264,
translation_key="volume_doorbell",
entity_category=EntityCategory.CONFIG,
native_step=1,
@@ -272,7 +269,6 @@ NUMBER_ENTITIES = (
ReolinkNumberEntityDescription(
key="pir_sensitivity",
cmd_key="GetPirInfo",
cmd_id=212,
translation_key="pir_sensitivity",
entity_category=EntityCategory.CONFIG,
native_step=1,
@@ -285,7 +281,6 @@ NUMBER_ENTITIES = (
ReolinkNumberEntityDescription(
key="pir_interval",
cmd_key="GetPirInfo",
cmd_id=212,
translation_key="pir_interval",
entity_category=EntityCategory.CONFIG,
device_class=NumberDeviceClass.DURATION,
@@ -301,7 +296,6 @@ NUMBER_ENTITIES = (
ReolinkNumberEntityDescription(
key="ai_face_sensititvity",
cmd_key="GetAiAlarm",
cmd_id=342,
translation_key="ai_face_sensitivity",
entity_category=EntityCategory.CONFIG,
native_step=1,
@@ -316,7 +310,6 @@ NUMBER_ENTITIES = (
ReolinkNumberEntityDescription(
key="ai_person_sensititvity",
cmd_key="GetAiAlarm",
cmd_id=342,
translation_key="ai_person_sensitivity",
entity_category=EntityCategory.CONFIG,
native_step=1,
@@ -331,7 +324,6 @@ NUMBER_ENTITIES = (
ReolinkNumberEntityDescription(
key="ai_vehicle_sensititvity",
cmd_key="GetAiAlarm",
cmd_id=342,
translation_key="ai_vehicle_sensitivity",
entity_category=EntityCategory.CONFIG,
native_step=1,
@@ -346,7 +338,6 @@ NUMBER_ENTITIES = (
ReolinkNumberEntityDescription(
key="ai_non_motor_vehicle_sensitivity",
cmd_key="GetAiAlarm",
cmd_id=342,
translation_key="ai_non_motor_vehicle_sensitivity",
entity_category=EntityCategory.CONFIG,
native_step=1,
@@ -364,7 +355,6 @@ NUMBER_ENTITIES = (
ReolinkNumberEntityDescription(
key="ai_package_sensititvity",
cmd_key="GetAiAlarm",
cmd_id=342,
translation_key="ai_package_sensitivity",
entity_category=EntityCategory.CONFIG,
native_step=1,
@@ -379,7 +369,6 @@ NUMBER_ENTITIES = (
ReolinkNumberEntityDescription(
key="ai_pet_sensititvity",
cmd_key="GetAiAlarm",
cmd_id=342,
translation_key="ai_pet_sensitivity",
entity_category=EntityCategory.CONFIG,
native_step=1,
@@ -396,7 +385,6 @@ NUMBER_ENTITIES = (
ReolinkNumberEntityDescription(
key="ai_pet_sensititvity",
cmd_key="GetAiAlarm",
cmd_id=342,
translation_key="ai_animal_sensitivity",
entity_category=EntityCategory.CONFIG,
native_step=1,
@@ -423,7 +411,6 @@ NUMBER_ENTITIES = (
ReolinkNumberEntityDescription(
key="ai_face_delay",
cmd_key="GetAiAlarm",
cmd_id=342,
translation_key="ai_face_delay",
entity_category=EntityCategory.CONFIG,
device_class=NumberDeviceClass.DURATION,
@@ -441,7 +428,6 @@ NUMBER_ENTITIES = (
ReolinkNumberEntityDescription(
key="ai_person_delay",
cmd_key="GetAiAlarm",
cmd_id=342,
translation_key="ai_person_delay",
entity_category=EntityCategory.CONFIG,
device_class=NumberDeviceClass.DURATION,
@@ -459,7 +445,6 @@ NUMBER_ENTITIES = (
ReolinkNumberEntityDescription(
key="ai_non_motor_vehicle_delay",
cmd_key="GetAiAlarm",
cmd_id=342,
translation_key="ai_non_motor_vehicle_delay",
entity_category=EntityCategory.CONFIG,
device_class=NumberDeviceClass.DURATION,
@@ -479,7 +464,6 @@ NUMBER_ENTITIES = (
ReolinkNumberEntityDescription(
key="ai_vehicle_delay",
cmd_key="GetAiAlarm",
cmd_id=342,
translation_key="ai_vehicle_delay",
entity_category=EntityCategory.CONFIG,
device_class=NumberDeviceClass.DURATION,
@@ -497,7 +481,6 @@ NUMBER_ENTITIES = (
ReolinkNumberEntityDescription(
key="ai_package_delay",
cmd_key="GetAiAlarm",
cmd_id=342,
translation_key="ai_package_delay",
entity_category=EntityCategory.CONFIG,
device_class=NumberDeviceClass.DURATION,
@@ -515,7 +498,6 @@ NUMBER_ENTITIES = (
ReolinkNumberEntityDescription(
key="ai_pet_delay",
cmd_key="GetAiAlarm",
cmd_id=342,
translation_key="ai_pet_delay",
entity_category=EntityCategory.CONFIG,
device_class=NumberDeviceClass.DURATION,
@@ -535,7 +517,6 @@ NUMBER_ENTITIES = (
ReolinkNumberEntityDescription(
key="ai_pet_delay",
cmd_key="GetAiAlarm",
cmd_id=342,
translation_key="ai_animal_delay",
entity_category=EntityCategory.CONFIG,
device_class=NumberDeviceClass.DURATION,
@@ -185,7 +185,6 @@ SELECT_ENTITIES = (
ReolinkSelectEntityDescription(
key="status_led",
cmd_key="GetPowerLed",
cmd_id=208,
translation_key="doorbell_led",
entity_category=EntityCategory.CONFIG,
get_options=lambda api, ch: api.doorbell_led_list(ch),
@@ -233,7 +232,6 @@ SELECT_ENTITIES = (
ReolinkSelectEntityDescription(
key="main_frame_rate",
cmd_key="GetEnc",
cmd_id=56,
translation_key="main_frame_rate",
entity_category=EntityCategory.CONFIG,
entity_registry_enabled_default=False,
@@ -246,7 +244,6 @@ SELECT_ENTITIES = (
ReolinkSelectEntityDescription(
key="sub_frame_rate",
cmd_key="GetEnc",
cmd_id=56,
translation_key="sub_frame_rate",
entity_category=EntityCategory.CONFIG,
entity_registry_enabled_default=False,
@@ -259,7 +256,6 @@ SELECT_ENTITIES = (
ReolinkSelectEntityDescription(
key="main_bit_rate",
cmd_key="GetEnc",
cmd_id=56,
translation_key="main_bit_rate",
entity_category=EntityCategory.CONFIG,
entity_registry_enabled_default=False,
@@ -272,7 +268,6 @@ SELECT_ENTITIES = (
ReolinkSelectEntityDescription(
key="sub_bit_rate",
cmd_key="GetEnc",
cmd_id=56,
translation_key="sub_bit_rate",
entity_category=EntityCategory.CONFIG,
entity_registry_enabled_default=False,
@@ -285,7 +280,6 @@ SELECT_ENTITIES = (
ReolinkSelectEntityDescription(
key="main_encoding",
cmd_key="GetEnc",
cmd_id=56,
translation_key="main_encoding",
entity_category=EntityCategory.CONFIG,
entity_registry_enabled_default=False,
@@ -297,7 +291,6 @@ SELECT_ENTITIES = (
ReolinkSelectEntityDescription(
key="sub_encoding",
cmd_key="GetEnc",
cmd_id=56,
translation_key="sub_encoding",
entity_category=EntityCategory.CONFIG,
entity_registry_enabled_default=False,
@@ -323,7 +316,6 @@ SELECT_ENTITIES = (
ReolinkSelectEntityDescription(
key="post_rec_time",
cmd_key="GetRec",
cmd_id=54,
translation_key="post_rec_time",
entity_category=EntityCategory.CONFIG,
entity_registry_enabled_default=False,
@@ -348,7 +340,6 @@ HOST_SELECT_ENTITIES = (
ReolinkHostSelectEntityDescription(
key="packing_time",
cmd_key="GetRec",
cmd_id=54,
translation_key="packing_time",
entity_category=EntityCategory.CONFIG,
entity_registry_enabled_default=False,
@@ -74,7 +74,6 @@ SWITCH_ENTITIES = (
ReolinkSwitchEntityDescription(
key="ir_lights",
cmd_key="GetIrLights",
cmd_id=208,
translation_key="ir_lights",
entity_category=EntityCategory.CONFIG,
supported=lambda api, ch: api.supported(ch, "ir_lights"),
@@ -84,7 +83,6 @@ SWITCH_ENTITIES = (
ReolinkSwitchEntityDescription(
key="record_audio",
cmd_key="GetEnc",
cmd_id=56,
translation_key="record_audio",
entity_category=EntityCategory.CONFIG,
supported=lambda api, ch: api.supported(ch, "audio"),
@@ -94,7 +92,6 @@ SWITCH_ENTITIES = (
ReolinkSwitchEntityDescription(
key="siren_on_event",
cmd_key="GetAudioAlarm",
cmd_id=232,
translation_key="siren_on_event",
entity_category=EntityCategory.CONFIG,
supported=lambda api, ch: api.supported(ch, "siren"),
@@ -139,7 +136,6 @@ SWITCH_ENTITIES = (
ReolinkSwitchEntityDescription(
key="email",
cmd_key="GetEmail",
cmd_id=217,
translation_key="email",
entity_category=EntityCategory.CONFIG,
supported=lambda api, ch: api.supported(ch, "email") and api.is_nvr,
@@ -149,7 +145,6 @@ SWITCH_ENTITIES = (
ReolinkSwitchEntityDescription(
key="ftp_upload",
cmd_key="GetFtp",
cmd_id=70,
translation_key="ftp_upload",
entity_category=EntityCategory.CONFIG,
supported=lambda api, ch: api.supported(ch, "ftp") and api.is_nvr,
@@ -168,7 +163,6 @@ SWITCH_ENTITIES = (
ReolinkSwitchEntityDescription(
key="record",
cmd_key="GetRec",
cmd_id=81,
translation_key="record",
entity_category=EntityCategory.CONFIG,
supported=lambda api, ch: api.supported(ch, "rec_enable") and api.is_nvr,
@@ -206,7 +200,6 @@ SWITCH_ENTITIES = (
ReolinkSwitchEntityDescription(
key="doorbell_button_sound",
cmd_key="GetAudioCfg",
cmd_id=264,
translation_key="doorbell_button_sound",
entity_category=EntityCategory.CONFIG,
supported=lambda api, ch: api.supported(ch, "doorbell_button_sound"),
@@ -216,7 +209,6 @@ SWITCH_ENTITIES = (
ReolinkSwitchEntityDescription(
key="pir_enabled",
cmd_key="GetPirInfo",
cmd_id=212,
translation_key="pir_enabled",
entity_category=EntityCategory.CONFIG,
entity_registry_enabled_default=False,
@@ -227,7 +219,6 @@ SWITCH_ENTITIES = (
ReolinkSwitchEntityDescription(
key="pir_reduce_alarm",
cmd_key="GetPirInfo",
cmd_id=212,
translation_key="pir_reduce_alarm",
entity_category=EntityCategory.CONFIG,
entity_registry_enabled_default=False,
@@ -269,7 +260,6 @@ HOST_SWITCH_ENTITIES = (
ReolinkHostSwitchEntityDescription(
key="email",
cmd_key="GetEmail",
cmd_id=217,
translation_key="email",
entity_category=EntityCategory.CONFIG,
supported=lambda api: api.supported(None, "email") and not api.is_hub,
@@ -279,7 +269,6 @@ HOST_SWITCH_ENTITIES = (
ReolinkHostSwitchEntityDescription(
key="ftp_upload",
cmd_key="GetFtp",
cmd_id=70,
translation_key="ftp_upload",
entity_category=EntityCategory.CONFIG,
supported=lambda api: api.supported(None, "ftp") and not api.is_hub,
@@ -298,7 +287,6 @@ HOST_SWITCH_ENTITIES = (
ReolinkHostSwitchEntityDescription(
key="record",
cmd_key="GetRec",
cmd_id=81,
translation_key="record",
entity_category=EntityCategory.CONFIG,
supported=lambda api: api.supported(None, "rec_enable") and not api.is_hub,
+4 -3
View File
@@ -15,12 +15,14 @@ from homeassistant.components.sensor import (
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_PARTS_PER_MILLION,
CONF_DEVICES,
CONF_NAME,
CONF_SENSOR_TYPE,
CONF_UNIT_OF_MEASUREMENT,
DEGREE,
LIGHT_LUX,
PERCENTAGE,
UV_INDEX,
UnitOfElectricCurrent,
UnitOfElectricPotential,
@@ -28,7 +30,6 @@ from homeassistant.const import (
UnitOfPower,
UnitOfPrecipitationDepth,
UnitOfPressure,
UnitOfRatio,
UnitOfSpeed,
UnitOfTemperature,
UnitOfVolumetricFlux,
@@ -84,7 +85,7 @@ SENSOR_TYPES = (
name="CO2 air quality",
device_class=SensorDeviceClass.CO2,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
),
SensorEntityDescription(
key="command",
@@ -139,7 +140,7 @@ SENSOR_TYPES = (
name="Humidity",
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
),
SensorEntityDescription(
key="humidity_status",
@@ -20,7 +20,7 @@
"loggers": ["roborock"],
"quality_scale": "silver",
"requirements": [
"python-roborock==5.21.0",
"python-roborock==5.14.2",
"vacuum-map-parser-roborock==0.1.5"
]
}
+2 -4
View File
@@ -35,7 +35,7 @@ class RoborockNumberDescription(NumberEntityDescription):
trait: Callable[[PropertiesApi], Any | None]
"""Function to determine if number entity is supported by the device."""
get_value: Callable[[Any], float | None]
get_value: Callable[[Any], float]
"""Function to get the value from the trait."""
set_value: Callable[[Any, float], Coroutine[Any, Any, None]]
@@ -51,9 +51,7 @@ NUMBER_DESCRIPTIONS: list[RoborockNumberDescription] = [
native_unit_of_measurement=PERCENTAGE,
entity_category=EntityCategory.CONFIG,
trait=lambda api: api.sound_volume,
get_value=lambda trait: (
float(trait.volume) if trait.volume is not None else None
),
get_value=lambda trait: float(trait.volume),
set_value=lambda trait, value: trait.set_volume(int(value)),
)
]
+13 -12
View File
@@ -30,13 +30,6 @@ _LOGGER = logging.getLogger(__name__)
PARALLEL_UPDATES = 0
def _build_time(hour: int | None, minute: int | None) -> datetime.time | None:
"""Build a time, returning None when either component is missing."""
if hour is None or minute is None:
return None
return datetime.time(hour=hour, minute=minute)
@dataclass(frozen=True, kw_only=True)
class RoborockTimeDescription(TimeEntityDescription):
"""Class to describe a Roborock time entity."""
@@ -44,7 +37,7 @@ class RoborockTimeDescription(TimeEntityDescription):
trait: Callable[[Any], Any | None]
"""Function to determine if time entity is supported by the device."""
get_value: Callable[[Any], datetime.time | None]
get_value: Callable[[Any], datetime.time]
"""Function to get the value from the trait."""
update_value: Callable[[Any, datetime.time], Coroutine[Any, Any, None]]
@@ -65,7 +58,9 @@ TIME_DESCRIPTIONS: list[RoborockTimeDescription] = [
end_minute=trait.end_minute,
)
),
get_value=lambda trait: _build_time(trait.start_hour, trait.start_minute),
get_value=lambda trait: datetime.time(
hour=trait.start_hour, minute=trait.start_minute
),
entity_category=EntityCategory.CONFIG,
),
RoborockTimeDescription(
@@ -81,7 +76,9 @@ TIME_DESCRIPTIONS: list[RoborockTimeDescription] = [
end_minute=desired_time.minute,
)
),
get_value=lambda trait: _build_time(trait.end_hour, trait.end_minute),
get_value=lambda trait: datetime.time(
hour=trait.end_hour, minute=trait.end_minute
),
entity_category=EntityCategory.CONFIG,
),
RoborockTimeDescription(
@@ -97,7 +94,9 @@ TIME_DESCRIPTIONS: list[RoborockTimeDescription] = [
end_minute=trait.end_minute,
)
),
get_value=lambda trait: _build_time(trait.start_hour, trait.start_minute),
get_value=lambda trait: datetime.time(
hour=trait.start_hour, minute=trait.start_minute
),
entity_category=EntityCategory.CONFIG,
entity_registry_enabled_default=False,
),
@@ -114,7 +113,9 @@ TIME_DESCRIPTIONS: list[RoborockTimeDescription] = [
end_minute=desired_time.minute,
)
),
get_value=lambda trait: _build_time(trait.end_hour, trait.end_minute),
get_value=lambda trait: datetime.time(
hour=trait.end_hour, minute=trait.end_minute
),
entity_category=EntityCategory.CONFIG,
entity_registry_enabled_default=False,
),
+5 -5
View File
@@ -17,7 +17,7 @@ from homeassistant.components.number import (
NumberMode,
RestoreNumber,
)
from homeassistant.const import EntityCategory, UnitOfRatio, UnitOfTemperature
from homeassistant.const import PERCENTAGE, EntityCategory, UnitOfTemperature
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
@@ -183,7 +183,7 @@ BLOCK_NUMBERS: dict[tuple[str, str], BlockNumberDescription] = {
("device", "valvePos"): BlockNumberDescription(
key="device|valvepos",
translation_key="valve_position",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
available=lambda block: cast(int, block.valveError) != 1,
entity_category=EntityCategory.CONFIG,
native_min_value=0,
@@ -291,7 +291,7 @@ RPC_NUMBERS: Final = {
native_max_value=100,
native_step=1,
mode=NumberMode.SLIDER,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
method="blu_trv_set_valve_position",
removal_condition=lambda config, _, key: (
config[key].get("enable", True) is True
@@ -307,7 +307,7 @@ RPC_NUMBERS: Final = {
native_max_value=100,
native_step=1,
mode=NumberMode.SLIDER,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
method="cury_set",
slot="left",
available=lambda status: (
@@ -325,7 +325,7 @@ RPC_NUMBERS: Final = {
native_max_value=100,
native_step=1,
mode=NumberMode.SLIDER,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
method="cury_set",
slot="right",
available=lambda status: (
+14 -13
View File
@@ -17,8 +17,10 @@ from homeassistant.components.sensor import (
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_PARTS_PER_MILLION,
DEGREE,
LIGHT_LUX,
PERCENTAGE,
SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
EntityCategory,
UnitOfApparentPower,
@@ -28,7 +30,6 @@ from homeassistant.const import (
UnitOfFrequency,
UnitOfPower,
UnitOfPressure,
UnitOfRatio,
UnitOfTemperature,
UnitOfTime,
UnitOfVolume,
@@ -202,7 +203,7 @@ class RpcBluTrvSensor(RpcSensor):
BLOCK_SENSORS: dict[tuple[str, str], BlockSensorDescription] = {
("device", "battery"): BlockSensorDescription(
key="device|battery",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.BATTERY,
state_class=SensorStateClass.MEASUREMENT,
removal_condition=lambda settings, _: settings.get("external_power") == 1,
@@ -349,7 +350,7 @@ BLOCK_SENSORS: dict[tuple[str, str], BlockSensorDescription] = {
("sensor", "concentration"): BlockSensorDescription(
key="sensor|concentration",
translation_key="gas_concentration",
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
state_class=SensorStateClass.MEASUREMENT,
),
("sensor", "temp"): BlockSensorDescription(
@@ -372,7 +373,7 @@ BLOCK_SENSORS: dict[tuple[str, str], BlockSensorDescription] = {
),
("sensor", "humidity"): BlockSensorDescription(
key="sensor|humidity",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
suggested_display_precision=1,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
@@ -397,7 +398,7 @@ BLOCK_SENSORS: dict[tuple[str, str], BlockSensorDescription] = {
("relay", "totalWorkTime"): BlockSensorDescription(
key="relay|totalWorkTime",
translation_key="lamp_life",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
value=get_shelly_air_lamp_life,
suggested_display_precision=1,
entity_category=EntityCategory.DIAGNOSTIC,
@@ -1254,7 +1255,7 @@ RPC_SENSORS: Final = {
"humidity_rh": RpcSensorDescription(
key="humidity",
sub_key="rh",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
suggested_display_precision=1,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
@@ -1265,7 +1266,7 @@ RPC_SENSORS: Final = {
"battery": RpcSensorDescription(
key="devicepower",
sub_key="battery",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
value=lambda status, _: status["percent"],
device_class=SensorDeviceClass.BATTERY,
state_class=SensorStateClass.MEASUREMENT,
@@ -1295,7 +1296,7 @@ RPC_SENSORS: Final = {
key="input",
sub_key="percent",
translation_key="analog",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
removal_condition=lambda config, _, key: (
config[key]["type"] != "analog" or config[key]["enable"] is False
@@ -1388,7 +1389,7 @@ RPC_SENSORS: Final = {
key="blutrv",
sub_key="pos",
translation_key="valve_position",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
removal_condition=lambda config, _, key: (
@@ -1399,7 +1400,7 @@ RPC_SENSORS: Final = {
"blutrv_battery": RpcSensorDescription(
key="blutrv",
sub_key="battery",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.BATTERY,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
@@ -1447,7 +1448,7 @@ RPC_SENSORS: Final = {
"number_current_humidity": RpcSensorDescription(
key="number",
sub_key="value",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
suggested_display_precision=1,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
@@ -1685,7 +1686,7 @@ RPC_SENSORS: Final = {
translation_key="left_slot_level",
value=lambda status, _: status["left"]["vial"]["level"],
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
entity_category=EntityCategory.DIAGNOSTIC,
available=lambda status: (
(left := status["left"]) is not None
@@ -1709,7 +1710,7 @@ RPC_SENSORS: Final = {
translation_key="right_slot_level",
value=lambda status, _: status["right"]["vial"]["level"],
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
entity_category=EntityCategory.DIAGNOSTIC,
available=lambda status: (
(right := status["right"]) is not None
@@ -1,8 +1,7 @@
"""The Steam integration."""
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import entity_registry as er
from homeassistant.core import HomeAssistant
from .coordinator import SteamConfigEntry, SteamDataUpdateCoordinator
@@ -22,22 +21,3 @@ async def async_setup_entry(hass: HomeAssistant, entry: SteamConfigEntry) -> boo
async def async_unload_entry(hass: HomeAssistant, entry: SteamConfigEntry) -> bool:
"""Unload a config entry."""
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
async def async_migrate_entry(hass: HomeAssistant, entry: SteamConfigEntry) -> bool:
"""Migrate old entry."""
if entry.version < 2:
# Migrate entity unique id
@callback
def migrate_unique_id(entity_entry: er.RegistryEntry) -> dict[str, str] | None:
if entity_entry.unique_id.startswith("sensor.steam_"):
new = entity_entry.unique_id.removeprefix("sensor.steam_") + "_account"
return {"new_unique_id": new}
return None
await er.async_migrate_entries(hass, entry.entry_id, migrate_unique_id)
hass.config_entries.async_update_entry(entry, version=2)
return True
@@ -41,8 +41,6 @@ def validate_input(user_input: dict[str, str]) -> dict[str, str | int]:
class SteamFlowHandler(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Steam."""
VERSION = 2
@staticmethod
@callback
@override
@@ -152,7 +150,7 @@ class SteamOptionsFlowHandler(OptionsFlowWithReload):
for _id in self.options[CONF_ACCOUNTS]:
if _id not in user_input[CONF_ACCOUNTS] and (
entity_id := er.async_get(self.hass).async_get_entity_id(
Platform.SENSOR, DOMAIN, f"{_id}_account"
Platform.SENSOR, DOMAIN, f"sensor.steam_{_id}"
)
):
er.async_get(self.hass).async_remove(entity_id)
@@ -1,6 +1,5 @@
"""Entity classes for the Steam integration."""
from homeassistant.components.sensor import SensorEntityDescription
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity
@@ -13,17 +12,9 @@ class SteamEntity(CoordinatorEntity[SteamDataUpdateCoordinator]):
_attr_has_entity_name = True
def __init__(
self,
coordinator: SteamDataUpdateCoordinator,
steamid: str,
description: SensorEntityDescription,
) -> None:
def __init__(self, coordinator: SteamDataUpdateCoordinator) -> None:
"""Initialize a Steam entity."""
super().__init__(coordinator)
self._steamid = steamid
self.entity_description = description
self._attr_unique_id = f"{steamid}_{description.key}"
self._attr_device_info = DeviceInfo(
configuration_url="https://store.steampowered.com",
entry_type=DeviceEntryType.SERVICE,
@@ -80,7 +80,10 @@ class SteamSensorEntity(SteamEntity, SensorEntity):
description: SteamSensorEntityDescription,
) -> None:
"""Initialize the sensor."""
super().__init__(coordinator, steamid, description)
super().__init__(coordinator)
self._steamid = steamid
self.entity_description = description
self._attr_unique_id = f"sensor.steam_{steamid}"
self._attr_name = self.entity_description.name_fn(coordinator.data[steamid])
@property
+20 -14
View File
@@ -85,18 +85,34 @@ def sun(
has_sunrise_condition = SUN_EVENT_SUNRISE in (before, after)
has_sunset_condition = SUN_EVENT_SUNSET in (before, after)
after_sunrise = sunrise is not None and today > dt_util.as_local(sunrise).date()
after_sunrise = today > dt_util.as_local(cast(datetime, sunrise)).date()
if after_sunrise and has_sunrise_condition:
tomorrow = today + timedelta(days=1)
sunrise = get_astral_event_date(hass, SUN_EVENT_SUNRISE, tomorrow)
after_sunset = sunset is not None and today > dt_util.as_local(sunset).date()
after_sunset = today > dt_util.as_local(cast(datetime, sunset)).date()
if after_sunset and has_sunset_condition:
tomorrow = today + timedelta(days=1)
sunset = get_astral_event_date(hass, SUN_EVENT_SUNSET, tomorrow)
# A missing sunrise/sunset means the sun doesn't rise/set on this day, which
# happens in polar regions.
# Special case: before sunrise OR after sunset
# This will handle the very rare case in the polar region when the sun rises/sets
# but does not set/rise.
# However this entire condition does not handle those full days of darkness
# or light, the following should be used instead:
#
# condition:
# condition: state
# entity_id: sun.sun
# state: 'above_horizon' (or 'below_horizon')
#
if before == SUN_EVENT_SUNRISE and after == SUN_EVENT_SUNSET:
wanted_time_before = cast(datetime, sunrise) + before_offset
condition_trace_update_result(wanted_time_before=wanted_time_before)
wanted_time_after = cast(datetime, sunset) + after_offset
condition_trace_update_result(wanted_time_after=wanted_time_after)
return utcnow < wanted_time_before or utcnow > wanted_time_after
if sunrise is None and has_sunrise_condition:
# There is no sunrise today
condition_trace_set_result(False, message="no sunrise today")
@@ -107,16 +123,6 @@ def sun(
condition_trace_set_result(False, message="no sunset today")
return False
# "before: sunrise" combined with "after: sunset" describes the dark period
# around midnight, so it is evaluated as an OR (true before sunrise or after
# sunset) rather than the usual AND of the two bounds.
if before == SUN_EVENT_SUNRISE and after == SUN_EVENT_SUNSET:
wanted_time_before = cast(datetime, sunrise) + before_offset
condition_trace_update_result(wanted_time_before=wanted_time_before)
wanted_time_after = cast(datetime, sunset) + after_offset
condition_trace_update_result(wanted_time_after=wanted_time_after)
return utcnow < wanted_time_before or utcnow > wanted_time_after
if before == SUN_EVENT_SUNRISE:
wanted_time_before = cast(datetime, sunrise) + before_offset
condition_trace_update_result(wanted_time_before=wanted_time_before)
@@ -42,5 +42,5 @@
"iot_class": "local_push",
"loggers": ["switchbot"],
"quality_scale": "gold",
"requirements": ["PySwitchbot==2.3.0"]
"requirements": ["PySwitchbot==2.2.0"]
}
@@ -14,5 +14,5 @@
"integration_type": "hub",
"iot_class": "cloud_polling",
"loggers": ["switchbot_api"],
"requirements": ["switchbot-api==2.12.0"]
"requirements": ["switchbot-api==2.11.1"]
}
@@ -0,0 +1 @@
"""The thermoworks_smoke component."""
@@ -0,0 +1,11 @@
{
"domain": "thermoworks_smoke",
"name": "ThermoWorks Smoke",
"codeowners": [],
"disabled": "This integration is disabled because it creates an unresolvable dependency conflict.",
"documentation": "https://www.home-assistant.io/integrations/thermoworks_smoke",
"iot_class": "cloud_polling",
"loggers": ["thermoworks_smoke"],
"quality_scale": "legacy",
"requirements": ["stringcase==1.2.0", "thermoworks-smoke==0.1.8"]
}
@@ -0,0 +1,166 @@
"""Support for getting the state of a Thermoworks Smoke Thermometer.
Requires Smoke Gateway Wifi with an internet connection.
"""
import logging
from requests import RequestException
from requests.exceptions import HTTPError
from stringcase import camelcase
import thermoworks_smoke
import voluptuous as vol
from homeassistant.components.sensor import (
PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
SensorDeviceClass,
SensorEntity,
)
from homeassistant.const import (
ATTR_BATTERY_LEVEL,
CONF_EMAIL,
CONF_EXCLUDE,
CONF_MONITORED_CONDITIONS,
CONF_PASSWORD,
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
from homeassistant.util import snakecase
_LOGGER = logging.getLogger(__name__)
PROBE_1 = "probe1"
PROBE_2 = "probe2"
PROBE_1_MIN = "probe1_min"
PROBE_1_MAX = "probe1_max"
PROBE_2_MIN = "probe2_min"
PROBE_2_MAX = "probe2_max"
BATTERY_LEVEL = "battery"
FIRMWARE = "firmware"
SERIAL_REGEX = "^(?:[0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$"
# map types to labels
SENSOR_TYPES = {
PROBE_1: "Probe 1",
PROBE_2: "Probe 2",
PROBE_1_MIN: "Probe 1 Min",
PROBE_1_MAX: "Probe 1 Max",
PROBE_2_MIN: "Probe 2 Min",
PROBE_2_MAX: "Probe 2 Max",
}
# exclude these keys from thermoworks data
EXCLUDE_KEYS = [FIRMWARE]
PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
{
vol.Required(CONF_EMAIL): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Optional(CONF_MONITORED_CONDITIONS, default=[PROBE_1, PROBE_2]): vol.All(
cv.ensure_list, [vol.In(SENSOR_TYPES)]
),
vol.Optional(CONF_EXCLUDE, default=[]): vol.All(
cv.ensure_list, [cv.matches_regex(SERIAL_REGEX)]
),
}
)
def setup_platform(
hass: HomeAssistant,
config: ConfigType,
add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the thermoworks sensor."""
email = config[CONF_EMAIL]
password = config[CONF_PASSWORD]
monitored_variables = config[CONF_MONITORED_CONDITIONS]
excluded = config[CONF_EXCLUDE]
try:
mgr = thermoworks_smoke.initialize_app(email, password, True, excluded)
except HTTPError as error:
msg = f"{error.strerror}"
if "EMAIL_NOT_FOUND" in msg or "INVALID_PASSWORD" in msg:
_LOGGER.error("Invalid email and password combination")
else:
_LOGGER.error(msg)
else:
add_entities(
(
ThermoworksSmokeSensor(variable, serial, mgr)
for serial in mgr.serials()
for variable in monitored_variables
),
True,
)
class ThermoworksSmokeSensor(SensorEntity):
"""Implementation of a thermoworks smoke sensor."""
def __init__(self, sensor_type, serial, mgr):
"""Initialize the sensor."""
self.type = sensor_type
self.serial = serial
self.mgr = mgr
self._attr_name = f"{mgr.name(serial)} {SENSOR_TYPES[sensor_type]}"
self._attr_native_unit_of_measurement = UnitOfTemperature.FAHRENHEIT
self._attr_unique_id = f"{serial}-{sensor_type}"
self._attr_device_class = SensorDeviceClass.TEMPERATURE
self.update_unit()
def update_unit(self):
"""Set the units from the data."""
if PROBE_2 in self.type:
self._attr_native_unit_of_measurement = self.mgr.units(self.serial, PROBE_2)
else:
self._attr_native_unit_of_measurement = self.mgr.units(self.serial, PROBE_1)
def update(self) -> None:
"""Get the monitored data from firebase."""
try:
values = self.mgr.data(self.serial)
# set state from data based on type of sensor
self._attr_native_value = values.get(camelcase(self.type))
# set units
self.update_unit()
# set basic attributes for all sensors
self._attr_extra_state_attributes = {
"time": values["time"],
"localtime": values["localtime"],
}
# set extended attributes for main probe sensors
if self.type in (PROBE_1, PROBE_2):
for key, val in values.items():
# add all attributes that don't contain any probe name
# or contain a matching probe name
if (self.type == PROBE_1 and key.find(PROBE_2) == -1) or (
self.type == PROBE_2 and key.find(PROBE_1) == -1
):
if key == BATTERY_LEVEL:
key = ATTR_BATTERY_LEVEL
else:
# strip probe label and convert to snake_case
key = snakecase(key.replace(self.type, ""))
# add to attrs
if key and key not in EXCLUDE_KEYS:
self._attr_extra_state_attributes[key] = val
# store actual unit because attributes are not converted
self._attr_extra_state_attributes["unit_of_min_max"] = (
self._attr_native_unit_of_measurement
)
except RequestException, ValueError, KeyError:
_LOGGER.warning("Could not update status for %s", self.name)
+10 -9
View File
@@ -21,12 +21,13 @@ from homeassistant.components.sensor import (
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_MILLION,
CONF_API_KEY,
UnitOfDensity,
PERCENTAGE,
UnitOfIrradiance,
UnitOfLength,
UnitOfPressure,
UnitOfRatio,
UnitOfSpeed,
UnitOfTemperature,
)
@@ -173,7 +174,7 @@ SENSOR_TYPES = (
key="cloud_cover",
translation_key="cloud_cover",
attribute=TMRW_ATTR_CLOUD_COVER,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
),
# Data comes in as m/s, convert to mi/h for imperial
TomorrowioSensorEntityDescription(
@@ -199,7 +200,7 @@ SENSOR_TYPES = (
TomorrowioSensorEntityDescription(
key="ozone",
attribute=TMRW_ATTR_OZONE,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
multiplication_factor=convert_ppb_to_ugm3(48),
device_class=SensorDeviceClass.OZONE,
state_class=SensorStateClass.MEASUREMENT,
@@ -207,14 +208,14 @@ SENSOR_TYPES = (
TomorrowioSensorEntityDescription(
key="particulate_matter_2_5_mm",
attribute=TMRW_ATTR_PARTICULATE_MATTER_25,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM25,
state_class=SensorStateClass.MEASUREMENT,
),
TomorrowioSensorEntityDescription(
key="particulate_matter_10_mm",
attribute=TMRW_ATTR_PARTICULATE_MATTER_10,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM10,
state_class=SensorStateClass.MEASUREMENT,
),
@@ -223,7 +224,7 @@ SENSOR_TYPES = (
TomorrowioSensorEntityDescription(
key="nitrogen_dioxide",
attribute=TMRW_ATTR_NITROGEN_DIOXIDE,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
multiplication_factor=convert_ppb_to_ugm3(46.01),
device_class=SensorDeviceClass.NITROGEN_DIOXIDE,
state_class=SensorStateClass.MEASUREMENT,
@@ -232,7 +233,7 @@ SENSOR_TYPES = (
TomorrowioSensorEntityDescription(
key="carbon_monoxide",
attribute=TMRW_ATTR_CARBON_MONOXIDE,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
multiplication_factor=1 / 1000,
device_class=SensorDeviceClass.CO,
state_class=SensorStateClass.MEASUREMENT,
@@ -242,7 +243,7 @@ SENSOR_TYPES = (
TomorrowioSensorEntityDescription(
key="sulphur_dioxide",
attribute=TMRW_ATTR_SULPHUR_DIOXIDE,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
multiplication_factor=convert_ppb_to_ugm3(64.07),
device_class=SensorDeviceClass.SULPHUR_DIOXIDE,
state_class=SensorStateClass.MEASUREMENT,
+8 -3
View File
@@ -13,7 +13,12 @@ from homeassistant.components.sensor import (
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.const import Platform, UnitOfDensity, UnitOfRatio, UnitOfTime
from homeassistant.const import (
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
PERCENTAGE,
Platform,
UnitOfTime,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
@@ -57,7 +62,7 @@ SENSOR_DESCRIPTIONS_BATTERY: tuple[TradfriSensorEntityDescription, ...] = (
key="battery_level",
device_class=SensorDeviceClass.BATTERY,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
value=lambda device: cast(int, device.device_info.battery_level),
),
)
@@ -68,7 +73,7 @@ SENSOR_DESCRIPTIONS_FAN: tuple[TradfriSensorEntityDescription, ...] = (
key="aqi",
translation_key="aqi",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
value=_get_air_quality,
),
TradfriSensorEntityDescription(
+10 -7
View File
@@ -6,18 +6,21 @@ import logging
from homeassistant.components.sensor import SensorDeviceClass
from homeassistant.const import (
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_BILLION,
CONCENTRATION_PARTS_PER_MILLION,
LIGHT_LUX,
PERCENTAGE,
SIGNAL_STRENGTH_DECIBELS,
SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
Platform,
UnitOfConductivity,
UnitOfDensity,
UnitOfElectricCurrent,
UnitOfElectricPotential,
UnitOfEnergy,
UnitOfPower,
UnitOfPressure,
UnitOfRatio,
UnitOfTemperature,
UnitOfVolume,
UnitOfVolumetricFlux,
@@ -1020,7 +1023,7 @@ UNITS = (
},
),
UnitOfMeasurement(
unit=UnitOfRatio.PERCENTAGE,
unit=PERCENTAGE,
aliases={"pct", "percent", "% RH"},
device_classes={
SensorDeviceClass.BATTERY,
@@ -1029,14 +1032,14 @@ UNITS = (
},
),
UnitOfMeasurement(
unit=UnitOfRatio.PARTS_PER_MILLION,
unit=CONCENTRATION_PARTS_PER_MILLION,
device_classes={
SensorDeviceClass.CO,
SensorDeviceClass.CO2,
},
),
UnitOfMeasurement(
unit=UnitOfRatio.PARTS_PER_BILLION,
unit=CONCENTRATION_PARTS_PER_BILLION,
device_classes={
SensorDeviceClass.CO,
SensorDeviceClass.CO2,
@@ -1083,7 +1086,7 @@ UNITS = (
device_classes={SensorDeviceClass.ILLUMINANCE},
),
UnitOfMeasurement(
unit=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
unit=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
# The μ-char has 2 unicode variants \u00b5 and \u03bc
# The \u03bc variant (Greek Mu char) is recommended
aliases={"ug/m3", "\u03bcg/m3", "\u00b5g/m3", "ug/m³"},
@@ -1100,7 +1103,7 @@ UNITS = (
},
),
UnitOfMeasurement(
unit=UnitOfDensity.MILLIGRAMS_PER_CUBIC_METER,
unit=CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
aliases={"mg/m3"},
device_classes={
SensorDeviceClass.NITROGEN_DIOXIDE,
+2 -2
View File
@@ -44,7 +44,7 @@
"iot_class": "cloud_push",
"loggers": ["tuya_sharing"],
"requirements": [
"tuya-device-handlers==0.0.24",
"tuya-device-sharing-sdk==0.2.10"
"tuya-device-handlers==0.0.22",
"tuya-device-sharing-sdk==0.2.8"
]
}
+25 -24
View File
@@ -31,13 +31,14 @@ from homeassistant.components.sensor import (
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_MILLION,
PERCENTAGE,
EntityCategory,
UnitOfDensity,
UnitOfElectricCurrent,
UnitOfElectricPotential,
UnitOfEnergy,
UnitOfPower,
UnitOfRatio,
UnitOfTime,
)
from homeassistant.core import HomeAssistant, callback
@@ -74,7 +75,7 @@ BATTERY_SENSORS: tuple[TuyaSensorEntityDescription, ...] = (
TuyaSensorEntityDescription(
key=DPCode.BATTERY_PERCENTAGE,
translation_key="battery",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.BATTERY,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
@@ -82,7 +83,7 @@ BATTERY_SENSORS: tuple[TuyaSensorEntityDescription, ...] = (
TuyaSensorEntityDescription(
key=DPCode.BATTERY, # Used by non-standard contact sensor implementations
translation_key="battery",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.BATTERY,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
@@ -180,7 +181,7 @@ SENSORS: dict[DeviceCategory, tuple[TuyaSensorEntityDescription, ...]] = {
translation_key="carbon_dioxide",
device_class=SensorDeviceClass.CO2,
state_class=SensorStateClass.MEASUREMENT,
suggested_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
suggested_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
),
TuyaSensorEntityDescription(
key=DPCode.CH2O_VALUE,
@@ -198,7 +199,7 @@ SENSORS: dict[DeviceCategory, tuple[TuyaSensorEntityDescription, ...]] = {
translation_key="pm25",
device_class=SensorDeviceClass.PM25,
state_class=SensorStateClass.MEASUREMENT,
suggested_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
suggested_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
),
TuyaSensorEntityDescription(
key=DPCode.PM10,
@@ -214,7 +215,7 @@ SENSORS: dict[DeviceCategory, tuple[TuyaSensorEntityDescription, ...]] = {
translation_key="carbon_monoxide",
device_class=SensorDeviceClass.CO,
state_class=SensorStateClass.MEASUREMENT,
suggested_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
suggested_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
),
*BATTERY_SENSORS,
),
@@ -301,21 +302,21 @@ SENSORS: dict[DeviceCategory, tuple[TuyaSensorEntityDescription, ...]] = {
translation_key="pm25",
device_class=SensorDeviceClass.PM25,
state_class=SensorStateClass.MEASUREMENT,
suggested_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
suggested_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
),
TuyaSensorEntityDescription(
key=DPCode.CO_VALUE,
translation_key="carbon_monoxide",
device_class=SensorDeviceClass.CO,
state_class=SensorStateClass.MEASUREMENT,
suggested_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
suggested_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
),
TuyaSensorEntityDescription(
key=DPCode.CO2_VALUE,
translation_key="carbon_dioxide",
device_class=SensorDeviceClass.CO2,
state_class=SensorStateClass.MEASUREMENT,
suggested_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
suggested_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
),
TuyaSensorEntityDescription(
key=DPCode.EC_CURRENT,
@@ -534,7 +535,7 @@ SENSORS: dict[DeviceCategory, tuple[TuyaSensorEntityDescription, ...]] = {
translation_key="carbon_dioxide",
device_class=SensorDeviceClass.CO2,
state_class=SensorStateClass.MEASUREMENT,
suggested_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
suggested_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
),
TuyaSensorEntityDescription(
key=DPCode.CH2O_VALUE,
@@ -552,14 +553,14 @@ SENSORS: dict[DeviceCategory, tuple[TuyaSensorEntityDescription, ...]] = {
translation_key="pm25",
device_class=SensorDeviceClass.PM25,
state_class=SensorStateClass.MEASUREMENT,
suggested_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
suggested_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
),
TuyaSensorEntityDescription(
key=DPCode.PM10,
translation_key="pm10",
device_class=SensorDeviceClass.PM10,
state_class=SensorStateClass.MEASUREMENT,
suggested_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
suggested_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
),
*BATTERY_SENSORS,
),
@@ -569,7 +570,7 @@ SENSORS: dict[DeviceCategory, tuple[TuyaSensorEntityDescription, ...]] = {
translation_key="carbon_dioxide",
device_class=SensorDeviceClass.CO2,
state_class=SensorStateClass.MEASUREMENT,
suggested_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
suggested_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
),
TuyaSensorEntityDescription(
key=DPCode.VOC_VALUE,
@@ -582,7 +583,7 @@ SENSORS: dict[DeviceCategory, tuple[TuyaSensorEntityDescription, ...]] = {
translation_key="pm25",
device_class=SensorDeviceClass.PM25,
state_class=SensorStateClass.MEASUREMENT,
suggested_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
suggested_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
),
TuyaSensorEntityDescription(
key=DPCode.VA_HUMIDITY,
@@ -683,7 +684,7 @@ SENSORS: dict[DeviceCategory, tuple[TuyaSensorEntityDescription, ...]] = {
translation_key="pm25",
device_class=SensorDeviceClass.PM25,
state_class=SensorStateClass.MEASUREMENT,
suggested_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
suggested_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
),
TuyaSensorEntityDescription(
key=DPCode.TEMP,
@@ -708,7 +709,7 @@ SENSORS: dict[DeviceCategory, tuple[TuyaSensorEntityDescription, ...]] = {
translation_key="concentration_carbon_dioxide",
device_class=SensorDeviceClass.CO2,
state_class=SensorStateClass.MEASUREMENT,
suggested_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
suggested_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
),
TuyaSensorEntityDescription(
key=DPCode.TOTAL_TIME,
@@ -755,7 +756,7 @@ SENSORS: dict[DeviceCategory, tuple[TuyaSensorEntityDescription, ...]] = {
translation_key="carbon_dioxide",
device_class=SensorDeviceClass.CO2,
state_class=SensorStateClass.MEASUREMENT,
suggested_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
suggested_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
),
*BATTERY_SENSORS,
),
@@ -807,7 +808,7 @@ SENSORS: dict[DeviceCategory, tuple[TuyaSensorEntityDescription, ...]] = {
translation_key="pm25",
device_class=SensorDeviceClass.PM25,
state_class=SensorStateClass.MEASUREMENT,
suggested_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
suggested_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
),
TuyaSensorEntityDescription(
key=DPCode.CH2O_VALUE,
@@ -831,7 +832,7 @@ SENSORS: dict[DeviceCategory, tuple[TuyaSensorEntityDescription, ...]] = {
translation_key="carbon_dioxide",
device_class=SensorDeviceClass.CO2,
state_class=SensorStateClass.MEASUREMENT,
suggested_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
suggested_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
),
TuyaSensorEntityDescription(
key=DPCode.HUMIDITY_VALUE,
@@ -844,14 +845,14 @@ SENSORS: dict[DeviceCategory, tuple[TuyaSensorEntityDescription, ...]] = {
translation_key="pm1",
device_class=SensorDeviceClass.PM1,
state_class=SensorStateClass.MEASUREMENT,
suggested_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
suggested_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
),
TuyaSensorEntityDescription(
key=DPCode.PM10,
translation_key="pm10",
device_class=SensorDeviceClass.PM10,
state_class=SensorStateClass.MEASUREMENT,
suggested_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
suggested_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
),
*BATTERY_SENSORS,
),
@@ -1229,14 +1230,14 @@ SENSORS: dict[DeviceCategory, tuple[TuyaSensorEntityDescription, ...]] = {
translation_key="carbon_dioxide",
device_class=SensorDeviceClass.CO2,
state_class=SensorStateClass.MEASUREMENT,
suggested_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
suggested_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
),
TuyaSensorEntityDescription(
key=DPCode.PM25_VALUE,
translation_key="pm25",
device_class=SensorDeviceClass.PM25,
state_class=SensorStateClass.MEASUREMENT,
suggested_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
suggested_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
),
TuyaSensorEntityDescription(
key=DPCode.CH2O_VALUE,
@@ -9,5 +9,5 @@
"iot_class": "local_push",
"loggers": ["uiprotect"],
"quality_scale": "platinum",
"requirements": ["uiprotect==15.1.0"]
"requirements": ["uiprotect==14.0.0"]
}
+17 -17
View File
@@ -20,16 +20,16 @@ from homeassistant.components.sensor import (
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
PERCENTAGE,
REVOLUTIONS_PER_MINUTE,
SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
EntityCategory,
UnitOfDensity,
UnitOfElectricCurrent,
UnitOfEnergy,
UnitOfMass,
UnitOfPower,
UnitOfPressure,
UnitOfRatio,
UnitOfTemperature,
UnitOfTime,
UnitOfVolume,
@@ -80,7 +80,7 @@ VICARE_UNIT_TO_HA_UNIT = {
VICARE_CUBIC_METER: UnitOfVolume.CUBIC_METERS,
VICARE_KW: UnitOfPower.KILO_WATT,
VICARE_KWH: UnitOfEnergy.KILO_WATT_HOUR,
VICARE_PERCENT: UnitOfRatio.PERCENTAGE,
VICARE_PERCENT: PERCENTAGE,
VICARE_W: UnitOfPower.WATT,
VICARE_WH: UnitOfEnergy.WATT_HOUR,
}
@@ -117,7 +117,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = (
ViCareSensorEntityDescription(
key="outside_humidity",
translation_key="outside_humidity",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
value_getter=lambda api: api.getOutsideHumidity(),
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
@@ -165,7 +165,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = (
ViCareSensorEntityDescription(
key="primary_circuit_pump_rotation",
translation_key="primary_circuit_pump_rotation",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
value_getter=lambda api: api.getPrimaryCircuitPumpRotation(),
unit_getter=lambda api: api.getPrimaryCircuitPumpRotationUnit(),
state_class=SensorStateClass.MEASUREMENT,
@@ -799,7 +799,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = (
ViCareSensorEntityDescription(
key="ess_state_of_charge",
translation_key="ess_state_of_charge",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.BATTERY,
state_class=SensorStateClass.MEASUREMENT,
value_getter=lambda api: api.getElectricalEnergySystemSOC(),
@@ -996,7 +996,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = (
ViCareSensorEntityDescription(
key="room_humidity",
device_class=SensorDeviceClass.HUMIDITY,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
value_getter=lambda api: api.getHumidity(),
),
@@ -1122,7 +1122,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = (
),
ViCareSensorEntityDescription(
key="battery_level",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
@@ -1142,7 +1142,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = (
translation_key="zigbee_signal_strength",
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
value_getter=lambda api: api.getZigbeeSignalStrength(),
entity_registry_enabled_default=False,
),
@@ -1150,7 +1150,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = (
key="valve_position",
translation_key="valve_position",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
value_getter=lambda api: api.getValvePosition(),
entity_registry_enabled_default=False,
),
@@ -1177,7 +1177,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = (
key="supply_humidity",
translation_key="supply_humidity",
device_class=SensorDeviceClass.HUMIDITY,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
value_getter=lambda api: api.getSupplyHumidity(),
),
@@ -1229,28 +1229,28 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = (
ViCareSensorEntityDescription(
key="pm01",
device_class=SensorDeviceClass.PM1,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value_getter=lambda api: api.getAirborneDustPM1(),
),
ViCareSensorEntityDescription(
key="pm02",
device_class=SensorDeviceClass.PM25,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value_getter=lambda api: api.getAirborneDustPM2d5(),
),
ViCareSensorEntityDescription(
key="pm04",
device_class=SensorDeviceClass.PM4,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value_getter=lambda api: api.getAirborneDustPM4(),
),
ViCareSensorEntityDescription(
key="pm10",
device_class=SensorDeviceClass.PM10,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value_getter=lambda api: api.getAirborneDustPM10(),
),
@@ -1293,7 +1293,7 @@ BURNER_SENSORS: tuple[ViCareSensorEntityDescription, ...] = (
ViCareSensorEntityDescription(
key="burner_modulation",
translation_key="burner_modulation",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
value_getter=lambda api: api.getModulation(),
state_class=SensorStateClass.MEASUREMENT,
),
@@ -1312,7 +1312,7 @@ COMPRESSOR_SENSORS: tuple[ViCareSensorEntityDescription, ...] = (
ViCareSensorEntityDescription(
key="compressor_modulation",
translation_key="compressor_modulation",
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
native_unit_of_measurement=PERCENTAGE,
value_getter=lambda api: api.getModulation(),
unit_getter=lambda api: api.getModulationUnit(),
state_class=SensorStateClass.MEASUREMENT,
@@ -145,7 +145,9 @@ class VodafoneStationRouter(DataUpdateCoordinator[UpdateCoordinatorDataType]):
translation_placeholders={"error": repr(err)},
) from err
except (
exceptions.VodafoneError,
exceptions.CannotConnect,
exceptions.AlreadyLogged,
exceptions.GenericLoginError,
JSONDecodeError,
) as err:
if isinstance(err, JSONDecodeError):
@@ -149,6 +149,7 @@ SELECTOR_TYPES = (
XiaomiMiioSelectDescription(
key=ATTR_DISPLAY_ORIENTATION,
attr_name=ATTR_DISPLAY_ORIENTATION,
name="Display Orientation",
options_map={
"Portrait": "Forward",
"LandscapeLeft": "Left",
@@ -164,6 +165,7 @@ SELECTOR_TYPES = (
XiaomiMiioSelectDescription(
key=ATTR_MODE,
attr_name=ATTR_MODE,
name="Mode",
set_method="set_mode",
set_method_error_message="Setting the mode of the fan failed.",
icon="mdi:fan",
@@ -174,6 +176,7 @@ SELECTOR_TYPES = (
XiaomiMiioSelectDescription(
key=ATTR_LED_BRIGHTNESS,
attr_name=ATTR_LED_BRIGHTNESS,
name="Led Brightness",
set_method="set_led_brightness",
set_method_error_message="Setting the led brightness failed.",
icon="mdi:brightness-6",
@@ -184,6 +187,7 @@ SELECTOR_TYPES = (
XiaomiMiioSelectDescription(
key=ATTR_PTC_LEVEL,
attr_name=ATTR_PTC_LEVEL,
name="Auxiliary Heat Level",
set_method="set_ptc_level",
set_method_error_message="Setting the ptc level failed.",
icon="mdi:fire-circle",
@@ -138,7 +138,6 @@
},
"select": {
"airpurifier_mode": {
"name": "Mode",
"state": {
"auto": "[%key:common::state::auto%]",
"favorite": "Favorite",
@@ -146,7 +145,6 @@
}
},
"display_orientation": {
"name": "Display orientation",
"state": {
"forward": "Forward",
"left": "Left",
@@ -154,7 +152,6 @@
}
},
"led_brightness": {
"name": "LED brightness",
"state": {
"bright": "Bright",
"dim": "Dim",
@@ -162,7 +159,6 @@
}
},
"ptc_level": {
"name": "Auxiliary heat level",
"state": {
"high": "[%key:common::state::high%]",
"low": "[%key:common::state::low%]",
+1 -1
View File
@@ -21,7 +21,7 @@ if TYPE_CHECKING:
APPLICATION_NAME: Final = "HomeAssistant"
MAJOR_VERSION: Final = 2026
MINOR_VERSION: Final = 8
MINOR_VERSION: Final = 7
PATCH_VERSION: Final = "0.dev0"
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
+12
View File
@@ -4587,6 +4587,12 @@
"config_flow": false,
"iot_class": "cloud_polling"
},
"mycroft": {
"name": "Mycroft",
"integration_type": "hub",
"config_flow": false,
"iot_class": "local_push"
},
"myneomitis": {
"name": "MyNeomitis",
"integration_type": "hub",
@@ -7201,6 +7207,12 @@
"config_flow": true,
"iot_class": "local_push"
},
"thermoworks_smoke": {
"name": "ThermoWorks Smoke",
"integration_type": "hub",
"config_flow": false,
"iot_class": "cloud_polling"
},
"thethingsnetwork": {
"name": "The Things Network",
"integration_type": "hub",
+1 -1
View File
@@ -740,7 +740,7 @@ def _get_exposed_entities(
if include_state and (
attributes := {
str(attr_name): (
attr_name: (
str(attr_value)
if isinstance(attr_value, (Enum, Decimal, int))
else attr_value
+4 -4
View File
@@ -35,12 +35,12 @@ file-read-backwards==2.0.0
fnv-hash-fast==2.0.3
go2rtc-client==0.4.0
ha-ffmpeg==3.2.2
habluetooth==6.25.1
habluetooth==6.23.1
hass-nabucasa==2.2.0
hassil==3.8.0
home-assistant-bluetooth==2.0.0
home-assistant-frontend==20260624.1
home-assistant-intents==2026.6.24
home-assistant-frontend==20260624.0
home-assistant-intents==2026.6.1
httpx==0.28.1
ifaddr==0.2.0
Jinja2==3.1.6
@@ -71,7 +71,7 @@ typing-extensions>=4.15.0,<5.0
ulid-transform==2.2.9
urllib3>=2.0
uv==0.11.21
voluptuous-openapi==0.4.1
voluptuous-openapi==0.3.0
voluptuous-serialize==2.7.0
voluptuous==0.15.2
webrtc-models==0.3.0
+1 -1
View File
@@ -5,7 +5,7 @@ To update, run python3 -m script.hassfest
from typing import Final
FRONTEND_VERSION: Final[str] = "20260624.1"
FRONTEND_VERSION: Final[str] = "20260624.0"
MDI_ICONS: Final[set[str]] = {
"ab-testing",
+2 -2
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "homeassistant"
version = "2026.8.0.dev0"
version = "2026.7.0.dev0"
license = "Apache-2.0"
license-files = ["LICENSE*", "homeassistant/backports/LICENSE*"]
description = "Open-source home automation platform running on Python 3."
@@ -77,7 +77,7 @@ dependencies = [
"uv==0.11.21",
"voluptuous==0.15.2",
"voluptuous-serialize==2.7.0",
"voluptuous-openapi==0.4.1",
"voluptuous-openapi==0.3.0",
"yarl==1.24.2",
"webrtc-models==0.3.0",
"zeroconf==0.150.0",
+2 -2
View File
@@ -27,7 +27,7 @@ ha-ffmpeg==3.2.2
hass-nabucasa==2.2.0
hassil==3.8.0
home-assistant-bluetooth==2.0.0
home-assistant-intents==2026.6.24
home-assistant-intents==2026.6.1
httpx==0.28.1
ifaddr==0.2.0
infrared-protocols==6.3.0
@@ -56,7 +56,7 @@ typing-extensions>=4.15.0,<5.0
ulid-transform==2.2.9
urllib3>=2.0
uv==0.11.21
voluptuous-openapi==0.4.1
voluptuous-openapi==0.3.0
voluptuous-serialize==2.7.0
voluptuous==0.15.2
webrtc-models==0.3.0
+13 -13
View File
@@ -83,7 +83,7 @@ PyRMVtransport==0.3.3
PySrDaliGateway==0.21.0
# homeassistant.components.switchbot
PySwitchbot==2.3.0
PySwitchbot==2.2.0
# homeassistant.components.switchmate
PySwitchmate==0.5.1
@@ -193,7 +193,7 @@ aioairzone-cloud==0.7.2
aioairzone==1.0.5
# homeassistant.components.alexa_devices
aioamazondevices==14.1.6
aioamazondevices==14.1.3
# homeassistant.components.ambient_network
# homeassistant.components.ambient_station
@@ -260,7 +260,7 @@ aioelectricitymaps==1.1.1
aioemonitor==1.0.5
# homeassistant.components.esphome
aioesphomeapi==45.5.2
aioesphomeapi==45.3.1
# homeassistant.components.matrix
# homeassistant.components.slack
@@ -1219,7 +1219,7 @@ ha-xthings-cloud==1.0.5
habiticalib==0.4.7
# homeassistant.components.bluetooth
habluetooth==6.25.1
habluetooth==6.23.1
# homeassistant.components.hanna
hanna-cloud==0.0.7
@@ -1272,10 +1272,10 @@ hole==0.9.2
holidays==0.99
# homeassistant.components.frontend
home-assistant-frontend==20260624.1
home-assistant-frontend==20260624.0
# homeassistant.components.conversation
home-assistant-intents==2026.6.24
home-assistant-intents==2026.6.1
# homeassistant.components.homekit
homekit-audio-proxy==1.2.1
@@ -1658,7 +1658,7 @@ nettigo-air-monitor==5.0.0
neurio==0.3.1
# homeassistant.components.nexia
nexia==2.13.0
nexia==2.11.1
# homeassistant.components.nextcloud
nextcloudmonitor==1.5.1
@@ -2730,7 +2730,7 @@ python-rabbitair==0.0.8
python-ripple-api==0.0.3
# homeassistant.components.roborock
python-roborock==5.21.0
python-roborock==5.14.2
# homeassistant.components.smarttub
python-smarttub==0.0.47
@@ -2899,7 +2899,7 @@ renault-api==0.5.12
renson-endura-delta==1.7.2
# homeassistant.components.reolink
reolink-aio==0.21.3
reolink-aio==0.21.2
# homeassistant.components.radio_frequency
rf-protocols==4.3.0
@@ -3114,7 +3114,7 @@ surepy==0.9.0
swisshydrodata==0.1.0
# homeassistant.components.switchbot_cloud
switchbot-api==2.12.0
switchbot-api==2.11.1
# homeassistant.components.synology_srm
synology-srm==0.2.0
@@ -3221,10 +3221,10 @@ ttls==1.8.3
ttn_client==1.3.0
# homeassistant.components.tuya
tuya-device-handlers==0.0.24
tuya-device-handlers==0.0.22
# homeassistant.components.tuya
tuya-device-sharing-sdk==0.2.10
tuya-device-sharing-sdk==0.2.8
# homeassistant.components.twentemilieu
twentemilieu==3.0.0
@@ -3245,7 +3245,7 @@ uasiren==0.0.1
uhooapi==1.2.8
# homeassistant.components.unifiprotect
uiprotect==15.1.0
uiprotect==14.0.0
# homeassistant.components.landisgyr_heat_meter
ultraheat-api==0.6.1
+1 -2
View File
@@ -19,7 +19,6 @@ mock-open==1.4.0
mypy==2.1.0
prek==0.2.28
pydantic==2.13.4
PyGithub==2.9.1
pylint==4.0.6
pylint-per-file-ignores==3.2.1
pipdeptree==2.26.1
@@ -27,7 +26,7 @@ pytest-asyncio==1.4.0
pytest-aiohttp==1.1.1
pytest-cov==7.1.0
pytest-freezer==0.4.9
pytest-github-actions-annotate-failures==0.4.1
pytest-github-actions-annotate-failures==0.4.0
pytest-socket==0.8.0
pytest-sugar==1.1.1
pytest-timeout==2.4.0

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