PiHoleV6

PiHole V6 integration using the new REST API. PiHole is a network-level advertisement and Internet tracker blocking application which acts as a DNS sinkhole.

IT Services · PiHole

Details

IDPiHoleV6
ProviderOpen Source
CategoryIT Services
From Version5.0.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix XSIAM

README

PiHole v6 integration using the new REST API. PiHole is a network-level advertisement and Internet tracker blocking application which acts as a DNS sinkhole and optionally a DHCP server, intended for use on a private network.
This integration was integrated and tested with PiHole v6.0 API.

Configure PiHole v6 in Cortex

Parameter Description Required
url Server URL (e.g. https://192.168.1.25) True
password PiHole admin password for API authentication True
insecure Trust any certificate (not secure) False
proxy Use system proxy settings False

Commands

You can execute these commands from the CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.

pihole-get-summary


Get overview of PiHole activity including query stats, client counts, and gravity info.

Base Command

pihole-get-summary

Input

| Argument Name | Description | Required |
| — | — | — |

Context Output

Path Type Description
PiHoleV6.Summary.queries Unknown Query statistics including total, blocked, cached, forwarded counts.
PiHoleV6.Summary.clients Unknown Client statistics.
PiHoleV6.Summary.gravity Unknown Gravity list statistics.

pihole-get-top-domains


Get top permitted or blocked domains.

Base Command

pihole-get-top-domains

Input

Argument Name Description Required
count Number of top domains to return. Default is 10. Optional
blocked If true, return top blocked domains instead of top permitted domains. Possible values are: true, false. Default is false. Optional

Context Output

Path Type Description
PiHoleV6.TopDomains.domains Unknown Array of top domains with counts.
PiHoleV6.TopDomains.total_queries Number Total number of queries.

pihole-get-top-clients


Get top clients by query count.

Base Command

pihole-get-top-clients

Input

Argument Name Description Required
count Number of top clients to return. Default is 10. Optional
blocked If true, return top blocked clients instead of top active clients. Possible values are: true, false. Default is false. Optional

Context Output

Path Type Description
PiHoleV6.TopClients.clients Unknown Array of top clients with counts.
PiHoleV6.TopClients.total_queries Number Total number of queries.

pihole-get-upstreams


Get metrics about upstream DNS destinations.

Base Command

pihole-get-upstreams

Input

| Argument Name | Description | Required |
| — | — | — |

Context Output

Path Type Description
PiHoleV6.Upstreams.upstreams Unknown Array of upstream destinations with query counts and response times.
PiHoleV6.Upstreams.forwarded_queries Number Number of forwarded queries.

pihole-get-query-types


Get breakdown of DNS query types.

Base Command

pihole-get-query-types

Input

| Argument Name | Description | Required |
| — | — | — |

Context Output

Path Type Description
PiHoleV6.QueryTypes.types Unknown Query type counts (A, AAAA, MX, etc).

pihole-get-recent-blocked


Get most recently blocked domain(s).

Base Command

pihole-get-recent-blocked

Input

Argument Name Description Required
count Number of recently blocked domains to return. Default is 1. Optional

Context Output

Path Type Description
PiHoleV6.RecentBlocked.blocked Unknown List of recently blocked domains.

pihole-get-history


Get activity graph data (queries over time).

Base Command

pihole-get-history

Input

| Argument Name | Description | Required |
| — | — | — |

Context Output

Path Type Description
PiHoleV6.History.history Unknown Activity over time data with timestamps, total, cached, blocked, forwarded counts.

pihole-get-history-clients


Get per-client activity graph data.

Base Command

pihole-get-history-clients

Input

Argument Name Description Required
N Maximum number of clients to return. 0 returns all. Default is 20. Optional

Context Output

Path Type Description
PiHoleV6.HistoryClients.clients Unknown Client information with query counts.
PiHoleV6.HistoryClients.history Unknown Per-client activity over time.

pihole-get-queries


Get DNS query log with optional filtering.

Base Command

pihole-get-queries

Input

Argument Name Description Required
length Number of queries to return. Default is 100. Optional
domain Filter by domain (wildcards supported). Optional
client_ip Filter by client IP (wildcards supported). Optional
client_name Filter by client hostname (wildcards supported). Optional
upstream Filter by upstream server. Optional
type Filter by query type (A, AAAA, etc). Optional
status Filter by status (GRAVITY, FORWARDED, CACHE, etc). Optional

Context Output

Path Type Description
PiHoleV6.Queries.queries Unknown Array of DNS query records.
PiHoleV6.Queries.recordsTotal Number Total number of available queries.

pihole-get-blocking-status


Get current PiHole blocking status.

Base Command

pihole-get-blocking-status

Input

| Argument Name | Description | Required |
| — | — | — |

Context Output

Path Type Description
PiHoleV6.Blocking.blocking String Current blocking status (enabled/disabled).
PiHoleV6.Blocking.timer Number Remaining seconds until blocking mode changes automatically. Null if permanent.

pihole-set-blocking


Enable or disable PiHole blocking, optionally with a timer.

Base Command

pihole-set-blocking

Input

Argument Name Description Required
blocking Enable or disable blocking. Possible values are: true, false. Required
timer Seconds until blocking mode automatically reverts. Leave empty for permanent change. Optional

Context Output

Path Type Description
PiHoleV6.Blocking.blocking String New blocking status.
PiHoleV6.Blocking.timer Number Timer value if set.

pihole-get-domains


Get domains from allow/deny lists. Filter by type and kind.

Base Command

pihole-get-domains

Input

Argument Name Description Required
type Domain type filter. Possible values are: allow, deny. Optional
kind Domain kind filter. Possible values are: exact, regex. Optional

Context Output

Path Type Description
PiHoleV6.Domains.domains Unknown Array of domain entries.

pihole-add-domain


Add a domain to allow/deny list.

Base Command

pihole-add-domain

Input

Argument Name Description Required
type Domain type. Possible values are: allow, deny. Required
kind Domain kind. Possible values are: exact, regex. Required
domain Domain to add. Required
comment Optional comment. Optional

Context Output

Path Type Description
PiHoleV6.Domains.domains Unknown Added domain details.

pihole-delete-domain


Delete a domain from allow/deny list.

Base Command

pihole-delete-domain

Input

Argument Name Description Required
type Domain type. Possible values are: allow, deny. Required
kind Domain kind. Possible values are: exact, regex. Required
domain Domain to delete. Required

Context Output

There is no context output for this command.

pihole-get-version


Get PiHole component versions.

Base Command

pihole-get-version

Input

| Argument Name | Description | Required |
| — | — | — |

Context Output

Path Type Description
PiHoleV6.Version Unknown Version information for all PiHole components.

pihole-get-system-info


Get system information from PiHole host.

Base Command

pihole-get-system-info

Input

| Argument Name | Description | Required |
| — | — | — |

Context Output

Path Type Description
PiHoleV6.SystemInfo Unknown System information (CPU, memory, disk, uptime, etc).

pihole-get-ftl-info


Get FTL (Faster Than Light) engine information.

Base Command

pihole-get-ftl-info

Input

| Argument Name | Description | Required |
| — | — | — |

Context Output

Path Type Description
PiHoleV6.FTLInfo Unknown FTL engine information.

pihole-get-host-info


Get host information.

Base Command

pihole-get-host-info

Input

| Argument Name | Description | Required |
| — | — | — |

Context Output

Path Type Description
PiHoleV6.HostInfo Unknown Host information.

pihole-get-sensors


Get sensor information from PiHole host.

Base Command

pihole-get-sensors

Input

| Argument Name | Description | Required |
| — | — | — |

Context Output

Path Type Description
PiHoleV6.Sensors Unknown Sensor readings (temperature, etc).

pihole-run-gravity


Update PiHole gravity (pull latest adlists).

Base Command

pihole-run-gravity

Input

| Argument Name | Description | Required |
| — | — | — |

Context Output

Path Type Description
PiHoleV6.Gravity.output String Gravity update output.

pihole-restart-dns


Restart the pihole-FTL DNS service.

Base Command

pihole-restart-dns

Input

| Argument Name | Description | Required |
| — | — | — |

Context Output

Path Type Description
PiHoleV6.RestartDNS Unknown Restart result.

pihole-flush-logs


Flush DNS logs and purge last 24 hours from database.

Base Command

pihole-flush-logs

Input

| Argument Name | Description | Required |
| — | — | — |

Context Output

Path Type Description
PiHoleV6.FlushLogs Unknown Flush result.

pihole-flush-network


Flush the network table (remove all known devices and addresses).

Base Command

pihole-flush-network

Input

| Argument Name | Description | Required |
| — | — | — |

Context Output

Path Type Description
PiHoleV6.FlushNetwork Unknown Flush result.

pihole-get-network-devices


Get network devices known to PiHole.

Base Command

pihole-get-network-devices

Input

| Argument Name | Description | Required |
| — | — | — |

Context Output

Path Type Description
PiHoleV6.NetworkDevices Unknown Network device information.

pihole-get-network-gateway


Get network gateway information.

Base Command

pihole-get-network-gateway

Input

| Argument Name | Description | Required |
| — | — | — |

Context Output

Path Type Description
PiHoleV6.Gateway Unknown Gateway information.

pihole-search-domain


Search for a domain across all PiHole lists and configuration.

Base Command

pihole-search-domain

Input

Argument Name Description Required
domain Domain to search for. Required

Context Output

Path Type Description
PiHoleV6.Search Unknown Search results showing where the domain appears in PiHole configuration.

pihole-get-dhcp-leases


Get current DHCP leases.

Base Command

pihole-get-dhcp-leases

Input

| Argument Name | Description | Required |
| — | — | — |

Context Output

Path Type Description
PiHoleV6.DHCPLeases Unknown DHCP lease information.

pihole-get-groups


Get all groups.

Base Command

pihole-get-groups

Input

| Argument Name | Description | Required |
| — | — | — |

Context Output

Path Type Description
PiHoleV6.Groups Unknown Group information.

pihole-add-group


Create a new group.

Base Command

pihole-add-group

Input

Argument Name Description Required
name Group name. Required
comment Optional comment. Optional

Context Output

Path Type Description
PiHoleV6.Groups Unknown Created group details.

pihole-delete-group


Delete a group.

Base Command

pihole-delete-group

Input

Argument Name Description Required
name Group name to delete. Required

Context Output

There is no context output for this command.

pihole-get-lists


Get all configured adlists.

Base Command

pihole-get-lists

Input

| Argument Name | Description | Required |
| — | — | — |

Context Output

Path Type Description
PiHoleV6.Lists Unknown Adlist information.

pihole-add-list


Add a new adlist.

Base Command

pihole-add-list

Input

Argument Name Description Required
address URL of the adlist to add. Required
comment Optional comment. Optional
enabled Whether the list should be enabled. Possible values are: true, false. Default is true. Optional

Context Output

Path Type Description
PiHoleV6.Lists Unknown Added list details.

pihole-delete-list


Delete an adlist.

Base Command

pihole-delete-list

Input

Argument Name Description Required
address URL of the adlist to delete. Required

Context Output

There is no context output for this command.

Configuration parameters

  • url — Server URL (required)
  • password — Password (required)
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings

Commands (33)

  • pihole-add-domain

    Add a domain to allow/deny list.

  • pihole-add-group

    Create a new group.

  • pihole-add-list

    Add a new adlist.

  • pihole-delete-domain

    Delete a domain from allow/deny list.

  • pihole-delete-group

    Delete a group.

  • pihole-delete-list

    Delete an adlist.

  • pihole-flush-logs

    Flush DNS logs and purge last 24 hours from database.

  • pihole-flush-network

    Flush the network table (remove all known devices and addresses).

  • pihole-get-blocking-status

    Get current PiHole blocking status.

  • pihole-get-dhcp-leases

    Get current DHCP leases.

  • pihole-get-domains

    Get domains from allow/deny lists. Filter by type and kind.

  • pihole-get-ftl-info

    Get FTL (Faster Than Light) engine information.

  • pihole-get-groups

    Get all groups.

  • pihole-get-history

    Get activity graph data (queries over time).

  • pihole-get-history-clients

    Get per-client activity graph data.

  • pihole-get-host-info

    Get host information.

  • pihole-get-lists

    Get all configured adlists.

  • pihole-get-network-devices

    Get network devices known to PiHole.

  • pihole-get-network-gateway

    Get network gateway information.

  • pihole-get-queries

    Get DNS query log with optional filtering.

  • pihole-get-query-types

    Get breakdown of DNS query types.

  • pihole-get-recent-blocked

    Get most recently blocked domain(s).

  • pihole-get-sensors

    Get sensor information from PiHole host.

  • pihole-get-summary

    Get overview of PiHole activity including query stats, client counts, and gravity info.

  • pihole-get-system-info

    Get system information from PiHole host.

  • pihole-get-top-clients

    Get top clients by query count.

  • pihole-get-top-domains

    Get top permitted or blocked domains.

  • pihole-get-upstreams

    Get metrics about upstream DNS destinations.

  • pihole-get-version

    Get PiHole component versions.

  • pihole-restart-dns

    Restart the pihole-FTL DNS service.

  • pihole-run-gravity

    Update PiHole gravity (pull latest adlists).

  • pihole-search-domain

    Search for a domain across all PiHole lists and configuration.

  • pihole-set-blocking

    Enable or disable PiHole blocking, optionally with a timer.

import demistomock as demisto
from CommonServerPython import *

""" IMPORTS """

import urllib3

# Disable insecure warnings
urllib3.disable_warnings()


class PiHoleV6Client(BaseClient):
    """Client class to interact with the PiHole v6 API."""

    def __init__(self, base_url: str, password: str, verify: bool, proxy: bool):
        super().__init__(base_url=base_url, verify=verify, proxy=proxy)
        self.password = password
        self.sid: str | None = None

    def authenticate(self) -> None:
        """Authenticate with PiHole v6 and obtain a session ID."""
        result = self._http_request("POST", "/auth", json_data={"password": self.password})
        session = result.get("session", {})
        if session.get("valid"):
            self.sid = session.get("sid")
        else:
            raise DemistoException(f"Authentication failed: {session.get('message', 'Unknown error')}")

    def logout(self) -> None:
        """Delete the current session."""
        if self.sid:
            try:
                self._http_request("DELETE", "/auth", headers={"sid": self.sid}, resp_type="response")
            except Exception:
                pass

    def api_request(
        self, method: str, url_suffix: str, params: dict | None = None, json_data: dict | None = None, resp_type: str = "json"
    ) -> Any:
        """Make an authenticated API request."""
        if not self.sid:
            self.authenticate()
        headers = {"sid": self.sid}
        return self._http_request(method, url_suffix, headers=headers, params=params, json_data=json_data, resp_type=resp_type)


def results_return(command: str, data: Any, title: str = "", headers: list[str] | None = None) -> None:
    if not title:
        title = f"PiHole V6 - {command}"

    table_data: Any = data
    if isinstance(data, dict):
        # Find the best key to use for the table
        for key in data:
            if isinstance(data[key], list):
                table_data = data[key]
                break

    readable = tableToMarkdown(title, table_data, headers=headers, removeNull=True)

    return_results(
        CommandResults(outputs_prefix=f"PiHoleV6.{command}", outputs_key_field="", outputs=data, readable_output=readable)
    )


def test_module(client: PiHoleV6Client) -> str:
    """Test connectivity and authentication."""
    client.authenticate()
    client.logout()
    return "ok"


# --- Stats Commands ---


def get_summary_command(client: PiHoleV6Client) -> None:
    data = client.api_request("GET", "/stats/summary")
    queries = data.get("queries", {})
    gravity = data.get("gravity", {})
    table = [
        {
            "Total Queries": queries.get("total"),
            "Blocked": queries.get("blocked"),
            "Cached": queries.get("cached"),
            "Forwarded": queries.get("forwarded"),
            "Gravity Domains": gravity.get("domains_being_blocked"),
        }
    ]
    readable = tableToMarkdown("PiHole V6 - Summary", table, removeNull=True)
    return_results(
        CommandResults(outputs_prefix="PiHoleV6.Summary", outputs_key_field="", outputs=data, readable_output=readable)
    )


def get_top_domains_command(client: PiHoleV6Client, args: dict) -> None:
    params: dict = {}
    if args.get("count"):
        params["count"] = int(args["count"])
    if args.get("blocked"):
        params["blocked"] = argToBoolean(args["blocked"])
    results_return("TopDomains", client.api_request("GET", "/stats/top_domains", params=params), title="PiHole V6 - Top Domains")


def get_top_clients_command(client: PiHoleV6Client, args: dict) -> None:
    params: dict = {}
    if args.get("count"):
        params["count"] = int(args["count"])
    if args.get("blocked"):
        params["blocked"] = argToBoolean(args["blocked"])
    results_return("TopClients", client.api_request("GET", "/stats/top_clients", params=params), title="PiHole V6 - Top Clients")


def get_upstreams_command(client: PiHoleV6Client) -> None:
    results_return("Upstreams", client.api_request("GET", "/stats/upstreams"), title="PiHole V6 - Upstreams")


def get_query_types_command(client: PiHoleV6Client) -> None:
    results_return("QueryTypes", client.api_request("GET", "/stats/query_types"), title="PiHole V6 - Query Types")


def get_recent_blocked_command(client: PiHoleV6Client, args: dict) -> None:
    params: dict = {}
    if args.get("count"):
        params["count"] = int(args["count"])
    results_return(
        "RecentBlocked", client.api_request("GET", "/stats/recent_blocked", params=params), title="PiHole V6 - Recently Blocked"
    )


# --- History Commands ---


def get_history_command(client: PiHoleV6Client) -> None:
    results_return("History", client.api_request("GET", "/history"), title="PiHole V6 - History")


def get_history_clients_command(client: PiHoleV6Client, args: dict) -> None:
    params: dict = {}
    if args.get("N"):
        params["N"] = int(args["N"])
    results_return(
        "HistoryClients", client.api_request("GET", "/history/clients", params=params), title="PiHole V6 - History Clients"
    )


# --- Queries Commands ---


def get_queries_command(client: PiHoleV6Client, args: dict) -> None:
    params: dict = {}
    for key in ["length", "domain", "client_ip", "client_name", "upstream", "type", "status"]:
        if args.get(key):
            params[key] = args[key]
    if args.get("length"):
        params["length"] = int(args["length"])
    results_return("Queries", client.api_request("GET", "/queries", params=params), title="PiHole V6 - Queries")


# --- DNS Blocking Commands ---


def get_blocking_status_command(client: PiHoleV6Client) -> None:
    data = client.api_request("GET", "/dns/blocking")
    table = [{"Blocking": data.get("blocking"), "Timer": data.get("timer")}]
    readable = tableToMarkdown("PiHole V6 - Blocking Status", table, removeNull=True)
    return_results(
        CommandResults(outputs_prefix="PiHoleV6.Blocking", outputs_key_field="", outputs=data, readable_output=readable)
    )


def set_blocking_command(client: PiHoleV6Client, args: dict) -> None:
    blocking = argToBoolean(args.get("blocking", "true"))
    json_data: dict = {"blocking": blocking}
    timer = args.get("timer")
    if timer:
        json_data["timer"] = int(timer)
    else:
        json_data["timer"] = None
    data = client.api_request("POST", "/dns/blocking", json_data=json_data)
    table = [{"Blocking": data.get("blocking"), "Timer": data.get("timer")}]
    readable = tableToMarkdown("PiHole V6 - Set Blocking", table, removeNull=True)
    return_results(
        CommandResults(outputs_prefix="PiHoleV6.Blocking", outputs_key_field="", outputs=data, readable_output=readable)
    )


# --- Domain Management Commands ---


def get_domains_command(client: PiHoleV6Client, args: dict) -> None:
    domain_type = args.get("type", "")
    kind = args.get("kind", "")
    suffix = "/domains"
    if domain_type:
        suffix += f"/{domain_type}"
    if kind:
        suffix += f"/{kind}"
    results_return("Domains", client.api_request("GET", suffix), title="PiHole V6 - Domains")


def add_domain_command(client: PiHoleV6Client, args: dict) -> None:
    domain_type = args.get("type")
    kind = args.get("kind")
    domain = args.get("domain")
    comment = args.get("comment")
    json_data: dict = {"domain": domain}
    if comment:
        json_data["comment"] = comment
    results_return(
        "Domains",
        client.api_request("POST", f"/domains/{domain_type}/{kind}", json_data=json_data),
        title=f"PiHole V6 - Domain Added ({domain_type}/{kind})",
    )


def delete_domain_command(client: PiHoleV6Client, args: dict) -> None:
    domain_type = args.get("type")
    kind = args.get("kind")
    domain = args.get("domain")
    client.api_request("DELETE", f"/domains/{domain_type}/{kind}/{domain}", resp_type="response")
    return_results(CommandResults(readable_output=f"### Domain '{domain}' deleted from {domain_type}/{kind}."))


# --- Info Commands ---


def get_version_command(client: PiHoleV6Client) -> None:
    data = client.api_request("GET", "/info/version")
    table = [
        {
            "Core": data.get("core", {}).get("local", {}).get("version"),
            "Web": data.get("web", {}).get("local", {}).get("version"),
            "FTL": data.get("ftl", {}).get("local", {}).get("version"),
        }
    ]
    readable = tableToMarkdown("PiHole V6 - Version", table, removeNull=True)
    return_results(
        CommandResults(outputs_prefix="PiHoleV6.Version", outputs_key_field="", outputs=data, readable_output=readable)
    )


def get_system_info_command(client: PiHoleV6Client) -> None:
    results_return("SystemInfo", client.api_request("GET", "/info/system"), title="PiHole V6 - System Info")


def get_ftl_info_command(client: PiHoleV6Client) -> None:
    results_return("FTLInfo", client.api_request("GET", "/info/ftl"), title="PiHole V6 - FTL Info")


def get_host_info_command(client: PiHoleV6Client) -> None:
    results_return("HostInfo", client.api_request("GET", "/info/host"), title="PiHole V6 - Host Info")


def get_sensors_command(client: PiHoleV6Client) -> None:
    results_return("Sensors", client.api_request("GET", "/info/sensors"), title="PiHole V6 - Sensors")


# --- Action Commands ---


def run_gravity_command(client: PiHoleV6Client) -> None:
    result = client.api_request("POST", "/action/gravity", resp_type="text")
    return_results(
        CommandResults(
            outputs_prefix="PiHoleV6.Gravity",
            outputs_key_field="",
            outputs={"output": result},
            readable_output=f"### PiHole V6 - Gravity Update\n{result}",
        )
    )


def restart_dns_command(client: PiHoleV6Client) -> None:
    results_return("RestartDNS", client.api_request("POST", "/action/restartdns"), title="PiHole V6 - Restart DNS")


def flush_logs_command(client: PiHoleV6Client) -> None:
    results_return("FlushLogs", client.api_request("POST", "/action/flush/logs"), title="PiHole V6 - Flush Logs")


def flush_network_command(client: PiHoleV6Client) -> None:
    results_return("FlushNetwork", client.api_request("POST", "/action/flush/network"), title="PiHole V6 - Flush Network")


# --- Network Commands ---


def get_network_devices_command(client: PiHoleV6Client) -> None:
    results_return("NetworkDevices", client.api_request("GET", "/network/devices"), title="PiHole V6 - Network Devices")


def get_network_gateway_command(client: PiHoleV6Client) -> None:
    results_return("Gateway", client.api_request("GET", "/network/gateway"), title="PiHole V6 - Gateway")


# --- Search Command ---


def search_domain_command(client: PiHoleV6Client, args: dict) -> None:
    domain = args.get("domain")
    results_return("Search", client.api_request("GET", f"/search/{domain}"), title=f"PiHole V6 - Search: {domain}")


# --- DHCP Commands ---


def get_dhcp_leases_command(client: PiHoleV6Client) -> None:
    results_return("DHCPLeases", client.api_request("GET", "/dhcp/leases"), title="PiHole V6 - DHCP Leases")


# --- Groups Commands ---


def get_groups_command(client: PiHoleV6Client) -> None:
    results_return("Groups", client.api_request("GET", "/groups"), title="PiHole V6 - Groups")


def add_group_command(client: PiHoleV6Client, args: dict) -> None:
    json_data: dict = {"name": args.get("name")}
    if args.get("comment"):
        json_data["comment"] = args["comment"]
    results_return("Groups", client.api_request("POST", "/groups", json_data=json_data), title="PiHole V6 - Group Added")


def delete_group_command(client: PiHoleV6Client, args: dict) -> None:
    name = args.get("name")
    client.api_request("DELETE", f"/groups/{name}", resp_type="response")
    return_results(CommandResults(readable_output=f"### Group '{name}' deleted."))


# --- Lists (Adlists) Commands ---


def get_lists_command(client: PiHoleV6Client) -> None:
    results_return("Lists", client.api_request("GET", "/lists"), title="PiHole V6 - Adlists")


def add_list_command(client: PiHoleV6Client, args: dict) -> None:
    json_data: dict = {"address": args.get("address")}
    if args.get("comment"):
        json_data["comment"] = args["comment"]
    if args.get("enabled") is not None:
        json_data["enabled"] = argToBoolean(args["enabled"])
    results_return("Lists", client.api_request("POST", "/lists", json_data=json_data), title="PiHole V6 - List Added")


def delete_list_command(client: PiHoleV6Client, args: dict) -> None:
    address = args.get("address")
    client.api_request("DELETE", f"/lists/{address}", resp_type="response")
    return_results(CommandResults(readable_output=f"### List '{address}' deleted."))


def main():
    """PARSE AND VALIDATE INTEGRATION PARAMS"""
    password = demisto.params().get("password")
    base_url = urljoin(demisto.params()["url"], "/api")
    verify_certificate = not demisto.params().get("insecure", False)
    proxy = demisto.params().get("proxy", False)

    LOG(f"Command being called is {demisto.command()}")

    client = PiHoleV6Client(base_url=base_url, password=password, verify=verify_certificate, proxy=proxy)

    try:
        command = demisto.command()
        args = demisto.args()

        if command == "test-module":
            result = test_module(client)
            demisto.results(result)

        # Stats
        elif command == "pihole-get-summary":
            get_summary_command(client)
        elif command == "pihole-get-top-domains":
            get_top_domains_command(client, args)
        elif command == "pihole-get-top-clients":
            get_top_clients_command(client, args)
        elif command == "pihole-get-upstreams":
            get_upstreams_command(client)
        elif command == "pihole-get-query-types":
            get_query_types_command(client)
        elif command == "pihole-get-recent-blocked":
            get_recent_blocked_command(client, args)

        # History
        elif command == "pihole-get-history":
            get_history_command(client)
        elif command == "pihole-get-history-clients":
            get_history_clients_command(client, args)

        # Queries
        elif command == "pihole-get-queries":
            get_queries_command(client, args)

        # DNS Blocking
        elif command == "pihole-get-blocking-status":
            get_blocking_status_command(client)
        elif command == "pihole-set-blocking":
            set_blocking_command(client, args)

        # Domain Management
        elif command == "pihole-get-domains":
            get_domains_command(client, args)
        elif command == "pihole-add-domain":
            add_domain_command(client, args)
        elif command == "pihole-delete-domain":
            delete_domain_command(client, args)

        # Info
        elif command == "pihole-get-version":
            get_version_command(client)
        elif command == "pihole-get-system-info":
            get_system_info_command(client)
        elif command == "pihole-get-ftl-info":
            get_ftl_info_command(client)
        elif command == "pihole-get-host-info":
            get_host_info_command(client)
        elif command == "pihole-get-sensors":
            get_sensors_command(client)

        # Actions
        elif command == "pihole-run-gravity":
            run_gravity_command(client)
        elif command == "pihole-restart-dns":
            restart_dns_command(client)
        elif command == "pihole-flush-logs":
            flush_logs_command(client)
        elif command == "pihole-flush-network":
            flush_network_command(client)

        # Network
        elif command == "pihole-get-network-devices":
            get_network_devices_command(client)
        elif command == "pihole-get-network-gateway":
            get_network_gateway_command(client)

        # Search
        elif command == "pihole-search-domain":
            search_domain_command(client, args)

        # DHCP
        elif command == "pihole-get-dhcp-leases":
            get_dhcp_leases_command(client)

        # Groups
        elif command == "pihole-get-groups":
            get_groups_command(client)
        elif command == "pihole-add-group":
            add_group_command(client, args)
        elif command == "pihole-delete-group":
            delete_group_command(client, args)

        # Lists (Adlists)
        elif command == "pihole-get-lists":
            get_lists_command(client)
        elif command == "pihole-add-list":
            add_list_command(client, args)
        elif command == "pihole-delete-list":
            delete_list_command(client, args)

        else:
            raise NotImplementedError(f"Command {command} is not implemented.")

    except Exception as e:
        return_error(f"Failed to execute {demisto.command()} command. Error: {e!s}")
    finally:
        client.logout()


if __name__ in ("__main__", "__builtin__", "builtins"):
    main()