pihole6api 0.1.0__py3-none-any.whl → 0.1.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pihole6api/client.py CHANGED
@@ -41,4 +41,8 @@ class PiHole6Client:
41
41
  :param full: Boolean flag to get the full dataset.
42
42
  :return: API response containing PADD summary.
43
43
  """
44
- return self.connection.get("padd", params={"full": str(full).lower()})
44
+ return self.connection.get("padd", params={"full": str(full).lower()})
45
+
46
+ def close_session(self):
47
+ """Close the Pi-hole session by calling the exit method in the connection."""
48
+ return self.connection.exit()
pihole6api/config.py CHANGED
@@ -1,4 +1,5 @@
1
1
  import json
2
+ import urllib.parse
2
3
 
3
4
  class PiHole6Configuration:
4
5
  def __init__(self, connection):
@@ -77,3 +78,49 @@ class PiHole6Configuration:
77
78
  :return: API response confirming the deletion.
78
79
  """
79
80
  return self.connection.delete(f"config/{element}/{value}")
81
+
82
+ def add_local_a_record(self, host, ip):
83
+ """
84
+ Add a local A record to Pi-hole.
85
+
86
+ :param host: The hostname (e.g., "foo.dev")
87
+ :param ip: The IP address (e.g., "192.168.1.1")
88
+ :return: API response
89
+ """
90
+ encoded_value = urllib.parse.quote(f"{ip} {host}")
91
+ return self.connection.put(f"config/dns/hosts/{encoded_value}")
92
+
93
+ def remove_local_a_record(self, host, ip):
94
+ """
95
+ Remove a local A record from Pi-hole.
96
+
97
+ :param host: The hostname (e.g., "foo.dev")
98
+ :param ip: The IP address (e.g., "192.168.1.1")
99
+ :return: API response
100
+ """
101
+ encoded_value = urllib.parse.quote(f"{ip} {host}")
102
+ return self.connection.delete(f"config/dns/hosts/{encoded_value}")
103
+
104
+ def add_local_cname(self, host, target, ttl=300):
105
+ """
106
+ Add a local CNAME record to Pi-hole.
107
+
108
+ :param host: The CNAME alias (e.g., "bar.xyz")
109
+ :param target: The target hostname (e.g., "foo.dev")
110
+ :param ttl: Time-to-live for the record (default: 300)
111
+ :return: API response
112
+ """
113
+ encoded_value = urllib.parse.quote(f"{host},{target},{ttl}")
114
+ return self.connection.put(f"config/dns/cnameRecords/{encoded_value}")
115
+
116
+ def remove_local_cname(self, host, target, ttl=300):
117
+ """
118
+ Remove a local CNAME record from Pi-hole.
119
+
120
+ :param host: The CNAME alias (e.g., "bar.xyz")
121
+ :param target: The target hostname (e.g., "foo.dev")
122
+ :param ttl: Time-to-live for the record (default: 300)
123
+ :return: API response
124
+ """
125
+ encoded_value = urllib.parse.quote(f"{host},{target},{ttl}")
126
+ return self.connection.delete(f"config/dns/cnameRecords/{encoded_value}")
pihole6api/conn.py CHANGED
@@ -124,4 +124,15 @@ class PiHole6Connection:
124
124
 
125
125
  def patch(self, endpoint, data=None):
126
126
  """Send a PATCH request."""
127
- return self._do_call("PATCH", endpoint, data=data)
127
+ return self._do_call("PATCH", endpoint, data=data)
128
+
129
+ def exit(self):
130
+ """Delete the current session."""
131
+ response = self.delete("auth")
132
+
133
+ # Clear stored session info
134
+ self.session_id = None
135
+ self.csrf_token = None
136
+ self.validity = None
137
+
138
+ return response
pihole6api/metrics.py CHANGED
@@ -11,68 +11,178 @@ class PiHole6Metrics:
11
11
  """Get activity graph data"""
12
12
  return self.connection.get("history")
13
13
 
14
- def get_history_clients(self):
15
- """Get per-client activity graph data"""
16
- return self.connection.get("history/clients")
14
+ def get_history_clients(self, clients=20):
15
+ """
16
+ Get per-client activity graph data
17
17
 
18
- def get_history_database(self):
19
- """Get long-term activity graph data"""
20
- return self.connection.get("history/database")
18
+ :param num: Number of clients to return, 0 will return all clients
19
+ """
20
+ params = {"n": clients}
21
+ return self.connection.get("history/clients", params=params)
21
22
 
