Skip to content

Commit ec32dae

Browse files
authored
Merge pull request #112 from Qrome/2.12
2.12
2 parents 8bf9df4 + ff2f089 commit ec32dae

File tree

9 files changed

+643
-79
lines changed

9 files changed

+643
-79
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ SOFTWARE.
3030
* Configured through Web Interface
3131
* Display 3D print progress from your OctoPrint Server
3232
* Option to display Bitcoin current value
33+
* Option to display Pi-hole status and graph (each pixel accross is 10 minutes)
3334
* Basic Authorization around Configuration web interface
3435
* Support for OTA (loading firmware over WiFi)
3536
* Update firmware through web interface

marquee.ino.d1_mini_2.11.bin

-436 KB
Binary file not shown.

marquee.ino.d1_mini_2.12.bin

444 KB
Binary file not shown.

marquee.ino.d1_mini_wide_2.11.bin

-436 KB
Binary file not shown.

marquee.ino.d1_mini_wide_2.12.bin

445 KB
Binary file not shown.

marquee/PiHoleClient.cpp

Lines changed: 300 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,300 @@
1+
/** The MIT License (MIT)
2+
3+
Copyright (c) 2018 David Payne
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
22+
*/
23+
24+
#include "PiHoleClient.h"
25+
26+
PiHoleClient::PiHoleClient() {
27+
//Constructor
28+
}
29+
30+
void PiHoleClient::getPiHoleData(String server, int port) {
31+
32+
errorMessage = "";
33+
String response = "";
34+
35+
String apiGetData = "http://" + server + ":" + String(port) + "/admin/api.php?summary";
36+
Serial.println("Sending: " + apiGetData);
37+
HTTPClient http; //Object of class HTTPClient
38+
http.begin(apiGetData);// get the result
39+
int httpCode = http.GET();
40+
//Check the returning code
41+
if (httpCode > 0) {
42+
response = http.getString();
43+
http.end(); //Close connection
44+
if (httpCode != 200) {
45+
// Bad Response Code
46+
errorMessage = "Error response (" + String(httpCode) + "): " + response;
47+
Serial.println(errorMessage);
48+
return;
49+
}
50+
Serial.println("Response Code: " + String(httpCode));
51+
//Serial.println("Response: " + response);
52+
} else {
53+
errorMessage = "Failed to connect and get data: " + apiGetData;
54+
Serial.println(errorMessage);
55+
return;
56+
}
57+
58+
const size_t bufferSize = 2*JSON_OBJECT_SIZE(3) + JSON_OBJECT_SIZE(17) + 470;
59+
DynamicJsonBuffer jsonBuffer(bufferSize);
60+
61+
// Parse JSON object
62+
JsonObject& root = jsonBuffer.parseObject(response);
63+
if (!root.success()) {
64+
errorMessage = "Data Summary Parsing failed: " + apiGetData;
65+
Serial.println(errorMessage);
66+
return;
67+
}
68+
69+
piHoleData.domains_being_blocked = (const char*)root["domains_being_blocked"];
70+
piHoleData.dns_queries_today = (const char*)root["dns_queries_today"];
71+
piHoleData.ads_blocked_today = (const char*)root["ads_blocked_today"];
72+
piHoleData.ads_percentage_today = (const char*)root["ads_percentage_today"];
73+
piHoleData.unique_domains = (const char*)root["unique_domains"];
74+
piHoleData.queries_forwarded = (const char*)root["queries_forwarded"];
75+
piHoleData.queries_cached = (const char*)root["queries_cached"];
76+
piHoleData.clients_ever_seen = (const char*)root["clients_ever_seen"];
77+
piHoleData.unique_clients = (const char*)root["unique_clients"];
78+
piHoleData.dns_queries_all_types = (const char*)root["dns_queries_all_types"];
79+
piHoleData.reply_NODATA = (const char*)root["reply_NODATA"];
80+
piHoleData.reply_NXDOMAIN = (const char*)root["reply_NXDOMAIN"];
81+
piHoleData.reply_CNAME = (const char*)root["reply_CNAME"];
82+
piHoleData.reply_IP = (const char*)root["reply_IP"];
83+
piHoleData.privacy_level = (const char*)root["privacy_level"];
84+
piHoleData.piHoleStatus = (const char*)root["status"];
85+
86+
Serial.println("Pi-Hole Status: " + piHoleData.piHoleStatus);
87+
Serial.println("Todays Percentage Blocked: " + piHoleData.ads_percentage_today);
88+
Serial.println();
89+
}
90+
91+
void PiHoleClient::getTopClientsBlocked(String server, int port, String apiKey) {
92+
errorMessage = "";
93+
resetClientsBlocked();
94+
95+
if (apiKey == "") {
96+
errorMessage = "Pi-hole API Key is required to view Top Clients Blocked.";
97+
return;
98+
}
99+
100+
String response = "";
101+
102+
String apiGetData = "http://" + server + ":" + String(port) + "/admin/api.php?topClientsBlocked=3&auth=" + apiKey;
103+
Serial.println("Sending: " + apiGetData);
104+
HTTPClient http; //Object of class HTTPClient
105+
http.begin(apiGetData);// get the result
106+
int httpCode = http.GET();
107+
//Check the returning code
108+
if (httpCode > 0) {
109+
response = http.getString();
110+
http.end(); //Close connection
111+
if (httpCode != 200) {
112+
// Bad Response Code
113+
errorMessage = "Error response (" + String(httpCode) + "): " + response;
114+
Serial.println(errorMessage);
115+
return;
116+
}
117+
Serial.println("Response Code: " + String(httpCode));
118+
//Serial.println("Response: " + response);
119+
} else {
120+
errorMessage = "Failed to get data: " + apiGetData;
121+
Serial.println(errorMessage);
122+
return;
123+
}
124+
125+
const size_t bufferSize = JSON_OBJECT_SIZE(1) + JSON_OBJECT_SIZE(3) + 70;
126+
DynamicJsonBuffer jsonBuffer(bufferSize);
127+
128+
// Parse JSON object
129+
JsonObject& root = jsonBuffer.parseObject(response);
130+
if (!root.success()) {
131+
errorMessage = "Data Parsing failed -- verify your Pi-hole API key.";
132+
Serial.println(errorMessage);
133+
return;
134+
}
135+
136+
JsonObject& blocked = root["top_sources_blocked"];
137+
int count = 0;
138+
for (JsonPair p : blocked) {
139+
blockedClients[count].clientAddress = (const char*)p.key;
140+
blockedClients[count].blockedCount = p.value.as<int>();
141+
Serial.println("Blocked Client " + String(count+1) + ": " + blockedClients[count].clientAddress + " (" + String(blockedClients[count].blockedCount) + ")");
142+
count++;
143+
}
144+
Serial.println();
145+
}
146+
147+
void PiHoleClient::getGraphData(String server, int port) {
148+
149+
HTTPClient http;
150+
151+
String apiGetData = "http://" + server + ":" + String(port) + "/admin/api.php?overTimeData10mins";
152+
resetBlockedGraphData();
153+
Serial.println("Getting Pi-Hole Graph Data");
154+
Serial.println(apiGetData);
155+
http.begin(apiGetData);
156+
int httpCode = http.GET();
157+
158+
String result = "";
159+
errorMessage = "";
160+
boolean track = false;
161+
int countBracket = 0;
162+
blockedCount = 0;
163+
164+
if (httpCode > 0) { // checks for connection
165+
Serial.printf("[HTTP] GET... code: %d\n", httpCode);
166+
if(httpCode == HTTP_CODE_OK) {
167+
// get length of document (is -1 when Server sends no Content-Length header)
168+
int len = http.getSize();
169+
// create buffer for read
170+
char buff[128] = { 0 };
171+
// get tcp stream
172+
WiFiClient * stream = http.getStreamPtr();
173+
// read all data from server
174+
Serial.println("Start reading...");
175+
while(http.connected() && (len > 0 || len == -1)) {
176+
// get available data size
177+
size_t size = stream->available();
178+
if(size) {
179+
// read up to 128 byte
180+
int c = stream->readBytes(buff, ((size > sizeof(buff)) ? sizeof(buff) : size));
181+
for(int i=0;i<c;i++) {
182+
if (track && countBracket >= 3) {
183+
if (buff[i] == ',' || buff[i] == '}') {
184+
blocked[blockedCount] = result.toInt();
185+
if (blocked[blockedCount] > blockedHigh) {
186+
blockedHigh = blocked[blockedCount];
187+
}
188+
//Serial.println("Pi-hole Graph point (" + String(blockedCount+1) + "): " + String(blocked[blockedCount]));
189+
blockedCount++;
190+
result = "";
191+
track = false;
192+
} else {
193+
result += buff[i];
194+
}
195+
} else if (buff[i] == '{') {
196+
countBracket++;
197+
} else if (countBracket >= 3 && buff[i] == ':') {
198+
track = true;
199+
}
200+
}
201+
202+
if(len > 0)
203+
len -= c;
204+
}
205+
delay(1);
206+
}
207+
}
208+
http.end();
209+
} else {
210+
errorMessage = "Connection for Pi-Hole data failed: " + String(apiGetData);
211+
Serial.println(errorMessage); //error message if no client connect
212+
Serial.println();
213+
return;
214+
}
215+
216+
217+
Serial.println("High Value: " + String(blockedHigh));
218+
Serial.println("Count: " + String(blockedCount));
219+
Serial.println();
220+
}
221+
222+
void PiHoleClient::resetClientsBlocked() {
223+
for (int inx = 0; inx < 3; inx++) {
224+
blockedClients[inx].clientAddress = "";
225+
blockedClients[inx].blockedCount = 0;
226+
}
227+
}
228+
229+
void PiHoleClient::resetBlockedGraphData() {
230+
for (int inx = 0; inx < 144; inx++) {
231+
blocked[inx] = 0;
232+
}
233+
blockedCount = 0;
234+
blockedHigh = 0;
235+
}
236+
237+
String PiHoleClient::getDomainsBeingBlocked() {
238+
return piHoleData.domains_being_blocked;
239+
}
240+
241+
String PiHoleClient::getDnsQueriesToday() {
242+
return piHoleData.dns_queries_today;
243+
}
244+
245+
String PiHoleClient::getAdsBlockedToday() {
246+
return piHoleData.ads_blocked_today;
247+
}
248+
249+
String PiHoleClient::getAdsPercentageToday() {
250+
return piHoleData.ads_percentage_today;
251+
}
252+
253+
String PiHoleClient::getUniqueClients() {
254+
return piHoleData.unique_clients;
255+
}
256+
257+
String PiHoleClient::getClientsEverSeen() {
258+
return piHoleData.clients_ever_seen;
259+
}
260+
261+
/* //Need to add the following
262+
String getUniqueDomains();
263+
String getQueriesForwarded();
264+
String getQueriesCached();
265+
String getDnsQueriesAllTypes();
266+
String getReplyNODATA();
267+
String getReplyNXDOMAIN();
268+
String getReplyCNAME();
269+
String getReplyIP();
270+
String getPrivacyLevel();
271+
*/
272+
273+
274+
String PiHoleClient::getPiHoleStatus() {
275+
return piHoleData.piHoleStatus;
276+
}
277+
278+
String PiHoleClient::getError() {
279+
return errorMessage;
280+
}
281+
282+
int *PiHoleClient::getBlockedAds() {
283+
return blocked;
284+
}
285+
286+
int PiHoleClient::getBlockedCount() {
287+
return blockedCount;
288+
}
289+
290+
int PiHoleClient::getBlockedHigh() {
291+
return blockedHigh;
292+
}
293+
294+
String PiHoleClient::getTopClientBlocked(int index) {
295+
return blockedClients[index].clientAddress;
296+
}
297+
298+
int PiHoleClient::getTopClientBlockedCount(int index) {
299+
return blockedClients[index].blockedCount;
300+
}

0 commit comments

Comments
 (0)