2 * Copyright (c) 2010-2024 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.pushbullet.internal.handler;
15 import static org.openhab.binding.pushbullet.internal.PushbulletBindingConstants.*;
17 import java.io.ByteArrayInputStream;
18 import java.io.IOException;
19 import java.io.InputStream;
20 import java.nio.charset.StandardCharsets;
21 import java.util.Collection;
22 import java.util.Properties;
24 import java.util.regex.Matcher;
25 import java.util.regex.Pattern;
27 import javax.mail.internet.AddressException;
28 import javax.mail.internet.InternetAddress;
30 import org.eclipse.jdt.annotation.NonNullByDefault;
31 import org.eclipse.jdt.annotation.Nullable;
32 import org.eclipse.jetty.http.HttpHeader;
33 import org.eclipse.jetty.http.HttpMethod;
34 import org.eclipse.jetty.http.MimeTypes;
35 import org.openhab.binding.pushbullet.internal.PushbulletConfiguration;
36 import org.openhab.binding.pushbullet.internal.action.PushbulletActions;
37 import org.openhab.binding.pushbullet.internal.model.Push;
38 import org.openhab.binding.pushbullet.internal.model.PushResponse;
39 import org.openhab.core.io.net.http.HttpUtil;
40 import org.openhab.core.thing.ChannelUID;
41 import org.openhab.core.thing.Thing;
42 import org.openhab.core.thing.ThingStatus;
43 import org.openhab.core.thing.binding.BaseThingHandler;
44 import org.openhab.core.thing.binding.ThingHandlerService;
45 import org.openhab.core.types.Command;
46 import org.osgi.framework.FrameworkUtil;
47 import org.osgi.framework.Version;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
51 import com.google.gson.Gson;
52 import com.google.gson.GsonBuilder;
55 * The {@link PushbulletHandler} is responsible for handling commands, which are
56 * sent to one of the channels.
58 * @author Hakan Tandogan - Initial contribution
61 public class PushbulletHandler extends BaseThingHandler {
63 private final Logger logger = LoggerFactory.getLogger(PushbulletHandler.class);
65 private final Gson gson = new GsonBuilder().create();
67 private static final Version VERSION = FrameworkUtil.getBundle(PushbulletHandler.class).getVersion();
69 private static final Pattern CHANNEL_PATTERN = Pattern.compile("^[a-zA-Z0-9_-]+$");
71 private @Nullable PushbulletConfiguration config;
73 public PushbulletHandler(Thing thing) {
78 public void handleCommand(ChannelUID channelUID, Command command) {
79 logger.debug("About to handle {} on {}", command, channelUID);
81 // Future improvement: If recipient is already set, send a push on a command channel change
82 // check reconnect channel of the unifi binding for that
84 logger.debug("The Pushbullet binding is a read-only binding and cannot handle command '{}'.", command);
88 public void initialize() {
89 logger.debug("Start initializing!");
90 config = getConfigAs(PushbulletConfiguration.class);
92 // Name and Token are both "required", so set the Thing immediately ONLINE.
93 updateStatus(ThingStatus.ONLINE);
95 logger.debug("Finished initializing!");
99 public Collection<Class<? extends ThingHandlerService>> getServices() {
100 return Set.of(PushbulletActions.class);
103 public boolean sendPush(@Nullable String recipient, @Nullable String message, String type) {
104 return sendPush(recipient, "", message, type);
107 public boolean sendPush(@Nullable String recipient, @Nullable String title, @Nullable String message, String type) {
108 boolean result = false;
110 logger.debug("sendPush is called for ");
111 logger.debug("Thing {}", thing);
112 logger.debug("Thing Label: '{}'", thing.getLabel());
114 PushbulletConfiguration configuration = getConfigAs(PushbulletConfiguration.class);
115 logger.debug("CFG {}", configuration);
117 Properties headers = prepareRequestHeaders(configuration);
119 String request = prepareMessageBody(recipient, title, message, type);
121 try (InputStream stream = new ByteArrayInputStream(request.getBytes(StandardCharsets.UTF_8))) {
122 String pushAPI = configuration.getApiUrlBase() + "/" + API_METHOD_PUSHES;
124 String responseString = HttpUtil.executeUrl(HttpMethod.POST.asString(), pushAPI, headers, stream,
125 MimeTypes.Type.APPLICATION_JSON.asString(), TIMEOUT);
127 logger.debug("Got Response: {}", responseString);
128 PushResponse response = gson.fromJson(responseString, PushResponse.class);
130 logger.debug("Unpacked Response: {}", response);
132 if ((null != response) && (null == response.getPushError())) {
135 } catch (IOException e) {
136 logger.warn("IO problems pushing note: {}", e.getMessage());
143 * helper method to populate the request headers
145 * @param configuration
148 private Properties prepareRequestHeaders(PushbulletConfiguration configuration) {
149 Properties headers = new Properties();
150 headers.put(HttpHeader.USER_AGENT, "openHAB / Pushbullet binding " + VERSION);
151 headers.put(HttpHeader.CONTENT_TYPE, MimeTypes.Type.APPLICATION_JSON.asString());
152 headers.put("Access-Token", configuration.getToken());
154 logger.debug("Headers: {}", headers);
160 * helper method to create a message body from data to be transferred.
167 * @return the message as a String to be posted
169 private String prepareMessageBody(@Nullable String recipient, @Nullable String title, @Nullable String message,
171 logger.debug("Recipient is '{}'", recipient);
172 logger.debug("Title is '{}'", title);
173 logger.debug("Message is '{}'", message);
175 Push push = new Push();
176 push.setTitle(title);
177 push.setBody(message);
180 if (recipient != null) {
181 if (isValidEmail(recipient)) {
182 logger.debug("Recipient is an email address");
183 push.setEmail(recipient);
184 } else if (isValidChannel(recipient)) {
185 logger.debug("Recipient is a channel tag");
186 push.setChannel(recipient);
188 logger.warn("Invalid recipient: {}", recipient);
189 logger.warn("Message will be broadcast to all user's devices.");
193 logger.debug("Push: {}", push);
195 String request = gson.toJson(push);
196 logger.debug("Packed Request: {}", request);
202 * helper method checking if channel tag is valid.
207 private static boolean isValidChannel(String channel) {
208 Matcher m = CHANNEL_PATTERN.matcher(channel);
213 * helper method checking if email address is valid.
218 private static boolean isValidEmail(String email) {
220 InternetAddress emailAddr = new InternetAddress(email);
221 emailAddr.validate();
223 } catch (AddressException e) {