]> git.basschouten.com Git - openhab-addons.git/commitdiff
[pushbullet] Add link and file push type support (#17472)
authorJeremy <jsetton@users.noreply.github.com>
Sat, 28 Sep 2024 20:58:23 +0000 (16:58 -0400)
committerGitHub <noreply@github.com>
Sat, 28 Sep 2024 20:58:23 +0000 (22:58 +0200)
* [pushbullet] Add link and file push type support

Signed-off-by: jsetton <jeremy.setton@gmail.com>
21 files changed:
bundles/org.openhab.binding.pushbullet/README.md
bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/PushbulletBindingConstants.java
bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/PushbulletConfiguration.java
bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/PushbulletHandlerFactory.java
bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/PushbulletHttpClient.java [new file with mode: 0644]
bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/action/PushbulletActions.java
bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/exception/PushbulletApiException.java [new file with mode: 0644]
bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/exception/PushbulletAuthenticationException.java [new file with mode: 0644]
bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/handler/PushbulletHandler.java
bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/InstantDeserializer.java [new file with mode: 0644]
bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/Push.java [deleted file]
bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/PushError.java [deleted file]
bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/PushRequest.java [new file with mode: 0644]
bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/PushResponse.java
bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/PushType.java [new file with mode: 0644]
bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/UploadRequest.java [new file with mode: 0644]
bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/UploadResponse.java [new file with mode: 0644]
bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/User.java [new file with mode: 0644]
bundles/org.openhab.binding.pushbullet/src/main/resources/OH-INF/addon/addon.xml
bundles/org.openhab.binding.pushbullet/src/main/resources/OH-INF/i18n/pushbullet.properties
bundles/org.openhab.binding.pushbullet/src/main/resources/OH-INF/thing/thing-types.xml

index 99472b6f07f1e3e165615f1e3e2d46d76177004e..54e70a65586a15ed114eee3fc42f1483d392403c 100644 (file)
@@ -48,6 +48,11 @@ Two different actions available:
 
 - `sendPushbulletNote(String recipient, String messsage)`
 - `sendPushbulletNote(String recipient, String title, String messsage)`
+- `sendPushbulletLink(String recipient, String url)`
+- `sendPushbulletLink(String recipient, String title, String messsage, String url)`
+- `sendPushbulletFile(String recipient, String content)`
+- `sendPushbulletFile(String recipient, String title, String messsage, String content)`
+- `sendPushbulletFile(String recipient, String title, String messsage, String content, String fileName)`
 
 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.
@@ -56,11 +61,25 @@ Once this action instance is retrieved, you can invoke the action method on it.
 The recipient can either be an email address, a channel tag or `null`.
 If it is not specified or properly formatted, the note will be broadcast to all of the user account's devices.
 
+The file content can be an image URL, a local file path or an Image item state.
+
+The file name is used in the upload link and how it appears in the push message for non-image content.
+If it is not specified, it is automatically determined from the image URL or file path.
+For Image item state content, it defaults to `image.jpg`.
+
+For the `sendPushbulletNote` action, parameter `message` is required.
+Likewise, for `sendPushbulletLink`, `url` and for `sendPushbulletFile`, `content` parameters are required.
+Any other parameters for these actions are optional and can be set to `null`.
+
 Examples:
 
 ```java
 val actions = getActions("pushbullet", "pushbullet:bot:r2d2")
-val result = actions.sendPushbulletNote("someone@example.com", "R2D2 talks here...", "This is the pushed note.")
+actions.sendPushbulletNote("someone@example.com", "Note Example", "This is the pushed note.")
+actions.sendPushbulletLink("someone@example.com", "Link Example", "This is the pushed link", "https://example.com")
+actions.sendPushbulletFile("someone@example.com", "File Example", "This is the pushed file", "https://example.com/image.png")
+actions.sendPushbulletFile("someone@example.com", null, null, "/path/to/somefile.pdf", "document.pdf")
+actions.sendPushbulletFile("someone@example.com", ImageItem.state.toFullString)
 ```
 
 ## Full Example
index 8f8f14382749bd7b4597393682e5b2e0da131484..6f55cd9c11ff5f44df1d3aa649b8890fa6db37a8 100644 (file)
@@ -22,11 +22,12 @@ import org.openhab.core.thing.ThingTypeUID;
  * used across the whole binding.
  *
  * @author Hakan Tandogan - Initial contribution
+ * @author Jeremy Setton - Add link and file push type support
  */
 @NonNullByDefault
 public class PushbulletBindingConstants {
 
-    private static final String BINDING_ID = "pushbullet";
+    public static final String BINDING_ID = "pushbullet";
 
     // List of all Thing Type UIDs
     public static final ThingTypeUID THING_TYPE_BOT = new ThingTypeUID(BINDING_ID, "bot");
@@ -38,8 +39,16 @@ public class PushbulletBindingConstants {
     public static final String TITLE = "title";
     public static final String MESSAGE = "message";
 
+    // Thing properties
+    public static final String PROPERTY_EMAIL = "email";
+    public static final String PROPERTY_NAME = "name";
+
     // Binding logic constants
-    public static final String API_METHOD_PUSHES = "pushes";
+    public static final String API_ENDPOINT_PUSHES = "/pushes";
+    public static final String API_ENDPOINT_UPLOAD_REQUEST = "/upload-request";
+    public static final String API_ENDPOINT_USERS_ME = "/users/me";
+
+    public static final String IMAGE_FILE_NAME = "image.jpg";
 
-    public static final int TIMEOUT = 30 * 1000; // 30 seconds
+    public static final int MAX_UPLOAD_SIZE = 26214400;
 }
index 125b3cf79177e4c0f38754d0a8787c2c35cb6bad..8884d07f4a3c8f09b5750e6baa0aeac9d101a243 100644 (file)
@@ -19,37 +19,26 @@ import org.eclipse.jdt.annotation.Nullable;
  * The {@link PushbulletConfiguration} class contains fields mapping thing configuration parameters.
  *
  * @author Hakan Tandogan - Initial contribution
+ * @author Jeremy Setton - Add link and file push type support
  */
 @NonNullByDefault
 public class PushbulletConfiguration {
 
     private @Nullable String name;
 
-    private String token = "invalid";
+    private String token = "";
 
-    private String apiUrlBase = "invalid";
+    private String apiUrlBase = "https://api.pushbullet.com/v2";
 
     public @Nullable String getName() {
         return name;
     }
 
-    public void setName(String name) {
-        this.name = name;
-    }
-
-    public String getToken() {
+    public String getAccessToken() {
         return token;
     }
 
-    public void setToken(String token) {
-        this.token = token;
-    }
-
     public String getApiUrlBase() {
         return apiUrlBase;
     }
-
-    public void setApiUrlBase(String apiUrlBase) {
-        this.apiUrlBase = apiUrlBase;
-    }
 }
index 96867dd6b62e52b3ee8b1ca20fafdf19af515e4d..a3b1e488988b12249faa6176fa1bf89d8f669d94 100644 (file)
@@ -16,24 +16,36 @@ import static org.openhab.binding.pushbullet.internal.PushbulletBindingConstants
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jetty.client.HttpClient;
 import org.openhab.binding.pushbullet.internal.handler.PushbulletHandler;
+import org.openhab.core.io.net.http.HttpClientFactory;
 import org.openhab.core.thing.Thing;
 import org.openhab.core.thing.ThingTypeUID;
 import org.openhab.core.thing.binding.BaseThingHandlerFactory;
 import org.openhab.core.thing.binding.ThingHandler;
 import org.openhab.core.thing.binding.ThingHandlerFactory;
+import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
 
 /**
  * The {@link PushbulletHandlerFactory} is responsible for creating things and thing
  * handlers.
  *
  * @author Hakan Tandogan - Initial contribution
+ * @author Jeremy Setton - Add link and file push type support
  */
 @NonNullByDefault
 @Component(configurationPid = "binding.pushbullet", service = ThingHandlerFactory.class)
 public class PushbulletHandlerFactory extends BaseThingHandlerFactory {
 
+    private final HttpClient httpClient;
+
+    @Activate
+    public PushbulletHandlerFactory(final @Reference HttpClientFactory httpClientFactory) {
+        this.httpClient = httpClientFactory.getCommonHttpClient();
+    }
+
     @Override
     public boolean supportsThingType(ThingTypeUID thingTypeUID) {
         return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
@@ -44,7 +56,7 @@ public class PushbulletHandlerFactory extends BaseThingHandlerFactory {
         ThingTypeUID thingTypeUID = thing.getThingTypeUID();
 
         if (THING_TYPE_BOT.equals(thingTypeUID)) {
-            return new PushbulletHandler(thing);
+            return new PushbulletHandler(thing, httpClient);
         }
 
         return null;
diff --git a/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/PushbulletHttpClient.java b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/PushbulletHttpClient.java
new file mode 100644 (file)
index 0000000..eebd311
--- /dev/null
@@ -0,0 +1,203 @@
+/**
+ * Copyright (c) 2010-2024 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 java.time.Instant;
+import java.util.Objects;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.client.util.BytesContentProvider;
+import org.eclipse.jetty.client.util.MultiPartContentProvider;
+import org.eclipse.jetty.client.util.StringContentProvider;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.MimeTypes;
+import org.openhab.binding.pushbullet.internal.exception.PushbulletApiException;
+import org.openhab.binding.pushbullet.internal.exception.PushbulletAuthenticationException;
+import org.openhab.binding.pushbullet.internal.model.InstantDeserializer;
+import org.openhab.core.OpenHAB;
+import org.openhab.core.library.types.RawType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonSyntaxException;
+
+/**
+ * The {@link PushbulletHttpClient} handles requests to Pushbullet API
+ *
+ * @author Jeremy Setton - Initial contribution
+ */
+@NonNullByDefault
+public class PushbulletHttpClient {
+    private static final String AGENT = "openHAB/" + OpenHAB.getVersion();
+
+    private static final int TIMEOUT = 30; // in seconds
+
+    private static final String HEADER_RATELIMIT_RESET = "X-Ratelimit-Reset";
+
+    private final Logger logger = LoggerFactory.getLogger(PushbulletHttpClient.class);
+
+    private final Gson gson = new GsonBuilder().registerTypeHierarchyAdapter(Instant.class, new InstantDeserializer())
+            .create();
+
+    private PushbulletConfiguration config = new PushbulletConfiguration();
+
+    private final HttpClient httpClient;
+
+    public PushbulletHttpClient(HttpClient httpClient) {
+        this.httpClient = httpClient;
+    }
+
+    public void setConfiguration(PushbulletConfiguration config) {
+        this.config = config;
+    }
+
+    /**
+     * Executes an api request
+     *
+     * @param apiEndpoint the request api endpoint
+     * @param responseType the response type
+     * @return the unpacked response
+     * @throws PushbulletApiException
+     */
+    public <T> T executeRequest(String apiEndpoint, Class<T> responseType) throws PushbulletApiException {
+        return executeRequest(apiEndpoint, null, responseType);
+    }
+
+    /**
+     * Executes an api request
+     *
+     * @param apiEndpoint the request api endpoint
+     * @param body the request body object
+     * @param responseType the response type
+     * @return the unpacked response
+     * @throws PushbulletApiException
+     */
+    public <T> T executeRequest(String apiEndpoint, @Nullable Object body, Class<T> responseType)
+            throws PushbulletApiException {
+        String url = config.getApiUrlBase() + apiEndpoint;
+        String accessToken = config.getAccessToken();
+
+        Request request = newRequest(url).header("Access-Token", accessToken);
+
+        if (body != null) {
+            StringContentProvider content = new StringContentProvider(gson.toJson(body));
+            String contentType = MimeTypes.Type.APPLICATION_JSON.asString();
+
+            request.method(HttpMethod.POST).content(content, contentType);
+        }
+
+        String responseBody = sendRequest(request);
+
+        try {
+            T response = Objects.requireNonNull(gson.fromJson(responseBody, responseType));
+            logger.debug("Unpacked Response: {}", response);
+            return response;
+        } catch (JsonSyntaxException e) {
+            logger.debug("Failed to unpack response as '{}': {}", responseType.getSimpleName(), e.getMessage());
+            throw new PushbulletApiException(e);
+        }
+    }
+
+    /**
+     * Uploads a file
+     *
+     * @param url the upload url
+     * @param data the file data
+     * @throws PushbulletApiException
+     */
+    public void uploadFile(String url, RawType data) throws PushbulletApiException {
+        MultiPartContentProvider content = new MultiPartContentProvider();
+        content.addFieldPart("file", new BytesContentProvider(data.getMimeType(), data.getBytes()), null);
+
+        Request request = newRequest(url).method(HttpMethod.POST).content(content);
+
+        sendRequest(request);
+    }
+
+    /**
+     * Creates a new http request
+     *
+     * @param url the request url
+     * @return the new Request object with default parameters
+     */
+    private Request newRequest(String url) {
+        return httpClient.newRequest(url).agent(AGENT).timeout(TIMEOUT, TimeUnit.SECONDS);
+    }
+
+    /**
+     * Sends a http request
+     *
+     * @param request the request to send
+     * @return the response body
+     * @throws PushbulletApiException
+     */
+    private String sendRequest(Request request) throws PushbulletApiException {
+        try {
+            logger.debug("Request {} {}", request.getMethod(), request.getURI());
+            logger.debug("Request Headers: {}", request.getHeaders());
+
+            ContentResponse response = request.send();
+
+            int statusCode = response.getStatus();
+            String statusReason = response.getReason();
+            String responseBody = response.getContentAsString();
+
+            logger.debug("Got HTTP {} Response: '{}'", statusCode, responseBody);
+
+            switch (statusCode) {
+                case HttpStatus.OK_200:
+                case HttpStatus.NO_CONTENT_204:
+                    return responseBody;
+                case HttpStatus.UNAUTHORIZED_401:
+                case HttpStatus.FORBIDDEN_403:
+                    throw new PushbulletAuthenticationException(statusReason);
+                case HttpStatus.TOO_MANY_REQUESTS_429:
+                    logger.warn("Rate limited for making too many requests until {}",
+                            getRateLimitResetTime(response.getHeaders()));
+                default:
+                    throw new PushbulletApiException(statusReason);
+            }
+        } catch (InterruptedException | TimeoutException | ExecutionException e) {
+            logger.debug("Failed to send request: {}", e.getMessage());
+            throw new PushbulletApiException(e);
+        }
+    }
+
+    /**
+     * Returns the rate limit reset time included in response headers
+     *
+     * @param headers the response headers
+     * @return the rate limit reset time if found in headers, otherwise null
+     */
+    private @Nullable Instant getRateLimitResetTime(HttpFields headers) {
+        try {
+            long resetTime = headers.getLongField(HEADER_RATELIMIT_RESET);
+            if (resetTime != -1) {
+                return Instant.ofEpochSecond(resetTime);
+            }
+        } catch (NumberFormatException ignored) {
+        }
+        return null;
+    }
+}
index 79f5afd071d6444619e2c04430f449cbb2a138b3..c8fa95dadecc6fb4c0993f9d7ad8b8a5f8b3e51c 100644 (file)
@@ -30,6 +30,7 @@ import org.slf4j.LoggerFactory;
  * The {@link PushbulletActions} class defines rule actions for sending notifications
  *
  * @author Hakan Tandogan - Initial contribution
+ * @author Jeremy Setton - Add link and file push type support
  */
 @Component(scope = ServiceScope.PROTOTYPE, service = PushbulletActions.class)
 @ThingActionsScope(name = "pushbullet")
@@ -52,9 +53,9 @@ public class PushbulletActions implements ThingActions {
 
     @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) {
+            @ActionInput(name = "recipient", label = "@text/actionSendPushbulletNoteInputRecipientLabel", description = "@text/actionSendPushbulletNoteInputRecipientDesc", type = "java.lang.String") @Nullable String recipient,
+            @ActionInput(name = "title", label = "@text/actionSendPushbulletNoteInputTitleLabel", description = "@text/actionSendPushbulletNoteInputTitleDesc", type = "java.lang.String") @Nullable String title,
+            @ActionInput(name = "message", label = "@text/actionSendPushbulletNoteInputMessageLabel", description = "@text/actionSendPushbulletNoteInputMessageDesc", type = "java.lang.String", required = true) String message) {
         logger.trace("sendPushbulletNote '{}', '{}', '{}'", recipient, title, message);
 
         PushbulletHandler localHandler = handler;
@@ -63,18 +64,18 @@ public class PushbulletActions implements ThingActions {
             return false;
         }
 
-        return localHandler.sendPush(recipient, title, message, "note");
+        return localHandler.sendPushNote(recipient, title, message);
     }
 
     public static boolean sendPushbulletNote(ThingActions actions, @Nullable String recipient, @Nullable String title,
-            @Nullable String message) {
+            String message) {
         return ((PushbulletActions) actions).sendPushbulletNote(recipient, title, message);
     }
 
     @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) {
+            @ActionInput(name = "recipient", label = "@text/actionSendPushbulletNoteInputRecipientLabel", description = "@text/actionSendPushbulletNoteInputRecipientDesc", type = "java.lang.String") @Nullable String recipient,
+            @ActionInput(name = "message", label = "@text/actionSendPushbulletNoteInputMessageLabel", description = "@text/actionSendPushbulletNoteInputMessageDesc", type = "java.lang.String", required = true) String message) {
         logger.trace("sendPushbulletNote '{}', '{}'", recipient, message);
 
         PushbulletHandler localHandler = handler;
@@ -83,11 +84,115 @@ public class PushbulletActions implements ThingActions {
             return false;
         }
 
-        return localHandler.sendPush(recipient, message, "note");
+        return localHandler.sendPushNote(recipient, null, message);
     }
 
-    public static boolean sendPushbulletNote(ThingActions actions, @Nullable String recipient,
-            @Nullable String message) {
+    public static boolean sendPushbulletNote(ThingActions actions, @Nullable String recipient, String message) {
         return ((PushbulletActions) actions).sendPushbulletNote(recipient, message);
     }
+
+    @RuleAction(label = "@text/actionSendPushbulletLinkLabel", description = "@text/actionSendPushbulletLinkDesc")
+    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean sendPushbulletLink(
+            @ActionInput(name = "recipient", label = "@text/actionSendPushbulletNoteInputRecipientLabel", description = "@text/actionSendPushbulletNoteInputRecipientDesc", type = "java.lang.String") @Nullable String recipient,
+            @ActionInput(name = "title", label = "@text/actionSendPushbulletNoteInputTitleLabel", description = "@text/actionSendPushbulletNoteInputTitleDesc", type = "java.lang.String") @Nullable String title,
+            @ActionInput(name = "message", label = "@text/actionSendPushbulletNoteInputMessageLabel", description = "@text/actionSendPushbulletNoteInputMessageDesc", type = "java.lang.String") @Nullable String message,
+            @ActionInput(name = "url", label = "@text/actionSendPushbulletLinkInputUrlLabel", description = "@text/actionSendPushbulletLinkInputUrlDesc", type = "java.lang.String", required = true) String url) {
+        logger.trace("sendPushbulletLink '{}', '{}', '{}', '{}'", recipient, title, message, url);
+
+        PushbulletHandler localHandler = handler;
+        if (localHandler == null) {
+            logger.warn("Pushbullet service Handler is null!");
+            return false;
+        }
+
+        return localHandler.sendPushLink(recipient, title, message, url);
+    }
+
+    public static boolean sendPushbulletLink(ThingActions actions, @Nullable String recipient, @Nullable String title,
+            @Nullable String message, String url) {
+        return ((PushbulletActions) actions).sendPushbulletLink(recipient, title, message, url);
+    }
+
+    @RuleAction(label = "@text/actionSendPushbulletLinkLabel", description = "@text/actionSendPushbulletLinkDesc")
+    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean sendPushbulletLink(
+            @ActionInput(name = "recipient", label = "@text/actionSendPushbulletNoteInputRecipientLabel", description = "@text/actionSendPushbulletNoteInputRecipientDesc", type = "java.lang.String") @Nullable String recipient,
+            @ActionInput(name = "url", label = "@text/actionSendPushbulletLinkInputUrlLabel", description = "@text/actionSendPushbulletLinkInputUrlDesc", type = "java.lang.String", required = true) String url) {
+        logger.trace("sendPushbulletLink '{}', '{}'", recipient, url);
+
+        PushbulletHandler localHandler = handler;
+        if (localHandler == null) {
+            logger.warn("Pushbullet service Handler is null!");
+            return false;
+        }
+
+        return localHandler.sendPushLink(recipient, null, null, url);
+    }
+
+    public static boolean sendPushbulletLink(ThingActions actions, @Nullable String recipient, String url) {
+        return ((PushbulletActions) actions).sendPushbulletLink(recipient, url);
+    }
+
+    @RuleAction(label = "@text/actionSendPushbulletFileLabel", description = "@text/actionSendPushbulletFileDesc")
+    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean sendPushbulletFile(
+            @ActionInput(name = "recipient", label = "@text/actionSendPushbulletNoteInputRecipientLabel", description = "@text/actionSendPushbulletNoteInputRecipientDesc", type = "java.lang.String") @Nullable String recipient,
+            @ActionInput(name = "title", label = "@text/actionSendPushbulletNoteInputTitleLabel", description = "@text/actionSendPushbulletNoteInputTitleDesc", type = "java.lang.String") @Nullable String title,
+            @ActionInput(name = "message", label = "@text/actionSendPushbulletNoteInputMessageLabel", description = "@text/actionSendPushbulletNoteInputMessageDesc", type = "java.lang.String") @Nullable String message,
+            @ActionInput(name = "content", label = "@text/actionSendPushbulletFileInputContent", description = "@text/actionSendPushbulletFileInputContentDesc", type = "java.lang.String", required = true) String content,
+            @ActionInput(name = "filename", label = "@text/actionSendPushbulletFileInputName", description = "@text/actionSendPushbulletFileInputNameDesc", type = "java.lang.String") @Nullable String fileName) {
+        logger.trace("sendPushbulletFile '{}', '{}', '{}', '{}', '{}'", recipient, title, message, content, fileName);
+
+        PushbulletHandler localHandler = handler;
+        if (localHandler == null) {
+            logger.warn("Pushbullet service Handler is null!");
+            return false;
+        }
+
+        return localHandler.sendPushFile(recipient, title, message, content, fileName);
+    }
+
+    public static boolean sendPushbulletFile(ThingActions actions, @Nullable String recipient, @Nullable String title,
+            @Nullable String message, String content, @Nullable String filename) {
+        return ((PushbulletActions) actions).sendPushbulletFile(recipient, title, message, content, filename);
+    }
+
+    @RuleAction(label = "@text/actionSendPushbulletFileLabel", description = "@text/actionSendPushbulletFileDesc")
+    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean sendPushbulletFile(
+            @ActionInput(name = "recipient", label = "@text/actionSendPushbulletNoteInputRecipientLabel", description = "@text/actionSendPushbulletNoteInputRecipientDesc", type = "java.lang.String") @Nullable String recipient,
+            @ActionInput(name = "title", label = "@text/actionSendPushbulletNoteInputTitleLabel", description = "@text/actionSendPushbulletNoteInputTitleDesc", type = "java.lang.String") @Nullable String title,
+            @ActionInput(name = "message", label = "@text/actionSendPushbulletNoteInputMessageLabel", description = "@text/actionSendPushbulletNoteInputMessageDesc", type = "java.lang.String") @Nullable String message,
+            @ActionInput(name = "content", label = "@text/actionSendPushbulletFileInputContent", description = "@text/actionSendPushbulletFileInputContentDesc", type = "java.lang.String", required = true) String content) {
+        logger.trace("sendPushbulletFile '{}', '{}', '{}', '{}'", recipient, title, message, content);
+
+        PushbulletHandler localHandler = handler;
+        if (localHandler == null) {
+            logger.warn("Pushbullet service Handler is null!");
+            return false;
+        }
+
+        return localHandler.sendPushFile(recipient, title, message, content, null);
+    }
+
+    public static boolean sendPushbulletFile(ThingActions actions, @Nullable String recipient, @Nullable String title,
+            @Nullable String message, String content) {
+        return ((PushbulletActions) actions).sendPushbulletFile(recipient, title, message, content);
+    }
+
+    @RuleAction(label = "@text/actionSendPushbulletFileLabel", description = "@text/actionSendPushbulletFileDesc")
+    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean sendPushbulletFile(
+            @ActionInput(name = "recipient", label = "@text/actionSendPushbulletNoteInputRecipientLabel", description = "@text/actionSendPushbulletNoteInputRecipientDesc", type = "java.lang.String") @Nullable String recipient,
+            @ActionInput(name = "content", label = "@text/actionSendPushbulletFileInputContent", description = "@text/actionSendPushbulletFileInputContentDesc", type = "java.lang.String", required = true) String content) {
+        logger.trace("sendPushbulletFile '{}', '{}'", recipient, content);
+
+        PushbulletHandler localHandler = handler;
+        if (localHandler == null) {
+            logger.warn("Pushbullet service Handler is null!");
+            return false;
+        }
+
+        return localHandler.sendPushFile(recipient, null, null, content, null);
+    }
+
+    public static boolean sendPushbulletFile(ThingActions actions, @Nullable String recipient, String content) {
+        return ((PushbulletActions) actions).sendPushbulletFile(recipient, content);
+    }
 }
diff --git a/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/exception/PushbulletApiException.java b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/exception/PushbulletApiException.java
new file mode 100644 (file)
index 0000000..60ef7e1
--- /dev/null
@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2010-2024 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.exception;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link PushbulletApiException} represents a Pushbullet API exception
+ *
+ * @author Jeremy Setton - Initial contribution
+ */
+@NonNullByDefault
+public class PushbulletApiException extends Exception {
+    private static final long serialVersionUID = 1L;
+
+    public PushbulletApiException(String message) {
+        super(message);
+    }
+
+    public PushbulletApiException(Exception exception) {
+        super(exception);
+    }
+}
diff --git a/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/exception/PushbulletAuthenticationException.java b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/exception/PushbulletAuthenticationException.java
new file mode 100644 (file)
index 0000000..56ec028
--- /dev/null
@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) 2010-2024 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.exception;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link PushbulletAuthenticationException} represents a Pushbullet authentication exception
+ *
+ * @author Jeremy Setton - Initial contribution
+ */
+@NonNullByDefault
+public class PushbulletAuthenticationException extends PushbulletApiException {
+    private static final long serialVersionUID = 1L;
+
+    public PushbulletAuthenticationException(String message) {
+        super(message);
+    }
+}
index f1ceaaefd2bd290142b6c86f8e177710d4f94332..3055ea3f80ca541702f3aee7daa08f2077dc12a9 100644 (file)
@@ -14,14 +14,14 @@ package org.openhab.binding.pushbullet.internal.handler;
 
 import static org.openhab.binding.pushbullet.internal.PushbulletBindingConstants.*;
 
-import java.io.ByteArrayInputStream;
 import java.io.IOException;
-import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.util.Collection;
-import java.util.Properties;
+import java.util.Objects;
 import java.util.Set;
-import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import javax.mail.internet.AddressException;
@@ -29,49 +29,51 @@ import javax.mail.internet.InternetAddress;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
-import org.eclipse.jetty.http.HttpHeader;
-import org.eclipse.jetty.http.HttpMethod;
-import org.eclipse.jetty.http.MimeTypes;
+import org.eclipse.jetty.client.HttpClient;
 import org.openhab.binding.pushbullet.internal.PushbulletConfiguration;
+import org.openhab.binding.pushbullet.internal.PushbulletHttpClient;
 import org.openhab.binding.pushbullet.internal.action.PushbulletActions;
-import org.openhab.binding.pushbullet.internal.model.Push;
+import org.openhab.binding.pushbullet.internal.exception.PushbulletApiException;
+import org.openhab.binding.pushbullet.internal.exception.PushbulletAuthenticationException;
+import org.openhab.binding.pushbullet.internal.model.PushRequest;
 import org.openhab.binding.pushbullet.internal.model.PushResponse;
+import org.openhab.binding.pushbullet.internal.model.PushType;
+import org.openhab.binding.pushbullet.internal.model.UploadRequest;
+import org.openhab.binding.pushbullet.internal.model.UploadResponse;
+import org.openhab.binding.pushbullet.internal.model.User;
 import org.openhab.core.io.net.http.HttpUtil;
+import org.openhab.core.library.types.RawType;
 import org.openhab.core.thing.ChannelUID;
 import org.openhab.core.thing.Thing;
 import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
 import org.openhab.core.thing.binding.BaseThingHandler;
 import org.openhab.core.thing.binding.ThingHandlerService;
 import org.openhab.core.types.Command;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.Version;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-
 /**
  * The {@link PushbulletHandler} is responsible for handling commands, which are
  * sent to one of the channels.
  *
  * @author Hakan Tandogan - Initial contribution
+ * @author Jeremy Setton - Add link and file push type support
  */
 @NonNullByDefault
 public class PushbulletHandler extends BaseThingHandler {
 
-    private final Logger logger = LoggerFactory.getLogger(PushbulletHandler.class);
+    private static final Pattern CHANNEL_PATTERN = Pattern.compile("^[a-zA-Z0-9_-]+$");
 
-    private final Gson gson = new GsonBuilder().create();
+    private final Logger logger = LoggerFactory.getLogger(PushbulletHandler.class);
 
-    private static final Version VERSION = FrameworkUtil.getBundle(PushbulletHandler.class).getVersion();
+    private final PushbulletHttpClient httpClient;
 
-    private static final Pattern CHANNEL_PATTERN = Pattern.compile("^[a-zA-Z0-9_-]+$");
+    private int maxUploadSize;
 
-    private @Nullable PushbulletConfiguration config;
-
-    public PushbulletHandler(Thing thing) {
+    public PushbulletHandler(Thing thing, HttpClient httpClient) {
         super(thing);
+        this.httpClient = new PushbulletHttpClient(httpClient);
     }
 
     @Override
@@ -86,13 +88,42 @@ public class PushbulletHandler extends BaseThingHandler {
 
     @Override
     public void initialize() {
-        logger.debug("Start initializing!");
-        config = getConfigAs(PushbulletConfiguration.class);
+        logger.debug("Starting {}", thing.getUID());
+
+        PushbulletConfiguration config = getConfigAs(PushbulletConfiguration.class);
+
+        if (config.getAccessToken().isEmpty()) {
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Undefined access token.");
+            return;
+        }
 
-        // Name and Token are both "required", so set the Thing immediately ONLINE.
-        updateStatus(ThingStatus.ONLINE);
+        httpClient.setConfiguration(config);
 
-        logger.debug("Finished initializing!");
+        scheduler.execute(() -> retrieveAccountInfo());
+
+        updateStatus(ThingStatus.UNKNOWN);
+    }
+
+    private void retrieveAccountInfo() {
+        try {
+            User user = httpClient.executeRequest(API_ENDPOINT_USERS_ME, User.class);
+
+            maxUploadSize = Objects.requireNonNullElse(user.getMaxUploadSize(), MAX_UPLOAD_SIZE);
+
+            logger.debug("Set maximum upload size for {} to {} bytes", thing.getUID(), maxUploadSize);
+
+            updateProperty(PROPERTY_NAME, user.getName());
+            updateProperty(PROPERTY_EMAIL, user.getEmail());
+
+            logger.debug("Updated properties for {} to {}", thing.getUID(), thing.getProperties());
+
+            updateStatus(ThingStatus.ONLINE);
+        } catch (PushbulletAuthenticationException e) {
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Invalid access token.");
+        } catch (PushbulletApiException e) {
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
+                    "Unable to retrieve account info.");
+        }
     }
 
     @Override
@@ -100,125 +131,227 @@ public class PushbulletHandler extends BaseThingHandler {
         return Set.of(PushbulletActions.class);
     }
 
-    public boolean sendPush(@Nullable String recipient, @Nullable String message, String type) {
-        return sendPush(recipient, "", message, type);
+    /**
+     * Sends a push note
+     *
+     * @param recipient the recipient
+     * @param title the title
+     * @param message the message
+     * @return true if successful
+     */
+    public boolean sendPushNote(@Nullable String recipient, @Nullable String title, String message) {
+        PushRequest request = newPushRequest(recipient, title, message, PushType.NOTE);
+
+        return sendPush(request);
     }
 
-    public boolean sendPush(@Nullable String recipient, @Nullable String title, @Nullable String message, String type) {
-        boolean result = false;
+    /**
+     * Sends a push link
+     *
+     * @param recipient the recipient
+     * @param title the title
+     * @param message the message
+     * @param url the message url
+     * @return true if successful
+     */
+    public boolean sendPushLink(@Nullable String recipient, @Nullable String title, @Nullable String message,
+            String url) {
+        PushRequest request = newPushRequest(recipient, title, message, PushType.LINK);
+        request.setUrl(url);
 
-        logger.debug("sendPush is called for ");
-        logger.debug("Thing {}", thing);
-        logger.debug("Thing Label: '{}'", thing.getLabel());
+        return sendPush(request);
+    }
 
-        PushbulletConfiguration configuration = getConfigAs(PushbulletConfiguration.class);
-        logger.debug("CFG {}", configuration);
+    /**
+     * Sends a push file
+     *
+     * @param recipient the recipient
+     * @param title the title
+     * @param message the message
+     * @param content the file content
+     * @param fileName the file name
+     * @return true if successful
+     */
+    public boolean sendPushFile(@Nullable String recipient, @Nullable String title, @Nullable String message,
+            String content, @Nullable String fileName) {
+        UploadResponse upload = uploadFile(content, fileName);
+        if (upload == null) {
+            return false;
+        }
 
-        Properties headers = prepareRequestHeaders(configuration);
+        PushRequest request = newPushRequest(recipient, title, message, PushType.FILE);
+        request.setFileName(upload.getFileName());
+        request.setFileType(upload.getFileType());
+        request.setFileUrl(upload.getFileUrl());
 
-        String request = prepareMessageBody(recipient, title, message, type);
+        return sendPush(request);
+    }
 
-        try (InputStream stream = new ByteArrayInputStream(request.getBytes(StandardCharsets.UTF_8))) {
-            String pushAPI = configuration.getApiUrlBase() + "/" + API_METHOD_PUSHES;
+    /**
+     * Helper method to send a push request
+     *
+     * @param request the push request
+     * @return true if successful
+     */
+    private boolean sendPush(PushRequest request) {
+        logger.debug("Sending push notification for {}", thing.getUID());
+        logger.debug("Push Request: {}", request);
 
-            String responseString = HttpUtil.executeUrl(HttpMethod.POST.asString(), pushAPI, headers, stream,
-                    MimeTypes.Type.APPLICATION_JSON.asString(), TIMEOUT);
+        try {
+            httpClient.executeRequest(API_ENDPOINT_PUSHES, request, PushResponse.class);
+            return true;
+        } catch (PushbulletApiException e) {
+            return false;
+        }
+    }
 
-            logger.debug("Got Response: {}", responseString);
-            PushResponse response = gson.fromJson(responseString, PushResponse.class);
+    /**
+     * Helper method to upload a file to use in push message
+     *
+     * @param content the file content
+     * @param fileName the file name
+     * @return the upload response if successful, otherwise null
+     */
+    private @Nullable UploadResponse uploadFile(String content, @Nullable String fileName) {
+        RawType data = getContentData(content);
+        if (data == null) {
+            logger.warn("Failed to get content data from '{}'", content);
+            return null;
+        }
 
-            logger.debug("Unpacked Response: {}", response);
+        logger.debug("Content Data: {}", data);
 
-            if ((null != response) && (null == response.getPushError())) {
-                result = true;
-            }
-        } catch (IOException e) {
-            logger.warn("IO problems pushing note: {}", e.getMessage());
+        int size = data.getBytes().length;
+        if (size > maxUploadSize) {
+            logger.warn("Content data size {} is greater than maximum upload size {}", size, maxUploadSize);
+            return null;
         }
 
-        return result;
+        try {
+            UploadRequest request = new UploadRequest();
+            request.setFileName(fileName != null ? fileName : getContentFileName(content));
+            request.setFileType(data.getMimeType());
+
+            logger.debug("Upload Request: {}", request);
+
+            UploadResponse response = httpClient.executeRequest(API_ENDPOINT_UPLOAD_REQUEST, request,
+                    UploadResponse.class);
+
+            String uploadUrl = response.getUploadUrl();
+            if (uploadUrl == null) {
+                throw new PushbulletApiException("Undefined upload url");
+            }
+
+            httpClient.uploadFile(uploadUrl, data);
+
+            return response;
+        } catch (PushbulletApiException e) {
+            return null;
+        }
     }
 
     /**
-     * helper method to populate the request headers
+     * Helper method to get the data for a given content
      *
-     * @param configuration
-     * @return
+     * @param content the file content
+     * @return the data raw type if available, otherwise null
      */
-    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());
-
-        logger.debug("Headers: {}", headers);
+    private @Nullable RawType getContentData(String content) {
+        try {
+            if (content.startsWith("data:")) {
+                return RawType.valueOf(content);
+            } else if (content.startsWith("http")) {
+                return HttpUtil.downloadImage(content);
+            } else {
+                Path path = Path.of(content);
+                byte[] bytes = Files.readAllBytes(path);
+                String mimeType = Files.probeContentType(path);
+                return new RawType(bytes, mimeType);
+            }
+        } catch (IllegalArgumentException | IOException e) {
+            logger.debug("Failed to get content data: {}", e.getMessage());
+            return null;
+        }
+    }
 
-        return headers;
+    /**
+     * Helper method to get the file name for a given content
+     *
+     * @param content the file content
+     * @return the file name if available, otherwise null
+     */
+    private @Nullable String getContentFileName(String content) {
+        if (content.startsWith("data:")) {
+            return IMAGE_FILE_NAME;
+        }
+        try {
+            Path fileName = Path.of(content.startsWith("http") ? new URL(content).getPath() : content).getFileName();
+            if (fileName != null) {
+                return fileName.toString();
+            }
+        } catch (MalformedURLException e) {
+            logger.debug("Malformed url content: {}", e.getMessage());
+        }
+        return null;
     }
 
     /**
-     * helper method to create a message body from data to be transferred.
+     * Helper method to create a push request
      *
-     * @param recipient
-     * @param title
-     * @param message
-     * @param type
+     * @param recipient the recipient
+     * @param title the title
+     * @param message the message
+     * @param type the push type
      *
-     * @return the message as a String to be posted
+     * @return the push request object
      */
-    private String prepareMessageBody(@Nullable String recipient, @Nullable String title, @Nullable String message,
-            String type) {
+    private PushRequest newPushRequest(@Nullable String recipient, @Nullable String title, @Nullable String message,
+            PushType type) {
         logger.debug("Recipient is '{}'", recipient);
         logger.debug("Title is     '{}'", title);
         logger.debug("Message is   '{}'", message);
+        logger.debug("Type is      '{}'", type);
 
-        Push push = new Push();
-        push.setTitle(title);
-        push.setBody(message);
-        push.setType(type);
+        PushRequest request = new PushRequest();
+        request.setTitle(title);
+        request.setBody(message);
+        request.setType(type);
 
         if (recipient != null) {
             if (isValidEmail(recipient)) {
                 logger.debug("Recipient is an email address");
-                push.setEmail(recipient);
+                request.setEmail(recipient);
             } else if (isValidChannel(recipient)) {
                 logger.debug("Recipient is a channel tag");
-                push.setChannel(recipient);
+                request.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);
-
         return request;
     }
 
     /**
-     * helper method checking if channel tag is valid.
+     * Helper method checking if channel tag is valid
      *
-     * @param channel
-     * @return
+     * @param channel the channel tag
+     * @return true if matches pattern
      */
     private static boolean isValidChannel(String channel) {
-        Matcher m = CHANNEL_PATTERN.matcher(channel);
-        return m.matches();
+        return CHANNEL_PATTERN.matcher(channel).matches();
     }
 
     /**
-     * helper method checking if email address is valid.
+     * Helper method checking if email address is valid
      *
-     * @param email
-     * @return
+     * @param email the email address
+     * @return true if parsed successfully
      */
     private static boolean isValidEmail(String email) {
         try {
-            InternetAddress emailAddr = new InternetAddress(email);
-            emailAddr.validate();
+            new InternetAddress(email, true);
             return true;
         } catch (AddressException e) {
             return false;
diff --git a/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/InstantDeserializer.java b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/InstantDeserializer.java
new file mode 100644 (file)
index 0000000..28d5ea0
--- /dev/null
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2010-2024 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 java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.time.Instant;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+
+/**
+ * The {@link InstantDeserializer} deserializes a timestamp returned by the API.
+ *
+ * @author Jeremy Setton - Initial contribution
+ */
+@NonNullByDefault
+public class InstantDeserializer implements JsonDeserializer<Instant> {
+
+    @Override
+    public @Nullable Instant deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) {
+        BigDecimal timestamp = json.getAsBigDecimal();
+        BigDecimal[] parts = timestamp.divideAndRemainder(BigDecimal.ONE);
+        long seconds = parts[0].longValueExact();
+        long nanos = parts[1].movePointRight(9).longValue();
+        return Instant.ofEpochSecond(seconds, nanos);
+    }
+}
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
deleted file mode 100644 (file)
index e65bf82..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/**
- * Copyright (c) 2010-2024 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
deleted file mode 100644 (file)
index a8c1504..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-/**
- * Copyright (c) 2010-2024 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/PushRequest.java b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/PushRequest.java
new file mode 100644 (file)
index 0000000..8c9df02
--- /dev/null
@@ -0,0 +1,128 @@
+/**
+ * Copyright (c) 2010-2024 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 org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+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
+ * @author Jeremy Setton - Add link and file push type support
+ */
+@NonNullByDefault
+public class PushRequest {
+
+    @SerializedName("type")
+    private @Nullable PushType type;
+
+    @SerializedName("title")
+    private @Nullable String title;
+
+    @SerializedName("body")
+    private @Nullable String body;
+
+    @SerializedName("url")
+    private @Nullable String url;
+
+    @SerializedName("file_name")
+    private @Nullable String fileName;
+
+    @SerializedName("file_type")
+    private @Nullable String fileType;
+
+    @SerializedName("file_url")
+    private @Nullable String fileUrl;
+
+    @SerializedName("source_device_iden")
+    private @Nullable String sourceDeviceIden;
+
+    @SerializedName("device_iden")
+    private @Nullable String deviceIden;
+
+    @SerializedName("client_iden")
+    private @Nullable String clientIden;
+
+    @SerializedName("channel_tag")
+    private @Nullable String channelTag;
+
+    @SerializedName("email")
+    private @Nullable String email;
+
+    @SerializedName("guid")
+    private @Nullable String guid;
+
+    public void setTitle(@Nullable String title) {
+        this.title = title;
+    }
+
+    public void setType(@Nullable PushType type) {
+        this.type = type;
+    }
+
+    public void setBody(@Nullable String body) {
+        this.body = body;
+    }
+
+    public void setUrl(@Nullable String url) {
+        this.url = url;
+    }
+
+    public void setFileName(@Nullable String fileName) {
+        this.fileName = fileName;
+    }
+
+    public void setFileType(@Nullable String fileType) {
+        this.fileType = fileType;
+    }
+
+    public void setFileUrl(@Nullable String fileUrl) {
+        this.fileUrl = fileUrl;
+    }
+
+    public void setSourceDeviceIden(@Nullable String sourceDeviceIden) {
+        this.sourceDeviceIden = sourceDeviceIden;
+    }
+
+    public void setDeviceIden(@Nullable String deviceIden) {
+        this.deviceIden = deviceIden;
+    }
+
+    public void setClientIden(@Nullable String clientIden) {
+        this.clientIden = clientIden;
+    }
+
+    public void setChannel(@Nullable String channelTag) {
+        this.channelTag = channelTag;
+    }
+
+    public void setEmail(@Nullable String email) {
+        this.email = email;
+    }
+
+    public void setGuid(@Nullable String guid) {
+        this.guid = guid;
+    }
+
+    @Override
+    public String toString() {
+        return "Push {type='" + type + "', title='" + title + "', body='" + body + "', url='" + url + "', fileName='"
+                + fileName + "', fileType='" + fileType + "', fileUrl='" + fileUrl + "', sourceDeviceIden='"
+                + sourceDeviceIden + "', deviceIden='" + deviceIden + "', clientIden='" + clientIden + "', channelTag='"
+                + channelTag + "', email='" + email + "', guid='" + guid + "'}";
+    }
+}
index 246980d5754b10085789e11e1a8b6d20f2b44d10..89e1279ccbad033a436670169510f7d89aef3e3d 100644 (file)
  */
 package org.openhab.binding.pushbullet.internal.model;
 
+import java.time.Instant;
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
 import com.google.gson.annotations.SerializedName;
 
 /**
- * This class represents the answer to pushes provided by the API.
+ * This class represents the push response received from the API.
  *
  * @author Hakan Tandogan - Initial contribution
  * @author Hakan Tandogan - Migrated from openHAB 1 action with the same name
+ * @author Jeremy Setton - Add link and file push type support
  */
+@NonNullByDefault
 public class PushResponse {
 
+    @SerializedName("iden")
+    private @Nullable String iden;
+
     @SerializedName("active")
-    private String active;
+    private @Nullable Boolean active;
 
-    @SerializedName("iden")
-    private String iden;
+    @SerializedName("created")
+    private @Nullable Instant created;
+
+    @SerializedName("modified")
+    private @Nullable Instant modified;
 
     @SerializedName("type")
-    private String type;
+    private @Nullable PushType type;
 
     @SerializedName("dismissed")
-    private Boolean dismissed;
+    private @Nullable Boolean dismissed;
+
+    @SerializedName("guid")
+    private @Nullable String guid;
 
     @SerializedName("direction")
-    private String direction;
+    private @Nullable String direction;
 
     @SerializedName("sender_iden")
-    private String senderIdentifier;
+    private @Nullable String senderIdentifier;
 
     @SerializedName("sender_email")
-    private String senderEmail;
+    private @Nullable String senderEmail;
 
     @SerializedName("sender_email_normalized")
-    private String senderEmailNormalized;
+    private @Nullable String senderEmailNormalized;
 
     @SerializedName("sender_name")
-    private String senderName;
+    private @Nullable String senderName;
 
     @SerializedName("receiver_iden")
-    private String receiverIdentifier;
+    private @Nullable String receiverIdentifier;
 
     @SerializedName("receiver_email")
-    private String receiverEmail;
+    private @Nullable String receiverEmail;
 
     @SerializedName("receiver_email_normalized")
-    private String receiverEmailNormalized;
+    private @Nullable String receiverEmailNormalized;
+
+    @SerializedName("target_device_iden")
+    private @Nullable String targetDeviceIden;
+
+    @SerializedName("source_device_iden")
+    private @Nullable String sourceDeviceIden;
+
+    @SerializedName("client_iden")
+    private @Nullable String clientIden;
+
+    @SerializedName("channel_iden")
+    private @Nullable String channelIden;
+
+    @SerializedName("awake_app_guids")
+    private @Nullable List<String> awakeAppGuids;
 
     @SerializedName("title")
-    private String title;
+    private @Nullable String title;
 
     @SerializedName("body")
-    private String body;
+    private @Nullable String body;
 
-    @SerializedName("error_code")
-    private String errorCode;
+    @SerializedName("url")
+    private @Nullable String url;
 
-    @SerializedName("error")
-    private PushError pushError;
+    @SerializedName("file_name")
+    private @Nullable String fileName;
 
-    public String getActive() {
-        return active;
-    }
+    @SerializedName("file_type")
+    private @Nullable String fileType;
 
-    public void setActive(String active) {
-        this.active = active;
-    }
+    @SerializedName("file_url")
+    private @Nullable String fileUrl;
+
+    @SerializedName("image_url")
+    private @Nullable String imageUrl;
 
-    public String getIden() {
+    @SerializedName("image_width")
+    private @Nullable Integer imageWidth;
+
+    @SerializedName("image_height")
+    private @Nullable Integer imageHeight;
+
+    public @Nullable String getIden() {
         return iden;
     }
 
-    public void setIden(String iden) {
-        this.iden = iden;
+    public @Nullable Boolean getActive() {
+        return active;
     }
 
-    public String getType() {
-        return type;
+    public @Nullable Instant getCreated() {
+        return created;
     }
 
-    public void setType(String type) {
-        this.type = type;
+    public @Nullable Instant getModified() {
+        return modified;
     }
 
-    public Boolean getDismissed() {
-        return dismissed;
+    public @Nullable PushType getType() {
+        return type;
     }
 
-    public void setDismissed(Boolean dismissed) {
-        this.dismissed = dismissed;
+    public @Nullable Boolean getDismissed() {
+        return dismissed;
     }
 
-    public String getDirection() {
-        return direction;
+    public @Nullable String getGuid() {
+        return guid;
     }
 
-    public void setDirection(String direction) {
-        this.direction = direction;
+    public @Nullable String getDirection() {
+        return direction;
     }
 
-    public String getSenderIdentifier() {
+    public @Nullable String getSenderIdentifier() {
         return senderIdentifier;
     }
 
-    public void setSenderIdentifier(String senderIdentifier) {
-        this.senderIdentifier = senderIdentifier;
-    }
-
-    public String getSenderEmail() {
+    public @Nullable String getSenderEmail() {
         return senderEmail;
     }
 
-    public void setSenderEmail(String senderEmail) {
-        this.senderEmail = senderEmail;
-    }
-
-    public String getSenderEmailNormalized() {
+    public @Nullable String getSenderEmailNormalized() {
         return senderEmailNormalized;
     }
 
-    public void setSenderEmailNormalized(String senderEmailNormalized) {
-        this.senderEmailNormalized = senderEmailNormalized;
+    public @Nullable String getSenderName() {
+        return senderName;
     }
 
-    public String getSenderName() {
-        return senderName;
+    public @Nullable String getReceiverIdentifier() {
+        return receiverIdentifier;
     }
 
-    public void setSenderName(String senderName) {
-        this.senderName = senderName;
+    public @Nullable String getReceiverEmail() {
+        return receiverEmail;
     }
 
-    public String getReceiverIdentifier() {
-        return receiverIdentifier;
+    public @Nullable String getReceiverEmailNormalized() {
+        return receiverEmailNormalized;
     }
 
-    public void setReceiverIdentifier(String receiverIdentifier) {
-        this.receiverIdentifier = receiverIdentifier;
+    public @Nullable String getTargetDeviceIden() {
+        return targetDeviceIden;
     }
 
-    public String getReceiverEmail() {
-        return receiverEmail;
+    public @Nullable String getSourceDeviceIden() {
+        return sourceDeviceIden;
     }
 
-    public void setReceiverEmail(String receiverEmail) {
-        this.receiverEmail = receiverEmail;
+    public @Nullable String getClientIden() {
+        return clientIden;
     }
 
-    public String getReceiverEmailNormalized() {
-        return receiverEmailNormalized;
+    public @Nullable String getChannelIden() {
+        return channelIden;
     }
 
-    public void setReceiverEmailNormalized(String receiverEmailNormalized) {
-        this.receiverEmailNormalized = receiverEmailNormalized;
+    public @Nullable List<String> getAwakeAppGuids() {
+        return awakeAppGuids;
     }
 
-    public String getTitle() {
+    public @Nullable String getTitle() {
         return title;
     }
 
-    public void setTitle(String title) {
-        this.title = title;
+    public @Nullable String getBody() {
+        return body;
     }
 
-    public String getBody() {
-        return body;
+    public @Nullable String getUrl() {
+        return url;
+    }
+
+    public @Nullable String getFileName() {
+        return fileName;
     }
 
-    public void setBody(String body) {
-        this.body = body;
+    public @Nullable String getFileType() {
+        return fileType;
     }
 
-    public String getErrorCode() {
-        return errorCode;
+    public @Nullable String getFileUrl() {
+        return fileUrl;
     }
 
-    public void setErrorCode(String errorCode) {
-        this.errorCode = errorCode;
+    public @Nullable String getImageUrl() {
+        return imageUrl;
     }
 
-    public PushError getPushError() {
-        return pushError;
+    public @Nullable Integer getImageWidth() {
+        return imageWidth;
     }
 
-    public void setPushError(PushError pushError) {
-        this.pushError = pushError;
+    public @Nullable Integer getImageHeight() {
+        return imageHeight;
     }
 
     @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 + '}';
+        return "PushResponse {iden='" + iden + ", active='" + active + "', created='" + created + "', modified='"
+                + modified + "', type='" + type + "', dismissed='" + dismissed + "', guid='" + guid + "', direction='"
+                + direction + "', senderIdentifier='" + senderIdentifier + "', senderEmail='" + senderEmail
+                + "', senderEmailNormalized='" + senderEmailNormalized + "', senderName='" + senderName
+                + "', receiverIdentifier='" + receiverIdentifier + "', receiverEmail='" + receiverEmail
+                + "', receiverEmailNormalized='" + receiverEmailNormalized + "', targetDeviceIden='" + targetDeviceIden
+                + "', sourceDeviceIden='" + sourceDeviceIden + "', clientIden='" + clientIden + "', channelIden='"
+                + channelIden + "', awakeAppGuids='" + awakeAppGuids + "', title='" + title + "', body='" + body
+                + "', url='" + url + "', fileName='" + fileName + "', fileType='" + fileType + "', fileUrl='" + fileUrl
+                + "', imageUrl='" + imageUrl + "', imageWidth='" + imageWidth + "', imageHeight='" + imageHeight + "'}";
     }
 }
diff --git a/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/PushType.java b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/PushType.java
new file mode 100644 (file)
index 0000000..cc1f194
--- /dev/null
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2010-2024 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 org.eclipse.jdt.annotation.NonNullByDefault;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * This class represents the push type.
+ *
+ * @author Jeremy Setton - Initial contribution
+ */
+@NonNullByDefault
+public enum PushType {
+    @SerializedName("note")
+    NOTE,
+    @SerializedName("link")
+    LINK,
+    @SerializedName("file")
+    FILE
+}
diff --git a/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/UploadRequest.java b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/UploadRequest.java
new file mode 100644 (file)
index 0000000..937ced4
--- /dev/null
@@ -0,0 +1,46 @@
+/**
+ * Copyright (c) 2010-2024 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 org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * This class represents the upload request sent to the API.
+ *
+ * @author Jeremy Setton - Initial contribution
+ */
+@NonNullByDefault
+public class UploadRequest {
+
+    @SerializedName("file_name")
+    private @Nullable String fileName;
+
+    @SerializedName("file_type")
+    private @Nullable String fileType;
+
+    public void setFileName(@Nullable String fileName) {
+        this.fileName = fileName;
+    }
+
+    public void setFileType(@Nullable String fileType) {
+        this.fileType = fileType;
+    }
+
+    @Override
+    public String toString() {
+        return "UploadRequest {fileName='" + fileName + "', fileType='" + fileType + "'}";
+    }
+}
diff --git a/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/UploadResponse.java b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/UploadResponse.java
new file mode 100644 (file)
index 0000000..392222f
--- /dev/null
@@ -0,0 +1,61 @@
+/**
+ * Copyright (c) 2010-2024 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 org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * This class represents the upload response received from the API.
+ *
+ * @author Jeremy Setton - Initial contribution
+ */
+@NonNullByDefault
+public class UploadResponse {
+
+    @SerializedName("file_name")
+    private @Nullable String fileName;
+
+    @SerializedName("file_type")
+    private @Nullable String fileType;
+
+    @SerializedName("file_url")
+    private @Nullable String fileUrl;
+
+    @SerializedName("upload_url")
+    private @Nullable String uploadUrl;
+
+    public @Nullable String getFileName() {
+        return fileName;
+    }
+
+    public @Nullable String getFileType() {
+        return fileType;
+    }
+
+    public @Nullable String getFileUrl() {
+        return fileUrl;
+    }
+
+    public @Nullable String getUploadUrl() {
+        return uploadUrl;
+    }
+
+    @Override
+    public String toString() {
+        return "UploadResponse {fileName='" + fileName + "', fileType='" + fileType + "', fileUrl='" + fileUrl
+                + "', uploadUrl='" + uploadUrl + "'}";
+    }
+}
diff --git a/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/User.java b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/User.java
new file mode 100644 (file)
index 0000000..5657c4d
--- /dev/null
@@ -0,0 +1,114 @@
+/**
+ * Copyright (c) 2010-2024 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 java.time.Instant;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * This class represents the user object received from the API.
+ *
+ * @author Jeremy Setton - Initial contribution
+ */
+@NonNullByDefault
+public class User {
+
+    @SerializedName("iden")
+    private @Nullable String iden;
+
+    @SerializedName("active")
+    private @Nullable Boolean active;
+
+    @SerializedName("created")
+    private @Nullable Instant created;
+
+    @SerializedName("modified")
+    private @Nullable Instant modified;
+
+    @SerializedName("email")
+    private @Nullable String email;
+
+    @SerializedName("email_normalized")
+    private @Nullable String emailNormalized;
+
+    @SerializedName("name")
+    private @Nullable String name;
+
+    @SerializedName("image_url")
+    private @Nullable String imageUrl;
+
+    @SerializedName("max_upload_size")
+    private @Nullable Integer maxUploadSize;
+
+    @SerializedName("referred_count")
+    private @Nullable Integer referredCount;
+
+    @SerializedName("referrer_iden")
+    private @Nullable String referrerIden;
+
+    public @Nullable String getIden() {
+        return iden;
+    }
+
+    public @Nullable Boolean getActive() {
+        return active;
+    }
+
+    public @Nullable Instant getCreated() {
+        return created;
+    }
+
+    public @Nullable Instant getModified() {
+        return modified;
+    }
+
+    public @Nullable String getEmail() {
+        return email;
+    }
+
+    public @Nullable String getEmailNormalized() {
+        return emailNormalized;
+    }
+
+    public @Nullable String getName() {
+        return name;
+    }
+
+    public @Nullable String getImageUrl() {
+        return imageUrl;
+    }
+
+    public @Nullable Integer getMaxUploadSize() {
+        return maxUploadSize;
+    }
+
+    public @Nullable Integer getReferredCount() {
+        return referredCount;
+    }
+
+    public @Nullable String getReferrerIden() {
+        return referrerIden;
+    }
+
+    @Override
+    public String toString() {
+        return "User {iden='" + iden + ", active='" + active + "', created='" + created + "', modified='" + modified
+                + "', email='" + email + "', emailNormalized='" + emailNormalized + "', name='" + name + "', imageUrl='"
+                + imageUrl + "', maxUploadSize='" + maxUploadSize + "', referredCount='" + referredCount
+                + "', referrerIden='" + referrerIden + "'}";
+    }
+}
index 52716087611ed8be6011726a08a8d912449eced7..0781854439e6d43a7fad1c27d12e08244d4961bc 100644 (file)
@@ -4,8 +4,8 @@
        xsi:schemaLocation="https://openhab.org/schemas/addon/v1.0.0 https://openhab.org/schemas/addon-1.0.0.xsd">
 
        <type>binding</type>
-       <name>@text/addon.pushbullet.name</name>
-       <description>@text/addon.pushbullet.description</description>
+       <name>Pushbullet Binding</name>
+       <description>The Pushbullet binding allows you to send messages to other users of the Pushbullet service.</description>
        <connection>cloud</connection>
 
 </addon:addon>
index 4b0ddc0593a27a60fb8f5119a16843b15bf1a4f4..beb771ae5f40e1f8c9e5e45a888ee5ea42a9891a 100644 (file)
@@ -2,9 +2,33 @@
 addon.pushbullet.name = Pushbullet Binding
 addon.pushbullet.description = The Pushbullet binding allows you to send messages to other users of the Pushbullet service.
 
+# thing types
+thing-type.pushbullet.bot.label = Pushbullet Bot
+thing-type.pushbullet.bot.description = Bot to send messages with.
+
+# thing types config
+thing-type.config.pushbullet.bot.name.label = Name
+thing-type.config.pushbullet.bot.name.description = Explicit Name of Bot, if wanted
+thing-type.config.pushbullet.bot.token.label = Access Token
+thing-type.config.pushbullet.bot.token.description = Access token obtained from the account settings page
+thing-type.config.pushbullet.bot.apiUrlBase.label = API Server
+thing-type.config.pushbullet.bot.apiUrlBase.description = The Pushbullet API Server to use, for local testing
+
+# channel types
+channel-type.pushbullet.recipient-channel.label = Recipient
+channel-type.pushbullet.recipient-channel.description = Mail address or Channel Name
+channel-type.pushbullet.title-channel.label = Title
+channel-type.pushbullet.title-channel.description = Title of the message
+channel-type.pushbullet.message-channel.label = Message
+channel-type.pushbullet.message-channel.description = The text that is to be sent
+
 # action
 actionSendPushbulletNoteLabel = publish a Pushbullet message
-actionSendPushbulletNoteDesc = Publishes a Title to the given Pushbullet Recipient.
+actionSendPushbulletNoteDesc = Publishes a note to the given Pushbullet Recipient.
+actionSendPushbulletLinkLabel = publish a Pushbullet message with link
+actionSendPushbulletLinkDesc = Publishes a link to the given Pushbullet Recipient.
+actionSendPushbulletFileLabel = publish a Pushbullet message with file
+actionSendPushbulletFileDesc = Publishes a file to the given Pushbullet Recipient.
 
 actionSendPushbulletNoteInputRecipientLabel = Pushbullet Recipient
 actionSendPushbulletNoteInputRecipientDesc = The Recipient to publish a Title to.
@@ -12,6 +36,12 @@ actionSendPushbulletNoteInputTitleLabel = Title
 actionSendPushbulletNoteInputTitleDesc = The Title to publish
 actionSendPushbulletNoteInputMessageLabel = Message
 actionSendPushbulletNoteInputMessageDesc = The Message to publish
+actionSendPushbulletLinkInputUrlLabel = Link URL
+actionSendPushbulletLinkInputUrlDesc = The Link URL to publish
+actionSendPushbulletFileInputContent = File Content
+actionSendPushbulletFileInputContentDesc = The File Content to publish
+actionSendPushbulletFileInputName = File Name
+actionSendPushbulletFileInputNameDesc = The File Name to publish
 
 # error texts
 offline.conf-error-httpresponseexception = The pushbullet server reported an error, possibly an expired token. Check on web site
index 1dfc83e70abc2676c04ee21596d6f6fbc83d971d..9e5e31ef0d50fec675836a030d71b7fa173017e5 100644 (file)
@@ -22,8 +22,8 @@
                        </parameter>
 
                        <parameter name="token" type="text" required="true">
-                               <label>Token</label>
-                               <description>Token as obtained from the server</description>
+                               <label>Access Token</label>
+                               <description>Access token obtained from the account settings page</description>
                        </parameter>
 
                        <parameter name="apiUrlBase" type="text" required="true">