Compare commits

..

21 Commits

Author SHA1 Message Date
Robert Resch 5f5d74cfbd Remove requirements_test_all file (#171530) 2026-05-20 23:54:31 +02:00
Josh Gustafson c188fdcc8b Clean up arcam_fmj config flow (#171161)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 22:58:10 +02:00
Michael Hansen a3b43fc19b Handle multiple intents in Wyoming conversation (#171615)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: balloob <1444314+balloob@users.noreply.github.com>
2026-05-20 16:48:56 -04:00
Maciej Bieniek 894a68acb6 Fix media_image_hash and validate the MIME type in the Shelly media player (#171585) 2026-05-20 22:25:30 +02:00
Kamil Breguła 30bc3fc412 Bump wled to 0.23.0 and remove backoff exception (#171622) 2026-05-20 22:16:43 +02:00
Michael 3cc0cc38ab Add missing translation placeholders for SMA exceptions (#171625) 2026-05-20 21:31:44 +02:00
Michael 296caa90c1 Fix exception strings in FRITZ!Box tools (#171603) 2026-05-20 20:55:42 +02:00
A. Gideonse bb4c211fb6 Add DHCP discovery to Indevolt (#169597)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-20 20:53:23 +02:00
Nick Haghiri d4fa904386 Add invalid_auth exception translation key to backblaze_b2 (#171584)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-20 20:51:44 +02:00
Erik Montnemery db98f0b434 Remove advanced mode from homeassistant service actions (#171440)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-20 20:40:36 +02:00
Erwin Douna 7341ac91ee SMA add missing exceptions (#171550) 2026-05-20 20:32:19 +02:00
Jan Bouwhuis b2fb5df0fb Remove positional message strings when translation_key is set in mqtt (#171617) 2026-05-20 20:16:41 +02:00
Manu 265485a7d0 Fix positional message strings in exceptions in Notify for Android TV / Fire TV integration (#171581) 2026-05-20 20:00:30 +02:00
J. Nick Koston bf1b93fb66 Bump aioesphomeapi to 45.0.4 (#171601) 2026-05-20 12:54:08 -05:00
dontinelli be9d4bedfd Fix update error message key in solarlog (#171611) 2026-05-20 19:53:19 +02:00
Franck Nijhof e8ac982e83 Add pylint checker for exception translation validation (#171453)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-20 19:45:56 +02:00
Abílio Costa 6c8e5a8e98 Add common availability test helper for IR/RF integrations (#171610)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-20 13:05:37 -04:00
Paul Bottein e40a3e18db Send entity domain in template config flow preview (#171599) 2026-05-20 13:00:23 -04:00
Robert Resch cba05caadd Fix aw generation (#171609) 2026-05-20 12:56:57 -04:00
Denis Shulyaka ef3bc61e2b Remove stale temperature key from anthropic strings (#171612) 2026-05-20 12:56:33 -04:00
shbatm 3eff36eb9d Use CONF_CODE from homeassistant.const in isy994 (#171608)
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 18:48:35 +02:00
154 changed files with 2345 additions and 3420 deletions
-1
View File
@@ -19,7 +19,6 @@ machine/* linguist-generated=true
mypy.ini linguist-generated=true
requirements.txt linguist-generated=true
requirements_all.txt linguist-generated=true
requirements_test_all.txt linguist-generated=true
requirements_test_pre_commit.txt linguist-generated=true
script/hassfest/docker/Dockerfile linguist-generated=true
.github/workflows/*.lock.yml linguist-generated=true
+17 -17
View File
@@ -1,4 +1,4 @@
# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"f302a3180732b8ac09ba6f393867233445c8f22d14301456227a56c6ab8fe758","compiler_version":"v0.74.4","strict":true,"agent_id":"copilot"}
# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"1ad29b8fb97f5df4466be54051779a3188f094d7efb041a8ed55211eab33c5f5","compiler_version":"v0.74.4","strict":true,"agent_id":"copilot"}
# 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":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"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":"d3abfe96a194bce3a523ed2093ddedd5704cdf62","version":"v0.74.4"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.46"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.46"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.46"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.9","digest":"sha256:64828b42a4482f58fab16509d7f8f495a6d97c972a98a68aff20543531ac0388","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.9@sha256:64828b42a4482f58fab16509d7f8f495a6d97c972a98a68aff20543531ac0388"},{"image":"ghcr.io/github/github-mcp-server:v1.0.4"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]}
# ___ _ _
# / _ \ | | (_)
@@ -112,7 +112,7 @@ jobs:
GH_AW_INFO_EXPERIMENTAL: "false"
GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true"
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
GH_AW_INFO_ALLOWED_DOMAINS: '["python"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
GH_AW_INFO_AWF_VERSION: "v0.25.46"
GH_AW_INFO_AWMG_VERSION: ""
@@ -189,20 +189,20 @@ jobs:
run: |
bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh"
{
cat << 'GH_AW_PROMPT_378af7db8428e64c_EOF'
cat << 'GH_AW_PROMPT_bb296919e461941b_EOF'
<system>
GH_AW_PROMPT_378af7db8428e64c_EOF
GH_AW_PROMPT_bb296919e461941b_EOF
cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md"
cat << 'GH_AW_PROMPT_378af7db8428e64c_EOF'
cat << 'GH_AW_PROMPT_bb296919e461941b_EOF'
<safe-output-tools>
Tools: add_comment, missing_tool, missing_data, noop
</safe-output-tools>
GH_AW_PROMPT_378af7db8428e64c_EOF
GH_AW_PROMPT_bb296919e461941b_EOF
cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md"
cat << 'GH_AW_PROMPT_378af7db8428e64c_EOF'
cat << 'GH_AW_PROMPT_bb296919e461941b_EOF'
<github-context>
The following GitHub context information is available for this workflow:
{{#if github.actor}}
@@ -231,12 +231,12 @@ jobs:
{{/if}}
</github-context>
GH_AW_PROMPT_378af7db8428e64c_EOF
GH_AW_PROMPT_bb296919e461941b_EOF
cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
cat << 'GH_AW_PROMPT_378af7db8428e64c_EOF'
cat << 'GH_AW_PROMPT_bb296919e461941b_EOF'
</system>
{{#runtime-import .github/workflows/check-requirements.md}}
GH_AW_PROMPT_378af7db8428e64c_EOF
GH_AW_PROMPT_bb296919e461941b_EOF
} > "$GH_AW_PROMPT"
- name: Interpolate variables and render templates
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
@@ -454,9 +454,9 @@ 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_39549f77910cdeb7_EOF'
cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_c09f5151c817ddfc_EOF'
{"add_comment":{"max":1,"target":"${{ env.PR_NUMBER }}"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{}}
GH_AW_SAFE_OUTPUTS_CONFIG_39549f77910cdeb7_EOF
GH_AW_SAFE_OUTPUTS_CONFIG_c09f5151c817ddfc_EOF
- name: Generate Safe Outputs Tools
env:
GH_AW_TOOLS_META_JSON: |
@@ -648,7 +648,7 @@ jobs:
mkdir -p /home/runner/.copilot
GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node)
cat << GH_AW_MCP_CONFIG_da05d5b6908ed578_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs"
cat << GH_AW_MCP_CONFIG_d12799b4d7ffe5c2_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs"
{
"mcpServers": {
"github": {
@@ -692,7 +692,7 @@ jobs:
"payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}"
}
}
GH_AW_MCP_CONFIG_da05d5b6908ed578_EOF
GH_AW_MCP_CONFIG_d12799b4d7ffe5c2_EOF
- name: Mount MCP servers as CLIs
id: mount-mcp-clis
continue-on-error: true
@@ -725,7 +725,7 @@ jobs:
GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true)
export GH_AW_NODE_BIN
(umask 177 && touch /tmp/gh-aw/agent-stdio.log)
printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.46/awf-config.schema.json","network":{"allowDomains":["api.business.githubcopilot.com","api.enterprise.githubcopilot.com","api.github.com","api.githubcopilot.com","api.individual.githubcopilot.com","api.snapcraft.io","archive.ubuntu.com","azure.archive.ubuntu.com","crl.geotrust.com","crl.globalsign.com","crl.identrust.com","crl.sectigo.com","crl.thawte.com","crl.usertrust.com","crl.verisign.com","crl3.digicert.com","crl4.digicert.com","crls.ssl.com","github.com","host.docker.internal","json-schema.org","json.schemastore.org","keyserver.ubuntu.com","ocsp.digicert.com","ocsp.geotrust.com","ocsp.globalsign.com","ocsp.identrust.com","ocsp.sectigo.com","ocsp.ssl.com","ocsp.thawte.com","ocsp.usertrust.com","ocsp.verisign.com","packagecloud.io","packages.cloud.google.com","packages.microsoft.com","ppa.launchpad.net","raw.githubusercontent.com","registry.npmjs.org","s.symcb.com","s.symcd.com","security.ubuntu.com","telemetry.enterprise.githubcopilot.com","ts-crl.ws.symantec.com","ts-ocsp.ws.symantec.com","www.googleapis.com"]},"apiProxy":{"enabled":true,"enableTokenSteering":true,"maxRuns":500,"maxEffectiveTokens":25000000,"models":{"auto":["large"],"coding":["copilot/gpt-5*codex*","openai/gpt-5*codex*","gpt-5-codex"],"deep-research":["copilot/deep-research*","copilot/o3-deep-research*","copilot/o4-mini-deep-research*","google/deep-research*","gemini/deep-research*","openai/o3-deep-research*","openai/o4-mini-deep-research*"],"gemini-flash":["copilot/gemini-*flash*","google/gemini-*flash*","gemini/gemini-*flash*"],"gemini-flash-lite":["copilot/gemini-*flash*lite*","google/gemini-*flash*lite*","gemini/gemini-*flash*lite*"],"gemini-pro":["copilot/gemini-*pro*","google/gemini-*pro*","gemini/gemini-*pro*"],"gemma":["copilot/gemma*","google/gemma*","gemini/gemma*"],"gpt-4.1":["copilot/gpt-4.1*","openai/gpt-4.1*"],"gpt-5":["copilot/gpt-5*","openai/gpt-5*"],"gpt-5-codex":["copilot/gpt-5*codex*","openai/gpt-5*codex*"],"gpt-5-mini":["copilot/gpt-5*mini*","openai/gpt-5*mini*"],"gpt-5-nano":["copilot/gpt-5*nano*","openai/gpt-5*nano*"],"gpt-5-pro":["copilot/gpt-5*pro*","openai/gpt-5*pro*"],"haiku":["copilot/*haiku*","anthropic/*haiku*"],"large":["sonnet","gpt-5-pro","gpt-5","gemini-pro"],"mini":["haiku","gpt-5-mini","gpt-5-nano","gemini-flash-lite"],"opus":["copilot/*opus*","anthropic/*opus*"],"reasoning":["copilot/o1*","copilot/o3*","copilot/o4*","openai/o1*","openai/o3*","openai/o4*"],"small":["mini"],"sonnet":["copilot/*sonnet*","anthropic/*sonnet*"],"vision":["copilot/gemini-*image*","gemini/gemini-*image*","copilot/gemini-*flash*","gemini/gemini-*flash*"]}},"container":{"imageTag":"0.25.46"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json" && cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json
printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.46/awf-config.schema.json","network":{"allowDomains":["*.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"]},"apiProxy":{"enabled":true,"enableTokenSteering":true,"maxRuns":500,"maxEffectiveTokens":25000000,"models":{"auto":["large"],"coding":["copilot/gpt-5*codex*","openai/gpt-5*codex*","gpt-5-codex"],"deep-research":["copilot/deep-research*","copilot/o3-deep-research*","copilot/o4-mini-deep-research*","google/deep-research*","gemini/deep-research*","openai/o3-deep-research*","openai/o4-mini-deep-research*"],"gemini-flash":["copilot/gemini-*flash*","google/gemini-*flash*","gemini/gemini-*flash*"],"gemini-flash-lite":["copilot/gemini-*flash*lite*","google/gemini-*flash*lite*","gemini/gemini-*flash*lite*"],"gemini-pro":["copilot/gemini-*pro*","google/gemini-*pro*","gemini/gemini-*pro*"],"gemma":["copilot/gemma*","google/gemma*","gemini/gemma*"],"gpt-4.1":["copilot/gpt-4.1*","openai/gpt-4.1*"],"gpt-5":["copilot/gpt-5*","openai/gpt-5*"],"gpt-5-codex":["copilot/gpt-5*codex*","openai/gpt-5*codex*"],"gpt-5-mini":["copilot/gpt-5*mini*","openai/gpt-5*mini*"],"gpt-5-nano":["copilot/gpt-5*nano*","openai/gpt-5*nano*"],"gpt-5-pro":["copilot/gpt-5*pro*","openai/gpt-5*pro*"],"haiku":["copilot/*haiku*","anthropic/*haiku*"],"large":["sonnet","gpt-5-pro","gpt-5","gemini-pro"],"mini":["haiku","gpt-5-mini","gpt-5-nano","gemini-flash-lite"],"opus":["copilot/*opus*","anthropic/*opus*"],"reasoning":["copilot/o1*","copilot/o3*","copilot/o4*","openai/o1*","openai/o3*","openai/o4*"],"small":["mini"],"sonnet":["copilot/*sonnet*","anthropic/*sonnet*"],"vision":["copilot/gemini-*image*","gemini/gemini-*image*","copilot/gemini-*flash*","gemini/gemini-*flash*"]}},"container":{"imageTag":"0.25.46"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json" && cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json
GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS=""
if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then
GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="--docker-host-path-prefix /tmp/gh-aw"
@@ -820,7 +820,7 @@ jobs:
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com"
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 }}
with:
@@ -1390,7 +1390,7 @@ jobs:
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com"
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\":\"${{ env.PR_NUMBER }}\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"report_incomplete\":{}}"
+3 -3
View File
@@ -132,7 +132,7 @@
"problemMatcher": []
},
{
"label": "Install all Requirements",
"label": "Install all production Requirements",
"type": "shell",
"command": "uv pip install -r requirements_all.txt",
"group": {
@@ -146,9 +146,9 @@
"problemMatcher": []
},
{
"label": "Install all Test Requirements",
"label": "Install all (test & production) Requirements",
"type": "shell",
"command": "uv pip install -r requirements.txt -r requirements_test_all.txt",
"command": "uv pip install -r requirements_all.txt -r requirements_test.txt",
"group": {
"kind": "build",
"isDefault": true
@@ -81,8 +81,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: AirOSConfigEntry) -> boo
) as err:
raise ConfigEntryAuthFailed from err
except AirOSKeyDataMissingError as err:
# pylint: disable-next=home-assistant-exception-not-translated
raise ConfigEntryError("key_data_missing") from err
except Exception as err:
# pylint: disable-next=home-assistant-exception-not-translated
raise ConfigEntryError("unknown") from err
airos_class: type[AirOS8 | AirOS6] = (
@@ -91,6 +91,7 @@ class AmazonDevicesCoordinator(DataUpdateCoordinator[dict[str, AmazonDevice]]):
translation_placeholders={"error": repr(err)},
) from err
except CannotAuthenticate as err:
# pylint: disable-next=home-assistant-exception-translation-key-missing
raise ConfigEntryAuthFailed(
translation_domain=DOMAIN,
translation_key="invalid_auth",
@@ -51,13 +51,11 @@
"advanced": {
"data": {
"chat_model": "[%key:common::generic::model%]",
"prompt_caching": "[%key:component::anthropic::config_subentries::conversation::step::advanced::data::prompt_caching%]",
"temperature": "[%key:component::anthropic::config_subentries::conversation::step::advanced::data::temperature%]"
"prompt_caching": "[%key:component::anthropic::config_subentries::conversation::step::advanced::data::prompt_caching%]"
},
"data_description": {
"chat_model": "[%key:component::anthropic::config_subentries::conversation::step::advanced::data_description::chat_model%]",
"prompt_caching": "[%key:component::anthropic::config_subentries::conversation::step::advanced::data_description::prompt_caching%]",
"temperature": "[%key:component::anthropic::config_subentries::conversation::step::advanced::data_description::temperature%]"
"prompt_caching": "[%key:component::anthropic::config_subentries::conversation::step::advanced::data_description::prompt_caching%]"
},
"title": "[%key:component::anthropic::config_subentries::conversation::step::advanced::title%]"
},
@@ -120,13 +118,11 @@
"advanced": {
"data": {
"chat_model": "[%key:common::generic::model%]",
"prompt_caching": "Caching strategy",
"temperature": "Temperature"
"prompt_caching": "Caching strategy"
},
"data_description": {
"chat_model": "The model to serve the responses.",
"prompt_caching": "Optimize your API cost and response times based on your usage.",
"temperature": "Control the randomness of the response, trading off between creativity and coherence."
"prompt_caching": "Optimize your API cost and response times based on your usage."
},
"title": "Advanced settings"
},
@@ -1,9 +1,11 @@
"""Config flow to configure the Arcam FMJ component."""
import socket
from typing import Any
from urllib.parse import urlparse
from arcam.fmj.client import Client, ConnectionFailed
from arcam.fmj import ConnectionFailed
from arcam.fmj.client import Client
from arcam.fmj.utils import get_uniqueid_from_host, get_uniqueid_from_udn
import voluptuous as vol
@@ -29,26 +31,19 @@ class ArcamFmjFlowHandler(ConfigFlow, domain=DOMAIN):
await self.async_set_unique_id(uuid)
self._abort_if_unique_id_configured({CONF_HOST: host, CONF_PORT: port})
async def _async_check_and_create(self, host: str, port: int) -> ConfigFlowResult:
async def _async_try_connect(self, host: str, port: int) -> None:
"""Verify the device is reachable."""
client = Client(host, port)
try:
await client.start()
except ConnectionFailed:
return self.async_abort(reason="cannot_connect")
finally:
await client.stop()
return self.async_create_entry(
title=f"{DEFAULT_NAME} ({host})",
data={CONF_HOST: host, CONF_PORT: port},
)
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle a discovered device."""
errors: dict[str, str] = {}
if user_input is not None:
uuid = await get_uniqueid_from_host(
async_get_clientsession(self.hass), user_input[CONF_HOST]
@@ -58,18 +53,36 @@ class ArcamFmjFlowHandler(ConfigFlow, domain=DOMAIN):
user_input[CONF_HOST], user_input[CONF_PORT], uuid
)
return await self._async_check_and_create(
user_input[CONF_HOST], user_input[CONF_PORT]
)
try:
await self._async_try_connect(
user_input[CONF_HOST], user_input[CONF_PORT]
)
except socket.gaierror:
errors["base"] = "invalid_host"
except TimeoutError:
errors["base"] = "timeout_connect"
except ConnectionRefusedError:
errors["base"] = "connection_refused"
except ConnectionFailed, OSError:
errors["base"] = "cannot_connect"
else:
return self.async_create_entry(
title=f"{DEFAULT_NAME} ({user_input[CONF_HOST]})",
data={
CONF_HOST: user_input[CONF_HOST],
CONF_PORT: user_input[CONF_PORT],
},
)
fields = {
vol.Required(CONF_HOST): str,
vol.Required(CONF_PORT, default=DEFAULT_PORT): int,
}
schema = vol.Schema(fields)
if user_input is not None:
schema = self.add_suggested_values_to_schema(schema, user_input)
return self.async_show_form(
step_id="user", data_schema=vol.Schema(fields), errors=errors
)
return self.async_show_form(step_id="user", data_schema=schema, errors=errors)
async def async_step_confirm(
self, user_input: dict[str, Any] | None = None
@@ -79,7 +92,10 @@ class ArcamFmjFlowHandler(ConfigFlow, domain=DOMAIN):
self.context["title_placeholders"] = placeholders
if user_input is not None:
return await self._async_check_and_create(self.host, self.port)
return self.async_create_entry(
title=f"{DEFAULT_NAME} ({self.host})",
data={CONF_HOST: self.host, CONF_PORT: self.port},
)
return self.async_show_form(
step_id="confirm", description_placeholders=placeholders
@@ -97,6 +113,11 @@ class ArcamFmjFlowHandler(ConfigFlow, domain=DOMAIN):
await self._async_set_unique_id_and_update(host, port, uuid)
try:
await self._async_try_connect(host, port)
except ConnectionFailed, OSError:
return self.async_abort(reason="cannot_connect")
self.host = host
self.port = DEFAULT_PORT
self.port = port
return await self.async_step_confirm()
@@ -5,6 +5,12 @@
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
},
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"connection_refused": "Host refused connection",
"invalid_host": "[%key:common::config_flow::error::invalid_host%]",
"timeout_connect": "[%key:common::config_flow::error::timeout_connect%]"
},
"flow_title": "{host}",
"step": {
"confirm": {
@@ -82,7 +82,7 @@ rules:
comment: |
This integration does not have any entities that should disabled by default.
entity-translations: done
exception-translations: done
exception-translations: todo
icon-translations: done
reconfiguration-flow: todo
repair-issues:
@@ -67,7 +67,7 @@ rules:
comment: |
Only one entity type (device_tracker) is created, making this not applicable.
entity-translations: done
exception-translations: done
exception-translations: todo
icon-translations: done
reconfiguration-flow:
status: todo
@@ -51,6 +51,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: S3ConfigEntry) -> bool:
translation_key="invalid_bucket_name",
) from err
except ValueError as err:
# pylint: disable-next=home-assistant-exception-translation-key-missing
raise ConfigEntryError(
translation_domain=DOMAIN,
translation_key="invalid_endpoint_url",
@@ -72,7 +72,7 @@ rules:
entity-device-class: done
entity-disabled-by-default: done
entity-translations: done
exception-translations: done
exception-translations: todo
icon-translations:
status: exempt
comment: This integration does not use icons.
@@ -75,11 +75,13 @@ def handle_backup_errors[_R, **P](
err.message,
exc_info=True,
)
# pylint: disable-next=home-assistant-exception-not-translated
raise BackupAgentError(
f"Error during backup operation in {func.__name__}:"
f" Status {err.status_code}, message: {err.message}"
) from err
except ServiceRequestError as err:
# pylint: disable-next=home-assistant-exception-not-translated
raise BackupAgentError(
f"Timeout during backup operation in {func.__name__}"
) from err
@@ -90,6 +92,7 @@ def handle_backup_errors[_R, **P](
err,
exc_info=True,
)
# pylint: disable-next=home-assistant-exception-not-translated
raise BackupAgentError(
f"Error during backup operation in {func.__name__}: {err}"
) from err
@@ -118,6 +121,7 @@ class AzureStorageBackupAgent(BackupAgent):
"""Download a backup file."""
blob = await self._find_blob_by_backup_id(backup_id)
if blob is None:
# pylint: disable-next=home-assistant-exception-not-translated
raise BackupNotFound(f"Backup {backup_id} not found")
download_stream = await self._client.download_blob(blob.name)
return download_stream.chunks()
@@ -155,6 +159,7 @@ class AzureStorageBackupAgent(BackupAgent):
"""Delete a backup file."""
blob = await self._find_blob_by_backup_id(backup_id)
if blob is None:
# pylint: disable-next=home-assistant-exception-not-translated
raise BackupNotFound(f"Backup {backup_id} not found")
await self._client.delete_blob(blob.name)
@@ -181,6 +186,7 @@ class AzureStorageBackupAgent(BackupAgent):
"""Return a backup."""
blob = await self._find_blob_by_backup_id(backup_id)
if blob is None:
# pylint: disable-next=home-assistant-exception-not-translated
raise BackupNotFound(f"Backup {backup_id} not found")
return AgentBackup.from_dict(json.loads(blob.metadata["backup_metadata"]))
@@ -89,6 +89,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: BackblazeConfigEntry) ->
translation_key="cannot_connect",
) from err
except exception.MissingAccountData as err:
# pylint: disable-next=home-assistant-exception-translation-key-missing
raise ConfigEntryAuthFailed(
translation_domain=DOMAIN,
translation_key="invalid_auth",
@@ -96,7 +96,7 @@ rules:
entity-translations:
status: exempt
comment: This integration does not have entities.
exception-translations: done
exception-translations: todo
icon-translations:
status: exempt
comment: This integration does not use icons.
@@ -67,6 +67,9 @@
"cannot_connect": {
"message": "Cannot connect to endpoint"
},
"invalid_auth": {
"message": "Authentication failed using the provided key ID and application key."
},
"invalid_bucket_name": {
"message": "Bucket does not exist or is not writable by the provided credentials."
},
+2
View File
@@ -169,6 +169,7 @@ class BlinkCamera(CoordinatorEntity[BlinkUpdateCoordinator], Camera):
try:
await self._camera.save_recent_clips(output_dir=file_path)
except OSError as err:
# pylint: disable-next=home-assistant-exception-message-with-translation
raise ServiceValidationError(
str(err),
translation_domain=DOMAIN,
@@ -190,6 +191,7 @@ class BlinkCamera(CoordinatorEntity[BlinkUpdateCoordinator], Camera):
try:
await self._camera.video_to_file(filename)
except OSError as err:
# pylint: disable-next=home-assistant-exception-message-with-translation
raise ServiceValidationError(
str(err),
translation_domain=DOMAIN,
@@ -183,6 +183,7 @@ class BSBLANClimate(BSBLanCircuitEntity, ClimateEntity):
try:
await self.coordinator.client.thermostat(**data, circuit=self._circuit)
except BSBLANError as err:
# pylint: disable-next=home-assistant-exception-message-with-translation
raise HomeAssistantError(
"An error occurred while updating the BSBLAN device",
translation_domain=DOMAIN,
@@ -94,7 +94,7 @@ rules:
entity-translations:
status: exempt
comment: This integration does not have entities.
exception-translations: done
exception-translations: todo
icon-translations:
status: exempt
comment: This integration does not use icons.
@@ -65,6 +65,7 @@ async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> dict[str,
translation_placeholders={"error": repr(err)},
) from err
except aiocomelit_exceptions.CannotAuthenticate as err:
# pylint: disable-next=home-assistant-exception-placeholder-mismatch
raise InvalidAuth(
translation_domain=DOMAIN,
translation_key="cannot_authenticate",
@@ -353,6 +353,7 @@ class EcovacsVacuum(
if self._capability.clean.action.area is None:
info = self._device.device_info
name = info.get("nick", info["name"])
# pylint: disable-next=home-assistant-exception-translation-key-missing
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="vacuum_send_command_area_not_supported",
@@ -88,7 +88,7 @@ rules:
entity-translations:
status: exempt
comment: This integration does not create its own entities.
exception-translations: done
exception-translations: todo
icon-translations:
status: exempt
comment: This integration does not create its own entities.
@@ -364,6 +364,7 @@ class ESPHomeManager:
response_dict = {"response": response}
except TemplateError as ex:
# pylint: disable-next=home-assistant-exception-not-translated
raise HomeAssistantError(
f"Error rendering response template: {ex}"
) from ex
@@ -17,7 +17,7 @@
"mqtt": ["esphome/discover/#"],
"quality_scale": "platinum",
"requirements": [
"aioesphomeapi==45.0.2",
"aioesphomeapi==45.0.4",
"esphome-dashboard-api==1.3.0",
"bleak-esphome==3.7.3"
],
@@ -63,7 +63,7 @@ rules:
comment: |
This integration does not have many entities. All of them are fundamental.
entity-translations: done
exception-translations: done
exception-translations: todo
icon-translations: done
reconfiguration-flow: done
repair-issues: todo
+9 -2
View File
@@ -61,13 +61,20 @@ async def async_setup_entry(hass: HomeAssistant, entry: FritzConfigEntry) -> boo
except FRITZ_AUTH_EXCEPTIONS as ex:
raise ConfigEntryAuthFailed from ex
except FRITZ_EXCEPTIONS as ex:
raise ConfigEntryNotReady from ex
raise ConfigEntryNotReady(
translation_domain=DOMAIN,
translation_key="error_connecting",
translation_placeholders={"error": str(ex)},
) from ex
if (
"X_AVM-DE_UPnP1" in avm_wrapper.connection.services
and not (await avm_wrapper.async_get_upnp_configuration())["NewEnable"]
):
raise ConfigEntryAuthFailed("Missing UPnP configuration")
raise ConfigEntryNotReady(
translation_domain=DOMAIN,
translation_key="error_upnp_disabled",
)
await avm_wrapper.async_config_entry_first_refresh()
+6 -3
View File
@@ -185,12 +185,18 @@
"config_entry_not_found": {
"message": "Failed to perform action \"{service}\". Config entry for target not found"
},
"error_connecting": {
"message": "Error connecting to the FRITZ!Box: {error}"
},
"error_parse_device_info": {
"message": "Error parsing device info. Please check the system event log of your FRITZ!Box for malformed data and clear the event list."
},
"error_refresh_hosts_info": {
"message": "Error refreshing hosts info"
},
"error_upnp_disabled": {
"message": "UPnP is disabled on the FRITZ!Box. Please enable UPnP to use this integration."
},
"service_dial_failed": {
"message": "Failed to dial, check if the click to dial service of the FRITZ!Box is activated"
},
@@ -200,9 +206,6 @@
"service_parameter_unknown": {
"message": "Action or parameter unknown"
},
"unable_to_connect": {
"message": "Unable to establish a connection"
},
"update_failed": {
"message": "Error while updating the data: {error}"
}
@@ -100,6 +100,7 @@ class GoogleDriveBackupAgent(BackupAgent):
try:
await self._client.async_upload_backup(wrapped_open_stream, backup)
except (GoogleDriveApiError, HomeAssistantError, TimeoutError) as err:
# pylint: disable-next=home-assistant-exception-not-translated
raise BackupAgentError(f"Failed to upload backup: {err}") from err
async def async_list_backups(self, **kwargs: Any) -> list[AgentBackup]:
@@ -107,6 +108,7 @@ class GoogleDriveBackupAgent(BackupAgent):
try:
return await self._client.async_list_backups()
except (GoogleDriveApiError, HomeAssistantError, TimeoutError) as err:
# pylint: disable-next=home-assistant-exception-not-translated
raise BackupAgentError(f"Failed to list backups: {err}") from err
async def async_get_backup(
@@ -119,6 +121,7 @@ class GoogleDriveBackupAgent(BackupAgent):
for backup in backups:
if backup.backup_id == backup_id:
return backup
# pylint: disable-next=home-assistant-exception-not-translated
raise BackupNotFound(f"Backup {backup_id} not found")
async def async_download_backup(
@@ -139,7 +142,9 @@ class GoogleDriveBackupAgent(BackupAgent):
stream = await self._client.async_download(file_id)
return ChunkAsyncStreamIterator(stream)
except (GoogleDriveApiError, HomeAssistantError, TimeoutError) as err:
# pylint: disable-next=home-assistant-exception-not-translated
raise BackupAgentError(f"Failed to download backup: {err}") from err
# pylint: disable-next=home-assistant-exception-not-translated
raise BackupNotFound(f"Backup {backup_id} not found")
async def async_delete_backup(
@@ -160,5 +165,7 @@ class GoogleDriveBackupAgent(BackupAgent):
_LOGGER.debug("Deleted backup_id: %s", backup_id)
return
except (GoogleDriveApiError, HomeAssistantError, TimeoutError) as err:
# pylint: disable-next=home-assistant-exception-not-translated
raise BackupAgentError(f"Failed to delete backup: {err}") from err
# pylint: disable-next=home-assistant-exception-not-translated
raise BackupNotFound(f"Backup {backup_id} not found")
@@ -84,6 +84,7 @@ async def _async_handle_upload(call: ServiceCall) -> ServiceResponse:
scopes = config_entry.data["token"]["scope"].split(" ")
if UPLOAD_SCOPE not in scopes:
# pylint: disable-next=home-assistant-exception-placeholder-mismatch
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="missing_upload_permission",
@@ -56,7 +56,7 @@ rules:
entity-device-class: done
entity-disabled-by-default: done
entity-translations: done
exception-translations: done
exception-translations: todo
icon-translations: done
reconfiguration-flow: done
repair-issues:
@@ -109,6 +109,7 @@ class HabiticaBaseNotifyEntity(HabiticaBase, NotifyEntity):
try:
await self._send_message(message)
except NotAuthorizedError as e:
# pylint: disable-next=home-assistant-exception-placeholder-mismatch
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="send_message_forbidden",
@@ -118,6 +119,7 @@ class HabiticaBaseNotifyEntity(HabiticaBase, NotifyEntity):
},
) from e
except NotFoundError as e:
# pylint: disable-next=home-assistant-exception-placeholder-mismatch
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="send_message_not_found",
@@ -266,6 +266,7 @@ class HomeConnectAirConditioningEntity(HomeConnectEntity, ClimateEntity):
value=BSH_POWER_ON,
)
except HomeConnectError as err:
# pylint: disable-next=home-assistant-exception-placeholder-mismatch
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="power_on",
@@ -52,9 +52,7 @@ reload_config_entry:
target:
fields:
entry_id:
advanced: true
required: false
example: 8955375327824e14ba89e4b29cc3ec9a
selector:
config_entry:
@@ -223,10 +223,10 @@
"name": "Reload all Home Assistant configuration"
},
"reload_config_entry": {
"description": "Reloads the specified config entry.",
"description": "Reloads any explicitly provided config entry ID and any config entries referenced by entities or devices in the target. If both are provided, the union of those config entries is reloaded.",
"fields": {
"entry_id": {
"description": "The configuration entry ID of the entry to be reloaded.",
"description": "Optional configuration entry ID to reload.",
"name": "Config entry ID"
}
},
@@ -59,7 +59,7 @@ rules:
entity-device-class: done
entity-disabled-by-default: done
entity-translations: done
exception-translations: done
exception-translations: todo
icon-translations: done
reconfiguration-flow: done
repair-issues: todo
@@ -50,12 +50,14 @@ def homevolt_exception_handler[_HomevoltEntityT: HomevoltEntity, **_P](
translation_key="auth_failed",
) from error
except HomevoltConnectionError as error:
# pylint: disable-next=home-assistant-exception-placeholder-mismatch
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="communication_error",
translation_placeholders={"error": str(error)},
) from error
except HomevoltError as error:
# pylint: disable-next=home-assistant-exception-placeholder-mismatch
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="unknown_error",
@@ -44,6 +44,7 @@ class HWEnergyDeviceUpdateCoordinator(DataUpdateCoordinator[DeviceResponseEntry]
data = await self.api.combined()
except RequestError as ex:
# pylint: disable-next=home-assistant-exception-message-with-translation
raise UpdateFailed(
ex, translation_domain=DOMAIN, translation_key="communication_error"
) from ex
@@ -60,6 +61,7 @@ class HWEnergyDeviceUpdateCoordinator(DataUpdateCoordinator[DeviceResponseEntry]
self.config_entry.entry_id
)
# pylint: disable-next=home-assistant-exception-message-with-translation
raise UpdateFailed(
ex, translation_domain=DOMAIN, translation_key="api_disabled"
) from ex
@@ -62,7 +62,7 @@ rules:
entity-device-class: done
entity-disabled-by-default: done
entity-translations: done
exception-translations: done
exception-translations: todo
icon-translations: done
reconfiguration-flow:
status: exempt
@@ -94,7 +94,7 @@ rules:
entity-translations:
status: exempt
comment: This integration does not have entities.
exception-translations: done
exception-translations: todo
icon-translations:
status: exempt
comment: This integration does not use icons.
@@ -62,7 +62,7 @@ rules:
comment: >
The device class is a service. When removed, entities are removed as well.
diagnostics: done
exception-translations: done
exception-translations: todo
icon-translations: done
reconfiguration-flow:
status: todo
@@ -73,6 +73,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: InComfortConfigEntry) ->
except InvalidHeaterList as exc:
raise NoHeaters from exc
except InvalidGateway as exc:
# pylint: disable-next=home-assistant-exception-not-translated
raise ConfigEntryAuthFailed("Incorrect credentials") from exc
except ClientResponseError as exc:
if exc.status == 404:
@@ -78,11 +78,15 @@ class InComfortDataCoordinator(DataUpdateCoordinator[InComfortData]):
for heater in self.incomfort_data.heaters:
await heater.update()
except TimeoutError as exc:
# pylint: disable-next=home-assistant-exception-not-translated
raise UpdateFailed("Timeout error") from exc
except ClientResponseError as exc:
if exc.status == 401:
# pylint: disable-next=home-assistant-exception-not-translated
raise ConfigEntryError("Incorrect credentials") from exc
# pylint: disable-next=home-assistant-exception-not-translated
raise UpdateFailed(exc.message) from exc
except InvalidHeaterList as exc:
# pylint: disable-next=home-assistant-exception-not-translated
raise UpdateFailed(exc.message) from exc
return self.incomfort_data
@@ -10,6 +10,7 @@ import voluptuous as vol
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_HOST, CONF_MODEL
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.service_info.dhcp import DhcpServiceInfo
from .const import CONF_GENERATION, CONF_SERIAL_NUMBER, DEFAULT_PORT, DOMAIN
@@ -21,6 +22,12 @@ class IndevoltConfigFlow(ConfigFlow, domain=DOMAIN):
VERSION = 1
def __init__(self) -> None:
"""Initialize the config flow."""
super().__init__()
self._discovered_host: str | None = None
self._discovered_device_data: dict[str, Any] | None = None
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
@@ -83,6 +90,55 @@ class IndevoltConfigFlow(ConfigFlow, domain=DOMAIN):
errors=errors,
)
async def async_step_dhcp(
self, discovery_info: DhcpServiceInfo
) -> ConfigFlowResult:
"""Handle DHCP discovery — probe the device to confirm it is an Indevolt device."""
host = discovery_info.ip
try:
device_data = await self._async_get_device_data(host)
except OSError, ClientError, KeyError:
return self.async_abort(reason="cannot_connect")
await self.async_set_unique_id(device_data[CONF_SERIAL_NUMBER])
self._abort_if_unique_id_configured(
updates={CONF_HOST: host}, reload_on_update=True
)
self.context["title_placeholders"] = {"model": device_data[CONF_MODEL]}
self._discovered_host = host
self._discovered_device_data = device_data
return await self.async_step_discovery_confirm()
async def async_step_discovery_confirm(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Confirm DHCP discovery by user."""
assert self._discovered_host is not None
assert self._discovered_device_data is not None
# Attempt to setup from user input
if user_input is not None:
return self.async_create_entry(
title=f"INDEVOLT {self._discovered_device_data[CONF_MODEL]}",
data={
CONF_HOST: self._discovered_host,
**self._discovered_device_data,
},
)
# Retrieve user confirmation
self._set_confirm_only()
return self.async_show_form(
step_id="discovery_confirm",
description_placeholders={
CONF_HOST: self._discovered_host,
CONF_MODEL: self._discovered_device_data[CONF_MODEL],
},
)
async def _async_validate_input(
self, user_input: dict[str, Any]
) -> tuple[dict[str, str], dict[str, Any] | None]:
@@ -3,6 +3,12 @@
"name": "Indevolt",
"codeowners": ["@xirt"],
"config_flow": true,
"dhcp": [
{ "macaddress": "1C784B*" },
{ "macaddress": "34EAE7*" },
{ "macaddress": "7C3E82*" },
{ "registered_devices": true }
],
"documentation": "https://www.home-assistant.io/integrations/indevolt",
"integration_type": "device",
"iot_class": "local_polling",
@@ -40,12 +40,8 @@ rules:
# Gold
devices: done
diagnostics: done
discovery-update-info:
status: exempt
comment: Integration does not support network discovery
discovery:
status: exempt
comment: Integration does not support network discovery
discovery-update-info: done
discovery: done
docs-data-update: done
docs-examples: done
docs-known-limitations: done
@@ -60,7 +56,7 @@ rules:
entity-device-class: done
entity-disabled-by-default: done
entity-translations: done
exception-translations: done
exception-translations: todo
icon-translations: done
reconfiguration-flow: done
repair-issues:
@@ -12,6 +12,10 @@
"unknown": "[%key:common::config_flow::error::unknown%]"
},
"step": {
"discovery_confirm": {
"description": "Do you want to add {model} ({host}) to Home Assistant?",
"title": "Discovered Indevolt {model}"
},
"reconfigure": {
"data": {
"host": "[%key:common::config_flow::data::host%]"
@@ -60,7 +60,7 @@ rules:
entity-device-class: done
entity-disabled-by-default: done
entity-translations: done
exception-translations: done
exception-translations: todo
icon-translations: done
reconfiguration-flow: todo
repair-issues: done
@@ -29,6 +29,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: IsraelRailConfigEntry) -
try:
await hass.async_add_executor_job(train_schedule.query, start, destination)
except Exception as e:
# pylint: disable-next=home-assistant-exception-translation-key-missing
raise ConfigEntryNotReady(
translation_domain=DOMAIN,
translation_key="request_timeout",
+1 -2
View File
@@ -7,6 +7,7 @@ import voluptuous as vol
from homeassistant.const import (
CONF_ADDRESS,
CONF_CODE,
CONF_COMMAND,
CONF_NAME,
CONF_UNIT_OF_MEASUREMENT,
@@ -38,8 +39,6 @@ SERVICE_DELETE_ZWAVE_LOCK_USER_CODE = "delete_zwave_lock_user_code"
CONF_PARAMETER = "parameter"
CONF_PARAMETERS = "parameters"
CONF_USER_NUM = "user_num"
# pylint: disable-next=home-assistant-duplicate-const
CONF_CODE = "code"
CONF_VALUE = "value"
CONF_INIT = "init"
CONF_ISY = "isy"
@@ -115,6 +115,7 @@ async def async_attach_trigger(
try:
trigger_config = TRIGGER_TRIGGER_SCHEMA(trigger_config)
except vol.Invalid as err:
# pylint: disable-next=home-assistant-exception-not-translated
raise InvalidDeviceAutomationConfig(f"{err}") from err
return await trigger.async_attach_trigger(
@@ -66,7 +66,7 @@ rules:
entity-device-class: done
entity-disabled-by-default: done
entity-translations: done
exception-translations: done
exception-translations: todo
icon-translations: done
reconfiguration-flow: todo
repair-issues: todo
@@ -105,6 +105,7 @@ class ThinQEntity(CoordinatorEntity[DeviceDataUpdateCoordinator]):
except ThinQAPIException as exc:
if on_fail_method:
on_fail_method()
# pylint: disable-next=home-assistant-exception-message-with-translation
raise ServiceValidationError(
exc.message, translation_domain=DOMAIN, translation_key=exc.code
) from exc
@@ -44,8 +44,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: LiebherrConfigEntry) ->
try:
devices = await client.get_devices()
except LiebherrAuthenticationError as err:
# pylint: disable-next=home-assistant-exception-not-translated
raise ConfigEntryAuthFailed("Invalid API key") from err
except LiebherrConnectionError as err:
# pylint: disable-next=home-assistant-exception-not-translated
raise ConfigEntryNotReady(f"Failed to connect to Liebherr API: {err}") from err
# Create a coordinator for each device (may be empty if no devices)
@@ -58,8 +58,10 @@ class LiebherrCoordinator(DataUpdateCoordinator[DeviceState]):
try:
await self.client.get_device(self.device_id)
except LiebherrAuthenticationError as err:
# pylint: disable-next=home-assistant-exception-not-translated
raise ConfigEntryAuthFailed("Invalid API key") from err
except LiebherrConnectionError as err:
# pylint: disable-next=home-assistant-exception-not-translated
raise ConfigEntryNotReady(
f"Failed to connect to device {self.device_id}: {err}"
) from err
@@ -69,12 +71,15 @@ class LiebherrCoordinator(DataUpdateCoordinator[DeviceState]):
try:
return await self.client.get_device_state(self.device_id)
except LiebherrAuthenticationError as err:
# pylint: disable-next=home-assistant-exception-not-translated
raise ConfigEntryAuthFailed("API key is no longer valid") from err
except LiebherrTimeoutError as err:
# pylint: disable-next=home-assistant-exception-not-translated
raise UpdateFailed(
f"Timeout communicating with device {self.device_id}"
) from err
except LiebherrConnectionError as err:
# pylint: disable-next=home-assistant-exception-not-translated
raise UpdateFailed(
f"Error communicating with device {self.device_id}"
) from err
@@ -15,6 +15,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Local file from a config entry."""
file_path: str = entry.options[CONF_FILE_PATH]
if not await hass.async_add_executor_job(check_file_path_access, file_path):
# pylint: disable-next=home-assistant-exception-translation-key-missing
raise ConfigEntryError(
translation_domain=DOMAIN,
translation_key="not_readable_path",
@@ -77,6 +77,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: LunatoneConfigEntry) ->
await coordinator_info.async_config_entry_first_refresh()
if info_api.data is None or info_api.serial_number is None:
# pylint: disable-next=home-assistant-exception-translation-key-missing
raise ConfigEntryError(
translation_domain=DOMAIN, translation_key="missing_device_info"
)
@@ -422,6 +422,7 @@ class ManualAlarm(AlarmControlPanelEntity, RestoreEntity):
},
)
# pylint: disable-next=home-assistant-exception-message-with-translation
raise ServiceValidationError(
"Invalid alarm code provided",
translation_domain=DOMAIN,
+2
View File
@@ -165,6 +165,7 @@ class MieleFan(MieleEntity, FanEntity):
try:
await self.api.send_action(self._device_id, {POWER_ON: True})
except ClientResponseError as ex:
# pylint: disable-next=home-assistant-exception-placeholder-mismatch
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="set_state_error",
@@ -182,6 +183,7 @@ class MieleFan(MieleEntity, FanEntity):
try:
await self.api.send_action(self._device_id, {POWER_OFF: True})
except ClientResponseError as ex:
# pylint: disable-next=home-assistant-exception-placeholder-mismatch
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="set_state_error",
@@ -60,7 +60,7 @@ rules:
entity-device-class: done
entity-disabled-by-default: done
entity-translations: done
exception-translations: done
exception-translations: todo
icon-translations: done
reconfiguration-flow: todo
repair-issues:
+3 -2
View File
@@ -291,12 +291,13 @@ async def async_check_config_schema(
message = conf_util.format_schema_error(
hass, exc, domain, config, integration.documentation
)
# pylint: disable-next=home-assistant-exception-message-with-translation
raise ServiceValidationError(
message,
translation_domain=DOMAIN,
translation_key="invalid_platform_config",
translation_key="invalid_platform_config_message",
translation_placeholders={
"domain": domain,
"message": message,
},
) from exc
+4 -4
View File
@@ -156,8 +156,8 @@ async def async_publish(
) -> None:
"""Publish message to a MQTT topic."""
if not mqtt_config_entry_enabled(hass):
# pylint: disable-next=home-assistant-exception-message-with-translation
raise HomeAssistantError(
f"Cannot publish to topic '{topic}', MQTT is not enabled",
translation_key="mqtt_not_setup_cannot_publish",
translation_domain=DOMAIN,
translation_placeholders={"topic": topic},
@@ -281,17 +281,17 @@ def async_subscribe_internal(
try:
mqtt_data = hass.data[DATA_MQTT]
except KeyError as exc:
# pylint: disable-next=home-assistant-exception-message-with-translation
raise HomeAssistantError(
f"Cannot subscribe to topic '{topic}', make sure MQTT is set up correctly",
translation_key="mqtt_not_setup_cannot_subscribe",
translation_domain=DOMAIN,
translation_placeholders={"topic": topic},
) from exc
client = mqtt_data.client
if not mqtt_config_entry_enabled(hass):
# pylint: disable-next=home-assistant-exception-message-with-translation
raise HomeAssistantError(
f"Cannot subscribe to topic '{topic}', MQTT is not enabled",
translation_key="mqtt_not_setup_cannot_subscribe",
translation_key="mqtt_not_enabled_cannot_subscribe",
translation_domain=DOMAIN,
translation_placeholders={"topic": topic},
)
+1 -2
View File
@@ -73,9 +73,8 @@ class SubscriptionID:
subscription_id = self._next_id
if subscription_id > MAX_28BIT:
# pylint: disable-next=home-assistant-exception-message-with-translation
raise HomeAssistantError(
"MQTT Subscription ID limit reached. "
"Cannot generate more IDs to subscribe",
translation_domain=DOMAIN,
translation_key="mqtt_max_subscription_id_reached",
)
+21 -23
View File
@@ -11,29 +11,27 @@ publish:
example: "The temperature is {{ states('sensor.temperature') }}"
selector:
template:
publish_options:
collapsed: true
fields:
evaluate_payload:
default: false
selector:
boolean:
qos:
default: "0"
selector:
select:
options:
- "0"
- "1"
- "2"
retain:
default: false
selector:
boolean:
message_expiry_interval:
selector:
duration:
enable_day: true
evaluate_payload:
default: false
selector:
boolean:
qos:
default: 0
selector:
select:
options:
- "0"
- "1"
- "2"
retain:
default: false
selector:
boolean:
message_expiry_interval:
selector:
duration:
enable_day: true
dump:
fields:
topic:
+6 -8
View File
@@ -1090,8 +1090,8 @@
"command_template_error": {
"message": "Parsing template `{command_template}` for entity `{entity_id}` failed with error: {error}."
},
"invalid_platform_config": {
"message": "Reloading YAML config for manually configured MQTT `{domain}` item failed. See logs for more details."
"invalid_platform_config_message": {
"message": "Reloading YAML config for manually configured MQTT `{domain}` item failed. Message: {message}"
},
"invalid_publish_topic": {
"message": "Unable to publish: topic template `{topic_template}` produced an invalid topic `{topic}` after rendering ({error})"
@@ -1105,6 +1105,9 @@
"mqtt_message_expiry_interval_not_supported": {
"message": "Publishing to topic {topic} with a Message Expiry Interval is not supported for protocol version {protocol}."
},
"mqtt_not_enabled_cannot_subscribe": {
"message": "Cannot subscribe to topic \"{topic}\" because MQTT is not enabled, make sure MQTT is set up correctly."
},
"mqtt_not_setup_cannot_publish": {
"message": "Cannot publish to topic \"{topic}\", make sure MQTT is set up correctly."
},
@@ -1575,12 +1578,7 @@
"name": "Topic"
}
},
"name": "Publish",
"sections": {
"publish_options": {
"name": "Publish options"
}
}
"name": "Publish"
},
"reload": {
"description": "Reloads MQTT entities from the YAML-configuration.",
@@ -50,6 +50,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: NASwebConfigEntry) -> bo
)
if not await webio_api.refresh_device_info():
_LOGGER.error("[%s] Refresh device info failed", entry.data[CONF_HOST])
# pylint: disable-next=home-assistant-exception-translation-key-domain-mismatch
raise ConfigEntryError(
translation_key="config_entry_error_internal_error",
translation_placeholders={"support_email": SUPPORT_EMAIL},
@@ -57,6 +58,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: NASwebConfigEntry) -> bo
webio_serial = webio_api.get_serial_number()
if webio_serial is None:
_LOGGER.error("[%s] Serial number not available", entry.data[CONF_HOST])
# pylint: disable-next=home-assistant-exception-translation-key-domain-mismatch
raise ConfigEntryError(
translation_key="config_entry_error_internal_error",
translation_placeholders={"support_email": SUPPORT_EMAIL},
@@ -65,6 +67,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: NASwebConfigEntry) -> bo
_LOGGER.error(
"[%s] Serial number doesn't match config entry", entry.data[CONF_HOST]
)
# pylint: disable-next=home-assistant-exception-translation-key-domain-mismatch
raise ConfigEntryError(translation_key="config_entry_error_serial_mismatch")
coordinator = NASwebCoordinator(
@@ -76,12 +79,14 @@ async def async_setup_entry(hass: HomeAssistant, entry: NASwebConfigEntry) -> bo
webhook_url = nasweb_data.get_webhook_url(hass)
if not await webio_api.status_subscription(webhook_url, True):
_LOGGER.error("Failed to subscribe for status updates from webio")
# pylint: disable-next=home-assistant-exception-translation-key-domain-mismatch
raise ConfigEntryError(
translation_key="config_entry_error_internal_error",
translation_placeholders={"support_email": SUPPORT_EMAIL},
)
if not await nasweb_data.notify_coordinator.check_connection(webio_serial):
_LOGGER.error("Did not receive status from device")
# pylint: disable-next=home-assistant-exception-translation-key-domain-mismatch
raise ConfigEntryError(
translation_key="config_entry_error_no_status_update",
translation_placeholders={"support_email": SUPPORT_EMAIL},
@@ -91,10 +96,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: NASwebConfigEntry) -> bo
f"[{entry.data[CONF_HOST]}] Check connection reached timeout"
) from error
except AuthError as error:
# pylint: disable-next=home-assistant-exception-translation-key-domain-mismatch
raise ConfigEntryError(
translation_key="config_entry_error_invalid_authentication"
) from error
except NoURLAvailableError as error:
# pylint: disable-next=home-assistant-exception-translation-key-domain-mismatch
raise ConfigEntryError(
translation_key="config_entry_error_missing_internal_url"
) from error
@@ -66,7 +66,7 @@ rules:
entity-translations:
status: exempt
comment: Entities use device name as entity name.
exception-translations: done
exception-translations: todo
icon-translations:
status: exempt
comment: No entity icons are used.
@@ -160,8 +160,8 @@ class NFAndroidTVNotificationService(BaseNotificationService):
auth=imagedata.get(ATTR_IMAGE_AUTH),
)
else:
# pylint: disable-next=home-assistant-exception-message-with-translation
raise ServiceValidationError(
"Invalid image provided",
translation_domain=DOMAIN,
translation_key="invalid_notification_image",
translation_placeholders={"type": type(imagedata).__name__},
@@ -182,8 +182,8 @@ class NFAndroidTVNotificationService(BaseNotificationService):
auth=icondata.get(ATTR_ICON_AUTH),
)
else:
# pylint: disable-next=home-assistant-exception-message-with-translation
raise ServiceValidationError(
"Invalid Icon provided",
translation_domain=DOMAIN,
translation_key="invalid_notification_icon",
translation_placeholders={"type": type(icondata).__name__},
@@ -52,6 +52,7 @@ class NintendoUpdateCoordinator(DataUpdateCoordinator[None]):
try:
return await self.api.update()
except InvalidOAuthConfigurationException as err:
# pylint: disable-next=home-assistant-exception-translation-key-missing
raise ConfigEntryError(
translation_domain=DOMAIN,
translation_key="invalid_auth",
@@ -57,7 +57,7 @@ rules:
entity-device-class: done
entity-disabled-by-default: done
entity-translations: done
exception-translations: done
exception-translations: todo
icon-translations:
status: exempt
comment: |
@@ -65,7 +65,7 @@ rules:
PR #170135.
entity-disabled-by-default: todo
entity-translations: todo
exception-translations: done
exception-translations: todo
icon-translations: todo
reconfiguration-flow: todo
repair-issues:
@@ -66,7 +66,7 @@ rules:
entity-device-class: done
entity-disabled-by-default: done
entity-translations: done
exception-translations: done
exception-translations: todo
icon-translations: done
reconfiguration-flow: done
repair-issues: todo
@@ -81,6 +81,7 @@ def handle_backup_errors[_R, **P](
return await func(self, *args, **kwargs)
except AuthenticationError as err:
self._entry.async_start_reauth(self._hass)
# pylint: disable-next=home-assistant-exception-not-translated
raise BackupAgentError("Authentication error") from err
except OneDriveException as err:
_LOGGER.error(
@@ -89,12 +90,14 @@ def handle_backup_errors[_R, **P](
err,
)
_LOGGER.debug("Full error: %s", err, exc_info=True)
# pylint: disable-next=home-assistant-exception-not-translated
raise BackupAgentError("Backup operation failed") from err
except TimeoutError as err:
_LOGGER.error(
"Error during backup in %s: Timeout",
func.__name__,
)
# pylint: disable-next=home-assistant-exception-not-translated
raise BackupAgentError("Backup operation timed out") from err
return wrapper
@@ -183,6 +186,7 @@ class OneDriveBackupAgent(BackupAgent):
),
)
except HashMismatchError as err:
# pylint: disable-next=home-assistant-exception-not-translated
raise BackupAgentError(
"Hash validation failed, backup file might be corrupt"
) from err
@@ -292,4 +296,5 @@ class OneDriveBackupAgent(BackupAgent):
if backup := metadata_files.get(backup_id):
return backup
# pylint: disable-next=home-assistant-exception-not-translated
raise BackupNotFound(f"Backup {backup_id} not found")
@@ -81,6 +81,7 @@ def handle_backup_errors[_R, **P](
return await func(self, *args, **kwargs)
except AuthenticationError as err:
self._entry.async_start_reauth(self._hass)
# pylint: disable-next=home-assistant-exception-not-translated
raise BackupAgentError("Authentication error") from err
except OneDriveException as err:
_LOGGER.error(
@@ -89,12 +90,14 @@ def handle_backup_errors[_R, **P](
err,
)
_LOGGER.debug("Full error: %s", err, exc_info=True)
# pylint: disable-next=home-assistant-exception-not-translated
raise BackupAgentError("Backup operation failed") from err
except TimeoutError as err:
_LOGGER.error(
"Error during backup in %s: Timeout",
func.__name__,
)
# pylint: disable-next=home-assistant-exception-not-translated
raise BackupAgentError("Backup operation timed out") from err
return wrapper
@@ -177,6 +180,7 @@ class OneDriveBackupAgent(BackupAgent):
),
)
except HashMismatchError as err:
# pylint: disable-next=home-assistant-exception-not-translated
raise BackupAgentError(
"Hash validation failed, backup file might be corrupt"
) from err
@@ -280,4 +284,5 @@ class OneDriveBackupAgent(BackupAgent):
if backup := metadata_files.get(backup_id):
return backup
# pylint: disable-next=home-assistant-exception-not-translated
raise BackupNotFound(f"Backup {backup_id} not found")
@@ -57,7 +57,7 @@ rules:
entity-device-class: done
entity-disabled-by-default: done
entity-translations: done
exception-translations: done
exception-translations: todo
icon-translations: done
reconfiguration-flow:
status: exempt
@@ -64,6 +64,7 @@ class OpenRGBCoordinator(DataUpdateCoordinator[dict[str, Device]]):
DEFAULT_CLIENT_NAME,
)
except CONNECTION_ERRORS as err:
# pylint: disable-next=home-assistant-exception-translation-key-missing
raise UpdateFailed(
translation_domain=DOMAIN,
translation_key="cannot_connect",
@@ -111,6 +111,7 @@ class OpowerCoordinator(DataUpdateCoordinator[dict[str, OpowerData]]):
raise ConfigEntryAuthFailed from err
except CannotConnect as err:
_LOGGER.error("Error during login: %s", err)
# pylint: disable-next=home-assistant-exception-not-translated
raise UpdateFailed(f"Error during login: {err}") from err
try:
@@ -65,7 +65,7 @@ rules:
entity-device-class: done
entity-disabled-by-default: todo
entity-translations: done
exception-translations: done
exception-translations: todo
icon-translations: done
reconfiguration-flow: todo
repair-issues:
@@ -48,10 +48,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: PeblarConfigEntry) -> bo
system_information = await peblar.system_information()
api = await peblar.rest_api(enable=True, access_mode=AccessMode.READ_WRITE)
except PeblarConnectionError as err:
# pylint: disable-next=home-assistant-exception-not-translated
raise ConfigEntryNotReady("Could not connect to Peblar charger") from err
except PeblarAuthenticationError as err:
raise ConfigEntryAuthFailed from err
except PeblarError as err:
# pylint: disable-next=home-assistant-exception-not-translated
raise ConfigEntryNotReady(
"Unknown error occurred while connecting to Peblar charger"
) from err
@@ -64,15 +64,18 @@ async def async_setup_entry(hass: HomeAssistant, entry: PooldoseConfigEntry) ->
try:
client_status = await client.connect()
except TimeoutError as err:
# pylint: disable-next=home-assistant-exception-not-translated
raise ConfigEntryNotReady(
f"Timeout connecting to PoolDose device: {err}"
) from err
except (ConnectionError, OSError) as err:
# pylint: disable-next=home-assistant-exception-not-translated
raise ConfigEntryNotReady(
f"Failed to connect to PoolDose device: {err}"
) from err
if client_status != RequestStatus.SUCCESS:
# pylint: disable-next=home-assistant-exception-not-translated
raise ConfigEntryNotReady(
f"Failed to create PoolDose client while initialization: {client_status}"
)
@@ -49,18 +49,22 @@ class PooldoseCoordinator(DataUpdateCoordinator[StructuredValuesDict]):
try:
status, instant_values = await self.client.instant_values_structured()
except TimeoutError as err:
# pylint: disable-next=home-assistant-exception-not-translated
raise UpdateFailed(
f"Timeout fetching data from PoolDose device: {err}"
) from err
except (ConnectionError, OSError) as err:
# pylint: disable-next=home-assistant-exception-not-translated
raise UpdateFailed(
f"Failed to connect to PoolDose device while fetching data: {err}"
) from err
if status != RequestStatus.SUCCESS:
# pylint: disable-next=home-assistant-exception-not-translated
raise UpdateFailed(f"API returned status: {status}")
if not instant_values:
# pylint: disable-next=home-assistant-exception-not-translated
raise UpdateFailed("No data received from API")
_LOGGER.debug("Instant values structured: %s", instant_values)
@@ -58,12 +58,14 @@ class PTDevicesCoordinator(DataUpdateCoordinator[PTDevicesResponseData]):
try:
data = await self.interface.get_data()
except aioptdevices.PTDevicesRequestError as err:
# pylint: disable-next=home-assistant-exception-placeholder-mismatch
raise UpdateFailed(
translation_domain=DOMAIN,
translation_key="cannot_connect",
translation_placeholders={"error": repr(err)},
) from err
except aioptdevices.PTDevicesUnauthorizedError as err:
# pylint: disable-next=home-assistant-exception-placeholder-mismatch
raise UpdateFailed(
translation_domain=DOMAIN,
translation_key="invalid_access_token",
@@ -52,7 +52,7 @@ rules:
entity-device-class: done
entity-disabled-by-default: done
entity-translations: done
exception-translations: done
exception-translations: todo
icon-translations: done
reconfiguration-flow: done
repair-issues: done
@@ -74,6 +74,7 @@ async def async_setup_entry(
await host.async_init()
except (UserNotAdmin, CredentialsInvalidError, PasswordIncompatible) as err:
await host.stop()
# pylint: disable-next=home-assistant-exception-not-translated
raise ConfigEntryAuthFailed(err) from err
except (
ReolinkException,
@@ -88,12 +88,15 @@ class ReolinkDeviceCoordinator(ReolinkCoordinator):
self._host.credential_errors += 1
if self._host.credential_errors >= NUM_CRED_ERRORS:
await self._host.stop()
# pylint: disable-next=home-assistant-exception-not-translated
raise ConfigEntryAuthFailed(err) from err
# pylint: disable-next=home-assistant-exception-not-translated
raise UpdateFailed(str(err)) from err
except LoginPrivacyModeError:
pass # HTTP API is shutdown when privacy mode is active
except ReolinkError as err:
self._host.credential_errors = 0
# pylint: disable-next=home-assistant-exception-not-translated
raise UpdateFailed(str(err)) from err
self._host.credential_errors = 0
@@ -167,6 +170,7 @@ class ReolinkFirmwareCoordinator(ReolinkCoordinator):
)
return
# pylint: disable-next=home-assistant-exception-not-translated
raise UpdateFailed(
"Error checking Reolink firmware update"
f" from {self._host.api.nvr_name}, "
+4
View File
@@ -171,6 +171,7 @@ class ReolinkHost:
translation_placeholders={"name": self._config_entry.title},
)
# pylint: disable-next=home-assistant-exception-not-translated
raise PasswordIncompatible(
"Reolink password contains incompatible special character or "
"is too long, please change the password to only contain characters: "
@@ -192,9 +193,11 @@ class ReolinkHost:
await self._api.get_host_data()
if self._api.mac_address is None:
# pylint: disable-next=home-assistant-exception-not-translated
raise ReolinkSetupException("Could not get mac address")
if not self._api.is_admin:
# pylint: disable-next=home-assistant-exception-not-translated
raise UserNotAdmin(
f"User '{self._api.username}' has authorization level "
f"'{self._api.user_level}', only admin users can change camera settings"
@@ -739,6 +742,7 @@ class ReolinkHost:
self._base_url = get_url(self._hass, prefer_external=True)
except NoURLAvailableError as err:
self.unregister_webhook()
# pylint: disable-next=home-assistant-exception-not-translated
raise ReolinkWebhookException(
f"Error registering URL for webhook {event_id}: "
"HomeAssistant URL is not available"
@@ -63,6 +63,7 @@ class ReolinkVODMediaSource(MediaSource):
if item.identifier is not None:
identifier = item.identifier.split("|", 6)
if identifier[0] != "FILE":
# pylint: disable-next=home-assistant-exception-not-translated
raise Unresolvable(f"Unknown media item '{item.identifier}'.")
_, config_entry_id, channel_str, stream_res, filename, start_time, end_time = (
@@ -172,6 +173,7 @@ class ReolinkVODMediaSource(MediaSource):
event,
)
# pylint: disable-next=home-assistant-exception-not-translated
raise Unresolvable(f"Unknown media item '{item.identifier}' during browsing.")
async def _async_generate_root(self) -> BrowseMediaSource:
+1
View File
@@ -62,6 +62,7 @@ def get_host(hass: HomeAssistant, config_entry_id: str) -> ReolinkHost:
config_entry_id
)
if config_entry is None:
# pylint: disable-next=home-assistant-exception-not-translated
raise Unresolvable(
f"Could not find Reolink config entry id '{config_entry_id}'."
)
@@ -95,6 +95,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: RoborockConfigEntry) ->
prefer_cache=False,
)
except RoborockInvalidCredentials as err:
# pylint: disable-next=home-assistant-exception-message-with-translation
raise ConfigEntryAuthFailed(
"Invalid credentials",
translation_domain=DOMAIN,
@@ -117,6 +118,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: RoborockConfigEntry) ->
) from err
except RoborockException as err:
_LOGGER.debug("Failed to get Roborock home data: %s", err)
# pylint: disable-next=home-assistant-exception-message-with-translation
raise ConfigEntryNotReady(
"Failed to get Roborock home data",
translation_domain=DOMAIN,
@@ -176,6 +178,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: RoborockConfigEntry) ->
len(v1_coords) + len(a01_coords) + len(b01_q7_coords) + len(b01_q10_coords) == 0
and enabled_devices
):
# pylint: disable-next=home-assistant-exception-message-with-translation
raise ConfigEntryNotReady(
"No devices were able to successfully setup",
translation_domain=DOMAIN,
@@ -161,6 +161,7 @@ class RoborockDataUpdateCoordinator(DataUpdateCoordinator[DeviceState | None]):
_LOGGER.info("Home discovery skipped while device is busy/cleaning")
except RoborockException as err:
_LOGGER.debug("Failed to get maps: %s", err)
# pylint: disable-next=home-assistant-exception-placeholder-mismatch
raise UpdateFailed(
translation_domain=DOMAIN,
translation_key="map_failure",
@@ -62,7 +62,7 @@ rules:
status: exempt
comment: There are no noisy entities.
entity-translations: done
exception-translations: done
exception-translations: todo
icon-translations: todo
reconfiguration-flow: todo
repair-issues: done
@@ -43,6 +43,7 @@ async def async_validate_trigger_config(
device = async_get_device_entry_by_device_id(hass, device_id)
async_get_client_by_device_entry(hass, device)
except ValueError as err:
# pylint: disable-next=home-assistant-exception-not-translated
raise InvalidDeviceAutomationConfig(err) from err
return config
@@ -39,6 +39,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: LeilSaunaConfigEntry) ->
try:
client = await SaunumClient.create(host)
except (SaunumConnectionError, SaunumTimeoutError) as exc:
# pylint: disable-next=home-assistant-exception-not-translated
raise ConfigEntryNotReady(f"Error connecting to {host}: {exc}") from exc
entry.async_on_unload(client.async_close)
@@ -111,7 +111,7 @@ rules:
status: exempt
comment: |
This integration does not have entities.
exception-translations: done
exception-translations: todo
icon-translations:
status: exempt
comment: |
@@ -401,6 +401,7 @@ class ShellyBlockCoordinator(ShellyCoordinatorBase[BlockDevice]):
"""Fetch data."""
if self.sleep_period:
# Sleeping device, no point polling it, just mark it unavailable
# pylint: disable-next=home-assistant-exception-placeholder-mismatch
raise UpdateFailed(
translation_domain=DOMAIN,
translation_key="update_error_sleeping_device",
@@ -670,6 +671,7 @@ class ShellyRpcCoordinator(ShellyCoordinatorBase[RpcDevice]):
if self.sleep_period:
# Sleeping device, no point polling it, just mark it unavailable
# pylint: disable-next=home-assistant-exception-placeholder-mismatch
raise UpdateFailed(
translation_domain=DOMAIN,
translation_key="update_error_sleeping_device",
+43 -10
View File
@@ -2,6 +2,7 @@
import base64
import binascii
import contextlib
from dataclasses import dataclass
import datetime
import hashlib
@@ -38,6 +39,15 @@ from .utils import get_device_entry_gen
CONTENT_TYPE_AUDIO = "audio"
CONTENT_TYPE_RADIO = "radio"
ALLOWED_IMAGE_MIME_TYPES: Final = frozenset(
{
"image/gif",
"image/jpeg",
"image/png",
"image/webp",
}
)
PARALLEL_UPDATES = 0
@@ -102,6 +112,9 @@ class ShellyRpcMediaPlayer(ShellyRpcAttributeEntity, MediaPlayerEntity):
_last_media_position: int | None = None
_last_media_position_updated_at: datetime.datetime | None = None
_cached_thumb: str | None = None
_cached_thumb_result: tuple[bytes, str] | None = None
def __init__(
self,
coordinator: ShellyRpcCoordinator,
@@ -215,9 +228,11 @@ class ShellyRpcMediaPlayer(ShellyRpcAttributeEntity, MediaPlayerEntity):
@property
def media_image_hash(self) -> str | None:
"""Hash value for media image."""
if (thumb := self._media_meta.get("thumb")) and thumb.startswith("data"):
return hashlib.sha256(thumb.encode("utf-8")).hexdigest()[:16]
return super().media_image_hash
thumb = self._media_meta.get("thumb")
if not thumb or self._decode_image_data(thumb) is None:
return super().media_image_hash
return hashlib.sha256(thumb.encode("utf-8")).hexdigest()[:16]
def _get_updated_media_position(self) -> int | None:
"""Return the current playback position and update its timestamp."""
@@ -235,15 +250,11 @@ class ShellyRpcMediaPlayer(ShellyRpcAttributeEntity, MediaPlayerEntity):
async def async_get_media_image(self) -> tuple[bytes | None, str | None]:
"""Fetch media image of current playing track."""
thumb = self._media_meta["thumb"]
try:
prefix, image_data = thumb.split(",", 1)
image = base64.b64decode(image_data, validate=True)
mime = prefix.split(";", 1)[0].rsplit(":", 1)[-1]
except binascii.Error, ValueError:
thumb = self._media_meta.get("thumb")
if not thumb or (result := self._decode_image_data(thumb)) is None:
return await super().async_get_media_image()
return image, mime
return result
@rpc_call
async def async_media_play(self) -> None:
@@ -434,3 +445,25 @@ class ShellyRpcMediaPlayer(ShellyRpcAttributeEntity, MediaPlayerEntity):
translation_key="unsupported_media_type",
translation_placeholders={"media_type": str(media_type)},
)
def _decode_image_data(self, thumb: str) -> tuple[bytes, str] | None:
"""Return image_bytes and mime_type for a valid image data or None."""
if thumb == self._cached_thumb:
return self._cached_thumb_result
result: tuple[bytes, str] | None = None
if thumb.startswith("data"):
try:
prefix, image_data = thumb.split(",", 1)
mime = prefix.split(";", 1)[0].rsplit(":", 1)[-1]
except IndexError, ValueError:
pass
else:
if mime in ALLOWED_IMAGE_MIME_TYPES:
with contextlib.suppress(binascii.Error):
result = base64.b64decode(image_data, validate=True), mime
self._cached_thumb = thumb
self._cached_thumb_result = result
return result
@@ -344,6 +344,7 @@ class RpcUpdateEntity(ShellyRpcAttributeEntity, UpdateEntity):
translation_placeholders={"device": self.coordinator.name},
) from err
except RpcCallError as err:
# pylint: disable-next=home-assistant-exception-placeholder-mismatch
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="ota_update_rpc_error",
@@ -69,12 +69,14 @@ class SMADataUpdateCoordinator(DataUpdateCoordinator[SMACoordinatorData]):
SmaConnectionException,
) as err:
await self.async_close_sma_session()
# pylint: disable-next=home-assistant-exception-translation-key-missing
raise ConfigEntryNotReady(
translation_domain=DOMAIN,
translation_key="cannot_connect",
translation_placeholders={"error": repr(err)},
) from err
except SmaAuthenticationException as err:
# pylint: disable-next=home-assistant-exception-translation-key-missing
raise ConfigEntryAuthFailed(
translation_domain=DOMAIN,
translation_key="invalid_auth",
@@ -89,12 +91,14 @@ class SMADataUpdateCoordinator(DataUpdateCoordinator[SMACoordinatorData]):
SmaReadException,
SmaConnectionException,
) as err:
# pylint: disable-next=home-assistant-exception-translation-key-missing
raise UpdateFailed(
translation_domain=DOMAIN,
translation_key="cannot_connect",
translation_placeholders={"error": repr(err)},
) from err
except SmaAuthenticationException as err:
# pylint: disable-next=home-assistant-exception-translation-key-missing
raise ConfigEntryAuthFailed(
translation_domain=DOMAIN,
translation_key="invalid_auth",
@@ -67,6 +67,14 @@
}
}
},
"exceptions": {
"cannot_connect": {
"message": "Could not connect to SMA device - {error}"
},
"invalid_auth": {
"message": "Invalid authentication for SMA device - {error}"
}
},
"selector": {
"group": {
"options": {
@@ -48,7 +48,7 @@ rules:
entity-device-class: done
entity-disabled-by-default: done
entity-translations: done
exception-translations: done
exception-translations: todo
icon-translations: done
reconfiguration-flow: todo
repair-issues: todo
+2
View File
@@ -80,6 +80,7 @@ class SnooSwitch(SnooDescriptionEntity, SwitchEntity):
True,
)
except SnooCommandException as err:
# pylint: disable-next=home-assistant-exception-placeholder-mismatch
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="switch_on_failed",
@@ -96,6 +97,7 @@ class SnooSwitch(SnooDescriptionEntity, SwitchEntity):
False,
)
except SnooCommandException as err:
# pylint: disable-next=home-assistant-exception-placeholder-mismatch
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="switch_off_failed",

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