]> git.basschouten.com Git - openhab-addons.git/blob
85996ced7fdc4450e15152ff44d0d800ce988ff7
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.Collections;
23 import java.util.Properties;
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 Collections.singleton(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             stream.close();
133
134             if ((null != response) && (null == response.getPushError())) {
135                 result = true;
136             }
137         } catch (IOException e) {
138             logger.warn("IO problems pushing note: {}", e.getMessage());
139         }
140
141         return result;
142     }
143
144     /**
145      * helper method to populate the request headers
146      *
147      * @param configuration
148      * @return
149      */
150     private Properties prepareRequestHeaders(PushbulletConfiguration configuration) {
151         Properties headers = new Properties();
152         headers.put(HttpHeader.USER_AGENT, "openHAB / Pushbullet binding " + VERSION);
153         headers.put(HttpHeader.CONTENT_TYPE, MimeTypes.Type.APPLICATION_JSON.asString());
154         headers.put("Access-Token", configuration.getToken());
155
156         logger.debug("Headers: {}", headers);
157
158         return headers;
159     }
160
161     /**
162      * helper method to create a message body from data to be transferred.
163      *
164      * @param recipient
165      * @param title
166      * @param message
167      * @param type
168      *
169      * @return the message as a String to be posted
170      */
171     private String prepareMessageBody(@Nullable String recipient, @Nullable String title, @Nullable String message,
172             String type) {
173         logger.debug("Recipient is '{}'", recipient);
174         logger.debug("Title is     '{}'", title);
175         logger.debug("Message is   '{}'", message);
176
177         Push push = new Push();
178         push.setTitle(title);
179         push.setBody(message);
180         push.setType(type);
181
182         if (recipient != null) {
183             if (isValidEmail(recipient)) {
184                 logger.debug("Recipient is an email address");
185                 push.setEmail(recipient);
186             } else if (isValidChannel(recipient)) {
187                 logger.debug("Recipient is a channel tag");
188                 push.setChannel(recipient);
189             } else {
190                 logger.warn("Invalid recipient: {}", recipient);
191                 logger.warn("Message will be broadcast to all user's devices.");
192             }
193         }
194
195         logger.debug("Push: {}", push);
196
197         String request = gson.toJson(push);
198         logger.debug("Packed Request: {}", request);
199
200         return request;
201     }
202
203     /**
204      * helper method checking if channel tag is valid.
205      *
206      * @param channel
207      * @return
208      */
209     private static boolean isValidChannel(String channel) {
210         Matcher m = CHANNEL_PATTERN.matcher(channel);
211         return m.matches();
212     }
213
214     /**
215      * helper method checking if email address is valid.
216      *
217      * @param email
218      * @return
219      */
220     private static boolean isValidEmail(String email) {
221         try {
222             InternetAddress emailAddr = new InternetAddress(email);
223             emailAddr.validate();
224             return true;
225         } catch (AddressException e) {
226             return false;
227         }
228     }
229 }