Skip to content

Commit

Permalink
[flume] Initial contribution (#17152)
Browse files Browse the repository at this point in the history
* Initial submission

Signed-off-by: Jeff James <[email protected]>
  • Loading branch information
jsjames authored Sep 8, 2024
1 parent 6f6787b commit 8f62374
Show file tree
Hide file tree
Showing 32 changed files with 2,504 additions and 0 deletions.
5 changes: 5 additions & 0 deletions bom/openhab-addons/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,11 @@
<artifactId>org.openhab.binding.flicbutton</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.flume</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.fmiweather</artifactId>
Expand Down
13 changes: 13 additions & 0 deletions bundles/org.openhab.binding.flume/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
120 changes: 120 additions & 0 deletions bundles/org.openhab.binding.flume/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# Flume Binding

This binding will interface with the cloud API to retrieve water usage from your [Flume](https://flumewater.com/) water monitor.

## Introduction

The Cloud Connector is required as a "bridge" to interface to the cloud service from Flume.
While the Flume API supports a rich querying of historical usage data, this binding only retrieves the cumulative water used and instantaneous water used, thus relying on openHAB's rich persistence services for exploring historical values.
The binding does support querying historical data through the use of the Rule Action.

## Supported Things

This binding supports the following things:

| Thing | id | Type | Description |
|---------- |--------- |-------- |------------------------------ |
| Flume Cloud Connector | cloud | Bridge | This represents the cloud account to interface with the Flume API. |
| Flume Meter Device | meter-device | Thing | This interfaces to a specific Flume water monitor associated with the account. |

This binding should work with multiple Flume monitors associated with the account, however it is currently only tested with a single device.

## Discovery

Once a Flume Cloud Connector is created and established, the binding will automatically discover any Flume Meter Devices' associated with the account.

## Flume Cloud Connector (Bridge) Configuration

The only configuration required is to create a Flume Cloud Connector thing and fill in the appropriate configuration parameters.
The client id and client secret can be found under Settings/API access from the [Flume portal online](https://portal.flumewater.com/settings).
Note, there is a rate limit of 120 queries per hour imposed by Flume so use caution when selecting the Refresh Interfacl.

| Name | id | Type | Description | Default | Required | Advanced |
|------- |------ |--------- |--------- |------- |------ |----- |
| Flume Username | username | text | Username to access Flume cloud | N/A | yes | no |
| Flume Password | password | text | Password to access Flume cloud | N/A | yes | no |
| Flume Client ID | clientId | text | ID retrieved from Flume cloud | N/A | yes | no |
| Flume Client Secret | clientSecret | text | Secret retrieved from Flume cloud | N/A | yes | no |
| Instantaneous Refresh Interval | refreshIntervalInstantaneous | integer | Polling interval (minutes) for instantaneous usage (rate limited to 120 queries/sec) | 1 | no | yes |
| Cumulative Refresh Interval | refreshIntervalCumulative | integer | Polling interval (minutes) for cumulative usage (rate-limited with above) | 5 | no | yes |

## Flume Meter Device Configuration

| Name | id | Type | Description | Default | Required | Advanced |
|------- |--------- |------ |--------- |------- |------ |----- |
| ID | id | text | ID of the Flume device | N/A | yes | no |

## Flume Meter Device Channels

| Channel | id | Type | Read/Write | Description |
|---------- |-------- |-------- |-------- |-------- |
| Instant Water Usage | instant-usage | Number:VolumetricFlowRate | R | Flow rate of water over the last minute |
| Cumulative Used | cumulative-usage | Number:Volume | R | Total volume of water used since the beginning of Flume install |
| Battery Level | battery-level | Number:Dimensionless | R | Estimate of percent of remaining battery level |
| Low Battery | low-battery | Switch | R | Indicator of low battery level |
| Last Seen | last-seen | DateTime | R | Date/Time when meter was last seen on the network |
| Usage Alert | usage-alert | Trigger | n/a | Trigger channel for usage alert notification |

## Full Example

### Thing Configuration

Please note that the device meter ID is only available through the API and not available on the Flume portal.
When the Bridge device is first created, there will be a log message with the ID of the discovered device which can be used in further configuring the device via the text files.

```
Bridge flume:cloud:cloudconnector [ username="xxx", password="xxx", clientId="xxx", clientSecret="xxx" ] {
meter-device meter [ id="xxx" ]
}
```

### Item Configuration

```
Number:VolumetricFlowRate InstantUsage "Instant Usage" { channel = "flume:meter-device:1:meter:instant-usage" }
Number:Volume CumulativeUsed "Cumulative Used" { channel = "flume:meter-device:1:meter:cumulative-usage" }
Number:Dimensionless BatteryLevel "Battery Level" { channel = "flume:meter-device:1:meter:battery-level" }
DateTime LastSeen "Last Seen" { channel = "flume:meter-device:1:meter:last-seen" }
Switch LowPower "Battery Low Power" { channel = "flume:meter-device:1:meter:low-battery" }
```

### Rules

```java
rule "Flume Usage Alert"
when
Channel 'flume:device:cloud:meter:usageAlert' triggered
then
logInfo("Flume Usage Alert", "Message: {}", receivedEvent)
end
```

## Rule Actions

There is an action where you can query the Flume Cloud for water usage as shown in the blow example:

```java
val flumeActions = getActions("flume", "flume:device:cloud:meter")

if(null === flumeActions) {
logInfo("actions", "flumeActions not found, check thing ID")
return
}

val LocalDateTime untilDateTime = LocalDateTime.now
val LocalDateTime sinceDateTime = untilDateTime.minusHours(24)

val usage = flumeActions.queryWaterUsage(sinceDateTime, untilDateTime, "MIN", "SUM")
logInfo("Flume", "Water usage is {}", usage.toString())
```

### queryWaterUsage(sinceDateTime, untilDateTime, bucket, operation)

Queries the cloud for water usage between the two dates.

- sinceDateTime (LocalDateTime): begin date/time of query range
- untilDateTime (LocalDateTime): end date/time of query range
- bucket (String), values: YR, MON, DAY, HR, MIN
- operation (String), values: SUM, AVG, MIN, MAX, CNT
17 changes: 17 additions & 0 deletions bundles/org.openhab.binding.flume/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>4.3.0-SNAPSHOT</version>
</parent>

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

<name>openHAB Add-ons :: Bundles :: Flume 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.flume-${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-flume" description="Flume Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.flume/${project.version}</bundle>
</feature>
</features>
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* 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.flume.internal;

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

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

private static final String BINDING_ID = "flume";

// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_CLOUD = new ThingTypeUID(BINDING_ID, "cloud");
public static final ThingTypeUID THING_TYPE_METER = new ThingTypeUID(BINDING_ID, "meter-device");

// Config options
public static final String PARAM_USERNAME = "username";
public static final String PARAM_PASSWORD = "password";
public static final String PARAM_CLIENTID = "clientId";
public static final String PARAM_CLIENTSECRET = "clientSecret";
public static final String PARAM_REFRESH_INTERVAL_INSTANTANEOUS = "refreshIntervalInstanteous";
public static final String PARAM_REFRESH_INTERVAL_CUMULATIVE = "refreshIntervalCumulative";

// List of all Device Channel ids
public static final String CHANNEL_DEVICE_CUMULATIVEUSAGE = "cumulative-usage";
public static final String CHANNEL_DEVICE_INSTANTUSAGE = "instant-usage";
public static final String CHANNEL_DEVICE_BATTERYLEVEL = "battery-level";
public static final String CHANNEL_DEVICE_LOWBATTERY = "low-battery";
public static final String CHANNEL_DEVICE_LASTSEEN = "last-seen";
public static final String CHANNEL_DEVICE_USAGEALERT = "usage-alert";

// Properties
public static final String PROPERTY_ID = "id";

public static final int DEFAULT_POLLING_INTERVAL_INSTANTANEOUS = 1;
public static final int DEFAULT_POLLING_INTERVAL_CUMULATIVE = 5;
}
Original file line number Diff line number Diff line change
@@ -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.flume.internal;

import static org.openhab.binding.flume.internal.FlumeBindingConstants.*;

import org.eclipse.jdt.annotation.NonNullByDefault;

/**
* The {@link FlumeBridgeConfig} class contains fields mapping thing configuration parameters.
*
* @author Jeff James - Initial contribution
*/
@NonNullByDefault
public class FlumeBridgeConfig {
public String clientId = "";
public String clientSecret = "";
public String username = "";
public String password = "";

public int refreshIntervalInstantaneous = DEFAULT_POLLING_INTERVAL_INSTANTANEOUS;
public int refreshIntervalCumulative = DEFAULT_POLLING_INTERVAL_CUMULATIVE;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* 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.flume.internal;

import org.eclipse.jdt.annotation.NonNullByDefault;

/**
* The {@link FlumeDeviceConfig} class contains fields mapping thing configuration parameters.
*
* @author Jeff James - Initial contribution
*/
@NonNullByDefault
public class FlumeDeviceConfig {
public String id = "";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/**
* 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.flume.internal;

import static org.openhab.binding.flume.internal.FlumeBindingConstants.*;

import java.util.Set;

import javax.measure.spi.SystemOfUnits;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.flume.internal.handler.FlumeBridgeHandler;
import org.openhab.binding.flume.internal.handler.FlumeDeviceHandler;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.i18n.UnitProvider;
import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.thing.Bridge;
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 FlumeHandlerFactory} is responsible for creating things and thing
* handlers.
*
* @author Jeff James - Initial contribution
*/
@NonNullByDefault
@Component(configurationPid = "binding.flume", service = ThingHandlerFactory.class)
public class FlumeHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_CLOUD, THING_TYPE_METER);

private final HttpClientFactory httpClientFactory;
private final TranslationProvider i18nProvider;
private final LocaleProvider localeProvider;
public final SystemOfUnits systemOfUnits;

@Activate
public FlumeHandlerFactory(@Reference UnitProvider unitProvider, @Reference HttpClientFactory httpClientFactory,
final @Reference TranslationProvider i18nProvider, final @Reference LocaleProvider localeProvider) {
this.systemOfUnits = unitProvider.getMeasurementSystem();
this.httpClientFactory = httpClientFactory;
this.i18nProvider = i18nProvider;
this.localeProvider = localeProvider;
}

@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
}

@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();

if (THING_TYPE_CLOUD.equals(thingTypeUID)) {
return new FlumeBridgeHandler((Bridge) thing, systemOfUnits, this.httpClientFactory.getCommonHttpClient(),
i18nProvider, localeProvider);
} else if (THING_TYPE_METER.equals(thingTypeUID)) {
return new FlumeDeviceHandler(thing);
}

return null;
}
}
Loading

0 comments on commit 8f62374

Please sign in to comment.