AdGuard Policy Sync is a Home Assistant custom integration that pushes per-client policy into AdGuard Home (AGH) from a simple devices JSON, and manages custom filter rules. It keeps things honest: only AGH’s native features are used — persistent clients, built-in tags (ctag), Safe Search, Browsing Security, Parental Control, blocked services, and $client/$ctag rules. No gimmicks.
- Client sync: create/update persistent clients with IDs (IP/MAC/custom), names, and tags.
- Tag handling: passes through valid AGH tags (
user_child,device_tv,os_ios, etc.). Friendly groups (child,media,guest,adult,iot) are optional and are mapped to realctags. - Safety flags: SafeSearch, Browsing Security, and Parental Control per client (with sensible defaults and JSON overrides).
- Blocked services: per-client lists or presets by group; no need to touch AGH UI.
- Rules-first: custom rules are applied before clients, compiled to AGH’s
[]stringAPI format. - Dynamic cohort rules (optional): generates
$client=rules from friendly groups. - Pause service: temporarily relax a client (filtering only or everything) and auto-restore.
- Robust updates: unique-name strategy, non-destructive tag updates (never wipes tags with an empty list), detailed logs.
- Home Assistant (Supervised/Container/Core) with access to your AGH instance.
- AdGuard Home reachable via HTTP(S) with credentials (Basic Auth).
- Optional: a JSON file of devices in your HA config directory (e.g.
/config/adguard_devices.json).
- Copy this folder to
/config/custom_components/adguard_policy_sync/. - Restart Home Assistant.
- In HA: Settings → Devices & Services → Add Integration → AdGuard Policy Sync.
- Enter:
base_url(e.g.http://10.2.0.3:3000)- Optional:
username/password - Optional:
devices_json(relative to/config) - Optional:
rules_text(inline rules to push on sync) - Optional:
allowed_groups(comma list, default:iot,media,child,adult,guest) - Optional:
append_dynamic_rules(default: on)
File is an array of device objects:
[
{
"name": "Kid iPad",
"ip": "10.2.0.42",
"mac": "aa:bb:cc:dd:ee:ff",
"id": "optional-custom-id",
"tags": ["user_child","os_ios","media"],
"safesearch": true,
"safebrowsing": true,
"parental": true,
"blocked_services": ["tiktok","discord","reddit"],
"use_global_blocked_services": false
}
]
- name: client display name in AGH (the integration ensures uniqueness).
- ip / mac / id: any mix; at least one required.
- tags:
- Prefer AGH
ctags:user_child,user_regular,user_admin,device_tv,device_other,os_ios, etc. - Optional friendly groups:
child,media,guest,adult,iot. These are mapped to realctags server-side.
- Prefer AGH
- safesearch / safebrowsing / parental: booleans; if omitted, reasonable defaults are derived from groups/ctags.
- blocked_services: list of AGH service IDs; omit to use group presets.
- use_global_blocked_services: false by default; set true to inherit AGH’s global list.
- AGH tags in your JSON are passed through unchanged (lower-cased and de-duplicated).
- Friendly groups (if present) are mapped to
ctags and unioned with your tags:child → user_childmedia → device_tvguest → user_regularadult → user_adminiot → device_other
- If the final tag list is empty, the integration omits the
tagsfield to avoid wiping tags in AGH.
adguard_policy_sync.sync — apply rules, then sync clients.
service: adguard_policy_sync.sync
data:
devices_json: adguard_devices.json # optional; overrides config entry
rules_text: | # optional; overrides default rules
||dns.google^$ctag=user_child
- Uploads custom rules (and appends dynamic rules if enabled).
- Creates or updates clients with tags, safety flags, and blocked services.
adguard_policy_sync.pause — temporarily relax protection for named clients and restore automatically.
service: adguard_policy_sync.pause
data:
clients: ["Kid iPad","My Laptop"]
minutes: 20
scope: filtering_only # or "all"
- filtering_only: disables filtering rules only.
- all: also disables Browsing Security & Parental, and clears blocked services during the pause.
- Static example rules target
$ctag(e.g. block DoH foruser_child, keep socials offdevice_tv). - Dynamic rules target
$client='Name'|'Name2'and are generated only if you use friendly groups in JSON. - Rules are sent as an array of strings (AGH’s preferred format). Comment lines starting with
!are preserved.
- SafeSearch: on for
childandguestcohorts (or ifuser_childtag is present). - Browsing Security: on by default for
child,adult, andguestunless you override. - Parental: on by default for
childunless you override. - Blocked services:
- child: curated set (e.g. tiktok/discord/reddit/…)
- media: social on TVs/streamers off by default
- guest: light social clamp
- Service not visible: confirm the folder name is exactly
adguard_policy_sync, restart HA; the integration registers services even if AGH is temporarily down. - 400 Bad Request (invalid tag): use only AGH’s fixed
ctags (e.g.user_child, notchild_user). - No tags applied: check logs for from_json_tags → final_tags; ensure your JSON tags are valid and lower-case; friendly groups are optional but must match the allowed list.
- Client not found for pause: names must match exactly what AGH shows under “Clients”.
# inspect server truth after a sync
curl -s -u USER:PASS http://AGH_HOST:PORT/control/clients \
| jq '.clients[] | {name, ids, tags, safesearch_enabled, safebrowsing_enabled, parental_enabled, blocked_services}'
- Use HTTPS to AGH or keep traffic on a trusted LAN. Credentials are stored in HA; protect your HA config.
- Pause is emulated by flipping client flags and restoring later; if you rename a client during a pause, restore may miss.