Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[amberelectric] Initial contribution #16850

Merged
merged 31 commits into from
Jun 15, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
29596de
Initial Commit
psmedley Aug 9, 2023
512c274
Update default refresh interval to 60s
psmedley Aug 9, 2023
b7053e1
Fix typo
psmedley Aug 9, 2023
ffaad3b
Support extra channels
psmedley Aug 12, 2023
4aabe49
Adjust code to configure site using NMI rather than siteid
psmedley Aug 19, 2023
04b0b87
Merge branch 'openhab:main' into amberelectric
psmedley Jun 4, 2024
38c6e9a
Updates
psmedley Jun 4, 2024
803ca89
Cleanups
psmedley Jun 4, 2024
7c2e177
Fix warnings
psmedley Jun 4, 2024
064dccf
Code cleanups
psmedley Jun 6, 2024
de072be
Fix business name
psmedley Jun 6, 2024
8bbaf41
build fix
psmedley Jun 6, 2024
f113fb2
Code Improvements
psmedley Jun 6, 2024
b7e8380
Remove poll delay
psmedley Jun 7, 2024
3b37959
Update bundles/org.openhab.binding.amberelectric/src/main/resources/O…
psmedley Jun 7, 2024
3c9da93
Update bundles/org.openhab.binding.amberelectric/README.md
psmedley Jun 7, 2024
df90729
Update bundles/org.openhab.binding.amberelectric/README.md
psmedley Jun 7, 2024
557a5ff
Update bundles/org.openhab.binding.amberelectric/src/main/java/org/op…
psmedley Jun 7, 2024
6232ee2
Further improvements
psmedley Jun 7, 2024
c1e6345
Improve thing definitions
psmedley Jun 7, 2024
8b48144
Cleanups
psmedley Jun 7, 2024
482e832
Cleanups
psmedley Jun 7, 2024
fe30cbe
Latest fixes
psmedley Jun 8, 2024
18c277a
Update bundles/org.openhab.binding.amberelectric/README.md
psmedley Jun 8, 2024
5fb7a1f
Final? Updates
psmedley Jun 8, 2024
404f663
Update NMI config with the detected one if it's left empty
psmedley Jun 9, 2024
f6ca4d9
Tidy up the setting of electricity price
psmedley Jun 10, 2024
f499c5f
Fallback to DecimalType if AUD is unknown
psmedley Jun 12, 2024
01541fb
Fixes
psmedley Jun 14, 2024
80d5e15
Fixes
psmedley Jun 14, 2024
075a8ee
(Hopefully) final updates before merge
psmedley Jun 15, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions bom/openhab-addons/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@
<artifactId>org.openhab.binding.amazonechocontrol</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.amberelectric</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.ambientweather</artifactId>
Expand Down
13 changes: 13 additions & 0 deletions bundles/org.openhab.binding.amberelectric/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
69 changes: 69 additions & 0 deletions bundles/org.openhab.binding.amberelectric/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Amber Electric Binding

A binding that supports the Australian energy retailer Amber's API (https://www.amber.com.au/) and provides data on the current pricing for buying and selling power, as well as the current level of renewables in the NEM.
psmedley marked this conversation as resolved.
Show resolved Hide resolved

## Supported Things

- `amber-electric` Amber Electric API
psmedley marked this conversation as resolved.
Show resolved Hide resolved

## Discovery

The binding does not support auto discovery.
psmedley marked this conversation as resolved.
Show resolved Hide resolved

## Thing Configuration

As a minimum, the IP address is needed:

- `apikey` - The API key from the 'Developer' section of https://apps.amber.com.au
- 'nmi' optional - the NMI for your property. Required if you have multiple properties with Amber
- 'refresh' the refresh rate for querying the API.

## Channels

| channel id | type | description |
|----------------------|---------------|---------------------------------------------------------------------------------------|
| elecprice | Number | Current price to import power from the grid
| clprice | Number | Current price to import power for Controlled Load
| feedinprice | Number | Current price to export power to the grid
| elecstatus | String | Current price status of grid import
| clstatus | String | Current price status of controlled load import
| feedinstatus | String | Current price status of Feed-In
| nemtime | String | NEM time of last pricing update
| renewables | Number | Current level of renewables in the grid
| spike | Switch | Report if the grid has a current price spike

## Full Example

### `amberelectric.things`:

