Skip to content

Commit

Permalink
[prowl] Initial contribution (#10967)
Browse files Browse the repository at this point in the history
Signed-off-by: Ondrej Pecta <opecta@gmail.com>
  • Loading branch information
octa22 authored Mar 13, 2022
1 parent 4f38441 commit 43fe75b
Show file tree
Hide file tree
Showing 15 changed files with 534 additions and 0 deletions.
1 change: 1 addition & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@
/bundles/org.openhab.binding.plugwiseha/ @lsiepel
/bundles/org.openhab.binding.powermax/ @lolodomo
/bundles/org.openhab.binding.proteusecometer/ @2chilled
/bundles/org.openhab.binding.prowl/ @octa22
/bundles/org.openhab.binding.publictransportswitzerland/ @jeremystucki
/bundles/org.openhab.binding.pulseaudio/ @peuter
/bundles/org.openhab.binding.pushbullet/ @hakan42
Expand Down
5 changes: 5 additions & 0 deletions bom/openhab-addons/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1241,6 +1241,11 @@
<artifactId>org.openhab.binding.proteusecometer</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.prowl</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.publictransportswitzerland</artifactId>
Expand Down
13 changes: 13 additions & 0 deletions bundles/org.openhab.binding.prowl/NOTICE
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
This content is produced and maintained by the openHAB project.

* Project home: https://www.openhab.org

== Declared Project Licenses

This program and the accompanying materials are made available under the terms
of the Eclipse Public License 2.0 which is available at
https://www.eclipse.org/legal/epl-2.0/.

== Source Code

https://github.com/openhab/openhab-addons
43 changes: 43 additions & 0 deletions bundles/org.openhab.binding.prowl/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Prowl Binding

This is the binding for the [Prowl](https://www.prowlapp.com) iOS push service.
It has been written from scratch and therefore it is not based on the original 1.x Prowl binding.
It has no other purpose than sending push messages to iOS devices.

## Binding Configuration

The binding does not require any manual configuration on the binding level.

## Thing Configuration

This binding has only one thing called _Broker_. If you want to use this binding, just add a broker instance and configure the API key, which you can generate on the Prowl website.
You can also modify the _application_ property, which identifies the originator of these push messages.
If you want to have specific refresh time for the remaining free push messages channel, you can edit the _refresh_ property.
Anyway beware - every check consumes one free push message you can send in an hour.

## Channels

The broker thing has only one channel keeping the number of free push messages, which can be sent.

| channel | type | description |
|------------|--------|--------------------------------------------------------|
| remaining | Number | This channel provides the number of free push messages |

## Example

_*.things_

```
Thing prowl:broker:mybroker "Prowl Broker" [ apiKey="0000000000000000000000000000000000000000" ]
```

_*.rules_

Once you have created the broker thing with a valid API key, you can use the Prowl service in your rules.
First you need to create an instance of the broker just before any call or on the top rules level. (replace the _mybroker_ with the right name of your instance).
Then you can call method _pushNotification_, which requires two parameters - _event_ and _description_.

```
val prowl = getActions("prowl","prowl:broker:mybroker")
prowl.pushNotification("Event", "This is the description of the event")
```
17 changes: 17 additions & 0 deletions bundles/org.openhab.binding.prowl/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>3.3.0-SNAPSHOT</version>
</parent>

<artifactId>org.openhab.binding.prowl</artifactId>

<name>openHAB Add-ons :: Bundles :: Prowl Binding</name>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.prowl-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>

<feature name="openhab-binding-prowl" description="Prowl Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.prowl/${project.version}</bundle>
</feature>
</features>
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* Copyright (c) 2010-2022 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.prowl.internal;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;

/**
* The {@link ProwlBindingConstants} class defines common constants, which are
* used across the whole binding.
*
* @author Ondrej Pecta - Initial contribution
*/
@NonNullByDefault
public class ProwlBindingConstants {

private static final String BINDING_ID = "prowl";

// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_BROKER = new ThingTypeUID(BINDING_ID, "broker");

// List of all Channel ids
public static final String CHANNEL_REMAINING = "remaining";

// constants
public static final String PROWL_ADD_URI = "https://api.prowlapp.com/publicapi/add";
public static final String PROWL_VERIFY_URI = "https://api.prowlapp.com/publicapi/verify";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* Copyright (c) 2010-2022 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.prowl.internal;

import org.eclipse.jdt.annotation.NonNullByDefault;

/**
* The {@link ProwlConfiguration} class contains fields mapping thing configuration parameters.
*
* @author Ondrej Pecta - Initial contribution
*/
@NonNullByDefault
public class ProwlConfiguration {

/**
* Prowl configuration parameters.
*/
public String apiKey = "";
public String application = "openHAB";
public int refresh = 30;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/**
* Copyright (c) 2010-2022 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.prowl.internal;

import static org.openhab.binding.prowl.internal.ProwlBindingConstants.*;

import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ScheduledFuture;
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.util.StringContentProvider;
import org.openhab.binding.prowl.internal.action.ProwlActions;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.thing.binding.ThingHandlerService;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* The {@link ProwlHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Ondrej Pecta - Initial contribution
*/
@NonNullByDefault
public class ProwlHandler extends BaseThingHandler {

private final Logger logger = LoggerFactory.getLogger(ProwlHandler.class);

private ProwlConfiguration config = new ProwlConfiguration();
final private HttpClient httpClient;

/**
* Future to poll for status
*/
private @Nullable ScheduledFuture<?> statusFuture;

public ProwlHandler(Thing thing, HttpClient client) {
super(thing);
this.httpClient = client;
}

@Override
public void handleCommand(ChannelUID channelUID, Command command) {
}

@Override
public void initialize() {
config = getConfigAs(ProwlConfiguration.class);
updateStatus(ThingStatus.UNKNOWN);

statusFuture = scheduler.scheduleWithFixedDelay(() -> updateStatus(), 0, config.refresh, TimeUnit.MINUTES);
}

private void updateStatus() {
if (keyVerificationSucceeded(config.apiKey)) {
updateStatus(ThingStatus.ONLINE);
} else {
updateStatus(ThingStatus.OFFLINE);
}
}

@Override
public void dispose() {
ScheduledFuture<?> localPollFuture = statusFuture;
if (localPollFuture != null && !localPollFuture.isCancelled()) {
localPollFuture.cancel(true);
}
super.dispose();
}

private boolean keyVerificationSucceeded(String apiKey) {
try {
ContentResponse response = httpClient.GET(PROWL_VERIFY_URI + "?apikey=" + apiKey);
String resp = response.getContentAsString();
logger.trace("verify response: {}", resp);
if (resp.contains("<success code=\"200\"")) {
updateFreeMessages(resp);
return true;
} else {
return false;
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (ExecutionException e) {
logger.debug("error during calling uri: {}", PROWL_ADD_URI, e);
} catch (TimeoutException e) {
logger.debug("timeout during calling uri: {}", PROWL_ADD_URI, e);
}
return false;
}

@Override
public Collection<Class<? extends ThingHandlerService>> getServices() {
return Collections.singletonList(ProwlActions.class);
}

public void pushNotification(@Nullable String event, @Nullable String description) {
if (event == null || description == null) {
logger.debug("Cannot push message with null event or null description");
return;
}

logger.debug("Pushing an event: {} with desc: {}", event, description);
try {
ContentResponse response = httpClient.POST(PROWL_ADD_URI).timeout(5, TimeUnit.SECONDS)
.content(
new StringContentProvider("apikey=" + config.apiKey + "&application=" + config.application
+ "&event=" + event + "&description=" + description),
"application/x-www-form-urlencoded; charset=UTF-8")
.send();
String resp = response.getContentAsString();
updateFreeMessages(resp);
logger.trace("add response: {}", resp);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (ExecutionException e) {
logger.debug("error during calling uri: {}", PROWL_ADD_URI, e);
} catch (TimeoutException e) {
logger.debug("timeout during calling uri: {}", PROWL_ADD_URI, e);
}
}

private void updateFreeMessages(String resp) {
final String str = "remaining=\"";

// trying to simply parse the simple xml rather than using XPATH
int start = resp.indexOf(str) + str.length();
int end = resp.indexOf("\"", start + 1);

try {
String messages = resp.substring(start, end);
logger.debug("remaining messages parsed: {}", messages);
int freeMessages = Integer.parseInt(messages);
updateState(CHANNEL_REMAINING, new DecimalType(freeMessages));
} catch (StringIndexOutOfBoundsException | NumberFormatException ex) {
logger.debug("Error parsing remaining messages", ex);
}
}
}
Loading

0 comments on commit 43fe75b

Please sign in to comment.