2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.prowl.internal;
15 import static org.openhab.binding.prowl.internal.ProwlBindingConstants.*;
17 import java.util.Collection;
18 import java.util.List;
19 import java.util.concurrent.ExecutionException;
20 import java.util.concurrent.ScheduledFuture;
21 import java.util.concurrent.TimeUnit;
22 import java.util.concurrent.TimeoutException;
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.eclipse.jetty.client.HttpClient;
27 import org.eclipse.jetty.client.api.ContentResponse;
28 import org.eclipse.jetty.client.util.StringContentProvider;
29 import org.openhab.binding.prowl.internal.action.ProwlActions;
30 import org.openhab.core.library.types.DecimalType;
31 import org.openhab.core.thing.ChannelUID;
32 import org.openhab.core.thing.Thing;
33 import org.openhab.core.thing.ThingStatus;
34 import org.openhab.core.thing.binding.BaseThingHandler;
35 import org.openhab.core.thing.binding.ThingHandlerService;
36 import org.openhab.core.types.Command;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
41 * The {@link ProwlHandler} is responsible for handling commands, which are
42 * sent to one of the channels.
44 * @author Ondrej Pecta - Initial contribution
47 public class ProwlHandler extends BaseThingHandler {
49 private final Logger logger = LoggerFactory.getLogger(ProwlHandler.class);
51 private ProwlConfiguration config = new ProwlConfiguration();
52 private final HttpClient httpClient;
55 * Future to poll for status
57 private @Nullable ScheduledFuture<?> statusFuture;
59 public ProwlHandler(Thing thing, HttpClient client) {
61 this.httpClient = client;
65 public void handleCommand(ChannelUID channelUID, Command command) {
69 public void initialize() {
70 config = getConfigAs(ProwlConfiguration.class);
71 updateStatus(ThingStatus.UNKNOWN);
73 statusFuture = scheduler.scheduleWithFixedDelay(() -> updateStatus(), 0, config.refresh, TimeUnit.MINUTES);
76 private void updateStatus() {
77 if (keyVerificationSucceeded(config.apiKey)) {
78 updateStatus(ThingStatus.ONLINE);
80 updateStatus(ThingStatus.OFFLINE);
85 public void dispose() {
86 ScheduledFuture<?> localPollFuture = statusFuture;
87 if (localPollFuture != null && !localPollFuture.isCancelled()) {
88 localPollFuture.cancel(true);
93 private boolean keyVerificationSucceeded(String apiKey) {
95 ContentResponse response = httpClient.GET(PROWL_VERIFY_URI + "?apikey=" + apiKey);
96 String resp = response.getContentAsString();
97 logger.trace("verify response: {}", resp);
98 if (resp.contains("<success code=\"200\"")) {
99 updateFreeMessages(resp);
104 } catch (InterruptedException e) {
105 Thread.currentThread().interrupt();
106 } catch (ExecutionException e) {
107 logger.debug("error during calling uri: {}", PROWL_ADD_URI, e);
108 } catch (TimeoutException e) {
109 logger.debug("timeout during calling uri: {}", PROWL_ADD_URI, e);
115 public Collection<Class<? extends ThingHandlerService>> getServices() {
116 return List.of(ProwlActions.class);
119 public void pushNotification(@Nullable String event, @Nullable String description) {
120 pushNotification(event, description, 0);
123 public void pushNotification(@Nullable String event, @Nullable String description, int priority) {
124 if (event == null || description == null) {
125 logger.debug("Cannot push message with null event or null description");
131 } else if (priority > 2) {
135 logger.debug("Pushing an event: {} with desc: {}", event, description);
137 ContentResponse response = httpClient.POST(PROWL_ADD_URI).timeout(5, TimeUnit.SECONDS)
139 new StringContentProvider("apikey=" + config.apiKey + "&application=" + config.application
140 + "&event=" + event + "&description=" + description + "&priority=" + priority),
141 "application/x-www-form-urlencoded; charset=UTF-8")
143 String resp = response.getContentAsString();
144 updateFreeMessages(resp);
145 logger.trace("add response: {}", resp);
146 } catch (InterruptedException e) {
147 Thread.currentThread().interrupt();
148 } catch (ExecutionException e) {
149 logger.debug("error during calling uri: {}", PROWL_ADD_URI, e);
150 } catch (TimeoutException e) {
151 logger.debug("timeout during calling uri: {}", PROWL_ADD_URI, e);
155 private void updateFreeMessages(String resp) {
156 final String str = "remaining=\"";
158 // trying to simply parse the simple xml rather than using XPATH
159 int start = resp.indexOf(str) + str.length();
160 int end = resp.indexOf("\"", start + 1);
163 String messages = resp.substring(start, end);
164 logger.debug("remaining messages parsed: {}", messages);
165 int freeMessages = Integer.parseInt(messages);
166 updateState(CHANNEL_REMAINING, new DecimalType(freeMessages));
167 } catch (StringIndexOutOfBoundsException | NumberFormatException ex) {
168 logger.debug("Error parsing remaining messages", ex);