-
-
Notifications
You must be signed in to change notification settings - Fork 34.2k
Enable Pihole API v6 #145890
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Enable Pihole API v6 #145890
Conversation
Hey there @shenxn, mind taking a look at this pull request as it has been labeled with an integration ( Code owner commandsCode owners of
|
@@ -101,25 +104,6 @@ async def async_step_user( | |||
errors=errors, | |||
) | |||
|
|||
async def async_step_api_key( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have removed the API key as a separate step for a few reasons
- The info in the previous step is not required to accept an api_key
- Due to the ambiguity between V5 and V6 APIs it is not easy to determine connectivity and the API version separately
pi_hole = self._api_by_version(5) | ||
await pi_hole.get_data() | ||
if pi_hole.data is not None and "error" in pi_hole.data: | ||
raise HoleError(pi_hole.data["error"]) # noqa: TRY301 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This lint error feels the exception should be handled in the api - I agree, but sadly it is not as the call returns a valid dictionary (which contains errors)
@@ -150,20 +135,72 @@ async def async_step_reauth_confirm( | |||
errors=errors, | |||
) | |||
|
|||
async def _async_try_connect(self) -> dict[str, str]: | |||
def _api_by_version( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like this could be factored out of setup_entry
and reused here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done :)
@@ -17,3 +19,9 @@ | |||
SERVICE_DISABLE_ATTR_DURATION = "duration" | |||
|
|||
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=5) | |||
|
|||
VERSION_6_RESPONSE_TO_5_ERROR = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand the reason for this being defined here like this, and that it being here is nice to have rather than essential, but it'd be nice if there was some link to the source in FTL which generated this error for reference, and to use a pattern match on the string, since pi.hole
is substituted for the configured hostname.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Link to source added. Now pattern matching used.
"issues": { | ||
"v5_to_v6_migration": { | ||
"title": "Recent migration from Pi-hole API v5 to v6", | ||
"description": "We have detected that you have likely updated you Pi-hole to a version 6 API from version 5. Some sensors are different in the new API as the daily sensors have been removed and your old API token will be invalid, please provide your new app password by following the re-authentication process in repairs or in Settings -> Devices & services -> Pi-hole" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you could word more simply and with less passive voice. Ref Home Assistant docs style guide
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hopefully improved now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Thanks for the approval. I've reached the limit of my abilities regarding code coverage and mocking the API. Hopefully it is enough |
@zweckj could you weigh in on the code coverage aspect? There's nothing in the developer's guide about 100% coverage being a requirement, the closest I found was "strongly consider adding tests". |
Hopefully we're focusing on tests that add value over 100% coverage! |
await holeV6.authenticate() | ||
# Ideally python-hole would raise a specific exception for authentication failures | ||
except HoleError as ex_v6: | ||
if str(ex_v6) == "Authentication failed: Invalid password": |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we really have to go by exception method here, is there no special type?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately, yes. Issue with the upstream library I already commented on.
_LOGGER.debug( | ||
"No API version specified, determining Pi-hole API version for %s", host | ||
) | ||
version = await determine_api_version(hass, dict(entry.data)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should probably store the determined version in the config entry. That way when it's 6 you know the user has upgraded and you don't need to check it anymore
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't that achieved 2 lines later with
hass.config_entries.async_update_entry(
entry, data={**entry.data, CONF_API_VERSION: version}
)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh yes, then we are just missing a check so we don’t do it every time unnecessarily
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if version is None
is the check where version
is instantiated as version = entry.data.get(CONF_API_VERSION)
a few lines earlier. I could use a walrus as a one liner, if you think that would be clearer?
try: | ||
await pi_hole.get_data() | ||
if pi_hole.data is not None and "error" in pi_hole.data: | ||
_LOGGER.debug( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we need 100% coverage for the config flow
@@ -6,5 +6,5 @@ | |||
"documentation": "https://www.home-assistant.io/integrations/pi_hole", | |||
"iot_class": "local_polling", | |||
"loggers": ["hole"], | |||
"requirements": ["hole==0.8.0"] | |||
"requirements": ["hole==0.9.0"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I've asked that before, but we can bump that in a prerequisite PR right? (This way we don't trigger the full CI run every time while we work on this rather large PR)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
0.9 is a breaking change. To hell with semver apparently.
@@ -89,6 +123,21 @@ def __init__( | |||
def native_value(self) -> StateType: | |||
"""Return the state of the device.""" | |||
try: | |||
return round(self.api.data[self.entity_description.key], 2) # type: ignore[no-any-return] | |||
return round(get_nested(self.api.data, self.entity_description.key), 2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i know this is old, but instead of rounding we shuold set suggested_display_precision
in the entity description
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed in 2eb1a21
ir.async_create_issue( | ||
hass, | ||
DOMAIN, | ||
issue_id=f"v5_to_v6_migration_{api.base_url}", | ||
is_fixable=False, | ||
severity=ir.IssueSeverity.ERROR, | ||
translation_key="v5_to_v6_migration", | ||
is_persistent=True, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's not raise an issue here, you already get a reauth prompt and will need to act. We can log a bit more info
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Happy to remove, the procedure for obtaining the auth changes from version 5 to version 6. Version 5 uses an API Key, version 6 uses an app passowrd that is found in a different settings menu. Hence, I thought it would be helpful to provide the user more specific instructions. Happy to remove if you think this is unnecessary.
Proposed change
Move from #145723
The current version of the pi-hole integration does not support the the update to the pihole API, and this affects many users as can be seen by the issue below.
Some of the sensor endpoints are changed in the new API, significantly, the daily sensors no longer exist. In a sense this is breaking, but since the integrations is currently already broken, I haven't marked this as a breaking change.
Includes a dependency update of python-hole from
0.8.0
to0.9.0
for V6 API support. The update makes the V6 API the default version and so a dependency upgrade alone would be breaking. In this PR all calls to the library declare the API version explicitly.Considering that the V6 API was released in February, I think we should consider a deprecation procedure in another pull request after this one is merged.
Python-hole change log:
What's Changed
Add Python 3.11 and remove Pyth0n 3.8 by @fabaff in home-assistant-ecosystem/python-hole#19
Output proper information for the web versions by @mrwacky42 in home-assistant-ecosystem/python-hole#21
Poll for new status after update by @natefox in home-assistant-ecosystem/python-hole#23
Update install action by @fabaff in home-assistant-ecosystem/python-hole#26
Update actions/setup-python by @fabaff in home-assistant-ecosystem/python-hole#24
Update actions/checkout by @fabaff in home-assistant-ecosystem/python-hole#25
Add support for Pi-hole v6 API by @LeonKohli in home-assistant-ecosystem/python-hole#29
Don't depend on async-timeout for Python 3.11+. by @Romain-Geissler-1A in home-assistant-ecosystem/python-hole#30
Type of change
Additional information
Checklist
ruff format homeassistant tests
)If user exposed functionality or configuration variables are added/changed:
If the code communicates with devices, web services, or third-party tools:
Updated and included derived files by running:
python3 -m script.hassfest
.requirements_all.txt
.Updated by running
python3 -m script.gen_requirements_all
.To help with the load of incoming pull requests: