Repository navigation
This release is a rewrite of the library. It now covers the whole AdGuard Home API, returns typed models instead of loose values, and comes with a command-line interface. Almost everything changed, so read the upgrade notes below before you update.
Highlights
- Typed models, one request each. Every area returns frozen dataclasses from a single request. Reading all statistics used to take a request per number; now
await adguard.stats.get()gets them all at once, including the top lists and history. - The whole API. Next to the areas the library already had, it now covers clients, DNS rewrites, DNS server settings, blocked services, access lists, DHCP, encryption (TLS), and the query log entries. Every area follows the same shape:
get()for data,config()andset_config()for settings, and verbs likeenable()orrefresh()for actions. - Units you do not have to think about. AdGuard Home mixes milliseconds, seconds, hours, and days. Durations are
timedeltaand timestampsdatetimenow, whatever the API uses underneath, and versions areAwesomeVersion. - A command-line interface. Install
adguardhome[cli]and runadguardhome status,adguardhome stats,adguardhome log, oradguardhome disable protection --for 10m. Every command that shows data can emit JSON. - Errors you can act on. Connection problems, timeouts, rejected credentials, unsupported endpoints, and error responses each have their own exception, carrying the status code and the message of AdGuard Home.
- Tested against real servers. Next to a test suite at 100% coverage, every area ran against AdGuard Home v0.107.68 and v0.107.79.
Requirements
- AdGuard Home v0.107.68 or newer. Use
status.supportedto check the server you connect to. - Python 3.11 or newer.
- aiohttp 3.14 or newer.
Upgrading
Connecting
The client takes the URL of the web interface, the same one you open in your browser, instead of a host, port, TLS flag, and base path. Credentials go in separate arguments, never in the URL.
# Before
AdGuardHome("192.168.1.2", port=3000, tls=False, username="admin", password="secret")
# Now
AdGuardHome("http://192.168.1.2:3000", username="admin", password="secret")
AdGuardHome("https://example.com/adguard", username="admin", password="secret")Redirects are not followed anymore, as they would resend a request, including a TLS private key, to wherever they point. With force HTTPS enabled in AdGuard Home, use its HTTPS URL; the error tells you which one.
Reading values
Methods that each returned a single value are gone. Read the model instead:
| Before | Now |
|---|---|
version() |
(await adguard.status()).version |
protection_enabled() |
(await adguard.status()).protection_enabled |
stats.dns_queries() and the other stats methods |
stats = await adguard.stats.get(), then stats.dns_queries, stats.blocked_filtering, stats.blocked_percentage, and so on |
stats.replaced_safebrowsing() |
stats.blocked_safebrowsing |
stats.replaced_parental() |
stats.blocked_parental |
stats.replaced_safesearch() |
stats.enforced_safesearch |
stats.avg_processing_time() (milliseconds) |
stats.avg_processing_time (a timedelta) |
stats.period() (days) |
(await adguard.stats.config()).retention (a timedelta) |
filtering.enabled() |
(await adguard.filtering.config()).enabled |
filtering.rules_count(allowlist=False) |
sum(f.rules_count for f in await adguard.filtering.blocklists.list()) |
filtering.interval() (hours) |
(await adguard.filtering.config()).update_interval (a timedelta) |
querylog.enabled() |
(await adguard.querylog.config()).enabled |
querylog.interval() (days) |
(await adguard.querylog.config()).retention (a timedelta) |
safesearch.enabled() |
(await adguard.safesearch.config()).enabled |
update.update_available() |
update.get() |
Parental control and safe browsing still have enabled(), enable(), and disable().
Changing settings
Settings are read as a model, changed with dataclasses.replace(), and written back:
from dataclasses import replace
from datetime import timedelta
config = await adguard.querylog.config()
await adguard.querylog.set_config(replace(config, retention=timedelta(days=7)))The interval() setters of filtering and the query log are replaced by that. enable() and disable() remain on every area that can be turned on and off.
Filter lists
Blocklists and allowlists are separate collections now, instead of an allowlist argument on every method:
| Before | Now |
|---|---|
filtering.add_url(allowlist=False, name=..., url=...) |
filtering.blocklists.add(url, name=...) |
filtering.remove_url(allowlist=False, url=...) |
filtering.blocklists.remove(url) |
filtering.enable_url(...) and disable_url(...) |
filtering.blocklists.enable(url) and disable(url) |
filtering.url_enabled(...) |
(await filtering.blocklists.get(url)).enabled |
filtering.refresh(allowlist=False, force=True) |
filtering.blocklists.refresh(), which returns the number of updated lists |
The force argument is gone: AdGuard Home always forces a refresh. Filter lists are matched by their exact URL, like AdGuard Home does.
Other changes
disable_protection()takes an optionaltimedelta, to pause protection for a while.status().protection_resumes_intells you how long a pause has left.update.begin_update()isupdate.install().request()is private now. Every endpoint has a method instead.- Exceptions raised by the library keep the status code and message of AdGuard Home. Catch
AdGuardHomeAuthenticationError,AdGuardHomeConnectionTimeoutError,AdGuardHomeUnsupportedError, orAdGuardHomeResponseErrorfor specific cases; all of them still derive fromAdGuardHomeError. - Safe search uses the newer settings endpoints, so enabling it keeps the settings per search engine.
Thanks
To everyone who contributed to this release, and to those whose earlier pull requests asked for things that are part of it now, like blocked services, pausing protection, and the top domains in the statistics.
What’s changed
🚨 Breaking changes
- Restructure the library around the AdGuard Home API resources @frenck (#1863)
- Cover the rest of the AdGuard Home API @frenck (#1865)
- Fix the findings of a review round @frenck (#1867)
✨ New features
- Add duration argument to disable_protection function @mib1185 (#1844)
- Add client resources @offbyone (#694)
- Added support for the DNS rewrite rules AdGuard API @finnhartshorn (#172)
- Cover the rest of the AdGuard Home API @frenck (#1865)
- Add a command-line interface @frenck (#1866)
🐛 Bug fixes
- Fix the Prettier hook, which never ran, and the editorconfig @frenck (#1862)
- Fix the findings of a review round @frenck (#1867)
- Accept the answers AdGuard Home gives when adding a filter list @frenck (#1868)
🚀 Enhancements
- Migrate tests to aiointercept and use aiohttp.encode_basic_auth @balloobbot (#1861)
- Restructure the library around the AdGuard Home API resources @frenck (#1863)
🧰 Maintenance
- Replace mypy with ty @frenck (#1787)
- 🎆 Updates maintenance/license year to 2026 @frenck (#1789)
- Replace issue template with an issue form @frenck (#1790)
- Tweaks to issue and feature request forms @frenck (#1791)
- 🧰 Modernize project tooling, CI, and documentation @frenck (#1854)
📚 Documentation
⬆️ Dependency updates
168 changes
- ⬆️ Lock file maintenance @renovate[bot] (#1681)
- ⬆️ Update dependency prek to v0.2.15 @renovate[bot] (#1682)
- ⬆️ Update dependency prek to v0.2.17 @renovate[bot] (#1684)
- ⬆️ Update actions/checkout action to v6 @renovate[bot] (#1687)
- ⬆️ Update dependency coverage to v7.12.0 @renovate[bot] (#1685)
- ⬆️ Update dependency ruff to v0.14.6 @renovate[bot] (#1689)
- ⬆️ Update github/codeql-action digest to e12f017 @renovate[bot] (#1686)
- ⬆️ Update dependency prek to v0.2.18 @renovate[bot] (#1688)
- ⬆️ Update github/codeql-action digest to fdbfb4d @renovate[bot] (#1690)
- ⬆️ Update actions/setup-python action to v6.1.0 @renovate[bot] (#1691)
- ⬆️ Update dependency zizmor to v1.17.0 @renovate[bot] (#1692)
- ⬆️ Update dependency prek to v0.2.19 @renovate[bot] (#1693)
- ⬆️ Update dependency prettier to v3.7.0 @renovate[bot] (#1694)
- ⬆️ Update dependency prettier to v3.7.1 @renovate[bot] (#1695)
- ⬆️ Update dependency mypy to v1.19.0 @renovate[bot] (#1696)
- ⬆️ Update dependency pylint to v4.0.4 @renovate[bot] (#1700)
- ⬆️ Update github/codeql-action digest to fe4161a @renovate[bot] (#1702)
- ⬆️ Update dependency zizmor to v1.18.0 @renovate[bot] (#1699)
- ⬆️ Lock file maintenance @renovate[bot] (#1701)
- ⬆️ Update dependency ruff to v0.14.7 @renovate[bot] (#1698)
- ⬆️ Update dependency prettier to v3.7.3 @renovate[bot] (#1697)
- ⬆️ Update dependency prettier to v3.7.4 @renovate[bot] (#1706)
- ⬆️ Update actions/stale action to v10.1.1 @renovate[bot] (#1705)
- ⬆️ Update actions/checkout action to v6.0.1 - autoclosed @renovate[bot] (#1703)
- ⬆️ Update actions/setup-node action to v6.1.0 @renovate[bot] (#1707)
- ⬆️ Update dependency ruff to v0.14.8 @renovate[bot] (#1708)
- ⬆️ Update github/codeql-action digest to cf1bb45 @renovate[bot] (#1709)
- ⬆️ Lock file maintenance @renovate[bot] (#1711)
- ⬆️ Update dependency pytest to v9.0.2 @renovate[bot] (#1710)
- ⬆️ Update dependency coverage to v7.13.0 @renovate[bot] (#1713)
- ⬆️ Update dependency prek to v0.2.20 @renovate[bot] (#1712)
- ⬆️ Update dependency python to v3.14.2 @renovate[bot] (#1714)
- ⬆️ Update dependency prek to v0.2.21 @renovate[bot] (#1715)
- ⬆️ Update codecov/codecov-action action to v5.5.2 @renovate[bot] (#1716)
- ⬆️ Update dependency node to v24.12.0 @renovate[bot] (#1717)
- ⬆️ Update dependency zizmor to v1.19.0 @renovate[bot] (#1726)
- ⬆️ Update actions/upload-artifact action to v6 @renovate[bot] (#1722)
- ⬆️ Update dessant/lock-threads action to v6 @renovate[bot] (#1720)
- ⬆️ Update actions/download-artifact action to v7 @renovate[bot] (#1721)
- ⬆️ Update github/codeql-action digest to 5d4e8d1 @renovate[bot] (#1719)
- ⬆️ Update dependency ruff to v0.14.10 @renovate[bot] (#1718)
- ⬆️ Update dependency coverage to v7.13.1 @renovate[bot] (#1727)
- ⬆️ Update dependency prek to v0.2.25 @renovate[bot] (#1723)
- ⬆️ Update dependency mypy to v1.19.1 @renovate[bot] (#1725)
- ⬆️ Lock file maintenance @renovate[bot] (#1724)
- ⬆️ Update dependency ruff to v0.14.11 @renovate[bot] (#1733)
- ⬆️ Update github/codeql-action digest to cdefb33 @renovate[bot] (#1734)
- ⬆️ Update dependency yamllint to v1.38.0 @renovate[bot] (#1735)
- ⬆️ Update dependency prek to v0.2.28 @renovate[bot] (#1732)
- ⬆️ Update dependency node to v24.13.0 @renovate[bot] (#1736)
- ⬆️ Update dependency aiohttp to v3.13.3 [SECURITY] @renovate[bot] (#1730)
- ⬆️ Update dependency zizmor to v1.20.0 @renovate[bot] (#1731)
- ⬆️ Update dependency prettier to v3.8.0 @renovate[bot] (#1737)
- ⬆️ Update actions/setup-node action to v6.2.0 @renovate[bot] (#1738)
- ⬆️ Update dependency ruff to v0.14.12 @renovate[bot] (#1739)
- ⬆️ Update dependency ruff to v0.14.13 @renovate[bot] (#1740)
- ⬆️ Update dependency zizmor to v1.21.0 @renovate[bot] (#1741)
- ⬆️ Update dependency prek to v0.2.30 @renovate[bot] (#1742)
- ⬆️ Update dependency poetry to v2.3.0 @renovate[bot] (#1744)
- ⬆️ Update dependency zizmor to v1.22.0 @renovate[bot] (#1743)
- ⬆️ Update release-drafter/release-drafter action to v6.1.1 @renovate[bot] (#1745)
- ⬆️ Lock file maintenance @renovate[bot] (#1729)
- ⬆️ Update dependency prettier to v3.8.1 @renovate[bot] (#1747)
- ⬆️ Update dependency poetry to v2.3.1 @renovate[bot] (#1746)
- ⬆️ Update actions/setup-python action to v6.2.0 @renovate[bot] (#1748)
- ⬆️ Update dependency prek to v0.3.0 @renovate[bot] (#1749)
- ⬆️ Update actions/checkout action to v6.0.2 @renovate[bot] (#1751)
- ⬆️ Update release-drafter/release-drafter digest to 267d2e0 @renovate[bot] (#1750)
- ⬆️ Update github/codeql-action digest to 19b2f06 @renovate[bot] (#1754)
- ⬆️ Update dependency ruff to v0.14.14 @renovate[bot] (#1753)
- ⬆️ Update release-drafter/release-drafter action to v6.2.0 @renovate[bot] (#1752)
- ⬆️ Update dependency syrupy to v5.1.0 @renovate[bot] (#1756)
- ⬆️ Update dependency coverage to v7.13.2 @renovate[bot] (#1755)
- ⬆️ Lock file maintenance @renovate[bot] (#1757)
- ⬆️ Update github/codeql-action digest to b20883b @renovate[bot] (#1758)
- ⬆️ Update dependency prek to v0.3.1 @renovate[bot] (#1760)
- ⬆️ Update dependency poetry to v2.3.2 @renovate[bot] (#1761)
- ⬆️ Update dependency pip to v26 [SECURITY] @renovate[bot] (#1764)
- ⬆️ Update github/codeql-action digest to 6bc82e0 @renovate[bot] (#1763)
- ⬆️ Update dependency ruff to v0.15.0 @renovate[bot] (#1766)
- ⬆️ Update dependency pip to v26.0.1 @renovate[bot] (#1759)
- ⬆️ Update dependency coverage to v7.13.3 @renovate[bot] (#1765)
- ⬆️ Lock file maintenance @renovate[bot] (#1762)
- ⬆️ Update github/codeql-action digest to 45cbd0c @renovate[bot] (#1767)
- ⬆️ Update dependency prek to v0.3.2 @renovate[bot] (#1768)
- ⬆️ Lock file maintenance @renovate[bot] (#1769)
- ⬆️ Update dependency coverage to v7.13.4 @renovate[bot] (#1770)
- ⬆️ Update dependency node to v24.13.1 @renovate[bot] (#1771)
- ⬆️ Update dependency ruff to v0.15.1 @renovate[bot] (#1772)
- ⬆️ Update github/codeql-action digest to 9e907b5 @renovate[bot] (#1773)
- ⬆️ Update dependency prek to v0.3.3 @renovate[bot] (#1774)
- ⬆️ Lock file maintenance @renovate[bot] (#1775)
- ⬆️ Update actions/stale action to v10.2.0 @renovate[bot] (#1776)
- ⬆️ Update actions/dependency-review-action action to v4.8.3 @renovate[bot] (#1778)
- ⬆️ Update dependency ruff to v0.15.2 @renovate[bot] (#1777)
- ⬆️ Update github/codeql-action digest to 89a39a4 @renovate[bot] (#1780)
- ⬆️ Update dependency pylint to v4.0.5 @renovate[bot] (#1779)
- ⬆️ Lock file maintenance @renovate[bot] (#1781)
- ⬆️ Update dependency node to v24.14.0 @renovate[bot] (#1782)
- ⬆️ Update dependency ruff to v0.15.3 @renovate[bot] (#1783)
- ⬆️ Update dependency ruff to v0.15.4 @renovate[bot] (#1784)
- ⬆️ Update actions/download-artifact action to v8 @renovate[bot] (#1785)
- ⬆️ Update dependency ty to v0.0.19 @renovate[bot] (#1788)
- ⬆️ Update actions/upload-artifact action to v7 @renovate[bot] (#1786)
- ⬆️ Update dependency prek to v0.3.4 @renovate[bot] (#1792)
- ⬆️ Update dependency yarl to v1.23.0 @renovate[bot] (#1793)
- ⬆️ Lock file maintenance @renovate[bot] (#1794)
- ⬆️ Update github/codeql-action digest to c793b71 @renovate[bot] (#1795)
- ⬆️ Update dependency ty to v0.0.20 @renovate[bot] (#1796)
- ⬆️ Update actions/dependency-review-action action to v4.9.0 @renovate[bot] (#1797)
- ⬆️ Update actions/setup-node action to v6.3.0 @renovate[bot] (#1798)
- ⬆️ Update dependency codespell to v2.4.2 @renovate[bot] (#1799)
- ⬆️ Update dependency ty to v0.0.21 @renovate[bot] (#1802)
- ⬆️ Update release-drafter/release-drafter action to v6.3.0 @renovate[bot] (#1803)
- ⬆️ Update dependency ruff to v0.15.5 @renovate[bot] (#1801)
- ⬆️ Update dependency prek to v0.3.5 @renovate[bot] (#1807)
- ⬆️ Update github/codeql-action digest to 0d579ff @renovate[bot] (#1800)
- ⬆️ Lock file maintenance @renovate[bot] (#1806)
- ⬆️ Update release-drafter/release-drafter action to v6.4.0 @renovate[bot] (#1804)
- ⬆️ Update dependency zizmor to v1.23.1 @renovate[bot] (#1805)
- ⬆️ Update actions/download-artifact action to v8.0.1 @renovate[bot] (#1808)
- ⬆️ Update github/codeql-action digest to b1bff81 @renovate[bot] (#1814)
- ⬆️ Update release-drafter/release-drafter action to v7 @renovate[bot] (#1811)
- ⬆️ Update dependency prek to v0.3.6 @renovate[bot] (#1813)
- ⬆️ Update dependency ruff to v0.15.6 @renovate[bot] (#1810)
- ⬆️ Update dependency ty to v0.0.23 @renovate[bot] (#1809)
- ⬆️ Lock file maintenance @renovate[bot] (#1812)
- ⬆️ Update github/codeql-action digest to 3869755 @renovate[bot] (#1820)
- ⬆️ Update release-drafter/release-drafter action to v7.1.1 @renovate[bot] (#1816)
- ⬆️ Update dependency ty to v0.0.24 @renovate[bot] (#1819)
- ⬆️ Update dependency prek to v0.3.8 @renovate[bot] (#1823)
- ⬆️ Update codecov/codecov-action action to v5.5.3 @renovate[bot] (#1817)
- ⬆️ Update dependency pytest-cov to v7.1.0 @renovate[bot] (#1821)
- ⬆️ Update dependency ruff to v0.15.7 @renovate[bot] (#1818)
- ⬆️ Update dependency coverage to v7.13.5 @renovate[bot] (#1815)
- ⬆️ Lock file maintenance @renovate[bot] (#1822)
- ⬆️ Update dependency ty to v0.0.25 @renovate[bot] (#1825)
- ⬆️ Update dependency node to v24.14.1 @renovate[bot] (#1824)
- ⬆️ Update dependency ty to v0.0.26 @renovate[bot] (#1827)
- ⬆️ Update dependency aiohttp to v3.13.4 @renovate[bot] (#1831)
- ⬆️ Update codecov/codecov-action action to v5.5.4 @renovate[bot] (#1826)
- ⬆️ Update dependency poetry to v2.3.3 @renovate[bot] (#1832)
- ⬆️ Update github/codeql-action digest to c10b806 @renovate[bot] (#1830)
- ⬆️ Update codecov/codecov-action action to v6 @renovate[bot] (#1829)
- ⬆️ Update dependency ruff to v0.15.8 @renovate[bot] (#1828)
- ⬆️ Update dependency aiohttp to v3.13.5 @renovate[bot] (#1835)
- ⬆️ Update dependency ty to v0.0.27 @renovate[bot] (#1834)
- ⬆️ Lock file maintenance @renovate[bot] (#1833)
- ⬆️ Update dependency ty to v0.0.28 @renovate[bot] (#1837)
- ⬆️ Update dependency ruff to v0.15.9 @renovate[bot] (#1836)
- ⬆️ Update dependency ty to v0.0.29 @renovate[bot] (#1838)
- ⬆️ Update release-drafter/release-drafter action to v7.2.0 @renovate[bot] (#1843)
- ⬆️ Update dependency prettier to v3.8.2 @renovate[bot] (#1845)
- ⬆️ Update dependency ruff to v0.15.10 @renovate[bot] (#1842)
- ⬆️ Update pypa/gh-action-pypi-publish action to v1.14.0 @renovate[bot] (#1840)
- ⬆️ Lock file maintenance @renovate[bot] (#1839)
- ⬆️ Update dependency pytest to v9.0.3 @renovate[bot] (#1841)
- ⬆️ Update dependency poetry to v2.3.4 @renovate[bot] (#1847)
- ⬆️ Update actions/upload-artifact action to v7.0.1 @renovate[bot] (#1846)
- ⬆️ Lock file maintenance @renovate[bot] (#1848)
- ⬆️ Update dependency zizmor to v1.24.0 @renovate[bot] (#1849)
- ⬆️ Update dependency prek to v0.3.9 @renovate[bot] (#1850)
- ⬆️ Update dependency zizmor to v1.24.1 @renovate[bot] (#1851)
- ⬆️ Update dependency prettier to v3.8.3 @renovate[bot] (#1853)
- ⬆️ Update github/codeql-action digest to 95e58e9 @renovate[bot] (#1852)
- ⬆️ Update dependency node to v24.15.0 @renovate[bot] (#1857)
- ⬆️ Pin github/codeql-action action to v4.35.2 @renovate[bot] (#1858)
- ⬆️ Update dependency ruff to v0.15.11 @renovate[bot] (#1859)