22
- def get_history_database_clients(self):
23
- """Get per-client long-term activity graph data"""
24
- return self.connection.get("history/database/clients")
23
+ def get_history_database(self, start, end):
24
+ """
25
+ Get long-term activity graph data
26
+
27
+ :param start: Start date in unix timestamp format
28
+ :param end: End date in unix timestamp format
29
+ """
30
+ params = {"from": start, "until": end}
31
+ return self.connection.get("history/database", params=params)
32
+
33
+ def get_history_database_clients(self, start, end):
34
+ """
35
+ Get per-client long-term activity graph data
36
+
37
+ :param start: Start date in unix timestamp format
38
+ :param end: End date in unix timestamp format
39
+ """
40
+ params = {"from": start, "until": end}
41
+ return self.connection.get("history/database/clients", params=params)
25
42
 
26
43
  # Query API Endpoints
27
- def get_queries(self):
28
- """Get query log"""
29
- return self.connection.get("queries")
44
+ def get_queries(self, n=100, from_ts=None, until_ts=None, upstream=None, domain=None, client=None, cursor=None):
45
+ """
46
+ Get query log with optional filtering parameters.
47
+
48
+ :param int n: Number of queries to retrieve (default: 100).
49
+ :param int from_ts: Unix timestamp to filter queries from this time onward (optional).
50
+ :param int until_ts: Unix timestamp to filter queries up to this time (optional).
51
+ :param str upstream: Filter queries sent to a specific upstream destination (optional).
52
+ :param str domain: Filter queries for specific domains, supports wildcards `*` (optional).
53
+ :param str client: Filter queries originating from a specific client (optional).
54
+ :param str cursor: Cursor for pagination to fetch the next chunk of results (optional).
55
+ """
56
+ params = {
57
+ "n": n,
58
+ "from": from_ts,
59
+ "until": until_ts,
60
+ "upstream": upstream,
61
+ "domain": domain,
62
+ "client": client,
63
+ "cursor": cursor
64
+ }
65
+ params = {k: v for k, v in params.items() if v is not None}
66
+ return self.connection.get("queries", params=params)
67
+
30
68
 
31
69
  def get_query_suggestions(self):
32
70
  """Get query filter suggestions"""
33
71
  return self.connection.get("queries/suggestions")
34
72
 
35
73
  # Stats Database API Endpoints
36
- def get_stats_database_query_types(self):
37
- """Get query types (long-term database)"""
38
- return self.connection.get("stats/database/query_types")
39
-
40
- def get_stats_database_summary(self):
41
- """Get database content details"""
42
- return self.connection.get("stats/database/summary")
74
+ def get_stats_database_query_types(self, start, end):
75
+ """
76
+ Get query types (long-term database)
77
+
78
+ :param start: Start date in unix timestamp format
79
+ :param end: End date in unix timestamp format
80
+ """
81
+ params = {"from": start, "until": end}
82
+ return self.connection.get("stats/database/query_types", params=params)
43
83
 
44
- def get_stats_database_top_clients(self):
45
- """Get top clients (long-term database)"""
46
- return self.connection.get("stats/database/top_clients")
84
+ def get_stats_database_summary(self, start, end):
85
+ """
86
+ Get database content details
87
+
88
+ :param start: Start date in unix timestamp format
89
+ :param end: End date in unix timestamp format
90
+ """
91
+ params = {"from": start, "until": end}
92
+ return self.connection.get("stats/database/summary", params=params)
47
93
 
