diff --git a/bundles/org.openhab.binding.pushover/README.md b/bundles/org.openhab.binding.pushover/README.md index 4eb52af55a0b9..8b2e3e66c5f79 100644 --- a/bundles/org.openhab.binding.pushover/README.md +++ b/bundles/org.openhab.binding.pushover/README.md @@ -42,10 +42,14 @@ The parameter `message` is **mandatory**, the `title` parameter defaults to what Parameters declared as `@Nullable` are not optional. One has to pass a `null` value if it should be skipped or the default value for it should be used. -- `sendMessage(String message, @Nullable String title, @Nullable String sound, @Nullable String url, @Nullable String urlTitle, @Nullable String attachment, @Nullable String contentType, @Nullable Integer priority, @Nullable String device)` - This method is used to send a plain text message providing all available parameters. +- `sendMessage(String message, @Nullable String title, @Nullable String sound, @Nullable String url, @Nullable String urlTitle, @Nullable String attachment, @Nullable String contentType, @Nullable Integer priority, @Nullable String device, @Nullable Duration ttl)` - This method is used to send a plain text message providing all available parameters. + +- `sendMessage(String message)` - This method is used to send a plain text message with default title. - `sendMessage(String message, @Nullable String title)` - This method is used to send a plain text message. +- `sendMessage(String message, @Nullable String title, @Nullable Duration ttl)` - This method is used to send a plain text message with TTL. + - `sendHtmlMessage(String message, @Nullable String title)` - This method is used to send a HTML message. - `sendMonospaceMessage(String message, @Nullable String title)` - This method is used to send a monospace message. @@ -102,3 +106,27 @@ if( receipt !== null ) { receipt = null } ``` + +:::: tabs + +::: tab DSL + +```java +val actions = getActions("pushover", "pushover:pushover-account:account") +// send expiring message +actions.sendMessage("Boost has been activated", "Recuperator", Duration.ofHours(1)) +``` + +::: + +::: tab JavaScript + +```javascript +var pushoverActions = actions.thingActions('pushover', 'pushover:pushover-account:account'); +// send expiring message +pushoverActions.sendMessage("Boost has been activated", "Recuperator", time.Duration.ofHours(1)); +``` + +::: + +:::: diff --git a/bundles/org.openhab.binding.pushover/src/main/java/org/openhab/binding/pushover/internal/actions/PushoverActions.java b/bundles/org.openhab.binding.pushover/src/main/java/org/openhab/binding/pushover/internal/actions/PushoverActions.java index 133c09c300400..3c95b7d8ec123 100644 --- a/bundles/org.openhab.binding.pushover/src/main/java/org/openhab/binding/pushover/internal/actions/PushoverActions.java +++ b/bundles/org.openhab.binding.pushover/src/main/java/org/openhab/binding/pushover/internal/actions/PushoverActions.java @@ -15,6 +15,8 @@ import static org.openhab.binding.pushover.internal.PushoverBindingConstants.*; import static org.openhab.binding.pushover.internal.connection.PushoverMessageBuilder.*; +import java.time.Duration; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.pushover.internal.connection.PushoverMessageBuilder; @@ -32,6 +34,7 @@ * Some automation actions to be used with a {@link PushoverAccountHandler}. * * @author Christoph Weitkamp - Initial contribution + * @author Jacob Laursen - Added support for Expiring Messages */ @ThingActionsScope(name = "pushover") @NonNullByDefault @@ -53,7 +56,8 @@ public class PushoverActions implements ThingActions { @ActionInput(name = "attachment", label = "@text/sendMessageActionInputAttachmentLabel", description = "@text/sendMessageActionInputAttachmentDescription", type = "java.lang.String") @Nullable String attachment, @ActionInput(name = "contentType", label = "@text/sendMessageActionInputContentTypeLabel", description = "@text/sendMessageActionInputContentTypeDescription", type = "java.lang.String", defaultValue = DEFAULT_CONTENT_TYPE) @Nullable String contentType, @ActionInput(name = "priority", label = "@text/sendMessageActionInputPriorityLabel", description = "@text/sendMessageActionInputPriorityDescription", type = "java.lang.Integer", defaultValue = DEFAULT_EMERGENCY_PRIORITY) @Nullable Integer priority, - @ActionInput(name = "device", label = "@text/sendMessageActionInputDeviceLabel", description = "@text/sendMessageActionInputDeviceDescription", type = "java.lang.String") @Nullable String device) { + @ActionInput(name = "device", label = "@text/sendMessageActionInputDeviceLabel", description = "@text/sendMessageActionInputDeviceDescription", type = "java.lang.String") @Nullable String device, + @ActionInput(name = "ttl", label = "@text/sendMessageActionInputTTLLabel", description = "@text/sendMessageActionInputTTLDescription", type = "java.time.Duration") @Nullable Duration ttl) { logger.trace( "ThingAction 'sendMessage' called with value(s): message='{}', title='{}', sound='{}', url='{}', urlTitle='{}', attachment='{}', contentType='{}', priority='{}', device='{}'", message, title, sound, url, urlTitle, attachment, contentType, priority, device); @@ -81,14 +85,28 @@ public class PushoverActions implements ThingActions { if (device != null) { builder.withDevice(device); } + if (ttl != null) { + builder.withTTL(ttl); + } return send(builder, title); } public static Boolean sendMessage(ThingActions actions, String message, @Nullable String title, @Nullable String sound, @Nullable String url, @Nullable String urlTitle, @Nullable String attachment, - @Nullable String contentType, @Nullable Integer priority, @Nullable String device) { + @Nullable String contentType, @Nullable Integer priority, @Nullable String device, @Nullable Duration ttl) { return ((PushoverActions) actions).sendMessage(message, title, sound, url, urlTitle, attachment, contentType, - priority, device); + priority, device, ttl); + } + + @RuleAction(label = "@text/sendMessageActionLabel", description = "@text/sendMessageActionDescription") + public @ActionOutput(name = "sent", label = "@text/sendMessageActionOutputLabel", description = "@text/sendMessageActionOutputDescription", type = "java.lang.Boolean") Boolean sendMessage( + @ActionInput(name = "message", label = "@text/sendMessageActionInputMessageLabel", description = "@text/sendMessageActionInputMessageDescription", type = "java.lang.String", required = true) String message) { + logger.trace("ThingAction 'sendMessage' called with value(s): message='{}'", message); + return send(getDefaultPushoverMessageBuilder(message), null); + } + + public static Boolean sendMessage(ThingActions actions, String message) { + return ((PushoverActions) actions).sendMessage(message); } @RuleAction(label = "@text/sendMessageActionLabel", description = "@text/sendMessageActionDescription") @@ -103,11 +121,30 @@ public static Boolean sendMessage(ThingActions actions, String message, @Nullabl return ((PushoverActions) actions).sendMessage(message, title); } + @RuleAction(label = "@text/sendMessageActionLabel", description = "@text/sendMessageActionDescription") + public @ActionOutput(name = "sent", label = "@text/sendMessageActionOutputLabel", description = "@text/sendMessageActionOutputDescription", type = "java.lang.Boolean") Boolean sendMessage( + @ActionInput(name = "message", label = "@text/sendMessageActionInputMessageLabel", description = "@text/sendMessageActionInputMessageDescription", type = "java.lang.String", required = true) String message, + @ActionInput(name = "title", label = "@text/sendMessageActionInputTitleLabel", description = "@text/sendMessageActionInputTitleDescription", type = "java.lang.String", defaultValue = DEFAULT_TITLE) @Nullable String title, + @ActionInput(name = "ttl", label = "@text/sendMessageActionInputTTLLabel", description = "@text/sendMessageActionInputTTLDescription", type = "java.time.Duration") @Nullable Duration ttl) { + logger.trace("ThingAction 'sendMessage' called with value(s): message='{}', title='{}', ttl='{}'", message, + title, ttl); + PushoverMessageBuilder builder = getDefaultPushoverMessageBuilder(message); + if (ttl != null) { + builder.withTTL(ttl); + } + return send(builder, title); + } + + public static Boolean sendMessage(ThingActions actions, String message, @Nullable String title, + @Nullable Duration ttl) { + return ((PushoverActions) actions).sendMessage(message, title, ttl); + } + @RuleAction(label = "@text/sendURLMessageActionLabel", description = "@text/sendURLMessageActionDescription") public @ActionOutput(name = "sent", label = "@text/sendMessageActionOutputLabel", description = "@text/sendMessageActionOutputDescription", type = "java.lang.Boolean") Boolean sendURLMessage( @ActionInput(name = "message", label = "@text/sendMessageActionInputMessageLabel", description = "@text/sendMessageActionInputMessageDescription", type = "java.lang.String", required = true) String message, @ActionInput(name = "title", label = "@text/sendMessageActionInputTitleLabel", description = "@text/sendMessageActionInputTitleDescription", type = "java.lang.String", defaultValue = DEFAULT_TITLE) @Nullable String title, - @ActionInput(name = "url", label = "@text/sendMessageActionInputURLLabel", description = "@text/sendMessageActionInputURLDescription", type = "java.lang.String", required = true) String url, + @ActionInput(name = "url", label = "@text/sendMessageActionInputURLLabel", description = "@text/sendMessageActionInputURLDescription", type = "java.lang.String", required = true) @Nullable String url, @ActionInput(name = "urlTitle", label = "@text/sendMessageActionInputURLTitleLabel", description = "@text/sendMessageActionInputURLTitleDescription", type = "java.lang.String") @Nullable String urlTitle) { logger.trace( "ThingAction 'sendURLMessage' called with value(s): message='{}', url='{}', title='{}', urlTitle='{}'", @@ -157,7 +194,7 @@ public static Boolean sendMonospaceMessage(ThingActions actions, String message, public @ActionOutput(name = "sent", label = "@text/sendMessageActionOutputLabel", description = "@text/sendMessageActionOutputDescription", type = "java.lang.Boolean") Boolean sendAttachmentMessage( @ActionInput(name = "message", label = "@text/sendMessageActionInputMessageLabel", description = "@text/sendMessageActionInputMessageDescription", type = "java.lang.String", required = true) String message, @ActionInput(name = "title", label = "@text/sendMessageActionInputTitleLabel", description = "@text/sendMessageActionInputTitleDescription", type = "java.lang.String", defaultValue = DEFAULT_TITLE) @Nullable String title, - @ActionInput(name = "attachment", label = "@text/sendMessageActionInputAttachmentLabel", description = "@text/sendMessageActionInputAttachmentDescription", type = "java.lang.String", required = true) String attachment, + @ActionInput(name = "attachment", label = "@text/sendMessageActionInputAttachmentLabel", description = "@text/sendMessageActionInputAttachmentDescription", type = "java.lang.String", required = true) @Nullable String attachment, @ActionInput(name = "contentType", label = "@text/sendMessageActionInputContentTypeLabel", description = "@text/sendMessageActionInputContentTypeDescription", type = "java.lang.String", defaultValue = DEFAULT_CONTENT_TYPE) @Nullable String contentType) { logger.trace( "ThingAction 'sendAttachmentMessage' called with value(s): message='{}', title='{}', attachment='{}', contentType='{}'", @@ -201,10 +238,10 @@ public static String sendPriorityMessage(ThingActions actions, String message, @ @RuleAction(label = "@text/cancelPriorityMessageActionLabel", description = "@text/cancelPriorityMessageActionDescription") public @ActionOutput(name = "canceled", label = "@text/cancelPriorityMessageActionOutputLabel", description = "@text/cancelPriorityMessageActionOutputDescription", type = "java.lang.Boolean") Boolean cancelPriorityMessage( - @ActionInput(name = "receipt", label = "@text/cancelPriorityMessageActionInputReceiptLabel", description = "@text/cancelPriorityMessageActionInputReceiptDescription", type = "java.lang.String", required = true) String receipt) { + @ActionInput(name = "receipt", label = "@text/cancelPriorityMessageActionInputReceiptLabel", description = "@text/cancelPriorityMessageActionInputReceiptDescription", type = "java.lang.String", required = true) @Nullable String receipt) { logger.trace("ThingAction 'cancelPriorityMessage' called with value(s): '{}'", receipt); if (accountHandler == null) { - throw new RuntimeException("PushoverAccountHandler is null!"); + throw new IllegalStateException("PushoverAccountHandler is null!"); } if (receipt == null) { @@ -220,7 +257,7 @@ public static Boolean cancelPriorityMessage(ThingActions actions, String receipt @RuleAction(label = "@text/sendMessageToDeviceActionLabel", description = "@text/sendMessageToDeviceActionDescription") public @ActionOutput(name = "sent", label = "@text/sendMessageActionOutputLabel", description = "@text/sendMessageActionOutputDescription", type = "java.lang.Boolean") Boolean sendMessageToDevice( - @ActionInput(name = "device", label = "@text/sendMessageActionInputDeviceLabel", description = "@text/sendMessageActionInputDeviceDescription", type = "java.lang.String", required = true) String device, + @ActionInput(name = "device", label = "@text/sendMessageActionInputDeviceLabel", description = "@text/sendMessageActionInputDeviceDescription", type = "java.lang.String", required = true) @Nullable String device, @ActionInput(name = "message", label = "@text/sendMessageActionInputMessageLabel", description = "@text/sendMessageActionInputMessageDescription", type = "java.lang.String", required = true) String message, @ActionInput(name = "title", label = "@text/sendMessageActionInputTitleLabel", description = "@text/sendMessageActionInputTitleDescription", type = "java.lang.String", defaultValue = DEFAULT_TITLE) @Nullable String title) { logger.trace("ThingAction 'sendMessageToDevice' called with value(s): device='{}', message='{}', title='{}'", @@ -237,9 +274,9 @@ public static Boolean sendMessageToDevice(ThingActions actions, String device, S return ((PushoverActions) actions).sendMessageToDevice(device, message, title); } - private PushoverMessageBuilder getDefaultPushoverMessageBuilder(String message) { + private PushoverMessageBuilder getDefaultPushoverMessageBuilder(@Nullable String message) { if (accountHandler == null) { - throw new RuntimeException("PushoverAccountHandler is null!"); + throw new IllegalStateException("PushoverAccountHandler is null!"); } if (message == null) { diff --git a/bundles/org.openhab.binding.pushover/src/main/java/org/openhab/binding/pushover/internal/connection/PushoverMessageBuilder.java b/bundles/org.openhab.binding.pushover/src/main/java/org/openhab/binding/pushover/internal/connection/PushoverMessageBuilder.java index 49e0b0845e533..a6cf9b6772a82 100644 --- a/bundles/org.openhab.binding.pushover/src/main/java/org/openhab/binding/pushover/internal/connection/PushoverMessageBuilder.java +++ b/bundles/org.openhab.binding.pushover/src/main/java/org/openhab/binding/pushover/internal/connection/PushoverMessageBuilder.java @@ -18,6 +18,7 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.time.Duration; import java.util.List; import java.util.stream.Collectors; @@ -52,6 +53,7 @@ public class PushoverMessageBuilder { private static final String MESSAGE_KEY_PRIORITY = "priority"; private static final String MESSAGE_KEY_RETRY = "retry"; private static final String MESSAGE_KEY_EXPIRE = "expire"; + private static final String MESSAGE_KEY_TTL = "ttl"; private static final String MESSAGE_KEY_URL = "url"; private static final String MESSAGE_KEY_URL_TITLE = "url_title"; private static final String MESSAGE_KEY_SOUND = "sound"; @@ -79,6 +81,7 @@ public class PushoverMessageBuilder { private int priority = DEFAULT_PRIORITY; private int retry = 300; private int expire = 3600; + private Duration ttl = Duration.ZERO; private @Nullable String url; private @Nullable String urlTitle; private @Nullable String sound; @@ -135,6 +138,11 @@ public PushoverMessageBuilder withExpire(int expire) { return this; } + public PushoverMessageBuilder withTTL(Duration ttl) { + this.ttl = ttl; + return this; + } + public PushoverMessageBuilder withUrl(String url) { this.url = url; return this; @@ -171,6 +179,7 @@ public PushoverMessageBuilder withMonospaceFormatting() { } public ContentProvider build() throws CommunicationException { + String message = this.message; if (message != null) { if (message.length() > MAX_MESSAGE_LENGTH) { throw new IllegalArgumentException(String.format( @@ -179,6 +188,7 @@ public ContentProvider build() throws CommunicationException { body.addFieldPart(MESSAGE_KEY_MESSAGE, new StringContentProvider(message), null); } + String title = this.title; if (title != null) { if (title.length() > MAX_TITLE_LENGTH) { throw new IllegalArgumentException(String @@ -187,6 +197,7 @@ public ContentProvider build() throws CommunicationException { body.addFieldPart(MESSAGE_KEY_TITLE, new StringContentProvider(title), null); } + String device = this.device; if (device != null) { if (device.length() > MAX_DEVICE_LENGTH) { logger.warn("Skip 'device' as it is longer than {} characters. Got: {}.", MAX_DEVICE_LENGTH, device); @@ -224,6 +235,14 @@ public ContentProvider build() throws CommunicationException { } } + if (!ttl.isZero()) { + if (priority == EMERGENCY_PRIORITY) { + logger.warn("TTL value of {} ignored for emergency priority.", ttl); + } + body.addFieldPart(MESSAGE_KEY_TTL, new StringContentProvider(String.valueOf(ttl.getSeconds())), null); + } + + String url = this.url; if (url != null) { if (url.length() > MAX_URL_LENGTH) { throw new IllegalArgumentException(String @@ -231,6 +250,7 @@ public ContentProvider build() throws CommunicationException { } body.addFieldPart(MESSAGE_KEY_URL, new StringContentProvider(url), null); + String urlTitle = this.urlTitle; if (urlTitle != null) { if (urlTitle.length() > MAX_URL_TITLE_LENGTH) { throw new IllegalArgumentException( @@ -245,9 +265,9 @@ public ContentProvider build() throws CommunicationException { body.addFieldPart(MESSAGE_KEY_SOUND, new StringContentProvider(sound), null); } + String attachment = this.attachment; if (attachment != null) { - String localAttachment = attachment; - if (localAttachment.startsWith("http")) { // support data HTTP(S) scheme + if (attachment.startsWith("http")) { // support data HTTP(S) scheme RawType rawImage = HttpUtil.downloadImage(attachment, 10000); if (rawImage == null) { throw new IllegalArgumentException( @@ -255,9 +275,9 @@ public ContentProvider build() throws CommunicationException { } addFilePart(createTempFile(rawImage.getBytes()), contentType == null ? rawImage.getMimeType() : contentType); - } else if (localAttachment.startsWith("data:")) { // support data URI scheme + } else if (attachment.startsWith("data:")) { // support data URI scheme try { - RawType rawImage = RawType.valueOf(localAttachment); + RawType rawImage = RawType.valueOf(attachment); addFilePart(createTempFile(rawImage.getBytes()), contentType == null ? rawImage.getMimeType() : contentType); } catch (IllegalArgumentException e) { diff --git a/bundles/org.openhab.binding.pushover/src/main/java/org/openhab/binding/pushover/internal/handler/PushoverAccountHandler.java b/bundles/org.openhab.binding.pushover/src/main/java/org/openhab/binding/pushover/internal/handler/PushoverAccountHandler.java index 4630376f54622..aa77cb9e39df7 100644 --- a/bundles/org.openhab.binding.pushover/src/main/java/org/openhab/binding/pushover/internal/handler/PushoverAccountHandler.java +++ b/bundles/org.openhab.binding.pushover/src/main/java/org/openhab/binding/pushover/internal/handler/PushoverAccountHandler.java @@ -101,6 +101,7 @@ public Collection> getServices() { */ public List getSounds() { try { + PushoverAPIConnection connection = this.connection; if (connection != null) { List sounds = connection.getSounds(); if (sounds != null) { @@ -145,6 +146,7 @@ public PushoverMessageBuilder getDefaultPushoverMessageBuilder(String message) { } public boolean sendMessage(PushoverMessageBuilder messageBuilder) { + PushoverAPIConnection connection = this.connection; if (connection != null) { try { return connection.sendMessage(messageBuilder); @@ -160,6 +162,7 @@ public boolean sendMessage(PushoverMessageBuilder messageBuilder) { } public String sendPriorityMessage(PushoverMessageBuilder messageBuilder) { + PushoverAPIConnection connection = this.connection; if (connection != null) { try { return connection.sendPriorityMessage(messageBuilder); @@ -175,6 +178,7 @@ public String sendPriorityMessage(PushoverMessageBuilder messageBuilder) { } public boolean cancelPriorityMessage(String receipt) { + PushoverAPIConnection connection = this.connection; if (connection != null) { try { return connection.cancelPriorityMessage(receipt); diff --git a/bundles/org.openhab.binding.pushover/src/main/resources/OH-INF/i18n/pushover.properties b/bundles/org.openhab.binding.pushover/src/main/resources/OH-INF/i18n/pushover.properties index d570bb1c6950a..be47aaa7cdb74 100644 --- a/bundles/org.openhab.binding.pushover/src/main/resources/OH-INF/i18n/pushover.properties +++ b/bundles/org.openhab.binding.pushover/src/main/resources/OH-INF/i18n/pushover.properties @@ -64,6 +64,8 @@ sendMessageActionInputMessageLabel = Message sendMessageActionInputMessageDescription = Message to be sent. sendMessageActionInputTitleLabel = Title sendMessageActionInputTitleDescription = The title of the message. +sendMessageActionInputTTLLabel = Time to Live +sendMessageActionInputTTLDescription = The Time to Live of the message. sendMessageToDeviceActionLabel = send a plain text message to a specific device sendMessageToDeviceActionDescription = This method is used to send a message to a specific device. sendMessageActionInputDeviceLabel = Device diff --git a/bundles/org.openhab.binding.pushover/src/test/java/org/openhab/binding/pushover/internal/actions/PushoverActionsTest.java b/bundles/org.openhab.binding.pushover/src/test/java/org/openhab/binding/pushover/internal/actions/PushoverActionsTest.java index 3edc7684e048d..83280e8b361e8 100644 --- a/bundles/org.openhab.binding.pushover/src/test/java/org/openhab/binding/pushover/internal/actions/PushoverActionsTest.java +++ b/bundles/org.openhab.binding.pushover/src/test/java/org/openhab/binding/pushover/internal/actions/PushoverActionsTest.java @@ -18,6 +18,8 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; +import java.time.Duration; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.junit.jupiter.api.BeforeEach; @@ -47,6 +49,7 @@ public class PushoverActionsTest { private static final String URL = "https://www.test.com"; private static final String URL_TITLE = "Some Link"; private static final String RECEIPT = "12345"; + private static final Duration TTL = Duration.ofSeconds(15); private final ThingActions thingActionsStub = new ThingActions() { @Override @@ -85,19 +88,26 @@ public void testSendMessageThingHandlerIsNull() { } @Test - public void testSendMessageWithoutTitle() { + public void testSendMessage() { pushoverThingActions.setThingHandler(mockPushoverAccountHandler); - boolean sent = PushoverActions.sendMessage(pushoverThingActions, MESSAGE, null); + boolean sent = PushoverActions.sendMessage(pushoverThingActions, MESSAGE); assertThat(sent, is(true)); } @Test - public void testSendMessage() { + public void testSendMessageWithTitle() { pushoverThingActions.setThingHandler(mockPushoverAccountHandler); boolean sent = PushoverActions.sendMessage(pushoverThingActions, MESSAGE, TITLE); assertThat(sent, is(true)); } + @Test + public void testSendMessageWithTitleAndTTL() { + pushoverThingActions.setThingHandler(mockPushoverAccountHandler); + boolean sent = PushoverActions.sendMessage(pushoverThingActions, MESSAGE, TITLE, TTL); + assertThat(sent, is(true)); + } + // sendURLMessage @Test public void testSendURLMessageThingActionsIsNotPushoverThingActions() {