From deeef36eb24fddb653789ec5e1b88a9066103820 Mon Sep 17 00:00:00 2001 From: Hakan Tandogan Date: Sat, 29 Jun 2019 15:25:16 +0200 Subject: [PATCH 1/5] [pushbullet] reimplementation for openHAB2 Signed-off-by: Hakan Tandogan --- CODEOWNERS | 1 + bom/openhab-addons/pom.xml | 5 + .../org.openhab.binding.pushbullet/.classpath | 32 +++ .../org.openhab.binding.pushbullet/.project | 23 ++ bundles/org.openhab.binding.pushbullet/NOTICE | 13 ++ .../org.openhab.binding.pushbullet/README.md | 162 +++++++++++++ .../org.openhab.binding.pushbullet/pom.xml | 25 ++ .../src/main/feature/feature.xml | 9 + .../internal/PushbulletBindingConstants.java | 46 ++++ .../internal/PushbulletConfiguration.java | 55 +++++ .../internal/PushbulletHandlerFactory.java | 52 +++++ .../internal/action/PushbulletActions.java | 100 ++++++++ .../internal/handler/PushbulletHandler.java | 218 ++++++++++++++++++ .../pushbullet/internal/model/Push.java | 85 +++++++ .../pushbullet/internal/model/PushError.java | 74 ++++++ .../internal/model/PushResponse.java | 211 +++++++++++++++++ .../resources/ESH-INF/binding/binding.xml | 9 + .../ESH-INF/i18n/pushbullet_en.properties | 17 ++ .../resources/ESH-INF/thing/thing-types.xml | 58 +++++ bundles/pom.xml | 1 + 20 files changed, 1196 insertions(+) create mode 100644 bundles/org.openhab.binding.pushbullet/.classpath create mode 100644 bundles/org.openhab.binding.pushbullet/.project create mode 100644 bundles/org.openhab.binding.pushbullet/NOTICE create mode 100644 bundles/org.openhab.binding.pushbullet/README.md create mode 100644 bundles/org.openhab.binding.pushbullet/pom.xml create mode 100644 bundles/org.openhab.binding.pushbullet/src/main/feature/feature.xml create mode 100644 bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/PushbulletBindingConstants.java create mode 100644 bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/PushbulletConfiguration.java create mode 100644 bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/PushbulletHandlerFactory.java create mode 100644 bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/action/PushbulletActions.java create mode 100644 bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/handler/PushbulletHandler.java create mode 100644 bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/Push.java create mode 100644 bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/PushError.java create mode 100644 bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/PushResponse.java create mode 100644 bundles/org.openhab.binding.pushbullet/src/main/resources/ESH-INF/binding/binding.xml create mode 100644 bundles/org.openhab.binding.pushbullet/src/main/resources/ESH-INF/i18n/pushbullet_en.properties create mode 100644 bundles/org.openhab.binding.pushbullet/src/main/resources/ESH-INF/thing/thing-types.xml diff --git a/CODEOWNERS b/CODEOWNERS index 2ca976d6f5e9f..63afc660f4523 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -122,6 +122,7 @@ /bundles/org.openhab.binding.plugwise/ @wborn /bundles/org.openhab.binding.powermax/ @lolodomo /bundles/org.openhab.binding.pulseaudio/ @peuter +/bundles/org.openhab.binding.pushbullet/ @hakan42 /bundles/org.openhab.binding.regoheatpump/ @crnjan /bundles/org.openhab.binding.rfxcom/ @martinvw @paulianttila /bundles/org.openhab.binding.rme/ @kgoderis diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml index bf733d6844d3b..6f00784adc50a 100644 --- a/bom/openhab-addons/pom.xml +++ b/bom/openhab-addons/pom.xml @@ -600,6 +600,11 @@ org.openhab.binding.pulseaudio ${project.version} + + org.openhab.addons.bundles + org.openhab.binding.pushbullet + ${project.version} + org.openhab.addons.bundles org.openhab.binding.regoheatpump diff --git a/bundles/org.openhab.binding.pushbullet/.classpath b/bundles/org.openhab.binding.pushbullet/.classpath new file mode 100644 index 0000000000000..a5d95095ccaaf --- /dev/null +++ b/bundles/org.openhab.binding.pushbullet/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.pushbullet/.project b/bundles/org.openhab.binding.pushbullet/.project new file mode 100644 index 0000000000000..89f11546d5550 --- /dev/null +++ b/bundles/org.openhab.binding.pushbullet/.project @@ -0,0 +1,23 @@ + + + org.openhab.binding.pushbullet + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/bundles/org.openhab.binding.pushbullet/NOTICE b/bundles/org.openhab.binding.pushbullet/NOTICE new file mode 100644 index 0000000000000..4c20ef446c1e4 --- /dev/null +++ b/bundles/org.openhab.binding.pushbullet/NOTICE @@ -0,0 +1,13 @@ +This content is produced and maintained by the openHAB project. + +* Project home: https://www.openhab.org + +== Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License 2.0 which is available at +https://www.eclipse.org/legal/epl-2.0/. + +== Source Code + +https://github.com/openhab/openhab2-addons diff --git a/bundles/org.openhab.binding.pushbullet/README.md b/bundles/org.openhab.binding.pushbullet/README.md new file mode 100644 index 0000000000000..b2d3871dc6df1 --- /dev/null +++ b/bundles/org.openhab.binding.pushbullet/README.md @@ -0,0 +1,162 @@ +# Pushbullet Binding + +The Pushbullet binding allows you to notify iOS, Android & Windows 10 Phone & Desktop devices of a message using the Pushbullet API web service. + +## Supported Things + +This binding supports a generic "bot" which is a representation of the client. + +## Discovery + +This binding provides no discovery. +The desired bots must be configured manually or via a things file. + +## Binding Configuration + +The binding has no configuration options itself, all configuration is done at 'Things' level. + +## Thing Configuration + +### Bot (`bot`) + +The bot thing is used to send messages to other recipients. +It has the following parameters: + +| Config | Description | Required | Advanced | +|------------|------------------------------------------------------------------|----------|----------| +| token | Pushbullet [API token](#obtaining-an-api-key) to send to devices | Yes | False | +| name | Explicit Name, for later use when the bot can receive messages | No | True | +| apiUrlBase | Address of own Pushbullet server, for testing purposes | No | True | + +```java +Thing pushbullet:bot:r2d2 "R2D2" @ "Somewhere" [ token = "verysecretwonttellyou" ] + +``` + +## Channels + +| Channel ID | Channel Description | Supported item type | Advanced | +|------------|-------------------------------------------------|----------------------|----------| +| recipient | for later use when the bot can receive messages | String | False | +| title | for later use when the bot can receive messages | String | False | +| message | for later use when the bot can receive messages | String | False | + +## Rule Action + +This binding includes rule actions for sending notes. +Two different actions available: + +* `sendPushbulletNote(String recipient, String messsage)` +* `sendPushbulletNote(String recipient, String title, String messsage)` + +Since there is a separate rule action instance for each `bot` thing, this needs to be retrieved through `getActions(scope, thingUID)`. +The first parameter always has to be `pushbullet` and the second is the full Thing UID of the bot that should be used. +Once this action instance is retrieved, you can invoke the action method on it. + +Examples: + +``` +val actions = getActions("pushbullet", "pushbullet:bot:r2d2") +val result = actions.sendPushbulletNote("someone@example.com", "R2D2 talks here...", "This is the pushed note.") +``` + +## Full Example + +_Provide a full usage example based on textual configuration files (*.things, *.items, *.sitemap)._ + +pushbullet.things: + +```java +Thing pushbullet:bot:r2d2 "R2D2" @ "Somewhere" [ token = "verysecretwonttellyou" ] + +``` + +pushbullet.items + +```java +Switch Pushbullet_R2D2_Button "Pushbullet Action bot R2D2" +``` + +pushbullet.sitemap + +```java +sitemap pushbullet label="Pushbullet" +{ + Switch item=Pushbullet_R2D2_Button +} +``` + +pushbullet.rules + +```java +rule "Pushbullet R2D2 changed" +when + Item Pushbullet_R2D2_Button changed +then + logInfo(filename, "Button R2D2 changed - OH2...") + + if (Pushbullet_R2D2_Button.state == ON) + { + sendCommand(Pushbullet_R2D2_Button, OFF) + + val actions = getActions("pushbullet", "pushbullet:bot:r2d2") + logInfo(filename, "Actions for 'R2D2' are: " + actions) + + if (actions != null) + { + val result = actions.sendPushbulletNote("someone@example.com", "Title R2D2 OH2", "This has been sent by the new R2D2 bot") + logInfo(filename, "Result of send action is: " + result) + } + } +end +``` + + + + + +## Creating an account for your bot(s) + +The pushbullet accounts are bound to either Google or Facebook accounts. + +- Create a bot account with either Facebook or Google +- Go to "" +- Chose to either "Sign up with Google" or "Sign up with Facebook". +- Complete the signup process as guided by the pushbullet web site. +- Continue with "Obtaining an API key". + +## Obtaining an API key + +The API keys are bound to the pushbullet account. + +- Go to the pushbullet site. +- Log in with either your personal account or the one you created for your bot. +- Go to "" +- Click on "Create Access Token". +- Copy the token created on the site. + +You must at least provide an API token (Private or Alias Key from Pushbullet.com) and a message in some manner before a message can be pushed. +All other parameters are optional. +If you use an alias key, the parameters (device, icon, sound, vibration) are overwritten by the alias setting on pushbullet. + +## Rate limits + +As of 2019, free accounts have a limit of 100 pushes per month. +This action does not evaluate the rate limiting headers though. + +## Translation + +This project is being translated on transifex. +If you want to help, please join the project at the URL: + +- https://www.transifex.com/hakan42/openhab-binding-pushbullet/dashboard/ + +## Libraries + +This action has been written without using libraries as jpushbullet or jpushbullet2. +Both of those libraries use various libraries themselves which makes integrating them into openHAB a challenge. + +## pushbullet API + +- +- diff --git a/bundles/org.openhab.binding.pushbullet/pom.xml b/bundles/org.openhab.binding.pushbullet/pom.xml new file mode 100644 index 0000000000000..f7d2623e8c3f2 --- /dev/null +++ b/bundles/org.openhab.binding.pushbullet/pom.xml @@ -0,0 +1,25 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.0-SNAPSHOT + + + org.openhab.binding.pushbullet + + openHAB Add-ons :: Bundles :: Pushbullet Binding + + + + javax.mail + mail + 1.4.7 + provided + + + + diff --git a/bundles/org.openhab.binding.pushbullet/src/main/feature/feature.xml b/bundles/org.openhab.binding.pushbullet/src/main/feature/feature.xml new file mode 100644 index 0000000000000..f0dfc37f14ad8 --- /dev/null +++ b/bundles/org.openhab.binding.pushbullet/src/main/feature/feature.xml @@ -0,0 +1,9 @@ + + + mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${project.version}/xml/features + + + openhab-runtime-base + mvn:org.openhab.addons.bundles/org.openhab.binding.pushbullet/${project.version} + + diff --git a/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/PushbulletBindingConstants.java b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/PushbulletBindingConstants.java new file mode 100644 index 0000000000000..d60d350de3423 --- /dev/null +++ b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/PushbulletBindingConstants.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2010-2019 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.pushbullet.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.thing.ThingTypeUID; + +import java.util.Collections; +import java.util.Set; + +/** + * The {@link PushbulletBindingConstants} class defines common constants, which are + * used across the whole binding. + * + * @author Hakan Tandogan - Initial contribution + */ +@NonNullByDefault +public class PushbulletBindingConstants { + + private static final String BINDING_ID = "pushbullet"; + + // List of all Thing Type UIDs + public static final ThingTypeUID THING_TYPE_BOT = new ThingTypeUID(BINDING_ID, "bot"); + + public static final Set SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_BOT); + + // List of all Channel ids + public static final String RECIPIENT = "recipient"; + public static final String TITLE = "title"; + public static final String MESSAGE = "message"; + + // Binding logic constants + public static final String API_METHOD_PUSHES = "pushes"; + + public static final int TIMEOUT = 30 * 1000; // 30 seconds +} diff --git a/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/PushbulletConfiguration.java b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/PushbulletConfiguration.java new file mode 100644 index 0000000000000..784e4553b99fa --- /dev/null +++ b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/PushbulletConfiguration.java @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2010-2019 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.pushbullet.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * The {@link PushbulletConfiguration} class contains fields mapping thing configuration parameters. + * + * @author Hakan Tandogan - Initial contribution + */ +@NonNullByDefault +public class PushbulletConfiguration { + + private @Nullable String name; + + private String token = "invalid"; + + private String apiUrlBase = "invalid"; + + public @Nullable String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + + public String getApiUrlBase() { + return apiUrlBase; + } + + public void setApiUrlBase(String apiUrlBase) { + this.apiUrlBase = apiUrlBase; + } +} diff --git a/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/PushbulletHandlerFactory.java b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/PushbulletHandlerFactory.java new file mode 100644 index 0000000000000..4b73f6c093613 --- /dev/null +++ b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/PushbulletHandlerFactory.java @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2010-2019 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.pushbullet.internal; + +import static org.openhab.binding.pushbullet.internal.PushbulletBindingConstants.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory; +import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory; +import org.openhab.binding.pushbullet.internal.handler.PushbulletHandler; +import org.osgi.service.component.annotations.Component; + +/** + * The {@link PushbulletHandlerFactory} is responsible for creating things and thing + * handlers. + * + * @author Hakan Tandogan - Initial contribution + */ +@NonNullByDefault +@Component(configurationPid = "binding.pushbullet", service = ThingHandlerFactory.class) +public class PushbulletHandlerFactory extends BaseThingHandlerFactory { + + @Override + public boolean supportsThingType(ThingTypeUID thingTypeUID) { + return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); + } + + @Override + protected @Nullable ThingHandler createHandler(Thing thing) { + ThingTypeUID thingTypeUID = thing.getThingTypeUID(); + + if (THING_TYPE_BOT.equals(thingTypeUID)) { + return new PushbulletHandler(thing); + } + + return null; + } +} diff --git a/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/action/PushbulletActions.java b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/action/PushbulletActions.java new file mode 100644 index 0000000000000..c8716bae1cdf1 --- /dev/null +++ b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/action/PushbulletActions.java @@ -0,0 +1,100 @@ +/** + * Copyright (c) 2010-2019 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.pushbullet.internal.action; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.binding.ThingActions; +import org.eclipse.smarthome.core.thing.binding.ThingActionsScope; +import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.openhab.binding.pushbullet.internal.handler.PushbulletHandler; +import org.openhab.core.automation.annotation.ActionInput; +import org.openhab.core.automation.annotation.ActionOutput; +import org.openhab.core.automation.annotation.RuleAction; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link PushbulletActions} class defines rule actions for sending notifications + * + * @author Hakan Tandogan - Initial contribution + */ +@ThingActionsScope(name = "pushbullet") +@NonNullByDefault +public class PushbulletActions implements ThingActions { + + private final Logger logger = LoggerFactory.getLogger(PushbulletActions.class); + + private @Nullable PushbulletHandler handler; + + @Override public void setThingHandler(@Nullable ThingHandler handler) { + this.handler = (PushbulletHandler) handler; + } + + @Override public @Nullable ThingHandler getThingHandler() { + return this.handler; + } + + @RuleAction(label = "@text/actionSendPushbulletNoteLabel", description = "@text/actionSendPushbulletNoteDesc") + public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean sendPushbulletNote( + @ActionInput(name = "recipient", label = "@text/actionSendPushbulletNoteInputRecipientLabel", description = "@text/actionSendPushbulletNoteInputRecipientDesc") @Nullable String recipient, + @ActionInput(name = "title", label = "@text/actionSendPushbulletNoteInputTitleLabel", description = "@text/actionSendPushbulletNoteInputTitleDesc") @Nullable String title, + @ActionInput(name = "message", label = "@text/actionSendPushbulletNoteInputMessageLabel", description = "@text/actionSendPushbulletNoteInputMessageDesc") @Nullable String message) { + logger.trace("sendPushbulletNote '{}', '{}', '{}'", recipient, title, message); + + // Use local variable so the SAT check can do proper flow analysis + PushbulletHandler localHandler = handler; + + if (localHandler == null) { + logger.warn("Pushbullet service Handler is null!"); + return false; + } + + return localHandler.sendPush(recipient, title, message, "note"); + } + + public static boolean sendPushbulletNote(@Nullable ThingActions actions, @Nullable String recipient, + @Nullable String title, @Nullable String message) { + if (actions instanceof PushbulletActions) { + return ((PushbulletActions) actions).sendPushbulletNote(recipient, title, message); + } else { + throw new IllegalArgumentException("Instance is not a PushbulletActions class ( " + actions + " )"); + } + } + + @RuleAction(label = "@text/actionSendPushbulletNoteLabel", description = "@text/actionSendPushbulletNoteDesc") + public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean sendPushbulletNote( + @ActionInput(name = "recipient", label = "@text/actionSendPushbulletNoteInputRecipientLabel", description = "@text/actionSendPushbulletNoteInputRecipientDesc") @Nullable String recipient, + @ActionInput(name = "message", label = "@text/actionSendPushbulletNoteInputMessageLabel", description = "@text/actionSendPushbulletNoteInputMessageDesc") @Nullable String message) { + logger.trace("sendPushbulletNote '{}', '{}'", recipient, message); + + // Use local variable so the SAT check can do proper flow analysis + PushbulletHandler localHandler = handler; + + if (localHandler == null) { + logger.warn("Pushbullet service Handler is null!"); + return false; + } + + return localHandler.sendPush(recipient, message, "note"); + } + + public static boolean sendPushbulletNote(@Nullable ThingActions actions, @Nullable String recipient, + @Nullable String message) { + if (actions instanceof PushbulletActions) { + return ((PushbulletActions) actions).sendPushbulletNote(recipient, message); + } else { + throw new IllegalArgumentException("Instance is not a PushbulletActions class ( " + actions + " )"); + } + } +} diff --git a/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/handler/PushbulletHandler.java b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/handler/PushbulletHandler.java new file mode 100644 index 0000000000000..c3bb000fe3ef4 --- /dev/null +++ b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/handler/PushbulletHandler.java @@ -0,0 +1,218 @@ +/** + * Copyright (c) 2010-2019 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.pushbullet.internal.handler; + +import static org.openhab.binding.pushbullet.internal.PushbulletBindingConstants.*; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpResponseException; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.MimeTypes; +import org.eclipse.smarthome.core.thing.ThingStatusDetail; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerService; +import org.eclipse.smarthome.io.net.http.HttpUtil; +import org.openhab.binding.pushbullet.internal.action.PushbulletActions; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandler; +import org.eclipse.smarthome.core.types.Command; +import org.openhab.binding.pushbullet.internal.PushbulletConfiguration; +import org.openhab.binding.pushbullet.internal.model.Push; +import org.openhab.binding.pushbullet.internal.model.PushResponse; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.Version; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.Collections; +import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.mail.internet.AddressException; +import javax.mail.internet.InternetAddress; + +/** + * The {@link PushbulletHandler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Hakan Tandogan - Initial contribution + */ +@NonNullByDefault +public class PushbulletHandler extends BaseThingHandler { + + private final Logger logger = LoggerFactory.getLogger(PushbulletHandler.class); + + private final Gson gson = new GsonBuilder().create(); + + private static final Version VERSION = FrameworkUtil.getBundle(PushbulletHandler.class).getVersion(); + + private static final Pattern CHANNEL_PATTERN = Pattern.compile("^[a-zA-Z0-9_-]+$"); + + private @Nullable PushbulletConfiguration config; + + public PushbulletHandler(Thing thing) { + super(thing); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + logger.debug("About to handle {} on {}", command, channelUID); + + // Future improvement: If recipient is already set, send a push on a command channel change + // check reconnect channel of the unifi binding for that + + logger.debug("The Pushbullet binding is a read-only binding and cannot handle command '{}'.", command); + } + + @Override + public void initialize() { + logger.debug("Start initializing!"); + config = getConfigAs(PushbulletConfiguration.class); + + // set the thing status to UNKNOWN temporarily and let the background task decide for the real status. + // the framework is then able to reuse the resources from the thing handler initialization. + // we set this upfront to reliably check status updates in unit tests. + updateStatus(ThingStatus.UNKNOWN); + + // Name and Token are both "required", so set the Thing immediately ONLINE. + updateStatus(ThingStatus.ONLINE); + + logger.debug("Finished initializing!"); + } + + @Override + public Collection> getServices() { + return Collections.singleton(PushbulletActions.class); + } + + public boolean sendPush(@Nullable String recipient, @Nullable String message, String type) { + return sendPush(recipient, "", message, type); + } + + public boolean sendPush(@Nullable String recipient, @Nullable String title, @Nullable String message, String type) { + boolean result = false; + + logger.debug("sendPush is called for "); + logger.debug("Thing {}", thing); + logger.debug("Thing Label: '{}'", thing.getLabel()); + + PushbulletConfiguration configuration = getConfigAs(PushbulletConfiguration.class); + logger.debug("CFG {}", configuration); + logger.debug("CFG Name '{}'", configuration.getName()); + + try { + logger.debug("Recipient is '{}'", recipient); + logger.debug("Title is '{}'", title); + logger.debug("Message is '{}'", message); + + Push push = new Push(); + push.setTitle(title); + push.setBody(message); + push.setType(type); + + if (recipient != null) { + if (isValidEmail(recipient)) { + logger.debug("Recipient is an email address"); + push.setEmail(recipient); + } else if (isValidChannel(recipient)) { + logger.debug("Recipient is a channel tag"); + push.setChannel(recipient); + } else { + logger.warn("Invalid recipient: {}", recipient); + logger.warn("Message will be broadcast to all user's devices."); + } + } + + logger.debug("Push: {}", push); + + String request = gson.toJson(push); + logger.debug("Packed Request: {}", request); + + Properties headers = new Properties(); + headers.put(HttpHeader.USER_AGENT, "openHAB / Pushbullet binding " + VERSION.toString()); + headers.put(HttpHeader.CONTENT_TYPE, MimeTypes.Type.APPLICATION_JSON.asString()); + headers.put("Access-Token", configuration.getToken()); + + logger.debug("Headers: {}", headers); + + try (InputStream stream = new ByteArrayInputStream(request.getBytes(StandardCharsets.UTF_8))) { + + String pushAPI = configuration.getApiUrlBase() + "/" + API_METHOD_PUSHES; + + String responseString = HttpUtil.executeUrl(HttpMethod.POST.asString(), pushAPI, headers, stream, + MimeTypes.Type.APPLICATION_JSON.asString(), TIMEOUT); + + logger.debug("Got Response: {}", responseString); + PushResponse response = gson.fromJson(responseString, PushResponse.class); + + logger.debug("Unpacked Response: {}", response); + + stream.close(); + + if ((null != response) && (null == response.getPushError())) { + result = true; + } + } + catch (IOException e) { + logger.warn("IO problems pushing note: {}", e.getMessage()); + } + + } catch (java.lang.Error e) { + if (e.getCause() instanceof HttpResponseException) { + logger.debug("Possibly expired token, check on web site", e); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "@text/offline.conf-error-httpresponseexception"); + } + + logger.debug("Error in getResponseString: ", e); + } + + return result; + } + + /** + * helper method checking if channel tag is valid. + * + * @param channel + * @return + */ + private static boolean isValidChannel(String channel) { + Matcher m = CHANNEL_PATTERN.matcher(channel); + return m.matches(); + } + + /** + * helper method checking if email address is valid. + * + * @param email + * @return + */ + private static boolean isValidEmail(String email) { + try { + InternetAddress emailAddr = new InternetAddress(email); + emailAddr.validate(); + return true; + } catch (AddressException e) { + return false; + } + } +} diff --git a/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/Push.java b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/Push.java new file mode 100644 index 0000000000000..e32ca0c711359 --- /dev/null +++ b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/Push.java @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2010-2019 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.pushbullet.internal.model; + +import com.google.gson.annotations.SerializedName; + +/** + * This class represents the push request sent to the API. + * + * @author Hakan Tandogan - Initial contribution + * @author Hakan Tandogan - Migrated from openHAB 1 action with the same name + */ +public class Push { + + @SerializedName("title") + private String title; + + @SerializedName("body") + private String body; + + @SerializedName("type") + private String type; + + @SerializedName("email") + private String email; + + @SerializedName("channel_tag") + private String channelTag; + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getBody() { + return body; + } + + public void setBody(String body) { + this.body = body; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getChannel() { + return channelTag; + } + + public void setChannel(String channelTag) { + this.channelTag = channelTag; + } + + @Override + public String toString() { + return "Push {" + "title='" + title + '\'' + ", body='" + body + '\'' + ", type='" + type + '\'' + ", email='" + + email + '\'' + ", channelTag='" + channelTag + '\'' + '}'; + } +} diff --git a/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/PushError.java b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/PushError.java new file mode 100644 index 0000000000000..85f963d62daa7 --- /dev/null +++ b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/PushError.java @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2010-2019 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.pushbullet.internal.model; + +import com.google.gson.annotations.SerializedName; + +/** + * This class represents errors in the response fetched from the API. + * + * @author Hakan Tandogan - Initial contribution + * @author Hakan Tandogan - Migrated from openHAB 1 action with the same name + */ +public class PushError { + + @SerializedName("type") + private String type; + + @SerializedName("message") + private String message; + + @SerializedName("param") + private String param; + + @SerializedName("cat") + private String cat; + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getParam() { + return param; + } + + public void setParam(String param) { + this.param = param; + } + + public String getCat() { + return cat; + } + + public void setCat(String cat) { + this.cat = cat; + } + + @Override + public String toString() { + return "PushError {" + "type='" + type + '\'' + ", message='" + message + '\'' + ", param='" + param + '\'' + + ", cat='" + cat + '\'' + '}'; + } +} diff --git a/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/PushResponse.java b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/PushResponse.java new file mode 100644 index 0000000000000..7183d3b66f26f --- /dev/null +++ b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/PushResponse.java @@ -0,0 +1,211 @@ +/** + * Copyright (c) 2010-2019 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.pushbullet.internal.model; + +import com.google.gson.annotations.SerializedName; + +/** + * This class represents the answer to pushes provided by the API. + * + * @author Hakan Tandogan - Initial contribution + * @author Hakan Tandogan - Migrated from openHAB 1 action with the same name + */ +public class PushResponse { + + @SerializedName("active") + private String active; + + @SerializedName("iden") + private String iden; + + @SerializedName("type") + private String type; + + @SerializedName("dismissed") + private Boolean dismissed; + + @SerializedName("direction") + private String direction; + + @SerializedName("sender_iden") + private String senderIdentifier; + + @SerializedName("sender_email") + private String senderEmail; + + @SerializedName("sender_email_normalized") + private String senderEmailNormalized; + + @SerializedName("sender_name") + private String senderName; + + @SerializedName("receiver_iden") + private String receiverIdentifier; + + @SerializedName("receiver_email") + private String receiverEmail; + + @SerializedName("receiver_email_normalized") + private String receiverEmailNormalized; + + @SerializedName("title") + private String title; + + @SerializedName("body") + private String body; + + @SerializedName("error_code") + private String errorCode; + + @SerializedName("error") + private PushError pushError; + + public String getActive() { + return active; + } + + public void setActive(String active) { + this.active = active; + } + + public String getIden() { + return iden; + } + + public void setIden(String iden) { + this.iden = iden; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Boolean getDismissed() { + return dismissed; + } + + public void setDismissed(Boolean dismissed) { + this.dismissed = dismissed; + } + + public String getDirection() { + return direction; + } + + public void setDirection(String direction) { + this.direction = direction; + } + + public String getSenderIdentifier() { + return senderIdentifier; + } + + public void setSenderIdentifier(String senderIdentifier) { + this.senderIdentifier = senderIdentifier; + } + + public String getSenderEmail() { + return senderEmail; + } + + public void setSenderEmail(String senderEmail) { + this.senderEmail = senderEmail; + } + + public String getSenderEmailNormalized() { + return senderEmailNormalized; + } + + public void setSenderEmailNormalized(String senderEmailNormalized) { + this.senderEmailNormalized = senderEmailNormalized; + } + + public String getSenderName() { + return senderName; + } + + public void setSenderName(String senderName) { + this.senderName = senderName; + } + + public String getReceiverIdentifier() { + return receiverIdentifier; + } + + public void setReceiverIdentifier(String receiverIdentifier) { + this.receiverIdentifier = receiverIdentifier; + } + + public String getReceiverEmail() { + return receiverEmail; + } + + public void setReceiverEmail(String receiverEmail) { + this.receiverEmail = receiverEmail; + } + + public String getReceiverEmailNormalized() { + return receiverEmailNormalized; + } + + public void setReceiverEmailNormalized(String receiverEmailNormalized) { + this.receiverEmailNormalized = receiverEmailNormalized; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getBody() { + return body; + } + + public void setBody(String body) { + this.body = body; + } + + public String getErrorCode() { + return errorCode; + } + + public void setErrorCode(String errorCode) { + this.errorCode = errorCode; + } + + public PushError getPushError() { + return pushError; + } + + public void setPushError(PushError pushError) { + this.pushError = pushError; + } + + @Override + public String toString() { + return "PushResponse {" + "active='" + active + '\'' + ", iden='" + iden + '\'' + ", type='" + type + '\'' + + ", dismissed=" + dismissed + ", direction='" + direction + '\'' + ", senderIdentifier='" + + senderIdentifier + '\'' + ", senderEmail='" + senderEmail + '\'' + ", senderEmailNormalized='" + + senderEmailNormalized + '\'' + ", senderName='" + senderName + '\'' + ", receiverIdentifier='" + + receiverIdentifier + '\'' + ", receiverEmail='" + receiverEmail + '\'' + ", receiverEmailNormalized='" + + receiverEmailNormalized + '\'' + ", title='" + title + '\'' + ", body='" + body + '\'' + + ", errorCode='" + errorCode + '\'' + ", pushError=" + pushError + '}'; + } +} diff --git a/bundles/org.openhab.binding.pushbullet/src/main/resources/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.pushbullet/src/main/resources/ESH-INF/binding/binding.xml new file mode 100644 index 0000000000000..18f0f94dfce51 --- /dev/null +++ b/bundles/org.openhab.binding.pushbullet/src/main/resources/ESH-INF/binding/binding.xml @@ -0,0 +1,9 @@ + + + + @text/binding.pushbullet.name + @text/binding.pushbullet.description + Hakan Tandoğan + + diff --git a/bundles/org.openhab.binding.pushbullet/src/main/resources/ESH-INF/i18n/pushbullet_en.properties b/bundles/org.openhab.binding.pushbullet/src/main/resources/ESH-INF/i18n/pushbullet_en.properties new file mode 100644 index 0000000000000..df79c02a7897b --- /dev/null +++ b/bundles/org.openhab.binding.pushbullet/src/main/resources/ESH-INF/i18n/pushbullet_en.properties @@ -0,0 +1,17 @@ +# binding +binding.pushbullet.name = Pushbullet Binding +binding.pushbullet.description = The Pushbullet binding allows you to send messages to other users of the Pushbullet service. + +# action +actionSendPushbulletNoteLabel = publish an Pushbullet message +actionSendPushbulletNoteDesc = Publishes a Title to the given Pushbullet Recipient. + +actionSendPushbulletNoteInputRecipientLabel = Pushbullet Recipient +actionSendPushbulletNoteInputRecipientDesc = The Recipient to publish a Title to. +actionSendPushbulletNoteInputTitleLabel = Title +actionSendPushbulletNoteInputTitleDesc = The Title to publish +actionSendPushbulletNoteInputMessageLabel = Message +actionSendPushbulletNoteInputMessageDesc = The Message to publish + +# error texts +offline.conf-error-httpresponseexception = The pushbullet server reported an error, possibly an expired token. Check on web site diff --git a/bundles/org.openhab.binding.pushbullet/src/main/resources/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.pushbullet/src/main/resources/ESH-INF/thing/thing-types.xml new file mode 100644 index 0000000000000..ba9c559eecfb7 --- /dev/null +++ b/bundles/org.openhab.binding.pushbullet/src/main/resources/ESH-INF/thing/thing-types.xml @@ -0,0 +1,58 @@ + + + + + + Bot to send messages with. + + + + + + + + + + + Explicit Name of Bot, if wanted + true + + + + + Token as obtained from the server + + + + + The Pushbullet API Server to use, for local testing + https://api.pushbullet.com/v2 + true + + + + + + + + String + + Mail address or Channel Name + + + + String + + Title of the message + + + + String + + The text that is to be sent + + + diff --git a/bundles/pom.xml b/bundles/pom.xml index dd57b7d15505c..19dac9996f14d 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -156,6 +156,7 @@ org.openhab.binding.plugwise org.openhab.binding.powermax org.openhab.binding.pulseaudio + org.openhab.binding.pushbullet org.openhab.binding.regoheatpump org.openhab.binding.rfxcom org.openhab.binding.rme From 568471ebdbb4ab30f3948ec4775c0f35eb57880d Mon Sep 17 00:00:00 2001 From: Hakan Tandogan Date: Sun, 30 Jun 2019 21:31:53 +0200 Subject: [PATCH 2/5] [pushbullet] removed superfluous newlines Signed-off-by: Hakan Tandogan --- bundles/org.openhab.binding.pushbullet/README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/bundles/org.openhab.binding.pushbullet/README.md b/bundles/org.openhab.binding.pushbullet/README.md index b2d3871dc6df1..57799d9593021 100644 --- a/bundles/org.openhab.binding.pushbullet/README.md +++ b/bundles/org.openhab.binding.pushbullet/README.md @@ -111,10 +111,6 @@ then end ``` - - - - ## Creating an account for your bot(s) The pushbullet accounts are bound to either Google or Facebook accounts. From 06eb671fb74096125fd09725843b2e2722eadd57 Mon Sep 17 00:00:00 2001 From: Hakan Tandogan Date: Sun, 30 Jun 2019 21:34:39 +0200 Subject: [PATCH 3/5] [pushbullet] remove superfluous state change Signed-off-by: Hakan Tandogan --- .../pushbullet/internal/handler/PushbulletHandler.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/handler/PushbulletHandler.java b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/handler/PushbulletHandler.java index c3bb000fe3ef4..af0a822a0f227 100644 --- a/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/handler/PushbulletHandler.java +++ b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/handler/PushbulletHandler.java @@ -89,11 +89,6 @@ public void initialize() { logger.debug("Start initializing!"); config = getConfigAs(PushbulletConfiguration.class); - // set the thing status to UNKNOWN temporarily and let the background task decide for the real status. - // the framework is then able to reuse the resources from the thing handler initialization. - // we set this upfront to reliably check status updates in unit tests. - updateStatus(ThingStatus.UNKNOWN); - // Name and Token are both "required", so set the Thing immediately ONLINE. updateStatus(ThingStatus.ONLINE); From 74a35a806558fb3ad5a449830d45b2bae807f97e Mon Sep 17 00:00:00 2001 From: Hakan Tandogan Date: Sun, 30 Jun 2019 21:44:51 +0200 Subject: [PATCH 4/5] [pushbullet] remove superfluous toString call Signed-off-by: Hakan Tandogan --- .../binding/pushbullet/internal/handler/PushbulletHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/handler/PushbulletHandler.java b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/handler/PushbulletHandler.java index af0a822a0f227..a93de242ccdeb 100644 --- a/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/handler/PushbulletHandler.java +++ b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/handler/PushbulletHandler.java @@ -144,7 +144,7 @@ public boolean sendPush(@Nullable String recipient, @Nullable String title, @Nul logger.debug("Packed Request: {}", request); Properties headers = new Properties(); - headers.put(HttpHeader.USER_AGENT, "openHAB / Pushbullet binding " + VERSION.toString()); + headers.put(HttpHeader.USER_AGENT, "openHAB / Pushbullet binding " + VERSION); headers.put(HttpHeader.CONTENT_TYPE, MimeTypes.Type.APPLICATION_JSON.asString()); headers.put("Access-Token", configuration.getToken()); From 19196eb8a24c9ead07fab571907b5773a35e80e0 Mon Sep 17 00:00:00 2001 From: Hakan Tandogan Date: Wed, 3 Jul 2019 15:15:22 +0200 Subject: [PATCH 5/5] [pushbullet] Split up sendPush() into smaller methods, don't catch Error anymore Signed-off-by: Hakan Tandogan --- .../internal/handler/PushbulletHandler.java | 126 ++++++++++-------- 1 file changed, 71 insertions(+), 55 deletions(-) diff --git a/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/handler/PushbulletHandler.java b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/handler/PushbulletHandler.java index a93de242ccdeb..45120307b7e64 100644 --- a/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/handler/PushbulletHandler.java +++ b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/handler/PushbulletHandler.java @@ -18,11 +18,9 @@ import com.google.gson.GsonBuilder; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.jetty.client.HttpResponseException; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.MimeTypes; -import org.eclipse.smarthome.core.thing.ThingStatusDetail; import org.eclipse.smarthome.core.thing.binding.ThingHandlerService; import org.eclipse.smarthome.io.net.http.HttpUtil; import org.openhab.binding.pushbullet.internal.action.PushbulletActions; @@ -113,75 +111,93 @@ public boolean sendPush(@Nullable String recipient, @Nullable String title, @Nul PushbulletConfiguration configuration = getConfigAs(PushbulletConfiguration.class); logger.debug("CFG {}", configuration); - logger.debug("CFG Name '{}'", configuration.getName()); - try { - logger.debug("Recipient is '{}'", recipient); - logger.debug("Title is '{}'", title); - logger.debug("Message is '{}'", message); - - Push push = new Push(); - push.setTitle(title); - push.setBody(message); - push.setType(type); - - if (recipient != null) { - if (isValidEmail(recipient)) { - logger.debug("Recipient is an email address"); - push.setEmail(recipient); - } else if (isValidChannel(recipient)) { - logger.debug("Recipient is a channel tag"); - push.setChannel(recipient); - } else { - logger.warn("Invalid recipient: {}", recipient); - logger.warn("Message will be broadcast to all user's devices."); - } - } + Properties headers = prepareRequestHeaders(configuration); - logger.debug("Push: {}", push); + String request = prepareMessageBody(recipient, title, message, type); - String request = gson.toJson(push); - logger.debug("Packed Request: {}", request); + try (InputStream stream = new ByteArrayInputStream(request.getBytes(StandardCharsets.UTF_8))) { - Properties headers = new Properties(); - headers.put(HttpHeader.USER_AGENT, "openHAB / Pushbullet binding " + VERSION); - headers.put(HttpHeader.CONTENT_TYPE, MimeTypes.Type.APPLICATION_JSON.asString()); - headers.put("Access-Token", configuration.getToken()); + String pushAPI = configuration.getApiUrlBase() + "/" + API_METHOD_PUSHES; - logger.debug("Headers: {}", headers); + String responseString = HttpUtil.executeUrl(HttpMethod.POST.asString(), pushAPI, headers, stream, + MimeTypes.Type.APPLICATION_JSON.asString(), TIMEOUT); - try (InputStream stream = new ByteArrayInputStream(request.getBytes(StandardCharsets.UTF_8))) { + logger.debug("Got Response: {}", responseString); + PushResponse response = gson.fromJson(responseString, PushResponse.class); - String pushAPI = configuration.getApiUrlBase() + "/" + API_METHOD_PUSHES; + logger.debug("Unpacked Response: {}", response); - String responseString = HttpUtil.executeUrl(HttpMethod.POST.asString(), pushAPI, headers, stream, - MimeTypes.Type.APPLICATION_JSON.asString(), TIMEOUT); + stream.close(); - logger.debug("Got Response: {}", responseString); - PushResponse response = gson.fromJson(responseString, PushResponse.class); + if ((null != response) && (null == response.getPushError())) { + result = true; + } + } + catch (IOException e) { + logger.warn("IO problems pushing note: {}", e.getMessage()); + } - logger.debug("Unpacked Response: {}", response); + return result; + } - stream.close(); + /** + * helper method to populate the request headers + * + * @param configuration + * @return + */ + private Properties prepareRequestHeaders(PushbulletConfiguration configuration) { + Properties headers = new Properties(); + headers.put(HttpHeader.USER_AGENT, "openHAB / Pushbullet binding " + VERSION); + headers.put(HttpHeader.CONTENT_TYPE, MimeTypes.Type.APPLICATION_JSON.asString()); + headers.put("Access-Token", configuration.getToken()); - if ((null != response) && (null == response.getPushError())) { - result = true; - } - } - catch (IOException e) { - logger.warn("IO problems pushing note: {}", e.getMessage()); - } + logger.debug("Headers: {}", headers); - } catch (java.lang.Error e) { - if (e.getCause() instanceof HttpResponseException) { - logger.debug("Possibly expired token, check on web site", e); - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "@text/offline.conf-error-httpresponseexception"); - } + return headers; + } - logger.debug("Error in getResponseString: ", e); + /** + * helper method to create a message body from data to be transferred. + * + * @param recipient + * @param title + * @param message + * @param type + * + * @return the message as a String to be posted + */ + private String prepareMessageBody(@Nullable String recipient, @Nullable String title, @Nullable String message, + String type) { + logger.debug("Recipient is '{}'", recipient); + logger.debug("Title is '{}'", title); + logger.debug("Message is '{}'", message); + + Push push = new Push(); + push.setTitle(title); + push.setBody(message); + push.setType(type); + + if (recipient != null) { + if (isValidEmail(recipient)) { + logger.debug("Recipient is an email address"); + push.setEmail(recipient); + } else if (isValidChannel(recipient)) { + logger.debug("Recipient is a channel tag"); + push.setChannel(recipient); + } else { + logger.warn("Invalid recipient: {}", recipient); + logger.warn("Message will be broadcast to all user's devices."); + } } - return result; + logger.debug("Push: {}", push); + + String request = gson.toJson(push); + logger.debug("Packed Request: {}", request); + + return request; } /**