```java
amberelectric:amberelectric:AmberElectric [ apikey="psk_xxxxxxxxxxxxxxxxxxxx" ]
```

### `amberelectric.items`:

```java
Number AmberElectric_ElecPrice { channel="amberelectric:amberelectric:AmberElectric:elecprice" }
Number AmberElectric_CLPrice { channel="amberelectric:amberelectric:AmberElectric:clprice" }
Number AmberElectric_FeedInPrice { channel="amberelectric:amberelectric:AmberElectric:feedinprice" }
String AmberElectric_ElecStatus { channel="amberelectric:amberelectric:AmberElectric:elecstatus" }
String AmberElectric_CLStatus { channel="amberelectric:amberelectric:AmberElectric:clstatus" }
String AmberElectric_FeedInStatus { channel="amberelectric:amberelectric:AmberElectric:feedinstatus" }
String AmberElectric_nemtime { channel="amberelectric:amberelectric:AmberElectric:nemtime" }
Number AmberElectric_Renewables { channel="amberelectric:amberelectric:AmberElectric:renewables" }
Switch AmberElectric_Spike { channel="amberelectric:amberelectric:AmberElectric:spike" }
```

### `amberelectric.sitemap`:

```perl
Text item=AmberElectric_ElecPrice label="Electricity Price"
Text item=AmberElectric_CLPrice label="Controlled Load Price"
Text item=AmberElectric_FeedInPrice label="Feed-In Price"
Text item=AmberElectric_ElecStatus label="Electricity Price Status"
Text item=AmberElectric_CLStatus label="Controlled Load Price Status"
Text item=AmberElectric_FeedInStatus label="Feed-In Price Status"
Text item=AmberElectric_nemtime label="Current time of NEM pricing"
Text item=AmberElectric_Renewables label="Renewables Level"
Switch item=AmberElectric_Spike label="Spike Status"
```
17 changes: 17 additions & 0 deletions bundles/org.openhab.binding.amberelectric/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://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.2.0-SNAPSHOT</version>
</parent>

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

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

import java.util.Collections;
import java.util.Set;

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

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

private static final String BINDING_ID = "amberelectric";

// List of all Thing Type UIDs
public static final ThingTypeUID AMBERELECTRIC_THING = new ThingTypeUID(BINDING_ID, "amber-electric");

// List of all Channel ids
public static final String CHANNEL_AMBERELECTRIC_ELECPRICE = "elecprice";
public static final String CHANNEL_AMBERELECTRIC_CLPRICE = "clprice";
public static final String CHANNEL_AMBERELECTRIC_FEEDINPRICE = "feedinprice";
public static final String CHANNEL_AMBERELECTRIC_ELECSTATUS = "elecstatus";
public static final String CHANNEL_AMBERELECTRIC_CLSTATUS = "clstatus";
public static final String CHANNEL_AMBERELECTRIC_FEEDINSTATUS = "feedinstatus";
public static final String CHANNEL_AMBERELECTRIC_NEMTIME = "nemtime";
public static final String CHANNEL_AMBERELECTRIC_RENEWABLES = "renewables";
public static final String CHANNEL_AMBERELECTRIC_SPIKE = "spike";

public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(AMBERELECTRIC_THING);
psmedley marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* 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.amberelectric.internal;

import org.eclipse.jdt.annotation.NonNullByDefault;

