-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
[pihole] Integration of API v6 #19350
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
8841ce8
d429e35
4b8b331
2b5f5d8
09987e9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -33,8 +33,10 @@ | |
| import org.eclipse.jdt.annotation.NonNullByDefault; | ||
| import org.eclipse.jdt.annotation.Nullable; | ||
| import org.eclipse.jetty.client.HttpClient; | ||
| import org.openhab.binding.pihole.internal.PiHoleBindingConstants.Channels.DisableEnable; | ||
| import org.openhab.binding.pihole.internal.rest.AdminService; | ||
| import org.openhab.binding.pihole.internal.rest.JettyAdminService; | ||
| import org.openhab.binding.pihole.internal.rest.JettyAdminServiceV6; | ||
| import org.openhab.binding.pihole.internal.rest.model.DnsStatistics; | ||
| import org.openhab.core.i18n.TimeZoneProvider; | ||
| import org.openhab.core.library.types.DateTimeType; | ||
|
|
@@ -51,28 +53,32 @@ | |
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
|
|
||
| import com.google.gson.Gson; | ||
|
|
||
| /** | ||
| * The {@link PiHoleHandler} is responsible for handling commands, which are | ||
| * sent to one of the channels. | ||
| * | ||
| * @author Martin Grzeslowski - Initial contribution | ||
| */ | ||
| @NonNullByDefault | ||
| public class PiHoleHandler extends BaseThingHandler implements AdminService { | ||
| public class PiHoleHandler extends BaseThingHandler { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Making There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did not see any obvious reason to expose these methods here. They are still available to actions. |
||
| private static final int HTTP_DELAY_SECONDS = 1; | ||
| private final Logger logger = LoggerFactory.getLogger(PiHoleHandler.class); | ||
| private final Object lock = new Object(); | ||
| private final TimeZoneProvider timeZoneProvider; | ||
| private final HttpClient httpClient; | ||
| private final Gson gson; | ||
|
|
||
| private @Nullable AdminService adminService; | ||
| private @Nullable DnsStatistics dnsStatistics; | ||
| private @Nullable ScheduledFuture<?> scheduledFuture; | ||
|
|
||
| public PiHoleHandler(Thing thing, TimeZoneProvider timeZoneProvider, HttpClient httpClient) { | ||
| public PiHoleHandler(Thing thing, TimeZoneProvider timeZoneProvider, HttpClient httpClient, Gson gson) { | ||
| super(thing); | ||
| this.timeZoneProvider = timeZoneProvider; | ||
| this.httpClient = httpClient; | ||
| this.gson = gson; | ||
| } | ||
|
|
||
| @Override | ||
|
|
@@ -101,7 +107,11 @@ public void initialize() { | |
| updateStatus(OFFLINE, CONFIGURATION_ERROR, "@text/handler.init.noToken"); | ||
| return; | ||
| } | ||
| adminService = new JettyAdminService(config.token, hostname, httpClient); | ||
|
|
||
| adminService = PiHoleConfiguration.API_V6.equals(config.serverVersion) | ||
| ? new JettyAdminServiceV6(config.token, hostname, httpClient, gson) | ||
| : new JettyAdminService(config.token, hostname, httpClient, gson); | ||
|
|
||
| scheduledFuture = scheduler.scheduleWithFixedDelay(this::update, 0, config.refreshIntervalSeconds, SECONDS); | ||
|
|
||
| // do not set status here, the background task will do it. | ||
|
|
@@ -234,7 +244,6 @@ public void dispose() { | |
| super.dispose(); | ||
| } | ||
|
|
||
| @Override | ||
| public Optional<DnsStatistics> summary() throws PiHoleException { | ||
| var local = adminService; | ||
| if (local == null) { | ||
|
|
@@ -243,7 +252,6 @@ public Optional<DnsStatistics> summary() throws PiHoleException { | |
| return local.summary(); | ||
| } | ||
|
|
||
| @Override | ||
| public void disableBlocking(long seconds) throws PiHoleException { | ||
| var local = adminService; | ||
| if (local == null) { | ||
|
|
@@ -259,7 +267,6 @@ public void disableBlocking(long seconds) throws PiHoleException { | |
| } | ||
| } | ||
|
|
||
| @Override | ||
| public void enableBlocking() throws PiHoleException { | ||
| var local = adminService; | ||
| if (local == null) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,36 +13,67 @@ | |
| package org.openhab.binding.pihole.internal.rest; | ||
|
|
||
| import java.util.Optional; | ||
| import java.util.concurrent.ExecutionException; | ||
| import java.util.concurrent.TimeoutException; | ||
|
|
||
| import org.eclipse.jdt.annotation.NonNullByDefault; | ||
| import org.eclipse.jetty.client.HttpClient; | ||
| import org.eclipse.jetty.client.api.ContentResponse; | ||
| import org.eclipse.jetty.client.api.Request; | ||
| import org.openhab.binding.pihole.internal.PiHoleException; | ||
| import org.openhab.binding.pihole.internal.rest.model.DnsStatistics; | ||
|
|
||
| import com.google.gson.Gson; | ||
|
|
||
| /** | ||
| * @author Martin Grzeslowski - Initial contribution | ||
magx2 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| * @author Gaël L'hopital - Initial contribution | ||
| */ | ||
| @NonNullByDefault | ||
| public interface AdminService { | ||
| public abstract class AdminService { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why did you make it a class? i do not see any logic added There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because it shares properties (and 1 method) between the two versions of the AdminService |
||
| protected static final long TIMEOUT_SECONDS = 10L; | ||
|
|
||
| protected final String token; | ||
|
|
||
| protected final HttpClient client; | ||
| protected final Gson gson; | ||
|
|
||
| public AdminService(String token, HttpClient client, Gson gson) { | ||
| super(); | ||
magx2 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| this.token = token; | ||
| this.client = client; | ||
| this.gson = gson; | ||
| } | ||
|
|
||
| /** | ||
| * Retrieves a summary of DNS statistics. | ||
| * | ||
| * @return An optional containing the DNS statistics. | ||
| * @throws PiHoleException In case of error | ||
| */ | ||
| Optional<DnsStatistics> summary() throws PiHoleException; | ||
| public abstract Optional<DnsStatistics> summary() throws PiHoleException; | ||
|
|
||
| /** | ||
| * Disables blocking for a specified duration. | ||
| * | ||
| * @param seconds The duration in seconds for which blocking should be disabled. | ||
| * @throws PiHoleException In case of error | ||
| */ | ||
| void disableBlocking(long seconds) throws PiHoleException; | ||
| public abstract void disableBlocking(long seconds) throws PiHoleException; | ||
|
|
||
| /** | ||
| * Enables blocking. | ||
| * | ||
| * @throws PiHoleException In case of error | ||
| */ | ||
| void enableBlocking() throws PiHoleException; | ||
| } | ||
| public abstract void enableBlocking() throws PiHoleException; | ||
|
|
||
| protected static ContentResponse send(Request request) throws PiHoleException { | ||
magx2 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| try { | ||
| return request.send(); | ||
| } catch (InterruptedException | TimeoutException | ExecutionException e) { | ||
| throw new PiHoleException( | ||
| "Exception while sending request to Pi-hole. %s".formatted(e.getLocalizedMessage()), e); | ||
| } | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| /* | ||
| * Copyright (c) 2010-2025 Contributors to the openHAB project | ||
| * | ||
| * See the NOTICE file(s) distributed with this work for additional | ||
| * information. | ||
| * | ||
| * This program and the accompanying materials are made available under the | ||
| * terms of the Eclipse Public License 2.0 which is available at | ||
| * http://www.eclipse.org/legal/epl-2.0 | ||
| * | ||
| * SPDX-License-Identifier: EPL-2.0 | ||
| */ | ||
| package org.openhab.binding.pihole.internal.rest; | ||
|
|
||
| import static java.util.concurrent.TimeUnit.SECONDS; | ||
|
|
||
| import java.net.URI; | ||
| import java.util.Optional; | ||
|
|
||
| import org.eclipse.jdt.annotation.NonNullByDefault; | ||
| import org.eclipse.jdt.annotation.Nullable; | ||
| import org.eclipse.jetty.client.HttpClient; | ||
| import org.eclipse.jetty.client.util.StringContentProvider; | ||
| import org.eclipse.jetty.http.HttpHeader; | ||
| import org.eclipse.jetty.http.HttpMethod; | ||
| import org.openhab.binding.pihole.internal.PiHoleException; | ||
| import org.openhab.binding.pihole.internal.rest.model.DnsStatistics; | ||
| import org.openhab.binding.pihole.internal.rest.model.v6.Password; | ||
| import org.openhab.binding.pihole.internal.rest.model.v6.SessionAnswer; | ||
| import org.openhab.binding.pihole.internal.rest.model.v6.SessionAnswer.Session; | ||
| import org.openhab.binding.pihole.internal.rest.model.v6.StatAnswer; | ||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
|
|
||
| import com.google.gson.Gson; | ||
|
|
||
| /** | ||
| * @author Martin Grzeslowski - Initial contribution | ||
|
||
| */ | ||
| @NonNullByDefault | ||
| public class JettyAdminServiceV6 extends AdminService { | ||
| private final Logger logger = LoggerFactory.getLogger(JettyAdminServiceV6.class); | ||
| protected final URI apiUrl; | ||
| private @Nullable Session session; | ||
|
|
||
| public JettyAdminServiceV6(String token, URI baseUrl, HttpClient client, Gson gson) { | ||
| super(token, client, gson); | ||
| apiUrl = baseUrl.resolve("/api"); | ||
| } | ||
|
|
||
| private void getAuth() throws PiHoleException { | ||
| logger.debug("Check if authentication is required"); | ||
| var authUrl = apiUrl.resolve(apiUrl.getPath() + "/auth"); | ||
| var request = client.newRequest(authUrl).timeout(TIMEOUT_SECONDS, SECONDS).method(HttpMethod.POST) | ||
| .header(HttpHeader.ACCEPT, "application/json") | ||
| .content(new StringContentProvider(gson.toJson(new Password(token)))); | ||
| var response = send(request); | ||
| var content = response.getContentAsString(); | ||
| SessionAnswer answer = gson.fromJson(content, SessionAnswer.class); | ||
| logger.debug(answer.session().message()); | ||
| session = answer.session(); | ||
| } | ||
|
|
||
| @Override | ||
| public Optional<DnsStatistics> summary() throws PiHoleException { | ||
| logger.debug("Getting summary"); | ||
| getAuth(); | ||
| var url = apiUrl.resolve(apiUrl.getPath() + "/stats/summary"); | ||
| var request = client.newRequest(url).timeout(TIMEOUT_SECONDS, SECONDS).method(HttpMethod.GET) | ||
| .header(HttpHeader.ACCEPT, "application/json").header("sid", session.sid()); | ||
| var response = send(request); | ||
| var content = response.getContentAsString(); | ||
| StatAnswer answer = gson.fromJson(content, StatAnswer.class); | ||
| DnsStatistics translated = new DnsStatistics(answer.gravity().domainsBeingBlocked(), null, null, null, | ||
| answer.queries().uniqueDomains(), answer.queries().forwarded(), answer.queries().cached(), null, null, | ||
| null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, | ||
| null, null); | ||
| return Optional.of(translated); | ||
| } | ||
|
|
||
| @Override | ||
| public void disableBlocking(long seconds) throws PiHoleException { | ||
| logger.debug("Disabling blocking for {} seconds", seconds); | ||
| // var url = baseUrl.resolve("/admin/api.php?disable=%s&auth=%s".formatted(seconds, token)); | ||
| // var request = client.newRequest(url).timeout(TIMEOUT_SECONDS, SECONDS); | ||
| // send(request); | ||
| } | ||
|
|
||
| @Override | ||
| public void enableBlocking() throws PiHoleException { | ||
| logger.debug("Enabling blocking"); | ||
| // var url = baseUrl.resolve("/admin/api.php?enable&auth=%s".formatted(token)); | ||
| // var request = client.newRequest(url).timeout(TIMEOUT_SECONDS, SECONDS); | ||
| // send(request); | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.