48
- def get_stats_database_top_domains(self):
49
- """Get top domains (long-term database)"""
50
- return self.connection.get("stats/database/top_domains")
94
+ def get_stats_database_top_clients(self, start, end, blocked=None, count=None):
95
+ """
96
+ Get top clients (long-term database).
51
97
 
52
- def get_stats_database_upstreams(self):
53
- """Get upstream metrics (long-term database)"""
54
- return self.connection.get("stats/database/upstreams")
98
+ :param int start: Start date in Unix timestamp format.
99
+ :param int end: End date in Unix timestamp format.
100
+ :param bool blocked: Return information about permitted or blocked queries (optional).
101
+ :param int count: Number of requested items (optional).
102
+ """
103
+ params = {
104
+ "from": start,
105
+ "until": end,
106
+ "blocked": str(blocked).lower() if blocked is not None else None,
107
+ "count": count
108
+ }
109
+ params = {k: v for k, v in params.items() if v is not None}
110
+ return self.connection.get("stats/database/top_clients", params=params)
111
+
112
+ def get_stats_database_top_domains(self, start, end, blocked=None, count=None):
113
+ """
114
+ Get top domains (long-term database)
115
+
116
+ :param int start: Start date in Unix timestamp format.
117
+ :param int end: End date in Unix timestamp format.
118
+ :param bool blocked: Return information about permitted or blocked queries (optional).
119
+ :param int count: Number of requested items (optional).
120
+ """
121
+ params = {
122
+ "from": start,
123
+ "until": end,
124
+ "blocked": str(blocked).lower() if blocked is not None else None,
125
+ "count": count
126
+ }
127
+ params = {k: v for k, v in params.items() if v is not None}
128
+ return self.connection.get("stats/database/top_domains", params=params)
129
+
130
+ def get_stats_database_upstreams(self, start, end):
131
+ """
132
+ Get upstream metrics (long-term database)
133
+
134
+ :param start: Start date in unix timestamp format
135
+ :param end: End date in unix timestamp format
136
+ """
137
+ params = {"from": start, "until": end}
138
+ return self.connection.get("stats/database/upstreams", params=params)
55
139
 
56
140
  # Stats API Endpoints
57
141
  def get_stats_query_types(self):
58
142
  """Get current query types"""
59
143
  return self.connection.get("stats/query_types")
60
144
 
61
- def get_stats_recent_blocked(self):
62
- """Get most recently blocked domain"""
63
- return self.connection.get("stats/recent_blocked")
145
+ def get_stats_recent_blocked(self, count=None):
146
+ """
147
+ Get most recently blocked domain
148
+
149
+ :param int count: Number of requested items (optional).
150
+ """
151
+ params = {"count": count}
152
+ params = {k: v for k, v in params.items() if v is not None}
153
+ return self.connection.get("stats/recent_blocked", params=params)
64
154
 
65
155
  def get_stats_summary(self):
66
156
  """Get an overview of Pi-hole activity"""
67
157
  return self.connection.get("stats/summary")
68
158
 
69
- def get_stats_top_clients(self):
70
- """Get top clients"""
71
- return self.connection.get("stats/top_clients")
72
-
73
- def get_stats_top_domains(self):
74
- """Get top domains"""
75
- return self.connection.get("stats/top_domains")
159
+ def get_stats_top_clients(self, blocked=None, count=None):
160
+ """
161
+ Get top clients
162
+
163
+ :param bool blocked: Return information about permitted or blocked queries (optional).
164
+ :param int count: Number of requested items (optional).
165
+ """
166
+ params = {
167
+ "blocked": str(blocked).lower() if blocked is not None else None,
168
+ "count": count
169
+ }
170
+ params = {k: v for k, v in params.items() if v is not None}
171
+ return self.connection.get("stats/top_clients", params=params)
172
+
173
+ def get_stats_top_domains(self, blocked=None, count=None):
174
+ """
175
+ Get top domains
176
+
177
+ :param bool blocked: Return information about permitted or blocked queries (optional).
178
+ :param int count: Number of requested items (optional).
179
+ """
180
+ params = {
181
+ "blocked": str(blocked).lower() if blocked is not None else None,
182
+ "count": count
183
+ }
184
+ params = {k: v for k, v in params.items() if v is not None}
185
+ return self.connection.get("stats/top_domains", params=params)
76
186
 
77
187
  def get_stats_upstreams(self):
78
188
  """Get upstream destinations"""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: pihole6api
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Summary: Python API Client for Pi-hole 6
5
5
  Author-email: Shane Barbetta <shane@barbetta.me>
6
6
  License: MIT
@@ -23,7 +23,7 @@ Description-Content-Type: text/markdown
23
23
  License-File: LICENSE
24
24
  Requires-Dist: requests>=2.26.0
25
25
 
26
- # pihole6api
26
+ # 🍓 pihole6api
27
27
 
28
28
  This package provides a simple, modular SDK for the PiHole 6 REST API.
29
29
 
@@ -55,7 +55,7 @@ pip install -e .
55
55
  ### Initialize the Client
56
56
 
57
57
  ```python
58
- from pihole6api.client import PiHole6Client
58
+ from pihole6api import PiHole6Client
59
59
  client = PiHole6Client("https://your-pihole.local/", "your-password")
60
60
  ```