/**
* Exception for when an unexpected response is received from the AmberAPI.
*
* @author Paul Smedley - Initial contribution
*
*/
@NonNullByDefault
public class AmberElectricCommunicationException extends Exception {
private static final long serialVersionUID = 529232811860854017L;

public AmberElectricCommunicationException(String message) {
super(message);
}

public AmberElectricCommunicationException(Throwable ex) {
super(ex);
}

public AmberElectricCommunicationException(String message, Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* 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.amberelectric.internal;

import org.eclipse.jdt.annotation.NonNullByDefault;

/**
* The AmberElectricConfiguration class contains fields mapping thing configuration parameters.
*
* @author Paul Smedley - Initial contribution
*/
@NonNullByDefault
public class AmberElectricConfiguration {
public String apikey = "";
public String nmi = "";
public long refresh = 60;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/**
* 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.amberelectric.internal;

import java.io.IOException;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.amberelectric.internal.api.CurrentPrices;
import org.openhab.binding.amberelectric.internal.api.Sites;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType;
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.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

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

private long refreshInterval;
private String apikey = "";
private String nmi = "";
private String siteID = "";

private @NonNullByDefault({}) AmberElectricConfiguration config;
private @NonNullByDefault({}) AmberElectricWebTargets webTargets;
private @Nullable ScheduledFuture<?> pollFuture;

public AmberElectricHandler(Thing thing) {
super(thing);
}

@Override
public void handleCommand(ChannelUID channelUID, Command command) {
logger.warn("This binding is read only");
psmedley marked this conversation as resolved.
Show resolved Hide resolved
}

@Override
public void initialize() {
config = getConfigAs(AmberElectricConfiguration.class);
if (config.apikey.isBlank()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "API Key must be set");
psmedley marked this conversation as resolved.
Show resolved Hide resolved
} else {
webTargets = new AmberElectricWebTargets();
updateStatus(ThingStatus.UNKNOWN);
refreshInterval = config.refresh;
nmi = config.nmi;
apikey = config.apikey;

schedulePoll();
}
}

@Override
public void dispose() {
super.dispose();
stopPoll();
}

private void schedulePoll() {
ScheduledFuture<?> pollFuture = this.pollFuture;
if (pollFuture != null) {
pollFuture.cancel(false);
}
psmedley marked this conversation as resolved.
Show resolved Hide resolved
logger.debug("Scheduling poll for 1 second out, then every {} s", refreshInterval);
this.pollFuture = scheduler.scheduleWithFixedDelay(this::poll, 1, refreshInterval, TimeUnit.SECONDS);
psmedley marked this conversation as resolved.
Show resolved Hide resolved
}

private void poll() {
try {
logger.debug("Polling for state");
pollStatus();
} catch (IOException e) {
logger.debug("Could not connect to AmberAPI", e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
} catch (RuntimeException e) {
logger.warn("Unexpected error connecting to AmberAPI", e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
}
}

private void stopPoll() {
final Future<?> future = pollFuture;
if (future != null && !future.isCancelled()) {
psmedley marked this conversation as resolved.
Show resolved Hide resolved
future.cancel(true);
pollFuture = null;
}
}

private void pollStatus() throws IOException {

psmedley marked this conversation as resolved.
Show resolved Hide resolved
try {
if (siteID.isEmpty()) {
Sites sites = webTargets.getSites(apikey, nmi);
// add error handling
siteID = sites.siteid;
logger.debug("Detected amber siteid is {}, for nmi {}", sites.siteid, sites.nmi);
}

CurrentPrices currentPrices = webTargets.getCurrentPrices(siteID, apikey);
updateStatus(ThingStatus.ONLINE);
updateState(AmberElectricBindingConstants.CHANNEL_AMBERELECTRIC_ELECPRICE,
new DecimalType(currentPrices.elecPerKwh));
updateState(AmberElectricBindingConstants.CHANNEL_AMBERELECTRIC_CLPRICE,
new DecimalType(currentPrices.clPerKwh));
updateState(AmberElectricBindingConstants.CHANNEL_AMBERELECTRIC_CLSTATUS,
new StringType(currentPrices.clStatus));
updateState(AmberElectricBindingConstants.CHANNEL_AMBERELECTRIC_FEEDINPRICE,
new DecimalType(currentPrices.feedInPerKwh));
updateState(AmberElectricBindingConstants.CHANNEL_AMBERELECTRIC_ELECSTATUS,
new StringType(currentPrices.elecStatus));
updateState(AmberElectricBindingConstants.CHANNEL_AMBERELECTRIC_FEEDINSTATUS,
new StringType(currentPrices.feedInStatus));
updateState(AmberElectricBindingConstants.CHANNEL_AMBERELECTRIC_NEMTIME,
new StringType(currentPrices.nemTime));
updateState(AmberElectricBindingConstants.CHANNEL_AMBERELECTRIC_RENEWABLES,
new DecimalType(currentPrices.renewables));
switch (currentPrices.spikeStatus) {
case "none":
updateState(AmberElectricBindingConstants.CHANNEL_AMBERELECTRIC_SPIKE, OnOffType.OFF);
break;
default:
updateState(AmberElectricBindingConstants.CHANNEL_AMBERELECTRIC_SPIKE, OnOffType.ON);
break;
psmedley marked this conversation as resolved.
Show resolved Hide resolved
}
} catch (AmberElectricCommunicationException e) {
logger.debug("Unexpected error connecting to Amber Electric API", e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
}
}
}
Loading