]> git.basschouten.com Git - openhab-addons.git/blob
f1ceaaefd2bd290142b6c86f8e177710d4f94332
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.pushbullet.internal.handler;
14
15 import static org.openhab.binding.pushbullet.internal.PushbulletBindingConstants.*;
16
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;
23 import java.util.Set;
24 import java.util.regex.Matcher;
25 import java.util.regex.Pattern;
26
27 import javax.mail.internet.AddressException;
28 import javax.mail.internet.InternetAddress;
29
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;
50
51 import com.google.gson.Gson;
52 import com.google.gson.GsonBuilder;
53
54 /**
55  * The {@link PushbulletHandler} is responsible for handling commands, which are
56  * sent to one of the channels.
57  *
58  * @author Hakan Tandogan - Initial contribution
59  */
60 @NonNullByDefault
61 public class PushbulletHandler extends BaseThingHandler {
62
63     private final Logger logger = LoggerFactory.getLogger(PushbulletHandler.class);
64
65     private final Gson gson = new GsonBuilder().create();
66
67     private static final Version VERSION = FrameworkUtil.getBundle(PushbulletHandler.class).getVersion();
68
69     private static final Pattern CHANNEL_PATTERN = Pattern.compile("^[a-zA-Z0-9_-]+$");
70
71     private @Nullable PushbulletConfiguration config;
72
73     public PushbulletHandler(Thing thing) {
74         super(thing);
75     }
76
77     @Override
78     public void handleCommand(ChannelUID channelUID, Command command) {
79         logger.debug("About to handle {} on {}", command, channelUID);
80
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
83
84         logger.debug("The Pushbullet binding is a read-only binding and cannot handle command '{}'.", command);
85     }
86
87     @Override
88     public void initialize() {
89         logger.debug("Start initializing!");
90         config = getConfigAs(PushbulletConfiguration.class);
91
92         // Name and Token are both "required", so set the Thing immediately ONLINE.
93         updateStatus(ThingStatus.ONLINE);
94
95         logger.debug("Finished initializing!");
96     }
97
98     @Override
99     public Collection<Class<? extends ThingHandlerService>> getServices() {
100         return Set.of(PushbulletActions.class);
101     }
102
103     public boolean sendPush(@Nullable String recipient, @Nullable String message, String type) {
104         return sendPush(recipient, "", message, type);
105     }
106
107     public boolean sendPush(@Nullable String recipient, @Nullable String title, @Nullable String message, String type) {
108         boolean result = false;
109
110         logger.debug("sendPush is called for ");
111         logger.debug("Thing {}", thing);
112         logger.debug("Thing Label: '{}'", thing.getLabel());
113
114         PushbulletConfiguration configuration = getConfigAs(PushbulletConfiguration.class);
115         logger.debug("CFG {}", configuration);
116
117         Properties headers = prepareRequestHeaders(configuration);
118
119         String request = prepareMessageBody(recipient, title, message, type);
120
121         try (InputStream stream = new ByteArrayInputStream(request.getBytes(StandardCharsets.UTF_8))) {
122             String pushAPI = configuration.getApiUrlBase() + "/" + API_METHOD_PUSHES;
123
124             String responseString = HttpUtil.executeUrl(HttpMethod.POST.asString(), pushAPI, headers, stream,
125                     MimeTypes.Type.APPLICATION_JSON.asString(), TIMEOUT);
126
127             logger.debug("Got Response: {}", responseString);
128             PushResponse response = gson.fromJson(responseString, PushResponse.class);
129
130             logger.debug("Unpacked Response: {}", response);
131
132             if ((null != response) && (null == response.getPushError())) {
133                 result = true;
134             }
135         } catch (IOException e) {
136             logger.warn("IO problems pushing note: {}", e.getMessage());
137         }
138
139         return result;
140     }
141
142     /**
143      * helper method to populate the request headers
144      *
145      * @param configuration
146      * @return
147      */
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());
153
154         logger.debug("Headers: {}", headers);
155
156         return headers;
157     }
158
159     /**
160      * helper method to create a message body from data to be transferred.
161      *
162      * @param recipient
163      * @param title
164      * @param message
165      * @param type
166      *
167      * @return the message as a String to be posted
168      */
169     private String prepareMessageBody(@Nullable String recipient, @Nullable String title, @Nullable String message,
170             String type) {
171         logger.debug("Recipient is '{}'", recipient);
172         logger.debug("Title is     '{}'", title);
173         logger.debug("Message is   '{}'", message);
174
175         Push push = new Push();
176         push.setTitle(title);
177         push.setBody(message);
178         push.setType(type);
179
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);
187             } else {
188                 logger.warn("Invalid recipient: {}", recipient);
189                 logger.warn("Message will be broadcast to all user's devices.");
190             }
191         }
192
193         logger.debug("Push: {}", push);
194
195         String request = gson.toJson(push);
196         logger.debug("Packed Request: {}", request);
197
198         return request;
199     }
200
201     /**
202      * helper method checking if channel tag is valid.
203      *
204      * @param channel
205      * @return
206      */
207     private static boolean isValidChannel(String channel) {
208         Matcher m = CHANNEL_PATTERN.matcher(channel);
209         return m.matches();
210     }
211
212     /**
213      * helper method checking if email address is valid.
214      *
215      * @param email
216      * @return
217      */
218     private static boolean isValidEmail(String email) {
219         try {
220             InternetAddress emailAddr = new InternetAddress(email);
221             emailAddr.validate();
222             return true;
223         } catch (AddressException e) {
224             return false;
225         }
226     }
227 }