61
61
 
@@ -1,19 +1,19 @@
1
1
  pihole6api/__init__.py,sha256=OKDAH2I6UjXcBmcj6rn5aNg5J60GCUBVFJ-_t83GiVQ,898
2
2
  pihole6api/actions.py,sha256=8CBkr8nYfT8yfdCO6F9M9nompaYcFdsaYGiEa1eVDCw,693
3
- pihole6api/client.py,sha256=IgUjiBUx_EFzQujgQZcX2h9dfDfI83aVeipUeStHWY0,1855
3
+ pihole6api/client.py,sha256=HYdRh3CSZJ0srbkpjIVnLo-iy1avqKDUne5ji2Aq394,2013
4
4
  pihole6api/client_management.py,sha256=opPYGrjuW6SiwuxuvoOxnqjpmflU2znKHsqFbGSS3Gg,2439
5
- pihole6api/config.py,sha256=RlTyIZ0G9K2qZxB6nZgT3HsCn03y7q1qRjVwJ4ZY5Kc,2914
6
- pihole6api/conn.py,sha256=sYjS8Gvk2GuA482eyvFknTKgeeesLnS5ylI9I4yUNRo,4581
5
+ pihole6api/config.py,sha256=NdBHOudz147oIs5YVR3U4WLvqk3hU3HlZHnshy1NK4g,4680
6
+ pihole6api/conn.py,sha256=ArXgFtb3QG2HeBeDvhEuZ7jcBDR6fW8QorlCF46Y3MM,4840
7
7
  pihole6api/dhcp.py,sha256=1A3z-3q9x51-6MOC3JMl7yR_5pHmRxZtMWtPqzWxYm0,629
8
8
  pihole6api/dns_control.py,sha256=mxV3AIuGCsx0-1ibpMXor9QUGd_fDFfeaUENPhIK_TY,853
9
9
  pihole6api/domain_management.py,sha256=vxhQSG5F8EFDGqtiNkF0H_KOWFMerXaAuJZT0nMa8ec,3492
10
10
  pihole6api/ftl_info.py,sha256=e9W9vwkF8nHzjVlHpIMe-55qhhQngMj0swMlp2QNvPg,2540
11
11
  pihole6api/group_management.py,sha256=MGHwegw-b9U9PIA-IBzqT-a1kYkpXyjfSXJJJjkyTxc,2225
12
12
  pihole6api/list_management.py,sha256=B98FdgsIZhfRJHRxtRmwWsRPJ4rGlhvN-9gFEGdbb-c,3396
13
- pihole6api/metrics.py,sha256=KgklFtXrO_f2ZnFd6C7tATVPuUJwMl_dGr4Ul5Wgc1k,2758
13
+ pihole6api/metrics.py,sha256=czNyx9tUf2yZi_HnUpqykrtW51c042Rxq3zFl2_GjLY,7379
14
14
  pihole6api/network_info.py,sha256=u5NIteFoI-yav05eE6v81-9gs3MeZVBlNCSBS5gzCT8,1006
15
- pihole6api-0.1.0.dist-info/LICENSE,sha256=hpO6J6J9O1VZxZeHQTxKMTmuobaHbApiZxp279I4xNU,1062
16
- pihole6api-0.1.0.dist-info/METADATA,sha256=Fefv0L6mqT5fYsBbNLr8uHNS2XBk6bIR0KyFvdCY5Ng,3839
17
- pihole6api-0.1.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
18
- pihole6api-0.1.0.dist-info/top_level.txt,sha256=Qrh46lxEC54rBR8T53em-tuZLWbmi1SDwL1rOhsgrME,11
19
- pihole6api-0.1.0.dist-info/RECORD,,
15
+ pihole6api-0.1.2.dist-info/LICENSE,sha256=hpO6J6J9O1VZxZeHQTxKMTmuobaHbApiZxp279I4xNU,1062
16
+ pihole6api-0.1.2.dist-info/METADATA,sha256=0kZSJq4RYKkx6fHMae0Tohtbwm7QVORbYnZ15GJdgPw,3837
17
+ pihole6api-0.1.2.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
18
+ pihole6api-0.1.2.dist-info/top_level.txt,sha256=Qrh46lxEC54rBR8T53em-tuZLWbmi1SDwL1rOhsgrME,11
19
+ pihole6api-0.1.2.dist-info/RECORD,,